(代码基于linux2.4.0)
void arp_send(int type,/*arp协议编码,如ARPOP_REPLY(arp响应)、ARPOP_REQUEST(arp请求)等*/
int ptype, /*以太网协议类型,或者说是接口的硬件类型,如ARP(ETH_P_ARP)、x.25(ETH_P_X25)、ip(ETH_P_IP)等*/
u32 dest_ip, /*目的ip地址*/
struct net_device *dev, /*用于发包的网卡设备*/
u32 src_ip, /*源ip地址*/
unsigned char *dest_hw, /*目的硬件地址*/
unsigned char *src_hw,/*源硬件地址*/
unsigned char *target_hw) /*目的硬件地址,它用于arp响应时填充到arp包中,arp请求应该填0*/
{
struct sk_buff *skb;/*用于管理封装arp包的存储空间的sk_buff指针*/
struct arphdr *arp;/*指向arp包头*/
unsigned char *arp_ptr;/*指向arp数据*/
/*
* No arp on this interface.
*/
if (dev->flags&IFF_NOARP)
return;
/*
* 分配缓冲区,
* ARP数据包格式为:
* 硬件类型(2bytes)+协议类型(2bytes)+硬件地址长度(1bytes)+协议长度(1bytes)+操作码(2bytes)
* +源mac地址(6bytes)+源IP地址(4bytes)+目的mac地址(6bytes)+目的IP地址(4bytes)
* 长度=以太网包头长度+arp包头长度+arp数据长度(源ip长度4+源硬件地址长度+目的ip长度4+目的硬件地址长度)+15(用作缓冲区字对齐)
*/
skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4)
+ dev->hard_header_len + 15, GFP_ATOMIC);
if (skb == NULL)
return;
skb_reserve(skb, (dev->hard_header_len+15)&~15);/*在skb中申请以太网硬件头缓冲区,且边界字对齐*/
skb->nh.raw = skb->data;
/*在skb中申请arp数据包缓冲区(包括arp头和数据)*/
arp = (struct arphdr *) skb_put(skb,sizeof(struct arphdr) + 2*(dev->addr_len+4));
skb->dev = dev;/*指定数据包发送网卡*/
skb->protocol = __constant_htons (ETH_P_ARP);
if (src_hw == NULL)
src_hw = dev->dev_addr;/*如果源硬件mac地址未提供则赋值为发送网卡的硬件地址*/
if (dest_hw == NULL)
dest_hw = dev->broadcast;/*如果目标硬件mac地址未提供则赋值为广播地址,通常arp请求时它置为广播地址*/
/*
*填充设备MAC地址.MAC帧格式:
*目的地址(6字节)+ 源地址(6字节)+ 2字节字段(IEEE802.3:数据长度/DIX以太网:数据类型)+ 数据(46~~1500)+FCS
*/
if (dev->hard_header &&
dev->hard_header(skb,dev,ptype,dest_hw,src_hw,skb->len) < 0)
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 = __constant_htons(ETH_P_IP);
break;
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
case ARPHRD_AX25:
arp->ar_hrd = __constant_htons(ARPHRD_AX25);
arp->ar_pro = __constant_htons(AX25_P_IP);
break;
#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
case ARPHRD_NETROM:
arp->ar_hrd = __constant_htons(ARPHRD_NETROM);
arp->ar_pro = __constant_htons(AX25_P_IP);
break;
#endif
#endif
#ifdef CONFIG_FDDI
case ARPHRD_FDDI:
arp->ar_hrd = __constant_htons(ARPHRD_ETHER);
arp->ar_pro = __constant_htons(ETH_P_IP);
break;
#endif
#ifdef CONFIG_TR
case ARPHRD_IEEE802_TR:
arp->ar_hrd = __constant_htons(ARPHRD_IEEE802);
arp->ar_pro = __constant_htons(ETH_P_IP);
break;
#endif
}
arp->ar_hln = dev->addr_len;/*对以太网而言,是MAC地址长度,应该为6*/
arp->ar_pln = 4;/*对IP协议则是IP地址的长度*/
arp->ar_op = htons(type);/*arp请求或响应*/
arp_ptr=(unsigned char *)(arp+1);/*跳过ARP头,指向数据部分*/
memcpy(arp_ptr, src_hw, dev->addr_len);/*填充源硬件地址*/
arp_ptr+=dev->addr_len;/*指针后移*/
memcpy(arp_ptr, &src_ip,4);/*填充源IP地址*/
arp_ptr+=4;/*指针后移*/
if (target_hw != NULL) /*不为空则填充目标硬件地址,一般用于arp响应,填充解析的硬件地址*/
memcpy(arp_ptr, target_hw, dev->addr_len);
else /*否则填充全0地址,一般用于arp请求,因为目标硬件地址未知*/
memset(arp_ptr, 0, dev->addr_len);
arp_ptr+=dev->addr_len; /*指针后移*/
memcpy(arp_ptr, &dest_ip, 4);/*填充目的IP地址*/
skb->dev = dev; /*指定发送数据包的网卡设备*/
dev_queue_xmit(skb);/*函数内部调用以太网卡驱动程序的发送函数将数据包发送到网络上*/
return;
out:
kfree_skb(skb);
}