概述
neigh_update函数用来更新指定的邻居项,更新内容是硬件地址和状态,更新之后,会根据新状态设置其输出函数,CONNECTED状态则使用快速输出,否则使用慢速输出;如果是由原来的无效状态变为现在的有效状态,则需要将数据包缓存队列中的数据包发送出去;
该函数在邻居子系统中被频繁调用;arp模块再收到邻居应答,收到邻居的情况,转发单播代理请求后,会调用该函数更新地址和状态;netlink或者ioctl模块添加或者删除邻居项,也会调用该函数更新地址和状态;
源码分析
1 /* 更新指定的邻居项,更新内容为硬件地址和状态,新状态有效,并且有缓存包,则发送 */ 2 int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, 3 u32 flags, u32 nlmsg_pid) 4 { 5 u8 old; 6 int err; 7 int notify = 0; 8 struct net_device *dev; 9 int update_isrouter = 0; 10 11 write_lock_bh(&neigh->lock); 12 13 dev = neigh->dev; 14 old = neigh->nud_state; 15 err = -EPERM; 16 17 /* 原状态是NOARP或者PERMANENT,必须要求是用户管理员发生的更新 */ 18 if (!(flags & NEIGH_UPDATE_F_ADMIN) && 19 (old & (NUD_NOARP | NUD_PERMANENT))) 20 goto out; 21 22 /* 已经在销毁状态 */ 23 if (neigh->dead) 24 goto out; 25 26 /* 新状态不是有效状态 */ 27 if (!(new & NUD_VALID)) { 28 29 /* 删除定时器 */ 30 neigh_del_timer(neigh); 31 32 /* 原状态是已连接状态,更新输出函数 */ 33 if (old & NUD_CONNECTED) 34 neigh_suspect(neigh); 35 /* 设置状态 */ 36 neigh->nud_state = new; 37 err = 0; 38 notify = old & NUD_VALID; 39 40 /* 原状态为INCOMPLETE或者PROBE,新状态为失败状态 */ 41 if ((old & (NUD_INCOMPLETE | NUD_PROBE)) && 42 (new & NUD_FAILED)) { 43 /* 清空缓存包队列 */ 44 neigh_invalidate(neigh); 45 notify = 1; 46 } 47 goto out; 48 } 49 50 /* Compare new lladdr with cached one */ 51 if (!dev->addr_len) { 52 /* First case: device needs no address. */ 53 lladdr = neigh->ha; 54 } else if (lladdr) { 55 /* The second case: if something is already cached 56 and a new address is proposed: 57 - compare new & old 58 - if they are different, check override flag 59 */ 60 if ((old & NUD_VALID) && 61 !memcmp(lladdr, neigh->ha, dev->addr_len)) 62 lladdr = neigh->ha; 63 } else { 64 /* No address is supplied; if we know something, 65 use it, otherwise discard the request. 66 */ 67 err = -EINVAL; 68 if (!(old & NUD_VALID)) 69 goto out; 70 lladdr = neigh->ha; 71 } 72 73 /* If entry was valid and address is not changed, 74 do not change entry state, if new one is STALE. 75 */ 76 err = 0; 77 update_isrouter = flags & NEIGH_UPDATE_F_OVERRIDE_ISROUTER; 78 79 /* 原状态有效 */ 80 if (old & NUD_VALID) { 81 /* 地址不同 && 无UPDATE_F_OVERRIDE标记 */ 82 if (lladdr != neigh->ha && !(flags & NEIGH_UPDATE_F_OVERRIDE)) { 83 update_isrouter = 0; 84 /* 有UPDATE_F_WEAK_OVERRIDE状态 && 原状态是连接状态 */ 85 if ((flags & NEIGH_UPDATE_F_WEAK_OVERRIDE) && 86 (old & NUD_CONNECTED)) { 87 /* 更新硬件地址为邻居项地址 */ 88 lladdr = neigh->ha; 89 /* 更新状态为STALE */ 90 new = NUD_STALE; 91 } else 92 goto out; 93 } 94 /* 地址相同或者有UPDATE_F_OVERRIDE标记 */ 95 else { 96 /* 地址相同&&新状态为STALE&&不是管理员更新,新状态设置为原状态 */ 97 if (lladdr == neigh->ha && new == NUD_STALE && 98 !(flags & NEIGH_UPDATE_F_ADMIN)) 99 new = old; 100 } 101 } 102 103 /* Update timestamps only once we know we will make a change to the 104 * neighbour entry. Otherwise we risk to move the locktime window with 105 * noop updates and ignore relevant ARP updates. 106 */ 107 /* 新旧状态不同或新旧地址不同 */ 108 if (new != old || lladdr != neigh->ha) { 109 /* 新状态是连接状态,更新确认时间 */ 110 if (new & NUD_CONNECTED) 111 neigh->confirmed = jiffies; 112 /* 更新更新时间 */ 113 neigh->updated = jiffies; 114 } 115 116 /* 新旧状态不同 */ 117 if (new != old) { 118 /* 删除定时器 */ 119 neigh_del_timer(neigh); 120 121 /* 新状态为PROBE,设置未接受到应答计数为0 */ 122 if (new & NUD_PROBE) 123 atomic_set(&neigh->probes, 0); 124 125 /* 新状态需要定时器,则添加 */ 126 if (new & NUD_IN_TIMER) 127 neigh_add_timer(neigh, (jiffies + 128 ((new & NUD_REACHABLE) ? 129 neigh->parms->reachable_time : 130 0))); 131 /* 设置新状态 */ 132 neigh->nud_state = new; 133 notify = 1; 134 } 135 136 /* 新旧地址不同 */ 137 if (lladdr != neigh->ha) { 138 write_seqlock(&neigh->ha_lock); 139 /* 拷贝新地址 */ 140 memcpy(&neigh->ha, lladdr, dev->addr_len); 141 write_sequnlock(&neigh->ha_lock); 142 /* 更新二层头缓存 */ 143 neigh_update_hhs(neigh); 144 145 /* 新状态不是连接状态,更新确认时间 */ 146 if (!(new & NUD_CONNECTED)) 147 neigh->confirmed = jiffies - 148 (NEIGH_VAR(neigh->parms, BASE_REACHABLE_TIME) << 1); 149 notify = 1; 150 } 151 152 /* 新旧状态相同 */ 153 if (new == old) 154 goto out; 155 156 /* 新状态为CONNECTED,更新输出函数为connected_out */ 157 if (new & NUD_CONNECTED) 158 neigh_connect(neigh); 159 /* 否则,输出函数为output */ 160 else 161 neigh_suspect(neigh); 162 163 /* 原状态无效,新状态有效 */ 164 if (!(old & NUD_VALID)) { 165 struct sk_buff *skb; 166 167 /* Again: avoid dead loop if something went wrong */ 168 169 /* 新状态有效,缓存队列不为空 */ 170 while (neigh->nud_state & NUD_VALID && 171 (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) { 172 struct dst_entry *dst = skb_dst(skb); 173 struct neighbour *n2, *n1 = neigh; 174 write_unlock_bh(&neigh->lock); 175 176 rcu_read_lock(); 177 178 /* Why not just use 'neigh' as-is? The problem is that 179 * things such as shaper, eql, and sch_teql can end up 180 * using alternative, different, neigh objects to output 181 * the packet in the output path. So what we need to do 182 * here is re-lookup the top-level neigh in the path so 183 * we can reinject the packet there. 184 */ 185 n2 = NULL; 186 /* 有路由缓存,则根据路由缓存获取邻居项,有则替换 */ 187 if (dst) { 188 n2 = dst_neigh_lookup_skb(dst, skb); 189 if (n2) 190 n1 = n2; 191 } 192 193 /* 输出数据包 */ 194 n1->output(n1, skb); 195 196 /* 是否引用的邻居项 */ 197 if (n2) 198 neigh_release(n2); 199 rcu_read_unlock(); 200 201 write_lock_bh(&neigh->lock); 202 } 203 204 /* 清空数据包缓存队列 */ 205 __skb_queue_purge(&neigh->arp_queue); 206 neigh->arp_queue_len_bytes = 0; 207 } 208 out: 209 if (update_isrouter) { 210 neigh->flags = (flags & NEIGH_UPDATE_F_ISROUTER) ? 211 (neigh->flags | NTF_ROUTER) : 212 (neigh->flags & ~NTF_ROUTER); 213 } 214 write_unlock_bh(&neigh->lock); 215 216 /* 通知其他关心的模块 */ 217 if (notify) 218 neigh_update_notify(neigh, nlmsg_pid); 219 220 return err; 221 }