📄 fib_semantics.c
字号:
return kmalloc(bytes, GFP_KERNEL); else return (struct hlist_head *) __get_free_pages(GFP_KERNEL, get_order(bytes));}static void fib_hash_free(struct hlist_head *hash, int bytes){ if (!hash) return; if (bytes <= PAGE_SIZE) kfree(hash); else free_pages((unsigned long) hash, get_order(bytes));}static void fib_hash_move(struct hlist_head *new_info_hash, struct hlist_head *new_laddrhash, unsigned int new_size){ struct hlist_head *old_info_hash, *old_laddrhash; unsigned int old_size = fib_hash_size; unsigned int i, bytes; spin_lock_bh(&fib_info_lock); old_info_hash = fib_info_hash; old_laddrhash = fib_info_laddrhash; fib_hash_size = new_size; for (i = 0; i < old_size; i++) { struct hlist_head *head = &fib_info_hash[i]; struct hlist_node *node, *n; struct fib_info *fi; hlist_for_each_entry_safe(fi, node, n, head, fib_hash) { struct hlist_head *dest; unsigned int new_hash; hlist_del(&fi->fib_hash); new_hash = fib_info_hashfn(fi); dest = &new_info_hash[new_hash]; hlist_add_head(&fi->fib_hash, dest); } } fib_info_hash = new_info_hash; for (i = 0; i < old_size; i++) { struct hlist_head *lhead = &fib_info_laddrhash[i]; struct hlist_node *node, *n; struct fib_info *fi; hlist_for_each_entry_safe(fi, node, n, lhead, fib_lhash) { struct hlist_head *ldest; unsigned int new_hash; hlist_del(&fi->fib_lhash); new_hash = fib_laddr_hashfn(fi->fib_prefsrc); ldest = &new_laddrhash[new_hash]; hlist_add_head(&fi->fib_lhash, ldest); } } fib_info_laddrhash = new_laddrhash; spin_unlock_bh(&fib_info_lock); bytes = old_size * sizeof(struct hlist_head *); fib_hash_free(old_info_hash, bytes); fib_hash_free(old_laddrhash, bytes);}struct fib_info *fib_create_info(struct fib_config *cfg){ int err; struct fib_info *fi = NULL; struct fib_info *ofi; int nhs = 1; /* Fast check to catch the most weird cases */ if (fib_props[cfg->fc_type].scope > cfg->fc_scope) goto err_inval;#ifdef CONFIG_IP_ROUTE_MULTIPATH if (cfg->fc_mp) { nhs = fib_count_nexthops(cfg->fc_mp, cfg->fc_mp_len); if (nhs == 0) goto err_inval; }#endif err = -ENOBUFS; if (fib_info_cnt >= fib_hash_size) { unsigned int new_size = fib_hash_size << 1; struct hlist_head *new_info_hash; struct hlist_head *new_laddrhash; unsigned int bytes; if (!new_size) new_size = 1; bytes = new_size * sizeof(struct hlist_head *); new_info_hash = fib_hash_alloc(bytes); new_laddrhash = fib_hash_alloc(bytes); if (!new_info_hash || !new_laddrhash) { fib_hash_free(new_info_hash, bytes); fib_hash_free(new_laddrhash, bytes); } else { memset(new_info_hash, 0, bytes); memset(new_laddrhash, 0, bytes); fib_hash_move(new_info_hash, new_laddrhash, new_size); } if (!fib_hash_size) goto failure; } fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); if (fi == NULL) goto failure; fib_info_cnt++; fi->fib_protocol = cfg->fc_protocol; fi->fib_flags = cfg->fc_flags; fi->fib_priority = cfg->fc_priority; fi->fib_prefsrc = cfg->fc_prefsrc; fi->fib_nhs = nhs; change_nexthops(fi) { nh->nh_parent = fi; } endfor_nexthops(fi) if (cfg->fc_mx) { struct nlattr *nla; int remaining; nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { int type = nla_type(nla); if (type) { if (type > RTAX_MAX) goto err_inval; fi->fib_metrics[type - 1] = nla_get_u32(nla); } } } if (cfg->fc_mp) {#ifdef CONFIG_IP_ROUTE_MULTIPATH err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg); if (err != 0) goto failure; if (cfg->fc_oif && fi->fib_nh->nh_oif != cfg->fc_oif) goto err_inval; if (cfg->fc_gw && fi->fib_nh->nh_gw != cfg->fc_gw) goto err_inval;#ifdef CONFIG_NET_CLS_ROUTE if (cfg->fc_flow && fi->fib_nh->nh_tclassid != cfg->fc_flow) goto err_inval;#endif#else goto err_inval;#endif } else { struct fib_nh *nh = fi->fib_nh; nh->nh_oif = cfg->fc_oif; nh->nh_gw = cfg->fc_gw; nh->nh_flags = cfg->fc_flags;#ifdef CONFIG_NET_CLS_ROUTE nh->nh_tclassid = cfg->fc_flow;#endif#ifdef CONFIG_IP_ROUTE_MULTIPATH nh->nh_weight = 1;#endif } if (fib_props[cfg->fc_type].error) { if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) goto err_inval; goto link_it; } if (cfg->fc_scope > RT_SCOPE_HOST) goto err_inval; if (cfg->fc_scope == RT_SCOPE_HOST) { struct fib_nh *nh = fi->fib_nh; /* Local address is added. */ if (nhs != 1 || nh->nh_gw) goto err_inval; nh->nh_scope = RT_SCOPE_NOWHERE; nh->nh_dev = dev_get_by_index(&init_net, fi->fib_nh->nh_oif); err = -ENODEV; if (nh->nh_dev == NULL) goto failure; } else { change_nexthops(fi) { if ((err = fib_check_nh(cfg, fi, nh)) != 0) goto failure; } endfor_nexthops(fi) } if (fi->fib_prefsrc) { if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || fi->fib_prefsrc != cfg->fc_dst) if (inet_addr_type(fi->fib_prefsrc) != RTN_LOCAL) goto err_inval; }link_it: if ((ofi = fib_find_info(fi)) != NULL) { fi->fib_dead = 1; free_fib_info(fi); ofi->fib_treeref++; return ofi; } fi->fib_treeref++; atomic_inc(&fi->fib_clntref); spin_lock_bh(&fib_info_lock); hlist_add_head(&fi->fib_hash, &fib_info_hash[fib_info_hashfn(fi)]); if (fi->fib_prefsrc) { struct hlist_head *head; head = &fib_info_laddrhash[fib_laddr_hashfn(fi->fib_prefsrc)]; hlist_add_head(&fi->fib_lhash, head); } change_nexthops(fi) { struct hlist_head *head; unsigned int hash; if (!nh->nh_dev) continue; hash = fib_devindex_hashfn(nh->nh_dev->ifindex); head = &fib_info_devhash[hash]; hlist_add_head(&nh->nh_hash, head); } endfor_nexthops(fi) spin_unlock_bh(&fib_info_lock); return fi;err_inval: err = -EINVAL;failure: if (fi) { fi->fib_dead = 1; free_fib_info(fi); } return ERR_PTR(err);}/* Note! fib_semantic_match intentionally uses RCU list functions. */int fib_semantic_match(struct list_head *head, const struct flowi *flp, struct fib_result *res, __be32 zone, __be32 mask, int prefixlen){ struct fib_alias *fa; int nh_sel = 0; list_for_each_entry_rcu(fa, head, fa_list) { int err; if (fa->fa_tos && fa->fa_tos != flp->fl4_tos) continue; if (fa->fa_scope < flp->fl4_scope) continue; fa->fa_state |= FA_S_ACCESSED; err = fib_props[fa->fa_type].error; if (err == 0) { struct fib_info *fi = fa->fa_info; if (fi->fib_flags & RTNH_F_DEAD) continue; switch (fa->fa_type) { case RTN_UNICAST: case RTN_LOCAL: case RTN_BROADCAST: case RTN_ANYCAST: case RTN_MULTICAST: for_nexthops(fi) { if (nh->nh_flags&RTNH_F_DEAD) continue; if (!flp->oif || flp->oif == nh->nh_oif) break; }#ifdef CONFIG_IP_ROUTE_MULTIPATH if (nhsel < fi->fib_nhs) { nh_sel = nhsel; goto out_fill_res; }#else if (nhsel < 1) { goto out_fill_res; }#endif endfor_nexthops(fi); continue; default: printk(KERN_DEBUG "impossible 102\n"); return -EINVAL; } } return err; } return 1;out_fill_res: res->prefixlen = prefixlen; res->nh_sel = nh_sel; res->type = fa->fa_type; res->scope = fa->fa_scope; res->fi = fa->fa_info; atomic_inc(&res->fi->fib_clntref); return 0;}/* Find appropriate source address to this destination */__be32 __fib_res_prefsrc(struct fib_result *res){ return inet_select_addr(FIB_RES_DEV(*res), FIB_RES_GW(*res), res->scope);}int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event, u32 tb_id, u8 type, u8 scope, __be32 dst, int dst_len, u8 tos, struct fib_info *fi, unsigned int flags){ struct nlmsghdr *nlh; struct rtmsg *rtm; nlh = nlmsg_put(skb, pid, seq, event, sizeof(*rtm), flags); if (nlh == NULL) return -EMSGSIZE; rtm = nlmsg_data(nlh); rtm->rtm_family = AF_INET; rtm->rtm_dst_len = dst_len; rtm->rtm_src_len = 0; rtm->rtm_tos = tos; rtm->rtm_table = tb_id; NLA_PUT_U32(skb, RTA_TABLE, tb_id); rtm->rtm_type = type; rtm->rtm_flags = fi->fib_flags; rtm->rtm_scope = scope; rtm->rtm_protocol = fi->fib_protocol; if (rtm->rtm_dst_len) NLA_PUT_BE32(skb, RTA_DST, dst); if (fi->fib_priority) NLA_PUT_U32(skb, RTA_PRIORITY, fi->fib_priority); if (rtnetlink_put_metrics(skb, fi->fib_metrics) < 0) goto nla_put_failure; if (fi->fib_prefsrc) NLA_PUT_BE32(skb, RTA_PREFSRC, fi->fib_prefsrc); if (fi->fib_nhs == 1) { if (fi->fib_nh->nh_gw) NLA_PUT_BE32(skb, RTA_GATEWAY, fi->fib_nh->nh_gw); if (fi->fib_nh->nh_oif) NLA_PUT_U32(skb, RTA_OIF, fi->fib_nh->nh_oif);#ifdef CONFIG_NET_CLS_ROUTE if (fi->fib_nh[0].nh_tclassid) NLA_PUT_U32(skb, RTA_FLOW, fi->fib_nh[0].nh_tclassid);#endif }#ifdef CONFIG_IP_ROUTE_MULTIPATH if (fi->fib_nhs > 1) { struct rtnexthop *rtnh; struct nlattr *mp; mp = nla_nest_start(skb, RTA_MULTIPATH); if (mp == NULL) goto nla_put_failure; for_nexthops(fi) { rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh)); if (rtnh == NULL) goto nla_put_failure; rtnh->rtnh_flags = nh->nh_flags & 0xFF; rtnh->rtnh_hops = nh->nh_weight - 1; rtnh->rtnh_ifindex = nh->nh_oif; if (nh->nh_gw) NLA_PUT_BE32(skb, RTA_GATEWAY, nh->nh_gw);#ifdef CONFIG_NET_CLS_ROUTE if (nh->nh_tclassid) NLA_PUT_U32(skb, RTA_FLOW, nh->nh_tclassid);#endif /* length of rtnetlink header + attributes */ rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *) rtnh; } endfor_nexthops(fi); nla_nest_end(skb, mp); }#endif return nlmsg_end(skb, nlh);nla_put_failure: nlmsg_cancel(skb, nlh); return -EMSGSIZE;}/* Update FIB if: - local address disappeared -> we must delete all the entries referring to it. - device went down -> we must shutdown all nexthops going via it. */int fib_sync_down(__be32 local, struct net_device *dev, int force){ int ret = 0; int scope = RT_SCOPE_NOWHERE; if (force) scope = -1; if (local && fib_info_laddrhash) { unsigned int hash = fib_laddr_hashfn(local); struct hlist_head *head = &fib_info_laddrhash[hash]; struct hlist_node *node; struct fib_info *fi; hlist_for_each_entry(fi, node, head, fib_lhash) { if (fi->fib_prefsrc == local) { fi->fib_flags |= RTNH_F_DEAD; ret++; } } } if (dev) { struct fib_info *prev_fi = NULL; unsigned int hash = fib_devindex_hashfn(dev->ifindex); struct hlist_head *head = &fib_info_devhash[hash]; struct hlist_node *node; struct fib_nh *nh; hlist_for_each_entry(nh, node, head, nh_hash) { struct fib_info *fi = nh->nh_parent; int dead; BUG_ON(!fi->fib_nhs); if (nh->nh_dev != dev || fi == prev_fi) continue; prev_fi = fi; dead = 0; change_nexthops(fi) { if (nh->nh_flags&RTNH_F_DEAD) dead++; else if (nh->nh_dev == dev && nh->nh_scope != scope) { nh->nh_flags |= RTNH_F_DEAD;#ifdef CONFIG_IP_ROUTE_MULTIPATH spin_lock_bh(&fib_multipath_lock); fi->fib_power -= nh->nh_power; nh->nh_power = 0; spin_unlock_bh(&fib_multipath_lock);#endif dead++; }#ifdef CONFIG_IP_ROUTE_MULTIPATH if (force > 1 && nh->nh_dev == dev) { dead = fi->fib_nhs; break; }#endif } endfor_nexthops(fi) if (dead == fi->fib_nhs) { fi->fib_flags |= RTNH_F_DEAD; ret++; } } } return ret;}#ifdef CONFIG_IP_ROUTE_MULTIPATH/* Dead device goes up. We wake up dead nexthops. It takes sense only on multipath routes. */int fib_sync_up(struct net_device *dev){ struct fib_info *prev_fi; unsigned int hash; struct hlist_head *head; struct hlist_node *node; struct fib_nh *nh; int ret; if (!(dev->flags&IFF_UP)) return 0; prev_fi = NULL; hash = fib_devindex_hashfn(dev->ifindex); head = &fib_info_devhash[hash]; ret = 0; hlist_for_each_entry(nh, node, head, nh_hash) { struct fib_info *fi = nh->nh_parent; int alive; BUG_ON(!fi->fib_nhs); if (nh->nh_dev != dev || fi == prev_fi) continue; prev_fi = fi; alive = 0; change_nexthops(fi) { if (!(nh->nh_flags&RTNH_F_DEAD)) { alive++; continue; } if (nh->nh_dev == NULL || !(nh->nh_dev->flags&IFF_UP)) continue; if (nh->nh_dev != dev || !__in_dev_get_rtnl(dev)) continue; alive++; spin_lock_bh(&fib_multipath_lock); nh->nh_power = 0; nh->nh_flags &= ~RTNH_F_DEAD; spin_unlock_bh(&fib_multipath_lock); } endfor_nexthops(fi) if (alive > 0) { fi->fib_flags &= ~RTNH_F_DEAD; ret++; } } return ret;}/* The algorithm is suboptimal, but it provides really fair weighted route distribution. */void fib_select_multipath(const struct flowi *flp, struct fib_result *res){ struct fib_info *fi = res->fi; int w; spin_lock_bh(&fib_multipath_lock); if (fi->fib_power <= 0) { int power = 0; change_nexthops(fi) { if (!(nh->nh_flags&RTNH_F_DEAD)) { power += nh->nh_weight; nh->nh_power = nh->nh_weight; } } endfor_nexthops(fi); fi->fib_power = power; if (power <= 0) { spin_unlock_bh(&fib_multipath_lock); /* Race condition: route has just become dead. */ res->nh_sel = 0; return; } } /* w should be random number [0..fi->fib_power-1], it is pretty bad approximation. */ w = jiffies % fi->fib_power; change_nexthops(fi) { if (!(nh->nh_flags&RTNH_F_DEAD) && nh->nh_power) { if ((w -= nh->nh_power) <= 0) { nh->nh_power--; fi->fib_power--; res->nh_sel = nhsel; spin_unlock_bh(&fib_multipath_lock); return; } } } endfor_nexthops(fi); /* Race condition: route has just become dead. */ res->nh_sel = 0; spin_unlock_bh(&fib_multipath_lock);}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -