(以下内容都基于linux内核2.4.0源码)
地址解析协议ARP负责映射IP地址到mac地址。ARP邻居是主机系统或单跳可达路由并用于链路层(MAC)
寻址以代替网络层IP寻址。
1、ARP数据结构
ARP数据结构的根(root)结构是neigh_table结构,它定义在include/net/neighbour.h。neigh_table结
构及其字段所指向的结构都用于arp缓存内部实现。每个使用ARP的网络层协议都有一个关联的neigh_table
结构实例。
139 struct neigh_table
140 {
141 struct neigh_table *next;
142 int family;
143 int entry_size;
144 int key_len;
145 __u32 (*hash)(void *pkey, struct net_device *);
146 int (*constructor)(struct neighbour *);
147 int (*pconstructor)(struct pneigh_entry *);
148 void (*pdestructor)(struct pneigh_entry *);
149 void (*proxy_redo)(struct sk_buff *skb);
150 char *id;
151 struct neigh_parms parms;
152 /* HACK. gc_* shoul follow parms without a gap! */
153 int gc_interval;
154 int gc_thresh1;
155 int gc_thresh2;
156 int gc_thresh3;
157 unsigned long last_flush;
158 struct timer_list gc_timer;
159 struct timer_list proxy_timer;
160 struct sk_buff_head proxy_queue;
161 int entries;
162 rwlock_t lock;
163 unsigned long last_rand;
164 struct neigh_parms *parms_list;
165 kmem_cache_t *kmem_cachep;
166 struct tasklet_struct gc_task;
167 struct neigh_statistics stats;
168 struct neighbour *hash_buckets[NEIGH_HASHMASK+1];
169 struct pneigh_entry *phash_buckets[PNEIGH_HASHMASK+1];
170 };
171
结构主要字段的功能:
next: 用于连接到邻居链表。neigh_tables指向此链表的第一个结构。似乎除了IPV4,
仅有DECNET和IPV6注册邻居表。
family: 协议簇(PF_INET).
id: arp缓存表的符号名
hash: 用于映射下一跳IP地址到特殊的哈希队列的哈希函数。对IP地址解析协议,这个函数是arp_hash()。
entry_size: neighbour结构大小+4(应该是key长度)。
key_len: 用于哈希函数的key长度(字节),由于IP地址就是贝用作key,所以对ARP而言,这个值就是4。
constructor: 初始化新的neighbour结构实例。ARP缓存中,每个元素都有个neighbour结构的条目。对于
IPV4的ARP而言,这个函数是arp_constructor()。
kmem_cache_p: 指向neighbour结构类型的slab缓存。
hash_buckets: neighbour结构的哈希队列。这里的哈希查找key就是下一跳的IP地址。
phash_buckets:pneigh_entry结构的哈希队列。这个可能用于ARP代理
gc_thresh*: 定义了邻居协议授权给缓存的neighbour条目内存使用的不同限制级别,ARP缓存增长得太
大时这些值用于减少缓存尺寸。
总共有32个neighbor结构的哈希队列和16个pneigh的哈希队列(include/net/neighbour.h):
131 #define NEIGH_HASHMASK 0x1F
132 #define PNEIGH_HASHMASK 0xF
2、IPv4邻居表
IPV4 ARP协议的邻居表静态定义,如下(net/ipv4/arp.c):
184 struct neigh_table arp_tbl =
185 {
186 NULL,
187 AF_INET,
188 sizeof(struct neighbour) + 4,
189 4,
190 arp_hash,
191 arp_constructor,
192 NULL,
193 NULL,
194 parp_redo,
195 "arp_cache",
196 { NULL, NULL, &arp_tbl, 0, NULL, NULL,
197 30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 64, 1*HZ },
198 30*HZ, 128, 512, 1024,
199 };
entry_size字段设置成sizeof(struct neighbour) + 4,neighbour结构的最后的字段primary_key[0]被定
义为一个0长度数组,entry_size字段中额外的4字节确保当结构动态分配时,包含primary_key[0]的实际尺寸空间。
parms定义了一些可操作的超时定时器。在标准的X86 Linux系统中时钟滴答每10毫秒一次,每秒中的滴答数
HZ=100。(include/asm-i386)
4 #ifndef HZ
5 #define HZ 100
6 #endif
3、neigh_parms结构
neigh_parms定义在include/net/neighbour.h.包含此结构实例或指针的是有neigh_table, neighbour,
和in_device结构.
53 struct neigh_parms
54 {
55 struct neigh_parms *next;
56 int (*neigh_setup)(struct neighbour *);
57 struct neigh_table *tbl;
58 int entries;
59 void *priv;
60
61 void *sysctl_table;
62
63 int base_reachable_time;
64 int retrans_time;
65 int gc_staletime;
66 int reachable_time;
67 int delay_probe_time;
68
69 int queue_len;
70 int ucast_probes;
71 int app_probes;
72 int mcast_probes;
73 int anycast_delay;
74 int proxy_delay;
75 int proxy_qlen;
76 int locktime;
77 };
4、neighbour结构
结构定义了单个ARP缓存元素的内容(include/net/neighbour.h)。
87 struct neighbour
88 {
89 struct neighbour *next;
90 struct neigh_table *tbl;
91 struct neigh_parms *parms;
92 struct net_device *dev;
93 unsigned long used;
94 unsigned long confirmed;
95 unsigned long updated;
96 __u8 flags;
97 __u8 nud_state;
98 __u8 type;
99 __u8 dead;
100 atomic_t probes;
101 rwlock_t lock;
102 unsigned char ha[(MAX_ADDR_LEN+sizeof(unsigned long)-1)&~(sizeof(unsigned long)-1)];
103 struct hh_cache *hh;
104 atomic_t refcnt;
105 int (*output)(struct sk_buff *skb);
106 struct sk_buff_head arp_queue;
107 struct timer_list timer;
108 struct neigh_ops *ops;
109 u8 primary_key[0];
110 };
结构主要字段的功能:
next:用于连接到特殊的hash_bucket的元素.
tbl: 向后指向拥有此结构的neigh_table结构.
parms: 向后指向其父亲neigh_table结构的成员parms组件的指针。
primary_key: 放置哈希函数使用的32位目标IP地址。其实际空间是动态分配的。
ha: 远端连接的网络设备的硬件(MAC)地址.
hh_cache: 指向目标节点的。
output: 指向用于发包的函数.当ARP缓存条目是NUD_REACHABLE状态其指向dev_queue_xmit();
否则指向neigh_resolve_output().
arp_queue: 所拥有的sk_buffs链表。
dev: 指向与ARP缓存相关联接口所对应的net_device结构.
timer_list: 用于管理各种超时情况的内核定时器。
ops: 指向不同输出的函数指针表。
5、hh_cache结构
硬件头缓存元素包含输出包必须的第一跳硬件头(include/linux/netdevice.h)
179 struct hh_cache
180 {
181 struct hh_cache *hh_next; /* Next entry */
182 atomic_t hh_refcnt; /* number of users */
183 unsigned short hh_type; /* protocol identifier, f.e ETH_P_IP */
184 int hh_len; /* length of header */
185 int (*hh_output)(struct sk_buff *skb);
186 rwlock_t hh_lock;
187 /* cached hardware header; allow for machine alignment needs. */
188 unsigned long hh_data[16/sizeof(unsigned long)];
189 };
结构主要字段的功能:
hh_next: 链接到下一个hh_cache结构.
hh_refcnt: 控制是否删除的引用计数
hh_len: MAC层的包头长度
hh_data: 用于放硬件头.
hh_output: 指向dev_queue_xmit()函数.
6、pneigh_entry结构
结构pneigh_entry可能描述邻居代理(include/net/neighbour.h).
124 struct pneigh_entry
125 {
126 struct pneigh_entry *next;
127 struct net_device *dev;
128 u8 key[0];
129 };
7、Neighbour操作
每个neighbour结构通过都neigh_ops定义了一组操作函数集,neigh_ops结构都由其父亲neigh_table定
义的构造函数填充(include/net/neighbour.h).
112 struct neigh_ops
113 {
114 int family;
115 void (*destructor)(struct neighbour *);
116 void (*solicit)(struct neighbour *, struct sk_buff*);
117 void (*error_report)(struct neighbour *, struct sk_buff*);
118 int (*output)(struct sk_buff*);
119 int (*connected_output)(struct sk_buff*);
120 int (*hh_output)(struct sk_buff*);
121 int (*queue_xmit)(struct sk_buff*);
122 };
arp_constructor()函数设置邻居的neigh_ops结构,用以下几个定义在net/ipv4/arp.c中实例.
通用neigh_ops结构.
136 static struct neigh_ops arp_generic_ops =
137 {
138 AF_INET,
139 NULL,
140 arp_solicit,
141 arp_error_report,
142 neigh_resolve_output,
143 neigh_connected_output,
144 dev_queue_xmit,
145 dev_queue_xmit
146 };
请求硬件头的设备的neigh_ops结构. 这种结构用于以太网设备.
148 static struct neigh_ops arp_hh_ops =
149 {
150 AF_INET,
151 NULL,
152 arp_solicit,
153 arp_error_report,
154 neigh_resolve_output,
155 neigh_resolve_output,
156 dev_queue_xmit,
157 dev_queue_xmit
158 };
不请求ARP的邻居的neigh_ops结构.
160 static struct neigh_ops arp_direct_ops =
161{
162 AF_INET,
163 NULL,
164 NULL,
165 NULL,
166 dev_queue_xmit,
167 dev_queue_xmit,
168 dev_queue_xmit,
169 dev_queue_xmit
170 };
用于使用旧有代码的设备(如业余无线电设备或某些WAN卡)的neigh_ops结构.
172 struct neigh_ops arp_broken_ops =
173{
174 AF_INET,
175 NULL,
176 arp_solicit,
177 arp_error_report,
178 neigh_compat_output,
179 neigh_compat_output,
180 dev_queue_xmit,
181 dev_queue_xmit,
182 };
8、arp_init()函数
定义在net/ipv4/arp.c
被inet_init()调用
包含的操作:
建立ARP缓存.
注册内核ARP包类型.
创建/proc/net/arp.
1156 void __init arp_init (void)
1157 {
1158 neigh_table_init(&arp_tbl);
1159
1160 dev_add_pack(&arp_packet_type);
1161
1162 proc_net_create ("arp", 0, arp_get_info);
1163
1164 #ifdef CONFIG_SYSCTL
1165 neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4, NET_IPV4_NEIGH, "ipv4");
1166 #endif
1167 }
8.1、邻居表初始化
每个主协议簇都可以提供它自己的地址解析服务和邻居表。目前IPV6和DECNET提供它们自己的服务,
IPV4用通用的ARP。
neigh_table_init()函数在net/core/neighbour.c中定义.
1114 void neigh_table_init(struct neigh_table *tbl)
1115 {
1116 unsigned long now = jiffies;
1117
这里reachable_time的值被统一设置成下面的随机值:
[base_reachable_time / 2, 3 x base_reachable_time]
重调用时base_reachable_time是30秒.
1118 tbl->parms.reachable_time =neigh_rand_reach_time(tbl->parms.base_reachable_time);
arp_cache被创建. 邻居对象将被从slab缓存中分配.entry_size先前已经被设置成sizeof(struct neighbor) + 4.
1120 if (tbl->kmem_cachep == NULL)
1121 tbl->kmem_cachep = kmem_cache_create(tbl->id,
1122 (tbl->entry_size+15)&~15,
1123 0, SLAB_HWCACHE_ALIGN,
1124 NULL, NULL);
1125
ARP用内核定时器驱动用于检测超时条件的出口函数。每个定时器结构包含下面的数据元素(include/linux/timer.h):
20 struct timer_list {
21 struct list_head list;
22 unsigned long expires;
23 unsigned long data;
24 void (*function)(unsigned long);
25 };
data: 传给定时器出口函数的任意值
function: 被调用的出口函数的地址
expires: 基于jiffies的时间值,到此时间时出口函数被调用.
init_timer()函数简单初始化timer_list结构的字段.调用add_timer()将此定时器加入定时器队列.
下面是neigh_table结构的gc_timer定时器字段的初始化,其超时值设置成30 * HZ + (~30 * HZ)
约等于1分钟.
1129 init_timer(&tbl->gc_timer);
1130 tbl->lock = RW_LOCK_UNLOCKED;
1131 tbl->gc_timer.data = (unsigned long)tbl;
1132 tbl->gc_timer.function = neigh_periodic_timer;
1133 tbl->gc_timer.expires = now + tbl->gc_interval +tbl->parms.reachable_time;
1134 add_timer(&tbl->gc_timer);
1135
代理定时器初始化但并不链入定时器队列,直到代理ARP元素都建立
1136 init_timer(&tbl->proxy_timer);
1137 tbl->proxy_timer.data = (unsigned long)tbl;
1138 tbl->proxy_timer.function = neigh_proxy_process;
1139 skb_queue_head_init(&tbl->proxy_queue);
1140
1141 tbl->last_flush = now;
1142 tbl->last_rand = now + tbl->parms.reachable_time*20;
初始化后的邻居表(arp_tbl)被插入到全局变量neigh_tables所指的邻居表链表队列.
1143 write_lock(&neigh_tbl_lock);
1144 tbl->next = neigh_tables;
1145 neigh_tables = tbl;
1146 write_unlock(&neigh_tbl_lock);
1147 }
8.2、注册ARP包类型
设置了ARP缓存后,arp_init()必须注册ARP包类型到链路层,这是通过调用dev_add_pack()实现.
1160 dev_add_pack(&arp_packet_type);
arp_packet_type静态定义如下
1147 static struct packet_type arp_packet_type =
1148 {
1149 __constant_htons(ETH_P_ARP),
1150 NULL, /* All devices */
1151 arp_rcv,
1152 (void*)1,
1153 NULL
1154 };
arp_rcv()是接收到ARP包时被调用的包处理函数.传递给它的参数如下:
573 int arp_rcv(struct sk_buff *skb, struct net_device *dev,struct packet_type *pt)
8.3、创建/proc/net/arp项
注册ARP包类型后,arp_init()创建proc项以通过arp_get_info()函数显示ARP缓存的内容.
arp_get_info()显示hash_buckets和phash_buckets中的条目.
1162 proc_net_create ("arp", 0, arp_get_info);
1163
1164 #ifdef CONFIG_SYSCTL
1165 neigh_sysctl_register(NULL, &arp_tbl.parms,NET_IPV4, NET_IPV4_NEIGH, "ipv4");
1166 #endif
1167 }
下表中,最后三个条目被代理:
/proc/net ==> cat arp
IP address HW type Flags HW address Mask Device
192.168.2.4 0x1 0x2 00:00:77:97:C3:A5 * lec0
192.168.2.5 0x1 0x2 00:00:77:88:A4:95 * lec0
192.168.2.6 0x1 0x2 00:00:77:88:A1:15 * lec0
192.168.2.35 0x1 0x2 00:50:DA:31:3F:4A * eth0
192.168.2.7 0x1 0x2 00:00:77:88:A5:A5 * lec0
192.168.2.1 0x1 0x2 00:20:48:2E:00:EE * lec0
130.127.48.184 0x1 0xc 00:00:00:00:00:00 * lec0
192.168.2.66 0x1 0xc 00:00:00:00:00:00 * lec0
192.168.2.35 0x1 0xc 00:00:00:00:00:00 * lec0