这就是rarp的早期实现。

void arp_send(int type, int ptype, unsigned long dest_ip,            struct device *dev, unsigned long src_ip,            unsigned char *dest_hw, unsigned char *src_hw) {     struct sk_buff *skb;     struct arphdr *arp;     unsigned char *arp_ptr;      /*      *    No arp on this interface.      */      if(dev->flags&IFF_NOARP)         return;      /*      *    Allocate a buffer      */     // 分配一个skb存储数据包     skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4)                 + dev->hard_header_len, GFP_ATOMIC);     // 构造arp协议数据包     skb->len = sizeof(struct arphdr) + dev->hard_header_len + 2*(dev->addr_len+4);     skb->arp = 1;     skb->dev = dev;     // 不存在缓存,发完可以销毁     skb->free = 1;     // 构造mac头     dev->hard_header(skb->data,dev,ptype,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len,skb);      /* Fill out the arp protocol part. */     arp = (struct arphdr *) (skb->data + dev->hard_header_len);     arp->ar_hrd = htons(dev->type);     arp->ar_pro = htons(ETH_P_IP);     arp->ar_hln = dev->addr_len;     arp->ar_pln = 4;     arp->ar_op = htons(type);     arp_ptr=(unsigned char *)(arp+1);     memcpy(arp_ptr, src_hw, dev->addr_len);     arp_ptr+=dev->addr_len;     memcpy(arp_ptr, &src_ip,4);     arp_ptr+=4;     if (dest_hw != NULL)         memcpy(arp_ptr, dest_hw, dev->addr_len);     else         memset(arp_ptr, 0, dev->addr_len);     arp_ptr+=dev->addr_len;     memcpy(arp_ptr, &dest_ip, 4);     // 调用mac头发送函数发送出去     dev_queue_xmit(skb, dev, 0); } 

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

int rarp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) { /*  *    We shouldn't use this type conversion. Check later.  */     // rarp协议报文     struct arphdr *rarp = (struct arphdr *)skb->h.raw;     // rarp协议数据部分     unsigned char *rarp_ptr = (unsigned char *)(rarp+1);     struct rarp_table *entry;     long sip,tip;     unsigned char *sha,*tha;            /* s for "source", t for "target" */      // 硬件地址长度或类型不一致则忽略     if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)          || dev->flags&IFF_NOARP)     {         kfree_skb(skb, FREE_READ);         return 0;     }      /*      *    If it's not a RARP request, delete it.      */     // 不是请求报文则忽略     if (rarp->ar_op != htons(ARPOP_RREQUEST))     {         kfree_skb(skb, FREE_READ);         return 0;     }     /*      *    Extract variable width fields      */     // rarp协议首地址     sha=rarp_ptr;     // 发送端mac地址长度     rarp_ptr+=dev->addr_len;     // 拿到发送端ip,存到sip     memcpy(&sip,rarp_ptr,4);     // 跳过4字节     rarp_ptr+=4;     // 目的mac地址     tha=rarp_ptr;     // 跳过mac地址长度     rarp_ptr+=dev->addr_len;     // 目的ip地址     memcpy(&tip,rarp_ptr,4);      /*      *    Process entry. Use tha for table lookup according to RFC903.      */      cli();     for (entry = rarp_tables; entry != NULL; entry = entry->next)         // 判断mac地址是否相等         if (!memcmp(entry->ha, tha, rarp->ar_hln))             break;     // 非空则说明找到     if (entry != NULL)     {    // 拿到对应的ip         sip=entry->ip;         sti();         // 回复,类似是响应ARPOP_RREPLY         arp_send(ARPOP_RREPLY, ETH_P_RARP, sip, dev, dev->pa_addr, sha,              dev->dev_addr);     }     else         sti();      kfree_skb(skb, FREE_READ);     return 0; } 

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

static struct packet_type rarp_packet_type = {     0,      0,                /* copy */     rarp_rcv,     NULL,     NULL }; 

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

static void rarp_init (void) {     /* Register the packet type */     rarp_packet_type.type=htons(ETH_P_RARP);     dev_add_pack(&rarp_packet_type); } 

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

static int rarp_req_set(struct arpreq *req) {     struct arpreq r;     struct rarp_table *entry;     struct sockaddr_in *si;     int htype, hlen;     unsigned long ip;     struct rtable *rt;      memcpy_fromfs(&r, req, sizeof(r));      /*      *    We only understand about IP addresses...       */      if (r.arp_pa.sa_family != AF_INET)         return -EPFNOSUPPORT;      switch (r.arp_ha.sa_family)      {         case ARPHRD_ETHER:             htype = ARPHRD_ETHER;             hlen = ETH_ALEN;             break;         default:             return -EPFNOSUPPORT;     }      si = (struct sockaddr_in *) &r.arp_pa;     ip = si->sin_addr.s_addr;     if (ip == 0)     {         printk("RARP: SETRARP: requested PA is 0.0.0.0 !\n");         return -EINVAL;     }     //      rt = ip_rt_route(ip, NULL, NULL);     if (rt == NULL)         return -ENETUNREACH;  /*  *    Is there an existing entry for this address?  Find out...  */      cli();     // 判断之前是不是已经存在     for (entry = rarp_tables; entry != NULL; entry = entry->next)         if (entry->ip == ip)             break;  /*  *    If no entry was found, create a new one.  */     // 不存在则创建一个表项     if (entry == NULL)     {         entry = (struct rarp_table *) kmalloc(sizeof(struct rarp_table),                     GFP_ATOMIC);         // 还没初始化则初始化         if(initflag)         {             rarp_init();             initflag=0;         }          entry->next = rarp_tables;         rarp_tables = entry;     }      entry->ip = ip;     entry->hlen = hlen;     entry->htype = htype;     memcpy(&entry->ha, &r.arp_ha.sa_data, hlen);     entry->dev = rt->rt_dev;      sti();      return 0; } 
struct arpreq {   struct sockaddr    arp_pa;        /* protocol address        */   struct sockaddr    arp_ha;        /* hardware address        */   int            arp_flags;    /* flags            */   struct sockaddr       arp_netmask;    /* netmask (only for proxy arps) */ }; 

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

int rarp_ioctl(unsigned int cmd, void *arg) {     struct arpreq r;     struct sockaddr_in *si;     int err;      switch(cmd)     {         case SIOCDRARP:             if (!suser())                 return -EPERM;             err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));             if(err)                 return err;             memcpy_fromfs(&r, arg, sizeof(r));             if (r.arp_pa.sa_family != AF_INET)                 return -EPFNOSUPPORT;             si = (struct sockaddr_in *) &r.arp_pa;             rarp_destroy(si->sin_addr.s_addr);             return 0;          case SIOCGRARP:             err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq));             if(err)                 return err;             return rarp_req_get((struct arpreq *)arg);         case SIOCSRARP:             if (!suser())                 return -EPERM;             err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));             if(err)                 return err;             return rarp_req_set((struct arpreq *)arg);         default:             return -EINVAL;     }      /*NOTREACHED*/     return 0; } 

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

struct rarp_table {     struct rarp_table  *next;             /* Linked entry list           */     unsigned long      ip;                /* ip address of entry         */     unsigned char      ha[MAX_ADDR_LEN];  /* Hardware address            */     unsigned char      hlen;              /* Length of hardware address  */     unsigned char      htype;             /* Type of hardware in use     */     struct device      *dev;              /* Device the entry is tied to */ }; 

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