^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) /* SPDX-License-Identifier: GPL-2.0 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Generic nexthop implementation
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (c) 2017-19 Cumulus Networks
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Copyright (c) 2017-19 David Ahern <dsa@cumulusnetworks.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #ifndef __LINUX_NEXTHOP_H
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #define __LINUX_NEXTHOP_H
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/netdevice.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/notifier.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/route.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <net/ip_fib.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <net/ip6_fib.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <net/netlink.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #define NEXTHOP_VALID_USER_FLAGS RTNH_F_ONLINK
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) struct nexthop;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) struct nh_config {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) u32 nh_id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) u8 nh_family;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) u8 nh_protocol;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) u8 nh_blackhole;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) u8 nh_fdb;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) u32 nh_flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) int nh_ifindex;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) struct net_device *dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) union {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) __be32 ipv4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) struct in6_addr ipv6;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) } gw;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) struct nlattr *nh_grp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) u16 nh_grp_type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) struct nlattr *nh_encap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) u16 nh_encap_type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) u32 nlflags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) struct nl_info nlinfo;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) struct nh_info {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) struct hlist_node dev_hash; /* entry on netns devhash */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) struct nexthop *nh_parent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) u8 family;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) bool reject_nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) bool fdb_nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) union {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) struct fib_nh_common fib_nhc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) struct fib_nh fib_nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) struct fib6_nh fib6_nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) struct nh_grp_entry {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) struct nexthop *nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) u8 weight;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) atomic_t upper_bound;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) struct list_head nh_list;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) struct nexthop *nh_parent; /* nexthop of group with this entry */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) struct nh_group {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) struct nh_group *spare; /* spare group for removals */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) u16 num_nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) bool mpath;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) bool fdb_nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) bool has_v4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) struct nh_grp_entry nh_entries[];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) struct nexthop {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) struct rb_node rb_node; /* entry on netns rbtree */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) struct list_head fi_list; /* v4 entries using nh */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) struct list_head f6i_list; /* v6 entries using nh */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) struct list_head fdb_list; /* fdb entries using this nh */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) struct list_head grp_list; /* nh group entries using this nh */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) struct net *net;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) u32 id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) u8 protocol; /* app managing this nh */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) u8 nh_flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) bool is_group;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) refcount_t refcnt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) struct rcu_head rcu;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) union {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) struct nh_info __rcu *nh_info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) struct nh_group __rcu *nh_grp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) enum nexthop_event_type {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) NEXTHOP_EVENT_DEL
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) int register_nexthop_notifier(struct net *net, struct notifier_block *nb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) int unregister_nexthop_notifier(struct net *net, struct notifier_block *nb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) /* caller is holding rcu or rtnl; no reference taken to nexthop */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) struct nexthop *nexthop_find_by_id(struct net *net, u32 id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) void nexthop_free_rcu(struct rcu_head *head);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) static inline bool nexthop_get(struct nexthop *nh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) return refcount_inc_not_zero(&nh->refcnt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) static inline void nexthop_put(struct nexthop *nh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) if (refcount_dec_and_test(&nh->refcnt))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) call_rcu(&nh->rcu, nexthop_free_rcu);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) static inline bool nexthop_cmp(const struct nexthop *nh1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) const struct nexthop *nh2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) return nh1 == nh2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) static inline bool nexthop_is_fdb(const struct nexthop *nh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) if (nh->is_group) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) const struct nh_group *nh_grp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) nh_grp = rcu_dereference_rtnl(nh->nh_grp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) return nh_grp->fdb_nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) const struct nh_info *nhi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) nhi = rcu_dereference_rtnl(nh->nh_info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) return nhi->fdb_nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) static inline bool nexthop_has_v4(const struct nexthop *nh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) if (nh->is_group) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) struct nh_group *nh_grp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) nh_grp = rcu_dereference_rtnl(nh->nh_grp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) return nh_grp->has_v4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) static inline bool nexthop_is_multipath(const struct nexthop *nh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) if (nh->is_group) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) struct nh_group *nh_grp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) nh_grp = rcu_dereference_rtnl(nh->nh_grp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) return nh_grp->mpath;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) struct nexthop *nexthop_select_path(struct nexthop *nh, int hash);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) static inline unsigned int nexthop_num_path(const struct nexthop *nh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) unsigned int rc = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) if (nh->is_group) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) struct nh_group *nh_grp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) nh_grp = rcu_dereference_rtnl(nh->nh_grp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) if (nh_grp->mpath)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) rc = nh_grp->num_nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) return rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) static inline
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) struct nexthop *nexthop_mpath_select(const struct nh_group *nhg, int nhsel)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) /* for_nexthops macros in fib_semantics.c grabs a pointer to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) * the nexthop before checking nhsel
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) if (nhsel >= nhg->num_nh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) return nhg->nh_entries[nhsel].nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) static inline
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) int nexthop_mpath_fill_node(struct sk_buff *skb, struct nexthop *nh,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) u8 rt_family)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) struct nh_group *nhg = rtnl_dereference(nh->nh_grp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) for (i = 0; i < nhg->num_nh; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) struct nexthop *nhe = nhg->nh_entries[i].nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) struct nh_info *nhi = rcu_dereference_rtnl(nhe->nh_info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) struct fib_nh_common *nhc = &nhi->fib_nhc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) int weight = nhg->nh_entries[i].weight;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) if (fib_add_nexthop(skb, nhc, weight, rt_family, 0) < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) return -EMSGSIZE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) /* called with rcu lock */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) static inline bool nexthop_is_blackhole(const struct nexthop *nh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) const struct nh_info *nhi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) if (nh->is_group) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) struct nh_group *nh_grp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) nh_grp = rcu_dereference_rtnl(nh->nh_grp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) if (nh_grp->num_nh > 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) nh = nh_grp->nh_entries[0].nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) nhi = rcu_dereference_rtnl(nh->nh_info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) return nhi->reject_nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) static inline void nexthop_path_fib_result(struct fib_result *res, int hash)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) struct nh_info *nhi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) struct nexthop *nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) nh = nexthop_select_path(res->fi->nh, hash);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) nhi = rcu_dereference(nh->nh_info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) res->nhc = &nhi->fib_nhc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) /* called with rcu read lock or rtnl held */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) static inline
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) struct fib_nh_common *nexthop_fib_nhc(struct nexthop *nh, int nhsel)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) struct nh_info *nhi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) BUILD_BUG_ON(offsetof(struct fib_nh, nh_common) != 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) BUILD_BUG_ON(offsetof(struct fib6_nh, nh_common) != 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) if (nh->is_group) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) struct nh_group *nh_grp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) nh_grp = rcu_dereference_rtnl(nh->nh_grp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) if (nh_grp->mpath) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) nh = nexthop_mpath_select(nh_grp, nhsel);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) if (!nh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) nhi = rcu_dereference_rtnl(nh->nh_info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) return &nhi->fib_nhc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) /* called from fib_table_lookup with rcu_lock */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) static inline
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) struct fib_nh_common *nexthop_get_nhc_lookup(const struct nexthop *nh,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) int fib_flags,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) const struct flowi4 *flp,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) int *nhsel)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) struct nh_info *nhi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) if (nh->is_group) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) struct nh_group *nhg = rcu_dereference(nh->nh_grp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) for (i = 0; i < nhg->num_nh; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) struct nexthop *nhe = nhg->nh_entries[i].nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) nhi = rcu_dereference(nhe->nh_info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) if (fib_lookup_good_nhc(&nhi->fib_nhc, fib_flags, flp)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) *nhsel = i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) return &nhi->fib_nhc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) nhi = rcu_dereference(nh->nh_info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) if (fib_lookup_good_nhc(&nhi->fib_nhc, fib_flags, flp)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) *nhsel = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) return &nhi->fib_nhc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) static inline bool nexthop_uses_dev(const struct nexthop *nh,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) const struct net_device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) struct nh_info *nhi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) if (nh->is_group) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) struct nh_group *nhg = rcu_dereference(nh->nh_grp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) for (i = 0; i < nhg->num_nh; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) struct nexthop *nhe = nhg->nh_entries[i].nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) nhi = rcu_dereference(nhe->nh_info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) if (nhc_l3mdev_matches_dev(&nhi->fib_nhc, dev))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) return true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) nhi = rcu_dereference(nh->nh_info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) if (nhc_l3mdev_matches_dev(&nhi->fib_nhc, dev))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) return true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) static inline unsigned int fib_info_num_path(const struct fib_info *fi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) if (unlikely(fi->nh))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) return nexthop_num_path(fi->nh);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) return fi->fib_nhs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) int fib_check_nexthop(struct nexthop *nh, u8 scope,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) struct netlink_ext_ack *extack);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) static inline struct fib_nh_common *fib_info_nhc(struct fib_info *fi, int nhsel)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) if (unlikely(fi->nh))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) return nexthop_fib_nhc(fi->nh, nhsel);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) return &fi->fib_nh[nhsel].nh_common;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) /* only used when fib_nh is built into fib_info */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) static inline struct fib_nh *fib_info_nh(struct fib_info *fi, int nhsel)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) WARN_ON(fi->nh);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) return &fi->fib_nh[nhsel];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) * IPv6 variants
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) int fib6_check_nexthop(struct nexthop *nh, struct fib6_config *cfg,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) struct netlink_ext_ack *extack);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) /* Caller should either hold rcu_read_lock(), or RTNL. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) static inline struct fib6_nh *nexthop_fib6_nh(struct nexthop *nh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) struct nh_info *nhi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) if (nh->is_group) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) struct nh_group *nh_grp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) nh_grp = rcu_dereference_rtnl(nh->nh_grp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) nh = nexthop_mpath_select(nh_grp, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) if (!nh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) nhi = rcu_dereference_rtnl(nh->nh_info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) if (nhi->family == AF_INET6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) return &nhi->fib6_nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) /* Variant of nexthop_fib6_nh().
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) * Caller should either hold rcu_read_lock_bh(), or RTNL.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) static inline struct fib6_nh *nexthop_fib6_nh_bh(struct nexthop *nh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) struct nh_info *nhi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) if (nh->is_group) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) struct nh_group *nh_grp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) nh_grp = rcu_dereference_bh_rtnl(nh->nh_grp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) nh = nexthop_mpath_select(nh_grp, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) if (!nh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) nhi = rcu_dereference_bh_rtnl(nh->nh_info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) if (nhi->family == AF_INET6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) return &nhi->fib6_nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) static inline struct net_device *fib6_info_nh_dev(struct fib6_info *f6i)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) struct fib6_nh *fib6_nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) fib6_nh = f6i->nh ? nexthop_fib6_nh(f6i->nh) : f6i->fib6_nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) return fib6_nh->fib_nh_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) static inline void nexthop_path_fib6_result(struct fib6_result *res, int hash)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) struct nexthop *nh = res->f6i->nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) struct nh_info *nhi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) nh = nexthop_select_path(nh, hash);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) nhi = rcu_dereference_rtnl(nh->nh_info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) if (nhi->reject_nh) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) res->fib6_type = RTN_BLACKHOLE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) res->fib6_flags |= RTF_REJECT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) res->nh = nexthop_fib6_nh(nh);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) res->nh = &nhi->fib6_nh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) int nexthop_for_each_fib6_nh(struct nexthop *nh,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) int (*cb)(struct fib6_nh *nh, void *arg),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) void *arg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) static inline int nexthop_get_family(struct nexthop *nh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) struct nh_info *nhi = rcu_dereference_rtnl(nh->nh_info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) return nhi->family;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) static inline
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) struct fib_nh_common *nexthop_fdb_nhc(struct nexthop *nh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) struct nh_info *nhi = rcu_dereference_rtnl(nh->nh_info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) return &nhi->fib_nhc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) static inline struct fib_nh_common *nexthop_path_fdb_result(struct nexthop *nh,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) int hash)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) struct nh_info *nhi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) struct nexthop *nhp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) nhp = nexthop_select_path(nh, hash);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) if (unlikely(!nhp))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) nhi = rcu_dereference(nhp->nh_info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) return &nhi->fib_nhc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) #endif