Skip to content

Commit 4c7e808

Browse files
dsaherndavem330
authored andcommitted
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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent dcb1ecb commit 4c7e808

File tree

5 files changed

+229
-36
lines changed

5 files changed

+229
-36
lines changed

include/net/ip_fib.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,12 @@ struct fib_nh {
129129
* This structure contains data shared by many of routes.
130130
*/
131131

132+
struct nexthop;
133+
132134
struct fib_info {
133135
struct hlist_node fib_hash;
134136
struct hlist_node fib_lhash;
137+
struct list_head nh_list;
135138
struct net *fib_net;
136139
int fib_treeref;
137140
refcount_t fib_clntref;
@@ -151,6 +154,7 @@ struct fib_info {
151154
int fib_nhs;
152155
bool fib_nh_is_v6;
153156
bool nh_updated;
157+
struct nexthop *nh;
154158
struct rcu_head rcu;
155159
struct fib_nh fib_nh[0];
156160
};

include/net/nexthop.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ struct nh_group {
7777

7878
struct nexthop {
7979
struct rb_node rb_node; /* entry on netns rbtree */
80+
struct list_head fi_list; /* v4 entries using nh */
8081
struct list_head grp_list; /* nh group entries using this nh */
8182
struct net *net;
8283

@@ -110,6 +111,12 @@ static inline void nexthop_put(struct nexthop *nh)
110111
call_rcu(&nh->rcu, nexthop_free_rcu);
111112
}
112113

114+
static inline bool nexthop_cmp(const struct nexthop *nh1,
115+
const struct nexthop *nh2)
116+
{
117+
return nh1 == nh2;
118+
}
119+
113120
static inline bool nexthop_is_multipath(const struct nexthop *nh)
114121
{
115122
if (nh->is_group) {
@@ -193,18 +200,59 @@ static inline bool nexthop_is_blackhole(const struct nexthop *nh)
193200
return nhi->reject_nh;
194201
}
195202

203+
static inline void nexthop_path_fib_result(struct fib_result *res, int hash)
204+
{
205+
struct nh_info *nhi;
206+
struct nexthop *nh;
207+
208+
nh = nexthop_select_path(res->fi->nh, hash);
209+
nhi = rcu_dereference(nh->nh_info);
210+
res->nhc = &nhi->fib_nhc;
211+
}
212+
213+
/* called with rcu read lock or rtnl held */
214+
static inline
215+
struct fib_nh_common *nexthop_fib_nhc(struct nexthop *nh, int nhsel)
216+
{
217+
struct nh_info *nhi;
218+
219+
BUILD_BUG_ON(offsetof(struct fib_nh, nh_common) != 0);
220+
BUILD_BUG_ON(offsetof(struct fib6_nh, nh_common) != 0);
221+
222+
if (nexthop_is_multipath(nh)) {
223+
nh = nexthop_mpath_select(nh, nhsel);
224+
if (!nh)
225+
return NULL;
226+
}
227+
228+
nhi = rcu_dereference_rtnl(nh->nh_info);
229+
return &nhi->fib_nhc;
230+
}
231+
196232
static inline unsigned int fib_info_num_path(const struct fib_info *fi)
197233
{
234+
if (unlikely(fi->nh))
235+
return nexthop_num_path(fi->nh);
236+
198237
return fi->fib_nhs;
199238
}
200239

240+
int fib_check_nexthop(struct nexthop *nh, u8 scope,
241+
struct netlink_ext_ack *extack);
242+
201243
static inline struct fib_nh_common *fib_info_nhc(struct fib_info *fi, int nhsel)
202244
{
245+
if (unlikely(fi->nh))
246+
return nexthop_fib_nhc(fi->nh, nhsel);
247+
203248
return &fi->fib_nh[nhsel].nh_common;
204249
}
205250

251+
/* only used when fib_nh is built into fib_info */
206252
static inline struct fib_nh *fib_info_nh(struct fib_info *fi, int nhsel)
207253
{
254+
WARN_ON(fi->nh);
255+
208256
return &fi->fib_nh[nhsel];
209257
}
210258
#endif

net/ipv4/fib_semantics.c

Lines changed: 106 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -236,9 +236,13 @@ static void free_fib_info_rcu(struct rcu_head *head)
236236
{
237237
struct fib_info *fi = container_of(head, struct fib_info, rcu);
238238

239-
change_nexthops(fi) {
240-
fib_nh_release(fi->fib_net, nexthop_nh);
241-
} endfor_nexthops(fi);
239+
if (fi->nh) {
240+
nexthop_put(fi->nh);
241+
} else {
242+
change_nexthops(fi) {
243+
fib_nh_release(fi->fib_net, nexthop_nh);
244+
} endfor_nexthops(fi);
245+
}
242246

243247
ip_fib_metrics_put(fi->fib_metrics);
244248

@@ -264,11 +268,15 @@ void fib_release_info(struct fib_info *fi)
264268
hlist_del(&fi->fib_hash);
265269
if (fi->fib_prefsrc)
266270
hlist_del(&fi->fib_lhash);
267-
change_nexthops(fi) {
268-
if (!nexthop_nh->fib_nh_dev)
269-
continue;
270-
hlist_del(&nexthop_nh->nh_hash);
271-
} endfor_nexthops(fi)
271+
if (fi->nh) {
272+
list_del(&fi->nh_list);
273+
} else {
274+
change_nexthops(fi) {
275+
if (!nexthop_nh->fib_nh_dev)
276+
continue;
277+
hlist_del(&nexthop_nh->nh_hash);
278+
} endfor_nexthops(fi)
279+
}
272280
fi->fib_dead = 1;
273281
fib_info_put(fi);
274282
}
@@ -279,6 +287,12 @@ static inline int nh_comp(struct fib_info *fi, struct fib_info *ofi)
279287
{
280288
const struct fib_nh *onh;
281289

290+
if (fi->nh || ofi->nh)
291+
return nexthop_cmp(fi->nh, ofi->nh) ? 0 : -1;
292+
293+
if (ofi->fib_nhs == 0)
294+
return 0;
295+
282296
for_nexthops(fi) {
283297
onh = fib_info_nh(ofi, nhsel);
284298

@@ -323,9 +337,14 @@ static inline unsigned int fib_info_hashfn(const struct fib_info *fi)
323337
val ^= (fi->fib_protocol << 8) | fi->fib_scope;
324338
val ^= (__force u32)fi->fib_prefsrc;
325339
val ^= fi->fib_priority;
326-
for_nexthops(fi) {
327-
val ^= fib_devindex_hashfn(nh->fib_nh_oif);
328-
} endfor_nexthops(fi)
340+
341+
if (fi->nh) {
342+
val ^= fib_devindex_hashfn(fi->nh->id);
343+
} else {
344+
for_nexthops(fi) {
345+
val ^= fib_devindex_hashfn(nh->fib_nh_oif);
346+
} endfor_nexthops(fi)
347+
}
329348

330349
return (val ^ (val >> 7) ^ (val >> 12)) & mask;
331350
}
@@ -352,7 +371,7 @@ static struct fib_info *fib_find_info(struct fib_info *nfi)
352371
memcmp(nfi->fib_metrics, fi->fib_metrics,
353372
sizeof(u32) * RTAX_MAX) == 0 &&
354373
!((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_COMPARE_MASK) &&
355-
(nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0))
374+
nh_comp(fi, nfi) == 0)
356375
return fi;
357376
}
358377

@@ -399,6 +418,9 @@ static inline size_t fib_nlmsg_size(struct fib_info *fi)
399418
/* space for nested metrics */
400419
payload += nla_total_size((RTAX_MAX * nla_total_size(4)));
401420

421+
if (fi->nh)
422+
payload += nla_total_size(4); /* RTA_NH_ID */
423+
402424
if (nhs) {
403425
size_t nh_encapsize = 0;
404426
/* Also handles the special case nhs == 1 */
@@ -585,6 +607,7 @@ static int fib_count_nexthops(struct rtnexthop *rtnh, int remaining,
585607
return nhs;
586608
}
587609

610+
/* only called when fib_nh is integrated into fib_info */
588611
static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh,
589612
int remaining, struct fib_config *cfg,
590613
struct netlink_ext_ack *extack)
@@ -683,6 +706,7 @@ static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh,
683706
return ret;
684707
}
685708

709+
/* only called when fib_nh is integrated into fib_info */
686710
static void fib_rebalance(struct fib_info *fi)
687711
{
688712
int total;
@@ -1262,6 +1286,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg,
12621286
{
12631287
int err;
12641288
struct fib_info *fi = NULL;
1289+
struct nexthop *nh = NULL;
12651290
struct fib_info *ofi;
12661291
int nhs = 1;
12671292
struct net *net = cfg->fc_nlinfo.nl_net;
@@ -1333,14 +1358,25 @@ struct fib_info *fib_create_info(struct fib_config *cfg,
13331358
fi->fib_tb_id = cfg->fc_table;
13341359

13351360
fi->fib_nhs = nhs;
1336-
change_nexthops(fi) {
1337-
nexthop_nh->nh_parent = fi;
1338-
} endfor_nexthops(fi)
1361+
if (nh) {
1362+
if (!nexthop_get(nh)) {
1363+
NL_SET_ERR_MSG(extack, "Nexthop has been deleted");
1364+
err = -EINVAL;
1365+
} else {
1366+
err = 0;
1367+
fi->nh = nh;
1368+
}
1369+
} else {
1370+
change_nexthops(fi) {
1371+
nexthop_nh->nh_parent = fi;
1372+
} endfor_nexthops(fi)
13391373

1340-
if (cfg->fc_mp)
1341-
err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg, extack);
1342-
else
1343-
err = fib_nh_init(net, fi->fib_nh, cfg, 1, extack);
1374+
if (cfg->fc_mp)
1375+
err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg,
1376+
extack);
1377+
else
1378+
err = fib_nh_init(net, fi->fib_nh, cfg, 1, extack);
1379+
}
13441380

13451381
if (err != 0)
13461382
goto failure;
@@ -1371,7 +1407,11 @@ struct fib_info *fib_create_info(struct fib_config *cfg,
13711407
goto err_inval;
13721408
}
13731409

1374-
if (cfg->fc_scope == RT_SCOPE_HOST) {
1410+
if (fi->nh) {
1411+
err = fib_check_nexthop(fi->nh, cfg->fc_scope, extack);
1412+
if (err)
1413+
goto failure;
1414+
} else if (cfg->fc_scope == RT_SCOPE_HOST) {
13751415
struct fib_nh *nh = fi->fib_nh;
13761416

13771417
/* Local address is added. */
@@ -1411,14 +1451,16 @@ struct fib_info *fib_create_info(struct fib_config *cfg,
14111451
goto err_inval;
14121452
}
14131453

1414-
change_nexthops(fi) {
1415-
fib_info_update_nhc_saddr(net, &nexthop_nh->nh_common,
1416-
fi->fib_scope);
1417-
if (nexthop_nh->fib_nh_gw_family == AF_INET6)
1418-
fi->fib_nh_is_v6 = true;
1419-
} endfor_nexthops(fi)
1454+
if (!fi->nh) {
1455+
change_nexthops(fi) {
1456+
fib_info_update_nhc_saddr(net, &nexthop_nh->nh_common,
1457+
fi->fib_scope);
1458+
if (nexthop_nh->fib_nh_gw_family == AF_INET6)
1459+
fi->fib_nh_is_v6 = true;
1460+
} endfor_nexthops(fi)
14201461

1421-
fib_rebalance(fi);
1462+
fib_rebalance(fi);
1463+
}
14221464

14231465
link_it:
14241466
ofi = fib_find_info(fi);
@@ -1440,16 +1482,20 @@ struct fib_info *fib_create_info(struct fib_config *cfg,
14401482
head = &fib_info_laddrhash[fib_laddr_hashfn(fi->fib_prefsrc)];
14411483
hlist_add_head(&fi->fib_lhash, head);
14421484
}
1443-
change_nexthops(fi) {
1444-
struct hlist_head *head;
1445-
unsigned int hash;
1485+
if (fi->nh) {
1486+
list_add(&fi->nh_list, &nh->fi_list);
1487+
} else {
1488+
change_nexthops(fi) {
1489+
struct hlist_head *head;
1490+
unsigned int hash;
14461491

1447-
if (!nexthop_nh->fib_nh_dev)
1448-
continue;
1449-
hash = fib_devindex_hashfn(nexthop_nh->fib_nh_dev->ifindex);
1450-
head = &fib_info_devhash[hash];
1451-
hlist_add_head(&nexthop_nh->nh_hash, head);
1452-
} endfor_nexthops(fi)
1492+
if (!nexthop_nh->fib_nh_dev)
1493+
continue;
1494+
hash = fib_devindex_hashfn(nexthop_nh->fib_nh_dev->ifindex);
1495+
head = &fib_info_devhash[hash];
1496+
hlist_add_head(&nexthop_nh->nh_hash, head);
1497+
} endfor_nexthops(fi)
1498+
}
14531499
spin_unlock_bh(&fib_info_lock);
14541500
return fi;
14551501

@@ -1576,6 +1622,12 @@ static int fib_add_multipath(struct sk_buff *skb, struct fib_info *fi)
15761622
if (!mp)
15771623
goto nla_put_failure;
15781624

1625+
if (unlikely(fi->nh)) {
1626+
if (nexthop_mpath_fill_node(skb, fi->nh) < 0)
1627+
goto nla_put_failure;
1628+
goto mp_end;
1629+
}
1630+
15791631
for_nexthops(fi) {
15801632
if (fib_add_nexthop(skb, &nh->nh_common, nh->fib_nh_weight) < 0)
15811633
goto nla_put_failure;
@@ -1586,6 +1638,7 @@ static int fib_add_multipath(struct sk_buff *skb, struct fib_info *fi)
15861638
#endif
15871639
} endfor_nexthops(fi);
15881640

1641+
mp_end:
15891642
nla_nest_end(skb, mp);
15901643

15911644
return 0;
@@ -1640,6 +1693,14 @@ int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event,
16401693
if (fi->fib_prefsrc &&
16411694
nla_put_in_addr(skb, RTA_PREFSRC, fi->fib_prefsrc))
16421695
goto nla_put_failure;
1696+
1697+
if (fi->nh) {
1698+
if (nla_put_u32(skb, RTA_NH_ID, fi->nh->id))
1699+
goto nla_put_failure;
1700+
if (nexthop_is_blackhole(fi->nh))
1701+
rtm->rtm_type = RTN_BLACKHOLE;
1702+
}
1703+
16431704
if (nhs == 1) {
16441705
const struct fib_nh_common *nhc = fib_info_nhc(fi, 0);
16451706
unsigned char flags = 0;
@@ -1784,6 +1845,8 @@ void fib_sync_mtu(struct net_device *dev, u32 orig_mtu)
17841845
* NETDEV_DOWN 0 LINKDOWN|DEAD Link down, not for scope host
17851846
* NETDEV_DOWN 1 LINKDOWN|DEAD Last address removed
17861847
* NETDEV_UNREGISTER 1 LINKDOWN|DEAD Device removed
1848+
*
1849+
* only used when fib_nh is built into fib_info
17871850
*/
17881851
int fib_sync_down_dev(struct net_device *dev, unsigned long event, bool force)
17891852
{
@@ -1931,6 +1994,8 @@ static void fib_select_default(const struct flowi4 *flp, struct fib_result *res)
19311994
/*
19321995
* Dead device goes up. We wake up dead nexthops.
19331996
* It takes sense only on multipath routes.
1997+
*
1998+
* only used when fib_nh is built into fib_info
19341999
*/
19352000
int fib_sync_up(struct net_device *dev, unsigned char nh_flags)
19362001
{
@@ -2025,6 +2090,11 @@ void fib_select_multipath(struct fib_result *res, int hash)
20252090
struct net *net = fi->fib_net;
20262091
bool first = false;
20272092

2093+
if (unlikely(res->fi->nh)) {
2094+
nexthop_path_fib_result(res, hash);
2095+
return;
2096+
}
2097+
20282098
change_nexthops(fi) {
20292099
if (net->ipv4.sysctl_fib_multipath_use_neigh) {
20302100
if (!fib_good_nh(nexthop_nh))

0 commit comments

Comments
 (0)