1 /* SPDX-License-Identifier: GPL-2.0 */ 2 /* 3 * Generic nexthop implementation 4 * 5 * Copyright (c) 2017-19 Cumulus Networks 6 * Copyright (c) 2017-19 David Ahern <dsa@cumulusnetworks.com> 7 */ 8 9 #ifndef __LINUX_NEXTHOP_H 10 #define __LINUX_NEXTHOP_H 11 12 #include <linux/netdevice.h> 13 #include <linux/types.h> 14 #include <net/ip_fib.h> 15 #include <net/ip6_fib.h> 16 #include <net/netlink.h> 17 18 #define NEXTHOP_VALID_USER_FLAGS RTNH_F_ONLINK 19 20 struct nexthop; 21 22 struct nh_config { 23 u32 nh_id; 24 25 u8 nh_family; 26 u8 nh_protocol; 27 u8 nh_blackhole; 28 u32 nh_flags; 29 30 int nh_ifindex; 31 struct net_device *dev; 32 33 union { 34 __be32 ipv4; 35 struct in6_addr ipv6; 36 } gw; 37 38 struct nlattr *nh_grp; 39 u16 nh_grp_type; 40 41 struct nlattr *nh_encap; 42 u16 nh_encap_type; 43 44 u32 nlflags; 45 struct nl_info nlinfo; 46 }; 47 48 struct nh_info { 49 struct hlist_node dev_hash; /* entry on netns devhash */ 50 struct nexthop *nh_parent; 51 52 u8 family; 53 bool reject_nh; 54 55 union { 56 struct fib_nh_common fib_nhc; 57 struct fib_nh fib_nh; 58 struct fib6_nh fib6_nh; 59 }; 60 }; 61 62 struct nh_grp_entry { 63 struct nexthop *nh; 64 u8 weight; 65 atomic_t upper_bound; 66 67 struct list_head nh_list; 68 struct nexthop *nh_parent; /* nexthop of group with this entry */ 69 }; 70 71 struct nh_group { 72 u16 num_nh; 73 bool mpath; 74 bool has_v4; 75 struct nh_grp_entry nh_entries[0]; 76 }; 77 78 struct nexthop { 79 struct rb_node rb_node; /* entry on netns rbtree */ 80 struct list_head grp_list; /* nh group entries using this nh */ 81 struct net *net; 82 83 u32 id; 84 85 u8 protocol; /* app managing this nh */ 86 u8 nh_flags; 87 bool is_group; 88 89 refcount_t refcnt; 90 struct rcu_head rcu; 91 92 union { 93 struct nh_info __rcu *nh_info; 94 struct nh_group __rcu *nh_grp; 95 }; 96 }; 97 98 /* caller is holding rcu or rtnl; no reference taken to nexthop */ 99 struct nexthop *nexthop_find_by_id(struct net *net, u32 id); 100 void nexthop_free_rcu(struct rcu_head *head); 101 102 static inline bool nexthop_get(struct nexthop *nh) 103 { 104 return refcount_inc_not_zero(&nh->refcnt); 105 } 106 107 static inline void nexthop_put(struct nexthop *nh) 108 { 109 if (refcount_dec_and_test(&nh->refcnt)) 110 call_rcu(&nh->rcu, nexthop_free_rcu); 111 } 112 113 static inline bool nexthop_is_multipath(const struct nexthop *nh) 114 { 115 if (nh->is_group) { 116 struct nh_group *nh_grp; 117 118 nh_grp = rcu_dereference_rtnl(nh->nh_grp); 119 return nh_grp->mpath; 120 } 121 return false; 122 } 123 124 struct nexthop *nexthop_select_path(struct nexthop *nh, int hash); 125 126 static inline unsigned int nexthop_num_path(const struct nexthop *nh) 127 { 128 unsigned int rc = 1; 129 130 if (nexthop_is_multipath(nh)) { 131 struct nh_group *nh_grp; 132 133 nh_grp = rcu_dereference_rtnl(nh->nh_grp); 134 rc = nh_grp->num_nh; 135 } else { 136 const struct nh_info *nhi; 137 138 nhi = rcu_dereference_rtnl(nh->nh_info); 139 if (nhi->reject_nh) 140 rc = 0; 141 } 142 143 return rc; 144 } 145 146 static inline 147 struct nexthop *nexthop_mpath_select(const struct nexthop *nh, int nhsel) 148 { 149 const struct nh_group *nhg = rcu_dereference_rtnl(nh->nh_grp); 150 151 /* for_nexthops macros in fib_semantics.c grabs a pointer to 152 * the nexthop before checking nhsel 153 */ 154 if (nhsel > nhg->num_nh) 155 return NULL; 156 157 return nhg->nh_entries[nhsel].nh; 158 } 159 160 static inline 161 int nexthop_mpath_fill_node(struct sk_buff *skb, struct nexthop *nh) 162 { 163 struct nh_group *nhg = rtnl_dereference(nh->nh_grp); 164 int i; 165 166 for (i = 0; i < nhg->num_nh; i++) { 167 struct nexthop *nhe = nhg->nh_entries[i].nh; 168 struct nh_info *nhi = rcu_dereference_rtnl(nhe->nh_info); 169 struct fib_nh_common *nhc = &nhi->fib_nhc; 170 int weight = nhg->nh_entries[i].weight; 171 172 if (fib_add_nexthop(skb, nhc, weight) < 0) 173 return -EMSGSIZE; 174 } 175 176 return 0; 177 } 178 179 /* called with rcu lock */ 180 static inline bool nexthop_is_blackhole(const struct nexthop *nh) 181 { 182 const struct nh_info *nhi; 183 184 if (nexthop_is_multipath(nh)) { 185 if (nexthop_num_path(nh) > 1) 186 return false; 187 nh = nexthop_mpath_select(nh, 0); 188 if (!nh) 189 return false; 190 } 191 192 nhi = rcu_dereference_rtnl(nh->nh_info); 193 return nhi->reject_nh; 194 } 195 #endif 196