通過源碼理解Rarp協(xié)議(基于linux1.2.13)
本文轉(zhuǎn)載自微信公眾號(hào)「 編程雜技」,作者theanarkh 。轉(zhuǎn)載本文請(qǐng)聯(lián)系 編程雜技公眾號(hào)。
rarp是通過mac地址查詢ip的協(xié)議,主要用于有mac的主機(jī),但是沒有ip的情況。我們先看看rarp協(xié)議的協(xié)議定義(來自網(wǎng)上的圖[1])。
rarp協(xié)議的格式和arp協(xié)議是一樣的,他們都是通過一種地址查詢另外一種地址。操作系統(tǒng)內(nèi)維護(hù)了一個(gè)轉(zhuǎn)換表。定義如下。
- 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 */
- };
初始化的時(shí)候是空的,這個(gè)表格的數(shù)據(jù)來源于,用戶通過操作系統(tǒng)提供的接口設(shè)置。我們看如何操作這個(gè)表。
- 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;
- }
通過ioctl函數(shù),我們可以對(duì)表格進(jìn)行增刪改查。我們只關(guān)注新增的邏輯。因?yàn)槠渌氖穷愃频?。下面是arpreq 的定義
- 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) */
- };
- 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();
- // 判斷之前是不是已經(jīng)存在
- for (entry = rarp_tables; entry != NULL; entry = entry->next)
- if (entry->ip == ip)
- break;
- /*
- * If no entry was found, create a new one.
- */
- // 不存在則創(chuàng)建一個(gè)表項(xiàng)
- 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;
- }
我們看到這里會(huì)往表里插入一個(gè)表項(xiàng)(如果不存在的話),還有另外一個(gè)邏輯是rarp_init。
- static void rarp_init (void)
- {
- /* Register the packet type */
- rarp_packet_type.type=htons(ETH_P_RARP);
- dev_add_pack(&rarp_packet_type);
- }
這個(gè)函數(shù)是往底層注冊(cè)一個(gè)節(jié)點(diǎn),當(dāng)mac底層收到一個(gè)ETH_P_RARP類型的數(shù)據(jù)包的時(shí)候(在mac協(xié)議頭里定義),就會(huì)執(zhí)行rarp_packet_type中定義的函數(shù)。下面是該rarp_packet_type的定義
- static struct packet_type rarp_packet_type =
- {
- 0,
- 0, /* copy */
- rarp_rcv,
- NULL,
- NULL
- };
rarp_rcv函數(shù)就是收到一個(gè)rarp請(qǐng)求的時(shí)候(來自其他主機(jī)),執(zhí)行的函數(shù)。
- int rarp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
- {
- /*
- * We shouldn't use this type conversion. Check later.
- */
- // rarp協(xié)議報(bào)文
- struct arphdr *rarp = (struct arphdr *)skb->h.raw;
- // rarp協(xié)議數(shù)據(jù)部分
- 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.
- */
- // 不是請(qǐng)求報(bào)文則忽略
- if (rarp->ar_op != htons(ARPOP_RREQUEST))
- {
- kfree_skb(skb, FREE_READ);
- return 0;
- }
- /*
- * Extract variable width fields
- */
- // rarp協(xié)議首地址
- sha=rarp_ptr;
- // 發(fā)送端mac地址長度
- rarp_ptr+=dev->addr_len;
- // 拿到發(fā)送端ip,存到sip
- memcpy(&sip,rarp_ptr,4);
- // 跳過4字節(jié)
- 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)
- { // 拿到對(duì)應(yīng)的ip
- sip=entry->ip;
- sti();
- // 回復(fù),類似是響應(yīng)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;
- }
我們看到這個(gè)函數(shù)很長,不過邏輯比較簡單,就是解析收到的rarp請(qǐng)求中的數(shù)據(jù),然后根據(jù)其他主機(jī)請(qǐng)求的mac地址,從維護(hù)的表格中找到對(duì)應(yīng)的ip(如果有的話),然后調(diào)用arp_send函數(shù)發(fā)送回包。下面列一下該函數(shù)的代碼。
- 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
- */
- // 分配一個(gè)skb存儲(chǔ)數(shù)據(jù)包
- skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4)
- + dev->hard_header_len, GFP_ATOMIC);
- // 構(gòu)造arp協(xié)議數(shù)據(jù)包
- skb->len = sizeof(struct arphdr) + dev->hard_header_len + 2*(dev->addr_len+4);
- skb->arp = 1;
- skb->dev = dev;
- // 不存在緩存,發(fā)完可以銷毀
- skb->free = 1;
- // 構(gòu)造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);
- // 調(diào)用mac頭發(fā)送函數(shù)發(fā)送出去
- dev_queue_xmit(skb, dev, 0);
- }
這就是rarp的早期實(shí)現(xiàn)。
References
[1] 網(wǎng)上的圖: https://wenku.baidu.com/view/8fbb89a7f524ccbff12184a0.html#