1ab84be7eSDavid Ahern // SPDX-License-Identifier: GPL-2.0 2ab84be7eSDavid Ahern /* Generic nexthop implementation 3ab84be7eSDavid Ahern * 4ab84be7eSDavid Ahern * Copyright (c) 2017-19 Cumulus Networks 5ab84be7eSDavid Ahern * Copyright (c) 2017-19 David Ahern <dsa@cumulusnetworks.com> 6ab84be7eSDavid Ahern */ 7ab84be7eSDavid Ahern 8ab84be7eSDavid Ahern #include <linux/nexthop.h> 9ab84be7eSDavid Ahern #include <linux/rtnetlink.h> 10ab84be7eSDavid Ahern #include <linux/slab.h> 11430a0491SDavid Ahern #include <net/arp.h> 1253010f99SDavid Ahern #include <net/ipv6_stubs.h> 13b513bd03SDavid Ahern #include <net/lwtunnel.h> 14430a0491SDavid Ahern #include <net/ndisc.h> 15ab84be7eSDavid Ahern #include <net/nexthop.h> 16597cfe4fSDavid Ahern #include <net/route.h> 17ab84be7eSDavid Ahern #include <net/sock.h> 18ab84be7eSDavid Ahern 19430a0491SDavid Ahern static void remove_nexthop(struct net *net, struct nexthop *nh, 20430a0491SDavid Ahern struct nl_info *nlinfo); 21430a0491SDavid Ahern 22597cfe4fSDavid Ahern #define NH_DEV_HASHBITS 8 23597cfe4fSDavid Ahern #define NH_DEV_HASHSIZE (1U << NH_DEV_HASHBITS) 24597cfe4fSDavid Ahern 25ab84be7eSDavid Ahern static const struct nla_policy rtm_nh_policy[NHA_MAX + 1] = { 26ab84be7eSDavid Ahern [NHA_ID] = { .type = NLA_U32 }, 27ab84be7eSDavid Ahern [NHA_GROUP] = { .type = NLA_BINARY }, 28ab84be7eSDavid Ahern [NHA_GROUP_TYPE] = { .type = NLA_U16 }, 29ab84be7eSDavid Ahern [NHA_BLACKHOLE] = { .type = NLA_FLAG }, 30ab84be7eSDavid Ahern [NHA_OIF] = { .type = NLA_U32 }, 31ab84be7eSDavid Ahern [NHA_GATEWAY] = { .type = NLA_BINARY }, 32ab84be7eSDavid Ahern [NHA_ENCAP_TYPE] = { .type = NLA_U16 }, 33ab84be7eSDavid Ahern [NHA_ENCAP] = { .type = NLA_NESTED }, 34ab84be7eSDavid Ahern [NHA_GROUPS] = { .type = NLA_FLAG }, 35ab84be7eSDavid Ahern [NHA_MASTER] = { .type = NLA_U32 }, 3638428d68SRoopa Prabhu [NHA_FDB] = { .type = NLA_FLAG }, 37ab84be7eSDavid Ahern }; 38ab84be7eSDavid Ahern 398590ceedSRoopa Prabhu static int call_nexthop_notifiers(struct net *net, 40d8e79f1dSNathan Chancellor enum nexthop_event_type event_type, 418590ceedSRoopa Prabhu struct nexthop *nh) 428590ceedSRoopa Prabhu { 438590ceedSRoopa Prabhu int err; 448590ceedSRoopa Prabhu 458590ceedSRoopa Prabhu err = atomic_notifier_call_chain(&net->nexthop.notifier_chain, 468590ceedSRoopa Prabhu event_type, nh); 478590ceedSRoopa Prabhu return notifier_to_errno(err); 488590ceedSRoopa Prabhu } 498590ceedSRoopa Prabhu 50597cfe4fSDavid Ahern static unsigned int nh_dev_hashfn(unsigned int val) 51597cfe4fSDavid Ahern { 52597cfe4fSDavid Ahern unsigned int mask = NH_DEV_HASHSIZE - 1; 53597cfe4fSDavid Ahern 54597cfe4fSDavid Ahern return (val ^ 55597cfe4fSDavid Ahern (val >> NH_DEV_HASHBITS) ^ 56597cfe4fSDavid Ahern (val >> (NH_DEV_HASHBITS * 2))) & mask; 57597cfe4fSDavid Ahern } 58597cfe4fSDavid Ahern 59597cfe4fSDavid Ahern static void nexthop_devhash_add(struct net *net, struct nh_info *nhi) 60597cfe4fSDavid Ahern { 61597cfe4fSDavid Ahern struct net_device *dev = nhi->fib_nhc.nhc_dev; 62597cfe4fSDavid Ahern struct hlist_head *head; 63597cfe4fSDavid Ahern unsigned int hash; 64597cfe4fSDavid Ahern 65597cfe4fSDavid Ahern WARN_ON(!dev); 66597cfe4fSDavid Ahern 67597cfe4fSDavid Ahern hash = nh_dev_hashfn(dev->ifindex); 68597cfe4fSDavid Ahern head = &net->nexthop.devhash[hash]; 69597cfe4fSDavid Ahern hlist_add_head(&nhi->dev_hash, head); 70597cfe4fSDavid Ahern } 71597cfe4fSDavid Ahern 72430a0491SDavid Ahern static void nexthop_free_mpath(struct nexthop *nh) 73ab84be7eSDavid Ahern { 74430a0491SDavid Ahern struct nh_group *nhg; 75430a0491SDavid Ahern int i; 76430a0491SDavid Ahern 77430a0491SDavid Ahern nhg = rcu_dereference_raw(nh->nh_grp); 7890f33bffSNikolay Aleksandrov for (i = 0; i < nhg->num_nh; ++i) { 7990f33bffSNikolay Aleksandrov struct nh_grp_entry *nhge = &nhg->nh_entries[i]; 80430a0491SDavid Ahern 8190f33bffSNikolay Aleksandrov WARN_ON(!list_empty(&nhge->nh_list)); 8290f33bffSNikolay Aleksandrov nexthop_put(nhge->nh); 8390f33bffSNikolay Aleksandrov } 8490f33bffSNikolay Aleksandrov 8590f33bffSNikolay Aleksandrov WARN_ON(nhg->spare == nhg); 8690f33bffSNikolay Aleksandrov 8790f33bffSNikolay Aleksandrov kfree(nhg->spare); 88430a0491SDavid Ahern kfree(nhg); 89430a0491SDavid Ahern } 90430a0491SDavid Ahern 91430a0491SDavid Ahern static void nexthop_free_single(struct nexthop *nh) 92430a0491SDavid Ahern { 93ab84be7eSDavid Ahern struct nh_info *nhi; 94ab84be7eSDavid Ahern 95ab84be7eSDavid Ahern nhi = rcu_dereference_raw(nh->nh_info); 96597cfe4fSDavid Ahern switch (nhi->family) { 97597cfe4fSDavid Ahern case AF_INET: 98597cfe4fSDavid Ahern fib_nh_release(nh->net, &nhi->fib_nh); 99597cfe4fSDavid Ahern break; 10053010f99SDavid Ahern case AF_INET6: 10153010f99SDavid Ahern ipv6_stub->fib6_nh_release(&nhi->fib6_nh); 10253010f99SDavid Ahern break; 103597cfe4fSDavid Ahern } 104ab84be7eSDavid Ahern kfree(nhi); 105430a0491SDavid Ahern } 106430a0491SDavid Ahern 107430a0491SDavid Ahern void nexthop_free_rcu(struct rcu_head *head) 108430a0491SDavid Ahern { 109430a0491SDavid Ahern struct nexthop *nh = container_of(head, struct nexthop, rcu); 110430a0491SDavid Ahern 111430a0491SDavid Ahern if (nh->is_group) 112430a0491SDavid Ahern nexthop_free_mpath(nh); 113430a0491SDavid Ahern else 114430a0491SDavid Ahern nexthop_free_single(nh); 115ab84be7eSDavid Ahern 116ab84be7eSDavid Ahern kfree(nh); 117ab84be7eSDavid Ahern } 118ab84be7eSDavid Ahern EXPORT_SYMBOL_GPL(nexthop_free_rcu); 119ab84be7eSDavid Ahern 120ab84be7eSDavid Ahern static struct nexthop *nexthop_alloc(void) 121ab84be7eSDavid Ahern { 122ab84be7eSDavid Ahern struct nexthop *nh; 123ab84be7eSDavid Ahern 124ab84be7eSDavid Ahern nh = kzalloc(sizeof(struct nexthop), GFP_KERNEL); 125430a0491SDavid Ahern if (nh) { 1264c7e8084SDavid Ahern INIT_LIST_HEAD(&nh->fi_list); 127f88d8ea6SDavid Ahern INIT_LIST_HEAD(&nh->f6i_list); 128430a0491SDavid Ahern INIT_LIST_HEAD(&nh->grp_list); 12938428d68SRoopa Prabhu INIT_LIST_HEAD(&nh->fdb_list); 130430a0491SDavid Ahern } 131ab84be7eSDavid Ahern return nh; 132ab84be7eSDavid Ahern } 133ab84be7eSDavid Ahern 134430a0491SDavid Ahern static struct nh_group *nexthop_grp_alloc(u16 num_nh) 135430a0491SDavid Ahern { 136430a0491SDavid Ahern struct nh_group *nhg; 137430a0491SDavid Ahern 138d7d49dc7SIdo Schimmel nhg = kzalloc(struct_size(nhg, nh_entries, num_nh), GFP_KERNEL); 139430a0491SDavid Ahern if (nhg) 140430a0491SDavid Ahern nhg->num_nh = num_nh; 141430a0491SDavid Ahern 142430a0491SDavid Ahern return nhg; 143430a0491SDavid Ahern } 144430a0491SDavid Ahern 145ab84be7eSDavid Ahern static void nh_base_seq_inc(struct net *net) 146ab84be7eSDavid Ahern { 147ab84be7eSDavid Ahern while (++net->nexthop.seq == 0) 148ab84be7eSDavid Ahern ; 149ab84be7eSDavid Ahern } 150ab84be7eSDavid Ahern 151ab84be7eSDavid Ahern /* no reference taken; rcu lock or rtnl must be held */ 152ab84be7eSDavid Ahern struct nexthop *nexthop_find_by_id(struct net *net, u32 id) 153ab84be7eSDavid Ahern { 154ab84be7eSDavid Ahern struct rb_node **pp, *parent = NULL, *next; 155ab84be7eSDavid Ahern 156ab84be7eSDavid Ahern pp = &net->nexthop.rb_root.rb_node; 157ab84be7eSDavid Ahern while (1) { 158ab84be7eSDavid Ahern struct nexthop *nh; 159ab84be7eSDavid Ahern 160ab84be7eSDavid Ahern next = rcu_dereference_raw(*pp); 161ab84be7eSDavid Ahern if (!next) 162ab84be7eSDavid Ahern break; 163ab84be7eSDavid Ahern parent = next; 164ab84be7eSDavid Ahern 165ab84be7eSDavid Ahern nh = rb_entry(parent, struct nexthop, rb_node); 166ab84be7eSDavid Ahern if (id < nh->id) 167ab84be7eSDavid Ahern pp = &next->rb_left; 168ab84be7eSDavid Ahern else if (id > nh->id) 169ab84be7eSDavid Ahern pp = &next->rb_right; 170ab84be7eSDavid Ahern else 171ab84be7eSDavid Ahern return nh; 172ab84be7eSDavid Ahern } 173ab84be7eSDavid Ahern return NULL; 174ab84be7eSDavid Ahern } 175ab84be7eSDavid Ahern EXPORT_SYMBOL_GPL(nexthop_find_by_id); 176ab84be7eSDavid Ahern 177ab84be7eSDavid Ahern /* used for auto id allocation; called with rtnl held */ 178ab84be7eSDavid Ahern static u32 nh_find_unused_id(struct net *net) 179ab84be7eSDavid Ahern { 180ab84be7eSDavid Ahern u32 id_start = net->nexthop.last_id_allocated; 181ab84be7eSDavid Ahern 182ab84be7eSDavid Ahern while (1) { 183ab84be7eSDavid Ahern net->nexthop.last_id_allocated++; 184ab84be7eSDavid Ahern if (net->nexthop.last_id_allocated == id_start) 185ab84be7eSDavid Ahern break; 186ab84be7eSDavid Ahern 187ab84be7eSDavid Ahern if (!nexthop_find_by_id(net, net->nexthop.last_id_allocated)) 188ab84be7eSDavid Ahern return net->nexthop.last_id_allocated; 189ab84be7eSDavid Ahern } 190ab84be7eSDavid Ahern return 0; 191ab84be7eSDavid Ahern } 192ab84be7eSDavid Ahern 193430a0491SDavid Ahern static int nla_put_nh_group(struct sk_buff *skb, struct nh_group *nhg) 194430a0491SDavid Ahern { 195430a0491SDavid Ahern struct nexthop_grp *p; 196430a0491SDavid Ahern size_t len = nhg->num_nh * sizeof(*p); 197430a0491SDavid Ahern struct nlattr *nla; 198430a0491SDavid Ahern u16 group_type = 0; 199430a0491SDavid Ahern int i; 200430a0491SDavid Ahern 201430a0491SDavid Ahern if (nhg->mpath) 202430a0491SDavid Ahern group_type = NEXTHOP_GRP_TYPE_MPATH; 203430a0491SDavid Ahern 204430a0491SDavid Ahern if (nla_put_u16(skb, NHA_GROUP_TYPE, group_type)) 205430a0491SDavid Ahern goto nla_put_failure; 206430a0491SDavid Ahern 207430a0491SDavid Ahern nla = nla_reserve(skb, NHA_GROUP, len); 208430a0491SDavid Ahern if (!nla) 209430a0491SDavid Ahern goto nla_put_failure; 210430a0491SDavid Ahern 211430a0491SDavid Ahern p = nla_data(nla); 212430a0491SDavid Ahern for (i = 0; i < nhg->num_nh; ++i) { 213430a0491SDavid Ahern p->id = nhg->nh_entries[i].nh->id; 214430a0491SDavid Ahern p->weight = nhg->nh_entries[i].weight - 1; 215430a0491SDavid Ahern p += 1; 216430a0491SDavid Ahern } 217430a0491SDavid Ahern 218430a0491SDavid Ahern return 0; 219430a0491SDavid Ahern 220430a0491SDavid Ahern nla_put_failure: 221430a0491SDavid Ahern return -EMSGSIZE; 222430a0491SDavid Ahern } 223430a0491SDavid Ahern 224ab84be7eSDavid Ahern static int nh_fill_node(struct sk_buff *skb, struct nexthop *nh, 225ab84be7eSDavid Ahern int event, u32 portid, u32 seq, unsigned int nlflags) 226ab84be7eSDavid Ahern { 22753010f99SDavid Ahern struct fib6_nh *fib6_nh; 228597cfe4fSDavid Ahern struct fib_nh *fib_nh; 229ab84be7eSDavid Ahern struct nlmsghdr *nlh; 230ab84be7eSDavid Ahern struct nh_info *nhi; 231ab84be7eSDavid Ahern struct nhmsg *nhm; 232ab84be7eSDavid Ahern 233ab84be7eSDavid Ahern nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nhm), nlflags); 234ab84be7eSDavid Ahern if (!nlh) 235ab84be7eSDavid Ahern return -EMSGSIZE; 236ab84be7eSDavid Ahern 237ab84be7eSDavid Ahern nhm = nlmsg_data(nlh); 238ab84be7eSDavid Ahern nhm->nh_family = AF_UNSPEC; 239ab84be7eSDavid Ahern nhm->nh_flags = nh->nh_flags; 240ab84be7eSDavid Ahern nhm->nh_protocol = nh->protocol; 241ab84be7eSDavid Ahern nhm->nh_scope = 0; 242ab84be7eSDavid Ahern nhm->resvd = 0; 243ab84be7eSDavid Ahern 244ab84be7eSDavid Ahern if (nla_put_u32(skb, NHA_ID, nh->id)) 245ab84be7eSDavid Ahern goto nla_put_failure; 246ab84be7eSDavid Ahern 247430a0491SDavid Ahern if (nh->is_group) { 248430a0491SDavid Ahern struct nh_group *nhg = rtnl_dereference(nh->nh_grp); 249430a0491SDavid Ahern 250ce9ac056SDavid Ahern if (nhg->fdb_nh && nla_put_flag(skb, NHA_FDB)) 251ce9ac056SDavid Ahern goto nla_put_failure; 252430a0491SDavid Ahern if (nla_put_nh_group(skb, nhg)) 253430a0491SDavid Ahern goto nla_put_failure; 254430a0491SDavid Ahern goto out; 255430a0491SDavid Ahern } 256430a0491SDavid Ahern 257ab84be7eSDavid Ahern nhi = rtnl_dereference(nh->nh_info); 258ab84be7eSDavid Ahern nhm->nh_family = nhi->family; 259ab84be7eSDavid Ahern if (nhi->reject_nh) { 260ab84be7eSDavid Ahern if (nla_put_flag(skb, NHA_BLACKHOLE)) 261ab84be7eSDavid Ahern goto nla_put_failure; 262ab84be7eSDavid Ahern goto out; 263ce9ac056SDavid Ahern } else if (nhi->fdb_nh) { 264ce9ac056SDavid Ahern if (nla_put_flag(skb, NHA_FDB)) 265ce9ac056SDavid Ahern goto nla_put_failure; 266ce9ac056SDavid Ahern } else { 267597cfe4fSDavid Ahern const struct net_device *dev; 268597cfe4fSDavid Ahern 269597cfe4fSDavid Ahern dev = nhi->fib_nhc.nhc_dev; 270597cfe4fSDavid Ahern if (dev && nla_put_u32(skb, NHA_OIF, dev->ifindex)) 271597cfe4fSDavid Ahern goto nla_put_failure; 272597cfe4fSDavid Ahern } 273597cfe4fSDavid Ahern 274597cfe4fSDavid Ahern nhm->nh_scope = nhi->fib_nhc.nhc_scope; 275597cfe4fSDavid Ahern switch (nhi->family) { 276597cfe4fSDavid Ahern case AF_INET: 277597cfe4fSDavid Ahern fib_nh = &nhi->fib_nh; 278597cfe4fSDavid Ahern if (fib_nh->fib_nh_gw_family && 279*33d80996SIdo Schimmel nla_put_be32(skb, NHA_GATEWAY, fib_nh->fib_nh_gw4)) 280597cfe4fSDavid Ahern goto nla_put_failure; 281597cfe4fSDavid Ahern break; 28253010f99SDavid Ahern 28353010f99SDavid Ahern case AF_INET6: 28453010f99SDavid Ahern fib6_nh = &nhi->fib6_nh; 28553010f99SDavid Ahern if (fib6_nh->fib_nh_gw_family && 28653010f99SDavid Ahern nla_put_in6_addr(skb, NHA_GATEWAY, &fib6_nh->fib_nh_gw6)) 28753010f99SDavid Ahern goto nla_put_failure; 28853010f99SDavid Ahern break; 289ab84be7eSDavid Ahern } 290ab84be7eSDavid Ahern 291b513bd03SDavid Ahern if (nhi->fib_nhc.nhc_lwtstate && 292b513bd03SDavid Ahern lwtunnel_fill_encap(skb, nhi->fib_nhc.nhc_lwtstate, 293b513bd03SDavid Ahern NHA_ENCAP, NHA_ENCAP_TYPE) < 0) 294b513bd03SDavid Ahern goto nla_put_failure; 295b513bd03SDavid Ahern 296ab84be7eSDavid Ahern out: 297ab84be7eSDavid Ahern nlmsg_end(skb, nlh); 298ab84be7eSDavid Ahern return 0; 299ab84be7eSDavid Ahern 300ab84be7eSDavid Ahern nla_put_failure: 301d69100b8SStephen Worley nlmsg_cancel(skb, nlh); 302ab84be7eSDavid Ahern return -EMSGSIZE; 303ab84be7eSDavid Ahern } 304ab84be7eSDavid Ahern 305430a0491SDavid Ahern static size_t nh_nlmsg_size_grp(struct nexthop *nh) 306430a0491SDavid Ahern { 307430a0491SDavid Ahern struct nh_group *nhg = rtnl_dereference(nh->nh_grp); 308430a0491SDavid Ahern size_t sz = sizeof(struct nexthop_grp) * nhg->num_nh; 309430a0491SDavid Ahern 310430a0491SDavid Ahern return nla_total_size(sz) + 311430a0491SDavid Ahern nla_total_size(2); /* NHA_GROUP_TYPE */ 312430a0491SDavid Ahern } 313430a0491SDavid Ahern 314430a0491SDavid Ahern static size_t nh_nlmsg_size_single(struct nexthop *nh) 315ab84be7eSDavid Ahern { 316597cfe4fSDavid Ahern struct nh_info *nhi = rtnl_dereference(nh->nh_info); 317430a0491SDavid Ahern size_t sz; 318ab84be7eSDavid Ahern 319ab84be7eSDavid Ahern /* covers NHA_BLACKHOLE since NHA_OIF and BLACKHOLE 320ab84be7eSDavid Ahern * are mutually exclusive 321ab84be7eSDavid Ahern */ 322430a0491SDavid Ahern sz = nla_total_size(4); /* NHA_OIF */ 323ab84be7eSDavid Ahern 324597cfe4fSDavid Ahern switch (nhi->family) { 325597cfe4fSDavid Ahern case AF_INET: 326597cfe4fSDavid Ahern if (nhi->fib_nh.fib_nh_gw_family) 327597cfe4fSDavid Ahern sz += nla_total_size(4); /* NHA_GATEWAY */ 328597cfe4fSDavid Ahern break; 32953010f99SDavid Ahern 33053010f99SDavid Ahern case AF_INET6: 33153010f99SDavid Ahern /* NHA_GATEWAY */ 33253010f99SDavid Ahern if (nhi->fib6_nh.fib_nh_gw_family) 33353010f99SDavid Ahern sz += nla_total_size(sizeof(const struct in6_addr)); 33453010f99SDavid Ahern break; 335597cfe4fSDavid Ahern } 336597cfe4fSDavid Ahern 337b513bd03SDavid Ahern if (nhi->fib_nhc.nhc_lwtstate) { 338b513bd03SDavid Ahern sz += lwtunnel_get_encap_size(nhi->fib_nhc.nhc_lwtstate); 339b513bd03SDavid Ahern sz += nla_total_size(2); /* NHA_ENCAP_TYPE */ 340b513bd03SDavid Ahern } 341b513bd03SDavid Ahern 342ab84be7eSDavid Ahern return sz; 343ab84be7eSDavid Ahern } 344ab84be7eSDavid Ahern 345430a0491SDavid Ahern static size_t nh_nlmsg_size(struct nexthop *nh) 346430a0491SDavid Ahern { 347f9e95555SStephen Worley size_t sz = NLMSG_ALIGN(sizeof(struct nhmsg)); 348f9e95555SStephen Worley 349f9e95555SStephen Worley sz += nla_total_size(4); /* NHA_ID */ 350430a0491SDavid Ahern 351430a0491SDavid Ahern if (nh->is_group) 352430a0491SDavid Ahern sz += nh_nlmsg_size_grp(nh); 353430a0491SDavid Ahern else 354430a0491SDavid Ahern sz += nh_nlmsg_size_single(nh); 355430a0491SDavid Ahern 356430a0491SDavid Ahern return sz; 357430a0491SDavid Ahern } 358430a0491SDavid Ahern 359ab84be7eSDavid Ahern static void nexthop_notify(int event, struct nexthop *nh, struct nl_info *info) 360ab84be7eSDavid Ahern { 361ab84be7eSDavid Ahern unsigned int nlflags = info->nlh ? info->nlh->nlmsg_flags : 0; 362ab84be7eSDavid Ahern u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; 363ab84be7eSDavid Ahern struct sk_buff *skb; 364ab84be7eSDavid Ahern int err = -ENOBUFS; 365ab84be7eSDavid Ahern 366ab84be7eSDavid Ahern skb = nlmsg_new(nh_nlmsg_size(nh), gfp_any()); 367ab84be7eSDavid Ahern if (!skb) 368ab84be7eSDavid Ahern goto errout; 369ab84be7eSDavid Ahern 370ab84be7eSDavid Ahern err = nh_fill_node(skb, nh, event, info->portid, seq, nlflags); 371ab84be7eSDavid Ahern if (err < 0) { 372ab84be7eSDavid Ahern /* -EMSGSIZE implies BUG in nh_nlmsg_size() */ 373ab84be7eSDavid Ahern WARN_ON(err == -EMSGSIZE); 374ab84be7eSDavid Ahern kfree_skb(skb); 375ab84be7eSDavid Ahern goto errout; 376ab84be7eSDavid Ahern } 377ab84be7eSDavid Ahern 378ab84be7eSDavid Ahern rtnl_notify(skb, info->nl_net, info->portid, RTNLGRP_NEXTHOP, 379ab84be7eSDavid Ahern info->nlh, gfp_any()); 380ab84be7eSDavid Ahern return; 381ab84be7eSDavid Ahern errout: 382ab84be7eSDavid Ahern if (err < 0) 383ab84be7eSDavid Ahern rtnl_set_sk_err(info->nl_net, RTNLGRP_NEXTHOP, err); 384ab84be7eSDavid Ahern } 385ab84be7eSDavid Ahern 386430a0491SDavid Ahern static bool valid_group_nh(struct nexthop *nh, unsigned int npaths, 387ce9ac056SDavid Ahern bool *is_fdb, struct netlink_ext_ack *extack) 388597cfe4fSDavid Ahern { 389430a0491SDavid Ahern if (nh->is_group) { 390430a0491SDavid Ahern struct nh_group *nhg = rtnl_dereference(nh->nh_grp); 391430a0491SDavid Ahern 392430a0491SDavid Ahern /* nested multipath (group within a group) is not 393430a0491SDavid Ahern * supported 394430a0491SDavid Ahern */ 395430a0491SDavid Ahern if (nhg->mpath) { 396430a0491SDavid Ahern NL_SET_ERR_MSG(extack, 397430a0491SDavid Ahern "Multipath group can not be a nexthop within a group"); 398430a0491SDavid Ahern return false; 399430a0491SDavid Ahern } 400ce9ac056SDavid Ahern *is_fdb = nhg->fdb_nh; 401430a0491SDavid Ahern } else { 402430a0491SDavid Ahern struct nh_info *nhi = rtnl_dereference(nh->nh_info); 403430a0491SDavid Ahern 404430a0491SDavid Ahern if (nhi->reject_nh && npaths > 1) { 405430a0491SDavid Ahern NL_SET_ERR_MSG(extack, 406430a0491SDavid Ahern "Blackhole nexthop can not be used in a group with more than 1 path"); 407430a0491SDavid Ahern return false; 408430a0491SDavid Ahern } 409ce9ac056SDavid Ahern *is_fdb = nhi->fdb_nh; 410430a0491SDavid Ahern } 411430a0491SDavid Ahern 412430a0491SDavid Ahern return true; 413430a0491SDavid Ahern } 414430a0491SDavid Ahern 41538428d68SRoopa Prabhu static int nh_check_attr_fdb_group(struct nexthop *nh, u8 *nh_family, 41638428d68SRoopa Prabhu struct netlink_ext_ack *extack) 41738428d68SRoopa Prabhu { 41838428d68SRoopa Prabhu struct nh_info *nhi; 41938428d68SRoopa Prabhu 420ce9ac056SDavid Ahern nhi = rtnl_dereference(nh->nh_info); 421ce9ac056SDavid Ahern 422ce9ac056SDavid Ahern if (!nhi->fdb_nh) { 42338428d68SRoopa Prabhu NL_SET_ERR_MSG(extack, "FDB nexthop group can only have fdb nexthops"); 42438428d68SRoopa Prabhu return -EINVAL; 42538428d68SRoopa Prabhu } 42638428d68SRoopa Prabhu 42738428d68SRoopa Prabhu if (*nh_family == AF_UNSPEC) { 42838428d68SRoopa Prabhu *nh_family = nhi->family; 42938428d68SRoopa Prabhu } else if (*nh_family != nhi->family) { 43038428d68SRoopa Prabhu NL_SET_ERR_MSG(extack, "FDB nexthop group cannot have mixed family nexthops"); 43138428d68SRoopa Prabhu return -EINVAL; 43238428d68SRoopa Prabhu } 43338428d68SRoopa Prabhu 43438428d68SRoopa Prabhu return 0; 43538428d68SRoopa Prabhu } 43638428d68SRoopa Prabhu 437430a0491SDavid Ahern static int nh_check_attr_group(struct net *net, struct nlattr *tb[], 438430a0491SDavid Ahern struct netlink_ext_ack *extack) 439430a0491SDavid Ahern { 440430a0491SDavid Ahern unsigned int len = nla_len(tb[NHA_GROUP]); 44138428d68SRoopa Prabhu u8 nh_family = AF_UNSPEC; 442430a0491SDavid Ahern struct nexthop_grp *nhg; 443430a0491SDavid Ahern unsigned int i, j; 44438428d68SRoopa Prabhu u8 nhg_fdb = 0; 445430a0491SDavid Ahern 446eeaac363SNikolay Aleksandrov if (!len || len & (sizeof(struct nexthop_grp) - 1)) { 447430a0491SDavid Ahern NL_SET_ERR_MSG(extack, 448430a0491SDavid Ahern "Invalid length for nexthop group attribute"); 449430a0491SDavid Ahern return -EINVAL; 450430a0491SDavid Ahern } 451430a0491SDavid Ahern 452430a0491SDavid Ahern /* convert len to number of nexthop ids */ 453430a0491SDavid Ahern len /= sizeof(*nhg); 454430a0491SDavid Ahern 455430a0491SDavid Ahern nhg = nla_data(tb[NHA_GROUP]); 456430a0491SDavid Ahern for (i = 0; i < len; ++i) { 457430a0491SDavid Ahern if (nhg[i].resvd1 || nhg[i].resvd2) { 458430a0491SDavid Ahern NL_SET_ERR_MSG(extack, "Reserved fields in nexthop_grp must be 0"); 459430a0491SDavid Ahern return -EINVAL; 460430a0491SDavid Ahern } 461430a0491SDavid Ahern if (nhg[i].weight > 254) { 462430a0491SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid value for weight"); 463430a0491SDavid Ahern return -EINVAL; 464430a0491SDavid Ahern } 465430a0491SDavid Ahern for (j = i + 1; j < len; ++j) { 466430a0491SDavid Ahern if (nhg[i].id == nhg[j].id) { 467430a0491SDavid Ahern NL_SET_ERR_MSG(extack, "Nexthop id can not be used twice in a group"); 468430a0491SDavid Ahern return -EINVAL; 469430a0491SDavid Ahern } 470430a0491SDavid Ahern } 471430a0491SDavid Ahern } 472430a0491SDavid Ahern 47338428d68SRoopa Prabhu if (tb[NHA_FDB]) 47438428d68SRoopa Prabhu nhg_fdb = 1; 475430a0491SDavid Ahern nhg = nla_data(tb[NHA_GROUP]); 476430a0491SDavid Ahern for (i = 0; i < len; ++i) { 477430a0491SDavid Ahern struct nexthop *nh; 478ce9ac056SDavid Ahern bool is_fdb_nh; 479430a0491SDavid Ahern 480430a0491SDavid Ahern nh = nexthop_find_by_id(net, nhg[i].id); 481430a0491SDavid Ahern if (!nh) { 482430a0491SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid nexthop id"); 483430a0491SDavid Ahern return -EINVAL; 484430a0491SDavid Ahern } 485ce9ac056SDavid Ahern if (!valid_group_nh(nh, len, &is_fdb_nh, extack)) 486430a0491SDavid Ahern return -EINVAL; 48738428d68SRoopa Prabhu 48838428d68SRoopa Prabhu if (nhg_fdb && nh_check_attr_fdb_group(nh, &nh_family, extack)) 48938428d68SRoopa Prabhu return -EINVAL; 49038428d68SRoopa Prabhu 491ce9ac056SDavid Ahern if (!nhg_fdb && is_fdb_nh) { 49238428d68SRoopa Prabhu NL_SET_ERR_MSG(extack, "Non FDB nexthop group cannot have fdb nexthops"); 49338428d68SRoopa Prabhu return -EINVAL; 49438428d68SRoopa Prabhu } 495430a0491SDavid Ahern } 49684be69b8SDavid Ahern for (i = NHA_GROUP_TYPE + 1; i < __NHA_MAX; ++i) { 497430a0491SDavid Ahern if (!tb[i]) 498430a0491SDavid Ahern continue; 49938428d68SRoopa Prabhu if (tb[NHA_FDB]) 50038428d68SRoopa Prabhu continue; 501430a0491SDavid Ahern NL_SET_ERR_MSG(extack, 502430a0491SDavid Ahern "No other attributes can be set in nexthop groups"); 503430a0491SDavid Ahern return -EINVAL; 504430a0491SDavid Ahern } 505430a0491SDavid Ahern 506430a0491SDavid Ahern return 0; 507430a0491SDavid Ahern } 508430a0491SDavid Ahern 509430a0491SDavid Ahern static bool ipv6_good_nh(const struct fib6_nh *nh) 510430a0491SDavid Ahern { 511430a0491SDavid Ahern int state = NUD_REACHABLE; 512430a0491SDavid Ahern struct neighbour *n; 513430a0491SDavid Ahern 514430a0491SDavid Ahern rcu_read_lock_bh(); 515430a0491SDavid Ahern 516430a0491SDavid Ahern n = __ipv6_neigh_lookup_noref_stub(nh->fib_nh_dev, &nh->fib_nh_gw6); 517430a0491SDavid Ahern if (n) 518430a0491SDavid Ahern state = n->nud_state; 519430a0491SDavid Ahern 520430a0491SDavid Ahern rcu_read_unlock_bh(); 521430a0491SDavid Ahern 522430a0491SDavid Ahern return !!(state & NUD_VALID); 523430a0491SDavid Ahern } 524430a0491SDavid Ahern 525430a0491SDavid Ahern static bool ipv4_good_nh(const struct fib_nh *nh) 526430a0491SDavid Ahern { 527430a0491SDavid Ahern int state = NUD_REACHABLE; 528430a0491SDavid Ahern struct neighbour *n; 529430a0491SDavid Ahern 530430a0491SDavid Ahern rcu_read_lock_bh(); 531430a0491SDavid Ahern 532430a0491SDavid Ahern n = __ipv4_neigh_lookup_noref(nh->fib_nh_dev, 533430a0491SDavid Ahern (__force u32)nh->fib_nh_gw4); 534430a0491SDavid Ahern if (n) 535430a0491SDavid Ahern state = n->nud_state; 536430a0491SDavid Ahern 537430a0491SDavid Ahern rcu_read_unlock_bh(); 538430a0491SDavid Ahern 539430a0491SDavid Ahern return !!(state & NUD_VALID); 540430a0491SDavid Ahern } 541430a0491SDavid Ahern 542430a0491SDavid Ahern struct nexthop *nexthop_select_path(struct nexthop *nh, int hash) 543430a0491SDavid Ahern { 544430a0491SDavid Ahern struct nexthop *rc = NULL; 545430a0491SDavid Ahern struct nh_group *nhg; 546430a0491SDavid Ahern int i; 547430a0491SDavid Ahern 548430a0491SDavid Ahern if (!nh->is_group) 549430a0491SDavid Ahern return nh; 550430a0491SDavid Ahern 551430a0491SDavid Ahern nhg = rcu_dereference(nh->nh_grp); 552430a0491SDavid Ahern for (i = 0; i < nhg->num_nh; ++i) { 553430a0491SDavid Ahern struct nh_grp_entry *nhge = &nhg->nh_entries[i]; 554430a0491SDavid Ahern struct nh_info *nhi; 555430a0491SDavid Ahern 556430a0491SDavid Ahern if (hash > atomic_read(&nhge->upper_bound)) 557430a0491SDavid Ahern continue; 558430a0491SDavid Ahern 559ce9ac056SDavid Ahern nhi = rcu_dereference(nhge->nh->nh_info); 560ce9ac056SDavid Ahern if (nhi->fdb_nh) 56138428d68SRoopa Prabhu return nhge->nh; 56238428d68SRoopa Prabhu 563430a0491SDavid Ahern /* nexthops always check if it is good and does 564430a0491SDavid Ahern * not rely on a sysctl for this behavior 565430a0491SDavid Ahern */ 566430a0491SDavid Ahern switch (nhi->family) { 567430a0491SDavid Ahern case AF_INET: 568430a0491SDavid Ahern if (ipv4_good_nh(&nhi->fib_nh)) 569430a0491SDavid Ahern return nhge->nh; 570430a0491SDavid Ahern break; 571430a0491SDavid Ahern case AF_INET6: 572430a0491SDavid Ahern if (ipv6_good_nh(&nhi->fib6_nh)) 573430a0491SDavid Ahern return nhge->nh; 574430a0491SDavid Ahern break; 575430a0491SDavid Ahern } 576430a0491SDavid Ahern 577430a0491SDavid Ahern if (!rc) 578430a0491SDavid Ahern rc = nhge->nh; 579430a0491SDavid Ahern } 580430a0491SDavid Ahern 581430a0491SDavid Ahern return rc; 582430a0491SDavid Ahern } 583430a0491SDavid Ahern EXPORT_SYMBOL_GPL(nexthop_select_path); 584430a0491SDavid Ahern 585f88c9aa1SDavid Ahern int nexthop_for_each_fib6_nh(struct nexthop *nh, 586f88c9aa1SDavid Ahern int (*cb)(struct fib6_nh *nh, void *arg), 587f88c9aa1SDavid Ahern void *arg) 588f88c9aa1SDavid Ahern { 589f88c9aa1SDavid Ahern struct nh_info *nhi; 590f88c9aa1SDavid Ahern int err; 591f88c9aa1SDavid Ahern 592f88c9aa1SDavid Ahern if (nh->is_group) { 593f88c9aa1SDavid Ahern struct nh_group *nhg; 594f88c9aa1SDavid Ahern int i; 595f88c9aa1SDavid Ahern 596f88c9aa1SDavid Ahern nhg = rcu_dereference_rtnl(nh->nh_grp); 597f88c9aa1SDavid Ahern for (i = 0; i < nhg->num_nh; i++) { 598f88c9aa1SDavid Ahern struct nh_grp_entry *nhge = &nhg->nh_entries[i]; 599f88c9aa1SDavid Ahern 600f88c9aa1SDavid Ahern nhi = rcu_dereference_rtnl(nhge->nh->nh_info); 601f88c9aa1SDavid Ahern err = cb(&nhi->fib6_nh, arg); 602f88c9aa1SDavid Ahern if (err) 603f88c9aa1SDavid Ahern return err; 604f88c9aa1SDavid Ahern } 605f88c9aa1SDavid Ahern } else { 606f88c9aa1SDavid Ahern nhi = rcu_dereference_rtnl(nh->nh_info); 607f88c9aa1SDavid Ahern err = cb(&nhi->fib6_nh, arg); 608f88c9aa1SDavid Ahern if (err) 609f88c9aa1SDavid Ahern return err; 610f88c9aa1SDavid Ahern } 611f88c9aa1SDavid Ahern 612f88c9aa1SDavid Ahern return 0; 613f88c9aa1SDavid Ahern } 614f88c9aa1SDavid Ahern EXPORT_SYMBOL_GPL(nexthop_for_each_fib6_nh); 615f88c9aa1SDavid Ahern 6167bf4796dSDavid Ahern static int check_src_addr(const struct in6_addr *saddr, 6177bf4796dSDavid Ahern struct netlink_ext_ack *extack) 6187bf4796dSDavid Ahern { 6197bf4796dSDavid Ahern if (!ipv6_addr_any(saddr)) { 6207bf4796dSDavid Ahern NL_SET_ERR_MSG(extack, "IPv6 routes using source address can not use nexthop objects"); 6217bf4796dSDavid Ahern return -EINVAL; 6227bf4796dSDavid Ahern } 6237bf4796dSDavid Ahern return 0; 6247bf4796dSDavid Ahern } 6257bf4796dSDavid Ahern 626f88d8ea6SDavid Ahern int fib6_check_nexthop(struct nexthop *nh, struct fib6_config *cfg, 627f88d8ea6SDavid Ahern struct netlink_ext_ack *extack) 628f88d8ea6SDavid Ahern { 629f88d8ea6SDavid Ahern struct nh_info *nhi; 630ce9ac056SDavid Ahern bool is_fdb_nh; 63138428d68SRoopa Prabhu 632f88d8ea6SDavid Ahern /* fib6_src is unique to a fib6_info and limits the ability to cache 633f88d8ea6SDavid Ahern * routes in fib6_nh within a nexthop that is potentially shared 634f88d8ea6SDavid Ahern * across multiple fib entries. If the config wants to use source 635f88d8ea6SDavid Ahern * routing it can not use nexthop objects. mlxsw also does not allow 636f88d8ea6SDavid Ahern * fib6_src on routes. 637f88d8ea6SDavid Ahern */ 6387bf4796dSDavid Ahern if (cfg && check_src_addr(&cfg->fc_src, extack) < 0) 639f88d8ea6SDavid Ahern return -EINVAL; 640f88d8ea6SDavid Ahern 641f88d8ea6SDavid Ahern if (nh->is_group) { 642f88d8ea6SDavid Ahern struct nh_group *nhg; 643f88d8ea6SDavid Ahern 644f88d8ea6SDavid Ahern nhg = rtnl_dereference(nh->nh_grp); 645f88d8ea6SDavid Ahern if (nhg->has_v4) 646f88d8ea6SDavid Ahern goto no_v4_nh; 647ce9ac056SDavid Ahern is_fdb_nh = nhg->fdb_nh; 648f88d8ea6SDavid Ahern } else { 649f88d8ea6SDavid Ahern nhi = rtnl_dereference(nh->nh_info); 650f88d8ea6SDavid Ahern if (nhi->family == AF_INET) 651f88d8ea6SDavid Ahern goto no_v4_nh; 652ce9ac056SDavid Ahern is_fdb_nh = nhi->fdb_nh; 653ce9ac056SDavid Ahern } 654ce9ac056SDavid Ahern 655ce9ac056SDavid Ahern if (is_fdb_nh) { 656ce9ac056SDavid Ahern NL_SET_ERR_MSG(extack, "Route cannot point to a fdb nexthop"); 657ce9ac056SDavid Ahern return -EINVAL; 658f88d8ea6SDavid Ahern } 659f88d8ea6SDavid Ahern 660f88d8ea6SDavid Ahern return 0; 661f88d8ea6SDavid Ahern no_v4_nh: 662f88d8ea6SDavid Ahern NL_SET_ERR_MSG(extack, "IPv6 routes can not use an IPv4 nexthop"); 663f88d8ea6SDavid Ahern return -EINVAL; 664f88d8ea6SDavid Ahern } 665f88d8ea6SDavid Ahern EXPORT_SYMBOL_GPL(fib6_check_nexthop); 666f88d8ea6SDavid Ahern 6677bf4796dSDavid Ahern /* if existing nexthop has ipv6 routes linked to it, need 6687bf4796dSDavid Ahern * to verify this new spec works with ipv6 6697bf4796dSDavid Ahern */ 6707bf4796dSDavid Ahern static int fib6_check_nh_list(struct nexthop *old, struct nexthop *new, 6717bf4796dSDavid Ahern struct netlink_ext_ack *extack) 6727bf4796dSDavid Ahern { 6737bf4796dSDavid Ahern struct fib6_info *f6i; 6747bf4796dSDavid Ahern 6757bf4796dSDavid Ahern if (list_empty(&old->f6i_list)) 6767bf4796dSDavid Ahern return 0; 6777bf4796dSDavid Ahern 6787bf4796dSDavid Ahern list_for_each_entry(f6i, &old->f6i_list, nh_list) { 6797bf4796dSDavid Ahern if (check_src_addr(&f6i->fib6_src.addr, extack) < 0) 6807bf4796dSDavid Ahern return -EINVAL; 6817bf4796dSDavid Ahern } 6827bf4796dSDavid Ahern 6837bf4796dSDavid Ahern return fib6_check_nexthop(new, NULL, extack); 6847bf4796dSDavid Ahern } 6857bf4796dSDavid Ahern 686ce9ac056SDavid Ahern static int nexthop_check_scope(struct nh_info *nhi, u8 scope, 6874c7e8084SDavid Ahern struct netlink_ext_ack *extack) 6884c7e8084SDavid Ahern { 6894c7e8084SDavid Ahern if (scope == RT_SCOPE_HOST && nhi->fib_nhc.nhc_gw_family) { 6904c7e8084SDavid Ahern NL_SET_ERR_MSG(extack, 6914c7e8084SDavid Ahern "Route with host scope can not have a gateway"); 6924c7e8084SDavid Ahern return -EINVAL; 6934c7e8084SDavid Ahern } 6944c7e8084SDavid Ahern 6954c7e8084SDavid Ahern if (nhi->fib_nhc.nhc_flags & RTNH_F_ONLINK && scope >= RT_SCOPE_LINK) { 6964c7e8084SDavid Ahern NL_SET_ERR_MSG(extack, "Scope mismatch with nexthop"); 6974c7e8084SDavid Ahern return -EINVAL; 6984c7e8084SDavid Ahern } 6994c7e8084SDavid Ahern 7004c7e8084SDavid Ahern return 0; 7014c7e8084SDavid Ahern } 7024c7e8084SDavid Ahern 7034c7e8084SDavid Ahern /* Invoked by fib add code to verify nexthop by id is ok with 7044c7e8084SDavid Ahern * config for prefix; parts of fib_check_nh not done when nexthop 7054c7e8084SDavid Ahern * object is used. 7064c7e8084SDavid Ahern */ 7074c7e8084SDavid Ahern int fib_check_nexthop(struct nexthop *nh, u8 scope, 7084c7e8084SDavid Ahern struct netlink_ext_ack *extack) 7094c7e8084SDavid Ahern { 710ce9ac056SDavid Ahern struct nh_info *nhi; 7114c7e8084SDavid Ahern int err = 0; 7124c7e8084SDavid Ahern 713ce9ac056SDavid Ahern if (nh->is_group) { 714ce9ac056SDavid Ahern struct nh_group *nhg; 715ce9ac056SDavid Ahern 716ce9ac056SDavid Ahern nhg = rtnl_dereference(nh->nh_grp); 717ce9ac056SDavid Ahern if (nhg->fdb_nh) { 71838428d68SRoopa Prabhu NL_SET_ERR_MSG(extack, "Route cannot point to a fdb nexthop"); 71938428d68SRoopa Prabhu err = -EINVAL; 72038428d68SRoopa Prabhu goto out; 72138428d68SRoopa Prabhu } 72238428d68SRoopa Prabhu 7234c7e8084SDavid Ahern if (scope == RT_SCOPE_HOST) { 7244c7e8084SDavid Ahern NL_SET_ERR_MSG(extack, "Route with host scope can not have multiple nexthops"); 7254c7e8084SDavid Ahern err = -EINVAL; 7264c7e8084SDavid Ahern goto out; 7274c7e8084SDavid Ahern } 7284c7e8084SDavid Ahern 7294c7e8084SDavid Ahern /* all nexthops in a group have the same scope */ 730ce9ac056SDavid Ahern nhi = rtnl_dereference(nhg->nh_entries[0].nh->nh_info); 731ce9ac056SDavid Ahern err = nexthop_check_scope(nhi, scope, extack); 7324c7e8084SDavid Ahern } else { 733ce9ac056SDavid Ahern nhi = rtnl_dereference(nh->nh_info); 734ce9ac056SDavid Ahern if (nhi->fdb_nh) { 735ce9ac056SDavid Ahern NL_SET_ERR_MSG(extack, "Route cannot point to a fdb nexthop"); 736ce9ac056SDavid Ahern err = -EINVAL; 737ce9ac056SDavid Ahern goto out; 7384c7e8084SDavid Ahern } 739ce9ac056SDavid Ahern err = nexthop_check_scope(nhi, scope, extack); 740ce9ac056SDavid Ahern } 741ce9ac056SDavid Ahern 7424c7e8084SDavid Ahern out: 7434c7e8084SDavid Ahern return err; 7444c7e8084SDavid Ahern } 7454c7e8084SDavid Ahern 7467bf4796dSDavid Ahern static int fib_check_nh_list(struct nexthop *old, struct nexthop *new, 7477bf4796dSDavid Ahern struct netlink_ext_ack *extack) 7487bf4796dSDavid Ahern { 7497bf4796dSDavid Ahern struct fib_info *fi; 7507bf4796dSDavid Ahern 7517bf4796dSDavid Ahern list_for_each_entry(fi, &old->fi_list, nh_list) { 7527bf4796dSDavid Ahern int err; 7537bf4796dSDavid Ahern 7547bf4796dSDavid Ahern err = fib_check_nexthop(new, fi->fib_scope, extack); 7557bf4796dSDavid Ahern if (err) 7567bf4796dSDavid Ahern return err; 7577bf4796dSDavid Ahern } 7587bf4796dSDavid Ahern return 0; 7597bf4796dSDavid Ahern } 7607bf4796dSDavid Ahern 761430a0491SDavid Ahern static void nh_group_rebalance(struct nh_group *nhg) 762430a0491SDavid Ahern { 763430a0491SDavid Ahern int total = 0; 764430a0491SDavid Ahern int w = 0; 765430a0491SDavid Ahern int i; 766430a0491SDavid Ahern 767430a0491SDavid Ahern for (i = 0; i < nhg->num_nh; ++i) 768430a0491SDavid Ahern total += nhg->nh_entries[i].weight; 769430a0491SDavid Ahern 770430a0491SDavid Ahern for (i = 0; i < nhg->num_nh; ++i) { 771430a0491SDavid Ahern struct nh_grp_entry *nhge = &nhg->nh_entries[i]; 772430a0491SDavid Ahern int upper_bound; 773430a0491SDavid Ahern 774430a0491SDavid Ahern w += nhge->weight; 775430a0491SDavid Ahern upper_bound = DIV_ROUND_CLOSEST_ULL((u64)w << 31, total) - 1; 776430a0491SDavid Ahern atomic_set(&nhge->upper_bound, upper_bound); 777430a0491SDavid Ahern } 778430a0491SDavid Ahern } 779430a0491SDavid Ahern 780ac21753aSDavid Ahern static void remove_nh_grp_entry(struct net *net, struct nh_grp_entry *nhge, 781430a0491SDavid Ahern struct nl_info *nlinfo) 782430a0491SDavid Ahern { 78390f33bffSNikolay Aleksandrov struct nh_grp_entry *nhges, *new_nhges; 784ac21753aSDavid Ahern struct nexthop *nhp = nhge->nh_parent; 785430a0491SDavid Ahern struct nexthop *nh = nhge->nh; 78690f33bffSNikolay Aleksandrov struct nh_group *nhg, *newg; 78790f33bffSNikolay Aleksandrov int i, j; 788430a0491SDavid Ahern 789430a0491SDavid Ahern WARN_ON(!nh); 790430a0491SDavid Ahern 791ac21753aSDavid Ahern nhg = rtnl_dereference(nhp->nh_grp); 79290f33bffSNikolay Aleksandrov newg = nhg->spare; 793430a0491SDavid Ahern 79490f33bffSNikolay Aleksandrov /* last entry, keep it visible and remove the parent */ 79590f33bffSNikolay Aleksandrov if (nhg->num_nh == 1) { 79690f33bffSNikolay Aleksandrov remove_nexthop(net, nhp, nlinfo); 797430a0491SDavid Ahern return; 79890f33bffSNikolay Aleksandrov } 799430a0491SDavid Ahern 80090f33bffSNikolay Aleksandrov newg->has_v4 = nhg->has_v4; 80190f33bffSNikolay Aleksandrov newg->mpath = nhg->mpath; 802ce9ac056SDavid Ahern newg->fdb_nh = nhg->fdb_nh; 80390f33bffSNikolay Aleksandrov newg->num_nh = nhg->num_nh; 804430a0491SDavid Ahern 80590f33bffSNikolay Aleksandrov /* copy old entries to new except the one getting removed */ 80690f33bffSNikolay Aleksandrov nhges = nhg->nh_entries; 80790f33bffSNikolay Aleksandrov new_nhges = newg->nh_entries; 80890f33bffSNikolay Aleksandrov for (i = 0, j = 0; i < nhg->num_nh; ++i) { 80990f33bffSNikolay Aleksandrov /* current nexthop getting removed */ 81090f33bffSNikolay Aleksandrov if (nhg->nh_entries[i].nh == nh) { 81190f33bffSNikolay Aleksandrov newg->num_nh--; 81290f33bffSNikolay Aleksandrov continue; 81390f33bffSNikolay Aleksandrov } 814430a0491SDavid Ahern 81590f33bffSNikolay Aleksandrov list_del(&nhges[i].nh_list); 81690f33bffSNikolay Aleksandrov new_nhges[j].nh_parent = nhges[i].nh_parent; 81790f33bffSNikolay Aleksandrov new_nhges[j].nh = nhges[i].nh; 81890f33bffSNikolay Aleksandrov new_nhges[j].weight = nhges[i].weight; 81990f33bffSNikolay Aleksandrov list_add(&new_nhges[j].nh_list, &new_nhges[j].nh->grp_list); 82090f33bffSNikolay Aleksandrov j++; 82190f33bffSNikolay Aleksandrov } 82290f33bffSNikolay Aleksandrov 82390f33bffSNikolay Aleksandrov nh_group_rebalance(newg); 82490f33bffSNikolay Aleksandrov rcu_assign_pointer(nhp->nh_grp, newg); 82590f33bffSNikolay Aleksandrov 82690f33bffSNikolay Aleksandrov list_del(&nhge->nh_list); 82790f33bffSNikolay Aleksandrov nexthop_put(nhge->nh); 828430a0491SDavid Ahern 829430a0491SDavid Ahern if (nlinfo) 830ac21753aSDavid Ahern nexthop_notify(RTM_NEWNEXTHOP, nhp, nlinfo); 831430a0491SDavid Ahern } 832430a0491SDavid Ahern 833430a0491SDavid Ahern static void remove_nexthop_from_groups(struct net *net, struct nexthop *nh, 834430a0491SDavid Ahern struct nl_info *nlinfo) 835430a0491SDavid Ahern { 836430a0491SDavid Ahern struct nh_grp_entry *nhge, *tmp; 837430a0491SDavid Ahern 838ac21753aSDavid Ahern list_for_each_entry_safe(nhge, tmp, &nh->grp_list, nh_list) 839ac21753aSDavid Ahern remove_nh_grp_entry(net, nhge, nlinfo); 840430a0491SDavid Ahern 84190f33bffSNikolay Aleksandrov /* make sure all see the newly published array before releasing rtnl */ 84290f33bffSNikolay Aleksandrov synchronize_rcu(); 843430a0491SDavid Ahern } 844430a0491SDavid Ahern 845430a0491SDavid Ahern static void remove_nexthop_group(struct nexthop *nh, struct nl_info *nlinfo) 846430a0491SDavid Ahern { 847430a0491SDavid Ahern struct nh_group *nhg = rcu_dereference_rtnl(nh->nh_grp); 848430a0491SDavid Ahern int i, num_nh = nhg->num_nh; 849430a0491SDavid Ahern 850430a0491SDavid Ahern for (i = 0; i < num_nh; ++i) { 851430a0491SDavid Ahern struct nh_grp_entry *nhge = &nhg->nh_entries[i]; 852430a0491SDavid Ahern 853430a0491SDavid Ahern if (WARN_ON(!nhge->nh)) 854430a0491SDavid Ahern continue; 855430a0491SDavid Ahern 85690f33bffSNikolay Aleksandrov list_del_init(&nhge->nh_list); 857430a0491SDavid Ahern } 858430a0491SDavid Ahern } 859430a0491SDavid Ahern 8607bf4796dSDavid Ahern /* not called for nexthop replace */ 8614c7e8084SDavid Ahern static void __remove_nexthop_fib(struct net *net, struct nexthop *nh) 8624c7e8084SDavid Ahern { 863f88d8ea6SDavid Ahern struct fib6_info *f6i, *tmp; 8644c7e8084SDavid Ahern bool do_flush = false; 8654c7e8084SDavid Ahern struct fib_info *fi; 8664c7e8084SDavid Ahern 8678590ceedSRoopa Prabhu call_nexthop_notifiers(net, NEXTHOP_EVENT_DEL, nh); 8688590ceedSRoopa Prabhu 8694c7e8084SDavid Ahern list_for_each_entry(fi, &nh->fi_list, nh_list) { 8704c7e8084SDavid Ahern fi->fib_flags |= RTNH_F_DEAD; 8714c7e8084SDavid Ahern do_flush = true; 8724c7e8084SDavid Ahern } 8734c7e8084SDavid Ahern if (do_flush) 8744c7e8084SDavid Ahern fib_flush(net); 875f88d8ea6SDavid Ahern 876f88d8ea6SDavid Ahern /* ip6_del_rt removes the entry from this list hence the _safe */ 877f88d8ea6SDavid Ahern list_for_each_entry_safe(f6i, tmp, &nh->f6i_list, nh_list) { 878f88d8ea6SDavid Ahern /* __ip6_del_rt does a release, so do a hold here */ 879f88d8ea6SDavid Ahern fib6_info_hold(f6i); 8804f80116dSRoopa Prabhu ipv6_stub->ip6_del_rt(net, f6i, 8814f80116dSRoopa Prabhu !net->ipv4.sysctl_nexthop_compat_mode); 882f88d8ea6SDavid Ahern } 8834c7e8084SDavid Ahern } 8844c7e8084SDavid Ahern 885430a0491SDavid Ahern static void __remove_nexthop(struct net *net, struct nexthop *nh, 886430a0491SDavid Ahern struct nl_info *nlinfo) 887430a0491SDavid Ahern { 8884c7e8084SDavid Ahern __remove_nexthop_fib(net, nh); 8894c7e8084SDavid Ahern 890430a0491SDavid Ahern if (nh->is_group) { 891430a0491SDavid Ahern remove_nexthop_group(nh, nlinfo); 892430a0491SDavid Ahern } else { 893597cfe4fSDavid Ahern struct nh_info *nhi; 894597cfe4fSDavid Ahern 895597cfe4fSDavid Ahern nhi = rtnl_dereference(nh->nh_info); 896597cfe4fSDavid Ahern if (nhi->fib_nhc.nhc_dev) 897597cfe4fSDavid Ahern hlist_del(&nhi->dev_hash); 898430a0491SDavid Ahern 899430a0491SDavid Ahern remove_nexthop_from_groups(net, nh, nlinfo); 900430a0491SDavid Ahern } 901597cfe4fSDavid Ahern } 902597cfe4fSDavid Ahern 903ab84be7eSDavid Ahern static void remove_nexthop(struct net *net, struct nexthop *nh, 904430a0491SDavid Ahern struct nl_info *nlinfo) 905ab84be7eSDavid Ahern { 906ab84be7eSDavid Ahern /* remove from the tree */ 907ab84be7eSDavid Ahern rb_erase(&nh->rb_node, &net->nexthop.rb_root); 908ab84be7eSDavid Ahern 909ab84be7eSDavid Ahern if (nlinfo) 910ab84be7eSDavid Ahern nexthop_notify(RTM_DELNEXTHOP, nh, nlinfo); 911ab84be7eSDavid Ahern 912430a0491SDavid Ahern __remove_nexthop(net, nh, nlinfo); 913ab84be7eSDavid Ahern nh_base_seq_inc(net); 914ab84be7eSDavid Ahern 915ab84be7eSDavid Ahern nexthop_put(nh); 916ab84be7eSDavid Ahern } 917ab84be7eSDavid Ahern 9187bf4796dSDavid Ahern /* if any FIB entries reference this nexthop, any dst entries 9197bf4796dSDavid Ahern * need to be regenerated 9207bf4796dSDavid Ahern */ 9217bf4796dSDavid Ahern static void nh_rt_cache_flush(struct net *net, struct nexthop *nh) 9227bf4796dSDavid Ahern { 9237bf4796dSDavid Ahern struct fib6_info *f6i; 9247bf4796dSDavid Ahern 9257bf4796dSDavid Ahern if (!list_empty(&nh->fi_list)) 9267bf4796dSDavid Ahern rt_cache_flush(net); 9277bf4796dSDavid Ahern 9287bf4796dSDavid Ahern list_for_each_entry(f6i, &nh->f6i_list, nh_list) 9297bf4796dSDavid Ahern ipv6_stub->fib6_update_sernum(net, f6i); 9307bf4796dSDavid Ahern } 9317bf4796dSDavid Ahern 9327bf4796dSDavid Ahern static int replace_nexthop_grp(struct net *net, struct nexthop *old, 9337bf4796dSDavid Ahern struct nexthop *new, 9347bf4796dSDavid Ahern struct netlink_ext_ack *extack) 9357bf4796dSDavid Ahern { 9367bf4796dSDavid Ahern struct nh_group *oldg, *newg; 9377bf4796dSDavid Ahern int i; 9387bf4796dSDavid Ahern 9397bf4796dSDavid Ahern if (!new->is_group) { 9407bf4796dSDavid Ahern NL_SET_ERR_MSG(extack, "Can not replace a nexthop group with a nexthop."); 9417bf4796dSDavid Ahern return -EINVAL; 9427bf4796dSDavid Ahern } 9437bf4796dSDavid Ahern 9447bf4796dSDavid Ahern oldg = rtnl_dereference(old->nh_grp); 9457bf4796dSDavid Ahern newg = rtnl_dereference(new->nh_grp); 9467bf4796dSDavid Ahern 9477bf4796dSDavid Ahern /* update parents - used by nexthop code for cleanup */ 9487bf4796dSDavid Ahern for (i = 0; i < newg->num_nh; i++) 9497bf4796dSDavid Ahern newg->nh_entries[i].nh_parent = old; 9507bf4796dSDavid Ahern 9517bf4796dSDavid Ahern rcu_assign_pointer(old->nh_grp, newg); 9527bf4796dSDavid Ahern 9537bf4796dSDavid Ahern for (i = 0; i < oldg->num_nh; i++) 9547bf4796dSDavid Ahern oldg->nh_entries[i].nh_parent = new; 9557bf4796dSDavid Ahern 9567bf4796dSDavid Ahern rcu_assign_pointer(new->nh_grp, oldg); 9577bf4796dSDavid Ahern 9587bf4796dSDavid Ahern return 0; 9597bf4796dSDavid Ahern } 9607bf4796dSDavid Ahern 9617bf4796dSDavid Ahern static int replace_nexthop_single(struct net *net, struct nexthop *old, 9627bf4796dSDavid Ahern struct nexthop *new, 9637bf4796dSDavid Ahern struct netlink_ext_ack *extack) 9647bf4796dSDavid Ahern { 9657bf4796dSDavid Ahern struct nh_info *oldi, *newi; 9667bf4796dSDavid Ahern 9677bf4796dSDavid Ahern if (new->is_group) { 9687bf4796dSDavid Ahern NL_SET_ERR_MSG(extack, "Can not replace a nexthop with a nexthop group."); 9697bf4796dSDavid Ahern return -EINVAL; 9707bf4796dSDavid Ahern } 9717bf4796dSDavid Ahern 9727bf4796dSDavid Ahern oldi = rtnl_dereference(old->nh_info); 9737bf4796dSDavid Ahern newi = rtnl_dereference(new->nh_info); 9747bf4796dSDavid Ahern 9757bf4796dSDavid Ahern newi->nh_parent = old; 9767bf4796dSDavid Ahern oldi->nh_parent = new; 9777bf4796dSDavid Ahern 9787bf4796dSDavid Ahern old->protocol = new->protocol; 9797bf4796dSDavid Ahern old->nh_flags = new->nh_flags; 9807bf4796dSDavid Ahern 9817bf4796dSDavid Ahern rcu_assign_pointer(old->nh_info, newi); 9827bf4796dSDavid Ahern rcu_assign_pointer(new->nh_info, oldi); 9837bf4796dSDavid Ahern 9847bf4796dSDavid Ahern return 0; 9857bf4796dSDavid Ahern } 9867bf4796dSDavid Ahern 9877bf4796dSDavid Ahern static void __nexthop_replace_notify(struct net *net, struct nexthop *nh, 9887bf4796dSDavid Ahern struct nl_info *info) 9897bf4796dSDavid Ahern { 9907bf4796dSDavid Ahern struct fib6_info *f6i; 9917bf4796dSDavid Ahern 9927bf4796dSDavid Ahern if (!list_empty(&nh->fi_list)) { 9937bf4796dSDavid Ahern struct fib_info *fi; 9947bf4796dSDavid Ahern 9957bf4796dSDavid Ahern /* expectation is a few fib_info per nexthop and then 9967bf4796dSDavid Ahern * a lot of routes per fib_info. So mark the fib_info 9977bf4796dSDavid Ahern * and then walk the fib tables once 9987bf4796dSDavid Ahern */ 9997bf4796dSDavid Ahern list_for_each_entry(fi, &nh->fi_list, nh_list) 10007bf4796dSDavid Ahern fi->nh_updated = true; 10017bf4796dSDavid Ahern 10027bf4796dSDavid Ahern fib_info_notify_update(net, info); 10037bf4796dSDavid Ahern 10047bf4796dSDavid Ahern list_for_each_entry(fi, &nh->fi_list, nh_list) 10057bf4796dSDavid Ahern fi->nh_updated = false; 10067bf4796dSDavid Ahern } 10077bf4796dSDavid Ahern 10087bf4796dSDavid Ahern list_for_each_entry(f6i, &nh->f6i_list, nh_list) 10097bf4796dSDavid Ahern ipv6_stub->fib6_rt_update(net, f6i, info); 10107bf4796dSDavid Ahern } 10117bf4796dSDavid Ahern 10127bf4796dSDavid Ahern /* send RTM_NEWROUTE with REPLACE flag set for all FIB entries 10137bf4796dSDavid Ahern * linked to this nexthop and for all groups that the nexthop 10147bf4796dSDavid Ahern * is a member of 10157bf4796dSDavid Ahern */ 10167bf4796dSDavid Ahern static void nexthop_replace_notify(struct net *net, struct nexthop *nh, 10177bf4796dSDavid Ahern struct nl_info *info) 10187bf4796dSDavid Ahern { 10197bf4796dSDavid Ahern struct nh_grp_entry *nhge; 10207bf4796dSDavid Ahern 10217bf4796dSDavid Ahern __nexthop_replace_notify(net, nh, info); 10227bf4796dSDavid Ahern 10237bf4796dSDavid Ahern list_for_each_entry(nhge, &nh->grp_list, nh_list) 10247bf4796dSDavid Ahern __nexthop_replace_notify(net, nhge->nh_parent, info); 10257bf4796dSDavid Ahern } 10267bf4796dSDavid Ahern 1027ab84be7eSDavid Ahern static int replace_nexthop(struct net *net, struct nexthop *old, 1028ab84be7eSDavid Ahern struct nexthop *new, struct netlink_ext_ack *extack) 1029ab84be7eSDavid Ahern { 10307bf4796dSDavid Ahern bool new_is_reject = false; 10317bf4796dSDavid Ahern struct nh_grp_entry *nhge; 10327bf4796dSDavid Ahern int err; 10337bf4796dSDavid Ahern 10347bf4796dSDavid Ahern /* check that existing FIB entries are ok with the 10357bf4796dSDavid Ahern * new nexthop definition 10367bf4796dSDavid Ahern */ 10377bf4796dSDavid Ahern err = fib_check_nh_list(old, new, extack); 10387bf4796dSDavid Ahern if (err) 10397bf4796dSDavid Ahern return err; 10407bf4796dSDavid Ahern 10417bf4796dSDavid Ahern err = fib6_check_nh_list(old, new, extack); 10427bf4796dSDavid Ahern if (err) 10437bf4796dSDavid Ahern return err; 10447bf4796dSDavid Ahern 10457bf4796dSDavid Ahern if (!new->is_group) { 10467bf4796dSDavid Ahern struct nh_info *nhi = rtnl_dereference(new->nh_info); 10477bf4796dSDavid Ahern 10487bf4796dSDavid Ahern new_is_reject = nhi->reject_nh; 10497bf4796dSDavid Ahern } 10507bf4796dSDavid Ahern 10517bf4796dSDavid Ahern list_for_each_entry(nhge, &old->grp_list, nh_list) { 10527bf4796dSDavid Ahern /* if new nexthop is a blackhole, any groups using this 10537bf4796dSDavid Ahern * nexthop cannot have more than 1 path 10547bf4796dSDavid Ahern */ 10557bf4796dSDavid Ahern if (new_is_reject && 10567bf4796dSDavid Ahern nexthop_num_path(nhge->nh_parent) > 1) { 10577bf4796dSDavid Ahern NL_SET_ERR_MSG(extack, "Blackhole nexthop can not be a member of a group with more than one path"); 10587bf4796dSDavid Ahern return -EINVAL; 10597bf4796dSDavid Ahern } 10607bf4796dSDavid Ahern 10617bf4796dSDavid Ahern err = fib_check_nh_list(nhge->nh_parent, new, extack); 10627bf4796dSDavid Ahern if (err) 10637bf4796dSDavid Ahern return err; 10647bf4796dSDavid Ahern 10657bf4796dSDavid Ahern err = fib6_check_nh_list(nhge->nh_parent, new, extack); 10667bf4796dSDavid Ahern if (err) 10677bf4796dSDavid Ahern return err; 10687bf4796dSDavid Ahern } 10697bf4796dSDavid Ahern 10707bf4796dSDavid Ahern if (old->is_group) 10717bf4796dSDavid Ahern err = replace_nexthop_grp(net, old, new, extack); 10727bf4796dSDavid Ahern else 10737bf4796dSDavid Ahern err = replace_nexthop_single(net, old, new, extack); 10747bf4796dSDavid Ahern 10757bf4796dSDavid Ahern if (!err) { 10767bf4796dSDavid Ahern nh_rt_cache_flush(net, old); 10777bf4796dSDavid Ahern 10787bf4796dSDavid Ahern __remove_nexthop(net, new, NULL); 10797bf4796dSDavid Ahern nexthop_put(new); 10807bf4796dSDavid Ahern } 10817bf4796dSDavid Ahern 10827bf4796dSDavid Ahern return err; 1083ab84be7eSDavid Ahern } 1084ab84be7eSDavid Ahern 1085ab84be7eSDavid Ahern /* called with rtnl_lock held */ 1086ab84be7eSDavid Ahern static int insert_nexthop(struct net *net, struct nexthop *new_nh, 1087ab84be7eSDavid Ahern struct nh_config *cfg, struct netlink_ext_ack *extack) 1088ab84be7eSDavid Ahern { 1089ab84be7eSDavid Ahern struct rb_node **pp, *parent = NULL, *next; 1090ab84be7eSDavid Ahern struct rb_root *root = &net->nexthop.rb_root; 1091ab84be7eSDavid Ahern bool replace = !!(cfg->nlflags & NLM_F_REPLACE); 1092ab84be7eSDavid Ahern bool create = !!(cfg->nlflags & NLM_F_CREATE); 1093ab84be7eSDavid Ahern u32 new_id = new_nh->id; 10947bf4796dSDavid Ahern int replace_notify = 0; 1095ab84be7eSDavid Ahern int rc = -EEXIST; 1096ab84be7eSDavid Ahern 1097ab84be7eSDavid Ahern pp = &root->rb_node; 1098ab84be7eSDavid Ahern while (1) { 1099ab84be7eSDavid Ahern struct nexthop *nh; 1100ab84be7eSDavid Ahern 1101ab84be7eSDavid Ahern next = rtnl_dereference(*pp); 1102ab84be7eSDavid Ahern if (!next) 1103ab84be7eSDavid Ahern break; 1104ab84be7eSDavid Ahern 1105ab84be7eSDavid Ahern parent = next; 1106ab84be7eSDavid Ahern 1107ab84be7eSDavid Ahern nh = rb_entry(parent, struct nexthop, rb_node); 1108ab84be7eSDavid Ahern if (new_id < nh->id) { 1109ab84be7eSDavid Ahern pp = &next->rb_left; 1110ab84be7eSDavid Ahern } else if (new_id > nh->id) { 1111ab84be7eSDavid Ahern pp = &next->rb_right; 1112ab84be7eSDavid Ahern } else if (replace) { 1113ab84be7eSDavid Ahern rc = replace_nexthop(net, nh, new_nh, extack); 11147bf4796dSDavid Ahern if (!rc) { 1115ab84be7eSDavid Ahern new_nh = nh; /* send notification with old nh */ 11167bf4796dSDavid Ahern replace_notify = 1; 11177bf4796dSDavid Ahern } 1118ab84be7eSDavid Ahern goto out; 1119ab84be7eSDavid Ahern } else { 1120ab84be7eSDavid Ahern /* id already exists and not a replace */ 1121ab84be7eSDavid Ahern goto out; 1122ab84be7eSDavid Ahern } 1123ab84be7eSDavid Ahern } 1124ab84be7eSDavid Ahern 1125ab84be7eSDavid Ahern if (replace && !create) { 1126ab84be7eSDavid Ahern NL_SET_ERR_MSG(extack, "Replace specified without create and no entry exists"); 1127ab84be7eSDavid Ahern rc = -ENOENT; 1128ab84be7eSDavid Ahern goto out; 1129ab84be7eSDavid Ahern } 1130ab84be7eSDavid Ahern 1131ab84be7eSDavid Ahern rb_link_node_rcu(&new_nh->rb_node, parent, pp); 1132ab84be7eSDavid Ahern rb_insert_color(&new_nh->rb_node, root); 1133ab84be7eSDavid Ahern rc = 0; 1134ab84be7eSDavid Ahern out: 1135ab84be7eSDavid Ahern if (!rc) { 1136ab84be7eSDavid Ahern nh_base_seq_inc(net); 1137ab84be7eSDavid Ahern nexthop_notify(RTM_NEWNEXTHOP, new_nh, &cfg->nlinfo); 11384f80116dSRoopa Prabhu if (replace_notify && net->ipv4.sysctl_nexthop_compat_mode) 11397bf4796dSDavid Ahern nexthop_replace_notify(net, new_nh, &cfg->nlinfo); 1140ab84be7eSDavid Ahern } 1141ab84be7eSDavid Ahern 1142ab84be7eSDavid Ahern return rc; 1143ab84be7eSDavid Ahern } 1144ab84be7eSDavid Ahern 1145597cfe4fSDavid Ahern /* rtnl */ 1146597cfe4fSDavid Ahern /* remove all nexthops tied to a device being deleted */ 1147597cfe4fSDavid Ahern static void nexthop_flush_dev(struct net_device *dev) 1148597cfe4fSDavid Ahern { 1149597cfe4fSDavid Ahern unsigned int hash = nh_dev_hashfn(dev->ifindex); 1150597cfe4fSDavid Ahern struct net *net = dev_net(dev); 1151597cfe4fSDavid Ahern struct hlist_head *head = &net->nexthop.devhash[hash]; 1152597cfe4fSDavid Ahern struct hlist_node *n; 1153597cfe4fSDavid Ahern struct nh_info *nhi; 1154597cfe4fSDavid Ahern 1155597cfe4fSDavid Ahern hlist_for_each_entry_safe(nhi, n, head, dev_hash) { 1156597cfe4fSDavid Ahern if (nhi->fib_nhc.nhc_dev != dev) 1157597cfe4fSDavid Ahern continue; 1158597cfe4fSDavid Ahern 1159430a0491SDavid Ahern remove_nexthop(net, nhi->nh_parent, NULL); 1160597cfe4fSDavid Ahern } 1161597cfe4fSDavid Ahern } 1162597cfe4fSDavid Ahern 1163ab84be7eSDavid Ahern /* rtnl; called when net namespace is deleted */ 1164ab84be7eSDavid Ahern static void flush_all_nexthops(struct net *net) 1165ab84be7eSDavid Ahern { 1166ab84be7eSDavid Ahern struct rb_root *root = &net->nexthop.rb_root; 1167ab84be7eSDavid Ahern struct rb_node *node; 1168ab84be7eSDavid Ahern struct nexthop *nh; 1169ab84be7eSDavid Ahern 1170ab84be7eSDavid Ahern while ((node = rb_first(root))) { 1171ab84be7eSDavid Ahern nh = rb_entry(node, struct nexthop, rb_node); 1172430a0491SDavid Ahern remove_nexthop(net, nh, NULL); 1173ab84be7eSDavid Ahern cond_resched(); 1174ab84be7eSDavid Ahern } 1175ab84be7eSDavid Ahern } 1176ab84be7eSDavid Ahern 1177430a0491SDavid Ahern static struct nexthop *nexthop_create_group(struct net *net, 1178430a0491SDavid Ahern struct nh_config *cfg) 1179430a0491SDavid Ahern { 1180430a0491SDavid Ahern struct nlattr *grps_attr = cfg->nh_grp; 1181430a0491SDavid Ahern struct nexthop_grp *entry = nla_data(grps_attr); 118290f33bffSNikolay Aleksandrov u16 num_nh = nla_len(grps_attr) / sizeof(*entry); 1183430a0491SDavid Ahern struct nh_group *nhg; 1184430a0491SDavid Ahern struct nexthop *nh; 1185430a0491SDavid Ahern int i; 1186430a0491SDavid Ahern 1187eeaac363SNikolay Aleksandrov if (WARN_ON(!num_nh)) 1188eeaac363SNikolay Aleksandrov return ERR_PTR(-EINVAL); 1189eeaac363SNikolay Aleksandrov 1190430a0491SDavid Ahern nh = nexthop_alloc(); 1191430a0491SDavid Ahern if (!nh) 1192430a0491SDavid Ahern return ERR_PTR(-ENOMEM); 1193430a0491SDavid Ahern 1194430a0491SDavid Ahern nh->is_group = 1; 1195430a0491SDavid Ahern 119690f33bffSNikolay Aleksandrov nhg = nexthop_grp_alloc(num_nh); 1197430a0491SDavid Ahern if (!nhg) { 1198430a0491SDavid Ahern kfree(nh); 1199430a0491SDavid Ahern return ERR_PTR(-ENOMEM); 1200430a0491SDavid Ahern } 1201430a0491SDavid Ahern 120290f33bffSNikolay Aleksandrov /* spare group used for removals */ 120390f33bffSNikolay Aleksandrov nhg->spare = nexthop_grp_alloc(num_nh); 1204dafe2078SPatrick Eigensatz if (!nhg->spare) { 120590f33bffSNikolay Aleksandrov kfree(nhg); 120690f33bffSNikolay Aleksandrov kfree(nh); 1207dafe2078SPatrick Eigensatz return ERR_PTR(-ENOMEM); 120890f33bffSNikolay Aleksandrov } 120990f33bffSNikolay Aleksandrov nhg->spare->spare = nhg; 121090f33bffSNikolay Aleksandrov 1211430a0491SDavid Ahern for (i = 0; i < nhg->num_nh; ++i) { 1212430a0491SDavid Ahern struct nexthop *nhe; 1213430a0491SDavid Ahern struct nh_info *nhi; 1214430a0491SDavid Ahern 1215430a0491SDavid Ahern nhe = nexthop_find_by_id(net, entry[i].id); 1216430a0491SDavid Ahern if (!nexthop_get(nhe)) 1217430a0491SDavid Ahern goto out_no_nh; 1218430a0491SDavid Ahern 1219430a0491SDavid Ahern nhi = rtnl_dereference(nhe->nh_info); 1220430a0491SDavid Ahern if (nhi->family == AF_INET) 1221430a0491SDavid Ahern nhg->has_v4 = true; 1222430a0491SDavid Ahern 1223430a0491SDavid Ahern nhg->nh_entries[i].nh = nhe; 1224430a0491SDavid Ahern nhg->nh_entries[i].weight = entry[i].weight + 1; 1225430a0491SDavid Ahern list_add(&nhg->nh_entries[i].nh_list, &nhe->grp_list); 1226430a0491SDavid Ahern nhg->nh_entries[i].nh_parent = nh; 1227430a0491SDavid Ahern } 1228430a0491SDavid Ahern 1229430a0491SDavid Ahern if (cfg->nh_grp_type == NEXTHOP_GRP_TYPE_MPATH) { 1230430a0491SDavid Ahern nhg->mpath = 1; 1231430a0491SDavid Ahern nh_group_rebalance(nhg); 1232430a0491SDavid Ahern } 1233430a0491SDavid Ahern 123438428d68SRoopa Prabhu if (cfg->nh_fdb) 1235ce9ac056SDavid Ahern nhg->fdb_nh = 1; 123638428d68SRoopa Prabhu 1237430a0491SDavid Ahern rcu_assign_pointer(nh->nh_grp, nhg); 1238430a0491SDavid Ahern 1239430a0491SDavid Ahern return nh; 1240430a0491SDavid Ahern 1241430a0491SDavid Ahern out_no_nh: 1242430a0491SDavid Ahern for (; i >= 0; --i) 1243430a0491SDavid Ahern nexthop_put(nhg->nh_entries[i].nh); 1244430a0491SDavid Ahern 124590f33bffSNikolay Aleksandrov kfree(nhg->spare); 1246430a0491SDavid Ahern kfree(nhg); 1247430a0491SDavid Ahern kfree(nh); 1248430a0491SDavid Ahern 1249430a0491SDavid Ahern return ERR_PTR(-ENOENT); 1250430a0491SDavid Ahern } 1251430a0491SDavid Ahern 1252597cfe4fSDavid Ahern static int nh_create_ipv4(struct net *net, struct nexthop *nh, 1253597cfe4fSDavid Ahern struct nh_info *nhi, struct nh_config *cfg, 1254597cfe4fSDavid Ahern struct netlink_ext_ack *extack) 1255597cfe4fSDavid Ahern { 1256597cfe4fSDavid Ahern struct fib_nh *fib_nh = &nhi->fib_nh; 1257597cfe4fSDavid Ahern struct fib_config fib_cfg = { 1258597cfe4fSDavid Ahern .fc_oif = cfg->nh_ifindex, 1259597cfe4fSDavid Ahern .fc_gw4 = cfg->gw.ipv4, 1260597cfe4fSDavid Ahern .fc_gw_family = cfg->gw.ipv4 ? AF_INET : 0, 1261597cfe4fSDavid Ahern .fc_flags = cfg->nh_flags, 1262b513bd03SDavid Ahern .fc_encap = cfg->nh_encap, 1263b513bd03SDavid Ahern .fc_encap_type = cfg->nh_encap_type, 1264597cfe4fSDavid Ahern }; 126538428d68SRoopa Prabhu u32 tb_id = (cfg->dev ? l3mdev_fib_table(cfg->dev) : RT_TABLE_MAIN); 1266c76c9925SColin Ian King int err; 1267597cfe4fSDavid Ahern 1268597cfe4fSDavid Ahern err = fib_nh_init(net, fib_nh, &fib_cfg, 1, extack); 1269597cfe4fSDavid Ahern if (err) { 1270597cfe4fSDavid Ahern fib_nh_release(net, fib_nh); 1271597cfe4fSDavid Ahern goto out; 1272597cfe4fSDavid Ahern } 1273597cfe4fSDavid Ahern 1274ce9ac056SDavid Ahern if (nhi->fdb_nh) 127538428d68SRoopa Prabhu goto out; 127638428d68SRoopa Prabhu 1277597cfe4fSDavid Ahern /* sets nh_dev if successful */ 1278597cfe4fSDavid Ahern err = fib_check_nh(net, fib_nh, tb_id, 0, extack); 1279597cfe4fSDavid Ahern if (!err) { 1280597cfe4fSDavid Ahern nh->nh_flags = fib_nh->fib_nh_flags; 1281dcb1ecb5SDavid Ahern fib_info_update_nhc_saddr(net, &fib_nh->nh_common, 1282dcb1ecb5SDavid Ahern fib_nh->fib_nh_scope); 1283597cfe4fSDavid Ahern } else { 1284597cfe4fSDavid Ahern fib_nh_release(net, fib_nh); 1285597cfe4fSDavid Ahern } 1286597cfe4fSDavid Ahern out: 1287597cfe4fSDavid Ahern return err; 1288597cfe4fSDavid Ahern } 1289597cfe4fSDavid Ahern 129053010f99SDavid Ahern static int nh_create_ipv6(struct net *net, struct nexthop *nh, 129153010f99SDavid Ahern struct nh_info *nhi, struct nh_config *cfg, 129253010f99SDavid Ahern struct netlink_ext_ack *extack) 129353010f99SDavid Ahern { 129453010f99SDavid Ahern struct fib6_nh *fib6_nh = &nhi->fib6_nh; 129553010f99SDavid Ahern struct fib6_config fib6_cfg = { 129653010f99SDavid Ahern .fc_table = l3mdev_fib_table(cfg->dev), 129753010f99SDavid Ahern .fc_ifindex = cfg->nh_ifindex, 129853010f99SDavid Ahern .fc_gateway = cfg->gw.ipv6, 129953010f99SDavid Ahern .fc_flags = cfg->nh_flags, 1300b513bd03SDavid Ahern .fc_encap = cfg->nh_encap, 1301b513bd03SDavid Ahern .fc_encap_type = cfg->nh_encap_type, 130238428d68SRoopa Prabhu .fc_is_fdb = cfg->nh_fdb, 130353010f99SDavid Ahern }; 13046f43e525SColin Ian King int err; 130553010f99SDavid Ahern 130653010f99SDavid Ahern if (!ipv6_addr_any(&cfg->gw.ipv6)) 130753010f99SDavid Ahern fib6_cfg.fc_flags |= RTF_GATEWAY; 130853010f99SDavid Ahern 130953010f99SDavid Ahern /* sets nh_dev if successful */ 131053010f99SDavid Ahern err = ipv6_stub->fib6_nh_init(net, fib6_nh, &fib6_cfg, GFP_KERNEL, 131153010f99SDavid Ahern extack); 131253010f99SDavid Ahern if (err) 131353010f99SDavid Ahern ipv6_stub->fib6_nh_release(fib6_nh); 131453010f99SDavid Ahern else 131553010f99SDavid Ahern nh->nh_flags = fib6_nh->fib_nh_flags; 131653010f99SDavid Ahern 131753010f99SDavid Ahern return err; 131853010f99SDavid Ahern } 131953010f99SDavid Ahern 1320ab84be7eSDavid Ahern static struct nexthop *nexthop_create(struct net *net, struct nh_config *cfg, 1321ab84be7eSDavid Ahern struct netlink_ext_ack *extack) 1322ab84be7eSDavid Ahern { 1323ab84be7eSDavid Ahern struct nh_info *nhi; 1324ab84be7eSDavid Ahern struct nexthop *nh; 1325ab84be7eSDavid Ahern int err = 0; 1326ab84be7eSDavid Ahern 1327ab84be7eSDavid Ahern nh = nexthop_alloc(); 1328ab84be7eSDavid Ahern if (!nh) 1329ab84be7eSDavid Ahern return ERR_PTR(-ENOMEM); 1330ab84be7eSDavid Ahern 1331ab84be7eSDavid Ahern nhi = kzalloc(sizeof(*nhi), GFP_KERNEL); 1332ab84be7eSDavid Ahern if (!nhi) { 1333ab84be7eSDavid Ahern kfree(nh); 1334ab84be7eSDavid Ahern return ERR_PTR(-ENOMEM); 1335ab84be7eSDavid Ahern } 1336ab84be7eSDavid Ahern 1337ab84be7eSDavid Ahern nh->nh_flags = cfg->nh_flags; 1338ab84be7eSDavid Ahern nh->net = net; 1339ab84be7eSDavid Ahern 1340ab84be7eSDavid Ahern nhi->nh_parent = nh; 1341ab84be7eSDavid Ahern nhi->family = cfg->nh_family; 1342ab84be7eSDavid Ahern nhi->fib_nhc.nhc_scope = RT_SCOPE_LINK; 1343ab84be7eSDavid Ahern 134438428d68SRoopa Prabhu if (cfg->nh_fdb) 1345ce9ac056SDavid Ahern nhi->fdb_nh = 1; 134638428d68SRoopa Prabhu 1347ab84be7eSDavid Ahern if (cfg->nh_blackhole) { 1348ab84be7eSDavid Ahern nhi->reject_nh = 1; 1349ab84be7eSDavid Ahern cfg->nh_ifindex = net->loopback_dev->ifindex; 1350ab84be7eSDavid Ahern } 1351ab84be7eSDavid Ahern 1352597cfe4fSDavid Ahern switch (cfg->nh_family) { 1353597cfe4fSDavid Ahern case AF_INET: 1354597cfe4fSDavid Ahern err = nh_create_ipv4(net, nh, nhi, cfg, extack); 1355597cfe4fSDavid Ahern break; 135653010f99SDavid Ahern case AF_INET6: 135753010f99SDavid Ahern err = nh_create_ipv6(net, nh, nhi, cfg, extack); 135853010f99SDavid Ahern break; 1359597cfe4fSDavid Ahern } 1360597cfe4fSDavid Ahern 1361ab84be7eSDavid Ahern if (err) { 1362ab84be7eSDavid Ahern kfree(nhi); 1363ab84be7eSDavid Ahern kfree(nh); 1364ab84be7eSDavid Ahern return ERR_PTR(err); 1365ab84be7eSDavid Ahern } 1366ab84be7eSDavid Ahern 1367597cfe4fSDavid Ahern /* add the entry to the device based hash */ 1368ce9ac056SDavid Ahern if (!nhi->fdb_nh) 1369597cfe4fSDavid Ahern nexthop_devhash_add(net, nhi); 1370597cfe4fSDavid Ahern 1371ab84be7eSDavid Ahern rcu_assign_pointer(nh->nh_info, nhi); 1372ab84be7eSDavid Ahern 1373ab84be7eSDavid Ahern return nh; 1374ab84be7eSDavid Ahern } 1375ab84be7eSDavid Ahern 1376ab84be7eSDavid Ahern /* called with rtnl lock held */ 1377ab84be7eSDavid Ahern static struct nexthop *nexthop_add(struct net *net, struct nh_config *cfg, 1378ab84be7eSDavid Ahern struct netlink_ext_ack *extack) 1379ab84be7eSDavid Ahern { 1380ab84be7eSDavid Ahern struct nexthop *nh; 1381ab84be7eSDavid Ahern int err; 1382ab84be7eSDavid Ahern 1383ab84be7eSDavid Ahern if (cfg->nlflags & NLM_F_REPLACE && !cfg->nh_id) { 1384ab84be7eSDavid Ahern NL_SET_ERR_MSG(extack, "Replace requires nexthop id"); 1385ab84be7eSDavid Ahern return ERR_PTR(-EINVAL); 1386ab84be7eSDavid Ahern } 1387ab84be7eSDavid Ahern 1388ab84be7eSDavid Ahern if (!cfg->nh_id) { 1389ab84be7eSDavid Ahern cfg->nh_id = nh_find_unused_id(net); 1390ab84be7eSDavid Ahern if (!cfg->nh_id) { 1391ab84be7eSDavid Ahern NL_SET_ERR_MSG(extack, "No unused id"); 1392ab84be7eSDavid Ahern return ERR_PTR(-EINVAL); 1393ab84be7eSDavid Ahern } 1394ab84be7eSDavid Ahern } 1395ab84be7eSDavid Ahern 1396430a0491SDavid Ahern if (cfg->nh_grp) 1397430a0491SDavid Ahern nh = nexthop_create_group(net, cfg); 1398430a0491SDavid Ahern else 1399ab84be7eSDavid Ahern nh = nexthop_create(net, cfg, extack); 1400430a0491SDavid Ahern 1401ab84be7eSDavid Ahern if (IS_ERR(nh)) 1402ab84be7eSDavid Ahern return nh; 1403ab84be7eSDavid Ahern 1404ab84be7eSDavid Ahern refcount_set(&nh->refcnt, 1); 1405ab84be7eSDavid Ahern nh->id = cfg->nh_id; 1406ab84be7eSDavid Ahern nh->protocol = cfg->nh_protocol; 1407ab84be7eSDavid Ahern nh->net = net; 1408ab84be7eSDavid Ahern 1409ab84be7eSDavid Ahern err = insert_nexthop(net, nh, cfg, extack); 1410ab84be7eSDavid Ahern if (err) { 1411430a0491SDavid Ahern __remove_nexthop(net, nh, NULL); 1412ab84be7eSDavid Ahern nexthop_put(nh); 1413ab84be7eSDavid Ahern nh = ERR_PTR(err); 1414ab84be7eSDavid Ahern } 1415ab84be7eSDavid Ahern 1416ab84be7eSDavid Ahern return nh; 1417ab84be7eSDavid Ahern } 1418ab84be7eSDavid Ahern 1419ab84be7eSDavid Ahern static int rtm_to_nh_config(struct net *net, struct sk_buff *skb, 1420ab84be7eSDavid Ahern struct nlmsghdr *nlh, struct nh_config *cfg, 1421ab84be7eSDavid Ahern struct netlink_ext_ack *extack) 1422ab84be7eSDavid Ahern { 1423ab84be7eSDavid Ahern struct nhmsg *nhm = nlmsg_data(nlh); 1424ab84be7eSDavid Ahern struct nlattr *tb[NHA_MAX + 1]; 1425ab84be7eSDavid Ahern int err; 1426ab84be7eSDavid Ahern 1427ab84be7eSDavid Ahern err = nlmsg_parse(nlh, sizeof(*nhm), tb, NHA_MAX, rtm_nh_policy, 1428ab84be7eSDavid Ahern extack); 1429ab84be7eSDavid Ahern if (err < 0) 1430ab84be7eSDavid Ahern return err; 1431ab84be7eSDavid Ahern 1432ab84be7eSDavid Ahern err = -EINVAL; 1433ab84be7eSDavid Ahern if (nhm->resvd || nhm->nh_scope) { 1434ab84be7eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid values in ancillary header"); 1435ab84be7eSDavid Ahern goto out; 1436ab84be7eSDavid Ahern } 1437ab84be7eSDavid Ahern if (nhm->nh_flags & ~NEXTHOP_VALID_USER_FLAGS) { 1438ab84be7eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid nexthop flags in ancillary header"); 1439ab84be7eSDavid Ahern goto out; 1440ab84be7eSDavid Ahern } 1441ab84be7eSDavid Ahern 1442ab84be7eSDavid Ahern switch (nhm->nh_family) { 1443597cfe4fSDavid Ahern case AF_INET: 144453010f99SDavid Ahern case AF_INET6: 1445597cfe4fSDavid Ahern break; 1446430a0491SDavid Ahern case AF_UNSPEC: 1447430a0491SDavid Ahern if (tb[NHA_GROUP]) 1448430a0491SDavid Ahern break; 1449a8eceea8SJoe Perches fallthrough; 1450ab84be7eSDavid Ahern default: 1451ab84be7eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid address family"); 1452ab84be7eSDavid Ahern goto out; 1453ab84be7eSDavid Ahern } 1454ab84be7eSDavid Ahern 1455ab84be7eSDavid Ahern if (tb[NHA_GROUPS] || tb[NHA_MASTER]) { 1456ab84be7eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid attributes in request"); 1457ab84be7eSDavid Ahern goto out; 1458ab84be7eSDavid Ahern } 1459ab84be7eSDavid Ahern 1460ab84be7eSDavid Ahern memset(cfg, 0, sizeof(*cfg)); 1461ab84be7eSDavid Ahern cfg->nlflags = nlh->nlmsg_flags; 1462ab84be7eSDavid Ahern cfg->nlinfo.portid = NETLINK_CB(skb).portid; 1463ab84be7eSDavid Ahern cfg->nlinfo.nlh = nlh; 1464ab84be7eSDavid Ahern cfg->nlinfo.nl_net = net; 1465ab84be7eSDavid Ahern 1466ab84be7eSDavid Ahern cfg->nh_family = nhm->nh_family; 1467ab84be7eSDavid Ahern cfg->nh_protocol = nhm->nh_protocol; 1468ab84be7eSDavid Ahern cfg->nh_flags = nhm->nh_flags; 1469ab84be7eSDavid Ahern 1470ab84be7eSDavid Ahern if (tb[NHA_ID]) 1471ab84be7eSDavid Ahern cfg->nh_id = nla_get_u32(tb[NHA_ID]); 1472ab84be7eSDavid Ahern 147338428d68SRoopa Prabhu if (tb[NHA_FDB]) { 147438428d68SRoopa Prabhu if (tb[NHA_OIF] || tb[NHA_BLACKHOLE] || 147538428d68SRoopa Prabhu tb[NHA_ENCAP] || tb[NHA_ENCAP_TYPE]) { 147638428d68SRoopa Prabhu NL_SET_ERR_MSG(extack, "Fdb attribute can not be used with encap, oif or blackhole"); 147738428d68SRoopa Prabhu goto out; 147838428d68SRoopa Prabhu } 147938428d68SRoopa Prabhu if (nhm->nh_flags) { 148038428d68SRoopa Prabhu NL_SET_ERR_MSG(extack, "Unsupported nexthop flags in ancillary header"); 148138428d68SRoopa Prabhu goto out; 148238428d68SRoopa Prabhu } 148338428d68SRoopa Prabhu cfg->nh_fdb = nla_get_flag(tb[NHA_FDB]); 148438428d68SRoopa Prabhu } 148538428d68SRoopa Prabhu 1486430a0491SDavid Ahern if (tb[NHA_GROUP]) { 1487430a0491SDavid Ahern if (nhm->nh_family != AF_UNSPEC) { 1488430a0491SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid family for group"); 1489430a0491SDavid Ahern goto out; 1490430a0491SDavid Ahern } 1491430a0491SDavid Ahern cfg->nh_grp = tb[NHA_GROUP]; 1492430a0491SDavid Ahern 1493430a0491SDavid Ahern cfg->nh_grp_type = NEXTHOP_GRP_TYPE_MPATH; 1494430a0491SDavid Ahern if (tb[NHA_GROUP_TYPE]) 1495430a0491SDavid Ahern cfg->nh_grp_type = nla_get_u16(tb[NHA_GROUP_TYPE]); 1496430a0491SDavid Ahern 1497430a0491SDavid Ahern if (cfg->nh_grp_type > NEXTHOP_GRP_TYPE_MAX) { 1498430a0491SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid group type"); 1499430a0491SDavid Ahern goto out; 1500430a0491SDavid Ahern } 1501430a0491SDavid Ahern err = nh_check_attr_group(net, tb, extack); 1502430a0491SDavid Ahern 1503430a0491SDavid Ahern /* no other attributes should be set */ 1504430a0491SDavid Ahern goto out; 1505430a0491SDavid Ahern } 1506430a0491SDavid Ahern 1507ab84be7eSDavid Ahern if (tb[NHA_BLACKHOLE]) { 1508b513bd03SDavid Ahern if (tb[NHA_GATEWAY] || tb[NHA_OIF] || 150938428d68SRoopa Prabhu tb[NHA_ENCAP] || tb[NHA_ENCAP_TYPE] || tb[NHA_FDB]) { 151038428d68SRoopa Prabhu NL_SET_ERR_MSG(extack, "Blackhole attribute can not be used with gateway, oif, encap or fdb"); 1511ab84be7eSDavid Ahern goto out; 1512ab84be7eSDavid Ahern } 1513ab84be7eSDavid Ahern 1514ab84be7eSDavid Ahern cfg->nh_blackhole = 1; 1515ab84be7eSDavid Ahern err = 0; 1516ab84be7eSDavid Ahern goto out; 1517ab84be7eSDavid Ahern } 1518ab84be7eSDavid Ahern 151938428d68SRoopa Prabhu if (!cfg->nh_fdb && !tb[NHA_OIF]) { 152038428d68SRoopa Prabhu NL_SET_ERR_MSG(extack, "Device attribute required for non-blackhole and non-fdb nexthops"); 1521ab84be7eSDavid Ahern goto out; 1522ab84be7eSDavid Ahern } 1523ab84be7eSDavid Ahern 152438428d68SRoopa Prabhu if (!cfg->nh_fdb && tb[NHA_OIF]) { 1525ab84be7eSDavid Ahern cfg->nh_ifindex = nla_get_u32(tb[NHA_OIF]); 1526ab84be7eSDavid Ahern if (cfg->nh_ifindex) 1527ab84be7eSDavid Ahern cfg->dev = __dev_get_by_index(net, cfg->nh_ifindex); 1528ab84be7eSDavid Ahern 1529ab84be7eSDavid Ahern if (!cfg->dev) { 1530ab84be7eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid device index"); 1531ab84be7eSDavid Ahern goto out; 1532ab84be7eSDavid Ahern } else if (!(cfg->dev->flags & IFF_UP)) { 1533ab84be7eSDavid Ahern NL_SET_ERR_MSG(extack, "Nexthop device is not up"); 1534ab84be7eSDavid Ahern err = -ENETDOWN; 1535ab84be7eSDavid Ahern goto out; 1536ab84be7eSDavid Ahern } else if (!netif_carrier_ok(cfg->dev)) { 1537ab84be7eSDavid Ahern NL_SET_ERR_MSG(extack, "Carrier for nexthop device is down"); 1538ab84be7eSDavid Ahern err = -ENETDOWN; 1539ab84be7eSDavid Ahern goto out; 1540ab84be7eSDavid Ahern } 154138428d68SRoopa Prabhu } 1542ab84be7eSDavid Ahern 1543597cfe4fSDavid Ahern err = -EINVAL; 1544597cfe4fSDavid Ahern if (tb[NHA_GATEWAY]) { 1545597cfe4fSDavid Ahern struct nlattr *gwa = tb[NHA_GATEWAY]; 1546597cfe4fSDavid Ahern 1547597cfe4fSDavid Ahern switch (cfg->nh_family) { 1548597cfe4fSDavid Ahern case AF_INET: 1549597cfe4fSDavid Ahern if (nla_len(gwa) != sizeof(u32)) { 1550597cfe4fSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid gateway"); 1551597cfe4fSDavid Ahern goto out; 1552597cfe4fSDavid Ahern } 1553597cfe4fSDavid Ahern cfg->gw.ipv4 = nla_get_be32(gwa); 1554597cfe4fSDavid Ahern break; 155553010f99SDavid Ahern case AF_INET6: 155653010f99SDavid Ahern if (nla_len(gwa) != sizeof(struct in6_addr)) { 155753010f99SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid gateway"); 155853010f99SDavid Ahern goto out; 155953010f99SDavid Ahern } 156053010f99SDavid Ahern cfg->gw.ipv6 = nla_get_in6_addr(gwa); 156153010f99SDavid Ahern break; 1562597cfe4fSDavid Ahern default: 1563597cfe4fSDavid Ahern NL_SET_ERR_MSG(extack, 1564597cfe4fSDavid Ahern "Unknown address family for gateway"); 1565597cfe4fSDavid Ahern goto out; 1566597cfe4fSDavid Ahern } 1567597cfe4fSDavid Ahern } else { 1568597cfe4fSDavid Ahern /* device only nexthop (no gateway) */ 1569597cfe4fSDavid Ahern if (cfg->nh_flags & RTNH_F_ONLINK) { 1570597cfe4fSDavid Ahern NL_SET_ERR_MSG(extack, 1571597cfe4fSDavid Ahern "ONLINK flag can not be set for nexthop without a gateway"); 1572597cfe4fSDavid Ahern goto out; 1573597cfe4fSDavid Ahern } 1574597cfe4fSDavid Ahern } 1575597cfe4fSDavid Ahern 1576b513bd03SDavid Ahern if (tb[NHA_ENCAP]) { 1577b513bd03SDavid Ahern cfg->nh_encap = tb[NHA_ENCAP]; 1578b513bd03SDavid Ahern 1579b513bd03SDavid Ahern if (!tb[NHA_ENCAP_TYPE]) { 1580b513bd03SDavid Ahern NL_SET_ERR_MSG(extack, "LWT encapsulation type is missing"); 1581b513bd03SDavid Ahern goto out; 1582b513bd03SDavid Ahern } 1583b513bd03SDavid Ahern 1584b513bd03SDavid Ahern cfg->nh_encap_type = nla_get_u16(tb[NHA_ENCAP_TYPE]); 1585b513bd03SDavid Ahern err = lwtunnel_valid_encap_type(cfg->nh_encap_type, extack); 1586b513bd03SDavid Ahern if (err < 0) 1587b513bd03SDavid Ahern goto out; 1588b513bd03SDavid Ahern 1589b513bd03SDavid Ahern } else if (tb[NHA_ENCAP_TYPE]) { 1590b513bd03SDavid Ahern NL_SET_ERR_MSG(extack, "LWT encapsulation attribute is missing"); 1591b513bd03SDavid Ahern goto out; 1592b513bd03SDavid Ahern } 1593b513bd03SDavid Ahern 1594b513bd03SDavid Ahern 1595ab84be7eSDavid Ahern err = 0; 1596ab84be7eSDavid Ahern out: 1597ab84be7eSDavid Ahern return err; 1598ab84be7eSDavid Ahern } 1599ab84be7eSDavid Ahern 1600ab84be7eSDavid Ahern /* rtnl */ 1601ab84be7eSDavid Ahern static int rtm_new_nexthop(struct sk_buff *skb, struct nlmsghdr *nlh, 1602ab84be7eSDavid Ahern struct netlink_ext_ack *extack) 1603ab84be7eSDavid Ahern { 1604ab84be7eSDavid Ahern struct net *net = sock_net(skb->sk); 1605ab84be7eSDavid Ahern struct nh_config cfg; 1606ab84be7eSDavid Ahern struct nexthop *nh; 1607ab84be7eSDavid Ahern int err; 1608ab84be7eSDavid Ahern 1609ab84be7eSDavid Ahern err = rtm_to_nh_config(net, skb, nlh, &cfg, extack); 1610ab84be7eSDavid Ahern if (!err) { 1611ab84be7eSDavid Ahern nh = nexthop_add(net, &cfg, extack); 1612ab84be7eSDavid Ahern if (IS_ERR(nh)) 1613ab84be7eSDavid Ahern err = PTR_ERR(nh); 1614ab84be7eSDavid Ahern } 1615ab84be7eSDavid Ahern 1616ab84be7eSDavid Ahern return err; 1617ab84be7eSDavid Ahern } 1618ab84be7eSDavid Ahern 1619ab84be7eSDavid Ahern static int nh_valid_get_del_req(struct nlmsghdr *nlh, u32 *id, 1620ab84be7eSDavid Ahern struct netlink_ext_ack *extack) 1621ab84be7eSDavid Ahern { 1622ab84be7eSDavid Ahern struct nhmsg *nhm = nlmsg_data(nlh); 1623ab84be7eSDavid Ahern struct nlattr *tb[NHA_MAX + 1]; 1624ab84be7eSDavid Ahern int err, i; 1625ab84be7eSDavid Ahern 1626ab84be7eSDavid Ahern err = nlmsg_parse(nlh, sizeof(*nhm), tb, NHA_MAX, rtm_nh_policy, 1627ab84be7eSDavid Ahern extack); 1628ab84be7eSDavid Ahern if (err < 0) 1629ab84be7eSDavid Ahern return err; 1630ab84be7eSDavid Ahern 1631ab84be7eSDavid Ahern err = -EINVAL; 1632ab84be7eSDavid Ahern for (i = 0; i < __NHA_MAX; ++i) { 1633ab84be7eSDavid Ahern if (!tb[i]) 1634ab84be7eSDavid Ahern continue; 1635ab84be7eSDavid Ahern 1636ab84be7eSDavid Ahern switch (i) { 1637ab84be7eSDavid Ahern case NHA_ID: 1638ab84be7eSDavid Ahern break; 1639ab84be7eSDavid Ahern default: 1640ab84be7eSDavid Ahern NL_SET_ERR_MSG_ATTR(extack, tb[i], 1641ab84be7eSDavid Ahern "Unexpected attribute in request"); 1642ab84be7eSDavid Ahern goto out; 1643ab84be7eSDavid Ahern } 1644ab84be7eSDavid Ahern } 1645ab84be7eSDavid Ahern if (nhm->nh_protocol || nhm->resvd || nhm->nh_scope || nhm->nh_flags) { 1646ab84be7eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid values in header"); 1647ab84be7eSDavid Ahern goto out; 1648ab84be7eSDavid Ahern } 1649ab84be7eSDavid Ahern 1650ab84be7eSDavid Ahern if (!tb[NHA_ID]) { 1651ab84be7eSDavid Ahern NL_SET_ERR_MSG(extack, "Nexthop id is missing"); 1652ab84be7eSDavid Ahern goto out; 1653ab84be7eSDavid Ahern } 1654ab84be7eSDavid Ahern 1655ab84be7eSDavid Ahern *id = nla_get_u32(tb[NHA_ID]); 1656ab84be7eSDavid Ahern if (!(*id)) 1657ab84be7eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid nexthop id"); 1658ab84be7eSDavid Ahern else 1659ab84be7eSDavid Ahern err = 0; 1660ab84be7eSDavid Ahern out: 1661ab84be7eSDavid Ahern return err; 1662ab84be7eSDavid Ahern } 1663ab84be7eSDavid Ahern 1664ab84be7eSDavid Ahern /* rtnl */ 1665ab84be7eSDavid Ahern static int rtm_del_nexthop(struct sk_buff *skb, struct nlmsghdr *nlh, 1666ab84be7eSDavid Ahern struct netlink_ext_ack *extack) 1667ab84be7eSDavid Ahern { 1668ab84be7eSDavid Ahern struct net *net = sock_net(skb->sk); 1669ab84be7eSDavid Ahern struct nl_info nlinfo = { 1670ab84be7eSDavid Ahern .nlh = nlh, 1671ab84be7eSDavid Ahern .nl_net = net, 1672ab84be7eSDavid Ahern .portid = NETLINK_CB(skb).portid, 1673ab84be7eSDavid Ahern }; 1674ab84be7eSDavid Ahern struct nexthop *nh; 1675ab84be7eSDavid Ahern int err; 1676ab84be7eSDavid Ahern u32 id; 1677ab84be7eSDavid Ahern 1678ab84be7eSDavid Ahern err = nh_valid_get_del_req(nlh, &id, extack); 1679ab84be7eSDavid Ahern if (err) 1680ab84be7eSDavid Ahern return err; 1681ab84be7eSDavid Ahern 1682ab84be7eSDavid Ahern nh = nexthop_find_by_id(net, id); 1683ab84be7eSDavid Ahern if (!nh) 1684ab84be7eSDavid Ahern return -ENOENT; 1685ab84be7eSDavid Ahern 1686430a0491SDavid Ahern remove_nexthop(net, nh, &nlinfo); 1687ab84be7eSDavid Ahern 1688ab84be7eSDavid Ahern return 0; 1689ab84be7eSDavid Ahern } 1690ab84be7eSDavid Ahern 1691ab84be7eSDavid Ahern /* rtnl */ 1692ab84be7eSDavid Ahern static int rtm_get_nexthop(struct sk_buff *in_skb, struct nlmsghdr *nlh, 1693ab84be7eSDavid Ahern struct netlink_ext_ack *extack) 1694ab84be7eSDavid Ahern { 1695ab84be7eSDavid Ahern struct net *net = sock_net(in_skb->sk); 1696ab84be7eSDavid Ahern struct sk_buff *skb = NULL; 1697ab84be7eSDavid Ahern struct nexthop *nh; 1698ab84be7eSDavid Ahern int err; 1699ab84be7eSDavid Ahern u32 id; 1700ab84be7eSDavid Ahern 1701ab84be7eSDavid Ahern err = nh_valid_get_del_req(nlh, &id, extack); 1702ab84be7eSDavid Ahern if (err) 1703ab84be7eSDavid Ahern return err; 1704ab84be7eSDavid Ahern 1705ab84be7eSDavid Ahern err = -ENOBUFS; 1706ab84be7eSDavid Ahern skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 1707ab84be7eSDavid Ahern if (!skb) 1708ab84be7eSDavid Ahern goto out; 1709ab84be7eSDavid Ahern 1710ab84be7eSDavid Ahern err = -ENOENT; 1711ab84be7eSDavid Ahern nh = nexthop_find_by_id(net, id); 1712ab84be7eSDavid Ahern if (!nh) 1713ab84be7eSDavid Ahern goto errout_free; 1714ab84be7eSDavid Ahern 1715ab84be7eSDavid Ahern err = nh_fill_node(skb, nh, RTM_NEWNEXTHOP, NETLINK_CB(in_skb).portid, 1716ab84be7eSDavid Ahern nlh->nlmsg_seq, 0); 1717ab84be7eSDavid Ahern if (err < 0) { 1718ab84be7eSDavid Ahern WARN_ON(err == -EMSGSIZE); 1719ab84be7eSDavid Ahern goto errout_free; 1720ab84be7eSDavid Ahern } 1721ab84be7eSDavid Ahern 1722ab84be7eSDavid Ahern err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 1723ab84be7eSDavid Ahern out: 1724ab84be7eSDavid Ahern return err; 1725ab84be7eSDavid Ahern errout_free: 1726ab84be7eSDavid Ahern kfree_skb(skb); 1727ab84be7eSDavid Ahern goto out; 1728ab84be7eSDavid Ahern } 1729ab84be7eSDavid Ahern 1730430a0491SDavid Ahern static bool nh_dump_filtered(struct nexthop *nh, int dev_idx, int master_idx, 1731430a0491SDavid Ahern bool group_filter, u8 family) 1732ab84be7eSDavid Ahern { 1733ab84be7eSDavid Ahern const struct net_device *dev; 1734ab84be7eSDavid Ahern const struct nh_info *nhi; 1735ab84be7eSDavid Ahern 1736430a0491SDavid Ahern if (group_filter && !nh->is_group) 1737430a0491SDavid Ahern return true; 1738430a0491SDavid Ahern 1739ab84be7eSDavid Ahern if (!dev_idx && !master_idx && !family) 1740ab84be7eSDavid Ahern return false; 1741ab84be7eSDavid Ahern 1742430a0491SDavid Ahern if (nh->is_group) 1743430a0491SDavid Ahern return true; 1744430a0491SDavid Ahern 1745ab84be7eSDavid Ahern nhi = rtnl_dereference(nh->nh_info); 1746ab84be7eSDavid Ahern if (family && nhi->family != family) 1747ab84be7eSDavid Ahern return true; 1748ab84be7eSDavid Ahern 1749ab84be7eSDavid Ahern dev = nhi->fib_nhc.nhc_dev; 1750ab84be7eSDavid Ahern if (dev_idx && (!dev || dev->ifindex != dev_idx)) 1751ab84be7eSDavid Ahern return true; 1752ab84be7eSDavid Ahern 1753ab84be7eSDavid Ahern if (master_idx) { 1754ab84be7eSDavid Ahern struct net_device *master; 1755ab84be7eSDavid Ahern 1756ab84be7eSDavid Ahern if (!dev) 1757ab84be7eSDavid Ahern return true; 1758ab84be7eSDavid Ahern 1759ab84be7eSDavid Ahern master = netdev_master_upper_dev_get((struct net_device *)dev); 1760ab84be7eSDavid Ahern if (!master || master->ifindex != master_idx) 1761ab84be7eSDavid Ahern return true; 1762ab84be7eSDavid Ahern } 1763ab84be7eSDavid Ahern 1764ab84be7eSDavid Ahern return false; 1765ab84be7eSDavid Ahern } 1766ab84be7eSDavid Ahern 1767430a0491SDavid Ahern static int nh_valid_dump_req(const struct nlmsghdr *nlh, int *dev_idx, 1768430a0491SDavid Ahern int *master_idx, bool *group_filter, 176938428d68SRoopa Prabhu bool *fdb_filter, struct netlink_callback *cb) 1770ab84be7eSDavid Ahern { 1771ab84be7eSDavid Ahern struct netlink_ext_ack *extack = cb->extack; 1772ab84be7eSDavid Ahern struct nlattr *tb[NHA_MAX + 1]; 1773ab84be7eSDavid Ahern struct nhmsg *nhm; 1774ab84be7eSDavid Ahern int err, i; 1775ab84be7eSDavid Ahern u32 idx; 1776ab84be7eSDavid Ahern 1777ab84be7eSDavid Ahern err = nlmsg_parse(nlh, sizeof(*nhm), tb, NHA_MAX, rtm_nh_policy, 1778ab84be7eSDavid Ahern NULL); 1779ab84be7eSDavid Ahern if (err < 0) 1780ab84be7eSDavid Ahern return err; 1781ab84be7eSDavid Ahern 1782ab84be7eSDavid Ahern for (i = 0; i <= NHA_MAX; ++i) { 1783ab84be7eSDavid Ahern if (!tb[i]) 1784ab84be7eSDavid Ahern continue; 1785ab84be7eSDavid Ahern 1786ab84be7eSDavid Ahern switch (i) { 1787ab84be7eSDavid Ahern case NHA_OIF: 1788ab84be7eSDavid Ahern idx = nla_get_u32(tb[i]); 1789ab84be7eSDavid Ahern if (idx > INT_MAX) { 1790ab84be7eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid device index"); 1791ab84be7eSDavid Ahern return -EINVAL; 1792ab84be7eSDavid Ahern } 1793ab84be7eSDavid Ahern *dev_idx = idx; 1794ab84be7eSDavid Ahern break; 1795ab84be7eSDavid Ahern case NHA_MASTER: 1796ab84be7eSDavid Ahern idx = nla_get_u32(tb[i]); 1797ab84be7eSDavid Ahern if (idx > INT_MAX) { 1798ab84be7eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid master device index"); 1799ab84be7eSDavid Ahern return -EINVAL; 1800ab84be7eSDavid Ahern } 1801ab84be7eSDavid Ahern *master_idx = idx; 1802ab84be7eSDavid Ahern break; 1803430a0491SDavid Ahern case NHA_GROUPS: 1804430a0491SDavid Ahern *group_filter = true; 1805430a0491SDavid Ahern break; 180638428d68SRoopa Prabhu case NHA_FDB: 180738428d68SRoopa Prabhu *fdb_filter = true; 180838428d68SRoopa Prabhu break; 1809ab84be7eSDavid Ahern default: 1810ab84be7eSDavid Ahern NL_SET_ERR_MSG(extack, "Unsupported attribute in dump request"); 1811ab84be7eSDavid Ahern return -EINVAL; 1812ab84be7eSDavid Ahern } 1813ab84be7eSDavid Ahern } 1814ab84be7eSDavid Ahern 1815ab84be7eSDavid Ahern nhm = nlmsg_data(nlh); 1816ab84be7eSDavid Ahern if (nhm->nh_protocol || nhm->resvd || nhm->nh_scope || nhm->nh_flags) { 1817ab84be7eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid values in header for nexthop dump request"); 1818ab84be7eSDavid Ahern return -EINVAL; 1819ab84be7eSDavid Ahern } 1820ab84be7eSDavid Ahern 1821ab84be7eSDavid Ahern return 0; 1822ab84be7eSDavid Ahern } 1823ab84be7eSDavid Ahern 1824ab84be7eSDavid Ahern /* rtnl */ 1825ab84be7eSDavid Ahern static int rtm_dump_nexthop(struct sk_buff *skb, struct netlink_callback *cb) 1826ab84be7eSDavid Ahern { 182738428d68SRoopa Prabhu bool group_filter = false, fdb_filter = false; 1828ab84be7eSDavid Ahern struct nhmsg *nhm = nlmsg_data(cb->nlh); 1829ab84be7eSDavid Ahern int dev_filter_idx = 0, master_idx = 0; 1830ab84be7eSDavid Ahern struct net *net = sock_net(skb->sk); 1831ab84be7eSDavid Ahern struct rb_root *root = &net->nexthop.rb_root; 1832ab84be7eSDavid Ahern struct rb_node *node; 1833ab84be7eSDavid Ahern int idx = 0, s_idx; 1834ab84be7eSDavid Ahern int err; 1835ab84be7eSDavid Ahern 1836430a0491SDavid Ahern err = nh_valid_dump_req(cb->nlh, &dev_filter_idx, &master_idx, 183738428d68SRoopa Prabhu &group_filter, &fdb_filter, cb); 1838ab84be7eSDavid Ahern if (err < 0) 1839ab84be7eSDavid Ahern return err; 1840ab84be7eSDavid Ahern 1841ab84be7eSDavid Ahern s_idx = cb->args[0]; 1842ab84be7eSDavid Ahern for (node = rb_first(root); node; node = rb_next(node)) { 1843ab84be7eSDavid Ahern struct nexthop *nh; 1844ab84be7eSDavid Ahern 1845ab84be7eSDavid Ahern if (idx < s_idx) 1846ab84be7eSDavid Ahern goto cont; 1847ab84be7eSDavid Ahern 1848ab84be7eSDavid Ahern nh = rb_entry(node, struct nexthop, rb_node); 1849ab84be7eSDavid Ahern if (nh_dump_filtered(nh, dev_filter_idx, master_idx, 1850430a0491SDavid Ahern group_filter, nhm->nh_family)) 1851ab84be7eSDavid Ahern goto cont; 1852ab84be7eSDavid Ahern 1853ab84be7eSDavid Ahern err = nh_fill_node(skb, nh, RTM_NEWNEXTHOP, 1854ab84be7eSDavid Ahern NETLINK_CB(cb->skb).portid, 1855ab84be7eSDavid Ahern cb->nlh->nlmsg_seq, NLM_F_MULTI); 1856ab84be7eSDavid Ahern if (err < 0) { 1857ab84be7eSDavid Ahern if (likely(skb->len)) 1858ab84be7eSDavid Ahern goto out; 1859ab84be7eSDavid Ahern 1860ab84be7eSDavid Ahern goto out_err; 1861ab84be7eSDavid Ahern } 1862ab84be7eSDavid Ahern cont: 1863ab84be7eSDavid Ahern idx++; 1864ab84be7eSDavid Ahern } 1865ab84be7eSDavid Ahern 1866ab84be7eSDavid Ahern out: 1867ab84be7eSDavid Ahern err = skb->len; 1868ab84be7eSDavid Ahern out_err: 1869ab84be7eSDavid Ahern cb->args[0] = idx; 1870ab84be7eSDavid Ahern cb->seq = net->nexthop.seq; 1871ab84be7eSDavid Ahern nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 1872ab84be7eSDavid Ahern 1873ab84be7eSDavid Ahern return err; 1874ab84be7eSDavid Ahern } 1875ab84be7eSDavid Ahern 1876597cfe4fSDavid Ahern static void nexthop_sync_mtu(struct net_device *dev, u32 orig_mtu) 1877597cfe4fSDavid Ahern { 1878597cfe4fSDavid Ahern unsigned int hash = nh_dev_hashfn(dev->ifindex); 1879597cfe4fSDavid Ahern struct net *net = dev_net(dev); 1880597cfe4fSDavid Ahern struct hlist_head *head = &net->nexthop.devhash[hash]; 1881597cfe4fSDavid Ahern struct hlist_node *n; 1882597cfe4fSDavid Ahern struct nh_info *nhi; 1883597cfe4fSDavid Ahern 1884597cfe4fSDavid Ahern hlist_for_each_entry_safe(nhi, n, head, dev_hash) { 1885597cfe4fSDavid Ahern if (nhi->fib_nhc.nhc_dev == dev) { 1886597cfe4fSDavid Ahern if (nhi->family == AF_INET) 1887597cfe4fSDavid Ahern fib_nhc_update_mtu(&nhi->fib_nhc, dev->mtu, 1888597cfe4fSDavid Ahern orig_mtu); 1889597cfe4fSDavid Ahern } 1890597cfe4fSDavid Ahern } 1891597cfe4fSDavid Ahern } 1892597cfe4fSDavid Ahern 1893597cfe4fSDavid Ahern /* rtnl */ 1894597cfe4fSDavid Ahern static int nh_netdev_event(struct notifier_block *this, 1895597cfe4fSDavid Ahern unsigned long event, void *ptr) 1896597cfe4fSDavid Ahern { 1897597cfe4fSDavid Ahern struct net_device *dev = netdev_notifier_info_to_dev(ptr); 1898597cfe4fSDavid Ahern struct netdev_notifier_info_ext *info_ext; 1899597cfe4fSDavid Ahern 1900597cfe4fSDavid Ahern switch (event) { 1901597cfe4fSDavid Ahern case NETDEV_DOWN: 1902597cfe4fSDavid Ahern case NETDEV_UNREGISTER: 1903597cfe4fSDavid Ahern nexthop_flush_dev(dev); 1904597cfe4fSDavid Ahern break; 1905597cfe4fSDavid Ahern case NETDEV_CHANGE: 1906597cfe4fSDavid Ahern if (!(dev_get_flags(dev) & (IFF_RUNNING | IFF_LOWER_UP))) 1907597cfe4fSDavid Ahern nexthop_flush_dev(dev); 1908597cfe4fSDavid Ahern break; 1909597cfe4fSDavid Ahern case NETDEV_CHANGEMTU: 1910597cfe4fSDavid Ahern info_ext = ptr; 1911597cfe4fSDavid Ahern nexthop_sync_mtu(dev, info_ext->ext.mtu); 1912597cfe4fSDavid Ahern rt_cache_flush(dev_net(dev)); 1913597cfe4fSDavid Ahern break; 1914597cfe4fSDavid Ahern } 1915597cfe4fSDavid Ahern return NOTIFY_DONE; 1916597cfe4fSDavid Ahern } 1917597cfe4fSDavid Ahern 1918597cfe4fSDavid Ahern static struct notifier_block nh_netdev_notifier = { 1919597cfe4fSDavid Ahern .notifier_call = nh_netdev_event, 1920597cfe4fSDavid Ahern }; 1921597cfe4fSDavid Ahern 19228590ceedSRoopa Prabhu int register_nexthop_notifier(struct net *net, struct notifier_block *nb) 19238590ceedSRoopa Prabhu { 19248590ceedSRoopa Prabhu return atomic_notifier_chain_register(&net->nexthop.notifier_chain, nb); 19258590ceedSRoopa Prabhu } 19268590ceedSRoopa Prabhu EXPORT_SYMBOL(register_nexthop_notifier); 19278590ceedSRoopa Prabhu 19288590ceedSRoopa Prabhu int unregister_nexthop_notifier(struct net *net, struct notifier_block *nb) 19298590ceedSRoopa Prabhu { 19308590ceedSRoopa Prabhu return atomic_notifier_chain_unregister(&net->nexthop.notifier_chain, 19318590ceedSRoopa Prabhu nb); 19328590ceedSRoopa Prabhu } 19338590ceedSRoopa Prabhu EXPORT_SYMBOL(unregister_nexthop_notifier); 19348590ceedSRoopa Prabhu 1935ab84be7eSDavid Ahern static void __net_exit nexthop_net_exit(struct net *net) 1936ab84be7eSDavid Ahern { 1937ab84be7eSDavid Ahern rtnl_lock(); 1938ab84be7eSDavid Ahern flush_all_nexthops(net); 1939ab84be7eSDavid Ahern rtnl_unlock(); 1940597cfe4fSDavid Ahern kfree(net->nexthop.devhash); 1941ab84be7eSDavid Ahern } 1942ab84be7eSDavid Ahern 1943ab84be7eSDavid Ahern static int __net_init nexthop_net_init(struct net *net) 1944ab84be7eSDavid Ahern { 1945597cfe4fSDavid Ahern size_t sz = sizeof(struct hlist_head) * NH_DEV_HASHSIZE; 1946597cfe4fSDavid Ahern 1947ab84be7eSDavid Ahern net->nexthop.rb_root = RB_ROOT; 1948597cfe4fSDavid Ahern net->nexthop.devhash = kzalloc(sz, GFP_KERNEL); 1949597cfe4fSDavid Ahern if (!net->nexthop.devhash) 1950597cfe4fSDavid Ahern return -ENOMEM; 19518590ceedSRoopa Prabhu ATOMIC_INIT_NOTIFIER_HEAD(&net->nexthop.notifier_chain); 1952ab84be7eSDavid Ahern 1953ab84be7eSDavid Ahern return 0; 1954ab84be7eSDavid Ahern } 1955ab84be7eSDavid Ahern 1956ab84be7eSDavid Ahern static struct pernet_operations nexthop_net_ops = { 1957ab84be7eSDavid Ahern .init = nexthop_net_init, 1958ab84be7eSDavid Ahern .exit = nexthop_net_exit, 1959ab84be7eSDavid Ahern }; 1960ab84be7eSDavid Ahern 1961ab84be7eSDavid Ahern static int __init nexthop_init(void) 1962ab84be7eSDavid Ahern { 1963ab84be7eSDavid Ahern register_pernet_subsys(&nexthop_net_ops); 1964ab84be7eSDavid Ahern 1965597cfe4fSDavid Ahern register_netdevice_notifier(&nh_netdev_notifier); 1966597cfe4fSDavid Ahern 1967ab84be7eSDavid Ahern rtnl_register(PF_UNSPEC, RTM_NEWNEXTHOP, rtm_new_nexthop, NULL, 0); 1968ab84be7eSDavid Ahern rtnl_register(PF_UNSPEC, RTM_DELNEXTHOP, rtm_del_nexthop, NULL, 0); 1969ab84be7eSDavid Ahern rtnl_register(PF_UNSPEC, RTM_GETNEXTHOP, rtm_get_nexthop, 1970ab84be7eSDavid Ahern rtm_dump_nexthop, 0); 1971ab84be7eSDavid Ahern 1972ab84be7eSDavid Ahern rtnl_register(PF_INET, RTM_NEWNEXTHOP, rtm_new_nexthop, NULL, 0); 1973ab84be7eSDavid Ahern rtnl_register(PF_INET, RTM_GETNEXTHOP, NULL, rtm_dump_nexthop, 0); 1974ab84be7eSDavid Ahern 1975ab84be7eSDavid Ahern rtnl_register(PF_INET6, RTM_NEWNEXTHOP, rtm_new_nexthop, NULL, 0); 1976ab84be7eSDavid Ahern rtnl_register(PF_INET6, RTM_GETNEXTHOP, NULL, rtm_dump_nexthop, 0); 1977ab84be7eSDavid Ahern 1978ab84be7eSDavid Ahern return 0; 1979ab84be7eSDavid Ahern } 1980ab84be7eSDavid Ahern subsys_initcall(nexthop_init); 1981