科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件arp_rcv函数实现分析

arp_rcv函数实现分析

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

学习linux内核网络实现时的笔记很早就写完了,一直想做些check,但最近一直很忙,先放上来再说,^_^。

作者:松哥 来源:CSDN 2008年3月26日

关键字: 分析 函数 arp_rcv 开源

  • 评论
  • 分享微博
  • 分享邮件

/*
 * 函数用于网络层收到一个arp请求时
 */

int arp_rcv(struct sk_buff *skb, /*接收到的包缓冲区指针*/
    struct net_device *dev, /*接收到ARP包的网卡设备结构*/
    struct packet_type *pt /*捕获的协议包类型,ARP应该为arp_packet_type(net/ipv4/arp.c的1147行),不过在arp_rcv函数中未使用*/
    )
{
 struct arphdr *arp = skb->nh.arph; /*获取以太网帧中的数据部分,即以太网包体,此处是ARP数据包,指向ARP数据包头*/
 unsigned char *arp_ptr= (unsigned char *)(arp+1); /*arp_ptr跳过ARP包头,指向ARP包体*/
 struct rtable *rt; /*路由结构指针*/
 unsigned char *sha, *tha; /*源、目的硬件地址指针;s-source t-target*/
 u32 sip, tip; /*源、目的IP地址*/
 u16 dev_type = dev->type; /*网络设备硬件类型*/
 int addr_type; /**/
 struct in_device *in_dev = in_dev_get(dev); /*IP层设备结构指针*/
 struct neighbour *n; /*邻居结构指针*/

/*
 * 包的硬件长度应该和网络设备的硬件长度匹配,这点需要check
 * 类似的,硬件类型也需要匹配,设备应该是支持ARP的.同时如果pln!=4,
 * 则ARP查找并非来自IP协议。这些情况都将包丢弃.
 */ 
 if (in_dev == NULL ||  /**/
     arp->ar_hln != dev->addr_len    ||
     dev->flags & IFF_NOARP || /*表示设备不支持ARP协议。通常的网络接口能传输ARP包,如果想让接口不执行ARP,可置上该标志位。如点对点接口不需要运行ARP*/
     skb->pkt_type == PACKET_OTHERHOST || /*需转发的包不应该ARP处理*/
     skb->pkt_type == PACKET_LOOPBACK || /*环回接口不应该ARP处理*/
     arp->ar_pln != 4)
  goto out;

 if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)  /*检测数据包是否共享并返回新的包*/
  goto out_of_mem;

 switch (dev_type) {  /*根据设备硬件类型check协议地址类型*/
 default: 
  if (arp->ar_pro != __constant_htons(ETH_P_IP))
   goto out;
  if (htons(dev_type) != arp->ar_hrd)
   goto out;
  break;
#ifdef CONFIG_NET_ETHERNET
 case ARPHRD_ETHER:/*如果是以太网*/
  /*
   * 以太网接收的ARP类型是
   * 1 (ARPHRD_ETHER,以太网) 或 6 (ARPHRD_IEEE802,IEEE 802.2).
   */
  if (arp->ar_hrd != __constant_htons(ARPHRD_ETHER) &&
      arp->ar_hrd != __constant_htons(ARPHRD_IEEE802))
   goto out;
  if (arp->ar_pro != __constant_htons(ETH_P_IP)) /*如果发送方的高层协议类型不是IP协议,则丢弃包*/
   goto out;
  break;
#endif
#ifdef CONFIG_TR
 case ARPHRD_IEEE802_TR:
  /*
   * 令牌环设备接受的ARP硬件类型要麽是
   * 1 (Ethernet) 要麽是 6 (IEEE 802.2).
   */
  if (arp->ar_hrd != __constant_htons(ARPHRD_ETHER) &&
      arp->ar_hrd != __constant_htons(ARPHRD_IEEE802))
   goto out;
  if (arp->ar_pro != __constant_htons(ETH_P_IP))
   goto out;
  break;
#endif
#ifdef CONFIG_FDDI
 case ARPHRD_FDDI:
  /*
   * 根据RFC1390, FDDI设备接受的ARP硬件类型应该是
   * 1(以太网).但是绝大多数情况下,我们接受的硬件类型要麽是
   * 1 (Ethernet) 要麽是 6 (IEEE 802.2).
   */
  if (arp->ar_hrd != __constant_htons(ARPHRD_ETHER) &&
      arp->ar_hrd != __constant_htons(ARPHRD_IEEE802))
   goto out;
  if (arp->ar_pro != __constant_htons(ETH_P_IP))
   goto out;
  break;
#endif
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 case ARPHRD_AX25:
  if (arp->ar_pro != __constant_htons(AX25_P_IP))
   goto out;
  if (arp->ar_hrd != __constant_htons(ARPHRD_AX25))
   goto out;
  break;
#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 case ARPHRD_NETROM:
  if (arp->ar_pro != __constant_htons(AX25_P_IP))
   goto out;
  if (arp->ar_hrd != __constant_htons(ARPHRD_NETROM))
   goto out;
  break;
#endif
#endif
 }

 /* 目前ARP接收函数只支持ARP请求(ARPOP_REPLY)和ARP响应(ARPOP_REQUEST),否则丢弃 */

 if (arp->ar_op != __constant_htons(ARPOP_REPLY) &&   /**/
     arp->ar_op != __constant_htons(ARPOP_REQUEST))
  goto out;

/*
 * 撷取ARP包体的各个字段
 * ARP包头:硬件类型(2bytes)+上层协议类型(2bytes)+硬件地址长度(1bytes)+协议地址长度(1bytes)+操作类型(2bytes,请求or响应)
 * ARP包体:源mac地址(6bytes)+源IP地址(4bytes)+目的mac地址(6bytes,ARP请求则此处全0)+目的IP地址(4bytes)
 */
 sha=arp_ptr; /*源mac地址指针*/
 arp_ptr += dev->addr_len; /*移动源mac地址长度,使指针到源IP地址处*/
 memcpy(&sip, arp_ptr, 4); /*copy源IP地址到sip*/
 arp_ptr += 4; /*移动IP地址长度*/
 tha=arp_ptr; /*目的mac地址指针*/
 arp_ptr += dev->addr_len; /*移动目的mac地址长度,使指针到目的IP地址处*/
 memcpy(&tip, arp_ptr, 4); /*copy目的IP地址到sip*/
/*
 * check错误的arp请求,如请求解析环回地址127.x.x.x 或者多播地址.如果有这种情况则丢弃包
 */
 if (LOOPBACK(tip) || MULTICAST(tip))
  goto out;

/*
 *  基本校验通过后,函数具体的处理入口。这里的处理思路是:如果是请求本机的包则将回送响应;
 *  如果是请求其它机器的信息,则函数将作为代理转发请求。
 *  如果是对本机所发请求的响应或请求本机的MAC地址,则需要在系统的缓存中加入一个条目. 
 *  (假定情况是有人请求本机mac地址,则对方随后可能会与本机交互,因此如果本机缓存了对方地址,
 *  则会很省时.由于本机不在对方的缓存中,则对方的地址也可能不在本机的缓存中.)
 */

 /* 特殊情况处理:IPv4地址冲突检测(RFC2131:DHCP协议,它基于ARP协议,其发送的请求包的源地址为0) */
 if (sip == 0) { /**/
  if (arp->ar_op == __constant_htons(ARPOP_REQUEST) &&
      inet_addr_type(tip) == RTN_LOCAL)/*如果DHCP询问的,且目标地址是本机地址,则ARP响应;否则将包丢弃*/
   arp_send(ARPOP_REPLY,ETH_P_ARP,tip,dev,tip,sha,dev->dev_addr,dev->dev_addr);
  goto out;
 }

 if (arp->ar_op == __constant_htons(ARPOP_REQUEST) && /*处理正常的ARP请求*/
     ip_route_input(skb, tip, sip, 0, dev) == 0) {/*且在路由表中找到目标IP得路由信息,并保存在skb->dst中*/

  rt = (struct rtable*)skb->dst; /*获取目标IP的路由信息*/
  addr_type = rt->rt_type; /*rt_type为路由类型。路由类型有:RTN_UNICAST(单播),*/
     /*RTN_LOCAL(本地终结),RTN_BROADCAST(广播接收、广播发送)等*/
  if (addr_type == RTN_LOCAL) { /*ARP请求解析的tip是本机IP地址*/
   n = neigh_event_ns(&arp_tbl, sha, &sip, dev); /*更新arp_tbl表*/
   if (n) { /*发送arp响应消息告知本机mac地址*/
    arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
    neigh_release(n);
   }
   goto out;
  } else if (IN_DEV_FORWARD(in_dev)) { /*否则若是转发的请求,则做proxyer*/
   if ((rt->rt_flags&RTCF_DNAT) || /*rt_flags为路由表的标志位表,可以有RTF_UP(路由可用)、*/
       /*RTF_GATEWAY(目的是一个网关)、RTF_HOST(目的是一个主机)、*/
       /*RTF_REINSTATE、RTF_DYNAMIC(动态创建的路由)、RTF_MODIFIED、*/
       /*RTF_MTU(需要指定MTU值)、RTF_WINDOW、RTF_REJECT等值.*/
       /*RTCF_DNAT、RTCF_SNAT、RTCF_NAT等只用于FastNAT模式*/
       (addr_type == RTN_UNICAST  && rt->u.dst.dev != dev && /**/
        (IN_DEV_PROXY_ARP(in_dev) || pneigh_lookup(&arp_tbl, &tip, dev, 0)))) {
    n = neigh_event_ns(&arp_tbl, sha, &sip, dev); /**/
    if (n) /**/
     neigh_release(n);

    if (skb->stamp.tv_sec == 0 || /**/
        skb->pkt_type == PACKET_HOST ||
        in_dev->arp_parms->proxy_delay == 0) {
     arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
    } else { /**/
     pneigh_enqueue(&arp_tbl, in_dev->arp_parms, skb); /**/
     in_dev_put(in_dev); /**/
     return 0;
    }
    goto out;
   }
  }
 }

 /* 处理完ARP请求后,不管是请求包还是应答包,都要更新arp_tbl表 */

 n = __neigh_lookup(&arp_tbl, &sip, dev, 0); /*在arp_tbl表中查找源ip(sip)对应的邻居结构*/

#ifdef CONFIG_IP_ACCEPT_UNSOLICITED_ARP /**/
 /* Unsolicited ARP is not accepted by default.
    It is possible, that this option should be enabled for some
    devices (strip is candidate)
  */
 if (n == NULL &&
     arp->ar_op == __constant_htons(ARPOP_REPLY) &&
     inet_addr_type(sip) == RTN_UNICAST)
  n = __neigh_lookup(&arp_tbl, &sip, dev, -1);
#endif

 if (n) { /*如果找到*/
  int state = NUD_REACHABLE;
  int override = 0;

  /* 如果有几个不同的ARP响应陆续到达,则使用最先到达的包.
     这种情况主要发生在有不同的ARP代理激活时,系统选取第一
     个响应包以防止无用的地址解析,并选择最快的路由.
   */
  if (jiffies - n->updated >= n->parms->locktime) /*struct neighbour的updated域是以jiffies表示的其被neigh_update最近更新时间*/
   override = 1;  /*struct neigh_parms的locktime域是以jiffies表示的邻居结构必须被更新的最小时间*/
     /*上述语句中override=1表示邻居表的更新时间间隔已经超过阀值,必须强制更新了,此参数作为入参传入neigh_update*/
  /*
     广播响应与请求包不能够用于判断邻居的可达
   */
  if (arp->ar_op != __constant_htons(ARPOP_REPLY) || /*如果是arp应答包或数据包是传递给本机的,则修改state状态*/
      skb->pkt_type != PACKET_HOST)
   state = NUD_STALE;
  neigh_update(n, sha, state, override, 1); /*更新邻居表*/
  neigh_release(n);/*释放*/
 }

out:
 kfree_skb(skb);/*释放数据包*/
 if (in_dev)
  in_dev_put(in_dev); /*释放in_dev*/
out_of_mem:
 return 0;
}

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章