一.概述 同上一篇tcp一樣,udp也是封裝在ip報文裡面。創建UDP的原始套接字如下: 1 (sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP); 同樣,如果要構造udp的ip首部,要開啟IP_HDRINCL選項! udp首部格式: 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。