arp3-探测

对每种Neighboring协议,都要定义一个neigh_ops结构,ARP对应的定义为:

[ net/ipv4/arp.c ]

static const struct neigh_ops arp_generic_ops = {
	.family =		AF_INET,
	.solicit =		arp_solicit,
	.error_report =		arp_error_report,
	.output =		neigh_resolve_output,
	.connected_output =	neigh_connected_output,
};
其中的arp_solicit用来进行目标地址的探测:

[ net/ipv4/arp.c ]

// ARP探测
static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
{
	__be32 saddr = 0;
	u8 dst_ha[MAX_ADDR_LEN], *dst_hw = NULL;
	struct net_device *dev = neigh->dev;	// 网络设备
	__be32 target = *(__be32 *)neigh->primary_key;	// 目标地址
	int probes = atomic_read(&neigh->probes);	// 探测次数
	struct in_device *in_dev;

	rcu_read_lock();
	in_dev = __in_dev_get_rcu(dev);	// IPv4 specific data
	if (!in_dev) {
		rcu_read_unlock();
		return;
	}
	switch (IN_DEV_ARP_ANNOUNCE(in_dev)) {	// 本地发出的ARP请求包,当源地址使用本机的IP地址时,等级限制
	default:
	case 0:		/* By default announce any local IP */
		if (skb && inet_addr_type(dev_net(dev), ip_hdr(skb)->saddr) == RTN_LOCAL) // 目标为ip_hdr(skb)->saddr的路由为 Accept locally
			saddr = ip_hdr(skb)->saddr;
		break;
	case 1:		/* Restrict announcements of saddr in same subnet */
		if (!skb)
			break;
		saddr = ip_hdr(skb)->saddr;
		if (inet_addr_type(dev_net(dev), saddr) == RTN_LOCAL) {	// 目标为ip_hdr(skb)->saddr的路由为 Accept locally
			/* saddr should be known to target */
			if (inet_addr_onlink(in_dev, target, saddr))
				break;
		}
		saddr = 0;
		break;
	case 2:		/* Avoid secondary IPs, get a primary/preferred one */
		break;
	}
	rcu_read_unlock();

	if (!saddr)
		saddr = inet_select_addr(dev, target, RT_SCOPE_LINK); // 找到与目标地址target相同的本机地址,在局域网内

	probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES);	// 探测次数减去unicast solicitations数量
	if (probes < 0) {
		if (!(neigh->nud_state & NUD_VALID))
			pr_debug("trying to ucast probe in NUD_INVALID\n");
		neigh_ha_snapshot(dst_ha, neigh, dev);	// neigh中与IP地址对应的硬件地址赋值给dst_ha
		dst_hw = dst_ha;
	} else {
		probes -= NEIGH_VAR(neigh->parms, APP_PROBES);// 探测次数减去user-space application solicitations数量
		if (probes < 0) {
			neigh_app_ns(neigh);	// 发送通知给内核
			return;
		}
	}

	// 发送ARP请求
	arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr,
		 dst_hw, dev->dev_addr, NULL);
}
其中arp_send用来发送ARP请求,它也用来发送ARP响应:

[ net/ipv4/arp.c ]

/*
 *	Create and send an arp packet.
 */
void arp_send(int type, int ptype, __be32 dest_ip,
	      struct net_device *dev, __be32 src_ip,
	      const unsigned char *dest_hw, const unsigned char *src_hw,
	      const unsigned char *target_hw)
{
	struct sk_buff *skb;

	/*
	 *	No arp on this interface.
	 */

	if (dev->flags&IFF_NOARP)
		return;

	// 创建一个ARP包
	skb = arp_create(type, ptype, dest_ip, dev, src_ip,
			 dest_hw, src_hw, target_hw);
	if (skb == NULL)
		return;

	arp_xmit(skb);	// 发送ARP包
}
EXPORT_SYMBOL(arp_send);

 /*
 *    Create an arp packet. If (dest_hw == NULL), we create a broadcast
 *    message.
 */
struct sk_buff *arp_create(int type, int ptype, __be32 dest_ip,
               struct net_device *dev, __be32 src_ip,
               const unsigned char *dest_hw,
               const unsigned char *src_hw,
               const unsigned char *target_hw)
{
    struct sk_buff *skb;
    struct arphdr *arp;
    unsigned char *arp_ptr;
    int hlen = LL_RESERVED_SPACE(dev);    // 头部长度
    int tlen = dev->needed_tailroom;    // 尾部预留空间

    /*
     *    Allocate a buffer
     */

    skb = alloc_skb(arp_hdr_len(dev) + hlen + tlen, GFP_ATOMIC);
    if (skb == NULL)
        return NULL;


    skb_reserve(skb, hlen);
    skb_reset_network_header(skb);
    arp = (struct arphdr *) skb_put(skb, arp_hdr_len(dev));
    skb->dev = dev;
    skb->protocol = htons(ETH_P_ARP);    // 协议类型
    if (src_hw == NULL)
        src_hw = dev->dev_addr;    // 源地址
    if (dest_hw == NULL)
        dest_hw = dev->broadcast;    // 广播地址

    /*
     *    Fill the device header for the ARP frame
     */
    if (dev_hard_header(skb, dev, ptype, dest_hw, src_hw, skb->len) < 0)    // create the Ethernet header
        goto out;

    /*
     * Fill out the arp protocol part.
     *
     * The arp hardware type should match the device type, except for FDDI,
     * which (according to RFC 1390) should always equal 1 (Ethernet).
     */
    /*
     *    Exceptions everywhere. AX.25 uses the AX.25 PID value not the
     *    DIX code for the protocol. Make these device structure fields.
     */
    switch (dev->type) {
    default:
        arp->ar_hrd = htons(dev->type);
        arp->ar_pro = htons(ETH_P_IP);
        break;


#if IS_ENABLED(CONFIG_AX25)
    case ARPHRD_AX25:
        arp->ar_hrd = htons(ARPHRD_AX25);
        arp->ar_pro = htons(AX25_P_IP);
        break;

#if IS_ENABLED(CONFIG_NETROM)
    case ARPHRD_NETROM:
        arp->ar_hrd = htons(ARPHRD_NETROM);
        arp->ar_pro = htons(AX25_P_IP);
        break;
#endif
#endif

#if IS_ENABLED(CONFIG_FDDI)
    case ARPHRD_FDDI:
        arp->ar_hrd = htons(ARPHRD_ETHER);
        arp->ar_pro = htons(ETH_P_IP);
        break;
#endif
    }

    arp->ar_hln = dev->addr_len;    // length of hardware address
    arp->ar_pln = 4;    // length of protocol address
    arp->ar_op = htons(type);    // 类型:请求或响应

    arp_ptr = (unsigned char *)(arp + 1);


    memcpy(arp_ptr, src_hw, dev->addr_len);    // sender hardware address
    arp_ptr += dev->addr_len;
    memcpy(arp_ptr, &src_ip, 4);    // sender IP address
    arp_ptr += 4;

    switch (dev->type) {
#if IS_ENABLED(CONFIG_FIREWIRE_NET)
    case ARPHRD_IEEE1394:
        break;
#endif
    default:
        if (target_hw != NULL)
            memcpy(arp_ptr, target_hw, dev->addr_len);    // target hardware address
        else
            memset(arp_ptr, 0, dev->addr_len);
        arp_ptr += dev->addr_len;
    }
    memcpy(arp_ptr, &dest_ip, 4);    // IP address

    return skb;

out:
    kfree_skb(skb);
    return NULL;
}
EXPORT_SYMBOL(arp_create);

/*
 *    Send an arp packet.
 */
void arp_xmit(struct sk_buff *skb)
{
    /* Send it off, maybe filter it using firewalling first.  */
    NF_HOOK(NFPROTO_ARP, NF_ARP_OUT, skb, NULL, skb->dev, dev_queue_xmit);
}
EXPORT_SYMBOL(arp_xmit);