xref: /kernel/linux/linux-5.10/include/net/nexthop.h (revision 8c2ecf20)
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Generic nexthop implementation
4 *
5 * Copyright (c) 2017-19 Cumulus Networks
6 * Copyright (c) 2017-19 David Ahern <dsa@cumulusnetworks.com>
7 */
8
9#ifndef __LINUX_NEXTHOP_H
10#define __LINUX_NEXTHOP_H
11
12#include <linux/netdevice.h>
13#include <linux/notifier.h>
14#include <linux/route.h>
15#include <linux/types.h>
16#include <net/ip_fib.h>
17#include <net/ip6_fib.h>
18#include <net/netlink.h>
19
20#define NEXTHOP_VALID_USER_FLAGS RTNH_F_ONLINK
21
22struct nexthop;
23
24struct nh_config {
25	u32		nh_id;
26
27	u8		nh_family;
28	u8		nh_protocol;
29	u8		nh_blackhole;
30	u8		nh_fdb;
31	u32		nh_flags;
32
33	int		nh_ifindex;
34	struct net_device *dev;
35
36	union {
37		__be32		ipv4;
38		struct in6_addr	ipv6;
39	} gw;
40
41	struct nlattr	*nh_grp;
42	u16		nh_grp_type;
43
44	struct nlattr	*nh_encap;
45	u16		nh_encap_type;
46
47	u32		nlflags;
48	struct nl_info	nlinfo;
49};
50
51struct nh_info {
52	struct hlist_node	dev_hash;    /* entry on netns devhash */
53	struct nexthop		*nh_parent;
54
55	u8			family;
56	bool			reject_nh;
57	bool			fdb_nh;
58
59	union {
60		struct fib_nh_common	fib_nhc;
61		struct fib_nh		fib_nh;
62		struct fib6_nh		fib6_nh;
63	};
64};
65
66struct nh_grp_entry {
67	struct nexthop	*nh;
68	u8		weight;
69	atomic_t	upper_bound;
70
71	struct list_head nh_list;
72	struct nexthop	*nh_parent;  /* nexthop of group with this entry */
73};
74
75struct nh_group {
76	struct nh_group		*spare; /* spare group for removals */
77	u16			num_nh;
78	bool			mpath;
79	bool			fdb_nh;
80	bool			has_v4;
81	struct nh_grp_entry	nh_entries[];
82};
83
84struct nexthop {
85	struct rb_node		rb_node;    /* entry on netns rbtree */
86	struct list_head	fi_list;    /* v4 entries using nh */
87	struct list_head	f6i_list;   /* v6 entries using nh */
88	struct list_head        fdb_list;   /* fdb entries using this nh */
89	struct list_head	grp_list;   /* nh group entries using this nh */
90	struct net		*net;
91
92	u32			id;
93
94	u8			protocol;   /* app managing this nh */
95	u8			nh_flags;
96	bool			is_group;
97
98	refcount_t		refcnt;
99	struct rcu_head		rcu;
100
101	union {
102		struct nh_info	__rcu *nh_info;
103		struct nh_group __rcu *nh_grp;
104	};
105};
106
107enum nexthop_event_type {
108	NEXTHOP_EVENT_DEL
109};
110
111int register_nexthop_notifier(struct net *net, struct notifier_block *nb);
112int unregister_nexthop_notifier(struct net *net, struct notifier_block *nb);
113
114/* caller is holding rcu or rtnl; no reference taken to nexthop */
115struct nexthop *nexthop_find_by_id(struct net *net, u32 id);
116void nexthop_free_rcu(struct rcu_head *head);
117
118static inline bool nexthop_get(struct nexthop *nh)
119{
120	return refcount_inc_not_zero(&nh->refcnt);
121}
122
123static inline void nexthop_put(struct nexthop *nh)
124{
125	if (refcount_dec_and_test(&nh->refcnt))
126		call_rcu(&nh->rcu, nexthop_free_rcu);
127}
128
129static inline bool nexthop_cmp(const struct nexthop *nh1,
130			       const struct nexthop *nh2)
131{
132	return nh1 == nh2;
133}
134
135static inline bool nexthop_is_fdb(const struct nexthop *nh)
136{
137	if (nh->is_group) {
138		const struct nh_group *nh_grp;
139
140		nh_grp = rcu_dereference_rtnl(nh->nh_grp);
141		return nh_grp->fdb_nh;
142	} else {
143		const struct nh_info *nhi;
144
145		nhi = rcu_dereference_rtnl(nh->nh_info);
146		return nhi->fdb_nh;
147	}
148}
149
150static inline bool nexthop_has_v4(const struct nexthop *nh)
151{
152	if (nh->is_group) {
153		struct nh_group *nh_grp;
154
155		nh_grp = rcu_dereference_rtnl(nh->nh_grp);
156		return nh_grp->has_v4;
157	}
158	return false;
159}
160
161static inline bool nexthop_is_multipath(const struct nexthop *nh)
162{
163	if (nh->is_group) {
164		struct nh_group *nh_grp;
165
166		nh_grp = rcu_dereference_rtnl(nh->nh_grp);
167		return nh_grp->mpath;
168	}
169	return false;
170}
171
172struct nexthop *nexthop_select_path(struct nexthop *nh, int hash);
173
174static inline unsigned int nexthop_num_path(const struct nexthop *nh)
175{
176	unsigned int rc = 1;
177
178	if (nh->is_group) {
179		struct nh_group *nh_grp;
180
181		nh_grp = rcu_dereference_rtnl(nh->nh_grp);
182		if (nh_grp->mpath)
183			rc = nh_grp->num_nh;
184	}
185
186	return rc;
187}
188
189static inline
190struct nexthop *nexthop_mpath_select(const struct nh_group *nhg, int nhsel)
191{
192	/* for_nexthops macros in fib_semantics.c grabs a pointer to
193	 * the nexthop before checking nhsel
194	 */
195	if (nhsel >= nhg->num_nh)
196		return NULL;
197
198	return nhg->nh_entries[nhsel].nh;
199}
200
201static inline
202int nexthop_mpath_fill_node(struct sk_buff *skb, struct nexthop *nh,
203			    u8 rt_family)
204{
205	struct nh_group *nhg = rtnl_dereference(nh->nh_grp);
206	int i;
207
208	for (i = 0; i < nhg->num_nh; i++) {
209		struct nexthop *nhe = nhg->nh_entries[i].nh;
210		struct nh_info *nhi = rcu_dereference_rtnl(nhe->nh_info);
211		struct fib_nh_common *nhc = &nhi->fib_nhc;
212		int weight = nhg->nh_entries[i].weight;
213
214		if (fib_add_nexthop(skb, nhc, weight, rt_family, 0) < 0)
215			return -EMSGSIZE;
216	}
217
218	return 0;
219}
220
221/* called with rcu lock */
222static inline bool nexthop_is_blackhole(const struct nexthop *nh)
223{
224	const struct nh_info *nhi;
225
226	if (nh->is_group) {
227		struct nh_group *nh_grp;
228
229		nh_grp = rcu_dereference_rtnl(nh->nh_grp);
230		if (nh_grp->num_nh > 1)
231			return false;
232
233		nh = nh_grp->nh_entries[0].nh;
234	}
235
236	nhi = rcu_dereference_rtnl(nh->nh_info);
237	return nhi->reject_nh;
238}
239
240static inline void nexthop_path_fib_result(struct fib_result *res, int hash)
241{
242	struct nh_info *nhi;
243	struct nexthop *nh;
244
245	nh = nexthop_select_path(res->fi->nh, hash);
246	nhi = rcu_dereference(nh->nh_info);
247	res->nhc = &nhi->fib_nhc;
248}
249
250/* called with rcu read lock or rtnl held */
251static inline
252struct fib_nh_common *nexthop_fib_nhc(struct nexthop *nh, int nhsel)
253{
254	struct nh_info *nhi;
255
256	BUILD_BUG_ON(offsetof(struct fib_nh, nh_common) != 0);
257	BUILD_BUG_ON(offsetof(struct fib6_nh, nh_common) != 0);
258
259	if (nh->is_group) {
260		struct nh_group *nh_grp;
261
262		nh_grp = rcu_dereference_rtnl(nh->nh_grp);
263		if (nh_grp->mpath) {
264			nh = nexthop_mpath_select(nh_grp, nhsel);
265			if (!nh)
266				return NULL;
267		}
268	}
269
270	nhi = rcu_dereference_rtnl(nh->nh_info);
271	return &nhi->fib_nhc;
272}
273
274/* called from fib_table_lookup with rcu_lock */
275static inline
276struct fib_nh_common *nexthop_get_nhc_lookup(const struct nexthop *nh,
277					     int fib_flags,
278					     const struct flowi4 *flp,
279					     int *nhsel)
280{
281	struct nh_info *nhi;
282
283	if (nh->is_group) {
284		struct nh_group *nhg = rcu_dereference(nh->nh_grp);
285		int i;
286
287		for (i = 0; i < nhg->num_nh; i++) {
288			struct nexthop *nhe = nhg->nh_entries[i].nh;
289
290			nhi = rcu_dereference(nhe->nh_info);
291			if (fib_lookup_good_nhc(&nhi->fib_nhc, fib_flags, flp)) {
292				*nhsel = i;
293				return &nhi->fib_nhc;
294			}
295		}
296	} else {
297		nhi = rcu_dereference(nh->nh_info);
298		if (fib_lookup_good_nhc(&nhi->fib_nhc, fib_flags, flp)) {
299			*nhsel = 0;
300			return &nhi->fib_nhc;
301		}
302	}
303
304	return NULL;
305}
306
307static inline bool nexthop_uses_dev(const struct nexthop *nh,
308				    const struct net_device *dev)
309{
310	struct nh_info *nhi;
311
312	if (nh->is_group) {
313		struct nh_group *nhg = rcu_dereference(nh->nh_grp);
314		int i;
315
316		for (i = 0; i < nhg->num_nh; i++) {
317			struct nexthop *nhe = nhg->nh_entries[i].nh;
318
319			nhi = rcu_dereference(nhe->nh_info);
320			if (nhc_l3mdev_matches_dev(&nhi->fib_nhc, dev))
321				return true;
322		}
323	} else {
324		nhi = rcu_dereference(nh->nh_info);
325		if (nhc_l3mdev_matches_dev(&nhi->fib_nhc, dev))
326			return true;
327	}
328
329	return false;
330}
331
332static inline unsigned int fib_info_num_path(const struct fib_info *fi)
333{
334	if (unlikely(fi->nh))
335		return nexthop_num_path(fi->nh);
336
337	return fi->fib_nhs;
338}
339
340int fib_check_nexthop(struct nexthop *nh, u8 scope,
341		      struct netlink_ext_ack *extack);
342
343static inline struct fib_nh_common *fib_info_nhc(struct fib_info *fi, int nhsel)
344{
345	if (unlikely(fi->nh))
346		return nexthop_fib_nhc(fi->nh, nhsel);
347
348	return &fi->fib_nh[nhsel].nh_common;
349}
350
351/* only used when fib_nh is built into fib_info */
352static inline struct fib_nh *fib_info_nh(struct fib_info *fi, int nhsel)
353{
354	WARN_ON(fi->nh);
355
356	return &fi->fib_nh[nhsel];
357}
358
359/*
360 * IPv6 variants
361 */
362int fib6_check_nexthop(struct nexthop *nh, struct fib6_config *cfg,
363		       struct netlink_ext_ack *extack);
364
365/* Caller should either hold rcu_read_lock(), or RTNL. */
366static inline struct fib6_nh *nexthop_fib6_nh(struct nexthop *nh)
367{
368	struct nh_info *nhi;
369
370	if (nh->is_group) {
371		struct nh_group *nh_grp;
372
373		nh_grp = rcu_dereference_rtnl(nh->nh_grp);
374		nh = nexthop_mpath_select(nh_grp, 0);
375		if (!nh)
376			return NULL;
377	}
378
379	nhi = rcu_dereference_rtnl(nh->nh_info);
380	if (nhi->family == AF_INET6)
381		return &nhi->fib6_nh;
382
383	return NULL;
384}
385
386/* Variant of nexthop_fib6_nh().
387 * Caller should either hold rcu_read_lock_bh(), or RTNL.
388 */
389static inline struct fib6_nh *nexthop_fib6_nh_bh(struct nexthop *nh)
390{
391	struct nh_info *nhi;
392
393	if (nh->is_group) {
394		struct nh_group *nh_grp;
395
396		nh_grp = rcu_dereference_bh_rtnl(nh->nh_grp);
397		nh = nexthop_mpath_select(nh_grp, 0);
398		if (!nh)
399			return NULL;
400	}
401
402	nhi = rcu_dereference_bh_rtnl(nh->nh_info);
403	if (nhi->family == AF_INET6)
404		return &nhi->fib6_nh;
405
406	return NULL;
407}
408
409static inline struct net_device *fib6_info_nh_dev(struct fib6_info *f6i)
410{
411	struct fib6_nh *fib6_nh;
412
413	fib6_nh = f6i->nh ? nexthop_fib6_nh(f6i->nh) : f6i->fib6_nh;
414	return fib6_nh->fib_nh_dev;
415}
416
417static inline void nexthop_path_fib6_result(struct fib6_result *res, int hash)
418{
419	struct nexthop *nh = res->f6i->nh;
420	struct nh_info *nhi;
421
422	nh = nexthop_select_path(nh, hash);
423
424	nhi = rcu_dereference_rtnl(nh->nh_info);
425	if (nhi->reject_nh) {
426		res->fib6_type = RTN_BLACKHOLE;
427		res->fib6_flags |= RTF_REJECT;
428		res->nh = nexthop_fib6_nh(nh);
429	} else {
430		res->nh = &nhi->fib6_nh;
431	}
432}
433
434int nexthop_for_each_fib6_nh(struct nexthop *nh,
435			     int (*cb)(struct fib6_nh *nh, void *arg),
436			     void *arg);
437
438static inline int nexthop_get_family(struct nexthop *nh)
439{
440	struct nh_info *nhi = rcu_dereference_rtnl(nh->nh_info);
441
442	return nhi->family;
443}
444
445static inline
446struct fib_nh_common *nexthop_fdb_nhc(struct nexthop *nh)
447{
448	struct nh_info *nhi = rcu_dereference_rtnl(nh->nh_info);
449
450	return &nhi->fib_nhc;
451}
452
453static inline struct fib_nh_common *nexthop_path_fdb_result(struct nexthop *nh,
454							    int hash)
455{
456	struct nh_info *nhi;
457	struct nexthop *nhp;
458
459	nhp = nexthop_select_path(nh, hash);
460	if (unlikely(!nhp))
461		return NULL;
462	nhi = rcu_dereference(nhp->nh_info);
463	return &nhi->fib_nhc;
464}
465#endif
466