ip_route_output_flow
2015-04-23 16:59
796 查看
[ip_route_output_flow]
[ip_route_output_flow->__ip_route_output_key]
在路由表中找查的结果会放在res当中,res.fi指向fib_info,res.table指向fib_table,保存fl4的输出设备ID,将fl4的输入设备设为环回接口,根据tos设置fls的scope,RT_SCOPE_LINK为局域网;RT_SCOPE_UNIVERSE表示没有直接相连的路由,使用下一站路由(next hop gateway)。下面看源地址不为0的情况:
当源地址为多播地址,广播地址,全0时,直接返回。
当输出设备ID为0并且目标地址为多播地址或广播地址时,查找与源地址绑定的设备,如果没找到,返回
[ip_route_output_flow->__ip_route_output_key->__ip_dev_find]
inet_addr_lst是一个哈希表:static struct hlist_head inet_addr_lst[256]; 里面保存的列表项类型为in_ifaddr,表示一个地址。先计算地址的哈希值,然后开始查找。如果地址相同,并且设备的网络名字空间也相同,成功找到。
如果没找到,在本地路由表中查找与addr匹配的项,结果放在res中。如果找到并且类型为RTN_LOCAL时,得到设备。
[ip_route_output_flow->__ip_route_output_key->__ip_dev_find->fib_table_lookup]
路由表被组织为一棵树,tb->tb_data指向根节点,查找的key是地址,从第一个子节点开始查找:
如果第一个子节点是一个叶子,说明整个树只有一个叶子,只要查找它就可以了:
[ip_route_output_flow->__ip_route_output_key->__ip_dev_find->fib_table_lookup->check_leaf]
每个叶子中都有一个leaf_info列表。先找到key相同的项
一个地址有可能有多个路由,li->falh指向一个路由列表。要从中选出一个满足条件的。它们的tos要相同,fib_dead为false,路由的scope要匹配。这些条件都满足后,设置fa的状态为FA_S_ACCESSED
fib_props是一个全局数组,它对不同的路由类型定义了一个错误值,如:RTN_UNREACHABLE的error值为-EHOSTUNREACH。如果路由的状态不正确,直接返回。还要检查路由的flags不能为RTNH_F_DEAD(Nexthop is dead)
下一站路由地址会cache在fi->fib_nh中,此数组的大小为fi->fib_nhs。如果flp设置了输出设备,要与下一站地址的相同。然后设置返回值,返回0表示成功。
[ip_route_output_flow->__ip_route_output_key->__ip_dev_find->fib_table_lookup]
如果有多个叶子,就要通过循环来查找了。通过计算key,得到子树索引,并得到子树
如果是叶子,在当中查找。
通过一系列计算得到下一个叶子。如果成功找到就返回0。
[ip_route_output_flow->__ip_route_output_key]
如果输出设备没找到,出错返回。此时输出设备为与在路由表中与源地址对应的设备。转到make_route
总结下,当源地址不为0时,如果是多播地址或广播地址,直接返回。如果目标地址是多播地址或广播地址并且输出设备ID为0,通过源地址在路由表中去找设备,如果找到,转到make_route处。然后如果没有设置FLOWI_FLAG_ANYSRC,调用__ip_dev_find查找,如果没找到,出错返回。到这里,要么是源地址为0,要么源地址不为0并且通过源地址找到了输出设备。下面来看对目标地址的处理:
如果输出设备不为0,先从ID得到设备,如果设备没有有被激活或因为竞争关系无法得到设备,出错。扣果目标地址为本地多播或广播,转到make_route,此时如果源地址不为0,将其设置为scope为RT_SCOPE_LINK的地址。再判断如果源地址为0,目的地址为多播,将其设置为scope为fl4->flowi4_scope的地址;目的地址为0,将其设置为scope为RT_SCOPE_HOST的地址。
[ip_route_output_flow->__ip_route_output_key->inet_select_addr]
得到设备,在设备的primary地址列表中查找。地址的scope要大于要求的,如果目标地址为0或与列表中的地址匹配,就找到了。如果上面条件不符合,就选择列表中的第一个地址。如果找到,就返回了。
如果没找到,在同一个网络名字空间的所有设备上查找。设备不能为空,然后在每个设备的primary地址列表中查找scope不大于目标地址并不等于RT_SCOPE_LINK的地址。
[ip_route_output_flow->__ip_route_output_key]
目标地址为0的情况。先把目标地址设为源地址,如果两者都为0,都设为环回地址。输出设备设为环回接口,地址类型设为本机地址。转到make_route。
[ip_route_output_flow->__ip_route_output_key->fib_lookup]
在本地路由表和main路由表中分别查找。
[ip_route_output_flow->__ip_route_output_key]
没有找到的情况:如果输出设备不为0,结果设为前往默认路由,此时如果源地址为0,设为scope为RT_SCOPE_LINK的本机地址,然后转向make_route。如果输出设备为0,直接返回。
如果是本机地址,输出设备设为环回接口,fl4的输出设备号设为其输入设备号;此时如果源地址为0,如果结果设置了fib_prefsrc,将其设为源地址,否则设为目标地址。转到make_route。
如果路由表长度为0,并且路由表tb_num_default > 1并且路由指向gateway,并且输出设备为0,选择默认路由
[ip_route_output_flow->__ip_route_output_key->fib_select_default]
有可能多个路由项指向同一个目标地址,而这些路由仅仅是因为TOS不同。这些不同的路由项放在fa_head中
在fa_head列表中查找,scope不同或类型不是RTN_UNICAST的跳过;优先级大的,结束查找,因为列表是按照优先级来排序的;下上站路由的gateway为0或scope不为RT_SCOPE_LINK的跳过。
第一次循环时如果它们指向的fib_info不同(目标地址不同),结束查找。
[ip_route_output_flow->__ip_route_output_key->fib_select_default->fib_detect_death]
arp_tbl主要用来缓冲ARP地址,这里来查找下一站地址。找到后,设备状态state
[ip_route_output_flow->__ip_route_output_key->fib_select_default->fib_detect_death->neigh_lookup]
计算出pkey的哈希值,这里的tbl为arp_table
在哈希表中查找。
[ip_route_output_flow->__ip_route_output_key->fib_select_default->fib_detect_death]
NUD_REACHABLE表示处在连接状态;
如果源地址为0,如果结果设置了fib_prefsrc,将其设为源地址,否则设为目标地址。
把结果放入缓冲中。
struct rtable *ip_route_output_flow(struct net *net, struct flowi4 *flp4, struct sock *sk) { struct rtable *rt = __ip_route_output_key(net, flp4); return rt; }
[ip_route_output_flow->__ip_route_output_key]
struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4) { __u8 tos = RT_FL_TOS(fl4); // tos struct fib_result res; // 查找结果 int orig_oif; res.tclassid = 0; res.fi = NULL; // fib_info res.table = NULL; // fib_table orig_oif = fl4->flowi4_oif; // 输出设备ID fl4->flowi4_iif = LOOPBACK_IFINDEX; // 输入设备ID设为环回接口 fl4->flowi4_tos = tos & IPTOS_RT_MASK; fl4->flowi4_scope = ((tos & RTO_ONLINK) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE);
在路由表中找查的结果会放在res当中,res.fi指向fib_info,res.table指向fib_table,保存fl4的输出设备ID,将fl4的输入设备设为环回接口,根据tos设置fls的scope,RT_SCOPE_LINK为局域网;RT_SCOPE_UNIVERSE表示没有直接相连的路由,使用下一站路由(next hop gateway)。下面看源地址不为0的情况:
if (fl4->saddr) { if (ipv4_is_multicast(fl4->saddr) || ipv4_is_lbcast(fl4->saddr) || ipv4_is_zeronet(fl4->saddr)) goto out;
当源地址为多播地址,广播地址,全0时,直接返回。
if (fl4->flowi4_oif == 0 && (ipv4_is_multicast(fl4->daddr) || ipv4_is_lbcast(fl4->daddr))) { dev_out = __ip_dev_find(net, fl4->saddr, false); if (dev_out == NULL) goto out;
当输出设备ID为0并且目标地址为多播地址或广播地址时,查找与源地址绑定的设备,如果没找到,返回
[ip_route_output_flow->__ip_route_output_key->__ip_dev_find]
struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) { u32 hash = inet_addr_hash(net, addr); // 地址的哈希值 hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) { if (ifa->ifa_local == addr) { struct net_device *dev = ifa->ifa_dev->dev; if (!net_eq(dev_net(dev), net)) continue; result = dev; break; } }
inet_addr_lst是一个哈希表:static struct hlist_head inet_addr_lst[256]; 里面保存的列表项类型为in_ifaddr,表示一个地址。先计算地址的哈希值,然后开始查找。如果地址相同,并且设备的网络名字空间也相同,成功找到。
if (!result) { struct flowi4 fl4 = { .daddr = addr }; struct fib_table *local; local = fib_get_table(net, RT_TABLE_LOCAL); // 得到本地路由表 if (local && !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) && res.type == RTN_LOCAL) result = FIB_RES_DEV(res); } return result; }
如果没找到,在本地路由表中查找与addr匹配的项,结果放在res中。如果找到并且类型为RTN_LOCAL时,得到设备。
[ip_route_output_flow->__ip_route_output_key->__ip_dev_find->fib_table_lookup]
int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, struct fib_result *res, int fib_flags) { struct trie *t = (struct trie *) tb->tb_data; // 根节点 t_key key = ntohl(flp->daddr); // 地址作为key struct rt_trie_node *n; n = rcu_dereference(t->trie); // 字典树节点 if (!n) goto failed;
路由表被组织为一棵树,tb->tb_data指向根节点,查找的key是地址,从第一个子节点开始查找:
/* Just a leaf? */ if (IS_LEAF(n)) { ret = check_leaf(tb, t, (struct leaf *)n, key, flp, res, fib_flags); goto found; }
如果第一个子节点是一个叶子,说明整个树只有一个叶子,只要查找它就可以了:
[ip_route_output_flow->__ip_route_output_key->__ip_dev_find->fib_table_lookup->check_leaf]
static int check_leaf(struct fib_table *tb, struct trie *t, struct leaf *l, t_key key, const struct flowi4 *flp, struct fib_result *res, int fib_flags) { struct leaf_info *li; struct hlist_head *hhead = &l->list; hlist_for_each_entry_rcu(li, hhead, hlist) { struct fib_alias *fa; if (l->key != (key & li->mask_plen)) continue;
每个叶子中都有一个leaf_info列表。先找到key相同的项
list_for_each_entry_rcu(fa, &li->falh, fa_list) { struct fib_info *fi = fa->fa_info; int nhsel, err; if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos) continue; if (fi->fib_dead) continue; if (fa->fa_info->fib_scope < flp->flowi4_scope) continue; if (!(fa->fa_state & FA_S_ACCESSED)) fa->fa_state |= FA_S_ACCESSED;
一个地址有可能有多个路由,li->falh指向一个路由列表。要从中选出一个满足条件的。它们的tos要相同,fib_dead为false,路由的scope要匹配。这些条件都满足后,设置fa的状态为FA_S_ACCESSED
err = fib_props[fa->fa_type].error; if (err) { return err; } if (fi->fib_flags & RTNH_F_DEAD) continue;
fib_props是一个全局数组,它对不同的路由类型定义了一个错误值,如:RTN_UNREACHABLE的error值为-EHOSTUNREACH。如果路由的状态不正确,直接返回。还要检查路由的flags不能为RTNH_F_DEAD(Nexthop is dead)
for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) { const struct fib_nh *nh = &fi->fib_nh[nhsel]; if (nh->nh_flags & RTNH_F_DEAD) continue; if (flp->flowi4_oif && flp->flowi4_oif != nh->nh_oif) continue; res->prefixlen = li->plen; res->nh_sel = nhsel; res->type = fa->fa_type; res->scope = fa->fa_info->fib_scope; res->fi = fi; res->table = tb; res->fa_head = &li->falh; if (!(fib_flags & FIB_LOOKUP_NOREF)) atomic_inc(&fi->fib_clntref); return 0; } } } return 1; }
下一站路由地址会cache在fi->fib_nh中,此数组的大小为fi->fib_nhs。如果flp设置了输出设备,要与下一站地址的相同。然后设置返回值,返回0表示成功。
[ip_route_output_flow->__ip_route_output_key->__ip_dev_find->fib_table_lookup]
// 是一个根节点 pn = (struct tnode *) n; chopped_off = 0; while (pn) { pos = pn->pos; bits = pn->bits; if (!chopped_off) cindex = tkey_extract_bits(mask_pfx(key, current_prefix_length), pos, bits); n = tnode_get_child_rcu(pn, cindex); // 得到子树 if (n == NULL) { goto backtrace; }
如果有多个叶子,就要通过循环来查找了。通过计算key,得到子树索引,并得到子树
if (IS_LEAF(n)) { ret = check_leaf(tb, t, (struct leaf *)n, key, flp, res, fib_flags); if (ret > 0) goto backtrace; goto found; } cn = (struct tnode *)n;
如果是叶子,在当中查找。
if (current_prefix_length < pos+bits) { if (tkey_extract_bits(cn->key, current_prefix_length, cn->pos - current_prefix_length) || !(cn->child[0])) goto backtrace; } pref_mismatch = mask_pfx(cn->key ^ key, cn->pos); if (pref_mismatch) { /* fls(x) = __fls(x) + 1 */ int mp = KEYLENGTH - __fls(pref_mismatch) - 1; if (tkey_extract_bits(cn->key, mp, cn->pos - mp) != 0) goto backtrace; if (current_prefix_length >= cn->pos) current_prefix_length = mp; } pn = (struct tnode *)n; /* Descend */ chopped_off = 0; continue; backtrace: chopped_off++; /* As zero don't change the child key (cindex) */ while ((chopped_off <= pn->bits) && !(cindex & (1<<(chopped_off-1)))) chopped_off++; /* Decrease current_... with bits chopped off */ if (current_prefix_length > pn->pos + pn->bits - chopped_off) current_prefix_length = pn->pos + pn->bits - chopped_off; /* * Either we do the actual chop off according or if we have * chopped off all bits in this tnode walk up to our parent. */ if (chopped_off <= pn->bits) { cindex &= ~(1 << (chopped_off-1)); } else { struct tnode *parent = node_parent_rcu((struct rt_trie_node *) pn); if (!parent) goto failed; /* Get Child's index */ cindex = tkey_extract_bits(pn->key, parent->pos, parent->bits); pn = parent; chopped_off = 0; goto backtrace; } } failed: ret = 1; found: return ret; }
通过一系列计算得到下一个叶子。如果成功找到就返回0。
[ip_route_output_flow->__ip_route_output_key]
if (dev_out == NULL) goto out; fl4->flowi4_oif = dev_out->ifindex; // 设置输出设备的ID goto make_route; }
如果输出设备没找到,出错返回。此时输出设备为与在路由表中与源地址对应的设备。转到make_route
if (!(fl4->flowi4_flags & FLOWI_FLAG_ANYSRC)) { if (!__ip_dev_find(net, fl4->saddr, false)) goto out; } }
总结下,当源地址不为0时,如果是多播地址或广播地址,直接返回。如果目标地址是多播地址或广播地址并且输出设备ID为0,通过源地址在路由表中去找设备,如果找到,转到make_route处。然后如果没有设置FLOWI_FLAG_ANYSRC,调用__ip_dev_find查找,如果没找到,出错返回。到这里,要么是源地址为0,要么源地址不为0并且通过源地址找到了输出设备。下面来看对目标地址的处理:
if (fl4->flowi4_oif) { // 输出设备ID不为0 dev_out = dev_get_by_index_rcu(net, fl4->flowi4_oif); // 从ID得到设备 if (dev_out == NULL) goto out; /* RACE: Check return value of inet_select_addr instead. */ if (!(dev_out->flags & IFF_UP) || !__in_dev_get_rcu(dev_out)) { goto out; } /* 本地多播 * 广播 */ if (ipv4_is_local_multicast(fl4->daddr) || ipv4_is_lbcast(fl4->daddr)) { if (!fl4->saddr) // 源地址不为0 fl4->saddr = inet_select_addr(dev_out, 0, RT_SCOPE_LINK); goto make_route; } if (!fl4->saddr) {// 源地址为0 if (ipv4_is_multicast(fl4->daddr)) // 多播 fl4->saddr = inet_select_addr(dev_out, 0, fl4->flowi4_scope); else if (!fl4->daddr) // 目标地址为0 fl4->saddr = inet_select_addr(dev_out, 0, RT_SCOPE_HOST); } }
如果输出设备不为0,先从ID得到设备,如果设备没有有被激活或因为竞争关系无法得到设备,出错。扣果目标地址为本地多播或广播,转到make_route,此时如果源地址不为0,将其设置为scope为RT_SCOPE_LINK的地址。再判断如果源地址为0,目的地址为多播,将其设置为scope为fl4->flowi4_scope的地址;目的地址为0,将其设置为scope为RT_SCOPE_HOST的地址。
[ip_route_output_flow->__ip_route_output_key->inet_select_addr]
__be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) { __be32 addr = 0; struct in_device *in_dev; in_dev = __in_dev_get_rcu(dev); // 设备 if (!in_dev) goto no_in_dev; for_primary_ifa(in_dev) { // 设备的所有primary地址 if (ifa->ifa_scope > scope) // 地址的范围大于参数传进来的 continue; /* 目标地址为空 * 与目标地址相同 */ if (!dst || inet_ifa_match(dst, ifa)) { addr = ifa->ifa_local; // 得到地址 break; } if (!addr) // 如果没设地址初始化addr addr = ifa->ifa_local; } endfor_ifa(in_dev); if (addr) goto out_unlock;
得到设备,在设备的primary地址列表中查找。地址的scope要大于要求的,如果目标地址为0或与列表中的地址匹配,就找到了。如果上面条件不符合,就选择列表中的第一个地址。如果找到,就返回了。
no_in_dev: /* Not loopback addresses on loopback should be preferred in this case. It is importnat that lo is the first interface in dev_base list. */ for_each_netdev_rcu(net, dev) { // 所有网络设备 in_dev = __in_dev_get_rcu(dev); // 设备 if (!in_dev) continue; for_primary_ifa(in_dev) { // 设备的所有primary地址 if (ifa->ifa_scope != RT_SCOPE_LINK && ifa->ifa_scope <= scope) { addr = ifa->ifa_local; // 得到地址 goto out_unlock; } } endfor_ifa(in_dev); } out_unlock: return addr; }
如果没找到,在同一个网络名字空间的所有设备上查找。设备不能为空,然后在每个设备的primary地址列表中查找scope不大于目标地址并不等于RT_SCOPE_LINK的地址。
[ip_route_output_flow->__ip_route_output_key]
if (!fl4->daddr) { fl4->daddr = fl4->saddr; // 目标地址设为源地址 if (!fl4->daddr) // 目标地址为0 fl4->daddr = fl4->saddr = htonl(INADDR_LOOPBACK); dev_out = net->loopback_dev; // 输出设备设为环回接口 fl4->flowi4_oif = LOOPBACK_IFINDEX; // 输出设备ID设为环回接口 res.type = RTN_LOCAL; // 本机地址 flags |= RTCF_LOCAL; // 本机地址 goto make_route; }
目标地址为0的情况。先把目标地址设为源地址,如果两者都为0,都设为环回地址。输出设备设为环回接口,地址类型设为本机地址。转到make_route。
if (fib_lookup(net, fl4, &res)) { // 路由表中查找
[ip_route_output_flow->__ip_route_output_key->fib_lookup]
static inline int fib_lookup(struct net *net, const struct flowi4 *flp, struct fib_result *res) { struct fib_table *table; table = fib_get_table(net, RT_TABLE_LOCAL); if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF)) return 0; table = fib_get_table(net, RT_TABLE_MAIN); if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF)) return 0; return -ENETUNREACH; }
在本地路由表和main路由表中分别查找。
[ip_route_output_flow->__ip_route_output_key]
res.fi = NULL; res.table = NULL; if (fl4->flowi4_oif) { // 输出设备ID不为0 if (fl4->saddr == 0) // 源地址为0 fl4->saddr = inet_select_addr(dev_out, 0, RT_SCOPE_LINK); res.type = RTN_UNICAST; /* Gateway or direct route */ goto make_route; } goto out; }
没有找到的情况:如果输出设备不为0,结果设为前往默认路由,此时如果源地址为0,设为scope为RT_SCOPE_LINK的本机地址,然后转向make_route。如果输出设备为0,直接返回。
if (res.type == RTN_LOCAL) { // 本机地址 if (!fl4->saddr) { // 源地址为0 if (res.fi->fib_prefsrc) fl4->saddr = res.fi->fib_prefsrc;// 源IP地址 else fl4->saddr = fl4->daddr; // 源地址设为目标地址 } dev_out = net->loopback_dev; // 环回接口 fl4->flowi4_oif = dev_out->ifindex; // 输出设备 ID flags |= RTCF_LOCAL; // 本机地址 goto make_route; }
如果是本机地址,输出设备设为环回接口,fl4的输出设备号设为其输入设备号;此时如果源地址为0,如果结果设置了fib_prefsrc,将其设为源地址,否则设为目标地址。转到make_route。
if (!res.prefixlen && res.table->tb_num_default > 1 && res.type == RTN_UNICAST && !fl4->flowi4_oif) fib_select_default(&res); // 默认路由
如果路由表长度为0,并且路由表tb_num_default > 1并且路由指向gateway,并且输出设备为0,选择默认路由
[ip_route_output_flow->__ip_route_output_key->fib_select_default]
void fib_select_default(struct fib_result *res) { struct fib_info *fi = NULL, *last_resort = NULL; struct list_head *fa_head = res->fa_head; struct fib_table *tb = res->table; int order = -1, last_idx = -1; struct fib_alias *fa;
有可能多个路由项指向同一个目标地址,而这些路由仅仅是因为TOS不同。这些不同的路由项放在fa_head中
list_for_each_entry_rcu(fa, fa_head, fa_list) { struct fib_info *next_fi = fa->fa_info; if (next_fi->fib_scope != res->scope || fa->fa_type != RTN_UNICAST) continue; if (next_fi->fib_priority > res->fi->fib_priority) break; if (!next_fi->fib_nh[0].nh_gw || next_fi->fib_nh[0].nh_scope != RT_SCOPE_LINK) continue; fib_alias_accessed(fa);
在fa_head列表中查找,scope不同或类型不是RTN_UNICAST的跳过;优先级大的,结束查找,因为列表是按照优先级来排序的;下上站路由的gateway为0或scope不为RT_SCOPE_LINK的跳过。
if (fi == NULL) { if (next_fi != res->fi) break; } else if (!fib_detect_death(fi, order, &last_resort, &last_idx, tb->tb_default)) { fib_result_assign(res, fi); tb->tb_default = order; goto out; } fi = next_fi; order++; }
第一次循环时如果它们指向的fib_info不同(目标地址不同),结束查找。
[ip_route_output_flow->__ip_route_output_key->fib_select_default->fib_detect_death]
static int fib_detect_death(struct fib_info *fi, int order, struct fib_info **last_resort, int *last_idx, int dflt) { struct neighbour *n; int state = NUD_NONE; n = neigh_lookup(&arp_tbl, &fi->fib_nh[0].nh_gw, fi->fib_dev); if (n) { state = n->nud_state; neigh_release(n); }
arp_tbl主要用来缓冲ARP地址,这里来查找下一站地址。找到后,设备状态state
[ip_route_output_flow->__ip_route_output_key->fib_select_default->fib_detect_death->neigh_lookup]
struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, struct net_device *dev) { struct neighbour *n; int key_len = tbl->key_len; // 地址长度 u32 hash_val; struct neigh_hash_table *nht; NEIGH_CACHE_STAT_INC(tbl, lookups); rcu_read_lock_bh(); nht = rcu_dereference_bh(tbl->nht); // neighbour对象的哈希表 hash_val = tbl->hash(pkey, dev, nht->hash_rnd) >> (32 - nht->hash_shift);
计算出pkey的哈希值,这里的tbl为arp_table
for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); // 哈希表子表 n != NULL; n = rcu_dereference_bh(n->next)) { if (dev == n->dev && !memcmp(n->primary_key, pkey, key_len)) { // 设备和KEY都相同 if (!atomic_inc_not_zero(&n->refcnt)) // 增加引用计数 n = NULL; NEIGH_CACHE_STAT_INC(tbl, hits); // 记录状态 break; } } rcu_read_unlock_bh(); return n; }
在哈希表中查找。
[ip_route_output_flow->__ip_route_output_key->fib_select_default->fib_detect_death]
if (state == NUD_REACHABLE) return 0; if ((state & NUD_VALID) && order != dflt) return 0; if ((state & NUD_VALID) || (*last_idx < 0 && order > dflt)) { *last_resort = fi; *last_idx = order; } return 1; }
NUD_REACHABLE表示处在连接状态;
if (!fl4->saddr) // 源地址为0 fl4->saddr = FIB_RES_PREFSRC(net, res); dev_out = FIB_RES_DEV(res); fl4->flowi4_oif = dev_out->ifindex; // 输出设备ID
如果源地址为0,如果结果设置了fib_prefsrc,将其设为源地址,否则设为目标地址。
make_route: rth = __mkroute_output(&res, fl4, orig_oif, dev_out, flags); // 新增路由缓冲 out: rcu_read_unlock(); return rth; }
把结果放入缓冲中。
相关文章推荐
- ip_route_output_key函数分析(1)
- Linux内核路由过程简述 ip_route_output_slow() ip_route_input()
- route 一个很奇怪的现象:我的主机能ping通同一网段的其它主机,并也能xshell 远程其它的主机,而其它的主机不能ping通我的ip,也不能远程我和主机
- GG-01224 TCP/IP error 113 (No route to host)
- 從大規模轉址事件看IP spoofing、ARP spoofing、ARP掛馬與路徑(route)安全
- show ip interface brief | tee tftp://10.1.1.1/show-output.txt 保护到某处
- kernel ipv4/ip_output.c
- telnet route-server.ip.att.net
- Linux TCP/IP 网络工具:iproute2
- 本地IP路由表命令route
- linux系统网络命令ifconfig-route-ip-ss
- linux网络配置命令ip(ifconfig+route)小结
- 网络服务及网络管理命令 ifconfig, ip, route, netstat 总结
- ip route 0.0.0.0 0.0.0.0和ip defult-network区别
- no ip mroute-cache是什么意思
- kernel ipv4/ip_output.c
- Linux IP、DNS、Route配置
- 常用Linux路由命令(route、ip、ifconfig等等)
- 【Linux4.1.12源码分析】IP层报文发送之ip_output