/*
* 函数用于网络层收到一个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;
}