blob: d13730ff9aebc87be4c61cbbcb01e92bbc661aee [file] [log] [blame]
David Ahernab84be72019-05-24 14:43:04 -07001// SPDX-License-Identifier: GPL-2.0
2/* Generic nexthop implementation
3 *
4 * Copyright (c) 2017-19 Cumulus Networks
5 * Copyright (c) 2017-19 David Ahern <dsa@cumulusnetworks.com>
6 */
7
8#include <linux/nexthop.h>
9#include <linux/rtnetlink.h>
10#include <linux/slab.h>
David Ahern430a0492019-05-24 14:43:08 -070011#include <net/arp.h>
David Ahern53010f92019-05-24 14:43:06 -070012#include <net/ipv6_stubs.h>
David Ahernb513bd02019-05-24 14:43:07 -070013#include <net/lwtunnel.h>
David Ahern430a0492019-05-24 14:43:08 -070014#include <net/ndisc.h>
David Ahernab84be72019-05-24 14:43:04 -070015#include <net/nexthop.h>
David Ahern597cfe4f2019-05-24 14:43:05 -070016#include <net/route.h>
David Ahernab84be72019-05-24 14:43:04 -070017#include <net/sock.h>
18
David Ahern430a0492019-05-24 14:43:08 -070019static void remove_nexthop(struct net *net, struct nexthop *nh,
20 struct nl_info *nlinfo);
21
David Ahern597cfe4f2019-05-24 14:43:05 -070022#define NH_DEV_HASHBITS 8
23#define NH_DEV_HASHSIZE (1U << NH_DEV_HASHBITS)
24
David Ahernab84be72019-05-24 14:43:04 -070025static const struct nla_policy rtm_nh_policy[NHA_MAX + 1] = {
David Ahernab84be72019-05-24 14:43:04 -070026 [NHA_ID] = { .type = NLA_U32 },
27 [NHA_GROUP] = { .type = NLA_BINARY },
28 [NHA_GROUP_TYPE] = { .type = NLA_U16 },
29 [NHA_BLACKHOLE] = { .type = NLA_FLAG },
30 [NHA_OIF] = { .type = NLA_U32 },
31 [NHA_GATEWAY] = { .type = NLA_BINARY },
32 [NHA_ENCAP_TYPE] = { .type = NLA_U16 },
33 [NHA_ENCAP] = { .type = NLA_NESTED },
34 [NHA_GROUPS] = { .type = NLA_FLAG },
35 [NHA_MASTER] = { .type = NLA_U32 },
Roopa Prabhu38428d62020-05-21 22:26:13 -070036 [NHA_FDB] = { .type = NLA_FLAG },
David Ahernab84be72019-05-24 14:43:04 -070037};
38
Roopa Prabhu8590ceed2020-05-21 22:26:15 -070039static int call_nexthop_notifiers(struct net *net,
Nathan Chancellord8e79f12020-05-27 01:00:20 -070040 enum nexthop_event_type event_type,
Roopa Prabhu8590ceed2020-05-21 22:26:15 -070041 struct nexthop *nh)
42{
43 int err;
44
45 err = atomic_notifier_call_chain(&net->nexthop.notifier_chain,
46 event_type, nh);
47 return notifier_to_errno(err);
48}
49
David Ahern597cfe4f2019-05-24 14:43:05 -070050static unsigned int nh_dev_hashfn(unsigned int val)
51{
52 unsigned int mask = NH_DEV_HASHSIZE - 1;
53
54 return (val ^
55 (val >> NH_DEV_HASHBITS) ^
56 (val >> (NH_DEV_HASHBITS * 2))) & mask;
57}
58
59static void nexthop_devhash_add(struct net *net, struct nh_info *nhi)
60{
61 struct net_device *dev = nhi->fib_nhc.nhc_dev;
62 struct hlist_head *head;
63 unsigned int hash;
64
65 WARN_ON(!dev);
66
67 hash = nh_dev_hashfn(dev->ifindex);
68 head = &net->nexthop.devhash[hash];
69 hlist_add_head(&nhi->dev_hash, head);
70}
71
David Ahern430a0492019-05-24 14:43:08 -070072static void nexthop_free_mpath(struct nexthop *nh)
David Ahernab84be72019-05-24 14:43:04 -070073{
David Ahern430a0492019-05-24 14:43:08 -070074 struct nh_group *nhg;
75 int i;
76
77 nhg = rcu_dereference_raw(nh->nh_grp);
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -060078 for (i = 0; i < nhg->num_nh; ++i) {
79 struct nh_grp_entry *nhge = &nhg->nh_entries[i];
David Ahern430a0492019-05-24 14:43:08 -070080
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -060081 WARN_ON(!list_empty(&nhge->nh_list));
82 nexthop_put(nhge->nh);
83 }
84
85 WARN_ON(nhg->spare == nhg);
86
87 kfree(nhg->spare);
David Ahern430a0492019-05-24 14:43:08 -070088 kfree(nhg);
89}
90
91static void nexthop_free_single(struct nexthop *nh)
92{
David Ahernab84be72019-05-24 14:43:04 -070093 struct nh_info *nhi;
94
95 nhi = rcu_dereference_raw(nh->nh_info);
David Ahern597cfe4f2019-05-24 14:43:05 -070096 switch (nhi->family) {
97 case AF_INET:
98 fib_nh_release(nh->net, &nhi->fib_nh);
99 break;
David Ahern53010f92019-05-24 14:43:06 -0700100 case AF_INET6:
101 ipv6_stub->fib6_nh_release(&nhi->fib6_nh);
102 break;
David Ahern597cfe4f2019-05-24 14:43:05 -0700103 }
David Ahernab84be72019-05-24 14:43:04 -0700104 kfree(nhi);
David Ahern430a0492019-05-24 14:43:08 -0700105}
106
107void nexthop_free_rcu(struct rcu_head *head)
108{
109 struct nexthop *nh = container_of(head, struct nexthop, rcu);
110
111 if (nh->is_group)
112 nexthop_free_mpath(nh);
113 else
114 nexthop_free_single(nh);
David Ahernab84be72019-05-24 14:43:04 -0700115
116 kfree(nh);
117}
118EXPORT_SYMBOL_GPL(nexthop_free_rcu);
119
120static struct nexthop *nexthop_alloc(void)
121{
122 struct nexthop *nh;
123
124 nh = kzalloc(sizeof(struct nexthop), GFP_KERNEL);
David Ahern430a0492019-05-24 14:43:08 -0700125 if (nh) {
David Ahern4c7e8082019-06-03 20:19:51 -0700126 INIT_LIST_HEAD(&nh->fi_list);
David Ahernf88d8ea2019-06-03 20:19:52 -0700127 INIT_LIST_HEAD(&nh->f6i_list);
David Ahern430a0492019-05-24 14:43:08 -0700128 INIT_LIST_HEAD(&nh->grp_list);
Roopa Prabhu38428d62020-05-21 22:26:13 -0700129 INIT_LIST_HEAD(&nh->fdb_list);
David Ahern430a0492019-05-24 14:43:08 -0700130 }
David Ahernab84be72019-05-24 14:43:04 -0700131 return nh;
132}
133
David Ahern430a0492019-05-24 14:43:08 -0700134static struct nh_group *nexthop_grp_alloc(u16 num_nh)
135{
David Ahern430a0492019-05-24 14:43:08 -0700136 struct nh_group *nhg;
137
Ido Schimmeld7d49dc2020-08-26 19:48:51 +0300138 nhg = kzalloc(struct_size(nhg, nh_entries, num_nh), GFP_KERNEL);
David Ahern430a0492019-05-24 14:43:08 -0700139 if (nhg)
140 nhg->num_nh = num_nh;
141
142 return nhg;
143}
144
David Ahernab84be72019-05-24 14:43:04 -0700145static void nh_base_seq_inc(struct net *net)
146{
147 while (++net->nexthop.seq == 0)
148 ;
149}
150
151/* no reference taken; rcu lock or rtnl must be held */
152struct nexthop *nexthop_find_by_id(struct net *net, u32 id)
153{
154 struct rb_node **pp, *parent = NULL, *next;
155
156 pp = &net->nexthop.rb_root.rb_node;
157 while (1) {
158 struct nexthop *nh;
159
160 next = rcu_dereference_raw(*pp);
161 if (!next)
162 break;
163 parent = next;
164
165 nh = rb_entry(parent, struct nexthop, rb_node);
166 if (id < nh->id)
167 pp = &next->rb_left;
168 else if (id > nh->id)
169 pp = &next->rb_right;
170 else
171 return nh;
172 }
173 return NULL;
174}
175EXPORT_SYMBOL_GPL(nexthop_find_by_id);
176
177/* used for auto id allocation; called with rtnl held */
178static u32 nh_find_unused_id(struct net *net)
179{
180 u32 id_start = net->nexthop.last_id_allocated;
181
182 while (1) {
183 net->nexthop.last_id_allocated++;
184 if (net->nexthop.last_id_allocated == id_start)
185 break;
186
187 if (!nexthop_find_by_id(net, net->nexthop.last_id_allocated))
188 return net->nexthop.last_id_allocated;
189 }
190 return 0;
191}
192
David Ahern430a0492019-05-24 14:43:08 -0700193static int nla_put_nh_group(struct sk_buff *skb, struct nh_group *nhg)
194{
195 struct nexthop_grp *p;
196 size_t len = nhg->num_nh * sizeof(*p);
197 struct nlattr *nla;
198 u16 group_type = 0;
199 int i;
200
201 if (nhg->mpath)
202 group_type = NEXTHOP_GRP_TYPE_MPATH;
203
204 if (nla_put_u16(skb, NHA_GROUP_TYPE, group_type))
205 goto nla_put_failure;
206
207 nla = nla_reserve(skb, NHA_GROUP, len);
208 if (!nla)
209 goto nla_put_failure;
210
211 p = nla_data(nla);
212 for (i = 0; i < nhg->num_nh; ++i) {
213 p->id = nhg->nh_entries[i].nh->id;
214 p->weight = nhg->nh_entries[i].weight - 1;
215 p += 1;
216 }
217
218 return 0;
219
220nla_put_failure:
221 return -EMSGSIZE;
222}
223
David Ahernab84be72019-05-24 14:43:04 -0700224static int nh_fill_node(struct sk_buff *skb, struct nexthop *nh,
225 int event, u32 portid, u32 seq, unsigned int nlflags)
226{
David Ahern53010f92019-05-24 14:43:06 -0700227 struct fib6_nh *fib6_nh;
David Ahern597cfe4f2019-05-24 14:43:05 -0700228 struct fib_nh *fib_nh;
David Ahernab84be72019-05-24 14:43:04 -0700229 struct nlmsghdr *nlh;
230 struct nh_info *nhi;
231 struct nhmsg *nhm;
232
233 nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nhm), nlflags);
234 if (!nlh)
235 return -EMSGSIZE;
236
237 nhm = nlmsg_data(nlh);
238 nhm->nh_family = AF_UNSPEC;
239 nhm->nh_flags = nh->nh_flags;
240 nhm->nh_protocol = nh->protocol;
241 nhm->nh_scope = 0;
242 nhm->resvd = 0;
243
244 if (nla_put_u32(skb, NHA_ID, nh->id))
245 goto nla_put_failure;
246
David Ahern430a0492019-05-24 14:43:08 -0700247 if (nh->is_group) {
248 struct nh_group *nhg = rtnl_dereference(nh->nh_grp);
249
David Ahernce9ac052020-06-08 20:54:43 -0600250 if (nhg->fdb_nh && nla_put_flag(skb, NHA_FDB))
251 goto nla_put_failure;
David Ahern430a0492019-05-24 14:43:08 -0700252 if (nla_put_nh_group(skb, nhg))
253 goto nla_put_failure;
254 goto out;
255 }
256
David Ahernab84be72019-05-24 14:43:04 -0700257 nhi = rtnl_dereference(nh->nh_info);
258 nhm->nh_family = nhi->family;
259 if (nhi->reject_nh) {
260 if (nla_put_flag(skb, NHA_BLACKHOLE))
261 goto nla_put_failure;
262 goto out;
David Ahernce9ac052020-06-08 20:54:43 -0600263 } else if (nhi->fdb_nh) {
264 if (nla_put_flag(skb, NHA_FDB))
265 goto nla_put_failure;
266 } else {
David Ahern597cfe4f2019-05-24 14:43:05 -0700267 const struct net_device *dev;
268
269 dev = nhi->fib_nhc.nhc_dev;
270 if (dev && nla_put_u32(skb, NHA_OIF, dev->ifindex))
271 goto nla_put_failure;
272 }
273
274 nhm->nh_scope = nhi->fib_nhc.nhc_scope;
275 switch (nhi->family) {
276 case AF_INET:
277 fib_nh = &nhi->fib_nh;
278 if (fib_nh->fib_nh_gw_family &&
279 nla_put_u32(skb, NHA_GATEWAY, fib_nh->fib_nh_gw4))
280 goto nla_put_failure;
281 break;
David Ahern53010f92019-05-24 14:43:06 -0700282
283 case AF_INET6:
284 fib6_nh = &nhi->fib6_nh;
285 if (fib6_nh->fib_nh_gw_family &&
286 nla_put_in6_addr(skb, NHA_GATEWAY, &fib6_nh->fib_nh_gw6))
287 goto nla_put_failure;
288 break;
David Ahernab84be72019-05-24 14:43:04 -0700289 }
290
David Ahernb513bd02019-05-24 14:43:07 -0700291 if (nhi->fib_nhc.nhc_lwtstate &&
292 lwtunnel_fill_encap(skb, nhi->fib_nhc.nhc_lwtstate,
293 NHA_ENCAP, NHA_ENCAP_TYPE) < 0)
294 goto nla_put_failure;
295
David Ahernab84be72019-05-24 14:43:04 -0700296out:
297 nlmsg_end(skb, nlh);
298 return 0;
299
300nla_put_failure:
Stephen Worleyd69100b2020-05-19 21:57:12 -0400301 nlmsg_cancel(skb, nlh);
David Ahernab84be72019-05-24 14:43:04 -0700302 return -EMSGSIZE;
303}
304
David Ahern430a0492019-05-24 14:43:08 -0700305static size_t nh_nlmsg_size_grp(struct nexthop *nh)
306{
307 struct nh_group *nhg = rtnl_dereference(nh->nh_grp);
308 size_t sz = sizeof(struct nexthop_grp) * nhg->num_nh;
309
310 return nla_total_size(sz) +
311 nla_total_size(2); /* NHA_GROUP_TYPE */
312}
313
314static size_t nh_nlmsg_size_single(struct nexthop *nh)
David Ahernab84be72019-05-24 14:43:04 -0700315{
David Ahern597cfe4f2019-05-24 14:43:05 -0700316 struct nh_info *nhi = rtnl_dereference(nh->nh_info);
David Ahern430a0492019-05-24 14:43:08 -0700317 size_t sz;
David Ahernab84be72019-05-24 14:43:04 -0700318
319 /* covers NHA_BLACKHOLE since NHA_OIF and BLACKHOLE
320 * are mutually exclusive
321 */
David Ahern430a0492019-05-24 14:43:08 -0700322 sz = nla_total_size(4); /* NHA_OIF */
David Ahernab84be72019-05-24 14:43:04 -0700323
David Ahern597cfe4f2019-05-24 14:43:05 -0700324 switch (nhi->family) {
325 case AF_INET:
326 if (nhi->fib_nh.fib_nh_gw_family)
327 sz += nla_total_size(4); /* NHA_GATEWAY */
328 break;
David Ahern53010f92019-05-24 14:43:06 -0700329
330 case AF_INET6:
331 /* NHA_GATEWAY */
332 if (nhi->fib6_nh.fib_nh_gw_family)
333 sz += nla_total_size(sizeof(const struct in6_addr));
334 break;
David Ahern597cfe4f2019-05-24 14:43:05 -0700335 }
336
David Ahernb513bd02019-05-24 14:43:07 -0700337 if (nhi->fib_nhc.nhc_lwtstate) {
338 sz += lwtunnel_get_encap_size(nhi->fib_nhc.nhc_lwtstate);
339 sz += nla_total_size(2); /* NHA_ENCAP_TYPE */
340 }
341
David Ahernab84be72019-05-24 14:43:04 -0700342 return sz;
343}
344
David Ahern430a0492019-05-24 14:43:08 -0700345static size_t nh_nlmsg_size(struct nexthop *nh)
346{
Stephen Worleyf9e95552020-01-24 16:53:27 -0500347 size_t sz = NLMSG_ALIGN(sizeof(struct nhmsg));
348
349 sz += nla_total_size(4); /* NHA_ID */
David Ahern430a0492019-05-24 14:43:08 -0700350
351 if (nh->is_group)
352 sz += nh_nlmsg_size_grp(nh);
353 else
354 sz += nh_nlmsg_size_single(nh);
355
356 return sz;
357}
358
David Ahernab84be72019-05-24 14:43:04 -0700359static void nexthop_notify(int event, struct nexthop *nh, struct nl_info *info)
360{
361 unsigned int nlflags = info->nlh ? info->nlh->nlmsg_flags : 0;
362 u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
363 struct sk_buff *skb;
364 int err = -ENOBUFS;
365
366 skb = nlmsg_new(nh_nlmsg_size(nh), gfp_any());
367 if (!skb)
368 goto errout;
369
370 err = nh_fill_node(skb, nh, event, info->portid, seq, nlflags);
371 if (err < 0) {
372 /* -EMSGSIZE implies BUG in nh_nlmsg_size() */
373 WARN_ON(err == -EMSGSIZE);
374 kfree_skb(skb);
375 goto errout;
376 }
377
378 rtnl_notify(skb, info->nl_net, info->portid, RTNLGRP_NEXTHOP,
379 info->nlh, gfp_any());
380 return;
381errout:
382 if (err < 0)
383 rtnl_set_sk_err(info->nl_net, RTNLGRP_NEXTHOP, err);
384}
385
David Ahern430a0492019-05-24 14:43:08 -0700386static bool valid_group_nh(struct nexthop *nh, unsigned int npaths,
David Ahernce9ac052020-06-08 20:54:43 -0600387 bool *is_fdb, struct netlink_ext_ack *extack)
David Ahern597cfe4f2019-05-24 14:43:05 -0700388{
David Ahern430a0492019-05-24 14:43:08 -0700389 if (nh->is_group) {
390 struct nh_group *nhg = rtnl_dereference(nh->nh_grp);
David Ahern597cfe4f2019-05-24 14:43:05 -0700391
David Ahern430a0492019-05-24 14:43:08 -0700392 /* nested multipath (group within a group) is not
393 * supported
394 */
395 if (nhg->mpath) {
396 NL_SET_ERR_MSG(extack,
397 "Multipath group can not be a nexthop within a group");
398 return false;
399 }
David Ahernce9ac052020-06-08 20:54:43 -0600400 *is_fdb = nhg->fdb_nh;
David Ahern430a0492019-05-24 14:43:08 -0700401 } else {
402 struct nh_info *nhi = rtnl_dereference(nh->nh_info);
403
404 if (nhi->reject_nh && npaths > 1) {
405 NL_SET_ERR_MSG(extack,
406 "Blackhole nexthop can not be used in a group with more than 1 path");
407 return false;
408 }
David Ahernce9ac052020-06-08 20:54:43 -0600409 *is_fdb = nhi->fdb_nh;
David Ahern430a0492019-05-24 14:43:08 -0700410 }
411
412 return true;
413}
414
Roopa Prabhu38428d62020-05-21 22:26:13 -0700415static int nh_check_attr_fdb_group(struct nexthop *nh, u8 *nh_family,
416 struct netlink_ext_ack *extack)
417{
418 struct nh_info *nhi;
419
David Ahernce9ac052020-06-08 20:54:43 -0600420 nhi = rtnl_dereference(nh->nh_info);
421
422 if (!nhi->fdb_nh) {
Roopa Prabhu38428d62020-05-21 22:26:13 -0700423 NL_SET_ERR_MSG(extack, "FDB nexthop group can only have fdb nexthops");
424 return -EINVAL;
425 }
426
Roopa Prabhu38428d62020-05-21 22:26:13 -0700427 if (*nh_family == AF_UNSPEC) {
428 *nh_family = nhi->family;
429 } else if (*nh_family != nhi->family) {
430 NL_SET_ERR_MSG(extack, "FDB nexthop group cannot have mixed family nexthops");
431 return -EINVAL;
432 }
433
434 return 0;
435}
436
David Ahern430a0492019-05-24 14:43:08 -0700437static int nh_check_attr_group(struct net *net, struct nlattr *tb[],
438 struct netlink_ext_ack *extack)
439{
440 unsigned int len = nla_len(tb[NHA_GROUP]);
Roopa Prabhu38428d62020-05-21 22:26:13 -0700441 u8 nh_family = AF_UNSPEC;
David Ahern430a0492019-05-24 14:43:08 -0700442 struct nexthop_grp *nhg;
443 unsigned int i, j;
Roopa Prabhu38428d62020-05-21 22:26:13 -0700444 u8 nhg_fdb = 0;
David Ahern430a0492019-05-24 14:43:08 -0700445
Nikolay Aleksandroveeaac362020-08-22 15:06:36 +0300446 if (!len || len & (sizeof(struct nexthop_grp) - 1)) {
David Ahern430a0492019-05-24 14:43:08 -0700447 NL_SET_ERR_MSG(extack,
448 "Invalid length for nexthop group attribute");
449 return -EINVAL;
450 }
451
452 /* convert len to number of nexthop ids */
453 len /= sizeof(*nhg);
454
455 nhg = nla_data(tb[NHA_GROUP]);
456 for (i = 0; i < len; ++i) {
457 if (nhg[i].resvd1 || nhg[i].resvd2) {
458 NL_SET_ERR_MSG(extack, "Reserved fields in nexthop_grp must be 0");
459 return -EINVAL;
460 }
461 if (nhg[i].weight > 254) {
462 NL_SET_ERR_MSG(extack, "Invalid value for weight");
463 return -EINVAL;
464 }
465 for (j = i + 1; j < len; ++j) {
466 if (nhg[i].id == nhg[j].id) {
467 NL_SET_ERR_MSG(extack, "Nexthop id can not be used twice in a group");
468 return -EINVAL;
469 }
470 }
471 }
472
Roopa Prabhu38428d62020-05-21 22:26:13 -0700473 if (tb[NHA_FDB])
474 nhg_fdb = 1;
David Ahern430a0492019-05-24 14:43:08 -0700475 nhg = nla_data(tb[NHA_GROUP]);
476 for (i = 0; i < len; ++i) {
477 struct nexthop *nh;
David Ahernce9ac052020-06-08 20:54:43 -0600478 bool is_fdb_nh;
David Ahern430a0492019-05-24 14:43:08 -0700479
480 nh = nexthop_find_by_id(net, nhg[i].id);
481 if (!nh) {
482 NL_SET_ERR_MSG(extack, "Invalid nexthop id");
483 return -EINVAL;
484 }
David Ahernce9ac052020-06-08 20:54:43 -0600485 if (!valid_group_nh(nh, len, &is_fdb_nh, extack))
David Ahern430a0492019-05-24 14:43:08 -0700486 return -EINVAL;
Roopa Prabhu38428d62020-05-21 22:26:13 -0700487
488 if (nhg_fdb && nh_check_attr_fdb_group(nh, &nh_family, extack))
489 return -EINVAL;
490
David Ahernce9ac052020-06-08 20:54:43 -0600491 if (!nhg_fdb && is_fdb_nh) {
Roopa Prabhu38428d62020-05-21 22:26:13 -0700492 NL_SET_ERR_MSG(extack, "Non FDB nexthop group cannot have fdb nexthops");
493 return -EINVAL;
494 }
David Ahern430a0492019-05-24 14:43:08 -0700495 }
David Ahern84be69b2020-05-17 11:26:32 -0600496 for (i = NHA_GROUP_TYPE + 1; i < __NHA_MAX; ++i) {
David Ahern430a0492019-05-24 14:43:08 -0700497 if (!tb[i])
498 continue;
Roopa Prabhu38428d62020-05-21 22:26:13 -0700499 if (tb[NHA_FDB])
500 continue;
David Ahern430a0492019-05-24 14:43:08 -0700501 NL_SET_ERR_MSG(extack,
502 "No other attributes can be set in nexthop groups");
503 return -EINVAL;
504 }
505
506 return 0;
507}
508
509static bool ipv6_good_nh(const struct fib6_nh *nh)
510{
511 int state = NUD_REACHABLE;
512 struct neighbour *n;
513
514 rcu_read_lock_bh();
515
516 n = __ipv6_neigh_lookup_noref_stub(nh->fib_nh_dev, &nh->fib_nh_gw6);
517 if (n)
518 state = n->nud_state;
519
520 rcu_read_unlock_bh();
521
522 return !!(state & NUD_VALID);
523}
524
525static bool ipv4_good_nh(const struct fib_nh *nh)
526{
527 int state = NUD_REACHABLE;
528 struct neighbour *n;
529
530 rcu_read_lock_bh();
531
532 n = __ipv4_neigh_lookup_noref(nh->fib_nh_dev,
533 (__force u32)nh->fib_nh_gw4);
534 if (n)
535 state = n->nud_state;
536
537 rcu_read_unlock_bh();
538
539 return !!(state & NUD_VALID);
540}
541
542struct nexthop *nexthop_select_path(struct nexthop *nh, int hash)
543{
544 struct nexthop *rc = NULL;
545 struct nh_group *nhg;
546 int i;
547
548 if (!nh->is_group)
549 return nh;
550
551 nhg = rcu_dereference(nh->nh_grp);
552 for (i = 0; i < nhg->num_nh; ++i) {
553 struct nh_grp_entry *nhge = &nhg->nh_entries[i];
554 struct nh_info *nhi;
555
556 if (hash > atomic_read(&nhge->upper_bound))
557 continue;
558
David Ahernce9ac052020-06-08 20:54:43 -0600559 nhi = rcu_dereference(nhge->nh->nh_info);
560 if (nhi->fdb_nh)
Roopa Prabhu38428d62020-05-21 22:26:13 -0700561 return nhge->nh;
562
David Ahern430a0492019-05-24 14:43:08 -0700563 /* nexthops always check if it is good and does
564 * not rely on a sysctl for this behavior
565 */
David Ahern430a0492019-05-24 14:43:08 -0700566 switch (nhi->family) {
567 case AF_INET:
568 if (ipv4_good_nh(&nhi->fib_nh))
569 return nhge->nh;
570 break;
571 case AF_INET6:
572 if (ipv6_good_nh(&nhi->fib6_nh))
573 return nhge->nh;
574 break;
575 }
576
577 if (!rc)
578 rc = nhge->nh;
579 }
580
581 return rc;
582}
583EXPORT_SYMBOL_GPL(nexthop_select_path);
584
David Ahernf88c9aa2019-06-08 14:53:22 -0700585int nexthop_for_each_fib6_nh(struct nexthop *nh,
586 int (*cb)(struct fib6_nh *nh, void *arg),
587 void *arg)
588{
589 struct nh_info *nhi;
590 int err;
591
592 if (nh->is_group) {
593 struct nh_group *nhg;
594 int i;
595
596 nhg = rcu_dereference_rtnl(nh->nh_grp);
597 for (i = 0; i < nhg->num_nh; i++) {
598 struct nh_grp_entry *nhge = &nhg->nh_entries[i];
599
600 nhi = rcu_dereference_rtnl(nhge->nh->nh_info);
601 err = cb(&nhi->fib6_nh, arg);
602 if (err)
603 return err;
604 }
605 } else {
606 nhi = rcu_dereference_rtnl(nh->nh_info);
607 err = cb(&nhi->fib6_nh, arg);
608 if (err)
609 return err;
610 }
611
612 return 0;
613}
614EXPORT_SYMBOL_GPL(nexthop_for_each_fib6_nh);
615
David Ahern7bf47962019-06-08 14:53:35 -0700616static int check_src_addr(const struct in6_addr *saddr,
617 struct netlink_ext_ack *extack)
618{
619 if (!ipv6_addr_any(saddr)) {
620 NL_SET_ERR_MSG(extack, "IPv6 routes using source address can not use nexthop objects");
621 return -EINVAL;
622 }
623 return 0;
624}
625
David Ahernf88d8ea2019-06-03 20:19:52 -0700626int fib6_check_nexthop(struct nexthop *nh, struct fib6_config *cfg,
627 struct netlink_ext_ack *extack)
628{
629 struct nh_info *nhi;
David Ahernce9ac052020-06-08 20:54:43 -0600630 bool is_fdb_nh;
Roopa Prabhu38428d62020-05-21 22:26:13 -0700631
David Ahernf88d8ea2019-06-03 20:19:52 -0700632 /* fib6_src is unique to a fib6_info and limits the ability to cache
633 * routes in fib6_nh within a nexthop that is potentially shared
634 * across multiple fib entries. If the config wants to use source
635 * routing it can not use nexthop objects. mlxsw also does not allow
636 * fib6_src on routes.
637 */
David Ahern7bf47962019-06-08 14:53:35 -0700638 if (cfg && check_src_addr(&cfg->fc_src, extack) < 0)
David Ahernf88d8ea2019-06-03 20:19:52 -0700639 return -EINVAL;
David Ahernf88d8ea2019-06-03 20:19:52 -0700640
641 if (nh->is_group) {
642 struct nh_group *nhg;
643
644 nhg = rtnl_dereference(nh->nh_grp);
645 if (nhg->has_v4)
646 goto no_v4_nh;
David Ahernce9ac052020-06-08 20:54:43 -0600647 is_fdb_nh = nhg->fdb_nh;
David Ahernf88d8ea2019-06-03 20:19:52 -0700648 } else {
649 nhi = rtnl_dereference(nh->nh_info);
650 if (nhi->family == AF_INET)
651 goto no_v4_nh;
David Ahernce9ac052020-06-08 20:54:43 -0600652 is_fdb_nh = nhi->fdb_nh;
653 }
654
655 if (is_fdb_nh) {
656 NL_SET_ERR_MSG(extack, "Route cannot point to a fdb nexthop");
657 return -EINVAL;
David Ahernf88d8ea2019-06-03 20:19:52 -0700658 }
659
660 return 0;
661no_v4_nh:
662 NL_SET_ERR_MSG(extack, "IPv6 routes can not use an IPv4 nexthop");
663 return -EINVAL;
664}
665EXPORT_SYMBOL_GPL(fib6_check_nexthop);
666
David Ahern7bf47962019-06-08 14:53:35 -0700667/* if existing nexthop has ipv6 routes linked to it, need
668 * to verify this new spec works with ipv6
669 */
670static int fib6_check_nh_list(struct nexthop *old, struct nexthop *new,
671 struct netlink_ext_ack *extack)
672{
673 struct fib6_info *f6i;
674
675 if (list_empty(&old->f6i_list))
676 return 0;
677
678 list_for_each_entry(f6i, &old->f6i_list, nh_list) {
679 if (check_src_addr(&f6i->fib6_src.addr, extack) < 0)
680 return -EINVAL;
681 }
682
683 return fib6_check_nexthop(new, NULL, extack);
684}
685
David Ahernce9ac052020-06-08 20:54:43 -0600686static int nexthop_check_scope(struct nh_info *nhi, u8 scope,
David Ahern4c7e8082019-06-03 20:19:51 -0700687 struct netlink_ext_ack *extack)
688{
David Ahern4c7e8082019-06-03 20:19:51 -0700689 if (scope == RT_SCOPE_HOST && nhi->fib_nhc.nhc_gw_family) {
690 NL_SET_ERR_MSG(extack,
691 "Route with host scope can not have a gateway");
692 return -EINVAL;
693 }
694
695 if (nhi->fib_nhc.nhc_flags & RTNH_F_ONLINK && scope >= RT_SCOPE_LINK) {
696 NL_SET_ERR_MSG(extack, "Scope mismatch with nexthop");
697 return -EINVAL;
698 }
699
700 return 0;
701}
702
703/* Invoked by fib add code to verify nexthop by id is ok with
704 * config for prefix; parts of fib_check_nh not done when nexthop
705 * object is used.
706 */
707int fib_check_nexthop(struct nexthop *nh, u8 scope,
708 struct netlink_ext_ack *extack)
709{
David Ahernce9ac052020-06-08 20:54:43 -0600710 struct nh_info *nhi;
David Ahern4c7e8082019-06-03 20:19:51 -0700711 int err = 0;
712
713 if (nh->is_group) {
714 struct nh_group *nhg;
715
David Ahernce9ac052020-06-08 20:54:43 -0600716 nhg = rtnl_dereference(nh->nh_grp);
717 if (nhg->fdb_nh) {
718 NL_SET_ERR_MSG(extack, "Route cannot point to a fdb nexthop");
719 err = -EINVAL;
720 goto out;
721 }
722
David Ahern4c7e8082019-06-03 20:19:51 -0700723 if (scope == RT_SCOPE_HOST) {
724 NL_SET_ERR_MSG(extack, "Route with host scope can not have multiple nexthops");
725 err = -EINVAL;
726 goto out;
727 }
728
David Ahern4c7e8082019-06-03 20:19:51 -0700729 /* all nexthops in a group have the same scope */
David Ahernce9ac052020-06-08 20:54:43 -0600730 nhi = rtnl_dereference(nhg->nh_entries[0].nh->nh_info);
731 err = nexthop_check_scope(nhi, scope, extack);
David Ahern4c7e8082019-06-03 20:19:51 -0700732 } else {
David Ahernce9ac052020-06-08 20:54:43 -0600733 nhi = rtnl_dereference(nh->nh_info);
734 if (nhi->fdb_nh) {
735 NL_SET_ERR_MSG(extack, "Route cannot point to a fdb nexthop");
736 err = -EINVAL;
737 goto out;
738 }
739 err = nexthop_check_scope(nhi, scope, extack);
David Ahern4c7e8082019-06-03 20:19:51 -0700740 }
David Ahernce9ac052020-06-08 20:54:43 -0600741
David Ahern4c7e8082019-06-03 20:19:51 -0700742out:
743 return err;
744}
745
David Ahern7bf47962019-06-08 14:53:35 -0700746static int fib_check_nh_list(struct nexthop *old, struct nexthop *new,
747 struct netlink_ext_ack *extack)
748{
749 struct fib_info *fi;
750
751 list_for_each_entry(fi, &old->fi_list, nh_list) {
752 int err;
753
754 err = fib_check_nexthop(new, fi->fib_scope, extack);
755 if (err)
756 return err;
757 }
758 return 0;
759}
760
David Ahern430a0492019-05-24 14:43:08 -0700761static void nh_group_rebalance(struct nh_group *nhg)
762{
763 int total = 0;
764 int w = 0;
765 int i;
766
767 for (i = 0; i < nhg->num_nh; ++i)
768 total += nhg->nh_entries[i].weight;
769
770 for (i = 0; i < nhg->num_nh; ++i) {
771 struct nh_grp_entry *nhge = &nhg->nh_entries[i];
772 int upper_bound;
773
774 w += nhge->weight;
775 upper_bound = DIV_ROUND_CLOSEST_ULL((u64)w << 31, total) - 1;
776 atomic_set(&nhge->upper_bound, upper_bound);
777 }
778}
779
David Ahernac217532020-05-26 12:56:14 -0600780static void remove_nh_grp_entry(struct net *net, struct nh_grp_entry *nhge,
David Ahern430a0492019-05-24 14:43:08 -0700781 struct nl_info *nlinfo)
782{
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -0600783 struct nh_grp_entry *nhges, *new_nhges;
David Ahernac217532020-05-26 12:56:14 -0600784 struct nexthop *nhp = nhge->nh_parent;
David Ahern430a0492019-05-24 14:43:08 -0700785 struct nexthop *nh = nhge->nh;
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -0600786 struct nh_group *nhg, *newg;
787 int i, j;
David Ahern430a0492019-05-24 14:43:08 -0700788
789 WARN_ON(!nh);
790
David Ahernac217532020-05-26 12:56:14 -0600791 nhg = rtnl_dereference(nhp->nh_grp);
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -0600792 newg = nhg->spare;
793
794 /* last entry, keep it visible and remove the parent */
795 if (nhg->num_nh == 1) {
796 remove_nexthop(net, nhp, nlinfo);
797 return;
David Ahern430a0492019-05-24 14:43:08 -0700798 }
799
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -0600800 newg->has_v4 = nhg->has_v4;
801 newg->mpath = nhg->mpath;
David Ahernce9ac052020-06-08 20:54:43 -0600802 newg->fdb_nh = nhg->fdb_nh;
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -0600803 newg->num_nh = nhg->num_nh;
David Ahern430a0492019-05-24 14:43:08 -0700804
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -0600805 /* copy old entries to new except the one getting removed */
806 nhges = nhg->nh_entries;
807 new_nhges = newg->nh_entries;
808 for (i = 0, j = 0; i < nhg->num_nh; ++i) {
809 /* current nexthop getting removed */
810 if (nhg->nh_entries[i].nh == nh) {
811 newg->num_nh--;
812 continue;
813 }
David Ahern430a0492019-05-24 14:43:08 -0700814
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -0600815 list_del(&nhges[i].nh_list);
816 new_nhges[j].nh_parent = nhges[i].nh_parent;
817 new_nhges[j].nh = nhges[i].nh;
818 new_nhges[j].weight = nhges[i].weight;
819 list_add(&new_nhges[j].nh_list, &new_nhges[j].nh->grp_list);
820 j++;
821 }
David Ahern430a0492019-05-24 14:43:08 -0700822
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -0600823 nh_group_rebalance(newg);
824 rcu_assign_pointer(nhp->nh_grp, newg);
825
826 list_del(&nhge->nh_list);
827 nexthop_put(nhge->nh);
David Ahern430a0492019-05-24 14:43:08 -0700828
829 if (nlinfo)
David Ahernac217532020-05-26 12:56:14 -0600830 nexthop_notify(RTM_NEWNEXTHOP, nhp, nlinfo);
David Ahern430a0492019-05-24 14:43:08 -0700831}
832
833static void remove_nexthop_from_groups(struct net *net, struct nexthop *nh,
834 struct nl_info *nlinfo)
835{
836 struct nh_grp_entry *nhge, *tmp;
837
David Ahernac217532020-05-26 12:56:14 -0600838 list_for_each_entry_safe(nhge, tmp, &nh->grp_list, nh_list)
839 remove_nh_grp_entry(net, nhge, nlinfo);
David Ahern430a0492019-05-24 14:43:08 -0700840
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -0600841 /* make sure all see the newly published array before releasing rtnl */
842 synchronize_rcu();
David Ahern430a0492019-05-24 14:43:08 -0700843}
844
845static void remove_nexthop_group(struct nexthop *nh, struct nl_info *nlinfo)
846{
847 struct nh_group *nhg = rcu_dereference_rtnl(nh->nh_grp);
848 int i, num_nh = nhg->num_nh;
849
850 for (i = 0; i < num_nh; ++i) {
851 struct nh_grp_entry *nhge = &nhg->nh_entries[i];
852
853 if (WARN_ON(!nhge->nh))
854 continue;
855
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -0600856 list_del_init(&nhge->nh_list);
David Ahern430a0492019-05-24 14:43:08 -0700857 }
858}
859
David Ahern7bf47962019-06-08 14:53:35 -0700860/* not called for nexthop replace */
David Ahern4c7e8082019-06-03 20:19:51 -0700861static void __remove_nexthop_fib(struct net *net, struct nexthop *nh)
862{
David Ahernf88d8ea2019-06-03 20:19:52 -0700863 struct fib6_info *f6i, *tmp;
David Ahern4c7e8082019-06-03 20:19:51 -0700864 bool do_flush = false;
865 struct fib_info *fi;
866
Roopa Prabhu8590ceed2020-05-21 22:26:15 -0700867 call_nexthop_notifiers(net, NEXTHOP_EVENT_DEL, nh);
868
David Ahern4c7e8082019-06-03 20:19:51 -0700869 list_for_each_entry(fi, &nh->fi_list, nh_list) {
870 fi->fib_flags |= RTNH_F_DEAD;
871 do_flush = true;
872 }
873 if (do_flush)
874 fib_flush(net);
David Ahernf88d8ea2019-06-03 20:19:52 -0700875
876 /* ip6_del_rt removes the entry from this list hence the _safe */
877 list_for_each_entry_safe(f6i, tmp, &nh->f6i_list, nh_list) {
878 /* __ip6_del_rt does a release, so do a hold here */
879 fib6_info_hold(f6i);
Roopa Prabhu4f801162020-04-27 13:56:46 -0700880 ipv6_stub->ip6_del_rt(net, f6i,
881 !net->ipv4.sysctl_nexthop_compat_mode);
David Ahernf88d8ea2019-06-03 20:19:52 -0700882 }
David Ahern4c7e8082019-06-03 20:19:51 -0700883}
884
David Ahern430a0492019-05-24 14:43:08 -0700885static void __remove_nexthop(struct net *net, struct nexthop *nh,
886 struct nl_info *nlinfo)
887{
David Ahern4c7e8082019-06-03 20:19:51 -0700888 __remove_nexthop_fib(net, nh);
889
David Ahern430a0492019-05-24 14:43:08 -0700890 if (nh->is_group) {
891 remove_nexthop_group(nh, nlinfo);
892 } else {
893 struct nh_info *nhi;
894
895 nhi = rtnl_dereference(nh->nh_info);
896 if (nhi->fib_nhc.nhc_dev)
897 hlist_del(&nhi->dev_hash);
898
899 remove_nexthop_from_groups(net, nh, nlinfo);
900 }
David Ahern597cfe4f2019-05-24 14:43:05 -0700901}
902
David Ahernab84be72019-05-24 14:43:04 -0700903static void remove_nexthop(struct net *net, struct nexthop *nh,
David Ahern430a0492019-05-24 14:43:08 -0700904 struct nl_info *nlinfo)
David Ahernab84be72019-05-24 14:43:04 -0700905{
906 /* remove from the tree */
907 rb_erase(&nh->rb_node, &net->nexthop.rb_root);
908
909 if (nlinfo)
910 nexthop_notify(RTM_DELNEXTHOP, nh, nlinfo);
911
David Ahern430a0492019-05-24 14:43:08 -0700912 __remove_nexthop(net, nh, nlinfo);
David Ahernab84be72019-05-24 14:43:04 -0700913 nh_base_seq_inc(net);
914
915 nexthop_put(nh);
916}
917
David Ahern7bf47962019-06-08 14:53:35 -0700918/* if any FIB entries reference this nexthop, any dst entries
919 * need to be regenerated
920 */
921static void nh_rt_cache_flush(struct net *net, struct nexthop *nh)
922{
923 struct fib6_info *f6i;
924
925 if (!list_empty(&nh->fi_list))
926 rt_cache_flush(net);
927
928 list_for_each_entry(f6i, &nh->f6i_list, nh_list)
929 ipv6_stub->fib6_update_sernum(net, f6i);
930}
931
932static int replace_nexthop_grp(struct net *net, struct nexthop *old,
933 struct nexthop *new,
934 struct netlink_ext_ack *extack)
935{
936 struct nh_group *oldg, *newg;
937 int i;
938
939 if (!new->is_group) {
940 NL_SET_ERR_MSG(extack, "Can not replace a nexthop group with a nexthop.");
941 return -EINVAL;
942 }
943
944 oldg = rtnl_dereference(old->nh_grp);
945 newg = rtnl_dereference(new->nh_grp);
946
947 /* update parents - used by nexthop code for cleanup */
948 for (i = 0; i < newg->num_nh; i++)
949 newg->nh_entries[i].nh_parent = old;
950
951 rcu_assign_pointer(old->nh_grp, newg);
952
953 for (i = 0; i < oldg->num_nh; i++)
954 oldg->nh_entries[i].nh_parent = new;
955
956 rcu_assign_pointer(new->nh_grp, oldg);
957
958 return 0;
959}
960
961static int replace_nexthop_single(struct net *net, struct nexthop *old,
962 struct nexthop *new,
963 struct netlink_ext_ack *extack)
964{
965 struct nh_info *oldi, *newi;
966
967 if (new->is_group) {
968 NL_SET_ERR_MSG(extack, "Can not replace a nexthop with a nexthop group.");
969 return -EINVAL;
970 }
971
972 oldi = rtnl_dereference(old->nh_info);
973 newi = rtnl_dereference(new->nh_info);
974
975 newi->nh_parent = old;
976 oldi->nh_parent = new;
977
978 old->protocol = new->protocol;
979 old->nh_flags = new->nh_flags;
980
981 rcu_assign_pointer(old->nh_info, newi);
982 rcu_assign_pointer(new->nh_info, oldi);
983
984 return 0;
985}
986
987static void __nexthop_replace_notify(struct net *net, struct nexthop *nh,
988 struct nl_info *info)
989{
990 struct fib6_info *f6i;
991
992 if (!list_empty(&nh->fi_list)) {
993 struct fib_info *fi;
994
995 /* expectation is a few fib_info per nexthop and then
996 * a lot of routes per fib_info. So mark the fib_info
997 * and then walk the fib tables once
998 */
999 list_for_each_entry(fi, &nh->fi_list, nh_list)
1000 fi->nh_updated = true;
1001
1002 fib_info_notify_update(net, info);
1003
1004 list_for_each_entry(fi, &nh->fi_list, nh_list)
1005 fi->nh_updated = false;
1006 }
1007
1008 list_for_each_entry(f6i, &nh->f6i_list, nh_list)
1009 ipv6_stub->fib6_rt_update(net, f6i, info);
1010}
1011
1012/* send RTM_NEWROUTE with REPLACE flag set for all FIB entries
1013 * linked to this nexthop and for all groups that the nexthop
1014 * is a member of
1015 */
1016static void nexthop_replace_notify(struct net *net, struct nexthop *nh,
1017 struct nl_info *info)
1018{
1019 struct nh_grp_entry *nhge;
1020
1021 __nexthop_replace_notify(net, nh, info);
1022
1023 list_for_each_entry(nhge, &nh->grp_list, nh_list)
1024 __nexthop_replace_notify(net, nhge->nh_parent, info);
1025}
1026
David Ahernab84be72019-05-24 14:43:04 -07001027static int replace_nexthop(struct net *net, struct nexthop *old,
1028 struct nexthop *new, struct netlink_ext_ack *extack)
1029{
David Ahern7bf47962019-06-08 14:53:35 -07001030 bool new_is_reject = false;
1031 struct nh_grp_entry *nhge;
1032 int err;
1033
1034 /* check that existing FIB entries are ok with the
1035 * new nexthop definition
1036 */
1037 err = fib_check_nh_list(old, new, extack);
1038 if (err)
1039 return err;
1040
1041 err = fib6_check_nh_list(old, new, extack);
1042 if (err)
1043 return err;
1044
1045 if (!new->is_group) {
1046 struct nh_info *nhi = rtnl_dereference(new->nh_info);
1047
1048 new_is_reject = nhi->reject_nh;
1049 }
1050
1051 list_for_each_entry(nhge, &old->grp_list, nh_list) {
1052 /* if new nexthop is a blackhole, any groups using this
1053 * nexthop cannot have more than 1 path
1054 */
1055 if (new_is_reject &&
1056 nexthop_num_path(nhge->nh_parent) > 1) {
1057 NL_SET_ERR_MSG(extack, "Blackhole nexthop can not be a member of a group with more than one path");
1058 return -EINVAL;
1059 }
1060
1061 err = fib_check_nh_list(nhge->nh_parent, new, extack);
1062 if (err)
1063 return err;
1064
1065 err = fib6_check_nh_list(nhge->nh_parent, new, extack);
1066 if (err)
1067 return err;
1068 }
1069
1070 if (old->is_group)
1071 err = replace_nexthop_grp(net, old, new, extack);
1072 else
1073 err = replace_nexthop_single(net, old, new, extack);
1074
1075 if (!err) {
1076 nh_rt_cache_flush(net, old);
1077
1078 __remove_nexthop(net, new, NULL);
1079 nexthop_put(new);
1080 }
1081
1082 return err;
David Ahernab84be72019-05-24 14:43:04 -07001083}
1084
1085/* called with rtnl_lock held */
1086static int insert_nexthop(struct net *net, struct nexthop *new_nh,
1087 struct nh_config *cfg, struct netlink_ext_ack *extack)
1088{
1089 struct rb_node **pp, *parent = NULL, *next;
1090 struct rb_root *root = &net->nexthop.rb_root;
1091 bool replace = !!(cfg->nlflags & NLM_F_REPLACE);
1092 bool create = !!(cfg->nlflags & NLM_F_CREATE);
1093 u32 new_id = new_nh->id;
David Ahern7bf47962019-06-08 14:53:35 -07001094 int replace_notify = 0;
David Ahernab84be72019-05-24 14:43:04 -07001095 int rc = -EEXIST;
1096
1097 pp = &root->rb_node;
1098 while (1) {
1099 struct nexthop *nh;
1100
1101 next = rtnl_dereference(*pp);
1102 if (!next)
1103 break;
1104
1105 parent = next;
1106
1107 nh = rb_entry(parent, struct nexthop, rb_node);
1108 if (new_id < nh->id) {
1109 pp = &next->rb_left;
1110 } else if (new_id > nh->id) {
1111 pp = &next->rb_right;
1112 } else if (replace) {
1113 rc = replace_nexthop(net, nh, new_nh, extack);
David Ahern7bf47962019-06-08 14:53:35 -07001114 if (!rc) {
David Ahernab84be72019-05-24 14:43:04 -07001115 new_nh = nh; /* send notification with old nh */
David Ahern7bf47962019-06-08 14:53:35 -07001116 replace_notify = 1;
1117 }
David Ahernab84be72019-05-24 14:43:04 -07001118 goto out;
1119 } else {
1120 /* id already exists and not a replace */
1121 goto out;
1122 }
1123 }
1124
1125 if (replace && !create) {
1126 NL_SET_ERR_MSG(extack, "Replace specified without create and no entry exists");
1127 rc = -ENOENT;
1128 goto out;
1129 }
1130
1131 rb_link_node_rcu(&new_nh->rb_node, parent, pp);
1132 rb_insert_color(&new_nh->rb_node, root);
1133 rc = 0;
1134out:
1135 if (!rc) {
1136 nh_base_seq_inc(net);
1137 nexthop_notify(RTM_NEWNEXTHOP, new_nh, &cfg->nlinfo);
Roopa Prabhu4f801162020-04-27 13:56:46 -07001138 if (replace_notify && net->ipv4.sysctl_nexthop_compat_mode)
David Ahern7bf47962019-06-08 14:53:35 -07001139 nexthop_replace_notify(net, new_nh, &cfg->nlinfo);
David Ahernab84be72019-05-24 14:43:04 -07001140 }
1141
1142 return rc;
1143}
1144
David Ahern597cfe4f2019-05-24 14:43:05 -07001145/* rtnl */
1146/* remove all nexthops tied to a device being deleted */
1147static void nexthop_flush_dev(struct net_device *dev)
1148{
1149 unsigned int hash = nh_dev_hashfn(dev->ifindex);
1150 struct net *net = dev_net(dev);
1151 struct hlist_head *head = &net->nexthop.devhash[hash];
1152 struct hlist_node *n;
1153 struct nh_info *nhi;
1154
1155 hlist_for_each_entry_safe(nhi, n, head, dev_hash) {
1156 if (nhi->fib_nhc.nhc_dev != dev)
1157 continue;
1158
David Ahern430a0492019-05-24 14:43:08 -07001159 remove_nexthop(net, nhi->nh_parent, NULL);
David Ahern597cfe4f2019-05-24 14:43:05 -07001160 }
1161}
1162
David Ahernab84be72019-05-24 14:43:04 -07001163/* rtnl; called when net namespace is deleted */
1164static void flush_all_nexthops(struct net *net)
1165{
1166 struct rb_root *root = &net->nexthop.rb_root;
1167 struct rb_node *node;
1168 struct nexthop *nh;
1169
1170 while ((node = rb_first(root))) {
1171 nh = rb_entry(node, struct nexthop, rb_node);
David Ahern430a0492019-05-24 14:43:08 -07001172 remove_nexthop(net, nh, NULL);
David Ahernab84be72019-05-24 14:43:04 -07001173 cond_resched();
1174 }
1175}
1176
David Ahern430a0492019-05-24 14:43:08 -07001177static struct nexthop *nexthop_create_group(struct net *net,
1178 struct nh_config *cfg)
1179{
1180 struct nlattr *grps_attr = cfg->nh_grp;
1181 struct nexthop_grp *entry = nla_data(grps_attr);
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -06001182 u16 num_nh = nla_len(grps_attr) / sizeof(*entry);
David Ahern430a0492019-05-24 14:43:08 -07001183 struct nh_group *nhg;
1184 struct nexthop *nh;
1185 int i;
1186
Nikolay Aleksandroveeaac362020-08-22 15:06:36 +03001187 if (WARN_ON(!num_nh))
1188 return ERR_PTR(-EINVAL);
1189
David Ahern430a0492019-05-24 14:43:08 -07001190 nh = nexthop_alloc();
1191 if (!nh)
1192 return ERR_PTR(-ENOMEM);
1193
1194 nh->is_group = 1;
1195
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -06001196 nhg = nexthop_grp_alloc(num_nh);
David Ahern430a0492019-05-24 14:43:08 -07001197 if (!nhg) {
1198 kfree(nh);
1199 return ERR_PTR(-ENOMEM);
1200 }
1201
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -06001202 /* spare group used for removals */
1203 nhg->spare = nexthop_grp_alloc(num_nh);
Patrick Eigensatzdafe2072020-06-01 13:12:01 +02001204 if (!nhg->spare) {
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -06001205 kfree(nhg);
1206 kfree(nh);
Patrick Eigensatzdafe2072020-06-01 13:12:01 +02001207 return ERR_PTR(-ENOMEM);
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -06001208 }
1209 nhg->spare->spare = nhg;
1210
David Ahern430a0492019-05-24 14:43:08 -07001211 for (i = 0; i < nhg->num_nh; ++i) {
1212 struct nexthop *nhe;
1213 struct nh_info *nhi;
1214
1215 nhe = nexthop_find_by_id(net, entry[i].id);
1216 if (!nexthop_get(nhe))
1217 goto out_no_nh;
1218
1219 nhi = rtnl_dereference(nhe->nh_info);
1220 if (nhi->family == AF_INET)
1221 nhg->has_v4 = true;
1222
1223 nhg->nh_entries[i].nh = nhe;
1224 nhg->nh_entries[i].weight = entry[i].weight + 1;
1225 list_add(&nhg->nh_entries[i].nh_list, &nhe->grp_list);
1226 nhg->nh_entries[i].nh_parent = nh;
1227 }
1228
1229 if (cfg->nh_grp_type == NEXTHOP_GRP_TYPE_MPATH) {
1230 nhg->mpath = 1;
1231 nh_group_rebalance(nhg);
1232 }
1233
Roopa Prabhu38428d62020-05-21 22:26:13 -07001234 if (cfg->nh_fdb)
David Ahernce9ac052020-06-08 20:54:43 -06001235 nhg->fdb_nh = 1;
Roopa Prabhu38428d62020-05-21 22:26:13 -07001236
David Ahern430a0492019-05-24 14:43:08 -07001237 rcu_assign_pointer(nh->nh_grp, nhg);
1238
1239 return nh;
1240
1241out_no_nh:
1242 for (; i >= 0; --i)
1243 nexthop_put(nhg->nh_entries[i].nh);
1244
Nikolay Aleksandrov90f33bf2020-05-26 12:56:15 -06001245 kfree(nhg->spare);
David Ahern430a0492019-05-24 14:43:08 -07001246 kfree(nhg);
1247 kfree(nh);
1248
1249 return ERR_PTR(-ENOENT);
1250}
1251
David Ahern597cfe4f2019-05-24 14:43:05 -07001252static int nh_create_ipv4(struct net *net, struct nexthop *nh,
1253 struct nh_info *nhi, struct nh_config *cfg,
1254 struct netlink_ext_ack *extack)
1255{
1256 struct fib_nh *fib_nh = &nhi->fib_nh;
1257 struct fib_config fib_cfg = {
1258 .fc_oif = cfg->nh_ifindex,
1259 .fc_gw4 = cfg->gw.ipv4,
1260 .fc_gw_family = cfg->gw.ipv4 ? AF_INET : 0,
1261 .fc_flags = cfg->nh_flags,
David Ahernb513bd02019-05-24 14:43:07 -07001262 .fc_encap = cfg->nh_encap,
1263 .fc_encap_type = cfg->nh_encap_type,
David Ahern597cfe4f2019-05-24 14:43:05 -07001264 };
Roopa Prabhu38428d62020-05-21 22:26:13 -07001265 u32 tb_id = (cfg->dev ? l3mdev_fib_table(cfg->dev) : RT_TABLE_MAIN);
Colin Ian Kingc76c9922019-08-22 13:53:40 +01001266 int err;
David Ahern597cfe4f2019-05-24 14:43:05 -07001267
1268 err = fib_nh_init(net, fib_nh, &fib_cfg, 1, extack);
1269 if (err) {
1270 fib_nh_release(net, fib_nh);
1271 goto out;
1272 }
1273
David Ahernce9ac052020-06-08 20:54:43 -06001274 if (nhi->fdb_nh)
Roopa Prabhu38428d62020-05-21 22:26:13 -07001275 goto out;
1276
David Ahern597cfe4f2019-05-24 14:43:05 -07001277 /* sets nh_dev if successful */
1278 err = fib_check_nh(net, fib_nh, tb_id, 0, extack);
1279 if (!err) {
1280 nh->nh_flags = fib_nh->fib_nh_flags;
David Aherndcb1ecb2019-06-03 20:19:50 -07001281 fib_info_update_nhc_saddr(net, &fib_nh->nh_common,
1282 fib_nh->fib_nh_scope);
David Ahern597cfe4f2019-05-24 14:43:05 -07001283 } else {
1284 fib_nh_release(net, fib_nh);
1285 }
1286out:
1287 return err;
1288}
1289
David Ahern53010f92019-05-24 14:43:06 -07001290static int nh_create_ipv6(struct net *net, struct nexthop *nh,
1291 struct nh_info *nhi, struct nh_config *cfg,
1292 struct netlink_ext_ack *extack)
1293{
1294 struct fib6_nh *fib6_nh = &nhi->fib6_nh;
1295 struct fib6_config fib6_cfg = {
1296 .fc_table = l3mdev_fib_table(cfg->dev),
1297 .fc_ifindex = cfg->nh_ifindex,
1298 .fc_gateway = cfg->gw.ipv6,
1299 .fc_flags = cfg->nh_flags,
David Ahernb513bd02019-05-24 14:43:07 -07001300 .fc_encap = cfg->nh_encap,
1301 .fc_encap_type = cfg->nh_encap_type,
Roopa Prabhu38428d62020-05-21 22:26:13 -07001302 .fc_is_fdb = cfg->nh_fdb,
David Ahern53010f92019-05-24 14:43:06 -07001303 };
Colin Ian King6f43e522019-05-30 16:57:54 +01001304 int err;
David Ahern53010f92019-05-24 14:43:06 -07001305
1306 if (!ipv6_addr_any(&cfg->gw.ipv6))
1307 fib6_cfg.fc_flags |= RTF_GATEWAY;
1308
1309 /* sets nh_dev if successful */
1310 err = ipv6_stub->fib6_nh_init(net, fib6_nh, &fib6_cfg, GFP_KERNEL,
1311 extack);
1312 if (err)
1313 ipv6_stub->fib6_nh_release(fib6_nh);
1314 else
1315 nh->nh_flags = fib6_nh->fib_nh_flags;
1316
1317 return err;
1318}
1319
David Ahernab84be72019-05-24 14:43:04 -07001320static struct nexthop *nexthop_create(struct net *net, struct nh_config *cfg,
1321 struct netlink_ext_ack *extack)
1322{
1323 struct nh_info *nhi;
1324 struct nexthop *nh;
1325 int err = 0;
1326
1327 nh = nexthop_alloc();
1328 if (!nh)
1329 return ERR_PTR(-ENOMEM);
1330
1331 nhi = kzalloc(sizeof(*nhi), GFP_KERNEL);
1332 if (!nhi) {
1333 kfree(nh);
1334 return ERR_PTR(-ENOMEM);
1335 }
1336
1337 nh->nh_flags = cfg->nh_flags;
1338 nh->net = net;
1339
1340 nhi->nh_parent = nh;
1341 nhi->family = cfg->nh_family;
1342 nhi->fib_nhc.nhc_scope = RT_SCOPE_LINK;
1343
Roopa Prabhu38428d62020-05-21 22:26:13 -07001344 if (cfg->nh_fdb)
David Ahernce9ac052020-06-08 20:54:43 -06001345 nhi->fdb_nh = 1;
Roopa Prabhu38428d62020-05-21 22:26:13 -07001346
David Ahernab84be72019-05-24 14:43:04 -07001347 if (cfg->nh_blackhole) {
1348 nhi->reject_nh = 1;
1349 cfg->nh_ifindex = net->loopback_dev->ifindex;
1350 }
1351
David Ahern597cfe4f2019-05-24 14:43:05 -07001352 switch (cfg->nh_family) {
1353 case AF_INET:
1354 err = nh_create_ipv4(net, nh, nhi, cfg, extack);
1355 break;
David Ahern53010f92019-05-24 14:43:06 -07001356 case AF_INET6:
1357 err = nh_create_ipv6(net, nh, nhi, cfg, extack);
1358 break;
David Ahern597cfe4f2019-05-24 14:43:05 -07001359 }
1360
David Ahernab84be72019-05-24 14:43:04 -07001361 if (err) {
1362 kfree(nhi);
1363 kfree(nh);
1364 return ERR_PTR(err);
1365 }
1366
David Ahern597cfe4f2019-05-24 14:43:05 -07001367 /* add the entry to the device based hash */
David Ahernce9ac052020-06-08 20:54:43 -06001368 if (!nhi->fdb_nh)
Roopa Prabhu38428d62020-05-21 22:26:13 -07001369 nexthop_devhash_add(net, nhi);
David Ahern597cfe4f2019-05-24 14:43:05 -07001370
David Ahernab84be72019-05-24 14:43:04 -07001371 rcu_assign_pointer(nh->nh_info, nhi);
1372
1373 return nh;
1374}
1375
1376/* called with rtnl lock held */
1377static struct nexthop *nexthop_add(struct net *net, struct nh_config *cfg,
1378 struct netlink_ext_ack *extack)
1379{
1380 struct nexthop *nh;
1381 int err;
1382
1383 if (cfg->nlflags & NLM_F_REPLACE && !cfg->nh_id) {
1384 NL_SET_ERR_MSG(extack, "Replace requires nexthop id");
1385 return ERR_PTR(-EINVAL);
1386 }
1387
1388 if (!cfg->nh_id) {
1389 cfg->nh_id = nh_find_unused_id(net);
1390 if (!cfg->nh_id) {
1391 NL_SET_ERR_MSG(extack, "No unused id");
1392 return ERR_PTR(-EINVAL);
1393 }
1394 }
1395
David Ahern430a0492019-05-24 14:43:08 -07001396 if (cfg->nh_grp)
1397 nh = nexthop_create_group(net, cfg);
1398 else
1399 nh = nexthop_create(net, cfg, extack);
1400
David Ahernab84be72019-05-24 14:43:04 -07001401 if (IS_ERR(nh))
1402 return nh;
1403
1404 refcount_set(&nh->refcnt, 1);
1405 nh->id = cfg->nh_id;
1406 nh->protocol = cfg->nh_protocol;
1407 nh->net = net;
1408
1409 err = insert_nexthop(net, nh, cfg, extack);
1410 if (err) {
David Ahern430a0492019-05-24 14:43:08 -07001411 __remove_nexthop(net, nh, NULL);
David Ahernab84be72019-05-24 14:43:04 -07001412 nexthop_put(nh);
1413 nh = ERR_PTR(err);
1414 }
1415
1416 return nh;
1417}
1418
1419static int rtm_to_nh_config(struct net *net, struct sk_buff *skb,
1420 struct nlmsghdr *nlh, struct nh_config *cfg,
1421 struct netlink_ext_ack *extack)
1422{
1423 struct nhmsg *nhm = nlmsg_data(nlh);
1424 struct nlattr *tb[NHA_MAX + 1];
1425 int err;
1426
1427 err = nlmsg_parse(nlh, sizeof(*nhm), tb, NHA_MAX, rtm_nh_policy,
1428 extack);
1429 if (err < 0)
1430 return err;
1431
1432 err = -EINVAL;
1433 if (nhm->resvd || nhm->nh_scope) {
1434 NL_SET_ERR_MSG(extack, "Invalid values in ancillary header");
1435 goto out;
1436 }
1437 if (nhm->nh_flags & ~NEXTHOP_VALID_USER_FLAGS) {
1438 NL_SET_ERR_MSG(extack, "Invalid nexthop flags in ancillary header");
1439 goto out;
1440 }
1441
1442 switch (nhm->nh_family) {
David Ahern597cfe4f2019-05-24 14:43:05 -07001443 case AF_INET:
David Ahern53010f92019-05-24 14:43:06 -07001444 case AF_INET6:
David Ahern597cfe4f2019-05-24 14:43:05 -07001445 break;
David Ahern430a0492019-05-24 14:43:08 -07001446 case AF_UNSPEC:
1447 if (tb[NHA_GROUP])
1448 break;
Joe Perchesa8eceea2020-03-12 15:50:22 -07001449 fallthrough;
David Ahernab84be72019-05-24 14:43:04 -07001450 default:
1451 NL_SET_ERR_MSG(extack, "Invalid address family");
1452 goto out;
1453 }
1454
1455 if (tb[NHA_GROUPS] || tb[NHA_MASTER]) {
1456 NL_SET_ERR_MSG(extack, "Invalid attributes in request");
1457 goto out;
1458 }
1459
1460 memset(cfg, 0, sizeof(*cfg));
1461 cfg->nlflags = nlh->nlmsg_flags;
1462 cfg->nlinfo.portid = NETLINK_CB(skb).portid;
1463 cfg->nlinfo.nlh = nlh;
1464 cfg->nlinfo.nl_net = net;
1465
1466 cfg->nh_family = nhm->nh_family;
1467 cfg->nh_protocol = nhm->nh_protocol;
1468 cfg->nh_flags = nhm->nh_flags;
1469
1470 if (tb[NHA_ID])
1471 cfg->nh_id = nla_get_u32(tb[NHA_ID]);
1472
Roopa Prabhu38428d62020-05-21 22:26:13 -07001473 if (tb[NHA_FDB]) {
1474 if (tb[NHA_OIF] || tb[NHA_BLACKHOLE] ||
1475 tb[NHA_ENCAP] || tb[NHA_ENCAP_TYPE]) {
1476 NL_SET_ERR_MSG(extack, "Fdb attribute can not be used with encap, oif or blackhole");
1477 goto out;
1478 }
1479 if (nhm->nh_flags) {
1480 NL_SET_ERR_MSG(extack, "Unsupported nexthop flags in ancillary header");
1481 goto out;
1482 }
1483 cfg->nh_fdb = nla_get_flag(tb[NHA_FDB]);
1484 }
1485
David Ahern430a0492019-05-24 14:43:08 -07001486 if (tb[NHA_GROUP]) {
1487 if (nhm->nh_family != AF_UNSPEC) {
1488 NL_SET_ERR_MSG(extack, "Invalid family for group");
1489 goto out;
1490 }
1491 cfg->nh_grp = tb[NHA_GROUP];
1492
1493 cfg->nh_grp_type = NEXTHOP_GRP_TYPE_MPATH;
1494 if (tb[NHA_GROUP_TYPE])
1495 cfg->nh_grp_type = nla_get_u16(tb[NHA_GROUP_TYPE]);
1496
1497 if (cfg->nh_grp_type > NEXTHOP_GRP_TYPE_MAX) {
1498 NL_SET_ERR_MSG(extack, "Invalid group type");
1499 goto out;
1500 }
1501 err = nh_check_attr_group(net, tb, extack);
1502
1503 /* no other attributes should be set */
1504 goto out;
1505 }
1506
David Ahernab84be72019-05-24 14:43:04 -07001507 if (tb[NHA_BLACKHOLE]) {
David Ahernb513bd02019-05-24 14:43:07 -07001508 if (tb[NHA_GATEWAY] || tb[NHA_OIF] ||
Roopa Prabhu38428d62020-05-21 22:26:13 -07001509 tb[NHA_ENCAP] || tb[NHA_ENCAP_TYPE] || tb[NHA_FDB]) {
1510 NL_SET_ERR_MSG(extack, "Blackhole attribute can not be used with gateway, oif, encap or fdb");
David Ahernab84be72019-05-24 14:43:04 -07001511 goto out;
1512 }
1513
1514 cfg->nh_blackhole = 1;
1515 err = 0;
1516 goto out;
1517 }
1518
Roopa Prabhu38428d62020-05-21 22:26:13 -07001519 if (!cfg->nh_fdb && !tb[NHA_OIF]) {
1520 NL_SET_ERR_MSG(extack, "Device attribute required for non-blackhole and non-fdb nexthops");
David Ahernab84be72019-05-24 14:43:04 -07001521 goto out;
1522 }
1523
Roopa Prabhu38428d62020-05-21 22:26:13 -07001524 if (!cfg->nh_fdb && tb[NHA_OIF]) {
1525 cfg->nh_ifindex = nla_get_u32(tb[NHA_OIF]);
1526 if (cfg->nh_ifindex)
1527 cfg->dev = __dev_get_by_index(net, cfg->nh_ifindex);
David Ahernab84be72019-05-24 14:43:04 -07001528
Roopa Prabhu38428d62020-05-21 22:26:13 -07001529 if (!cfg->dev) {
1530 NL_SET_ERR_MSG(extack, "Invalid device index");
1531 goto out;
1532 } else if (!(cfg->dev->flags & IFF_UP)) {
1533 NL_SET_ERR_MSG(extack, "Nexthop device is not up");
1534 err = -ENETDOWN;
1535 goto out;
1536 } else if (!netif_carrier_ok(cfg->dev)) {
1537 NL_SET_ERR_MSG(extack, "Carrier for nexthop device is down");
1538 err = -ENETDOWN;
1539 goto out;
1540 }
David Ahernab84be72019-05-24 14:43:04 -07001541 }
1542
David Ahern597cfe4f2019-05-24 14:43:05 -07001543 err = -EINVAL;
1544 if (tb[NHA_GATEWAY]) {
1545 struct nlattr *gwa = tb[NHA_GATEWAY];
1546
1547 switch (cfg->nh_family) {
1548 case AF_INET:
1549 if (nla_len(gwa) != sizeof(u32)) {
1550 NL_SET_ERR_MSG(extack, "Invalid gateway");
1551 goto out;
1552 }
1553 cfg->gw.ipv4 = nla_get_be32(gwa);
1554 break;
David Ahern53010f92019-05-24 14:43:06 -07001555 case AF_INET6:
1556 if (nla_len(gwa) != sizeof(struct in6_addr)) {
1557 NL_SET_ERR_MSG(extack, "Invalid gateway");
1558 goto out;
1559 }
1560 cfg->gw.ipv6 = nla_get_in6_addr(gwa);
1561 break;
David Ahern597cfe4f2019-05-24 14:43:05 -07001562 default:
1563 NL_SET_ERR_MSG(extack,
1564 "Unknown address family for gateway");
1565 goto out;
1566 }
1567 } else {
1568 /* device only nexthop (no gateway) */
1569 if (cfg->nh_flags & RTNH_F_ONLINK) {
1570 NL_SET_ERR_MSG(extack,
1571 "ONLINK flag can not be set for nexthop without a gateway");
1572 goto out;
1573 }
1574 }
1575
David Ahernb513bd02019-05-24 14:43:07 -07001576 if (tb[NHA_ENCAP]) {
1577 cfg->nh_encap = tb[NHA_ENCAP];
1578
1579 if (!tb[NHA_ENCAP_TYPE]) {
1580 NL_SET_ERR_MSG(extack, "LWT encapsulation type is missing");
1581 goto out;
1582 }
1583
1584 cfg->nh_encap_type = nla_get_u16(tb[NHA_ENCAP_TYPE]);
1585 err = lwtunnel_valid_encap_type(cfg->nh_encap_type, extack);
1586 if (err < 0)
1587 goto out;
1588
1589 } else if (tb[NHA_ENCAP_TYPE]) {
1590 NL_SET_ERR_MSG(extack, "LWT encapsulation attribute is missing");
1591 goto out;
1592 }
1593
1594
David Ahernab84be72019-05-24 14:43:04 -07001595 err = 0;
1596out:
1597 return err;
1598}
1599
1600/* rtnl */
1601static int rtm_new_nexthop(struct sk_buff *skb, struct nlmsghdr *nlh,
1602 struct netlink_ext_ack *extack)
1603{
1604 struct net *net = sock_net(skb->sk);
1605 struct nh_config cfg;
1606 struct nexthop *nh;
1607 int err;
1608
1609 err = rtm_to_nh_config(net, skb, nlh, &cfg, extack);
1610 if (!err) {
1611 nh = nexthop_add(net, &cfg, extack);
1612 if (IS_ERR(nh))
1613 err = PTR_ERR(nh);
1614 }
1615
1616 return err;
1617}
1618
1619static int nh_valid_get_del_req(struct nlmsghdr *nlh, u32 *id,
1620 struct netlink_ext_ack *extack)
1621{
1622 struct nhmsg *nhm = nlmsg_data(nlh);
1623 struct nlattr *tb[NHA_MAX + 1];
1624 int err, i;
1625
1626 err = nlmsg_parse(nlh, sizeof(*nhm), tb, NHA_MAX, rtm_nh_policy,
1627 extack);
1628 if (err < 0)
1629 return err;
1630
1631 err = -EINVAL;
1632 for (i = 0; i < __NHA_MAX; ++i) {
1633 if (!tb[i])
1634 continue;
1635
1636 switch (i) {
1637 case NHA_ID:
1638 break;
1639 default:
1640 NL_SET_ERR_MSG_ATTR(extack, tb[i],
1641 "Unexpected attribute in request");
1642 goto out;
1643 }
1644 }
1645 if (nhm->nh_protocol || nhm->resvd || nhm->nh_scope || nhm->nh_flags) {
1646 NL_SET_ERR_MSG(extack, "Invalid values in header");
1647 goto out;
1648 }
1649
1650 if (!tb[NHA_ID]) {
1651 NL_SET_ERR_MSG(extack, "Nexthop id is missing");
1652 goto out;
1653 }
1654
1655 *id = nla_get_u32(tb[NHA_ID]);
1656 if (!(*id))
1657 NL_SET_ERR_MSG(extack, "Invalid nexthop id");
1658 else
1659 err = 0;
1660out:
1661 return err;
1662}
1663
1664/* rtnl */
1665static int rtm_del_nexthop(struct sk_buff *skb, struct nlmsghdr *nlh,
1666 struct netlink_ext_ack *extack)
1667{
1668 struct net *net = sock_net(skb->sk);
1669 struct nl_info nlinfo = {
1670 .nlh = nlh,
1671 .nl_net = net,
1672 .portid = NETLINK_CB(skb).portid,
1673 };
1674 struct nexthop *nh;
1675 int err;
1676 u32 id;
1677
1678 err = nh_valid_get_del_req(nlh, &id, extack);
1679 if (err)
1680 return err;
1681
1682 nh = nexthop_find_by_id(net, id);
1683 if (!nh)
1684 return -ENOENT;
1685
David Ahern430a0492019-05-24 14:43:08 -07001686 remove_nexthop(net, nh, &nlinfo);
David Ahernab84be72019-05-24 14:43:04 -07001687
1688 return 0;
1689}
1690
1691/* rtnl */
1692static int rtm_get_nexthop(struct sk_buff *in_skb, struct nlmsghdr *nlh,
1693 struct netlink_ext_ack *extack)
1694{
1695 struct net *net = sock_net(in_skb->sk);
1696 struct sk_buff *skb = NULL;
1697 struct nexthop *nh;
1698 int err;
1699 u32 id;
1700
1701 err = nh_valid_get_del_req(nlh, &id, extack);
1702 if (err)
1703 return err;
1704
1705 err = -ENOBUFS;
1706 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
1707 if (!skb)
1708 goto out;
1709
1710 err = -ENOENT;
1711 nh = nexthop_find_by_id(net, id);
1712 if (!nh)
1713 goto errout_free;
1714
1715 err = nh_fill_node(skb, nh, RTM_NEWNEXTHOP, NETLINK_CB(in_skb).portid,
1716 nlh->nlmsg_seq, 0);
1717 if (err < 0) {
1718 WARN_ON(err == -EMSGSIZE);
1719 goto errout_free;
1720 }
1721
1722 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
1723out:
1724 return err;
1725errout_free:
1726 kfree_skb(skb);
1727 goto out;
1728}
1729
David Ahern430a0492019-05-24 14:43:08 -07001730static bool nh_dump_filtered(struct nexthop *nh, int dev_idx, int master_idx,
1731 bool group_filter, u8 family)
David Ahernab84be72019-05-24 14:43:04 -07001732{
1733 const struct net_device *dev;
1734 const struct nh_info *nhi;
1735
David Ahern430a0492019-05-24 14:43:08 -07001736 if (group_filter && !nh->is_group)
1737 return true;
1738
David Ahernab84be72019-05-24 14:43:04 -07001739 if (!dev_idx && !master_idx && !family)
1740 return false;
1741
David Ahern430a0492019-05-24 14:43:08 -07001742 if (nh->is_group)
1743 return true;
1744
David Ahernab84be72019-05-24 14:43:04 -07001745 nhi = rtnl_dereference(nh->nh_info);
1746 if (family && nhi->family != family)
1747 return true;
1748
1749 dev = nhi->fib_nhc.nhc_dev;
1750 if (dev_idx && (!dev || dev->ifindex != dev_idx))
1751 return true;
1752
1753 if (master_idx) {
1754 struct net_device *master;
1755
1756 if (!dev)
1757 return true;
1758
1759 master = netdev_master_upper_dev_get((struct net_device *)dev);
1760 if (!master || master->ifindex != master_idx)
1761 return true;
1762 }
1763
1764 return false;
1765}
1766
David Ahern430a0492019-05-24 14:43:08 -07001767static int nh_valid_dump_req(const struct nlmsghdr *nlh, int *dev_idx,
1768 int *master_idx, bool *group_filter,
Roopa Prabhu38428d62020-05-21 22:26:13 -07001769 bool *fdb_filter, struct netlink_callback *cb)
David Ahernab84be72019-05-24 14:43:04 -07001770{
1771 struct netlink_ext_ack *extack = cb->extack;
1772 struct nlattr *tb[NHA_MAX + 1];
1773 struct nhmsg *nhm;
1774 int err, i;
1775 u32 idx;
1776
1777 err = nlmsg_parse(nlh, sizeof(*nhm), tb, NHA_MAX, rtm_nh_policy,
1778 NULL);
1779 if (err < 0)
1780 return err;
1781
1782 for (i = 0; i <= NHA_MAX; ++i) {
1783 if (!tb[i])
1784 continue;
1785
1786 switch (i) {
1787 case NHA_OIF:
1788 idx = nla_get_u32(tb[i]);
1789 if (idx > INT_MAX) {
1790 NL_SET_ERR_MSG(extack, "Invalid device index");
1791 return -EINVAL;
1792 }
1793 *dev_idx = idx;
1794 break;
1795 case NHA_MASTER:
1796 idx = nla_get_u32(tb[i]);
1797 if (idx > INT_MAX) {
1798 NL_SET_ERR_MSG(extack, "Invalid master device index");
1799 return -EINVAL;
1800 }
1801 *master_idx = idx;
1802 break;
David Ahern430a0492019-05-24 14:43:08 -07001803 case NHA_GROUPS:
1804 *group_filter = true;
1805 break;
Roopa Prabhu38428d62020-05-21 22:26:13 -07001806 case NHA_FDB:
1807 *fdb_filter = true;
1808 break;
David Ahernab84be72019-05-24 14:43:04 -07001809 default:
1810 NL_SET_ERR_MSG(extack, "Unsupported attribute in dump request");
1811 return -EINVAL;
1812 }
1813 }
1814
1815 nhm = nlmsg_data(nlh);
1816 if (nhm->nh_protocol || nhm->resvd || nhm->nh_scope || nhm->nh_flags) {
1817 NL_SET_ERR_MSG(extack, "Invalid values in header for nexthop dump request");
1818 return -EINVAL;
1819 }
1820
1821 return 0;
1822}
1823
1824/* rtnl */
1825static int rtm_dump_nexthop(struct sk_buff *skb, struct netlink_callback *cb)
1826{
Roopa Prabhu38428d62020-05-21 22:26:13 -07001827 bool group_filter = false, fdb_filter = false;
David Ahernab84be72019-05-24 14:43:04 -07001828 struct nhmsg *nhm = nlmsg_data(cb->nlh);
1829 int dev_filter_idx = 0, master_idx = 0;
1830 struct net *net = sock_net(skb->sk);
1831 struct rb_root *root = &net->nexthop.rb_root;
1832 struct rb_node *node;
1833 int idx = 0, s_idx;
1834 int err;
1835
David Ahern430a0492019-05-24 14:43:08 -07001836 err = nh_valid_dump_req(cb->nlh, &dev_filter_idx, &master_idx,
Roopa Prabhu38428d62020-05-21 22:26:13 -07001837 &group_filter, &fdb_filter, cb);
David Ahernab84be72019-05-24 14:43:04 -07001838 if (err < 0)
1839 return err;
1840
1841 s_idx = cb->args[0];
1842 for (node = rb_first(root); node; node = rb_next(node)) {
1843 struct nexthop *nh;
1844
1845 if (idx < s_idx)
1846 goto cont;
1847
1848 nh = rb_entry(node, struct nexthop, rb_node);
1849 if (nh_dump_filtered(nh, dev_filter_idx, master_idx,
David Ahern430a0492019-05-24 14:43:08 -07001850 group_filter, nhm->nh_family))
David Ahernab84be72019-05-24 14:43:04 -07001851 goto cont;
1852
1853 err = nh_fill_node(skb, nh, RTM_NEWNEXTHOP,
1854 NETLINK_CB(cb->skb).portid,
1855 cb->nlh->nlmsg_seq, NLM_F_MULTI);
1856 if (err < 0) {
1857 if (likely(skb->len))
1858 goto out;
1859
1860 goto out_err;
1861 }
1862cont:
1863 idx++;
1864 }
1865
1866out:
1867 err = skb->len;
1868out_err:
1869 cb->args[0] = idx;
1870 cb->seq = net->nexthop.seq;
1871 nl_dump_check_consistent(cb, nlmsg_hdr(skb));
1872
1873 return err;
1874}
1875
David Ahern597cfe4f2019-05-24 14:43:05 -07001876static void nexthop_sync_mtu(struct net_device *dev, u32 orig_mtu)
1877{
1878 unsigned int hash = nh_dev_hashfn(dev->ifindex);
1879 struct net *net = dev_net(dev);
1880 struct hlist_head *head = &net->nexthop.devhash[hash];
1881 struct hlist_node *n;
1882 struct nh_info *nhi;
1883
1884 hlist_for_each_entry_safe(nhi, n, head, dev_hash) {
1885 if (nhi->fib_nhc.nhc_dev == dev) {
1886 if (nhi->family == AF_INET)
1887 fib_nhc_update_mtu(&nhi->fib_nhc, dev->mtu,
1888 orig_mtu);
1889 }
1890 }
1891}
1892
1893/* rtnl */
1894static int nh_netdev_event(struct notifier_block *this,
1895 unsigned long event, void *ptr)
1896{
1897 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1898 struct netdev_notifier_info_ext *info_ext;
1899
1900 switch (event) {
1901 case NETDEV_DOWN:
1902 case NETDEV_UNREGISTER:
1903 nexthop_flush_dev(dev);
1904 break;
1905 case NETDEV_CHANGE:
1906 if (!(dev_get_flags(dev) & (IFF_RUNNING | IFF_LOWER_UP)))
1907 nexthop_flush_dev(dev);
1908 break;
1909 case NETDEV_CHANGEMTU:
1910 info_ext = ptr;
1911 nexthop_sync_mtu(dev, info_ext->ext.mtu);
1912 rt_cache_flush(dev_net(dev));
1913 break;
1914 }
1915 return NOTIFY_DONE;
1916}
1917
1918static struct notifier_block nh_netdev_notifier = {
1919 .notifier_call = nh_netdev_event,
1920};
1921
Roopa Prabhu8590ceed2020-05-21 22:26:15 -07001922int register_nexthop_notifier(struct net *net, struct notifier_block *nb)
1923{
1924 return atomic_notifier_chain_register(&net->nexthop.notifier_chain, nb);
1925}
1926EXPORT_SYMBOL(register_nexthop_notifier);
1927
1928int unregister_nexthop_notifier(struct net *net, struct notifier_block *nb)
1929{
1930 return atomic_notifier_chain_unregister(&net->nexthop.notifier_chain,
1931 nb);
1932}
1933EXPORT_SYMBOL(unregister_nexthop_notifier);
1934
David Ahernab84be72019-05-24 14:43:04 -07001935static void __net_exit nexthop_net_exit(struct net *net)
1936{
1937 rtnl_lock();
1938 flush_all_nexthops(net);
1939 rtnl_unlock();
David Ahern597cfe4f2019-05-24 14:43:05 -07001940 kfree(net->nexthop.devhash);
David Ahernab84be72019-05-24 14:43:04 -07001941}
1942
1943static int __net_init nexthop_net_init(struct net *net)
1944{
David Ahern597cfe4f2019-05-24 14:43:05 -07001945 size_t sz = sizeof(struct hlist_head) * NH_DEV_HASHSIZE;
1946
David Ahernab84be72019-05-24 14:43:04 -07001947 net->nexthop.rb_root = RB_ROOT;
David Ahern597cfe4f2019-05-24 14:43:05 -07001948 net->nexthop.devhash = kzalloc(sz, GFP_KERNEL);
1949 if (!net->nexthop.devhash)
1950 return -ENOMEM;
Roopa Prabhu8590ceed2020-05-21 22:26:15 -07001951 ATOMIC_INIT_NOTIFIER_HEAD(&net->nexthop.notifier_chain);
David Ahernab84be72019-05-24 14:43:04 -07001952
1953 return 0;
1954}
1955
1956static struct pernet_operations nexthop_net_ops = {
1957 .init = nexthop_net_init,
1958 .exit = nexthop_net_exit,
1959};
1960
1961static int __init nexthop_init(void)
1962{
1963 register_pernet_subsys(&nexthop_net_ops);
1964
David Ahern597cfe4f2019-05-24 14:43:05 -07001965 register_netdevice_notifier(&nh_netdev_notifier);
1966
David Ahernab84be72019-05-24 14:43:04 -07001967 rtnl_register(PF_UNSPEC, RTM_NEWNEXTHOP, rtm_new_nexthop, NULL, 0);
1968 rtnl_register(PF_UNSPEC, RTM_DELNEXTHOP, rtm_del_nexthop, NULL, 0);
1969 rtnl_register(PF_UNSPEC, RTM_GETNEXTHOP, rtm_get_nexthop,
1970 rtm_dump_nexthop, 0);
1971
1972 rtnl_register(PF_INET, RTM_NEWNEXTHOP, rtm_new_nexthop, NULL, 0);
1973 rtnl_register(PF_INET, RTM_GETNEXTHOP, NULL, rtm_dump_nexthop, 0);
1974
1975 rtnl_register(PF_INET6, RTM_NEWNEXTHOP, rtm_new_nexthop, NULL, 0);
1976 rtnl_register(PF_INET6, RTM_GETNEXTHOP, NULL, rtm_dump_nexthop, 0);
1977
1978 return 0;
1979}
1980subsys_initcall(nexthop_init);