Commit 3b1137fe authored by David Ahern's avatar David Ahern Committed by David S. Miller

net: ipv6: Change notifications for multipath add to RTA_MULTIPATH

Change ip6_route_multipath_add to send one notifciation with the full
route encoded with RTA_MULTIPATH instead of a series of individual routes.
This is done by adding a skip_notify flag to the nl_info struct. The
flag is used to skip sending of the notification in the fib code that
actually inserts the route. Once the full route has been added, a
notification is generated with all nexthops.

ip6_route_multipath_add handles 3 use cases: new routes, route replace,
and route append. The multipath notification generated needs to be
consistent with the order of the nexthops and it should be consistent
with the order in a FIB dump which means the route with the first nexthop
needs to be used as the route reference. For the first 2 cases (new and
replace), a reference to the route used to send the notification is
obtained by saving the first route added. For the append case, the last
route added is used to loop back to its first sibling route which is
the first nexthop in the multipath route.
Signed-off-by: default avatarDavid Ahern <dsa@cumulusnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent beb1afac
...@@ -229,6 +229,7 @@ struct nl_info { ...@@ -229,6 +229,7 @@ struct nl_info {
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
struct net *nl_net; struct net *nl_net;
u32 portid; u32 portid;
bool skip_notify;
}; };
int netlink_rcv_skb(struct sk_buff *skb, int netlink_rcv_skb(struct sk_buff *skb,
......
...@@ -881,6 +881,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, ...@@ -881,6 +881,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
*ins = rt; *ins = rt;
rt->rt6i_node = fn; rt->rt6i_node = fn;
atomic_inc(&rt->rt6i_ref); atomic_inc(&rt->rt6i_ref);
if (!info->skip_notify)
inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags); inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
info->nl_net->ipv6.rt6_stats->fib_rt_entries++; info->nl_net->ipv6.rt6_stats->fib_rt_entries++;
...@@ -907,6 +908,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, ...@@ -907,6 +908,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
rt->rt6i_node = fn; rt->rt6i_node = fn;
rt->dst.rt6_next = iter->dst.rt6_next; rt->dst.rt6_next = iter->dst.rt6_next;
atomic_inc(&rt->rt6i_ref); atomic_inc(&rt->rt6i_ref);
if (!info->skip_notify)
inet6_rt_notify(RTM_NEWROUTE, rt, info, NLM_F_REPLACE); inet6_rt_notify(RTM_NEWROUTE, rt, info, NLM_F_REPLACE);
if (!(fn->fn_flags & RTN_RTINFO)) { if (!(fn->fn_flags & RTN_RTINFO)) {
info->nl_net->ipv6.rt6_stats->fib_route_nodes++; info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
......
...@@ -3023,13 +3023,37 @@ static int ip6_route_info_append(struct list_head *rt6_nh_list, ...@@ -3023,13 +3023,37 @@ static int ip6_route_info_append(struct list_head *rt6_nh_list,
return 0; return 0;
} }
static void ip6_route_mpath_notify(struct rt6_info *rt,
struct rt6_info *rt_last,
struct nl_info *info,
__u16 nlflags)
{
/* if this is an APPEND route, then rt points to the first route
* inserted and rt_last points to last route inserted. Userspace
* wants a consistent dump of the route which starts at the first
* nexthop. Since sibling routes are always added at the end of
* the list, find the first sibling of the last route appended
*/
if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->rt6i_nsiblings) {
rt = list_first_entry(&rt_last->rt6i_siblings,
struct rt6_info,
rt6i_siblings);
}
if (rt)
inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
}
static int ip6_route_multipath_add(struct fib6_config *cfg) static int ip6_route_multipath_add(struct fib6_config *cfg)
{ {
struct rt6_info *rt_notif = NULL, *rt_last = NULL;
struct nl_info *info = &cfg->fc_nlinfo;
struct fib6_config r_cfg; struct fib6_config r_cfg;
struct rtnexthop *rtnh; struct rtnexthop *rtnh;
struct rt6_info *rt; struct rt6_info *rt;
struct rt6_nh *err_nh; struct rt6_nh *err_nh;
struct rt6_nh *nh, *nh_safe; struct rt6_nh *nh, *nh_safe;
__u16 nlflags;
int remaining; int remaining;
int attrlen; int attrlen;
int err = 1; int err = 1;
...@@ -3038,6 +3062,10 @@ static int ip6_route_multipath_add(struct fib6_config *cfg) ...@@ -3038,6 +3062,10 @@ static int ip6_route_multipath_add(struct fib6_config *cfg)
(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE)); (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE));
LIST_HEAD(rt6_nh_list); LIST_HEAD(rt6_nh_list);
nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE;
if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND)
nlflags |= NLM_F_APPEND;
remaining = cfg->fc_mp_len; remaining = cfg->fc_mp_len;
rtnh = (struct rtnexthop *)cfg->fc_mp; rtnh = (struct rtnexthop *)cfg->fc_mp;
...@@ -3080,9 +3108,20 @@ static int ip6_route_multipath_add(struct fib6_config *cfg) ...@@ -3080,9 +3108,20 @@ static int ip6_route_multipath_add(struct fib6_config *cfg)
rtnh = rtnh_next(rtnh, &remaining); rtnh = rtnh_next(rtnh, &remaining);
} }
/* for add and replace send one notification with all nexthops.
* Skip the notification in fib6_add_rt2node and send one with
* the full route when done
*/
info->skip_notify = 1;
err_nh = NULL; err_nh = NULL;
list_for_each_entry(nh, &rt6_nh_list, next) { list_for_each_entry(nh, &rt6_nh_list, next) {
err = __ip6_ins_rt(nh->rt6_info, &cfg->fc_nlinfo, &nh->mxc); rt_last = nh->rt6_info;
err = __ip6_ins_rt(nh->rt6_info, info, &nh->mxc);
/* save reference to first route for notification */
if (!rt_notif && !err)
rt_notif = nh->rt6_info;
/* nh->rt6_info is used or freed at this point, reset to NULL*/ /* nh->rt6_info is used or freed at this point, reset to NULL*/
nh->rt6_info = NULL; nh->rt6_info = NULL;
if (err) { if (err) {
...@@ -3104,9 +3143,18 @@ static int ip6_route_multipath_add(struct fib6_config *cfg) ...@@ -3104,9 +3143,18 @@ static int ip6_route_multipath_add(struct fib6_config *cfg)
nhn++; nhn++;
} }
/* success ... tell user about new route */
ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
goto cleanup; goto cleanup;
add_errout: add_errout:
/* send notification for routes that were added so that
* the delete notifications sent by ip6_route_del are
* coherent
*/
if (rt_notif)
ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags);
/* Delete routes that were already added */ /* Delete routes that were already added */
list_for_each_entry(nh, &rt6_nh_list, next) { list_for_each_entry(nh, &rt6_nh_list, next) {
if (err_nh == nh) if (err_nh == nh)
......
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