在以太网上传输IP数据报时,以太网设备并不能识别32位IP地址,而是以48位以太网地址传输以太网数据包的。因此,IP数据报在以太网上传输前需要封装为以太网帧,而以太网帧的目的地址正是通过IP数据报的目的IP地址查询得到的。因此IP地址和以太网地址之间存在着映射,通过查看ARP表就可以得到这两地址间的对应关系。地址解析协议(Address Resolution Protocol-ARP)就是用来确定这些对应关系的协议。
ARP协议的处理涉及以下文件:
include/linux/if_arp.h 定义Arp报文等结构、宏和函数原型
net/ipv4/arp.c Arp协议实现
定义arp报文头的结构
struct arphdr { __be16 ar_hrd; /* format of hardware address */ __be16 ar_pro; /* format of protocol address */ unsigned char ar_hln; /* length of hardware address */ unsigned char ar_pln; /* length of protocol address */ __be16 ar_op; /* ARP opcode (command) */ #if 0 /* * Ethernet looks like this : This bit is variable sized however... */ unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ unsigned char ar_sip[4]; /* sender IP address */ unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ unsigned char ar_tip[4]; /* target IP address */ #endif };由于不同网络介质的MAC地址长度是不同的,因此ARP报文的结构不能包括操作码后面的内容。这里只是列举了以太网上的ARP定义。
ARP协议并不仅仅被IPv4使用,在内核的网络模块代码中使用缩写SIP和TIP来代表发送方IP地址和目的IP地址
ARP报文像IP数据报一样,也是作为数据封装在以太网帧中发送的。ARP报文由arp_rcv()接收处理,ARP模块初始化时需要在协议栈中注册ARP报文的类型
/* * Called once on startup. */ static struct packet_type arp_packet_type __read_mostly = { .type = cpu_to_be16(ETH_P_ARP), .func = arp_rcv, };
ARP模块的初始化是由arp_init()完成的,该函数由IPv4协议栈初始化函数inet_init()调用,首先初始化arp协议的邻居表,然后在协议栈中注册ARP协议,最后建立proc对象,注册事件通知
void __init arp_init(void) { neigh_table_init(&arp_tbl); dev_add_pack(&arp_packet_type); arp_proc_init(); #ifdef CONFIG_SYSCTL neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4, NET_IPV4_NEIGH, "ipv4", NULL, NULL); #endif register_netdevice_notifier(&arp_netdev_notifier); }ARP的邻居项函数指针表
在ARP中,根据不同的介质,提供了多种邻居项函数指针表的实例,例如通用的arp_generic_ops,支持缓存硬件首部arp_hh_ops,不支持ARP的arp_direct_ops以及支持业余无线电设备等的arp_broken_ops。除了两个输出函数指针output和connected_output,这些邻居项函数指针表实例区别不大
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, .hh_output = dev_queue_xmit, .queue_xmit = dev_queue_xmit, }; static const struct neigh_ops arp_hh_ops = { .family = AF_INET, .solicit = arp_solicit, .error_report = arp_error_report, .output = neigh_resolve_output, .connected_output = neigh_resolve_output, .hh_output = dev_queue_xmit, .queue_xmit = dev_queue_xmit, }; static const struct neigh_ops arp_direct_ops = { .family = AF_INET, .output = dev_queue_xmit, .connected_output = dev_queue_xmit, .hh_output = dev_queue_xmit, .queue_xmit = dev_queue_xmit, }; const struct neigh_ops arp_broken_ops = { .family = AF_INET, .solicit = arp_solicit, .error_report = arp_error_report, .output = neigh_compat_output, .connected_output = neigh_compat_output, .hh_output = dev_queue_xmit, .queue_xmit = dev_queue_xmit, };ARP表
ARP的邻居表为arp_tbl,其中与协议特性相关的字段有:地址族为AF_INET;邻居项的大小为neighbour结构+4(IPv4地址的长度);哈希算法为arp_hash();arp初始化函数arp_constructor();延时处理代理ARP报文的例程为parp_redo(),以及调整ARP表特性的参数。
struct neigh_table arp_tbl = { .family = AF_INET, .entry_size = sizeof(struct neighbour) + 4, .key_len = 4, .hash = arp_hash, .constructor = arp_constructor, .proxy_redo = parp_redo, .id = "arp_cache", .parms = { .tbl = &arp_tbl, .base_reachable_time = 30 * HZ, .retrans_time = 1 * HZ, .gc_staletime = 60 * HZ, .reachable_time = 30 * HZ, .delay_probe_time = 5 * HZ, .queue_len = 3, .ucast_probes = 3, .mcast_probes = 3, .anycast_delay = 1 * HZ, .proxy_delay = (8 * HZ) / 10, .proxy_qlen = 64, .locktime = 1 * HZ, }, .gc_interval = 30 * HZ, .gc_thresh1 = 128, .gc_thresh2 = 512, .gc_thresh3 = 1024, };
arp_constructor()是ARP的邻居初始化函数,用来创建新的neighbour结构实例,在邻居表创建函数neigh_create()中被调用。
static int arp_constructor(struct neighbour *neigh) { __be32 addr = *(__be32*)neigh->primary_key; struct net_device *dev = neigh->dev; struct in_device *in_dev; struct neigh_parms *parms; rcu_read_lock(); in_dev = __in_dev_get_rcu(dev); if (in_dev == NULL) { rcu_read_unlock(); return -EINVAL; } neigh->type = inet_addr_type(dev_net(dev), addr); parms = in_dev->arp_parms; __neigh_parms_put(neigh->parms); neigh->parms = neigh_parms_clone(parms); rcu_read_unlock(); if (!dev->header_ops) { neigh->nud_state = NUD_NOARP; neigh->ops = &arp_direct_ops; neigh->output = neigh->ops->queue_xmit; } else { /* Good devices (checked by reading texts, but only Ethernet is tested) ARPHRD_ETHER: (ethernet, apfddi) ARPHRD_FDDI: (fddi) ARPHRD_IEEE802: (tr) ARPHRD_METRICOM: (strip) ARPHRD_ARCNET: etc. etc. etc. ARPHRD_IPDDP will also work, if author repairs it. I did not it, because this driver does not work even in old paradigm. */ #if 1 /* So... these "amateur" devices are hopeless. The only thing, that I can say now: It is very sad that we need to keep ugly obsolete code to make them happy. They should be moved to more reasonable state, now they use rebuild_header INSTEAD OF hard_start_xmit!!! Besides that, they are sort of out of date (a lot of redundant clones/copies, useless in 2.1), I wonder why people believe that they work. */ switch (dev->type) { default: break; case ARPHRD_ROSE: #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) case ARPHRD_AX25: #if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE) case ARPHRD_NETROM: #endif neigh->ops = &arp_broken_ops; neigh->output = neigh->ops->output; return 0; #endif ;} #endif if (neigh->type == RTN_MULTICAST) { neigh->nud_state = NUD_NOARP; arp_mc_map(addr, neigh->ha, dev, 1); } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) { neigh->nud_state = NUD_NOARP; memcpy(neigh->ha, dev->dev_addr, dev->addr_len); } else if (neigh->type == RTN_BROADCAST || dev->flags&IFF_POINTOPOINT) { neigh->nud_state = NUD_NOARP; memcpy(neigh->ha, dev->broadcast, dev->addr_len); } if (dev->header_ops->cache) neigh->ops = &arp_hh_ops; else neigh->ops = &arp_generic_ops; if (neigh->nud_state&NUD_VALID) neigh->output = neigh->ops->connected_output; else neigh->output = neigh->ops->output; } return 0; }
arp_send()先创建一个ARP报文,如果创建成功就将其发送出。该函数的参数与arp_create()相同
/* * 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; skb = arp_create(type, ptype, dest_ip, dev, src_ip, dest_hw, src_hw, target_hw); if (skb == NULL) { return; } arp_xmit(skb); }
arp_rcv()用来从二层接收并处理一个ARP报文
/* * Receive an arp request from the device layer. */ static int arp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { struct arphdr *arp; /* ARP header, plus 2 device addresses, plus 2 IP addresses. */ if (!pskb_may_pull(skb, arp_hdr_len(dev))) goto freeskb; arp = arp_hdr(skb); if (arp->ar_hln != dev->addr_len || dev->flags & IFF_NOARP || skb->pkt_type == PACKET_OTHERHOST || skb->pkt_type == PACKET_LOOPBACK || arp->ar_pln != 4) goto freeskb; if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) goto out_of_mem; memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb)); return NF_HOOK(NFPROTO_ARP, NF_ARP_IN, skb, dev, NULL, arp_process); freeskb: kfree_skb(skb); out_of_mem: return 0; }
在路由模块中,每当添加一条输出路由或是单播转发路由时,会尝试将该路由与该路由目的地址相对应的邻居项绑定。arp_bind_neighbour()实现了路由表项与邻居绑定的功能,在绑定过程中,如果对应的邻居项不存在,则会创建一个邻居项然后将路由项与之绑定。绑定之后,再输出报文时就能通过路由缓存找到输出函数。
原文:http://blog.csdn.net/wangpeihuixyz/article/details/39449143