自己动手写路由器之ARP数据包的传输

在写路由器时,涉及到对ARP数据包的传输和接收!本来我是打算将广播,传输,接收一块儿写完之后再来说这块儿的,但是下午突然就没心思了,明天开始要休息五天,所以决定先把目前写了的部分讲述一下吧!

我们先来看看ARP数据包的结构:

一个ARP数据包为42个字节,前14个字节为以太网首部,后28个字节为ARP请求/应答部分。

在看代码之前,我们首先得弄懂,ARP数据包的传输原理。假设A想B发送一个数据包,A并不知道B在哪里,那么A首先会发一个广播的ARP请求,这个网段上的所有计算机(电脑)都会接收到来自A的ARP请求,由于每台计算机(电脑)都有自己唯一的MAC和IP,那么它会分析目的IP是不是自己的IP,如果不是,网卡会自动丢弃数据包。如果B接收到了,经过分析,目的IP是自己的,于是更新自己的ARP高速缓存,记录下A的IP和MAC。然后B就 会回应A一个ARP应答,就是把A的源IP,源MAC变成现在目的IP,和目的MAC,再带上自己的源IP,源MAC,发送给A。当A机接收到ARP应答后,更新自己的ARP高速缓存,即把arp应答中的B机的源IP,源MAC的映射关系记录在高速缓存中。那么现在A机中有B的MAC和IP,B机中也有A的MAC和IP。arp请求和应答过程就结束了。

先来看看写的两个函数接口吧,详解请看注释:

第一个是ARP数据包的传输:

 1 void sendArp(int sockfd,char *ip)
 2 {
 3     int socketfd = sockfd;
 4     unsigned char sendmsg[46] = 
 5     {
 6         /*前14位是以太网手部,后28位是ARP请求/应答
 7           MAC地址是是由48位组成的地址,为方便起见,通常用12位16进制表示,
 8           前6位16进制数字由IEEE负责统一分发,后6位16进制数字由各厂商自己负责管理。*/
 9         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,     //IEEE制定
10         0x00, 0x0c, 0x29, 0x42, 0xae, 0x53,     //这是我的MAC地址
11         0x08, 0x06,                             //这是ARP的协议号,IEEE制定
12 
13         0x00, 0x01,                             //硬件类型0x0001表示以太网
14         0x08, 0x00,                             //协议类型0x0800表示IP协议
15         0x06, 0x04,                             //硬件地址(MAC地址)长度为6,协议地址(IP地址)长度为4
16         0x00, 0x0c, 0x29, 0x44, 0xae, 0x53,     //发送端的MAC地址
17         192,  168,  44,   153,                  //发送端的IP地址
18         0x00, 0x00, 0x00, 0x00, 0x00, 0x00,     //目的MAC地址,先置0,等待填写
19         192,  168,  44,   156                   //目的IP地址
20      };
21 
22     int aim_ip;
23     inet_pton(AF_INET, ip, &aim_ip); 
24     memcpy(&sendmsg[38],&aim_ip,4);             //待填写目的IP地址
25     printf("the aim_ip is %d.%d.%d.%d 
",sendmsg[38],sendmsg[39],sendmsg[40],sendmsg[41]);
26     
27     struct sockaddr_ll sll;                     //原始套接字地址结构
28     struct ifreq ethreq;                        //网络接口地址
29 
30     /*更新自己的ARP高速缓存*/
31     if(send_msg[41]==156)
32     {
33         strncpy(ethreq.ifr_name, "eth1", IFNAMSIZ);       //指定网卡名称
34         memcpy(&send_msg[28],net_interface[2].ip,4);
35         memcpy(&send_msg[6],net_interface[2].mac,6);
36     }
37     if(send_msg[41]==153)
38     {
39          strncpy(ethreq.ifr_name, "eth0", IFNAMSIZ);      //指定网卡名称  
40          memcpy(&sendmsg[28],net_interface[1].ip,4);
41          memcpy(&sendmsg[6],net_interface[1].mac,6);
42     }
43 
44     //把接口索引存入ethreq.ifr_ifindex
45     if( -1 == ioctl(socketfd,SIOCGIFINDEX,(char *)&ethreq)) 
46     {
47         perror("SIOCGIFINDEX ioctl");
48         return;
49     }
50 
51     bzero(&sll,sizeof(struct sockaddr_ll));
52     sll.sll_ifindex = ethreq.ifr_ifindex;
53 
54     if( -1 == sendto(socketfd, sendmsg, 42, 0 , (struct sockaddr *)&sll, sizeof(sll)))
55     {
56         perror("sendto");
57         return;
58      }
59 }

第二个是广播ARP请求:

 1 void brdcast(int sockfd)
 2 {
 3     int socketfd = sockfd;
 4     unsigned char sendmsg[46] = 
 5      {
 6          /*前14位是以太网手部,后28位是ARP请求/应答
 7           MAC地址是是由48位组成的地址,为方便起见,通常用12位16进制表示,
 8           前6位16进制数字由IEEE负责统一分发,后6位16进制数字由各厂商自己负责管理。*/
 9         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,     //IEEE制定
10         0x00, 0x0c, 0x29, 0x42, 0xae, 0x53,     //这是我的MAC地址
11         0x08, 0x06,                             //这是ARP的协议号,IEEE制定
12 
13         0x00, 0x01,                             //硬件类型0x0001表示以太网
14         0x08, 0x00,                             //协议类型0x0800表示IP协议
15         0x06, 0x04,                             //硬件地址(MAC地址)长度为6,协议地址(IP地址)长度为4
16         0x00, 0x0c, 0x29, 0x44, 0xae, 0x53,     //发送端的MAC地址
17         192,  168,  44,   153,                  //发送端的IP地址
18         0x00, 0x00, 0x00, 0x00, 0x00, 0x00,     //目的MAC地址,先置0,等待填写
19         192,  168,  44,   156                   //目的IP地址
20     }; 
21 
22     int tmpi;
23     int tmpj;
24     struct ifreq ethreq;
25     struct sockaddr_ll sll;
26     tmpj = int getinterfaceNumber();                       //这里的getinterfaceNumber()会在下面进行解释
27 
28     for(tmpi = 1;tmpi <tmpj ;tmpi++)                  
29     {
30         memcpy(&sendmsg[38],net_interface[2].ip,4);        //这里的net_interface会在下面进行解释
31         memcpy(&send_msg[6],net_interface[tmpi].mac,6);
32         memcpy(&send_msg[28],net_interface[tmpi].ip,4);
33 
34         //把接口索引存入ethreq.ifr_ifindex
35         if( -1 == ioctl(socketfd,SIOCGIFINDEX,(char *)&ethreq)) 
36         {
37             perror("SIOCGIFINDEX ioctl");
38             return;
39          }
40 
41         bzero(&sll,sizeof(struct sockaddr_ll));
42         sll.sll_ifindex = ethreq.ifr_ifindex;
43 
44         if( -1 == sendto(socketfd, sendmsg, 42, 0 , (struct sockaddr *)&sll, sizeof(sll)))
45         {
46             perror("sendto");
47             return;
48          }
49      }
50     return
51 }

在代码里,有出现一些莫名其妙的函数或者结构体,比如net_interface和getinterfaceNumber(),net_interface里存储的是网络接口里读取到的一些信息,比如ip地址,子网掩码,mac地址等等,getinterfaceNumber()的返回值是接入的数量。这些是在另外的一个.c里实现的,我在这里是直接调用的!

具体的网口信息读取的实现方法在我的上一篇博客里有介绍:自己动手写路由器之ioctl获取网络接口信息

由于我的这个路由器刚写没多久,目前只是写了部分接口而已,我是写一点儿记录一点儿,有些代码未经测试我就放上来了,所以若代码里有问题,请指出来,我会进行修改的。当所有工作全部完成之时,我会将整个所编写的路由器代码公布上来的。

原文地址:https://www.cnblogs.com/rade/p/3518948.html