Commit 430a0491 authored by David Ahern's avatar David Ahern Committed by David S. Miller

nexthop: Add support for nexthop groups

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: default avatarDavid Ahern <dsahern@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b513bd03
...@@ -35,6 +35,9 @@ struct nh_config { ...@@ -35,6 +35,9 @@ struct nh_config {
struct in6_addr ipv6; struct in6_addr ipv6;
} gw; } gw;
struct nlattr *nh_grp;
u16 nh_grp_type;
struct nlattr *nh_encap; struct nlattr *nh_encap;
u16 nh_encap_type; u16 nh_encap_type;
...@@ -56,20 +59,39 @@ struct nh_info { ...@@ -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 nexthop {
struct rb_node rb_node; /* entry on netns rbtree */ struct rb_node rb_node; /* entry on netns rbtree */
struct list_head grp_list; /* nh group entries using this nh */
struct net *net; struct net *net;
u32 id; u32 id;
u8 protocol; /* app managing this nh */ u8 protocol; /* app managing this nh */
u8 nh_flags; u8 nh_flags;
bool is_group;
refcount_t refcnt; refcount_t refcnt;
struct rcu_head rcu; struct rcu_head rcu;
union { union {
struct nh_info __rcu *nh_info; struct nh_info __rcu *nh_info;
struct nh_group __rcu *nh_grp;
}; };
}; };
...@@ -88,12 +110,86 @@ static inline void nexthop_put(struct nexthop *nh) ...@@ -88,12 +110,86 @@ static inline void nexthop_put(struct nexthop *nh)
call_rcu(&nh->rcu, nexthop_free_rcu); 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 */ /* called with rcu lock */
static inline bool nexthop_is_blackhole(const struct nexthop *nh) static inline bool nexthop_is_blackhole(const struct nexthop *nh)
{ {
const struct nh_info *nhi; 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; return nhi->reject_nh;
} }
#endif #endif
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment