18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Generic nexthop implementation 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2017-19 Cumulus Networks 58c2ecf20Sopenharmony_ci * Copyright (c) 2017-19 David Ahern <dsa@cumulusnetworks.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/nexthop.h> 98c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <net/arp.h> 128c2ecf20Sopenharmony_ci#include <net/ipv6_stubs.h> 138c2ecf20Sopenharmony_ci#include <net/lwtunnel.h> 148c2ecf20Sopenharmony_ci#include <net/ndisc.h> 158c2ecf20Sopenharmony_ci#include <net/nexthop.h> 168c2ecf20Sopenharmony_ci#include <net/route.h> 178c2ecf20Sopenharmony_ci#include <net/sock.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic void remove_nexthop(struct net *net, struct nexthop *nh, 208c2ecf20Sopenharmony_ci struct nl_info *nlinfo); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define NH_DEV_HASHBITS 8 238c2ecf20Sopenharmony_ci#define NH_DEV_HASHSIZE (1U << NH_DEV_HASHBITS) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic const struct nla_policy rtm_nh_policy[NHA_MAX + 1] = { 268c2ecf20Sopenharmony_ci [NHA_ID] = { .type = NLA_U32 }, 278c2ecf20Sopenharmony_ci [NHA_GROUP] = { .type = NLA_BINARY }, 288c2ecf20Sopenharmony_ci [NHA_GROUP_TYPE] = { .type = NLA_U16 }, 298c2ecf20Sopenharmony_ci [NHA_BLACKHOLE] = { .type = NLA_FLAG }, 308c2ecf20Sopenharmony_ci [NHA_OIF] = { .type = NLA_U32 }, 318c2ecf20Sopenharmony_ci [NHA_GATEWAY] = { .type = NLA_BINARY }, 328c2ecf20Sopenharmony_ci [NHA_ENCAP_TYPE] = { .type = NLA_U16 }, 338c2ecf20Sopenharmony_ci [NHA_ENCAP] = { .type = NLA_NESTED }, 348c2ecf20Sopenharmony_ci [NHA_GROUPS] = { .type = NLA_FLAG }, 358c2ecf20Sopenharmony_ci [NHA_MASTER] = { .type = NLA_U32 }, 368c2ecf20Sopenharmony_ci [NHA_FDB] = { .type = NLA_FLAG }, 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int call_nexthop_notifiers(struct net *net, 408c2ecf20Sopenharmony_ci enum nexthop_event_type event_type, 418c2ecf20Sopenharmony_ci struct nexthop *nh) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci int err; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci err = blocking_notifier_call_chain(&net->nexthop.notifier_chain, 468c2ecf20Sopenharmony_ci event_type, nh); 478c2ecf20Sopenharmony_ci return notifier_to_errno(err); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic unsigned int nh_dev_hashfn(unsigned int val) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci unsigned int mask = NH_DEV_HASHSIZE - 1; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci return (val ^ 558c2ecf20Sopenharmony_ci (val >> NH_DEV_HASHBITS) ^ 568c2ecf20Sopenharmony_ci (val >> (NH_DEV_HASHBITS * 2))) & mask; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic void nexthop_devhash_add(struct net *net, struct nh_info *nhi) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct net_device *dev = nhi->fib_nhc.nhc_dev; 628c2ecf20Sopenharmony_ci struct hlist_head *head; 638c2ecf20Sopenharmony_ci unsigned int hash; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci WARN_ON(!dev); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci hash = nh_dev_hashfn(dev->ifindex); 688c2ecf20Sopenharmony_ci head = &net->nexthop.devhash[hash]; 698c2ecf20Sopenharmony_ci hlist_add_head(&nhi->dev_hash, head); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void nexthop_free_mpath(struct nexthop *nh) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct nh_group *nhg; 758c2ecf20Sopenharmony_ci int i; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci nhg = rcu_dereference_raw(nh->nh_grp); 788c2ecf20Sopenharmony_ci for (i = 0; i < nhg->num_nh; ++i) { 798c2ecf20Sopenharmony_ci struct nh_grp_entry *nhge = &nhg->nh_entries[i]; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&nhge->nh_list)); 828c2ecf20Sopenharmony_ci nexthop_put(nhge->nh); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci WARN_ON(nhg->spare == nhg); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci kfree(nhg->spare); 888c2ecf20Sopenharmony_ci kfree(nhg); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic void nexthop_free_single(struct nexthop *nh) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct nh_info *nhi; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci nhi = rcu_dereference_raw(nh->nh_info); 968c2ecf20Sopenharmony_ci switch (nhi->family) { 978c2ecf20Sopenharmony_ci case AF_INET: 988c2ecf20Sopenharmony_ci fib_nh_release(nh->net, &nhi->fib_nh); 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci case AF_INET6: 1018c2ecf20Sopenharmony_ci ipv6_stub->fib6_nh_release(&nhi->fib6_nh); 1028c2ecf20Sopenharmony_ci break; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci kfree(nhi); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_civoid nexthop_free_rcu(struct rcu_head *head) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct nexthop *nh = container_of(head, struct nexthop, rcu); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (nh->is_group) 1128c2ecf20Sopenharmony_ci nexthop_free_mpath(nh); 1138c2ecf20Sopenharmony_ci else 1148c2ecf20Sopenharmony_ci nexthop_free_single(nh); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci kfree(nh); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nexthop_free_rcu); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic struct nexthop *nexthop_alloc(void) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct nexthop *nh; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci nh = kzalloc(sizeof(struct nexthop), GFP_KERNEL); 1258c2ecf20Sopenharmony_ci if (nh) { 1268c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&nh->fi_list); 1278c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&nh->f6i_list); 1288c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&nh->grp_list); 1298c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&nh->fdb_list); 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci return nh; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic struct nh_group *nexthop_grp_alloc(u16 num_nh) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct nh_group *nhg; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci nhg = kzalloc(struct_size(nhg, nh_entries, num_nh), GFP_KERNEL); 1398c2ecf20Sopenharmony_ci if (nhg) 1408c2ecf20Sopenharmony_ci nhg->num_nh = num_nh; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return nhg; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic void nh_base_seq_inc(struct net *net) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci while (++net->nexthop.seq == 0) 1488c2ecf20Sopenharmony_ci ; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* no reference taken; rcu lock or rtnl must be held */ 1528c2ecf20Sopenharmony_cistruct nexthop *nexthop_find_by_id(struct net *net, u32 id) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct rb_node **pp, *parent = NULL, *next; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci pp = &net->nexthop.rb_root.rb_node; 1578c2ecf20Sopenharmony_ci while (1) { 1588c2ecf20Sopenharmony_ci struct nexthop *nh; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci next = rcu_dereference_raw(*pp); 1618c2ecf20Sopenharmony_ci if (!next) 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci parent = next; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci nh = rb_entry(parent, struct nexthop, rb_node); 1668c2ecf20Sopenharmony_ci if (id < nh->id) 1678c2ecf20Sopenharmony_ci pp = &next->rb_left; 1688c2ecf20Sopenharmony_ci else if (id > nh->id) 1698c2ecf20Sopenharmony_ci pp = &next->rb_right; 1708c2ecf20Sopenharmony_ci else 1718c2ecf20Sopenharmony_ci return nh; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci return NULL; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nexthop_find_by_id); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/* used for auto id allocation; called with rtnl held */ 1788c2ecf20Sopenharmony_cistatic u32 nh_find_unused_id(struct net *net) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci u32 id_start = net->nexthop.last_id_allocated; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci while (1) { 1838c2ecf20Sopenharmony_ci net->nexthop.last_id_allocated++; 1848c2ecf20Sopenharmony_ci if (net->nexthop.last_id_allocated == id_start) 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (!nexthop_find_by_id(net, net->nexthop.last_id_allocated)) 1888c2ecf20Sopenharmony_ci return net->nexthop.last_id_allocated; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci return 0; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int nla_put_nh_group(struct sk_buff *skb, struct nh_group *nhg) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct nexthop_grp *p; 1968c2ecf20Sopenharmony_ci size_t len = nhg->num_nh * sizeof(*p); 1978c2ecf20Sopenharmony_ci struct nlattr *nla; 1988c2ecf20Sopenharmony_ci u16 group_type = 0; 1998c2ecf20Sopenharmony_ci int i; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (nhg->mpath) 2028c2ecf20Sopenharmony_ci group_type = NEXTHOP_GRP_TYPE_MPATH; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (nla_put_u16(skb, NHA_GROUP_TYPE, group_type)) 2058c2ecf20Sopenharmony_ci goto nla_put_failure; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci nla = nla_reserve(skb, NHA_GROUP, len); 2088c2ecf20Sopenharmony_ci if (!nla) 2098c2ecf20Sopenharmony_ci goto nla_put_failure; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci p = nla_data(nla); 2128c2ecf20Sopenharmony_ci for (i = 0; i < nhg->num_nh; ++i) { 2138c2ecf20Sopenharmony_ci *p++ = (struct nexthop_grp) { 2148c2ecf20Sopenharmony_ci .id = nhg->nh_entries[i].nh->id, 2158c2ecf20Sopenharmony_ci .weight = nhg->nh_entries[i].weight - 1, 2168c2ecf20Sopenharmony_ci }; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cinla_put_failure: 2228c2ecf20Sopenharmony_ci return -EMSGSIZE; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int nh_fill_node(struct sk_buff *skb, struct nexthop *nh, 2268c2ecf20Sopenharmony_ci int event, u32 portid, u32 seq, unsigned int nlflags) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct fib6_nh *fib6_nh; 2298c2ecf20Sopenharmony_ci struct fib_nh *fib_nh; 2308c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 2318c2ecf20Sopenharmony_ci struct nh_info *nhi; 2328c2ecf20Sopenharmony_ci struct nhmsg *nhm; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nhm), nlflags); 2358c2ecf20Sopenharmony_ci if (!nlh) 2368c2ecf20Sopenharmony_ci return -EMSGSIZE; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci nhm = nlmsg_data(nlh); 2398c2ecf20Sopenharmony_ci nhm->nh_family = AF_UNSPEC; 2408c2ecf20Sopenharmony_ci nhm->nh_flags = nh->nh_flags; 2418c2ecf20Sopenharmony_ci nhm->nh_protocol = nh->protocol; 2428c2ecf20Sopenharmony_ci nhm->nh_scope = 0; 2438c2ecf20Sopenharmony_ci nhm->resvd = 0; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (nla_put_u32(skb, NHA_ID, nh->id)) 2468c2ecf20Sopenharmony_ci goto nla_put_failure; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (nh->is_group) { 2498c2ecf20Sopenharmony_ci struct nh_group *nhg = rtnl_dereference(nh->nh_grp); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (nhg->fdb_nh && nla_put_flag(skb, NHA_FDB)) 2528c2ecf20Sopenharmony_ci goto nla_put_failure; 2538c2ecf20Sopenharmony_ci if (nla_put_nh_group(skb, nhg)) 2548c2ecf20Sopenharmony_ci goto nla_put_failure; 2558c2ecf20Sopenharmony_ci goto out; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci nhi = rtnl_dereference(nh->nh_info); 2598c2ecf20Sopenharmony_ci nhm->nh_family = nhi->family; 2608c2ecf20Sopenharmony_ci if (nhi->reject_nh) { 2618c2ecf20Sopenharmony_ci if (nla_put_flag(skb, NHA_BLACKHOLE)) 2628c2ecf20Sopenharmony_ci goto nla_put_failure; 2638c2ecf20Sopenharmony_ci goto out; 2648c2ecf20Sopenharmony_ci } else if (nhi->fdb_nh) { 2658c2ecf20Sopenharmony_ci if (nla_put_flag(skb, NHA_FDB)) 2668c2ecf20Sopenharmony_ci goto nla_put_failure; 2678c2ecf20Sopenharmony_ci } else { 2688c2ecf20Sopenharmony_ci const struct net_device *dev; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci dev = nhi->fib_nhc.nhc_dev; 2718c2ecf20Sopenharmony_ci if (dev && nla_put_u32(skb, NHA_OIF, dev->ifindex)) 2728c2ecf20Sopenharmony_ci goto nla_put_failure; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci nhm->nh_scope = nhi->fib_nhc.nhc_scope; 2768c2ecf20Sopenharmony_ci switch (nhi->family) { 2778c2ecf20Sopenharmony_ci case AF_INET: 2788c2ecf20Sopenharmony_ci fib_nh = &nhi->fib_nh; 2798c2ecf20Sopenharmony_ci if (fib_nh->fib_nh_gw_family && 2808c2ecf20Sopenharmony_ci nla_put_be32(skb, NHA_GATEWAY, fib_nh->fib_nh_gw4)) 2818c2ecf20Sopenharmony_ci goto nla_put_failure; 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci case AF_INET6: 2858c2ecf20Sopenharmony_ci fib6_nh = &nhi->fib6_nh; 2868c2ecf20Sopenharmony_ci if (fib6_nh->fib_nh_gw_family && 2878c2ecf20Sopenharmony_ci nla_put_in6_addr(skb, NHA_GATEWAY, &fib6_nh->fib_nh_gw6)) 2888c2ecf20Sopenharmony_ci goto nla_put_failure; 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (nhi->fib_nhc.nhc_lwtstate && 2938c2ecf20Sopenharmony_ci lwtunnel_fill_encap(skb, nhi->fib_nhc.nhc_lwtstate, 2948c2ecf20Sopenharmony_ci NHA_ENCAP, NHA_ENCAP_TYPE) < 0) 2958c2ecf20Sopenharmony_ci goto nla_put_failure; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ciout: 2988c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 2998c2ecf20Sopenharmony_ci return 0; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cinla_put_failure: 3028c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 3038c2ecf20Sopenharmony_ci return -EMSGSIZE; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic size_t nh_nlmsg_size_grp(struct nexthop *nh) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct nh_group *nhg = rtnl_dereference(nh->nh_grp); 3098c2ecf20Sopenharmony_ci size_t sz = sizeof(struct nexthop_grp) * nhg->num_nh; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return nla_total_size(sz) + 3128c2ecf20Sopenharmony_ci nla_total_size(2); /* NHA_GROUP_TYPE */ 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic size_t nh_nlmsg_size_single(struct nexthop *nh) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct nh_info *nhi = rtnl_dereference(nh->nh_info); 3188c2ecf20Sopenharmony_ci size_t sz; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* covers NHA_BLACKHOLE since NHA_OIF and BLACKHOLE 3218c2ecf20Sopenharmony_ci * are mutually exclusive 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_ci sz = nla_total_size(4); /* NHA_OIF */ 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci switch (nhi->family) { 3268c2ecf20Sopenharmony_ci case AF_INET: 3278c2ecf20Sopenharmony_ci if (nhi->fib_nh.fib_nh_gw_family) 3288c2ecf20Sopenharmony_ci sz += nla_total_size(4); /* NHA_GATEWAY */ 3298c2ecf20Sopenharmony_ci break; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci case AF_INET6: 3328c2ecf20Sopenharmony_ci /* NHA_GATEWAY */ 3338c2ecf20Sopenharmony_ci if (nhi->fib6_nh.fib_nh_gw_family) 3348c2ecf20Sopenharmony_ci sz += nla_total_size(sizeof(const struct in6_addr)); 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (nhi->fib_nhc.nhc_lwtstate) { 3398c2ecf20Sopenharmony_ci sz += lwtunnel_get_encap_size(nhi->fib_nhc.nhc_lwtstate); 3408c2ecf20Sopenharmony_ci sz += nla_total_size(2); /* NHA_ENCAP_TYPE */ 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return sz; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic size_t nh_nlmsg_size(struct nexthop *nh) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci size_t sz = NLMSG_ALIGN(sizeof(struct nhmsg)); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci sz += nla_total_size(4); /* NHA_ID */ 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (nh->is_group) 3538c2ecf20Sopenharmony_ci sz += nh_nlmsg_size_grp(nh); 3548c2ecf20Sopenharmony_ci else 3558c2ecf20Sopenharmony_ci sz += nh_nlmsg_size_single(nh); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return sz; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic void nexthop_notify(int event, struct nexthop *nh, struct nl_info *info) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci unsigned int nlflags = info->nlh ? info->nlh->nlmsg_flags : 0; 3638c2ecf20Sopenharmony_ci u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; 3648c2ecf20Sopenharmony_ci struct sk_buff *skb; 3658c2ecf20Sopenharmony_ci int err = -ENOBUFS; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci skb = nlmsg_new(nh_nlmsg_size(nh), gfp_any()); 3688c2ecf20Sopenharmony_ci if (!skb) 3698c2ecf20Sopenharmony_ci goto errout; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci err = nh_fill_node(skb, nh, event, info->portid, seq, nlflags); 3728c2ecf20Sopenharmony_ci if (err < 0) { 3738c2ecf20Sopenharmony_ci /* -EMSGSIZE implies BUG in nh_nlmsg_size() */ 3748c2ecf20Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 3758c2ecf20Sopenharmony_ci kfree_skb(skb); 3768c2ecf20Sopenharmony_ci goto errout; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci rtnl_notify(skb, info->nl_net, info->portid, RTNLGRP_NEXTHOP, 3808c2ecf20Sopenharmony_ci info->nlh, gfp_any()); 3818c2ecf20Sopenharmony_ci return; 3828c2ecf20Sopenharmony_cierrout: 3838c2ecf20Sopenharmony_ci if (err < 0) 3848c2ecf20Sopenharmony_ci rtnl_set_sk_err(info->nl_net, RTNLGRP_NEXTHOP, err); 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic bool valid_group_nh(struct nexthop *nh, unsigned int npaths, 3888c2ecf20Sopenharmony_ci bool *is_fdb, struct netlink_ext_ack *extack) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci if (nh->is_group) { 3918c2ecf20Sopenharmony_ci struct nh_group *nhg = rtnl_dereference(nh->nh_grp); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* nested multipath (group within a group) is not 3948c2ecf20Sopenharmony_ci * supported 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ci if (nhg->mpath) { 3978c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 3988c2ecf20Sopenharmony_ci "Multipath group can not be a nexthop within a group"); 3998c2ecf20Sopenharmony_ci return false; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci *is_fdb = nhg->fdb_nh; 4028c2ecf20Sopenharmony_ci } else { 4038c2ecf20Sopenharmony_ci struct nh_info *nhi = rtnl_dereference(nh->nh_info); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (nhi->reject_nh && npaths > 1) { 4068c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 4078c2ecf20Sopenharmony_ci "Blackhole nexthop can not be used in a group with more than 1 path"); 4088c2ecf20Sopenharmony_ci return false; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci *is_fdb = nhi->fdb_nh; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci return true; 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic int nh_check_attr_fdb_group(struct nexthop *nh, u8 *nh_family, 4178c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct nh_info *nhi; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci nhi = rtnl_dereference(nh->nh_info); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (!nhi->fdb_nh) { 4248c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "FDB nexthop group can only have fdb nexthops"); 4258c2ecf20Sopenharmony_ci return -EINVAL; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (*nh_family == AF_UNSPEC) { 4298c2ecf20Sopenharmony_ci *nh_family = nhi->family; 4308c2ecf20Sopenharmony_ci } else if (*nh_family != nhi->family) { 4318c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "FDB nexthop group cannot have mixed family nexthops"); 4328c2ecf20Sopenharmony_ci return -EINVAL; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return 0; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic int nh_check_attr_group(struct net *net, struct nlattr *tb[], 4398c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci unsigned int len = nla_len(tb[NHA_GROUP]); 4428c2ecf20Sopenharmony_ci u8 nh_family = AF_UNSPEC; 4438c2ecf20Sopenharmony_ci struct nexthop_grp *nhg; 4448c2ecf20Sopenharmony_ci unsigned int i, j; 4458c2ecf20Sopenharmony_ci u8 nhg_fdb = 0; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (!len || len & (sizeof(struct nexthop_grp) - 1)) { 4488c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 4498c2ecf20Sopenharmony_ci "Invalid length for nexthop group attribute"); 4508c2ecf20Sopenharmony_ci return -EINVAL; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* convert len to number of nexthop ids */ 4548c2ecf20Sopenharmony_ci len /= sizeof(*nhg); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci nhg = nla_data(tb[NHA_GROUP]); 4578c2ecf20Sopenharmony_ci for (i = 0; i < len; ++i) { 4588c2ecf20Sopenharmony_ci if (nhg[i].resvd1 || nhg[i].resvd2) { 4598c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Reserved fields in nexthop_grp must be 0"); 4608c2ecf20Sopenharmony_ci return -EINVAL; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci if (nhg[i].weight > 254) { 4638c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid value for weight"); 4648c2ecf20Sopenharmony_ci return -EINVAL; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci for (j = i + 1; j < len; ++j) { 4678c2ecf20Sopenharmony_ci if (nhg[i].id == nhg[j].id) { 4688c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Nexthop id can not be used twice in a group"); 4698c2ecf20Sopenharmony_ci return -EINVAL; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (tb[NHA_FDB]) 4758c2ecf20Sopenharmony_ci nhg_fdb = 1; 4768c2ecf20Sopenharmony_ci nhg = nla_data(tb[NHA_GROUP]); 4778c2ecf20Sopenharmony_ci for (i = 0; i < len; ++i) { 4788c2ecf20Sopenharmony_ci struct nexthop *nh; 4798c2ecf20Sopenharmony_ci bool is_fdb_nh; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci nh = nexthop_find_by_id(net, nhg[i].id); 4828c2ecf20Sopenharmony_ci if (!nh) { 4838c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid nexthop id"); 4848c2ecf20Sopenharmony_ci return -EINVAL; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci if (!valid_group_nh(nh, len, &is_fdb_nh, extack)) 4878c2ecf20Sopenharmony_ci return -EINVAL; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (nhg_fdb && nh_check_attr_fdb_group(nh, &nh_family, extack)) 4908c2ecf20Sopenharmony_ci return -EINVAL; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (!nhg_fdb && is_fdb_nh) { 4938c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Non FDB nexthop group cannot have fdb nexthops"); 4948c2ecf20Sopenharmony_ci return -EINVAL; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci for (i = NHA_GROUP_TYPE + 1; i < __NHA_MAX; ++i) { 4988c2ecf20Sopenharmony_ci if (!tb[i]) 4998c2ecf20Sopenharmony_ci continue; 5008c2ecf20Sopenharmony_ci if (i == NHA_FDB) 5018c2ecf20Sopenharmony_ci continue; 5028c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 5038c2ecf20Sopenharmony_ci "No other attributes can be set in nexthop groups"); 5048c2ecf20Sopenharmony_ci return -EINVAL; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci return 0; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic bool ipv6_good_nh(const struct fib6_nh *nh) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci int state = NUD_REACHABLE; 5138c2ecf20Sopenharmony_ci struct neighbour *n; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci rcu_read_lock_bh(); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci n = __ipv6_neigh_lookup_noref_stub(nh->fib_nh_dev, &nh->fib_nh_gw6); 5188c2ecf20Sopenharmony_ci if (n) 5198c2ecf20Sopenharmony_ci state = n->nud_state; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci rcu_read_unlock_bh(); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci return !!(state & NUD_VALID); 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic bool ipv4_good_nh(const struct fib_nh *nh) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci int state = NUD_REACHABLE; 5298c2ecf20Sopenharmony_ci struct neighbour *n; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci rcu_read_lock_bh(); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci n = __ipv4_neigh_lookup_noref(nh->fib_nh_dev, 5348c2ecf20Sopenharmony_ci (__force u32)nh->fib_nh_gw4); 5358c2ecf20Sopenharmony_ci if (n) 5368c2ecf20Sopenharmony_ci state = n->nud_state; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci rcu_read_unlock_bh(); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci return !!(state & NUD_VALID); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistruct nexthop *nexthop_select_path(struct nexthop *nh, int hash) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci struct nexthop *rc = NULL; 5468c2ecf20Sopenharmony_ci struct nh_group *nhg; 5478c2ecf20Sopenharmony_ci int i; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (!nh->is_group) 5508c2ecf20Sopenharmony_ci return nh; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci nhg = rcu_dereference(nh->nh_grp); 5538c2ecf20Sopenharmony_ci for (i = 0; i < nhg->num_nh; ++i) { 5548c2ecf20Sopenharmony_ci struct nh_grp_entry *nhge = &nhg->nh_entries[i]; 5558c2ecf20Sopenharmony_ci struct nh_info *nhi; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (hash > atomic_read(&nhge->upper_bound)) 5588c2ecf20Sopenharmony_ci continue; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci nhi = rcu_dereference(nhge->nh->nh_info); 5618c2ecf20Sopenharmony_ci if (nhi->fdb_nh) 5628c2ecf20Sopenharmony_ci return nhge->nh; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci /* nexthops always check if it is good and does 5658c2ecf20Sopenharmony_ci * not rely on a sysctl for this behavior 5668c2ecf20Sopenharmony_ci */ 5678c2ecf20Sopenharmony_ci switch (nhi->family) { 5688c2ecf20Sopenharmony_ci case AF_INET: 5698c2ecf20Sopenharmony_ci if (ipv4_good_nh(&nhi->fib_nh)) 5708c2ecf20Sopenharmony_ci return nhge->nh; 5718c2ecf20Sopenharmony_ci break; 5728c2ecf20Sopenharmony_ci case AF_INET6: 5738c2ecf20Sopenharmony_ci if (ipv6_good_nh(&nhi->fib6_nh)) 5748c2ecf20Sopenharmony_ci return nhge->nh; 5758c2ecf20Sopenharmony_ci break; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (!rc) 5798c2ecf20Sopenharmony_ci rc = nhge->nh; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return rc; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nexthop_select_path); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ciint nexthop_for_each_fib6_nh(struct nexthop *nh, 5878c2ecf20Sopenharmony_ci int (*cb)(struct fib6_nh *nh, void *arg), 5888c2ecf20Sopenharmony_ci void *arg) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct nh_info *nhi; 5918c2ecf20Sopenharmony_ci int err; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci if (nh->is_group) { 5948c2ecf20Sopenharmony_ci struct nh_group *nhg; 5958c2ecf20Sopenharmony_ci int i; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci nhg = rcu_dereference_rtnl(nh->nh_grp); 5988c2ecf20Sopenharmony_ci for (i = 0; i < nhg->num_nh; i++) { 5998c2ecf20Sopenharmony_ci struct nh_grp_entry *nhge = &nhg->nh_entries[i]; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci nhi = rcu_dereference_rtnl(nhge->nh->nh_info); 6028c2ecf20Sopenharmony_ci err = cb(&nhi->fib6_nh, arg); 6038c2ecf20Sopenharmony_ci if (err) 6048c2ecf20Sopenharmony_ci return err; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci } else { 6078c2ecf20Sopenharmony_ci nhi = rcu_dereference_rtnl(nh->nh_info); 6088c2ecf20Sopenharmony_ci err = cb(&nhi->fib6_nh, arg); 6098c2ecf20Sopenharmony_ci if (err) 6108c2ecf20Sopenharmony_ci return err; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci return 0; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nexthop_for_each_fib6_nh); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic int check_src_addr(const struct in6_addr *saddr, 6188c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci if (!ipv6_addr_any(saddr)) { 6218c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "IPv6 routes using source address can not use nexthop objects"); 6228c2ecf20Sopenharmony_ci return -EINVAL; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci return 0; 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ciint fib6_check_nexthop(struct nexthop *nh, struct fib6_config *cfg, 6288c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci struct nh_info *nhi; 6318c2ecf20Sopenharmony_ci bool is_fdb_nh; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci /* fib6_src is unique to a fib6_info and limits the ability to cache 6348c2ecf20Sopenharmony_ci * routes in fib6_nh within a nexthop that is potentially shared 6358c2ecf20Sopenharmony_ci * across multiple fib entries. If the config wants to use source 6368c2ecf20Sopenharmony_ci * routing it can not use nexthop objects. mlxsw also does not allow 6378c2ecf20Sopenharmony_ci * fib6_src on routes. 6388c2ecf20Sopenharmony_ci */ 6398c2ecf20Sopenharmony_ci if (cfg && check_src_addr(&cfg->fc_src, extack) < 0) 6408c2ecf20Sopenharmony_ci return -EINVAL; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (nh->is_group) { 6438c2ecf20Sopenharmony_ci struct nh_group *nhg; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci nhg = rtnl_dereference(nh->nh_grp); 6468c2ecf20Sopenharmony_ci if (nhg->has_v4) 6478c2ecf20Sopenharmony_ci goto no_v4_nh; 6488c2ecf20Sopenharmony_ci is_fdb_nh = nhg->fdb_nh; 6498c2ecf20Sopenharmony_ci } else { 6508c2ecf20Sopenharmony_ci nhi = rtnl_dereference(nh->nh_info); 6518c2ecf20Sopenharmony_ci if (nhi->family == AF_INET) 6528c2ecf20Sopenharmony_ci goto no_v4_nh; 6538c2ecf20Sopenharmony_ci is_fdb_nh = nhi->fdb_nh; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (is_fdb_nh) { 6578c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Route cannot point to a fdb nexthop"); 6588c2ecf20Sopenharmony_ci return -EINVAL; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci return 0; 6628c2ecf20Sopenharmony_cino_v4_nh: 6638c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "IPv6 routes can not use an IPv4 nexthop"); 6648c2ecf20Sopenharmony_ci return -EINVAL; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib6_check_nexthop); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci/* if existing nexthop has ipv6 routes linked to it, need 6698c2ecf20Sopenharmony_ci * to verify this new spec works with ipv6 6708c2ecf20Sopenharmony_ci */ 6718c2ecf20Sopenharmony_cistatic int fib6_check_nh_list(struct nexthop *old, struct nexthop *new, 6728c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct fib6_info *f6i; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (list_empty(&old->f6i_list)) 6778c2ecf20Sopenharmony_ci return 0; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci list_for_each_entry(f6i, &old->f6i_list, nh_list) { 6808c2ecf20Sopenharmony_ci if (check_src_addr(&f6i->fib6_src.addr, extack) < 0) 6818c2ecf20Sopenharmony_ci return -EINVAL; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci return fib6_check_nexthop(new, NULL, extack); 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_cistatic int nexthop_check_scope(struct nh_info *nhi, u8 scope, 6888c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 6898c2ecf20Sopenharmony_ci{ 6908c2ecf20Sopenharmony_ci if (scope == RT_SCOPE_HOST && nhi->fib_nhc.nhc_gw_family) { 6918c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 6928c2ecf20Sopenharmony_ci "Route with host scope can not have a gateway"); 6938c2ecf20Sopenharmony_ci return -EINVAL; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci if (nhi->fib_nhc.nhc_flags & RTNH_F_ONLINK && scope >= RT_SCOPE_LINK) { 6978c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Scope mismatch with nexthop"); 6988c2ecf20Sopenharmony_ci return -EINVAL; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci return 0; 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci/* Invoked by fib add code to verify nexthop by id is ok with 7058c2ecf20Sopenharmony_ci * config for prefix; parts of fib_check_nh not done when nexthop 7068c2ecf20Sopenharmony_ci * object is used. 7078c2ecf20Sopenharmony_ci */ 7088c2ecf20Sopenharmony_ciint fib_check_nexthop(struct nexthop *nh, u8 scope, 7098c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 7108c2ecf20Sopenharmony_ci{ 7118c2ecf20Sopenharmony_ci struct nh_info *nhi; 7128c2ecf20Sopenharmony_ci int err = 0; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci if (nh->is_group) { 7158c2ecf20Sopenharmony_ci struct nh_group *nhg; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci nhg = rtnl_dereference(nh->nh_grp); 7188c2ecf20Sopenharmony_ci if (nhg->fdb_nh) { 7198c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Route cannot point to a fdb nexthop"); 7208c2ecf20Sopenharmony_ci err = -EINVAL; 7218c2ecf20Sopenharmony_ci goto out; 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if (scope == RT_SCOPE_HOST) { 7258c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Route with host scope can not have multiple nexthops"); 7268c2ecf20Sopenharmony_ci err = -EINVAL; 7278c2ecf20Sopenharmony_ci goto out; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* all nexthops in a group have the same scope */ 7318c2ecf20Sopenharmony_ci nhi = rtnl_dereference(nhg->nh_entries[0].nh->nh_info); 7328c2ecf20Sopenharmony_ci err = nexthop_check_scope(nhi, scope, extack); 7338c2ecf20Sopenharmony_ci } else { 7348c2ecf20Sopenharmony_ci nhi = rtnl_dereference(nh->nh_info); 7358c2ecf20Sopenharmony_ci if (nhi->fdb_nh) { 7368c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Route cannot point to a fdb nexthop"); 7378c2ecf20Sopenharmony_ci err = -EINVAL; 7388c2ecf20Sopenharmony_ci goto out; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci err = nexthop_check_scope(nhi, scope, extack); 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ciout: 7448c2ecf20Sopenharmony_ci return err; 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic int fib_check_nh_list(struct nexthop *old, struct nexthop *new, 7488c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci struct fib_info *fi; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci list_for_each_entry(fi, &old->fi_list, nh_list) { 7538c2ecf20Sopenharmony_ci int err; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci err = fib_check_nexthop(new, fi->fib_scope, extack); 7568c2ecf20Sopenharmony_ci if (err) 7578c2ecf20Sopenharmony_ci return err; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci return 0; 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic void nh_group_rebalance(struct nh_group *nhg) 7638c2ecf20Sopenharmony_ci{ 7648c2ecf20Sopenharmony_ci int total = 0; 7658c2ecf20Sopenharmony_ci int w = 0; 7668c2ecf20Sopenharmony_ci int i; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci for (i = 0; i < nhg->num_nh; ++i) 7698c2ecf20Sopenharmony_ci total += nhg->nh_entries[i].weight; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci for (i = 0; i < nhg->num_nh; ++i) { 7728c2ecf20Sopenharmony_ci struct nh_grp_entry *nhge = &nhg->nh_entries[i]; 7738c2ecf20Sopenharmony_ci int upper_bound; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci w += nhge->weight; 7768c2ecf20Sopenharmony_ci upper_bound = DIV_ROUND_CLOSEST_ULL((u64)w << 31, total) - 1; 7778c2ecf20Sopenharmony_ci atomic_set(&nhge->upper_bound, upper_bound); 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic void remove_nh_grp_entry(struct net *net, struct nh_grp_entry *nhge, 7828c2ecf20Sopenharmony_ci struct nl_info *nlinfo) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci struct nh_grp_entry *nhges, *new_nhges; 7858c2ecf20Sopenharmony_ci struct nexthop *nhp = nhge->nh_parent; 7868c2ecf20Sopenharmony_ci struct nexthop *nh = nhge->nh; 7878c2ecf20Sopenharmony_ci struct nh_group *nhg, *newg; 7888c2ecf20Sopenharmony_ci int i, j; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci WARN_ON(!nh); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci nhg = rtnl_dereference(nhp->nh_grp); 7938c2ecf20Sopenharmony_ci newg = nhg->spare; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci /* last entry, keep it visible and remove the parent */ 7968c2ecf20Sopenharmony_ci if (nhg->num_nh == 1) { 7978c2ecf20Sopenharmony_ci remove_nexthop(net, nhp, nlinfo); 7988c2ecf20Sopenharmony_ci return; 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci newg->has_v4 = false; 8028c2ecf20Sopenharmony_ci newg->mpath = nhg->mpath; 8038c2ecf20Sopenharmony_ci newg->fdb_nh = nhg->fdb_nh; 8048c2ecf20Sopenharmony_ci newg->num_nh = nhg->num_nh; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci /* copy old entries to new except the one getting removed */ 8078c2ecf20Sopenharmony_ci nhges = nhg->nh_entries; 8088c2ecf20Sopenharmony_ci new_nhges = newg->nh_entries; 8098c2ecf20Sopenharmony_ci for (i = 0, j = 0; i < nhg->num_nh; ++i) { 8108c2ecf20Sopenharmony_ci struct nh_info *nhi; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci /* current nexthop getting removed */ 8138c2ecf20Sopenharmony_ci if (nhg->nh_entries[i].nh == nh) { 8148c2ecf20Sopenharmony_ci newg->num_nh--; 8158c2ecf20Sopenharmony_ci continue; 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci nhi = rtnl_dereference(nhges[i].nh->nh_info); 8198c2ecf20Sopenharmony_ci if (nhi->family == AF_INET) 8208c2ecf20Sopenharmony_ci newg->has_v4 = true; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci list_del(&nhges[i].nh_list); 8238c2ecf20Sopenharmony_ci new_nhges[j].nh_parent = nhges[i].nh_parent; 8248c2ecf20Sopenharmony_ci new_nhges[j].nh = nhges[i].nh; 8258c2ecf20Sopenharmony_ci new_nhges[j].weight = nhges[i].weight; 8268c2ecf20Sopenharmony_ci list_add(&new_nhges[j].nh_list, &new_nhges[j].nh->grp_list); 8278c2ecf20Sopenharmony_ci j++; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci nh_group_rebalance(newg); 8318c2ecf20Sopenharmony_ci rcu_assign_pointer(nhp->nh_grp, newg); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci list_del(&nhge->nh_list); 8348c2ecf20Sopenharmony_ci nexthop_put(nhge->nh); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci if (nlinfo) 8378c2ecf20Sopenharmony_ci nexthop_notify(RTM_NEWNEXTHOP, nhp, nlinfo); 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cistatic void remove_nexthop_from_groups(struct net *net, struct nexthop *nh, 8418c2ecf20Sopenharmony_ci struct nl_info *nlinfo) 8428c2ecf20Sopenharmony_ci{ 8438c2ecf20Sopenharmony_ci struct nh_grp_entry *nhge, *tmp; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci list_for_each_entry_safe(nhge, tmp, &nh->grp_list, nh_list) 8468c2ecf20Sopenharmony_ci remove_nh_grp_entry(net, nhge, nlinfo); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* make sure all see the newly published array before releasing rtnl */ 8498c2ecf20Sopenharmony_ci synchronize_net(); 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_cistatic void remove_nexthop_group(struct nexthop *nh, struct nl_info *nlinfo) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci struct nh_group *nhg = rcu_dereference_rtnl(nh->nh_grp); 8558c2ecf20Sopenharmony_ci int i, num_nh = nhg->num_nh; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci for (i = 0; i < num_nh; ++i) { 8588c2ecf20Sopenharmony_ci struct nh_grp_entry *nhge = &nhg->nh_entries[i]; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci if (WARN_ON(!nhge->nh)) 8618c2ecf20Sopenharmony_ci continue; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci list_del_init(&nhge->nh_list); 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci/* not called for nexthop replace */ 8688c2ecf20Sopenharmony_cistatic void __remove_nexthop_fib(struct net *net, struct nexthop *nh) 8698c2ecf20Sopenharmony_ci{ 8708c2ecf20Sopenharmony_ci struct fib6_info *f6i, *tmp; 8718c2ecf20Sopenharmony_ci bool do_flush = false; 8728c2ecf20Sopenharmony_ci struct fib_info *fi; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci list_for_each_entry(fi, &nh->fi_list, nh_list) { 8758c2ecf20Sopenharmony_ci fi->fib_flags |= RTNH_F_DEAD; 8768c2ecf20Sopenharmony_ci do_flush = true; 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci if (do_flush) 8798c2ecf20Sopenharmony_ci fib_flush(net); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci /* ip6_del_rt removes the entry from this list hence the _safe */ 8828c2ecf20Sopenharmony_ci list_for_each_entry_safe(f6i, tmp, &nh->f6i_list, nh_list) { 8838c2ecf20Sopenharmony_ci /* __ip6_del_rt does a release, so do a hold here */ 8848c2ecf20Sopenharmony_ci fib6_info_hold(f6i); 8858c2ecf20Sopenharmony_ci ipv6_stub->ip6_del_rt(net, f6i, 8868c2ecf20Sopenharmony_ci !READ_ONCE(net->ipv4.sysctl_nexthop_compat_mode)); 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic void __remove_nexthop(struct net *net, struct nexthop *nh, 8918c2ecf20Sopenharmony_ci struct nl_info *nlinfo) 8928c2ecf20Sopenharmony_ci{ 8938c2ecf20Sopenharmony_ci __remove_nexthop_fib(net, nh); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci if (nh->is_group) { 8968c2ecf20Sopenharmony_ci remove_nexthop_group(nh, nlinfo); 8978c2ecf20Sopenharmony_ci } else { 8988c2ecf20Sopenharmony_ci struct nh_info *nhi; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci nhi = rtnl_dereference(nh->nh_info); 9018c2ecf20Sopenharmony_ci if (nhi->fib_nhc.nhc_dev) 9028c2ecf20Sopenharmony_ci hlist_del(&nhi->dev_hash); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci remove_nexthop_from_groups(net, nh, nlinfo); 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic void remove_nexthop(struct net *net, struct nexthop *nh, 9098c2ecf20Sopenharmony_ci struct nl_info *nlinfo) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci call_nexthop_notifiers(net, NEXTHOP_EVENT_DEL, nh); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci /* remove from the tree */ 9148c2ecf20Sopenharmony_ci rb_erase(&nh->rb_node, &net->nexthop.rb_root); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci if (nlinfo) 9178c2ecf20Sopenharmony_ci nexthop_notify(RTM_DELNEXTHOP, nh, nlinfo); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci __remove_nexthop(net, nh, nlinfo); 9208c2ecf20Sopenharmony_ci nh_base_seq_inc(net); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci nexthop_put(nh); 9238c2ecf20Sopenharmony_ci} 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci/* if any FIB entries reference this nexthop, any dst entries 9268c2ecf20Sopenharmony_ci * need to be regenerated 9278c2ecf20Sopenharmony_ci */ 9288c2ecf20Sopenharmony_cistatic void nh_rt_cache_flush(struct net *net, struct nexthop *nh, 9298c2ecf20Sopenharmony_ci struct nexthop *replaced_nh) 9308c2ecf20Sopenharmony_ci{ 9318c2ecf20Sopenharmony_ci struct fib6_info *f6i; 9328c2ecf20Sopenharmony_ci struct nh_group *nhg; 9338c2ecf20Sopenharmony_ci int i; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci if (!list_empty(&nh->fi_list)) 9368c2ecf20Sopenharmony_ci rt_cache_flush(net); 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci list_for_each_entry(f6i, &nh->f6i_list, nh_list) 9398c2ecf20Sopenharmony_ci ipv6_stub->fib6_update_sernum(net, f6i); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci /* if an IPv6 group was replaced, we have to release all old 9428c2ecf20Sopenharmony_ci * dsts to make sure all refcounts are released 9438c2ecf20Sopenharmony_ci */ 9448c2ecf20Sopenharmony_ci if (!replaced_nh->is_group) 9458c2ecf20Sopenharmony_ci return; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci /* new dsts must use only the new nexthop group */ 9488c2ecf20Sopenharmony_ci synchronize_net(); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci nhg = rtnl_dereference(replaced_nh->nh_grp); 9518c2ecf20Sopenharmony_ci for (i = 0; i < nhg->num_nh; i++) { 9528c2ecf20Sopenharmony_ci struct nh_grp_entry *nhge = &nhg->nh_entries[i]; 9538c2ecf20Sopenharmony_ci struct nh_info *nhi = rtnl_dereference(nhge->nh->nh_info); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci if (nhi->family == AF_INET6) 9568c2ecf20Sopenharmony_ci ipv6_stub->fib6_nh_release_dsts(&nhi->fib6_nh); 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci} 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_cistatic int replace_nexthop_grp(struct net *net, struct nexthop *old, 9618c2ecf20Sopenharmony_ci struct nexthop *new, 9628c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci struct nh_group *oldg, *newg; 9658c2ecf20Sopenharmony_ci int i; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci if (!new->is_group) { 9688c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Can not replace a nexthop group with a nexthop."); 9698c2ecf20Sopenharmony_ci return -EINVAL; 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci oldg = rtnl_dereference(old->nh_grp); 9738c2ecf20Sopenharmony_ci newg = rtnl_dereference(new->nh_grp); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci /* update parents - used by nexthop code for cleanup */ 9768c2ecf20Sopenharmony_ci for (i = 0; i < newg->num_nh; i++) 9778c2ecf20Sopenharmony_ci newg->nh_entries[i].nh_parent = old; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci rcu_assign_pointer(old->nh_grp, newg); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci for (i = 0; i < oldg->num_nh; i++) 9828c2ecf20Sopenharmony_ci oldg->nh_entries[i].nh_parent = new; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci rcu_assign_pointer(new->nh_grp, oldg); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci return 0; 9878c2ecf20Sopenharmony_ci} 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_cistatic void nh_group_v4_update(struct nh_group *nhg) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci struct nh_grp_entry *nhges; 9928c2ecf20Sopenharmony_ci bool has_v4 = false; 9938c2ecf20Sopenharmony_ci int i; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci nhges = nhg->nh_entries; 9968c2ecf20Sopenharmony_ci for (i = 0; i < nhg->num_nh; i++) { 9978c2ecf20Sopenharmony_ci struct nh_info *nhi; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci nhi = rtnl_dereference(nhges[i].nh->nh_info); 10008c2ecf20Sopenharmony_ci if (nhi->family == AF_INET) 10018c2ecf20Sopenharmony_ci has_v4 = true; 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci nhg->has_v4 = has_v4; 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_cistatic int replace_nexthop_single(struct net *net, struct nexthop *old, 10078c2ecf20Sopenharmony_ci struct nexthop *new, 10088c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 10098c2ecf20Sopenharmony_ci{ 10108c2ecf20Sopenharmony_ci struct nh_info *oldi, *newi; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci if (new->is_group) { 10138c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Can not replace a nexthop with a nexthop group."); 10148c2ecf20Sopenharmony_ci return -EINVAL; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci oldi = rtnl_dereference(old->nh_info); 10188c2ecf20Sopenharmony_ci newi = rtnl_dereference(new->nh_info); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci newi->nh_parent = old; 10218c2ecf20Sopenharmony_ci oldi->nh_parent = new; 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci old->protocol = new->protocol; 10248c2ecf20Sopenharmony_ci old->nh_flags = new->nh_flags; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci rcu_assign_pointer(old->nh_info, newi); 10278c2ecf20Sopenharmony_ci rcu_assign_pointer(new->nh_info, oldi); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci /* When replacing an IPv4 nexthop with an IPv6 nexthop, potentially 10308c2ecf20Sopenharmony_ci * update IPv4 indication in all the groups using the nexthop. 10318c2ecf20Sopenharmony_ci */ 10328c2ecf20Sopenharmony_ci if (oldi->family == AF_INET && newi->family == AF_INET6) { 10338c2ecf20Sopenharmony_ci struct nh_grp_entry *nhge; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci list_for_each_entry(nhge, &old->grp_list, nh_list) { 10368c2ecf20Sopenharmony_ci struct nexthop *nhp = nhge->nh_parent; 10378c2ecf20Sopenharmony_ci struct nh_group *nhg; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci nhg = rtnl_dereference(nhp->nh_grp); 10408c2ecf20Sopenharmony_ci nh_group_v4_update(nhg); 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci return 0; 10458c2ecf20Sopenharmony_ci} 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_cistatic void __nexthop_replace_notify(struct net *net, struct nexthop *nh, 10488c2ecf20Sopenharmony_ci struct nl_info *info) 10498c2ecf20Sopenharmony_ci{ 10508c2ecf20Sopenharmony_ci struct fib6_info *f6i; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (!list_empty(&nh->fi_list)) { 10538c2ecf20Sopenharmony_ci struct fib_info *fi; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci /* expectation is a few fib_info per nexthop and then 10568c2ecf20Sopenharmony_ci * a lot of routes per fib_info. So mark the fib_info 10578c2ecf20Sopenharmony_ci * and then walk the fib tables once 10588c2ecf20Sopenharmony_ci */ 10598c2ecf20Sopenharmony_ci list_for_each_entry(fi, &nh->fi_list, nh_list) 10608c2ecf20Sopenharmony_ci fi->nh_updated = true; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci fib_info_notify_update(net, info); 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci list_for_each_entry(fi, &nh->fi_list, nh_list) 10658c2ecf20Sopenharmony_ci fi->nh_updated = false; 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci list_for_each_entry(f6i, &nh->f6i_list, nh_list) 10698c2ecf20Sopenharmony_ci ipv6_stub->fib6_rt_update(net, f6i, info); 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci/* send RTM_NEWROUTE with REPLACE flag set for all FIB entries 10738c2ecf20Sopenharmony_ci * linked to this nexthop and for all groups that the nexthop 10748c2ecf20Sopenharmony_ci * is a member of 10758c2ecf20Sopenharmony_ci */ 10768c2ecf20Sopenharmony_cistatic void nexthop_replace_notify(struct net *net, struct nexthop *nh, 10778c2ecf20Sopenharmony_ci struct nl_info *info) 10788c2ecf20Sopenharmony_ci{ 10798c2ecf20Sopenharmony_ci struct nh_grp_entry *nhge; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci __nexthop_replace_notify(net, nh, info); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci list_for_each_entry(nhge, &nh->grp_list, nh_list) 10848c2ecf20Sopenharmony_ci __nexthop_replace_notify(net, nhge->nh_parent, info); 10858c2ecf20Sopenharmony_ci} 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_cistatic int replace_nexthop(struct net *net, struct nexthop *old, 10888c2ecf20Sopenharmony_ci struct nexthop *new, struct netlink_ext_ack *extack) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci bool new_is_reject = false; 10918c2ecf20Sopenharmony_ci struct nh_grp_entry *nhge; 10928c2ecf20Sopenharmony_ci int err; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci /* check that existing FIB entries are ok with the 10958c2ecf20Sopenharmony_ci * new nexthop definition 10968c2ecf20Sopenharmony_ci */ 10978c2ecf20Sopenharmony_ci err = fib_check_nh_list(old, new, extack); 10988c2ecf20Sopenharmony_ci if (err) 10998c2ecf20Sopenharmony_ci return err; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci err = fib6_check_nh_list(old, new, extack); 11028c2ecf20Sopenharmony_ci if (err) 11038c2ecf20Sopenharmony_ci return err; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci if (!new->is_group) { 11068c2ecf20Sopenharmony_ci struct nh_info *nhi = rtnl_dereference(new->nh_info); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci new_is_reject = nhi->reject_nh; 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci list_for_each_entry(nhge, &old->grp_list, nh_list) { 11128c2ecf20Sopenharmony_ci /* if new nexthop is a blackhole, any groups using this 11138c2ecf20Sopenharmony_ci * nexthop cannot have more than 1 path 11148c2ecf20Sopenharmony_ci */ 11158c2ecf20Sopenharmony_ci if (new_is_reject && 11168c2ecf20Sopenharmony_ci nexthop_num_path(nhge->nh_parent) > 1) { 11178c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Blackhole nexthop can not be a member of a group with more than one path"); 11188c2ecf20Sopenharmony_ci return -EINVAL; 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci err = fib_check_nh_list(nhge->nh_parent, new, extack); 11228c2ecf20Sopenharmony_ci if (err) 11238c2ecf20Sopenharmony_ci return err; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci err = fib6_check_nh_list(nhge->nh_parent, new, extack); 11268c2ecf20Sopenharmony_ci if (err) 11278c2ecf20Sopenharmony_ci return err; 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci if (old->is_group) 11318c2ecf20Sopenharmony_ci err = replace_nexthop_grp(net, old, new, extack); 11328c2ecf20Sopenharmony_ci else 11338c2ecf20Sopenharmony_ci err = replace_nexthop_single(net, old, new, extack); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (!err) { 11368c2ecf20Sopenharmony_ci nh_rt_cache_flush(net, old, new); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci __remove_nexthop(net, new, NULL); 11398c2ecf20Sopenharmony_ci nexthop_put(new); 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci return err; 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci/* called with rtnl_lock held */ 11468c2ecf20Sopenharmony_cistatic int insert_nexthop(struct net *net, struct nexthop *new_nh, 11478c2ecf20Sopenharmony_ci struct nh_config *cfg, struct netlink_ext_ack *extack) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci struct rb_node **pp, *parent = NULL, *next; 11508c2ecf20Sopenharmony_ci struct rb_root *root = &net->nexthop.rb_root; 11518c2ecf20Sopenharmony_ci bool replace = !!(cfg->nlflags & NLM_F_REPLACE); 11528c2ecf20Sopenharmony_ci bool create = !!(cfg->nlflags & NLM_F_CREATE); 11538c2ecf20Sopenharmony_ci u32 new_id = new_nh->id; 11548c2ecf20Sopenharmony_ci int replace_notify = 0; 11558c2ecf20Sopenharmony_ci int rc = -EEXIST; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci pp = &root->rb_node; 11588c2ecf20Sopenharmony_ci while (1) { 11598c2ecf20Sopenharmony_ci struct nexthop *nh; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci next = *pp; 11628c2ecf20Sopenharmony_ci if (!next) 11638c2ecf20Sopenharmony_ci break; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci parent = next; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci nh = rb_entry(parent, struct nexthop, rb_node); 11688c2ecf20Sopenharmony_ci if (new_id < nh->id) { 11698c2ecf20Sopenharmony_ci pp = &next->rb_left; 11708c2ecf20Sopenharmony_ci } else if (new_id > nh->id) { 11718c2ecf20Sopenharmony_ci pp = &next->rb_right; 11728c2ecf20Sopenharmony_ci } else if (replace) { 11738c2ecf20Sopenharmony_ci rc = replace_nexthop(net, nh, new_nh, extack); 11748c2ecf20Sopenharmony_ci if (!rc) { 11758c2ecf20Sopenharmony_ci new_nh = nh; /* send notification with old nh */ 11768c2ecf20Sopenharmony_ci replace_notify = 1; 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci goto out; 11798c2ecf20Sopenharmony_ci } else { 11808c2ecf20Sopenharmony_ci /* id already exists and not a replace */ 11818c2ecf20Sopenharmony_ci goto out; 11828c2ecf20Sopenharmony_ci } 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci if (replace && !create) { 11868c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Replace specified without create and no entry exists"); 11878c2ecf20Sopenharmony_ci rc = -ENOENT; 11888c2ecf20Sopenharmony_ci goto out; 11898c2ecf20Sopenharmony_ci } 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci rb_link_node_rcu(&new_nh->rb_node, parent, pp); 11928c2ecf20Sopenharmony_ci rb_insert_color(&new_nh->rb_node, root); 11938c2ecf20Sopenharmony_ci rc = 0; 11948c2ecf20Sopenharmony_ciout: 11958c2ecf20Sopenharmony_ci if (!rc) { 11968c2ecf20Sopenharmony_ci nh_base_seq_inc(net); 11978c2ecf20Sopenharmony_ci nexthop_notify(RTM_NEWNEXTHOP, new_nh, &cfg->nlinfo); 11988c2ecf20Sopenharmony_ci if (replace_notify && 11998c2ecf20Sopenharmony_ci READ_ONCE(net->ipv4.sysctl_nexthop_compat_mode)) 12008c2ecf20Sopenharmony_ci nexthop_replace_notify(net, new_nh, &cfg->nlinfo); 12018c2ecf20Sopenharmony_ci } 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci return rc; 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci/* rtnl */ 12078c2ecf20Sopenharmony_ci/* remove all nexthops tied to a device being deleted */ 12088c2ecf20Sopenharmony_cistatic void nexthop_flush_dev(struct net_device *dev, unsigned long event) 12098c2ecf20Sopenharmony_ci{ 12108c2ecf20Sopenharmony_ci unsigned int hash = nh_dev_hashfn(dev->ifindex); 12118c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 12128c2ecf20Sopenharmony_ci struct hlist_head *head = &net->nexthop.devhash[hash]; 12138c2ecf20Sopenharmony_ci struct hlist_node *n; 12148c2ecf20Sopenharmony_ci struct nh_info *nhi; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(nhi, n, head, dev_hash) { 12178c2ecf20Sopenharmony_ci if (nhi->fib_nhc.nhc_dev != dev) 12188c2ecf20Sopenharmony_ci continue; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci if (nhi->reject_nh && 12218c2ecf20Sopenharmony_ci (event == NETDEV_DOWN || event == NETDEV_CHANGE)) 12228c2ecf20Sopenharmony_ci continue; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci remove_nexthop(net, nhi->nh_parent, NULL); 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci} 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci/* rtnl; called when net namespace is deleted */ 12298c2ecf20Sopenharmony_cistatic void flush_all_nexthops(struct net *net) 12308c2ecf20Sopenharmony_ci{ 12318c2ecf20Sopenharmony_ci struct rb_root *root = &net->nexthop.rb_root; 12328c2ecf20Sopenharmony_ci struct rb_node *node; 12338c2ecf20Sopenharmony_ci struct nexthop *nh; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci while ((node = rb_first(root))) { 12368c2ecf20Sopenharmony_ci nh = rb_entry(node, struct nexthop, rb_node); 12378c2ecf20Sopenharmony_ci remove_nexthop(net, nh, NULL); 12388c2ecf20Sopenharmony_ci cond_resched(); 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci} 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_cistatic struct nexthop *nexthop_create_group(struct net *net, 12438c2ecf20Sopenharmony_ci struct nh_config *cfg) 12448c2ecf20Sopenharmony_ci{ 12458c2ecf20Sopenharmony_ci struct nlattr *grps_attr = cfg->nh_grp; 12468c2ecf20Sopenharmony_ci struct nexthop_grp *entry = nla_data(grps_attr); 12478c2ecf20Sopenharmony_ci u16 num_nh = nla_len(grps_attr) / sizeof(*entry); 12488c2ecf20Sopenharmony_ci struct nh_group *nhg; 12498c2ecf20Sopenharmony_ci struct nexthop *nh; 12508c2ecf20Sopenharmony_ci int i; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci if (WARN_ON(!num_nh)) 12538c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci nh = nexthop_alloc(); 12568c2ecf20Sopenharmony_ci if (!nh) 12578c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci nh->is_group = 1; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci nhg = nexthop_grp_alloc(num_nh); 12628c2ecf20Sopenharmony_ci if (!nhg) { 12638c2ecf20Sopenharmony_ci kfree(nh); 12648c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci /* spare group used for removals */ 12688c2ecf20Sopenharmony_ci nhg->spare = nexthop_grp_alloc(num_nh); 12698c2ecf20Sopenharmony_ci if (!nhg->spare) { 12708c2ecf20Sopenharmony_ci kfree(nhg); 12718c2ecf20Sopenharmony_ci kfree(nh); 12728c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 12738c2ecf20Sopenharmony_ci } 12748c2ecf20Sopenharmony_ci nhg->spare->spare = nhg; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci for (i = 0; i < nhg->num_nh; ++i) { 12778c2ecf20Sopenharmony_ci struct nexthop *nhe; 12788c2ecf20Sopenharmony_ci struct nh_info *nhi; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci nhe = nexthop_find_by_id(net, entry[i].id); 12818c2ecf20Sopenharmony_ci if (!nexthop_get(nhe)) 12828c2ecf20Sopenharmony_ci goto out_no_nh; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci nhi = rtnl_dereference(nhe->nh_info); 12858c2ecf20Sopenharmony_ci if (nhi->family == AF_INET) 12868c2ecf20Sopenharmony_ci nhg->has_v4 = true; 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci nhg->nh_entries[i].nh = nhe; 12898c2ecf20Sopenharmony_ci nhg->nh_entries[i].weight = entry[i].weight + 1; 12908c2ecf20Sopenharmony_ci list_add(&nhg->nh_entries[i].nh_list, &nhe->grp_list); 12918c2ecf20Sopenharmony_ci nhg->nh_entries[i].nh_parent = nh; 12928c2ecf20Sopenharmony_ci } 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci if (cfg->nh_grp_type == NEXTHOP_GRP_TYPE_MPATH) { 12958c2ecf20Sopenharmony_ci nhg->mpath = 1; 12968c2ecf20Sopenharmony_ci nh_group_rebalance(nhg); 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci if (cfg->nh_fdb) 13008c2ecf20Sopenharmony_ci nhg->fdb_nh = 1; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci rcu_assign_pointer(nh->nh_grp, nhg); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci return nh; 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ciout_no_nh: 13078c2ecf20Sopenharmony_ci for (i--; i >= 0; --i) { 13088c2ecf20Sopenharmony_ci list_del(&nhg->nh_entries[i].nh_list); 13098c2ecf20Sopenharmony_ci nexthop_put(nhg->nh_entries[i].nh); 13108c2ecf20Sopenharmony_ci } 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci kfree(nhg->spare); 13138c2ecf20Sopenharmony_ci kfree(nhg); 13148c2ecf20Sopenharmony_ci kfree(nh); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 13178c2ecf20Sopenharmony_ci} 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_cistatic int nh_create_ipv4(struct net *net, struct nexthop *nh, 13208c2ecf20Sopenharmony_ci struct nh_info *nhi, struct nh_config *cfg, 13218c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 13228c2ecf20Sopenharmony_ci{ 13238c2ecf20Sopenharmony_ci struct fib_nh *fib_nh = &nhi->fib_nh; 13248c2ecf20Sopenharmony_ci struct fib_config fib_cfg = { 13258c2ecf20Sopenharmony_ci .fc_oif = cfg->nh_ifindex, 13268c2ecf20Sopenharmony_ci .fc_gw4 = cfg->gw.ipv4, 13278c2ecf20Sopenharmony_ci .fc_gw_family = cfg->gw.ipv4 ? AF_INET : 0, 13288c2ecf20Sopenharmony_ci .fc_flags = cfg->nh_flags, 13298c2ecf20Sopenharmony_ci .fc_nlinfo = cfg->nlinfo, 13308c2ecf20Sopenharmony_ci .fc_encap = cfg->nh_encap, 13318c2ecf20Sopenharmony_ci .fc_encap_type = cfg->nh_encap_type, 13328c2ecf20Sopenharmony_ci }; 13338c2ecf20Sopenharmony_ci u32 tb_id = (cfg->dev ? l3mdev_fib_table(cfg->dev) : RT_TABLE_MAIN); 13348c2ecf20Sopenharmony_ci int err; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci err = fib_nh_init(net, fib_nh, &fib_cfg, 1, extack); 13378c2ecf20Sopenharmony_ci if (err) { 13388c2ecf20Sopenharmony_ci fib_nh_release(net, fib_nh); 13398c2ecf20Sopenharmony_ci goto out; 13408c2ecf20Sopenharmony_ci } 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci if (nhi->fdb_nh) 13438c2ecf20Sopenharmony_ci goto out; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci /* sets nh_dev if successful */ 13468c2ecf20Sopenharmony_ci err = fib_check_nh(net, fib_nh, tb_id, 0, extack); 13478c2ecf20Sopenharmony_ci if (!err) { 13488c2ecf20Sopenharmony_ci nh->nh_flags = fib_nh->fib_nh_flags; 13498c2ecf20Sopenharmony_ci fib_info_update_nhc_saddr(net, &fib_nh->nh_common, 13508c2ecf20Sopenharmony_ci !fib_nh->fib_nh_scope ? 0 : fib_nh->fib_nh_scope - 1); 13518c2ecf20Sopenharmony_ci } else { 13528c2ecf20Sopenharmony_ci fib_nh_release(net, fib_nh); 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ciout: 13558c2ecf20Sopenharmony_ci return err; 13568c2ecf20Sopenharmony_ci} 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_cistatic int nh_create_ipv6(struct net *net, struct nexthop *nh, 13598c2ecf20Sopenharmony_ci struct nh_info *nhi, struct nh_config *cfg, 13608c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 13618c2ecf20Sopenharmony_ci{ 13628c2ecf20Sopenharmony_ci struct fib6_nh *fib6_nh = &nhi->fib6_nh; 13638c2ecf20Sopenharmony_ci struct fib6_config fib6_cfg = { 13648c2ecf20Sopenharmony_ci .fc_table = l3mdev_fib_table(cfg->dev), 13658c2ecf20Sopenharmony_ci .fc_ifindex = cfg->nh_ifindex, 13668c2ecf20Sopenharmony_ci .fc_gateway = cfg->gw.ipv6, 13678c2ecf20Sopenharmony_ci .fc_flags = cfg->nh_flags, 13688c2ecf20Sopenharmony_ci .fc_nlinfo = cfg->nlinfo, 13698c2ecf20Sopenharmony_ci .fc_encap = cfg->nh_encap, 13708c2ecf20Sopenharmony_ci .fc_encap_type = cfg->nh_encap_type, 13718c2ecf20Sopenharmony_ci .fc_is_fdb = cfg->nh_fdb, 13728c2ecf20Sopenharmony_ci }; 13738c2ecf20Sopenharmony_ci int err; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&cfg->gw.ipv6)) 13768c2ecf20Sopenharmony_ci fib6_cfg.fc_flags |= RTF_GATEWAY; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci /* sets nh_dev if successful */ 13798c2ecf20Sopenharmony_ci err = ipv6_stub->fib6_nh_init(net, fib6_nh, &fib6_cfg, GFP_KERNEL, 13808c2ecf20Sopenharmony_ci extack); 13818c2ecf20Sopenharmony_ci if (err) { 13828c2ecf20Sopenharmony_ci /* IPv6 is not enabled, don't call fib6_nh_release */ 13838c2ecf20Sopenharmony_ci if (err == -EAFNOSUPPORT) 13848c2ecf20Sopenharmony_ci goto out; 13858c2ecf20Sopenharmony_ci ipv6_stub->fib6_nh_release(fib6_nh); 13868c2ecf20Sopenharmony_ci } else { 13878c2ecf20Sopenharmony_ci nh->nh_flags = fib6_nh->fib_nh_flags; 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ciout: 13908c2ecf20Sopenharmony_ci return err; 13918c2ecf20Sopenharmony_ci} 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_cistatic struct nexthop *nexthop_create(struct net *net, struct nh_config *cfg, 13948c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 13958c2ecf20Sopenharmony_ci{ 13968c2ecf20Sopenharmony_ci struct nh_info *nhi; 13978c2ecf20Sopenharmony_ci struct nexthop *nh; 13988c2ecf20Sopenharmony_ci int err = 0; 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci nh = nexthop_alloc(); 14018c2ecf20Sopenharmony_ci if (!nh) 14028c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci nhi = kzalloc(sizeof(*nhi), GFP_KERNEL); 14058c2ecf20Sopenharmony_ci if (!nhi) { 14068c2ecf20Sopenharmony_ci kfree(nh); 14078c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci nh->nh_flags = cfg->nh_flags; 14118c2ecf20Sopenharmony_ci nh->net = net; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci nhi->nh_parent = nh; 14148c2ecf20Sopenharmony_ci nhi->family = cfg->nh_family; 14158c2ecf20Sopenharmony_ci nhi->fib_nhc.nhc_scope = RT_SCOPE_LINK; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci if (cfg->nh_fdb) 14188c2ecf20Sopenharmony_ci nhi->fdb_nh = 1; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci if (cfg->nh_blackhole) { 14218c2ecf20Sopenharmony_ci nhi->reject_nh = 1; 14228c2ecf20Sopenharmony_ci cfg->nh_ifindex = net->loopback_dev->ifindex; 14238c2ecf20Sopenharmony_ci } 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci switch (cfg->nh_family) { 14268c2ecf20Sopenharmony_ci case AF_INET: 14278c2ecf20Sopenharmony_ci err = nh_create_ipv4(net, nh, nhi, cfg, extack); 14288c2ecf20Sopenharmony_ci break; 14298c2ecf20Sopenharmony_ci case AF_INET6: 14308c2ecf20Sopenharmony_ci err = nh_create_ipv6(net, nh, nhi, cfg, extack); 14318c2ecf20Sopenharmony_ci break; 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci if (err) { 14358c2ecf20Sopenharmony_ci kfree(nhi); 14368c2ecf20Sopenharmony_ci kfree(nh); 14378c2ecf20Sopenharmony_ci return ERR_PTR(err); 14388c2ecf20Sopenharmony_ci } 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci /* add the entry to the device based hash */ 14418c2ecf20Sopenharmony_ci if (!nhi->fdb_nh) 14428c2ecf20Sopenharmony_ci nexthop_devhash_add(net, nhi); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci rcu_assign_pointer(nh->nh_info, nhi); 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci return nh; 14478c2ecf20Sopenharmony_ci} 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci/* called with rtnl lock held */ 14508c2ecf20Sopenharmony_cistatic struct nexthop *nexthop_add(struct net *net, struct nh_config *cfg, 14518c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 14528c2ecf20Sopenharmony_ci{ 14538c2ecf20Sopenharmony_ci struct nexthop *nh; 14548c2ecf20Sopenharmony_ci int err; 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci if (cfg->nlflags & NLM_F_REPLACE && !cfg->nh_id) { 14578c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Replace requires nexthop id"); 14588c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 14598c2ecf20Sopenharmony_ci } 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci if (!cfg->nh_id) { 14628c2ecf20Sopenharmony_ci cfg->nh_id = nh_find_unused_id(net); 14638c2ecf20Sopenharmony_ci if (!cfg->nh_id) { 14648c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "No unused id"); 14658c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 14668c2ecf20Sopenharmony_ci } 14678c2ecf20Sopenharmony_ci } 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci if (cfg->nh_grp) 14708c2ecf20Sopenharmony_ci nh = nexthop_create_group(net, cfg); 14718c2ecf20Sopenharmony_ci else 14728c2ecf20Sopenharmony_ci nh = nexthop_create(net, cfg, extack); 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci if (IS_ERR(nh)) 14758c2ecf20Sopenharmony_ci return nh; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci refcount_set(&nh->refcnt, 1); 14788c2ecf20Sopenharmony_ci nh->id = cfg->nh_id; 14798c2ecf20Sopenharmony_ci nh->protocol = cfg->nh_protocol; 14808c2ecf20Sopenharmony_ci nh->net = net; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci err = insert_nexthop(net, nh, cfg, extack); 14838c2ecf20Sopenharmony_ci if (err) { 14848c2ecf20Sopenharmony_ci __remove_nexthop(net, nh, NULL); 14858c2ecf20Sopenharmony_ci nexthop_put(nh); 14868c2ecf20Sopenharmony_ci nh = ERR_PTR(err); 14878c2ecf20Sopenharmony_ci } 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci return nh; 14908c2ecf20Sopenharmony_ci} 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_cistatic int rtm_to_nh_config(struct net *net, struct sk_buff *skb, 14938c2ecf20Sopenharmony_ci struct nlmsghdr *nlh, struct nh_config *cfg, 14948c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 14958c2ecf20Sopenharmony_ci{ 14968c2ecf20Sopenharmony_ci struct nhmsg *nhm = nlmsg_data(nlh); 14978c2ecf20Sopenharmony_ci struct nlattr *tb[NHA_MAX + 1]; 14988c2ecf20Sopenharmony_ci int err; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci err = nlmsg_parse(nlh, sizeof(*nhm), tb, NHA_MAX, rtm_nh_policy, 15018c2ecf20Sopenharmony_ci extack); 15028c2ecf20Sopenharmony_ci if (err < 0) 15038c2ecf20Sopenharmony_ci return err; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci err = -EINVAL; 15068c2ecf20Sopenharmony_ci if (nhm->resvd || nhm->nh_scope) { 15078c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid values in ancillary header"); 15088c2ecf20Sopenharmony_ci goto out; 15098c2ecf20Sopenharmony_ci } 15108c2ecf20Sopenharmony_ci if (nhm->nh_flags & ~NEXTHOP_VALID_USER_FLAGS) { 15118c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid nexthop flags in ancillary header"); 15128c2ecf20Sopenharmony_ci goto out; 15138c2ecf20Sopenharmony_ci } 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci switch (nhm->nh_family) { 15168c2ecf20Sopenharmony_ci case AF_INET: 15178c2ecf20Sopenharmony_ci case AF_INET6: 15188c2ecf20Sopenharmony_ci break; 15198c2ecf20Sopenharmony_ci case AF_UNSPEC: 15208c2ecf20Sopenharmony_ci if (tb[NHA_GROUP]) 15218c2ecf20Sopenharmony_ci break; 15228c2ecf20Sopenharmony_ci fallthrough; 15238c2ecf20Sopenharmony_ci default: 15248c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid address family"); 15258c2ecf20Sopenharmony_ci goto out; 15268c2ecf20Sopenharmony_ci } 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci if (tb[NHA_GROUPS] || tb[NHA_MASTER]) { 15298c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid attributes in request"); 15308c2ecf20Sopenharmony_ci goto out; 15318c2ecf20Sopenharmony_ci } 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci memset(cfg, 0, sizeof(*cfg)); 15348c2ecf20Sopenharmony_ci cfg->nlflags = nlh->nlmsg_flags; 15358c2ecf20Sopenharmony_ci cfg->nlinfo.portid = NETLINK_CB(skb).portid; 15368c2ecf20Sopenharmony_ci cfg->nlinfo.nlh = nlh; 15378c2ecf20Sopenharmony_ci cfg->nlinfo.nl_net = net; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci cfg->nh_family = nhm->nh_family; 15408c2ecf20Sopenharmony_ci cfg->nh_protocol = nhm->nh_protocol; 15418c2ecf20Sopenharmony_ci cfg->nh_flags = nhm->nh_flags; 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci if (tb[NHA_ID]) 15448c2ecf20Sopenharmony_ci cfg->nh_id = nla_get_u32(tb[NHA_ID]); 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci if (tb[NHA_FDB]) { 15478c2ecf20Sopenharmony_ci if (tb[NHA_OIF] || tb[NHA_BLACKHOLE] || 15488c2ecf20Sopenharmony_ci tb[NHA_ENCAP] || tb[NHA_ENCAP_TYPE]) { 15498c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Fdb attribute can not be used with encap, oif or blackhole"); 15508c2ecf20Sopenharmony_ci goto out; 15518c2ecf20Sopenharmony_ci } 15528c2ecf20Sopenharmony_ci if (nhm->nh_flags) { 15538c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Unsupported nexthop flags in ancillary header"); 15548c2ecf20Sopenharmony_ci goto out; 15558c2ecf20Sopenharmony_ci } 15568c2ecf20Sopenharmony_ci cfg->nh_fdb = nla_get_flag(tb[NHA_FDB]); 15578c2ecf20Sopenharmony_ci } 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci if (tb[NHA_GROUP]) { 15608c2ecf20Sopenharmony_ci if (nhm->nh_family != AF_UNSPEC) { 15618c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid family for group"); 15628c2ecf20Sopenharmony_ci goto out; 15638c2ecf20Sopenharmony_ci } 15648c2ecf20Sopenharmony_ci cfg->nh_grp = tb[NHA_GROUP]; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci cfg->nh_grp_type = NEXTHOP_GRP_TYPE_MPATH; 15678c2ecf20Sopenharmony_ci if (tb[NHA_GROUP_TYPE]) 15688c2ecf20Sopenharmony_ci cfg->nh_grp_type = nla_get_u16(tb[NHA_GROUP_TYPE]); 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci if (cfg->nh_grp_type > NEXTHOP_GRP_TYPE_MAX) { 15718c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid group type"); 15728c2ecf20Sopenharmony_ci goto out; 15738c2ecf20Sopenharmony_ci } 15748c2ecf20Sopenharmony_ci err = nh_check_attr_group(net, tb, extack); 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci /* no other attributes should be set */ 15778c2ecf20Sopenharmony_ci goto out; 15788c2ecf20Sopenharmony_ci } 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci if (tb[NHA_BLACKHOLE]) { 15818c2ecf20Sopenharmony_ci if (tb[NHA_GATEWAY] || tb[NHA_OIF] || 15828c2ecf20Sopenharmony_ci tb[NHA_ENCAP] || tb[NHA_ENCAP_TYPE] || tb[NHA_FDB]) { 15838c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Blackhole attribute can not be used with gateway, oif, encap or fdb"); 15848c2ecf20Sopenharmony_ci goto out; 15858c2ecf20Sopenharmony_ci } 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci cfg->nh_blackhole = 1; 15888c2ecf20Sopenharmony_ci err = 0; 15898c2ecf20Sopenharmony_ci goto out; 15908c2ecf20Sopenharmony_ci } 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci if (!cfg->nh_fdb && !tb[NHA_OIF]) { 15938c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Device attribute required for non-blackhole and non-fdb nexthops"); 15948c2ecf20Sopenharmony_ci goto out; 15958c2ecf20Sopenharmony_ci } 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci if (!cfg->nh_fdb && tb[NHA_OIF]) { 15988c2ecf20Sopenharmony_ci cfg->nh_ifindex = nla_get_u32(tb[NHA_OIF]); 15998c2ecf20Sopenharmony_ci if (cfg->nh_ifindex) 16008c2ecf20Sopenharmony_ci cfg->dev = __dev_get_by_index(net, cfg->nh_ifindex); 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci if (!cfg->dev) { 16038c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid device index"); 16048c2ecf20Sopenharmony_ci goto out; 16058c2ecf20Sopenharmony_ci } else if (!(cfg->dev->flags & IFF_UP)) { 16068c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Nexthop device is not up"); 16078c2ecf20Sopenharmony_ci err = -ENETDOWN; 16088c2ecf20Sopenharmony_ci goto out; 16098c2ecf20Sopenharmony_ci } else if (!netif_carrier_ok(cfg->dev)) { 16108c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Carrier for nexthop device is down"); 16118c2ecf20Sopenharmony_ci err = -ENETDOWN; 16128c2ecf20Sopenharmony_ci goto out; 16138c2ecf20Sopenharmony_ci } 16148c2ecf20Sopenharmony_ci } 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci err = -EINVAL; 16178c2ecf20Sopenharmony_ci if (tb[NHA_GATEWAY]) { 16188c2ecf20Sopenharmony_ci struct nlattr *gwa = tb[NHA_GATEWAY]; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci switch (cfg->nh_family) { 16218c2ecf20Sopenharmony_ci case AF_INET: 16228c2ecf20Sopenharmony_ci if (nla_len(gwa) != sizeof(u32)) { 16238c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid gateway"); 16248c2ecf20Sopenharmony_ci goto out; 16258c2ecf20Sopenharmony_ci } 16268c2ecf20Sopenharmony_ci cfg->gw.ipv4 = nla_get_be32(gwa); 16278c2ecf20Sopenharmony_ci break; 16288c2ecf20Sopenharmony_ci case AF_INET6: 16298c2ecf20Sopenharmony_ci if (nla_len(gwa) != sizeof(struct in6_addr)) { 16308c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid gateway"); 16318c2ecf20Sopenharmony_ci goto out; 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci cfg->gw.ipv6 = nla_get_in6_addr(gwa); 16348c2ecf20Sopenharmony_ci break; 16358c2ecf20Sopenharmony_ci default: 16368c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 16378c2ecf20Sopenharmony_ci "Unknown address family for gateway"); 16388c2ecf20Sopenharmony_ci goto out; 16398c2ecf20Sopenharmony_ci } 16408c2ecf20Sopenharmony_ci } else { 16418c2ecf20Sopenharmony_ci /* device only nexthop (no gateway) */ 16428c2ecf20Sopenharmony_ci if (cfg->nh_flags & RTNH_F_ONLINK) { 16438c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 16448c2ecf20Sopenharmony_ci "ONLINK flag can not be set for nexthop without a gateway"); 16458c2ecf20Sopenharmony_ci goto out; 16468c2ecf20Sopenharmony_ci } 16478c2ecf20Sopenharmony_ci } 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci if (tb[NHA_ENCAP]) { 16508c2ecf20Sopenharmony_ci cfg->nh_encap = tb[NHA_ENCAP]; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci if (!tb[NHA_ENCAP_TYPE]) { 16538c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "LWT encapsulation type is missing"); 16548c2ecf20Sopenharmony_ci goto out; 16558c2ecf20Sopenharmony_ci } 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci cfg->nh_encap_type = nla_get_u16(tb[NHA_ENCAP_TYPE]); 16588c2ecf20Sopenharmony_ci err = lwtunnel_valid_encap_type(cfg->nh_encap_type, extack); 16598c2ecf20Sopenharmony_ci if (err < 0) 16608c2ecf20Sopenharmony_ci goto out; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci } else if (tb[NHA_ENCAP_TYPE]) { 16638c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "LWT encapsulation attribute is missing"); 16648c2ecf20Sopenharmony_ci goto out; 16658c2ecf20Sopenharmony_ci } 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci err = 0; 16698c2ecf20Sopenharmony_ciout: 16708c2ecf20Sopenharmony_ci return err; 16718c2ecf20Sopenharmony_ci} 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci/* rtnl */ 16748c2ecf20Sopenharmony_cistatic int rtm_new_nexthop(struct sk_buff *skb, struct nlmsghdr *nlh, 16758c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 16768c2ecf20Sopenharmony_ci{ 16778c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 16788c2ecf20Sopenharmony_ci struct nh_config cfg; 16798c2ecf20Sopenharmony_ci struct nexthop *nh; 16808c2ecf20Sopenharmony_ci int err; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci err = rtm_to_nh_config(net, skb, nlh, &cfg, extack); 16838c2ecf20Sopenharmony_ci if (!err) { 16848c2ecf20Sopenharmony_ci nh = nexthop_add(net, &cfg, extack); 16858c2ecf20Sopenharmony_ci if (IS_ERR(nh)) 16868c2ecf20Sopenharmony_ci err = PTR_ERR(nh); 16878c2ecf20Sopenharmony_ci } 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci return err; 16908c2ecf20Sopenharmony_ci} 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_cistatic int nh_valid_get_del_req(struct nlmsghdr *nlh, u32 *id, 16938c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 16948c2ecf20Sopenharmony_ci{ 16958c2ecf20Sopenharmony_ci struct nhmsg *nhm = nlmsg_data(nlh); 16968c2ecf20Sopenharmony_ci struct nlattr *tb[NHA_MAX + 1]; 16978c2ecf20Sopenharmony_ci int err, i; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci err = nlmsg_parse(nlh, sizeof(*nhm), tb, NHA_MAX, rtm_nh_policy, 17008c2ecf20Sopenharmony_ci extack); 17018c2ecf20Sopenharmony_ci if (err < 0) 17028c2ecf20Sopenharmony_ci return err; 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci err = -EINVAL; 17058c2ecf20Sopenharmony_ci for (i = 0; i < __NHA_MAX; ++i) { 17068c2ecf20Sopenharmony_ci if (!tb[i]) 17078c2ecf20Sopenharmony_ci continue; 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci switch (i) { 17108c2ecf20Sopenharmony_ci case NHA_ID: 17118c2ecf20Sopenharmony_ci break; 17128c2ecf20Sopenharmony_ci default: 17138c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, tb[i], 17148c2ecf20Sopenharmony_ci "Unexpected attribute in request"); 17158c2ecf20Sopenharmony_ci goto out; 17168c2ecf20Sopenharmony_ci } 17178c2ecf20Sopenharmony_ci } 17188c2ecf20Sopenharmony_ci if (nhm->nh_protocol || nhm->resvd || nhm->nh_scope || nhm->nh_flags) { 17198c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid values in header"); 17208c2ecf20Sopenharmony_ci goto out; 17218c2ecf20Sopenharmony_ci } 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci if (!tb[NHA_ID]) { 17248c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Nexthop id is missing"); 17258c2ecf20Sopenharmony_ci goto out; 17268c2ecf20Sopenharmony_ci } 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci *id = nla_get_u32(tb[NHA_ID]); 17298c2ecf20Sopenharmony_ci if (!(*id)) 17308c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid nexthop id"); 17318c2ecf20Sopenharmony_ci else 17328c2ecf20Sopenharmony_ci err = 0; 17338c2ecf20Sopenharmony_ciout: 17348c2ecf20Sopenharmony_ci return err; 17358c2ecf20Sopenharmony_ci} 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci/* rtnl */ 17388c2ecf20Sopenharmony_cistatic int rtm_del_nexthop(struct sk_buff *skb, struct nlmsghdr *nlh, 17398c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 17408c2ecf20Sopenharmony_ci{ 17418c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 17428c2ecf20Sopenharmony_ci struct nl_info nlinfo = { 17438c2ecf20Sopenharmony_ci .nlh = nlh, 17448c2ecf20Sopenharmony_ci .nl_net = net, 17458c2ecf20Sopenharmony_ci .portid = NETLINK_CB(skb).portid, 17468c2ecf20Sopenharmony_ci }; 17478c2ecf20Sopenharmony_ci struct nexthop *nh; 17488c2ecf20Sopenharmony_ci int err; 17498c2ecf20Sopenharmony_ci u32 id; 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci err = nh_valid_get_del_req(nlh, &id, extack); 17528c2ecf20Sopenharmony_ci if (err) 17538c2ecf20Sopenharmony_ci return err; 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci nh = nexthop_find_by_id(net, id); 17568c2ecf20Sopenharmony_ci if (!nh) 17578c2ecf20Sopenharmony_ci return -ENOENT; 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci remove_nexthop(net, nh, &nlinfo); 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci return 0; 17628c2ecf20Sopenharmony_ci} 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci/* rtnl */ 17658c2ecf20Sopenharmony_cistatic int rtm_get_nexthop(struct sk_buff *in_skb, struct nlmsghdr *nlh, 17668c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 17678c2ecf20Sopenharmony_ci{ 17688c2ecf20Sopenharmony_ci struct net *net = sock_net(in_skb->sk); 17698c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 17708c2ecf20Sopenharmony_ci struct nexthop *nh; 17718c2ecf20Sopenharmony_ci int err; 17728c2ecf20Sopenharmony_ci u32 id; 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci err = nh_valid_get_del_req(nlh, &id, extack); 17758c2ecf20Sopenharmony_ci if (err) 17768c2ecf20Sopenharmony_ci return err; 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci err = -ENOBUFS; 17798c2ecf20Sopenharmony_ci skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 17808c2ecf20Sopenharmony_ci if (!skb) 17818c2ecf20Sopenharmony_ci goto out; 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci err = -ENOENT; 17848c2ecf20Sopenharmony_ci nh = nexthop_find_by_id(net, id); 17858c2ecf20Sopenharmony_ci if (!nh) 17868c2ecf20Sopenharmony_ci goto errout_free; 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci err = nh_fill_node(skb, nh, RTM_NEWNEXTHOP, NETLINK_CB(in_skb).portid, 17898c2ecf20Sopenharmony_ci nlh->nlmsg_seq, 0); 17908c2ecf20Sopenharmony_ci if (err < 0) { 17918c2ecf20Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 17928c2ecf20Sopenharmony_ci goto errout_free; 17938c2ecf20Sopenharmony_ci } 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 17968c2ecf20Sopenharmony_ciout: 17978c2ecf20Sopenharmony_ci return err; 17988c2ecf20Sopenharmony_cierrout_free: 17998c2ecf20Sopenharmony_ci kfree_skb(skb); 18008c2ecf20Sopenharmony_ci goto out; 18018c2ecf20Sopenharmony_ci} 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_cistatic bool nh_dump_filtered(struct nexthop *nh, int dev_idx, int master_idx, 18048c2ecf20Sopenharmony_ci bool group_filter, u8 family) 18058c2ecf20Sopenharmony_ci{ 18068c2ecf20Sopenharmony_ci const struct net_device *dev; 18078c2ecf20Sopenharmony_ci const struct nh_info *nhi; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci if (group_filter && !nh->is_group) 18108c2ecf20Sopenharmony_ci return true; 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci if (!dev_idx && !master_idx && !family) 18138c2ecf20Sopenharmony_ci return false; 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci if (nh->is_group) 18168c2ecf20Sopenharmony_ci return true; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci nhi = rtnl_dereference(nh->nh_info); 18198c2ecf20Sopenharmony_ci if (family && nhi->family != family) 18208c2ecf20Sopenharmony_ci return true; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci dev = nhi->fib_nhc.nhc_dev; 18238c2ecf20Sopenharmony_ci if (dev_idx && (!dev || dev->ifindex != dev_idx)) 18248c2ecf20Sopenharmony_ci return true; 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci if (master_idx) { 18278c2ecf20Sopenharmony_ci struct net_device *master; 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci if (!dev) 18308c2ecf20Sopenharmony_ci return true; 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci master = netdev_master_upper_dev_get((struct net_device *)dev); 18338c2ecf20Sopenharmony_ci if (!master || master->ifindex != master_idx) 18348c2ecf20Sopenharmony_ci return true; 18358c2ecf20Sopenharmony_ci } 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci return false; 18388c2ecf20Sopenharmony_ci} 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_cistatic int nh_valid_dump_req(const struct nlmsghdr *nlh, int *dev_idx, 18418c2ecf20Sopenharmony_ci int *master_idx, bool *group_filter, 18428c2ecf20Sopenharmony_ci bool *fdb_filter, struct netlink_callback *cb) 18438c2ecf20Sopenharmony_ci{ 18448c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack = cb->extack; 18458c2ecf20Sopenharmony_ci struct nlattr *tb[NHA_MAX + 1]; 18468c2ecf20Sopenharmony_ci struct nhmsg *nhm; 18478c2ecf20Sopenharmony_ci int err, i; 18488c2ecf20Sopenharmony_ci u32 idx; 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci err = nlmsg_parse(nlh, sizeof(*nhm), tb, NHA_MAX, rtm_nh_policy, 18518c2ecf20Sopenharmony_ci NULL); 18528c2ecf20Sopenharmony_ci if (err < 0) 18538c2ecf20Sopenharmony_ci return err; 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci for (i = 0; i <= NHA_MAX; ++i) { 18568c2ecf20Sopenharmony_ci if (!tb[i]) 18578c2ecf20Sopenharmony_ci continue; 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci switch (i) { 18608c2ecf20Sopenharmony_ci case NHA_OIF: 18618c2ecf20Sopenharmony_ci idx = nla_get_u32(tb[i]); 18628c2ecf20Sopenharmony_ci if (idx > INT_MAX) { 18638c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid device index"); 18648c2ecf20Sopenharmony_ci return -EINVAL; 18658c2ecf20Sopenharmony_ci } 18668c2ecf20Sopenharmony_ci *dev_idx = idx; 18678c2ecf20Sopenharmony_ci break; 18688c2ecf20Sopenharmony_ci case NHA_MASTER: 18698c2ecf20Sopenharmony_ci idx = nla_get_u32(tb[i]); 18708c2ecf20Sopenharmony_ci if (idx > INT_MAX) { 18718c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid master device index"); 18728c2ecf20Sopenharmony_ci return -EINVAL; 18738c2ecf20Sopenharmony_ci } 18748c2ecf20Sopenharmony_ci *master_idx = idx; 18758c2ecf20Sopenharmony_ci break; 18768c2ecf20Sopenharmony_ci case NHA_GROUPS: 18778c2ecf20Sopenharmony_ci *group_filter = true; 18788c2ecf20Sopenharmony_ci break; 18798c2ecf20Sopenharmony_ci case NHA_FDB: 18808c2ecf20Sopenharmony_ci *fdb_filter = true; 18818c2ecf20Sopenharmony_ci break; 18828c2ecf20Sopenharmony_ci default: 18838c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Unsupported attribute in dump request"); 18848c2ecf20Sopenharmony_ci return -EINVAL; 18858c2ecf20Sopenharmony_ci } 18868c2ecf20Sopenharmony_ci } 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci nhm = nlmsg_data(nlh); 18898c2ecf20Sopenharmony_ci if (nhm->nh_protocol || nhm->resvd || nhm->nh_scope || nhm->nh_flags) { 18908c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid values in header for nexthop dump request"); 18918c2ecf20Sopenharmony_ci return -EINVAL; 18928c2ecf20Sopenharmony_ci } 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci return 0; 18958c2ecf20Sopenharmony_ci} 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci/* rtnl */ 18988c2ecf20Sopenharmony_cistatic int rtm_dump_nexthop(struct sk_buff *skb, struct netlink_callback *cb) 18998c2ecf20Sopenharmony_ci{ 19008c2ecf20Sopenharmony_ci bool group_filter = false, fdb_filter = false; 19018c2ecf20Sopenharmony_ci struct nhmsg *nhm = nlmsg_data(cb->nlh); 19028c2ecf20Sopenharmony_ci int dev_filter_idx = 0, master_idx = 0; 19038c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 19048c2ecf20Sopenharmony_ci struct rb_root *root = &net->nexthop.rb_root; 19058c2ecf20Sopenharmony_ci struct rb_node *node; 19068c2ecf20Sopenharmony_ci int idx = 0, s_idx; 19078c2ecf20Sopenharmony_ci int err; 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci err = nh_valid_dump_req(cb->nlh, &dev_filter_idx, &master_idx, 19108c2ecf20Sopenharmony_ci &group_filter, &fdb_filter, cb); 19118c2ecf20Sopenharmony_ci if (err < 0) 19128c2ecf20Sopenharmony_ci return err; 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci s_idx = cb->args[0]; 19158c2ecf20Sopenharmony_ci for (node = rb_first(root); node; node = rb_next(node)) { 19168c2ecf20Sopenharmony_ci struct nexthop *nh; 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci if (idx < s_idx) 19198c2ecf20Sopenharmony_ci goto cont; 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci nh = rb_entry(node, struct nexthop, rb_node); 19228c2ecf20Sopenharmony_ci if (nh_dump_filtered(nh, dev_filter_idx, master_idx, 19238c2ecf20Sopenharmony_ci group_filter, nhm->nh_family)) 19248c2ecf20Sopenharmony_ci goto cont; 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci err = nh_fill_node(skb, nh, RTM_NEWNEXTHOP, 19278c2ecf20Sopenharmony_ci NETLINK_CB(cb->skb).portid, 19288c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, NLM_F_MULTI); 19298c2ecf20Sopenharmony_ci if (err < 0) { 19308c2ecf20Sopenharmony_ci if (likely(skb->len)) 19318c2ecf20Sopenharmony_ci goto out; 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci goto out_err; 19348c2ecf20Sopenharmony_ci } 19358c2ecf20Sopenharmony_cicont: 19368c2ecf20Sopenharmony_ci idx++; 19378c2ecf20Sopenharmony_ci } 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ciout: 19408c2ecf20Sopenharmony_ci err = skb->len; 19418c2ecf20Sopenharmony_ciout_err: 19428c2ecf20Sopenharmony_ci cb->args[0] = idx; 19438c2ecf20Sopenharmony_ci cb->seq = net->nexthop.seq; 19448c2ecf20Sopenharmony_ci nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci return err; 19478c2ecf20Sopenharmony_ci} 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_cistatic void nexthop_sync_mtu(struct net_device *dev, u32 orig_mtu) 19508c2ecf20Sopenharmony_ci{ 19518c2ecf20Sopenharmony_ci unsigned int hash = nh_dev_hashfn(dev->ifindex); 19528c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 19538c2ecf20Sopenharmony_ci struct hlist_head *head = &net->nexthop.devhash[hash]; 19548c2ecf20Sopenharmony_ci struct hlist_node *n; 19558c2ecf20Sopenharmony_ci struct nh_info *nhi; 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(nhi, n, head, dev_hash) { 19588c2ecf20Sopenharmony_ci if (nhi->fib_nhc.nhc_dev == dev) { 19598c2ecf20Sopenharmony_ci if (nhi->family == AF_INET) 19608c2ecf20Sopenharmony_ci fib_nhc_update_mtu(&nhi->fib_nhc, dev->mtu, 19618c2ecf20Sopenharmony_ci orig_mtu); 19628c2ecf20Sopenharmony_ci } 19638c2ecf20Sopenharmony_ci } 19648c2ecf20Sopenharmony_ci} 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci/* rtnl */ 19678c2ecf20Sopenharmony_cistatic int nh_netdev_event(struct notifier_block *this, 19688c2ecf20Sopenharmony_ci unsigned long event, void *ptr) 19698c2ecf20Sopenharmony_ci{ 19708c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 19718c2ecf20Sopenharmony_ci struct netdev_notifier_info_ext *info_ext; 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_ci switch (event) { 19748c2ecf20Sopenharmony_ci case NETDEV_DOWN: 19758c2ecf20Sopenharmony_ci case NETDEV_UNREGISTER: 19768c2ecf20Sopenharmony_ci nexthop_flush_dev(dev, event); 19778c2ecf20Sopenharmony_ci break; 19788c2ecf20Sopenharmony_ci case NETDEV_CHANGE: 19798c2ecf20Sopenharmony_ci if (!(dev_get_flags(dev) & (IFF_RUNNING | IFF_LOWER_UP))) 19808c2ecf20Sopenharmony_ci nexthop_flush_dev(dev, event); 19818c2ecf20Sopenharmony_ci break; 19828c2ecf20Sopenharmony_ci case NETDEV_CHANGEMTU: 19838c2ecf20Sopenharmony_ci info_ext = ptr; 19848c2ecf20Sopenharmony_ci nexthop_sync_mtu(dev, info_ext->ext.mtu); 19858c2ecf20Sopenharmony_ci rt_cache_flush(dev_net(dev)); 19868c2ecf20Sopenharmony_ci break; 19878c2ecf20Sopenharmony_ci } 19888c2ecf20Sopenharmony_ci return NOTIFY_DONE; 19898c2ecf20Sopenharmony_ci} 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_cistatic struct notifier_block nh_netdev_notifier = { 19928c2ecf20Sopenharmony_ci .notifier_call = nh_netdev_event, 19938c2ecf20Sopenharmony_ci}; 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ciint register_nexthop_notifier(struct net *net, struct notifier_block *nb) 19968c2ecf20Sopenharmony_ci{ 19978c2ecf20Sopenharmony_ci return blocking_notifier_chain_register(&net->nexthop.notifier_chain, 19988c2ecf20Sopenharmony_ci nb); 19998c2ecf20Sopenharmony_ci} 20008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(register_nexthop_notifier); 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ciint unregister_nexthop_notifier(struct net *net, struct notifier_block *nb) 20038c2ecf20Sopenharmony_ci{ 20048c2ecf20Sopenharmony_ci return blocking_notifier_chain_unregister(&net->nexthop.notifier_chain, 20058c2ecf20Sopenharmony_ci nb); 20068c2ecf20Sopenharmony_ci} 20078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(unregister_nexthop_notifier); 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_cistatic void __net_exit nexthop_net_exit(struct net *net) 20108c2ecf20Sopenharmony_ci{ 20118c2ecf20Sopenharmony_ci rtnl_lock(); 20128c2ecf20Sopenharmony_ci flush_all_nexthops(net); 20138c2ecf20Sopenharmony_ci rtnl_unlock(); 20148c2ecf20Sopenharmony_ci kfree(net->nexthop.devhash); 20158c2ecf20Sopenharmony_ci} 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_cistatic int __net_init nexthop_net_init(struct net *net) 20188c2ecf20Sopenharmony_ci{ 20198c2ecf20Sopenharmony_ci size_t sz = sizeof(struct hlist_head) * NH_DEV_HASHSIZE; 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci net->nexthop.rb_root = RB_ROOT; 20228c2ecf20Sopenharmony_ci net->nexthop.devhash = kzalloc(sz, GFP_KERNEL); 20238c2ecf20Sopenharmony_ci if (!net->nexthop.devhash) 20248c2ecf20Sopenharmony_ci return -ENOMEM; 20258c2ecf20Sopenharmony_ci BLOCKING_INIT_NOTIFIER_HEAD(&net->nexthop.notifier_chain); 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci return 0; 20288c2ecf20Sopenharmony_ci} 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_cistatic struct pernet_operations nexthop_net_ops = { 20318c2ecf20Sopenharmony_ci .init = nexthop_net_init, 20328c2ecf20Sopenharmony_ci .exit = nexthop_net_exit, 20338c2ecf20Sopenharmony_ci}; 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_cistatic int __init nexthop_init(void) 20368c2ecf20Sopenharmony_ci{ 20378c2ecf20Sopenharmony_ci register_pernet_subsys(&nexthop_net_ops); 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci register_netdevice_notifier(&nh_netdev_notifier); 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_NEWNEXTHOP, rtm_new_nexthop, NULL, 0); 20428c2ecf20Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_DELNEXTHOP, rtm_del_nexthop, NULL, 0); 20438c2ecf20Sopenharmony_ci rtnl_register(PF_UNSPEC, RTM_GETNEXTHOP, rtm_get_nexthop, 20448c2ecf20Sopenharmony_ci rtm_dump_nexthop, 0); 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci rtnl_register(PF_INET, RTM_NEWNEXTHOP, rtm_new_nexthop, NULL, 0); 20478c2ecf20Sopenharmony_ci rtnl_register(PF_INET, RTM_GETNEXTHOP, NULL, rtm_dump_nexthop, 0); 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci rtnl_register(PF_INET6, RTM_NEWNEXTHOP, rtm_new_nexthop, NULL, 0); 20508c2ecf20Sopenharmony_ci rtnl_register(PF_INET6, RTM_GETNEXTHOP, NULL, rtm_dump_nexthop, 0); 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci return 0; 20538c2ecf20Sopenharmony_ci} 20548c2ecf20Sopenharmony_cisubsys_initcall(nexthop_init); 2055