ARP输入 之 arp_process
阅读原文时间:2023年07月11日阅读:1
概述

arp_process为ARP输入包的核心处理流程;

若输入为ARP请求且查路由成功,则进行如下判断:输入到本地,则进行应答;否则,允许转发,则转发,本文代码不包含转发流程;

若输入为ARP应答或者查路由失败,则更新邻居项;

源码分析

static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
struct in_device *in_dev = __in_dev_get_rcu(dev);
struct arphdr *arp;
unsigned char *arp_ptr;
struct rtable *rt;
unsigned char *sha;
unsigned char *tha = NULL;
__be32 sip, tip;
u16 dev_type = dev->type;
int addr_type;
struct neighbour *n;
struct dst_entry *reply_dst = NULL;
bool is_garp = false;

 /\* arp\_rcv below verifies the ARP header and verifies the device  
  \* is ARP'able.  
  \*/  
 /\* 获取ip配置块 \*/  
 if (!in\_dev)  
     goto out\_free\_skb;

 /\* 获取arp头 \*/  
 arp = arp\_hdr(skb);

 /\* 根据设备类型做检查 \*/  
 switch (dev\_type) {  
 default:  
     if (arp->ar\_pro != htons(ETH\_P\_IP) ||  
         htons(dev\_type) != arp->ar\_hrd)  
         goto out\_free\_skb;  
     break;  
 case ARPHRD\_ETHER:  
 case ARPHRD\_FDDI:  
 case ARPHRD\_IEEE802:  
     /\*  
      \* ETHERNET, and Fibre Channel (which are IEEE 802  
      \* devices, according to RFC 2625) devices will accept ARP  
      \* hardware types of either 1 (Ethernet) or 6 (IEEE 802.2).  
      \* This is the case also of FDDI, where the RFC 1390 says that  
      \* FDDI devices should accept ARP hardware of (1) Ethernet,  
      \* however, to be more robust, we'll accept both 1 (Ethernet)  
      \* or 6 (IEEE 802.2)  
      \*/  
     if ((arp->ar\_hrd != htons(ARPHRD\_ETHER) &&  
          arp->ar\_hrd != htons(ARPHRD\_IEEE802)) ||  
         arp->ar\_pro != htons(ETH\_P\_IP))  
         goto out\_free\_skb;  
     break;  
 case ARPHRD\_AX25:  
     if (arp->ar\_pro != htons(AX25\_P\_IP) ||  
         arp->ar\_hrd != htons(ARPHRD\_AX25))  
         goto out\_free\_skb;  
     break;  
 case ARPHRD\_NETROM:  
     if (arp->ar\_pro != htons(AX25\_P\_IP) ||  
         arp->ar\_hrd != htons(ARPHRD\_NETROM))  
         goto out\_free\_skb;  
     break;  
 }

 /\* Understand only these message types \*/

 /\* 操作码不是应答也不是请求 \*/  
 if (arp->ar\_op != htons(ARPOP\_REPLY) &&  
     arp->ar\_op != htons(ARPOP\_REQUEST))  
     goto out\_free\_skb;

/*
* Extract fields
*/
/* 获取arp指针 */
arp_ptr = (unsigned char *)(arp + );
/* 源mac */
sha = arp_ptr;
/* 源ip */
arp_ptr += dev->addr_len;
memcpy(&sip, arp_ptr, );
arp_ptr += ;

 /\* 设备类型 \*/  
 switch (dev\_type) {  

#if IS_ENABLED(CONFIG_FIREWIRE_NET)
case ARPHRD_IEEE1394:
break;
#endif
default:
/* 目的mac */
tha = arp_ptr;
arp_ptr += dev->addr_len;
}
/* 目的ip */
memcpy(&tip, arp_ptr, );
/*
* Check for bad requests for 127.x.x.x and requests for multicast
* addresses. If this is one such, delete it.
*/
/* 目的ip是组播||回环地址但是没有启用route_localnet */
if (ipv4_is_multicast(tip) ||
(!IN_DEV_ROUTE_LOCALNET(in_dev) && ipv4_is_loopback(tip)))
goto out_free_skb;

/*
* For some 802.11 wireless deployments (and possibly other networks),
* there will be an ARP proxy and gratuitous ARP frames are attacks
* and thus should not be accepted.
*/
/* 源ip和目的ip相同,设置了免费arp丢包 */
if (sip == tip && IN_DEV_ORCONF(in_dev, DROP_GRATUITOUS_ARP))
goto out_free_skb;

/*
* Special case: We must set Frame Relay source Q.922 address
*/
/* 设备类型为q.922,则设置源地址为广播地址 */
if (dev_type == ARPHRD_DLCI)
sha = dev->broadcast;

/*
* Process entry. The idea here is we want to send a reply if it is a
* request for us or if it is a request for someone else that we hold
* a proxy for. We want to add an entry to our cache if it is a reply
* to us or if it is a request for our address.
* (The assumption for this last is that if someone is requesting our
* address, they are probably intending to talk to us, so it saves time
* if we cache their address. Their address is also probably not in
* our cache, since ours is not in their cache.)
*
* Putting this another way, we only care about replies if they are to
* us, in which case we add them to the cache. For requests, we care
* about those for us and those for our proxies. We reply to both,
* and in the case of requests for us we add the requester to the arp
* cache.
*/

 if (arp->ar\_op == htons(ARPOP\_REQUEST) && skb\_metadata\_dst(skb))  
     reply\_dst = (struct dst\_entry \*)  
             iptunnel\_metadata\_reply(skb\_metadata\_dst(skb),  
                         GFP\_ATOMIC);

 /\* Special case: IPv4 duplicate address detection packet (RFC2131) \*/  
 /\* 源ip为0,用于检测地址冲突 \*/  
 if (sip == ) {  
     /\* ARP请求 && 地址是本地地址 && 不忽略该ARP请求,则发送ARP应答 \*/  
     if (arp->ar\_op == htons(ARPOP\_REQUEST) &&  
         inet\_addr\_type\_dev\_table(net, dev, tip) == RTN\_LOCAL &&  
         !arp\_ignore(in\_dev, sip, tip))  
         arp\_send\_dst(ARPOP\_REPLY, ETH\_P\_ARP, sip, dev, tip,  
                  sha, dev->dev\_addr, sha, reply\_dst);  
     goto out\_consume\_skb;  
 }

 /\* ARP请求 && 查路由成功 \*/  
 if (arp->ar\_op == htons(ARPOP\_REQUEST) &&  
     ip\_route\_input\_noref(skb, tip, sip, , dev) == ) {

     /\* 获取路由缓存 \*/  
     rt = skb\_rtable(skb);  
     addr\_type = rt->rt\_type;

     /\* 输入到本地 \*/  
     if (addr\_type == RTN\_LOCAL) {  
         int dont\_send;

         /\* 忽略检查 \*/  
         dont\_send = arp\_ignore(in\_dev, sip, tip);

         /\* 不忽略,配置了过滤,则判断过滤 \*/  
         if (!dont\_send && IN\_DEV\_ARPFILTER(in\_dev))  
             dont\_send = arp\_filter(sip, tip, dev);  
         /\* 允许输入 \*/  
         if (!dont\_send) {  
             /\* 查找邻居项,更新状态 \*/  
             n = neigh\_event\_ns(&arp\_tbl, sha, &sip, dev);

             /\* 邻居项存在,则回复ARP应答 \*/  
             if (n) {  
                 arp\_send\_dst(ARPOP\_REPLY, ETH\_P\_ARP,  
                          sip, dev, tip, sha,  
                          dev->dev\_addr, sha,  
                          reply\_dst);  
                 neigh\_release(n);  
             }  
         }  
         goto out\_consume\_skb;  
     } else if (IN\_DEV\_FORWARD(in\_dev)) {  
         /\* ARP代理 \*/  
     }  
 }

/\* ARP应答或者查路由失败 \*/

 /\* Update our ARP tables \*/  
 /\* 查找邻居项 \*/  
 n = \_\_neigh\_lookup(&arp\_tbl, &sip, dev, );

 addr\_type = -;  
 /\* 邻居项存在,或者启用了接收非请求应答 \*/  
 if (n || IN\_DEV\_ARP\_ACCEPT(in\_dev)) {  
     /\* 检查是否为免费ARP \*/  
     is\_garp = arp\_is\_garp(net, dev, &addr\_type, arp->ar\_op,  
                   sip, tip, sha, tha);  
 }

 /\* 启用了接收非请求应答 \*/  
 if (IN\_DEV\_ARP\_ACCEPT(in\_dev)) {  
     /\* Unsolicited ARP is not accepted by default.  
        It is possible, that this option should be enabled for some  
        devices (strip is candidate)  
      \*/  
     /\*  
       邻居项不存在,免费ARP || 邻居项不存在,不是免费ARP,则类型为应答,且为单播  
       创建邻居项  
     \*/  
     if (!n &&  
         (is\_garp ||  
          (arp->ar\_op == htons(ARPOP\_REPLY) &&  
           (addr\_type == RTN\_UNICAST ||  
            (addr\_type <  &&  
         /\* postpone calculation to as late as possible \*/  
         inet\_addr\_type\_dev\_table(net, dev, sip) ==  
             RTN\_UNICAST)))))  
         n = \_\_neigh\_lookup(&arp\_tbl, &sip, dev, );  
 }

 /\* 存在邻居表项 \*/  
 if (n) {  
     int state = NUD\_REACHABLE;  
     int override;

     /\* If several different ARP replies follows back-to-back,  
        use the FIRST one. It is possible, if several proxy  
        agents are active. Taking the first reply prevents  
        arp trashing and chooses the fastest router.  
      \*/  
     /\* 当前时间超过了下次更新时间 或者 为免费ARP \*/  
     override = time\_after(jiffies,  
                   n->updated +  
                   NEIGH\_VAR(n->parms, LOCKTIME)) ||  
            is\_garp;

     /\* Broadcast replies and request packets  
        do not assert neighbour reachability.  
      \*/  
     /\* 不是ARP应答,或者不是本机包,设置状态为STALE \*/  
     if (arp->ar\_op != htons(ARPOP\_REPLY) ||  
         skb->pkt\_type != PACKET\_HOST)  
         state = NUD\_STALE;  
     /\* 更新邻居项 \*/  
     neigh\_update(n, sha, state,  
              override ? NEIGH\_UPDATE\_F\_OVERRIDE : , );  
     neigh\_release(n);  
 }

out_consume_skb:
consume_skb(skb);

out_free_dst:
dst_release(reply_dst);
return NET_RX_SUCCESS;

out_free_skb:
kfree_skb(skb);
return NET_RX_DROP;
}