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