zoukankan      html  css  js  c++  java
  • 自己动手写路由器之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获取网络接口信息

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

  • 相关阅读:
    python 学习笔记 数值型(1)
    python 学习笔记 标识符和变量(3)
    python 学习笔记 字符串(2)
    jsp+servlet+javaBean+Dao
    面试被问到岗时间,是越快越好吗?
    有赞多平台推广接入与测试
    HTTPS 加密、证书、签名与握手
    开发到底要不要自己做测试?
    我也曾找不到工作
    世界第三大浏览器正在消亡
  • 原文地址:https://www.cnblogs.com/rade/p/3518948.html
Copyright © 2011-2022 走看看