分类: LINUX
2009-04-27 14:56:37
Linux内核ARP的发送函数分析
void arp_send(int type, int ptype, u32 dest_ip,
struct net_device *dev, u32 src_ip,
unsigned char *dest_hw, unsigned char *src_hw,
unsigned char *target_hw)
当系统的网络驱动程序收到一个arp包的时候,调用这个函数处理。简单来说,arp_rev
发回本机器或者它代理的其他机器的网卡硬件地址(mac address),并且将发送者的网卡硬件地址放在自己的缓存(arp cache)中。
实现过程:
(1) 分配一个sk_buff,用来构造ARP包,除了必要的空间以外,还预留了15字节作备用。
调用skb_reserve空出前面的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->nh.raw = skb->data;
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;
if (dest_hw == NULL)
dest_hw = dev->broadcast;
+ 调用网卡设备本身的MAC头部构造函数,填好这个ARP包的ethernet目的硬件地址和源硬件地址。
ethernet的缺省构造函数为eth_header,请参见net_init.c中的ether_setup函数。
if (dev->hard_header &&
dev->hard_header(skb,dev,ptype,dest_hw,src_hw,skb->len) ar_hrd = htons(dev->type);
arp->ar_pro = __constant_htons(ETH_P_IP);
+ 填写硬件地址长度(一般为6 字节)和协议地址长度(4 字节);
填写arp包的类型,arp请求为1,arp应答为2。
arp->ar_hln = dev->addr_len;
arp->ar_pln = 4;
arp->ar_op = htons(type);
填写arp包的数据,包括源ethernet地址(sha)、源ip地址(sip)、目的ethernet地址(tha) 和目的ip地址(tip)。
arp_ptr=(unsigned char *)(arp+1);
memcpy(arp_ptr, src_hw, dev->addr_len);
arp_ptr+=dev->addr_len;
memcpy(arp_ptr, &src_ip,4);
arp_ptr+=4;
if (target_hw != NULL)
memcpy(arp_ptr, target_hw, dev->addr_len);
else
memset(arp_ptr, 0, dev->addr_len);
arp_ptr+=dev->addr_len;
memcpy(arp_ptr, &dest_ip, 4);
+ 最后,调用dev_queue_xmit将这个ARP包放在发送队列中准备发送。
dev_queue_xmit(skb);