Branch data Line data Source code
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/route.h>
14 : : #include <linux/types.h>
15 : : #include <net/ip_fib.h>
16 : : #include <net/ip6_fib.h>
17 : : #include <net/netlink.h>
18 : :
19 : : #define NEXTHOP_VALID_USER_FLAGS RTNH_F_ONLINK
20 : :
21 : : struct nexthop;
22 : :
23 : : struct nh_config {
24 : : u32 nh_id;
25 : :
26 : : u8 nh_family;
27 : : u8 nh_protocol;
28 : : u8 nh_blackhole;
29 : : u32 nh_flags;
30 : :
31 : : int nh_ifindex;
32 : : struct net_device *dev;
33 : :
34 : : union {
35 : : __be32 ipv4;
36 : : struct in6_addr ipv6;
37 : : } gw;
38 : :
39 : : struct nlattr *nh_grp;
40 : : u16 nh_grp_type;
41 : :
42 : : struct nlattr *nh_encap;
43 : : u16 nh_encap_type;
44 : :
45 : : u32 nlflags;
46 : : struct nl_info nlinfo;
47 : : };
48 : :
49 : : struct nh_info {
50 : : struct hlist_node dev_hash; /* entry on netns devhash */
51 : : struct nexthop *nh_parent;
52 : :
53 : : u8 family;
54 : : bool reject_nh;
55 : :
56 : : union {
57 : : struct fib_nh_common fib_nhc;
58 : : struct fib_nh fib_nh;
59 : : struct fib6_nh fib6_nh;
60 : : };
61 : : };
62 : :
63 : : struct nh_grp_entry {
64 : : struct nexthop *nh;
65 : : u8 weight;
66 : : atomic_t upper_bound;
67 : :
68 : : struct list_head nh_list;
69 : : struct nexthop *nh_parent; /* nexthop of group with this entry */
70 : : };
71 : :
72 : : struct nh_group {
73 : : struct nh_group *spare; /* spare group for removals */
74 : : u16 num_nh;
75 : : bool mpath;
76 : : bool has_v4;
77 : : struct nh_grp_entry nh_entries[0];
78 : : };
79 : :
80 : : struct nexthop {
81 : : struct rb_node rb_node; /* entry on netns rbtree */
82 : : struct list_head fi_list; /* v4 entries using nh */
83 : : struct list_head f6i_list; /* v6 entries using nh */
84 : : struct list_head grp_list; /* nh group entries using this nh */
85 : : struct net *net;
86 : :
87 : : u32 id;
88 : :
89 : : u8 protocol; /* app managing this nh */
90 : : u8 nh_flags;
91 : : bool is_group;
92 : :
93 : : refcount_t refcnt;
94 : : struct rcu_head rcu;
95 : :
96 : : union {
97 : : struct nh_info __rcu *nh_info;
98 : : struct nh_group __rcu *nh_grp;
99 : : };
100 : : };
101 : :
102 : : /* caller is holding rcu or rtnl; no reference taken to nexthop */
103 : : struct nexthop *nexthop_find_by_id(struct net *net, u32 id);
104 : : void nexthop_free_rcu(struct rcu_head *head);
105 : :
106 : : static inline bool nexthop_get(struct nexthop *nh)
107 : : {
108 : 0 : return refcount_inc_not_zero(&nh->refcnt);
109 : : }
110 : :
111 : 0 : static inline void nexthop_put(struct nexthop *nh)
112 : : {
113 [ # # ]: 0 : if (refcount_dec_and_test(&nh->refcnt))
114 : 0 : call_rcu(&nh->rcu, nexthop_free_rcu);
115 : 0 : }
116 : :
117 : : static inline bool nexthop_cmp(const struct nexthop *nh1,
118 : : const struct nexthop *nh2)
119 : : {
120 : 0 : return nh1 == nh2;
121 : : }
122 : :
123 : : static inline bool nexthop_is_multipath(const struct nexthop *nh)
124 : : {
125 [ # # # # ]: 0 : if (nh->is_group) {
126 : : struct nh_group *nh_grp;
127 : :
128 : 0 : nh_grp = rcu_dereference_rtnl(nh->nh_grp);
129 : 0 : return nh_grp->mpath;
130 : : }
131 : : return false;
132 : : }
133 : :
134 : : struct nexthop *nexthop_select_path(struct nexthop *nh, int hash);
135 : :
136 : : static inline unsigned int nexthop_num_path(const struct nexthop *nh)
137 : : {
138 : : unsigned int rc = 1;
139 : :
140 [ # # # # : 0 : if (nh->is_group) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
141 : : struct nh_group *nh_grp;
142 : :
143 : 0 : nh_grp = rcu_dereference_rtnl(nh->nh_grp);
144 [ # # # # : 0 : if (nh_grp->mpath)
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
145 : 0 : rc = nh_grp->num_nh;
146 : : }
147 : :
148 : : return rc;
149 : : }
150 : :
151 : : static inline
152 : : struct nexthop *nexthop_mpath_select(const struct nh_group *nhg, int nhsel)
153 : : {
154 : : /* for_nexthops macros in fib_semantics.c grabs a pointer to
155 : : * the nexthop before checking nhsel
156 : : */
157 [ # # # # : 0 : if (nhsel >= nhg->num_nh)
# # # # #
# # # #
# ]
158 : : return NULL;
159 : :
160 : 0 : return nhg->nh_entries[nhsel].nh;
161 : : }
162 : :
163 : : static inline
164 : 0 : int nexthop_mpath_fill_node(struct sk_buff *skb, struct nexthop *nh,
165 : : u8 rt_family)
166 : : {
167 : 0 : struct nh_group *nhg = rtnl_dereference(nh->nh_grp);
168 : : int i;
169 : :
170 [ # # ]: 0 : for (i = 0; i < nhg->num_nh; i++) {
171 : 0 : struct nexthop *nhe = nhg->nh_entries[i].nh;
172 : 0 : struct nh_info *nhi = rcu_dereference_rtnl(nhe->nh_info);
173 : 0 : struct fib_nh_common *nhc = &nhi->fib_nhc;
174 : 0 : int weight = nhg->nh_entries[i].weight;
175 : :
176 [ # # ]: 0 : if (fib_add_nexthop(skb, nhc, weight, rt_family) < 0)
177 : : return -EMSGSIZE;
178 : : }
179 : :
180 : : return 0;
181 : : }
182 : :
183 : : /* called with rcu lock */
184 : : static inline bool nexthop_is_blackhole(const struct nexthop *nh)
185 : : {
186 : : const struct nh_info *nhi;
187 : :
188 [ # # # # : 0 : if (nh->is_group) {
# # # # #
# # # ]
189 : : struct nh_group *nh_grp;
190 : :
191 : 0 : nh_grp = rcu_dereference_rtnl(nh->nh_grp);
192 [ # # # # : 0 : if (nh_grp->num_nh > 1)
# # # # #
# # # ]
193 : : return false;
194 : :
195 : 0 : nh = nh_grp->nh_entries[0].nh;
196 : : }
197 : :
198 : 0 : nhi = rcu_dereference_rtnl(nh->nh_info);
199 : 0 : return nhi->reject_nh;
200 : : }
201 : :
202 : : static inline void nexthop_path_fib_result(struct fib_result *res, int hash)
203 : : {
204 : : struct nh_info *nhi;
205 : : struct nexthop *nh;
206 : :
207 : 0 : nh = nexthop_select_path(res->fi->nh, hash);
208 : 0 : nhi = rcu_dereference(nh->nh_info);
209 : 0 : res->nhc = &nhi->fib_nhc;
210 : : }
211 : :
212 : : /* called with rcu read lock or rtnl held */
213 : : static inline
214 : : struct fib_nh_common *nexthop_fib_nhc(struct nexthop *nh, int nhsel)
215 : : {
216 : : struct nh_info *nhi;
217 : :
218 : : BUILD_BUG_ON(offsetof(struct fib_nh, nh_common) != 0);
219 : : BUILD_BUG_ON(offsetof(struct fib6_nh, nh_common) != 0);
220 : :
221 [ # # ]: 0 : if (nh->is_group) {
222 : : struct nh_group *nh_grp;
223 : :
224 : 0 : nh_grp = rcu_dereference_rtnl(nh->nh_grp);
225 [ # # ]: 0 : if (nh_grp->mpath) {
226 : : nh = nexthop_mpath_select(nh_grp, nhsel);
227 [ # # ]: 0 : if (!nh)
228 : : return NULL;
229 : : }
230 : : }
231 : :
232 : 0 : nhi = rcu_dereference_rtnl(nh->nh_info);
233 : 0 : return &nhi->fib_nhc;
234 : : }
235 : :
236 : 0 : static inline bool nexthop_uses_dev(const struct nexthop *nh,
237 : : const struct net_device *dev)
238 : : {
239 : : struct nh_info *nhi;
240 : :
241 [ # # ]: 0 : if (nh->is_group) {
242 : 0 : struct nh_group *nhg = rcu_dereference(nh->nh_grp);
243 : : int i;
244 : :
245 [ # # ]: 0 : for (i = 0; i < nhg->num_nh; i++) {
246 : 0 : struct nexthop *nhe = nhg->nh_entries[i].nh;
247 : :
248 : 0 : nhi = rcu_dereference(nhe->nh_info);
249 [ # # ]: 0 : if (nhc_l3mdev_matches_dev(&nhi->fib_nhc, dev))
250 : : return true;
251 : : }
252 : : } else {
253 : 0 : nhi = rcu_dereference(nh->nh_info);
254 [ # # ]: 0 : if (nhc_l3mdev_matches_dev(&nhi->fib_nhc, dev))
255 : : return true;
256 : : }
257 : :
258 : : return false;
259 : : }
260 : :
261 : : static inline unsigned int fib_info_num_path(const struct fib_info *fi)
262 : : {
263 [ - + # # : 59489 : if (unlikely(fi->nh))
- + # # -
+ # # - +
- + - + -
+ # # - +
# # # # #
# - + - +
- + - + #
# - + ]
264 : : return nexthop_num_path(fi->nh);
265 : :
266 : 52451 : return fi->fib_nhs;
267 : : }
268 : :
269 : : int fib_check_nexthop(struct nexthop *nh, u8 scope,
270 : : struct netlink_ext_ack *extack);
271 : :
272 : 23259 : static inline struct fib_nh_common *fib_info_nhc(struct fib_info *fi, int nhsel)
273 : : {
274 [ - + ]: 23259 : if (unlikely(fi->nh))
275 : 0 : return nexthop_fib_nhc(fi->nh, nhsel);
276 : :
277 : 23259 : return &fi->fib_nh[nhsel].nh_common;
278 : : }
279 : :
280 : : /* only used when fib_nh is built into fib_info */
281 : : static inline struct fib_nh *fib_info_nh(struct fib_info *fi, int nhsel)
282 : : {
283 [ # # # # : 1035 : WARN_ON(fi->nh);
- + ]
284 : :
285 : : return &fi->fib_nh[nhsel];
286 : : }
287 : :
288 : : /*
289 : : * IPv6 variants
290 : : */
291 : : int fib6_check_nexthop(struct nexthop *nh, struct fib6_config *cfg,
292 : : struct netlink_ext_ack *extack);
293 : :
294 : : static inline struct fib6_nh *nexthop_fib6_nh(struct nexthop *nh)
295 : : {
296 : : struct nh_info *nhi;
297 : :
298 [ # # # # : 0 : if (nh->is_group) {
# # # # #
# # # #
# ]
299 : : struct nh_group *nh_grp;
300 : :
301 : 0 : nh_grp = rcu_dereference_rtnl(nh->nh_grp);
302 : : nh = nexthop_mpath_select(nh_grp, 0);
303 [ # # # # : 0 : if (!nh)
# # # # #
# # # #
# ]
304 : : return NULL;
305 : : }
306 : :
307 : 0 : nhi = rcu_dereference_rtnl(nh->nh_info);
308 [ # # # # : 0 : if (nhi->family == AF_INET6)
# # # # #
# # # #
# ]
309 : 0 : return &nhi->fib6_nh;
310 : :
311 : : return NULL;
312 : : }
313 : :
314 : 831 : static inline struct net_device *fib6_info_nh_dev(struct fib6_info *f6i)
315 : : {
316 : : struct fib6_nh *fib6_nh;
317 : :
318 [ - + ]: 1662 : fib6_nh = f6i->nh ? nexthop_fib6_nh(f6i->nh) : f6i->fib6_nh;
319 : 831 : return fib6_nh->fib_nh_dev;
320 : : }
321 : :
322 : 0 : static inline void nexthop_path_fib6_result(struct fib6_result *res, int hash)
323 : : {
324 : 0 : struct nexthop *nh = res->f6i->nh;
325 : : struct nh_info *nhi;
326 : :
327 : 0 : nh = nexthop_select_path(nh, hash);
328 : :
329 : 0 : nhi = rcu_dereference_rtnl(nh->nh_info);
330 [ # # ]: 0 : if (nhi->reject_nh) {
331 : 0 : res->fib6_type = RTN_BLACKHOLE;
332 : 0 : res->fib6_flags |= RTF_REJECT;
333 : 0 : res->nh = nexthop_fib6_nh(nh);
334 : : } else {
335 : 0 : res->nh = &nhi->fib6_nh;
336 : : }
337 : 0 : }
338 : :
339 : : int nexthop_for_each_fib6_nh(struct nexthop *nh,
340 : : int (*cb)(struct fib6_nh *nh, void *arg),
341 : : void *arg);
342 : : #endif
|