004.UDP--拼接UDP数据包,构造ip头和udp头通信(使用原始套接字)

一.大致流程:

    建立一个client端,一个server端,自己构建IP头和UDP头,写入数据(hello,world!)后通过原始套接字(SOCK_RAW)将包发出去。

server端收到数据后,打印UDP数据并发送确认消息(yes),client收到yes后将其打印。

二.其中:

client端IP:192.168.11.104 端口:8600

server端IP:192.168.11.105 端口:8686

三.注意事项:

1.运行原始套接字socket需要有root权限。

2.注意主机字节序和网络字节序的

四.涉及的数据结构

1.ip部分的结构图:

2.ip结构体定义: 

struct iphdr       /* 该结构体在<netinet/ip.h>中定义 */
  {
#if __BYTE_ORDER == __LITTLE_ENDIAN  /* 如果是小端字节序 */
    unsigned int ihl:4;     /*首部长度*/
    unsigned int version:4; /* 版本 */
#elif __BYTE_ORDER == __BIG_ENDIAN
    unsigned int version:4;
    unsigned int ihl:4;
#else
# error    "Please fix <bits/endian.h>"
#endif
    u_int8_t tos;           /* 区分服务 */
    u_int16_t tot_len;      /* 总长度 */
    u_int16_t id;           /* 标识 */
    u_int16_t frag_off;     /* 标志(3位)+片偏移(13位) */
    u_int8_t ttl;           /* 生存时间 */
    u_int8_t protocol;      /* 协议 */
    u_int16_t check;        /* 首部检验和 */
    u_int32_t saddr;        /* 源地址 */
    u_int32_t daddr;        /* 目的地址 */
    /*The options start here. */
  };

 3.udp数据包的结构图: 

4.udp结构体定义: 

struct udphdr  /* 该结构体在<netiniet/udp.h>中定义 */
{
  u_int16_t source;  /*源端口*/
  u_int16_t dest;    /*目的端口*/
  u_int16_t len;     /*长度*/
  u_int16_t check;   /*校验和*/
};

  

五.实现如下:

client端: 

  1 /*
  2  ============================================================================
  3  Name        : test_client.c
  4  Author      : huh
  5  Version     :
  6  Copyright   : ---notice---
  7  Description : Hello World in C, Ansi-style
  8  ============================================================================
  9  */
 10 
 11 #include <sys/types.h>
 12 #include <sys/socket.h>
 13 #include <stdio.h>
 14 #include <netinet/in.h>
 15 #include <arpa/inet.h>
 16 #include <unistd.h>
 17 #include <stdlib.h>
 18 #include <string.h>
 19 #include <netinet/ip_icmp.h>
 20 #include <netinet/udp.h>
 21 
 22 #define MAXLINE 1024*10
 23 
 24 struct udp_front  //udp
 25 {
 26     uint32_t srcip;
 27     uint32_t desip;
 28     u_int8_t zero;
 29     u_int8_t protocol;
 30     u_int16_t len;
 31 };
 32 
 33 u_int16_t in_chksum(u_int16_t *addr, int len);
 34 u_int16_t udp_check(char *sendbuf, int len, const struct udp_front front);
 35 int make_message(char sendbuf[], int send_buf_len, uint32_t src_ip, u_int16_t src_port, uint32_t des_ip, u_int16_t des_port);
 36 
 37 int main()
 38 {
 39     int raw_sockfd;
 40     int size = 1024*50;
 41     char send_message[MAXLINE];
 42     struct sockaddr_in server_address;
 43     //创建原始套接字
 44     raw_sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
 45     //创建套接字地址
 46     bzero(&server_address,sizeof(server_address));
 47     server_address.sin_family = AF_INET;
 48     server_address.sin_addr.s_addr = inet_addr("192.168.11.105");
 49     //设置套接字为随数据包含IP首部(设置这个选项后需要我们手动写入IP头)
 50     setsockopt(raw_sockfd, IPPROTO_IP, IP_HDRINCL, &size, sizeof(size));
 51 
 52     int len;
 53     bzero(&send_message, sizeof(send_message));
 54     //拼接完整的UDP数据包(IP头+UDP头+数据)
 55     int mesg_len = make_message(send_message, MAXLINE, inet_addr("192.168.11.104"), 8600, inet_addr("192.168.11.105"), 8686);
 56     //将IP数据包发送出去
 57     sendto(raw_sockfd, send_message, mesg_len, 0, (struct sockaddr *)&server_address, sizeof(server_address));
 58     close(raw_sockfd);
 59     //
 60     //下面我们开始接受服务器返回的包
 61     int client_sockfd;
 62     int server_len;
 63     char recv_message[MAXLINE];
 64     struct sockaddr_in server_addr;
 65     client_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
 66     server_addr.sin_family = AF_INET;
 67     server_addr.sin_addr.s_addr = inet_addr("192.168.11.104");
 68     server_addr.sin_port = htons(8600);
 69     server_len = sizeof(server_address);
 70     bind(client_sockfd, (struct sockaddr *)&server_addr, server_len);
 71 
 72     bzero(&recv_message, sizeof(recv_message));
 73        len = recvfrom(client_sockfd, recv_message, MAXLINE, 0, NULL, NULL);
 74        printf("收到的应答:%s\n",recv_message);
 75     return 0;
 76 }
 77 
 78 //拼接IP数据报
 79 int make_message(char sendbuf[], int send_buf_len, uint32_t src_ip, u_int16_t src_port, uint32_t des_ip, u_int16_t des_port)
 80 {
 81     char message[1005];
 82     bzero(message, sizeof(message));
 83     strcpy(message,"hello,world!");
 84     printf("message len:%d\n",strlen(message));
 85     struct iphdr *ip;
 86     ip = (struct iphdr *)sendbuf;
 87     ip->ihl = sizeof(struct iphdr) >> 2; //首部长度
 88     ip->version = 4;   //ip协议版本
 89     ip->tos = 0;   //服务类型字段
 90     ip->tot_len = 0;   //总长度
 91     ip->id = 1000;   //
 92     ip->frag_off = 0;
 93     ip->ttl = 128;
 94     ip->protocol = IPPROTO_UDP;
 95     ip->check = 0;  //内核会算相应的效验和
 96     ip->saddr = src_ip;
 97     ip->daddr = des_ip;
 98 
 99     struct udp_front front;
100     front.srcip = src_ip;
101     front.desip = des_ip;
102     front.len = htons(8+strlen(message));
103     front.protocol = 17;
104     front.zero = 0;
105 
106     struct udphdr *udp;
107     udp = (struct udphdr *)(sendbuf + sizeof(struct iphdr));
108     udp->source = htons(src_port);  //源端口
109     udp->dest = htons(des_port);    //目的端口
110     udp->check = 0;   //效验和,效验整个udp数据报
111     strcpy((sendbuf+20+8), message);
112     udp->len = htons(8+strlen(message)); //udp数据报总长度
113 
114     udp->check = udp_check((sendbuf+20), 8+strlen(message), front);
115 
116     ip->tot_len = htons(20 + 8 + strlen(message));   //总长度
117     printf("ip->tot_len:%d\n",ip->tot_len);
118     ip->check = in_chksum((unsigned short *)sendbuf, 20);
119 
120     return ntohs(ip->tot_len);
121 }
122 
123 //计算udp效验和
124 unsigned short udp_check(char *sendbuf, int len, const struct udp_front front)
125 {
126     char str[MAXLINE];
127     bzero(&str, MAXLINE);
128     bcopy(&front, str, sizeof(front));
129     bcopy(sendbuf, str+sizeof(front), len);
130     struct udp_front *ptr;
131     ptr = (struct udp_front *)str;
132     char *s;
133     s = (str+20);
134     return in_chksum((unsigned short *)str, sizeof(front)+len);
135 }
136 
137 //效验和算法
138 uint16_t in_chksum(uint16_t *addr, int len)
139 {
140     int nleft = len;
141     uint32_t sum = 0;
142     uint16_t *w = addr;
143     uint16_t answer = 0;
144     //把ICMP报头二进制数据以2字节为单位累加起来
145     while (nleft > 1)
146     {
147         sum += *w++;
148         nleft -= 2;
149     }
150     if (nleft == 1)
151     {
152         *(unsigned char *)(&answer) = *(unsigned char *)w;
153         sum += answer;
154     }
155     sum = (sum>>16) + (sum&0xffff);
156     sum += (sum>>16);
157     answer = ~sum;
158     return answer;
159 }

  

server端:

server端是一个简单的收包服务器,监听8686端口,当有udp数据包到来时,打印信息并返回给client一个信息。

 

 1 #include <sys/types.h>
 2 #include <sys/socket.h>
 3 #include <stdio.h>
 4 #include <netinet/in.h>
 5 #include <arpa/inet.h>
 6 #include <unistd.h>
 7 #include <stdlib.h>
 8 #include <string.h>
 9 
10 #define HOST_IP "192.168.11.105"
11 #define HOST_PORT 8686
12 
13 #define MAXLINE 1024*50
14 
15 int main()
16 {
17     int server_sockfd;
18     int server_len, client_len;
19     struct sockaddr_in server_address;
20     struct sockaddr_in client_address;
21 
22     server_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
23 
24     server_address.sin_family = AF_INET;
25     server_address.sin_addr.s_addr = inet_addr(HOST_IP);
26     server_address.sin_port = htons(HOST_PORT);
27 
28     server_len = sizeof(server_address);
29     bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
30 
31     for( ; ; )
32     {
33         int len;
34         char recv_mesg[MAXLINE];
35         char send_mesg[20];
36         client_len = sizeof(struct sockaddr_in);
37         printf("server2 waiting!\n");
38         len = recvfrom(server_sockfd, recv_mesg, MAXLINE, 0, (struct sockaddr *) &client_address, (socklen_t *) &client_len);
39         printf("收到包的长度为:%d\n",len);
40         printf("%s\n",recv_mesg);
41         strcpy(send_mesg,"yes");
42         sendto(server_sockfd, send_mesg, strlen(send_mesg), 0, (struct sockaddr *) &client_address, client_len);  //将包发出去
43     }
44     return 0;
45 }
server.c

 

posted @ 2015-11-19 20:55  ruo_yu  阅读(3444)  评论(1编辑  收藏  举报