ipv4: Plumb support for nexthop object in a fib_info
Add 'struct nexthop' and nh_list list_head to fib_info. nh_list is the fib_info side of the nexthop <-> fib_info relationship. Add fi_list list_head to 'struct nexthop' to track fib_info entries using a nexthop instance. Add __remove_nexthop_fib and add it to __remove_nexthop to walk the new list_head and mark those fib entries as dead when the nexthop is deleted. Add a few nexthop helpers for use when a nexthop is added to fib_info: - nexthop_cmp to determine if 2 nexthops are the same - nexthop_path_fib_result to select a path for a multipath 'struct nexthop' - nexthop_fib_nhc to select a specific fib_nh_common within a multipath 'struct nexthop' Update existing fib_info_nhc to use nexthop_fib_nhc if a fib_info uses a 'struct nexthop', and mark fib_info_nh as only used for the non-nexthop case. Update the fib_info functions to check for fi->nh and take a different path as needed: - free_fib_info_rcu - put the nexthop object reference - fib_release_info - remove the fib_info from the nexthop's fi_list - nh_comp - use nexthop_cmp when either fib_info references a nexthop object - fib_info_hashfn - use the nexthop id for the hashing vs the oif of each fib_nh in a fib_info - fib_nlmsg_size - add space for the RTA_NH_ID attribute - fib_create_info - verify nexthop reference can be taken, verify nexthop spec is valid for fib entry, and add fib_info to fi_list for a nexthop - fib_select_multipath - use the new nexthop_path_fib_result to select a path when nexthop objects are used - fib_table_lookup - if the 'struct nexthop' is a blackhole nexthop, treat it the same as a fib entry using 'blackhole' The bulk of the changes are in fib_semantics.c and most of that is moving the existing change_nexthops into an else branch. Update the nexthop code to walk fi_list on a nexthop deleted to remove fib entries referencing it. Signed-off-by: David Ahern <dsahern@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
dcb1ecb50e
commit
4c7e8084fd
@@ -236,9 +236,13 @@ static void free_fib_info_rcu(struct rcu_head *head)
|
||||
{
|
||||
struct fib_info *fi = container_of(head, struct fib_info, rcu);
|
||||
|
||||
change_nexthops(fi) {
|
||||
fib_nh_release(fi->fib_net, nexthop_nh);
|
||||
} endfor_nexthops(fi);
|
||||
if (fi->nh) {
|
||||
nexthop_put(fi->nh);
|
||||
} else {
|
||||
change_nexthops(fi) {
|
||||
fib_nh_release(fi->fib_net, nexthop_nh);
|
||||
} endfor_nexthops(fi);
|
||||
}
|
||||
|
||||
ip_fib_metrics_put(fi->fib_metrics);
|
||||
|
||||
@@ -264,11 +268,15 @@ void fib_release_info(struct fib_info *fi)
|
||||
hlist_del(&fi->fib_hash);
|
||||
if (fi->fib_prefsrc)
|
||||
hlist_del(&fi->fib_lhash);
|
||||
change_nexthops(fi) {
|
||||
if (!nexthop_nh->fib_nh_dev)
|
||||
continue;
|
||||
hlist_del(&nexthop_nh->nh_hash);
|
||||
} endfor_nexthops(fi)
|
||||
if (fi->nh) {
|
||||
list_del(&fi->nh_list);
|
||||
} else {
|
||||
change_nexthops(fi) {
|
||||
if (!nexthop_nh->fib_nh_dev)
|
||||
continue;
|
||||
hlist_del(&nexthop_nh->nh_hash);
|
||||
} endfor_nexthops(fi)
|
||||
}
|
||||
fi->fib_dead = 1;
|
||||
fib_info_put(fi);
|
||||
}
|
||||
@@ -279,6 +287,12 @@ static inline int nh_comp(struct fib_info *fi, struct fib_info *ofi)
|
||||
{
|
||||
const struct fib_nh *onh;
|
||||
|
||||
if (fi->nh || ofi->nh)
|
||||
return nexthop_cmp(fi->nh, ofi->nh) ? 0 : -1;
|
||||
|
||||
if (ofi->fib_nhs == 0)
|
||||
return 0;
|
||||
|
||||
for_nexthops(fi) {
|
||||
onh = fib_info_nh(ofi, nhsel);
|
||||
|
||||
@@ -323,9 +337,14 @@ static inline unsigned int fib_info_hashfn(const struct fib_info *fi)
|
||||
val ^= (fi->fib_protocol << 8) | fi->fib_scope;
|
||||
val ^= (__force u32)fi->fib_prefsrc;
|
||||
val ^= fi->fib_priority;
|
||||
for_nexthops(fi) {
|
||||
val ^= fib_devindex_hashfn(nh->fib_nh_oif);
|
||||
} endfor_nexthops(fi)
|
||||
|
||||
if (fi->nh) {
|
||||
val ^= fib_devindex_hashfn(fi->nh->id);
|
||||
} else {
|
||||
for_nexthops(fi) {
|
||||
val ^= fib_devindex_hashfn(nh->fib_nh_oif);
|
||||
} endfor_nexthops(fi)
|
||||
}
|
||||
|
||||
return (val ^ (val >> 7) ^ (val >> 12)) & mask;
|
||||
}
|
||||
@@ -352,7 +371,7 @@ static struct fib_info *fib_find_info(struct fib_info *nfi)
|
||||
memcmp(nfi->fib_metrics, fi->fib_metrics,
|
||||
sizeof(u32) * RTAX_MAX) == 0 &&
|
||||
!((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_COMPARE_MASK) &&
|
||||
(nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0))
|
||||
nh_comp(fi, nfi) == 0)
|
||||
return fi;
|
||||
}
|
||||
|
||||
@@ -399,6 +418,9 @@ static inline size_t fib_nlmsg_size(struct fib_info *fi)
|
||||
/* space for nested metrics */
|
||||
payload += nla_total_size((RTAX_MAX * nla_total_size(4)));
|
||||
|
||||
if (fi->nh)
|
||||
payload += nla_total_size(4); /* RTA_NH_ID */
|
||||
|
||||
if (nhs) {
|
||||
size_t nh_encapsize = 0;
|
||||
/* Also handles the special case nhs == 1 */
|
||||
@@ -585,6 +607,7 @@ static int fib_count_nexthops(struct rtnexthop *rtnh, int remaining,
|
||||
return nhs;
|
||||
}
|
||||
|
||||
/* only called when fib_nh is integrated into fib_info */
|
||||
static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh,
|
||||
int remaining, struct fib_config *cfg,
|
||||
struct netlink_ext_ack *extack)
|
||||
@@ -683,6 +706,7 @@ errout:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* only called when fib_nh is integrated into fib_info */
|
||||
static void fib_rebalance(struct fib_info *fi)
|
||||
{
|
||||
int total;
|
||||
@@ -1262,6 +1286,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg,
|
||||
{
|
||||
int err;
|
||||
struct fib_info *fi = NULL;
|
||||
struct nexthop *nh = NULL;
|
||||
struct fib_info *ofi;
|
||||
int nhs = 1;
|
||||
struct net *net = cfg->fc_nlinfo.nl_net;
|
||||
@@ -1333,14 +1358,25 @@ struct fib_info *fib_create_info(struct fib_config *cfg,
|
||||
fi->fib_tb_id = cfg->fc_table;
|
||||
|
||||
fi->fib_nhs = nhs;
|
||||
change_nexthops(fi) {
|
||||
nexthop_nh->nh_parent = fi;
|
||||
} endfor_nexthops(fi)
|
||||
if (nh) {
|
||||
if (!nexthop_get(nh)) {
|
||||
NL_SET_ERR_MSG(extack, "Nexthop has been deleted");
|
||||
err = -EINVAL;
|
||||
} else {
|
||||
err = 0;
|
||||
fi->nh = nh;
|
||||
}
|
||||
} else {
|
||||
change_nexthops(fi) {
|
||||
nexthop_nh->nh_parent = fi;
|
||||
} endfor_nexthops(fi)
|
||||
|
||||
if (cfg->fc_mp)
|
||||
err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg, extack);
|
||||
else
|
||||
err = fib_nh_init(net, fi->fib_nh, cfg, 1, extack);
|
||||
if (cfg->fc_mp)
|
||||
err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg,
|
||||
extack);
|
||||
else
|
||||
err = fib_nh_init(net, fi->fib_nh, cfg, 1, extack);
|
||||
}
|
||||
|
||||
if (err != 0)
|
||||
goto failure;
|
||||
@@ -1371,7 +1407,11 @@ struct fib_info *fib_create_info(struct fib_config *cfg,
|
||||
goto err_inval;
|
||||
}
|
||||
|
||||
if (cfg->fc_scope == RT_SCOPE_HOST) {
|
||||
if (fi->nh) {
|
||||
err = fib_check_nexthop(fi->nh, cfg->fc_scope, extack);
|
||||
if (err)
|
||||
goto failure;
|
||||
} else if (cfg->fc_scope == RT_SCOPE_HOST) {
|
||||
struct fib_nh *nh = fi->fib_nh;
|
||||
|
||||
/* Local address is added. */
|
||||
@@ -1411,14 +1451,16 @@ struct fib_info *fib_create_info(struct fib_config *cfg,
|
||||
goto err_inval;
|
||||
}
|
||||
|
||||
change_nexthops(fi) {
|
||||
fib_info_update_nhc_saddr(net, &nexthop_nh->nh_common,
|
||||
fi->fib_scope);
|
||||
if (nexthop_nh->fib_nh_gw_family == AF_INET6)
|
||||
fi->fib_nh_is_v6 = true;
|
||||
} endfor_nexthops(fi)
|
||||
if (!fi->nh) {
|
||||
change_nexthops(fi) {
|
||||
fib_info_update_nhc_saddr(net, &nexthop_nh->nh_common,
|
||||
fi->fib_scope);
|
||||
if (nexthop_nh->fib_nh_gw_family == AF_INET6)
|
||||
fi->fib_nh_is_v6 = true;
|
||||
} endfor_nexthops(fi)
|
||||
|
||||
fib_rebalance(fi);
|
||||
fib_rebalance(fi);
|
||||
}
|
||||
|
||||
link_it:
|
||||
ofi = fib_find_info(fi);
|
||||
@@ -1440,16 +1482,20 @@ link_it:
|
||||
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 (fi->nh) {
|
||||
list_add(&fi->nh_list, &nh->fi_list);
|
||||
} else {
|
||||
change_nexthops(fi) {
|
||||
struct hlist_head *head;
|
||||
unsigned int hash;
|
||||
|
||||
if (!nexthop_nh->fib_nh_dev)
|
||||
continue;
|
||||
hash = fib_devindex_hashfn(nexthop_nh->fib_nh_dev->ifindex);
|
||||
head = &fib_info_devhash[hash];
|
||||
hlist_add_head(&nexthop_nh->nh_hash, head);
|
||||
} endfor_nexthops(fi)
|
||||
if (!nexthop_nh->fib_nh_dev)
|
||||
continue;
|
||||
hash = fib_devindex_hashfn(nexthop_nh->fib_nh_dev->ifindex);
|
||||
head = &fib_info_devhash[hash];
|
||||
hlist_add_head(&nexthop_nh->nh_hash, head);
|
||||
} endfor_nexthops(fi)
|
||||
}
|
||||
spin_unlock_bh(&fib_info_lock);
|
||||
return fi;
|
||||
|
||||
@@ -1576,6 +1622,12 @@ static int fib_add_multipath(struct sk_buff *skb, struct fib_info *fi)
|
||||
if (!mp)
|
||||
goto nla_put_failure;
|
||||
|
||||
if (unlikely(fi->nh)) {
|
||||
if (nexthop_mpath_fill_node(skb, fi->nh) < 0)
|
||||
goto nla_put_failure;
|
||||
goto mp_end;
|
||||
}
|
||||
|
||||
for_nexthops(fi) {
|
||||
if (fib_add_nexthop(skb, &nh->nh_common, nh->fib_nh_weight) < 0)
|
||||
goto nla_put_failure;
|
||||
@@ -1586,6 +1638,7 @@ static int fib_add_multipath(struct sk_buff *skb, struct fib_info *fi)
|
||||
#endif
|
||||
} endfor_nexthops(fi);
|
||||
|
||||
mp_end:
|
||||
nla_nest_end(skb, mp);
|
||||
|
||||
return 0;
|
||||
@@ -1640,6 +1693,14 @@ int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event,
|
||||
if (fi->fib_prefsrc &&
|
||||
nla_put_in_addr(skb, RTA_PREFSRC, fi->fib_prefsrc))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (fi->nh) {
|
||||
if (nla_put_u32(skb, RTA_NH_ID, fi->nh->id))
|
||||
goto nla_put_failure;
|
||||
if (nexthop_is_blackhole(fi->nh))
|
||||
rtm->rtm_type = RTN_BLACKHOLE;
|
||||
}
|
||||
|
||||
if (nhs == 1) {
|
||||
const struct fib_nh_common *nhc = fib_info_nhc(fi, 0);
|
||||
unsigned char flags = 0;
|
||||
@@ -1784,6 +1845,8 @@ void fib_sync_mtu(struct net_device *dev, u32 orig_mtu)
|
||||
* NETDEV_DOWN 0 LINKDOWN|DEAD Link down, not for scope host
|
||||
* NETDEV_DOWN 1 LINKDOWN|DEAD Last address removed
|
||||
* NETDEV_UNREGISTER 1 LINKDOWN|DEAD Device removed
|
||||
*
|
||||
* only used when fib_nh is built into fib_info
|
||||
*/
|
||||
int fib_sync_down_dev(struct net_device *dev, unsigned long event, bool force)
|
||||
{
|
||||
@@ -1931,6 +1994,8 @@ out:
|
||||
/*
|
||||
* Dead device goes up. We wake up dead nexthops.
|
||||
* It takes sense only on multipath routes.
|
||||
*
|
||||
* only used when fib_nh is built into fib_info
|
||||
*/
|
||||
int fib_sync_up(struct net_device *dev, unsigned char nh_flags)
|
||||
{
|
||||
@@ -2025,6 +2090,11 @@ void fib_select_multipath(struct fib_result *res, int hash)
|
||||
struct net *net = fi->fib_net;
|
||||
bool first = false;
|
||||
|
||||
if (unlikely(res->fi->nh)) {
|
||||
nexthop_path_fib_result(res, hash);
|
||||
return;
|
||||
}
|
||||
|
||||
change_nexthops(fi) {
|
||||
if (net->ipv4.sysctl_fib_multipath_use_neigh) {
|
||||
if (!fib_good_nh(nexthop_nh))
|
||||
|
||||
Reference in New Issue
Block a user