通过源码理解Rarp协议(基于linux1.2.13)

网络 通信技术
arp是通过mac地址查询ip的协议,主要用于有mac的主机,但是没有ip的情况。

[[342609]]

本文转载自微信公众号「 编程杂技」,作者theanarkh 。转载本文请联系 编程杂技公众号。

rarp是通过mac地址查询ip的协议,主要用于有mac的主机,但是没有ip的情况。我们先看看rarp协议的协议定义(来自网上的图[1])。

rarp协议的格式和arp协议是一样的,他们都是通过一种地址查询另外一种地址。操作系统内维护了一个转换表。定义如下。

  1. struct rarp_table 
  2.     struct rarp_table  *next;             /* Linked entry list           */ 
  3.     unsigned long      ip;                /* ip address of entry         */ 
  4.     unsigned char      ha[MAX_ADDR_LEN];  /* Hardware address            */ 
  5.     unsigned char      hlen;              /* Length of hardware address  */ 
  6.     unsigned char      htype;             /* Type of hardware in use     */ 
  7.     struct device      *dev;              /* Device the entry is tied to */ 
  8. }; 

初始化的时候是空的,这个表格的数据来源于,用户通过操作系统提供的接口设置。我们看如何操作这个表。

  1. int rarp_ioctl(unsigned int cmd, void *arg) 
  2.     struct arpreq r; 
  3.     struct sockaddr_in *si; 
  4.     int err; 
  5.  
  6.     switch(cmd) 
  7.     { 
  8.         case SIOCDRARP: 
  9.             if (!suser()) 
  10.                 return -EPERM; 
  11.             err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq)); 
  12.             if(err) 
  13.                 return err; 
  14.             memcpy_fromfs(&r, arg, sizeof(r)); 
  15.             if (r.arp_pa.sa_family != AF_INET) 
  16.                 return -EPFNOSUPPORT; 
  17.             si = (struct sockaddr_in *) &r.arp_pa; 
  18.             rarp_destroy(si->sin_addr.s_addr); 
  19.             return 0; 
  20.  
  21.         case SIOCGRARP: 
  22.             err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq)); 
  23.             if(err) 
  24.                 return err; 
  25.             return rarp_req_get((struct arpreq *)arg); 
  26.         case SIOCSRARP: 
  27.             if (!suser()) 
  28.                 return -EPERM; 
  29.             err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq)); 
  30.             if(err) 
  31.                 return err; 
  32.             return rarp_req_set((struct arpreq *)arg); 
  33.         default
  34.             return -EINVAL; 
  35.     } 
  36.  
  37.     /*NOTREACHED*/ 
  38.     return 0; 

通过ioctl函数,我们可以对表格进行增删改查。我们只关注新增的逻辑。因为其他的是类似的。下面是arpreq 的定义

  1. struct arpreq { 
  2.   struct sockaddr    arp_pa;        /* protocol address        */ 
  3.   struct sockaddr    arp_ha;        /* hardware address        */ 
  4.   int            arp_flags;    /* flags            */ 
  5.   struct sockaddr       arp_netmask;    /* netmask (only for proxy arps) */ 
  6. }; 
  1. static int rarp_req_set(struct arpreq *req) 
  2.     struct arpreq r; 
  3.     struct rarp_table *entry; 
  4.     struct sockaddr_in *si; 
  5.     int htype, hlen; 
  6.     unsigned long ip; 
  7.     struct rtable *rt; 
  8.  
  9.     memcpy_fromfs(&r, req, sizeof(r)); 
  10.  
  11.     /* 
  12.      *    We only understand about IP addresses...  
  13.      */ 
  14.  
  15.     if (r.arp_pa.sa_family != AF_INET) 
  16.         return -EPFNOSUPPORT; 
  17.  
  18.     switch (r.arp_ha.sa_family)  
  19.     { 
  20.         case ARPHRD_ETHER: 
  21.             htype = ARPHRD_ETHER; 
  22.             hlen = ETH_ALEN; 
  23.             break; 
  24.         default
  25.             return -EPFNOSUPPORT; 
  26.     } 
  27.  
  28.     si = (struct sockaddr_in *) &r.arp_pa; 
  29.     ip = si->sin_addr.s_addr; 
  30.     if (ip == 0) 
  31.     { 
  32.         printk("RARP: SETRARP: requested PA is 0.0.0.0 !\n"); 
  33.         return -EINVAL; 
  34.     } 
  35.     //  
  36.     rt = ip_rt_route(ip, NULLNULL); 
  37.     if (rt == NULL
  38.         return -ENETUNREACH; 
  39.  
  40. /* 
  41.  *    Is there an existing entry for this address?  Find out... 
  42.  */ 
  43.  
  44.     cli(); 
  45.     // 判断之前是不是已经存在 
  46.     for (entry = rarp_tables; entry != NULL; entry = entry->next
  47.         if (entry->ip == ip) 
  48.             break; 
  49.  
  50. /* 
  51.  *    If no entry was found, create a new one. 
  52.  */ 
  53.     // 不存在则创建一个表项 
  54.     if (entry == NULL
  55.     { 
  56.         entry = (struct rarp_table *) kmalloc(sizeof(struct rarp_table), 
  57.                     GFP_ATOMIC); 
  58.         // 还没初始化则初始化 
  59.         if(initflag) 
  60.         { 
  61.             rarp_init(); 
  62.             initflag=0; 
  63.         } 
  64.  
  65.         entry->next = rarp_tables; 
  66.         rarp_tables = entry; 
  67.     } 
  68.  
  69.     entry->ip = ip; 
  70.     entry->hlen = hlen; 
  71.     entry->htype = htype; 
  72.     memcpy(&entry->ha, &r.arp_ha.sa_data, hlen); 
  73.     entry->dev = rt->rt_dev; 
  74.  
  75.     sti(); 
  76.  
  77.     return 0; 

我们看到这里会往表里插入一个表项(如果不存在的话),还有另外一个逻辑是rarp_init。

  1. static void rarp_init (void) 
  2.     /* Register the packet type */ 
  3.     rarp_packet_type.type=htons(ETH_P_RARP); 
  4.     dev_add_pack(&rarp_packet_type); 

这个函数是往底层注册一个节点,当mac底层收到一个ETH_P_RARP类型的数据包的时候(在mac协议头里定义),就会执行rarp_packet_type中定义的函数。下面是该rarp_packet_type的定义

  1. static struct packet_type rarp_packet_type = 
  2.     0,  
  3.     0,                /* copy */ 
  4.     rarp_rcv, 
  5.     NULL
  6.     NULL 
  7. }; 

rarp_rcv函数就是收到一个rarp请求的时候(来自其他主机),执行的函数。

  1. int rarp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) 
  2. /* 
  3.  *    We shouldn't use this type conversion. Check later. 
  4.  */ 
  5.     // rarp协议报文 
  6.     struct arphdr *rarp = (struct arphdr *)skb->h.raw; 
  7.     // rarp协议数据部分 
  8.     unsigned char *rarp_ptr = (unsigned char *)(rarp+1); 
  9.     struct rarp_table *entry; 
  10.     long sip,tip; 
  11.     unsigned char *sha,*tha;            /* s for "source", t for "target" */ 
  12.  
  13.     // 硬件地址长度或类型不一致则忽略 
  14.     if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)  
  15.         || dev->flags&IFF_NOARP) 
  16.     { 
  17.         kfree_skb(skb, FREE_READ); 
  18.         return 0; 
  19.     } 
  20.  
  21.     /* 
  22.      *    If it's not a RARP request, delete it. 
  23.      */ 
  24.     // 不是请求报文则忽略 
  25.     if (rarp->ar_op != htons(ARPOP_RREQUEST)) 
  26.     { 
  27.         kfree_skb(skb, FREE_READ); 
  28.         return 0; 
  29.     } 
  30.     /* 
  31.      *    Extract variable width fields 
  32.      */ 
  33.     // rarp协议首地址 
  34.     sha=rarp_ptr; 
  35.     // 发送端mac地址长度 
  36.     rarp_ptr+=dev->addr_len; 
  37.     // 拿到发送端ip,存到sip 
  38.     memcpy(&sip,rarp_ptr,4); 
  39.     // 跳过4字节 
  40.     rarp_ptr+=4; 
  41.     // 目的mac地址 
  42.     tha=rarp_ptr; 
  43.     // 跳过mac地址长度 
  44.     rarp_ptr+=dev->addr_len; 
  45.     // 目的ip地址 
  46.     memcpy(&tip,rarp_ptr,4); 
  47.  
  48.     /* 
  49.      *    Process entry. Use tha for table lookup according to RFC903. 
  50.      */ 
  51.  
  52.     cli(); 
  53.     for (entry = rarp_tables; entry != NULL; entry = entry->next
  54.         // 判断mac地址是否相等 
  55.         if (!memcmp(entry->ha, tha, rarp->ar_hln)) 
  56.             break; 
  57.     // 非空则说明找到 
  58.     if (entry != NULL
  59.     {    // 拿到对应的ip 
  60.         sip=entry->ip; 
  61.         sti(); 
  62.         // 回复,类似是响应ARPOP_RREPLY 
  63.         arp_send(ARPOP_RREPLY, ETH_P_RARP, sip, dev, dev->pa_addr, sha,  
  64.             dev->dev_addr); 
  65.     } 
  66.     else 
  67.         sti(); 
  68.  
  69.     kfree_skb(skb, FREE_READ); 
  70.     return 0; 

我们看到这个函数很长,不过逻辑比较简单,就是解析收到的rarp请求中的数据,然后根据其他主机请求的mac地址,从维护的表格中找到对应的ip(如果有的话),然后调用arp_send函数发送回包。下面列一下该函数的代码。

  1. void arp_send(int type, int ptype, unsigned long dest_ip,  
  2.           struct device *dev, unsigned long src_ip,  
  3.           unsigned char *dest_hw, unsigned char *src_hw) 
  4.     struct sk_buff *skb; 
  5.     struct arphdr *arp; 
  6.     unsigned char *arp_ptr; 
  7.  
  8.     /* 
  9.      *    No arp on this interface. 
  10.      */ 
  11.  
  12.     if(dev->flags&IFF_NOARP) 
  13.         return
  14.  
  15.     /* 
  16.      *    Allocate a buffer 
  17.      */ 
  18.     // 分配一个skb存储数据包 
  19.     skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4) 
  20.                 + dev->hard_header_len, GFP_ATOMIC); 
  21.     // 构造arp协议数据包 
  22.     skb->len = sizeof(struct arphdr) + dev->hard_header_len + 2*(dev->addr_len+4); 
  23.     skb->arp = 1; 
  24.     skb->dev = dev; 
  25.     // 不存在缓存,发完可以销毁 
  26.     skb->free = 1; 
  27.     // 构造mac头 
  28.     dev->hard_header(skb->data,dev,ptype,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len,skb); 
  29.  
  30.     /* Fill out the arp protocol part. */ 
  31.     arp = (struct arphdr *) (skb->data + dev->hard_header_len); 
  32.     arp->ar_hrd = htons(dev->type); 
  33.     arp->ar_pro = htons(ETH_P_IP); 
  34.     arp->ar_hln = dev->addr_len; 
  35.     arp->ar_pln = 4; 
  36.     arp->ar_op = htons(type); 
  37.     arp_ptr=(unsigned char *)(arp+1); 
  38.     memcpy(arp_ptr, src_hw, dev->addr_len); 
  39.     arp_ptr+=dev->addr_len; 
  40.     memcpy(arp_ptr, &src_ip,4); 
  41.     arp_ptr+=4; 
  42.     if (dest_hw != NULL
  43.         memcpy(arp_ptr, dest_hw, dev->addr_len); 
  44.     else 
  45.         memset(arp_ptr, 0, dev->addr_len); 
  46.     arp_ptr+=dev->addr_len; 
  47.     memcpy(arp_ptr, &dest_ip, 4); 
  48.     // 调用mac头发送函数发送出去 
  49.     dev_queue_xmit(skb, dev, 0); 

这就是rarp的早期实现。

References

[1] 网上的图: https://wenku.baidu.com/view/8fbb89a7f524ccbff12184a0.html#

 

 

责任编辑:武晓燕 来源: 编程杂技
相关推荐

2014-06-11 13:25:14

IPARPRARP

2013-07-01 15:06:04

2010-06-13 14:36:20

RARP协议

2011-07-19 11:35:09

linux网络协议

2023-07-04 09:39:45

Modbus鸿蒙

2016-12-19 15:09:02

Jedis源码

2021-05-28 05:30:55

HandleV8代码

2015-07-27 14:57:32

OpenFlow协议Ryu

2022-04-01 10:27:04

面向对象串口协议代码

2017-09-21 10:00:07

缓存web服务器

2023-07-03 19:18:27

2023-12-25 08:04:42

2023-03-04 13:43:31

云终端传输协议

2020-06-17 21:39:11

HTTP协议服务器

2022-11-02 21:45:54

SPIJava

2016-10-26 20:49:24

ReactJavascript前端

2011-04-18 19:36:10

HSRP协议

2020-11-04 00:00:29

Kerberos协议身份

2010-07-08 13:19:34

UDP协议

2020-10-09 14:13:04

Zookeeper Z
点赞
收藏

51CTO技术栈公众号