Skip to content

Commit

Permalink
nexthop: Add support for nexthop groups
Browse files Browse the repository at this point in the history
Allow the creation of nexthop groups which reference other nexthop
objects to create multipath routes:

                      +--------------+
   +------------+   +--------------+ |
   | nh  nh_grp --->| nh_grp_entry |-+
   +------------+   +---------|----+
     ^                |       |    +------------+
     +----------------+       +--->| nh, weight |
        nh_parent                  +------------+

A group entry points to a nexthop with a weight for that hop within the
group. The nexthop has a list_head, grp_list, for tracking which groups
it is a member of and the group entry has a reference back to the parent.
The grp_list is used when a nexthop is deleted - to efficiently remove
it from groups using it.

If a nexthop group spec is given, no other attributes can be set. Each
nexthop id in a group spec must already exist.

Similar to single nexthops, the specification of a nexthop group can be
updated so that data is managed with rcu locking.

Add path selection function to account for multiple paths and add
ipv{4,6}_good_nh helpers to know that if a neighbor entry exists it is
in a good state.

Update NETDEV event handling to rebalance multipath nexthop groups if
a nexthop is deleted due to a link event (down or unregister).

When a nexthop is removed any groups using it are updated. Groups using a
nexthop a tracked via a grp_list.

Nexthop dumps can be limited to groups only by adding NHA_GROUPS to the
request.

Signed-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
dsahern authored and davem330 committed May 29, 2019
1 parent b513bd0 commit 430a049
Show file tree
Hide file tree
Showing 2 changed files with 578 additions and 24 deletions.
98 changes: 97 additions & 1 deletion include/net/nexthop.h
Expand Up @@ -35,6 +35,9 @@ struct nh_config {
struct in6_addr ipv6;
} gw;

struct nlattr *nh_grp;
u16 nh_grp_type;

struct nlattr *nh_encap;
u16 nh_encap_type;

Expand All @@ -56,20 +59,39 @@ struct nh_info {
};
};

struct nh_grp_entry {
struct nexthop *nh;
u8 weight;
atomic_t upper_bound;

struct list_head nh_list;
struct nexthop *nh_parent; /* nexthop of group with this entry */
};

struct nh_group {
u16 num_nh;
bool mpath;
bool has_v4;
struct nh_grp_entry nh_entries[0];
};

struct nexthop {
struct rb_node rb_node; /* entry on netns rbtree */
struct list_head grp_list; /* nh group entries using this nh */
struct net *net;

u32 id;

u8 protocol; /* app managing this nh */
u8 nh_flags;
bool is_group;

refcount_t refcnt;
struct rcu_head rcu;

union {
struct nh_info __rcu *nh_info;
struct nh_group __rcu *nh_grp;
};
};

Expand All @@ -88,12 +110,86 @@ static inline void nexthop_put(struct nexthop *nh)
call_rcu(&nh->rcu, nexthop_free_rcu);
}

static inline bool nexthop_is_multipath(const struct nexthop *nh)
{
if (nh->is_group) {
struct nh_group *nh_grp;

nh_grp = rcu_dereference_rtnl(nh->nh_grp);
return nh_grp->mpath;
}
return false;
}

struct nexthop *nexthop_select_path(struct nexthop *nh, int hash);

static inline unsigned int nexthop_num_path(const struct nexthop *nh)
{
unsigned int rc = 1;

if (nexthop_is_multipath(nh)) {
struct nh_group *nh_grp;

nh_grp = rcu_dereference_rtnl(nh->nh_grp);
rc = nh_grp->num_nh;
} else {
const struct nh_info *nhi;

nhi = rcu_dereference_rtnl(nh->nh_info);
if (nhi->reject_nh)
rc = 0;
}

return rc;
}

static inline
struct nexthop *nexthop_mpath_select(const struct nexthop *nh, int nhsel)
{
const struct nh_group *nhg = rcu_dereference_rtnl(nh->nh_grp);

/* for_nexthops macros in fib_semantics.c grabs a pointer to
* the nexthop before checking nhsel
*/
if (nhsel > nhg->num_nh)
return NULL;

return nhg->nh_entries[nhsel].nh;
}

static inline
int nexthop_mpath_fill_node(struct sk_buff *skb, struct nexthop *nh)
{
struct nh_group *nhg = rtnl_dereference(nh->nh_grp);
int i;

for (i = 0; i < nhg->num_nh; i++) {
struct nexthop *nhe = nhg->nh_entries[i].nh;
struct nh_info *nhi = rcu_dereference_rtnl(nhe->nh_info);
struct fib_nh_common *nhc = &nhi->fib_nhc;
int weight = nhg->nh_entries[i].weight;

if (fib_add_nexthop(skb, nhc, weight) < 0)
return -EMSGSIZE;
}

return 0;
}

/* called with rcu lock */
static inline bool nexthop_is_blackhole(const struct nexthop *nh)
{
const struct nh_info *nhi;

nhi = rcu_dereference(nh->nh_info);
if (nexthop_is_multipath(nh)) {
if (nexthop_num_path(nh) > 1)
return false;
nh = nexthop_mpath_select(nh, 0);
if (!nh)
return false;
}

nhi = rcu_dereference_rtnl(nh->nh_info);
return nhi->reject_nh;
}
#endif

0 comments on commit 430a049

Please sign in to comment.