linux原始套接字-构造IP_UDP

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux原始套接字-构造IP_UDP相关的知识,希望对你有一定的参考价值。

一.概述                                                   

上一篇tcp一样,udp也是封装在ip报文里面。创建UDP的原始套接字如下:

1 (sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);

同样,如果要构造udp的ip首部,要开启IP_HDRINCL选项!

udp首部格式:

技术分享

udp的不可靠性,比tcp报文简单很多。上面的16位UDP长度是UDP首部+普通数据的总长度,这点跟ip首部的16位总长度一样!

udp结构定义在netinet/udp.h

 1 struct udphdr
 2 {
 3   __extension__ union
 4   {
 5     struct
 6     {
 7       u_int16_t uh_sport;        /* source port */
 8       u_int16_t uh_dport;        /* destination port */
 9       u_int16_t uh_ulen;        /* udp length */
10       u_int16_t uh_sum;        /* udp checksum */
11     };
12     struct
13     {
14       u_int16_t source;
15       u_int16_t dest;
16       u_int16_t len;
17       u_int16_t check;
18     };
19   };
20 };

二.构造IP_UDP报文发送                       

  1 /**
  2  * @file ip_udp_send.c
  3  */
  4 
  5 #include <stdio.h>
  6 #include <stdlib.h>
  7 #include <string.h>
  8 #include <unistd.h>
  9 #include <sys/socket.h>
 10 #include <arpa/inet.h>
 11 #include <netinet/ip.h>
 12 #include <netinet/udp.h>
 13 
 14 /* ip首部长度 */
 15 #define IP_HEADER_LEN sizeof(struct ip)
 16 /* udp首部长度 */
 17 #define UDP_HEADER_LEN sizeof(struct udphdr)
 18 /* ip首部 + udp首部长度 */
 19 #define IP_UDP_HEADER_LEN IP_HEADER_LEN + UDP_HEADER_LEN
 20 
 21 void err_exit(const char *err_msg)
 22 {
 23     perror(err_msg);
 24     exit(1);
 25 }
 26 
 27 /* 填充ip首部 */
 28 struct ip *fill_ip_header(const char *src_ip, const char *dst_ip, int ip_packet_len)
 29 {
 30     struct ip *ip_header;
 31 
 32     ip_header = (struct ip *)malloc(IP_HEADER_LEN);
 33     ip_header->ip_v = IPVERSION;
 34     ip_header->ip_hl = IP_HEADER_LEN / 4;
 35     ip_header->ip_tos = 0;
 36     ip_header->ip_len = htons(ip_packet_len);
 37     ip_header->ip_id = 0;
 38     ip_header->ip_off = 0;
 39     ip_header->ip_ttl = MAXTTL;
 40     ip_header->ip_p = IPPROTO_UDP;        /* 这里是UDP */
 41     ip_header->ip_sum = 0;
 42     ip_header->ip_src.s_addr = inet_addr(src_ip);
 43     ip_header->ip_dst.s_addr = inet_addr(dst_ip);
 44 
 45     return ip_header;
 46 }
 47 
 48 /* 填充udp首部 */
 49 struct udphdr *fill_udp_header(int src_port, int dst_port, int udp_packet_len)
 50 {
 51     struct udphdr *udp_header;
 52 
 53     udp_header = (struct udphdr *)malloc(UDP_HEADER_LEN);
 54     udp_header->source = htons(src_port);
 55     udp_header->source = htons(dst_port);
 56     /* 这里的长度是整个UDP报文 */
 57     udp_header->len = htons(udp_packet_len);
 58     udp_header->check = 0;
 59 
 60     return udp_header;
 61 }
 62 
 63 /* 发送ip_udp报文 */
 64 void ip_udp_send(const char *src_ip, int src_port, const char *dst_ip, int dst_port, const char *data)
 65 {
 66     struct ip *ip_header;
 67     struct udphdr *udp_header;
 68     struct sockaddr_in dst_addr;
 69     socklen_t sock_addrlen = sizeof(struct sockaddr_in);
 70 
 71     int data_len = strlen(data);
 72     int ip_packet_len = IP_UDP_HEADER_LEN + data_len;
 73     int udp_packet_len = UDP_HEADER_LEN + data_len;
 74     char buf[ip_packet_len];
 75     int sockfd, ret_len, on = 1;
 76 
 77     bzero(&dst_addr, sock_addrlen);
 78     dst_addr.sin_family = PF_INET;
 79     dst_addr.sin_addr.s_addr = inet_addr(dst_ip);
 80     dst_addr.sin_port = htons(dst_port);
 81 
 82     /* 创建udp原始套接字 */
 83     if ((sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP)) == -1)
 84         err_exit("socket()");
 85 
 86     /* 开启IP_HDRINCL,自定义IP首部 */
 87     if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) == -1)
 88         err_exit("setsockopt()");
 89 
 90     /* ip首部 */
 91     ip_header = fill_ip_header(src_ip, dst_ip, ip_packet_len);
 92     /* udp首部 */
 93     udp_header = fill_udp_header(src_port, dst_port, udp_packet_len);
 94 
 95     bzero(buf, ip_packet_len);
 96     memcpy(buf, ip_header, IP_HEADER_LEN);
 97     memcpy(buf + IP_HEADER_LEN, udp_header, UDP_HEADER_LEN);
 98     memcpy(buf + IP_UDP_HEADER_LEN, data, data_len);
 99 
100     /* 发送报文 */
101     ret_len = sendto(sockfd, buf, ip_packet_len, 0, (struct sockaddr *)&dst_addr, sock_addrlen);
102     if (ret_len > 0)
103         printf("sendto() ok!!!\\n");
104     else printf("sendto() failed\\n");
105 
106     close(sockfd);
107     free(ip_header);
108     free(udp_header);
109 }
110 
111 int main(int argc, const char *argv[])
112 {
113     if (argc != 6)
114     {
115         printf("usage:%s src_ip src_port dst_ip dst_port data\\n", argv[0]);
116         exit(1);
117     }
118 
119     /* 发送ip_udp报文 */
120     ip_udp_send(argv[1], atoi(argv[2]), argv[3], atoi(argv[4]), argv[5]);
121 
122     return 0;
123 }

上面的大部分代码都跟上一篇差不多。不同的是这次是填充udp首部,创建原始套接字的类型是UDP。

以上是关于linux原始套接字-构造IP_UDP的主要内容,如果未能解决你的问题,请参考以下文章

即使设置 cap_net_raw 也无法在 linux 容器中打开原始套接字

Python原始套接字编程

Linux Socket 原始套接字编程

linux原始套接字-icmp请求与接收

Linux网络编程:原始套接字的魔力上

原始套接字