162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Generic nexthop implementation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2017-19 Cumulus Networks 662306a36Sopenharmony_ci * Copyright (c) 2017-19 David Ahern <dsa@cumulusnetworks.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#ifndef __LINUX_NEXTHOP_H 1062306a36Sopenharmony_ci#define __LINUX_NEXTHOP_H 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/netdevice.h> 1362306a36Sopenharmony_ci#include <linux/notifier.h> 1462306a36Sopenharmony_ci#include <linux/route.h> 1562306a36Sopenharmony_ci#include <linux/types.h> 1662306a36Sopenharmony_ci#include <net/ip_fib.h> 1762306a36Sopenharmony_ci#include <net/ip6_fib.h> 1862306a36Sopenharmony_ci#include <net/netlink.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define NEXTHOP_VALID_USER_FLAGS RTNH_F_ONLINK 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct nexthop; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct nh_config { 2562306a36Sopenharmony_ci u32 nh_id; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci u8 nh_family; 2862306a36Sopenharmony_ci u8 nh_protocol; 2962306a36Sopenharmony_ci u8 nh_blackhole; 3062306a36Sopenharmony_ci u8 nh_fdb; 3162306a36Sopenharmony_ci u32 nh_flags; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci int nh_ifindex; 3462306a36Sopenharmony_ci struct net_device *dev; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci union { 3762306a36Sopenharmony_ci __be32 ipv4; 3862306a36Sopenharmony_ci struct in6_addr ipv6; 3962306a36Sopenharmony_ci } gw; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci struct nlattr *nh_grp; 4262306a36Sopenharmony_ci u16 nh_grp_type; 4362306a36Sopenharmony_ci u16 nh_grp_res_num_buckets; 4462306a36Sopenharmony_ci unsigned long nh_grp_res_idle_timer; 4562306a36Sopenharmony_ci unsigned long nh_grp_res_unbalanced_timer; 4662306a36Sopenharmony_ci bool nh_grp_res_has_num_buckets; 4762306a36Sopenharmony_ci bool nh_grp_res_has_idle_timer; 4862306a36Sopenharmony_ci bool nh_grp_res_has_unbalanced_timer; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci struct nlattr *nh_encap; 5162306a36Sopenharmony_ci u16 nh_encap_type; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci u32 nlflags; 5462306a36Sopenharmony_ci struct nl_info nlinfo; 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct nh_info { 5862306a36Sopenharmony_ci struct hlist_node dev_hash; /* entry on netns devhash */ 5962306a36Sopenharmony_ci struct nexthop *nh_parent; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci u8 family; 6262306a36Sopenharmony_ci bool reject_nh; 6362306a36Sopenharmony_ci bool fdb_nh; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci union { 6662306a36Sopenharmony_ci struct fib_nh_common fib_nhc; 6762306a36Sopenharmony_ci struct fib_nh fib_nh; 6862306a36Sopenharmony_ci struct fib6_nh fib6_nh; 6962306a36Sopenharmony_ci }; 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistruct nh_res_bucket { 7362306a36Sopenharmony_ci struct nh_grp_entry __rcu *nh_entry; 7462306a36Sopenharmony_ci atomic_long_t used_time; 7562306a36Sopenharmony_ci unsigned long migrated_time; 7662306a36Sopenharmony_ci bool occupied; 7762306a36Sopenharmony_ci u8 nh_flags; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistruct nh_res_table { 8162306a36Sopenharmony_ci struct net *net; 8262306a36Sopenharmony_ci u32 nhg_id; 8362306a36Sopenharmony_ci struct delayed_work upkeep_dw; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* List of NHGEs that have too few buckets ("uw" for underweight). 8662306a36Sopenharmony_ci * Reclaimed buckets will be given to entries in this list. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci struct list_head uw_nh_entries; 8962306a36Sopenharmony_ci unsigned long unbalanced_since; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci u32 idle_timer; 9262306a36Sopenharmony_ci u32 unbalanced_timer; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci u16 num_nh_buckets; 9562306a36Sopenharmony_ci struct nh_res_bucket nh_buckets[]; 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistruct nh_grp_entry { 9962306a36Sopenharmony_ci struct nexthop *nh; 10062306a36Sopenharmony_ci u8 weight; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci union { 10362306a36Sopenharmony_ci struct { 10462306a36Sopenharmony_ci atomic_t upper_bound; 10562306a36Sopenharmony_ci } hthr; 10662306a36Sopenharmony_ci struct { 10762306a36Sopenharmony_ci /* Member on uw_nh_entries. */ 10862306a36Sopenharmony_ci struct list_head uw_nh_entry; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci u16 count_buckets; 11162306a36Sopenharmony_ci u16 wants_buckets; 11262306a36Sopenharmony_ci } res; 11362306a36Sopenharmony_ci }; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci struct list_head nh_list; 11662306a36Sopenharmony_ci struct nexthop *nh_parent; /* nexthop of group with this entry */ 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistruct nh_group { 12062306a36Sopenharmony_ci struct nh_group *spare; /* spare group for removals */ 12162306a36Sopenharmony_ci u16 num_nh; 12262306a36Sopenharmony_ci bool is_multipath; 12362306a36Sopenharmony_ci bool hash_threshold; 12462306a36Sopenharmony_ci bool resilient; 12562306a36Sopenharmony_ci bool fdb_nh; 12662306a36Sopenharmony_ci bool has_v4; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci struct nh_res_table __rcu *res_table; 12962306a36Sopenharmony_ci struct nh_grp_entry nh_entries[]; 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistruct nexthop { 13362306a36Sopenharmony_ci struct rb_node rb_node; /* entry on netns rbtree */ 13462306a36Sopenharmony_ci struct list_head fi_list; /* v4 entries using nh */ 13562306a36Sopenharmony_ci struct list_head f6i_list; /* v6 entries using nh */ 13662306a36Sopenharmony_ci struct list_head fdb_list; /* fdb entries using this nh */ 13762306a36Sopenharmony_ci struct list_head grp_list; /* nh group entries using this nh */ 13862306a36Sopenharmony_ci struct net *net; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci u32 id; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci u8 protocol; /* app managing this nh */ 14362306a36Sopenharmony_ci u8 nh_flags; 14462306a36Sopenharmony_ci bool is_group; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci refcount_t refcnt; 14762306a36Sopenharmony_ci struct rcu_head rcu; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci union { 15062306a36Sopenharmony_ci struct nh_info __rcu *nh_info; 15162306a36Sopenharmony_ci struct nh_group __rcu *nh_grp; 15262306a36Sopenharmony_ci }; 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cienum nexthop_event_type { 15662306a36Sopenharmony_ci NEXTHOP_EVENT_DEL, 15762306a36Sopenharmony_ci NEXTHOP_EVENT_REPLACE, 15862306a36Sopenharmony_ci NEXTHOP_EVENT_RES_TABLE_PRE_REPLACE, 15962306a36Sopenharmony_ci NEXTHOP_EVENT_BUCKET_REPLACE, 16062306a36Sopenharmony_ci}; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cienum nh_notifier_info_type { 16362306a36Sopenharmony_ci NH_NOTIFIER_INFO_TYPE_SINGLE, 16462306a36Sopenharmony_ci NH_NOTIFIER_INFO_TYPE_GRP, 16562306a36Sopenharmony_ci NH_NOTIFIER_INFO_TYPE_RES_TABLE, 16662306a36Sopenharmony_ci NH_NOTIFIER_INFO_TYPE_RES_BUCKET, 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistruct nh_notifier_single_info { 17062306a36Sopenharmony_ci struct net_device *dev; 17162306a36Sopenharmony_ci u8 gw_family; 17262306a36Sopenharmony_ci union { 17362306a36Sopenharmony_ci __be32 ipv4; 17462306a36Sopenharmony_ci struct in6_addr ipv6; 17562306a36Sopenharmony_ci }; 17662306a36Sopenharmony_ci u8 is_reject:1, 17762306a36Sopenharmony_ci is_fdb:1, 17862306a36Sopenharmony_ci has_encap:1; 17962306a36Sopenharmony_ci}; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistruct nh_notifier_grp_entry_info { 18262306a36Sopenharmony_ci u8 weight; 18362306a36Sopenharmony_ci u32 id; 18462306a36Sopenharmony_ci struct nh_notifier_single_info nh; 18562306a36Sopenharmony_ci}; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistruct nh_notifier_grp_info { 18862306a36Sopenharmony_ci u16 num_nh; 18962306a36Sopenharmony_ci bool is_fdb; 19062306a36Sopenharmony_ci struct nh_notifier_grp_entry_info nh_entries[]; 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistruct nh_notifier_res_bucket_info { 19462306a36Sopenharmony_ci u16 bucket_index; 19562306a36Sopenharmony_ci unsigned int idle_timer_ms; 19662306a36Sopenharmony_ci bool force; 19762306a36Sopenharmony_ci struct nh_notifier_single_info old_nh; 19862306a36Sopenharmony_ci struct nh_notifier_single_info new_nh; 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistruct nh_notifier_res_table_info { 20262306a36Sopenharmony_ci u16 num_nh_buckets; 20362306a36Sopenharmony_ci struct nh_notifier_single_info nhs[]; 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistruct nh_notifier_info { 20762306a36Sopenharmony_ci struct net *net; 20862306a36Sopenharmony_ci struct netlink_ext_ack *extack; 20962306a36Sopenharmony_ci u32 id; 21062306a36Sopenharmony_ci enum nh_notifier_info_type type; 21162306a36Sopenharmony_ci union { 21262306a36Sopenharmony_ci struct nh_notifier_single_info *nh; 21362306a36Sopenharmony_ci struct nh_notifier_grp_info *nh_grp; 21462306a36Sopenharmony_ci struct nh_notifier_res_table_info *nh_res_table; 21562306a36Sopenharmony_ci struct nh_notifier_res_bucket_info *nh_res_bucket; 21662306a36Sopenharmony_ci }; 21762306a36Sopenharmony_ci}; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ciint register_nexthop_notifier(struct net *net, struct notifier_block *nb, 22062306a36Sopenharmony_ci struct netlink_ext_ack *extack); 22162306a36Sopenharmony_ciint unregister_nexthop_notifier(struct net *net, struct notifier_block *nb); 22262306a36Sopenharmony_civoid nexthop_set_hw_flags(struct net *net, u32 id, bool offload, bool trap); 22362306a36Sopenharmony_civoid nexthop_bucket_set_hw_flags(struct net *net, u32 id, u16 bucket_index, 22462306a36Sopenharmony_ci bool offload, bool trap); 22562306a36Sopenharmony_civoid nexthop_res_grp_activity_update(struct net *net, u32 id, u16 num_buckets, 22662306a36Sopenharmony_ci unsigned long *activity); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/* caller is holding rcu or rtnl; no reference taken to nexthop */ 22962306a36Sopenharmony_cistruct nexthop *nexthop_find_by_id(struct net *net, u32 id); 23062306a36Sopenharmony_civoid nexthop_free_rcu(struct rcu_head *head); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic inline bool nexthop_get(struct nexthop *nh) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci return refcount_inc_not_zero(&nh->refcnt); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic inline void nexthop_put(struct nexthop *nh) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci if (refcount_dec_and_test(&nh->refcnt)) 24062306a36Sopenharmony_ci call_rcu(&nh->rcu, nexthop_free_rcu); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic inline bool nexthop_cmp(const struct nexthop *nh1, 24462306a36Sopenharmony_ci const struct nexthop *nh2) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci return nh1 == nh2; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic inline bool nexthop_is_fdb(const struct nexthop *nh) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci if (nh->is_group) { 25262306a36Sopenharmony_ci const struct nh_group *nh_grp; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci nh_grp = rcu_dereference_rtnl(nh->nh_grp); 25562306a36Sopenharmony_ci return nh_grp->fdb_nh; 25662306a36Sopenharmony_ci } else { 25762306a36Sopenharmony_ci const struct nh_info *nhi; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci nhi = rcu_dereference_rtnl(nh->nh_info); 26062306a36Sopenharmony_ci return nhi->fdb_nh; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic inline bool nexthop_has_v4(const struct nexthop *nh) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci if (nh->is_group) { 26762306a36Sopenharmony_ci struct nh_group *nh_grp; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci nh_grp = rcu_dereference_rtnl(nh->nh_grp); 27062306a36Sopenharmony_ci return nh_grp->has_v4; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci return false; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic inline bool nexthop_is_multipath(const struct nexthop *nh) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci if (nh->is_group) { 27862306a36Sopenharmony_ci struct nh_group *nh_grp; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci nh_grp = rcu_dereference_rtnl(nh->nh_grp); 28162306a36Sopenharmony_ci return nh_grp->is_multipath; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci return false; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistruct nexthop *nexthop_select_path(struct nexthop *nh, int hash); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic inline unsigned int nexthop_num_path(const struct nexthop *nh) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci unsigned int rc = 1; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (nh->is_group) { 29362306a36Sopenharmony_ci struct nh_group *nh_grp; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci nh_grp = rcu_dereference_rtnl(nh->nh_grp); 29662306a36Sopenharmony_ci if (nh_grp->is_multipath) 29762306a36Sopenharmony_ci rc = nh_grp->num_nh; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return rc; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic inline 30462306a36Sopenharmony_cistruct nexthop *nexthop_mpath_select(const struct nh_group *nhg, int nhsel) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci /* for_nexthops macros in fib_semantics.c grabs a pointer to 30762306a36Sopenharmony_ci * the nexthop before checking nhsel 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_ci if (nhsel >= nhg->num_nh) 31062306a36Sopenharmony_ci return NULL; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return nhg->nh_entries[nhsel].nh; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic inline 31662306a36Sopenharmony_ciint nexthop_mpath_fill_node(struct sk_buff *skb, struct nexthop *nh, 31762306a36Sopenharmony_ci u8 rt_family) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct nh_group *nhg = rtnl_dereference(nh->nh_grp); 32062306a36Sopenharmony_ci int i; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci for (i = 0; i < nhg->num_nh; i++) { 32362306a36Sopenharmony_ci struct nexthop *nhe = nhg->nh_entries[i].nh; 32462306a36Sopenharmony_ci struct nh_info *nhi = rcu_dereference_rtnl(nhe->nh_info); 32562306a36Sopenharmony_ci struct fib_nh_common *nhc = &nhi->fib_nhc; 32662306a36Sopenharmony_ci int weight = nhg->nh_entries[i].weight; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (fib_add_nexthop(skb, nhc, weight, rt_family, 0) < 0) 32962306a36Sopenharmony_ci return -EMSGSIZE; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci/* called with rcu lock */ 33662306a36Sopenharmony_cistatic inline bool nexthop_is_blackhole(const struct nexthop *nh) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci const struct nh_info *nhi; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (nh->is_group) { 34162306a36Sopenharmony_ci struct nh_group *nh_grp; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci nh_grp = rcu_dereference_rtnl(nh->nh_grp); 34462306a36Sopenharmony_ci if (nh_grp->num_nh > 1) 34562306a36Sopenharmony_ci return false; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci nh = nh_grp->nh_entries[0].nh; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci nhi = rcu_dereference_rtnl(nh->nh_info); 35162306a36Sopenharmony_ci return nhi->reject_nh; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic inline void nexthop_path_fib_result(struct fib_result *res, int hash) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct nh_info *nhi; 35762306a36Sopenharmony_ci struct nexthop *nh; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci nh = nexthop_select_path(res->fi->nh, hash); 36062306a36Sopenharmony_ci nhi = rcu_dereference(nh->nh_info); 36162306a36Sopenharmony_ci res->nhc = &nhi->fib_nhc; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci/* called with rcu read lock or rtnl held */ 36562306a36Sopenharmony_cistatic inline 36662306a36Sopenharmony_cistruct fib_nh_common *nexthop_fib_nhc(struct nexthop *nh, int nhsel) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct nh_info *nhi; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct fib_nh, nh_common) != 0); 37162306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct fib6_nh, nh_common) != 0); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (nh->is_group) { 37462306a36Sopenharmony_ci struct nh_group *nh_grp; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci nh_grp = rcu_dereference_rtnl(nh->nh_grp); 37762306a36Sopenharmony_ci if (nh_grp->is_multipath) { 37862306a36Sopenharmony_ci nh = nexthop_mpath_select(nh_grp, nhsel); 37962306a36Sopenharmony_ci if (!nh) 38062306a36Sopenharmony_ci return NULL; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci nhi = rcu_dereference_rtnl(nh->nh_info); 38562306a36Sopenharmony_ci return &nhi->fib_nhc; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci/* called from fib_table_lookup with rcu_lock */ 38962306a36Sopenharmony_cistatic inline 39062306a36Sopenharmony_cistruct fib_nh_common *nexthop_get_nhc_lookup(const struct nexthop *nh, 39162306a36Sopenharmony_ci int fib_flags, 39262306a36Sopenharmony_ci const struct flowi4 *flp, 39362306a36Sopenharmony_ci int *nhsel) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct nh_info *nhi; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (nh->is_group) { 39862306a36Sopenharmony_ci struct nh_group *nhg = rcu_dereference(nh->nh_grp); 39962306a36Sopenharmony_ci int i; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci for (i = 0; i < nhg->num_nh; i++) { 40262306a36Sopenharmony_ci struct nexthop *nhe = nhg->nh_entries[i].nh; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci nhi = rcu_dereference(nhe->nh_info); 40562306a36Sopenharmony_ci if (fib_lookup_good_nhc(&nhi->fib_nhc, fib_flags, flp)) { 40662306a36Sopenharmony_ci *nhsel = i; 40762306a36Sopenharmony_ci return &nhi->fib_nhc; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci } else { 41162306a36Sopenharmony_ci nhi = rcu_dereference(nh->nh_info); 41262306a36Sopenharmony_ci if (fib_lookup_good_nhc(&nhi->fib_nhc, fib_flags, flp)) { 41362306a36Sopenharmony_ci *nhsel = 0; 41462306a36Sopenharmony_ci return &nhi->fib_nhc; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci return NULL; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic inline bool nexthop_uses_dev(const struct nexthop *nh, 42262306a36Sopenharmony_ci const struct net_device *dev) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct nh_info *nhi; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (nh->is_group) { 42762306a36Sopenharmony_ci struct nh_group *nhg = rcu_dereference(nh->nh_grp); 42862306a36Sopenharmony_ci int i; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci for (i = 0; i < nhg->num_nh; i++) { 43162306a36Sopenharmony_ci struct nexthop *nhe = nhg->nh_entries[i].nh; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci nhi = rcu_dereference(nhe->nh_info); 43462306a36Sopenharmony_ci if (nhc_l3mdev_matches_dev(&nhi->fib_nhc, dev)) 43562306a36Sopenharmony_ci return true; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci } else { 43862306a36Sopenharmony_ci nhi = rcu_dereference(nh->nh_info); 43962306a36Sopenharmony_ci if (nhc_l3mdev_matches_dev(&nhi->fib_nhc, dev)) 44062306a36Sopenharmony_ci return true; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return false; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic inline unsigned int fib_info_num_path(const struct fib_info *fi) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci if (unlikely(fi->nh)) 44962306a36Sopenharmony_ci return nexthop_num_path(fi->nh); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return fi->fib_nhs; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ciint fib_check_nexthop(struct nexthop *nh, u8 scope, 45562306a36Sopenharmony_ci struct netlink_ext_ack *extack); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic inline struct fib_nh_common *fib_info_nhc(struct fib_info *fi, int nhsel) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci if (unlikely(fi->nh)) 46062306a36Sopenharmony_ci return nexthop_fib_nhc(fi->nh, nhsel); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return &fi->fib_nh[nhsel].nh_common; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci/* only used when fib_nh is built into fib_info */ 46662306a36Sopenharmony_cistatic inline struct fib_nh *fib_info_nh(struct fib_info *fi, int nhsel) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci WARN_ON(fi->nh); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return &fi->fib_nh[nhsel]; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci/* 47462306a36Sopenharmony_ci * IPv6 variants 47562306a36Sopenharmony_ci */ 47662306a36Sopenharmony_ciint fib6_check_nexthop(struct nexthop *nh, struct fib6_config *cfg, 47762306a36Sopenharmony_ci struct netlink_ext_ack *extack); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci/* Caller should either hold rcu_read_lock(), or RTNL. */ 48062306a36Sopenharmony_cistatic inline struct fib6_nh *nexthop_fib6_nh(struct nexthop *nh) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci struct nh_info *nhi; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (nh->is_group) { 48562306a36Sopenharmony_ci struct nh_group *nh_grp; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci nh_grp = rcu_dereference_rtnl(nh->nh_grp); 48862306a36Sopenharmony_ci nh = nexthop_mpath_select(nh_grp, 0); 48962306a36Sopenharmony_ci if (!nh) 49062306a36Sopenharmony_ci return NULL; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci nhi = rcu_dereference_rtnl(nh->nh_info); 49462306a36Sopenharmony_ci if (nhi->family == AF_INET6) 49562306a36Sopenharmony_ci return &nhi->fib6_nh; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return NULL; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic inline struct net_device *fib6_info_nh_dev(struct fib6_info *f6i) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct fib6_nh *fib6_nh; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci fib6_nh = f6i->nh ? nexthop_fib6_nh(f6i->nh) : f6i->fib6_nh; 50562306a36Sopenharmony_ci return fib6_nh->fib_nh_dev; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic inline void nexthop_path_fib6_result(struct fib6_result *res, int hash) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct nexthop *nh = res->f6i->nh; 51162306a36Sopenharmony_ci struct nh_info *nhi; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci nh = nexthop_select_path(nh, hash); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci nhi = rcu_dereference_rtnl(nh->nh_info); 51662306a36Sopenharmony_ci if (nhi->reject_nh) { 51762306a36Sopenharmony_ci res->fib6_type = RTN_BLACKHOLE; 51862306a36Sopenharmony_ci res->fib6_flags |= RTF_REJECT; 51962306a36Sopenharmony_ci res->nh = nexthop_fib6_nh(nh); 52062306a36Sopenharmony_ci } else { 52162306a36Sopenharmony_ci res->nh = &nhi->fib6_nh; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ciint nexthop_for_each_fib6_nh(struct nexthop *nh, 52662306a36Sopenharmony_ci int (*cb)(struct fib6_nh *nh, void *arg), 52762306a36Sopenharmony_ci void *arg); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic inline int nexthop_get_family(struct nexthop *nh) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct nh_info *nhi = rcu_dereference_rtnl(nh->nh_info); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return nhi->family; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic inline 53762306a36Sopenharmony_cistruct fib_nh_common *nexthop_fdb_nhc(struct nexthop *nh) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci struct nh_info *nhi = rcu_dereference_rtnl(nh->nh_info); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci return &nhi->fib_nhc; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic inline struct fib_nh_common *nexthop_path_fdb_result(struct nexthop *nh, 54562306a36Sopenharmony_ci int hash) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct nh_info *nhi; 54862306a36Sopenharmony_ci struct nexthop *nhp; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci nhp = nexthop_select_path(nh, hash); 55162306a36Sopenharmony_ci if (unlikely(!nhp)) 55262306a36Sopenharmony_ci return NULL; 55362306a36Sopenharmony_ci nhi = rcu_dereference(nhp->nh_info); 55462306a36Sopenharmony_ci return &nhi->fib_nhc; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci#endif 557