Skip to content

Commit beb1afa

Browse files
David Aherndavem330
authored andcommitted
net: ipv6: Add support to dump multipath routes via RTA_MULTIPATH attribute
IPv6 returns multipath routes as a series of individual routes making their display and handling by userspace different and more complicated than IPv4, putting the burden on the user to see that a route is part of a multipath route and internally creating a multipath route if desired (e.g., libnl does this as of commit 29b71371e764). This patch addresses this difference, allowing multipath routes to be returned using the RTA_MULTIPATH attribute. The end result is that IPv6 multipath routes can be treated and displayed in a format similar to IPv4: $ ip -6 ro ls vrf red 2001:db8:1::/120 dev eth1 proto kernel metric 256 pref medium 2001:db8:2::/120 dev eth2 proto kernel metric 256 pref medium 2001:db8:200::/120 metric 1024 nexthop via 2001:db8:1::2 dev eth1 weight 1 nexthop via 2001:db8:2::2 dev eth2 weight 1 Signed-off-by: David Ahern <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 0ae8133 commit beb1afa

File tree

2 files changed

+105
-17
lines changed

2 files changed

+105
-17
lines changed

net/ipv6/ip6_fib.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,16 @@ static int fib6_dump_node(struct fib6_walker *w)
318318
w->leaf = rt;
319319
return 1;
320320
}
321+
322+
/* Multipath routes are dumped in one route with the
323+
* RTA_MULTIPATH attribute. Jump 'rt' to point to the
324+
* last sibling of this route (no need to dump the
325+
* sibling routes again)
326+
*/
327+
if (rt->rt6i_nsiblings)
328+
rt = list_last_entry(&rt->rt6i_siblings,
329+
struct rt6_info,
330+
rt6i_siblings);
321331
}
322332
w->leaf = NULL;
323333
return 0;

net/ipv6/route.c

Lines changed: 95 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3195,8 +3195,20 @@ static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
31953195
return ip6_route_add(&cfg);
31963196
}
31973197

3198-
static inline size_t rt6_nlmsg_size(struct rt6_info *rt)
3198+
static size_t rt6_nlmsg_size(struct rt6_info *rt)
31993199
{
3200+
int nexthop_len = 0;
3201+
3202+
if (rt->rt6i_nsiblings) {
3203+
nexthop_len = nla_total_size(0) /* RTA_MULTIPATH */
3204+
+ NLA_ALIGN(sizeof(struct rtnexthop))
3205+
+ nla_total_size(16) /* RTA_GATEWAY */
3206+
+ nla_total_size(4) /* RTA_OIF */
3207+
+ lwtunnel_get_encap_size(rt->dst.lwtstate);
3208+
3209+
nexthop_len *= rt->rt6i_nsiblings;
3210+
}
3211+
32003212
return NLMSG_ALIGN(sizeof(struct rtmsg))
32013213
+ nla_total_size(16) /* RTA_SRC */
32023214
+ nla_total_size(16) /* RTA_DST */
@@ -3210,7 +3222,62 @@ static inline size_t rt6_nlmsg_size(struct rt6_info *rt)
32103222
+ nla_total_size(sizeof(struct rta_cacheinfo))
32113223
+ nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
32123224
+ nla_total_size(1) /* RTA_PREF */
3213-
+ lwtunnel_get_encap_size(rt->dst.lwtstate);
3225+
+ lwtunnel_get_encap_size(rt->dst.lwtstate)
3226+
+ nexthop_len;
3227+
}
3228+
3229+
static int rt6_nexthop_info(struct sk_buff *skb, struct rt6_info *rt,
3230+
unsigned int *flags)
3231+
{
3232+
if (!netif_running(rt->dst.dev) || !netif_carrier_ok(rt->dst.dev)) {
3233+
*flags |= RTNH_F_LINKDOWN;
3234+
if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
3235+
*flags |= RTNH_F_DEAD;
3236+
}
3237+
3238+
if (rt->rt6i_flags & RTF_GATEWAY) {
3239+
if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0)
3240+
goto nla_put_failure;
3241+
}
3242+
3243+
if (rt->dst.dev &&
3244+
nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
3245+
goto nla_put_failure;
3246+
3247+
if (rt->dst.lwtstate &&
3248+
lwtunnel_fill_encap(skb, rt->dst.lwtstate) < 0)
3249+
goto nla_put_failure;
3250+
3251+
return 0;
3252+
3253+
nla_put_failure:
3254+
return -EMSGSIZE;
3255+
}
3256+
3257+
static int rt6_add_nexthop(struct sk_buff *skb, struct rt6_info *rt)
3258+
{
3259+
struct rtnexthop *rtnh;
3260+
unsigned int flags = 0;
3261+
3262+
rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh));
3263+
if (!rtnh)
3264+
goto nla_put_failure;
3265+
3266+
rtnh->rtnh_hops = 0;
3267+
rtnh->rtnh_ifindex = rt->dst.dev ? rt->dst.dev->ifindex : 0;
3268+
3269+
if (rt6_nexthop_info(skb, rt, &flags) < 0)
3270+
goto nla_put_failure;
3271+
3272+
rtnh->rtnh_flags = flags;
3273+
3274+
/* length of rtnetlink header + attributes */
3275+
rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *)rtnh;
3276+
3277+
return 0;
3278+
3279+
nla_put_failure:
3280+
return -EMSGSIZE;
32143281
}
32153282

32163283
static int rt6_fill_node(struct net *net,
@@ -3264,11 +3331,6 @@ static int rt6_fill_node(struct net *net,
32643331
else
32653332
rtm->rtm_type = RTN_UNICAST;
32663333
rtm->rtm_flags = 0;
3267-
if (!netif_running(rt->dst.dev) || !netif_carrier_ok(rt->dst.dev)) {
3268-
rtm->rtm_flags |= RTNH_F_LINKDOWN;
3269-
if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
3270-
rtm->rtm_flags |= RTNH_F_DEAD;
3271-
}
32723334
rtm->rtm_scope = RT_SCOPE_UNIVERSE;
32733335
rtm->rtm_protocol = rt->rt6i_protocol;
32743336
if (rt->rt6i_flags & RTF_DYNAMIC)
@@ -3332,17 +3394,35 @@ static int rt6_fill_node(struct net *net,
33323394
if (rtnetlink_put_metrics(skb, metrics) < 0)
33333395
goto nla_put_failure;
33343396

3335-
if (rt->rt6i_flags & RTF_GATEWAY) {
3336-
if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0)
3337-
goto nla_put_failure;
3338-
}
3339-
3340-
if (rt->dst.dev &&
3341-
nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
3342-
goto nla_put_failure;
33433397
if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
33443398
goto nla_put_failure;
33453399

3400+
/* For multipath routes, walk the siblings list and add
3401+
* each as a nexthop within RTA_MULTIPATH.
3402+
*/
3403+
if (rt->rt6i_nsiblings) {
3404+
struct rt6_info *sibling, *next_sibling;
3405+
struct nlattr *mp;
3406+
3407+
mp = nla_nest_start(skb, RTA_MULTIPATH);
3408+
if (!mp)
3409+
goto nla_put_failure;
3410+
3411+
if (rt6_add_nexthop(skb, rt) < 0)
3412+
goto nla_put_failure;
3413+
3414+
list_for_each_entry_safe(sibling, next_sibling,
3415+
&rt->rt6i_siblings, rt6i_siblings) {
3416+
if (rt6_add_nexthop(skb, sibling) < 0)
3417+
goto nla_put_failure;
3418+
}
3419+
3420+
nla_nest_end(skb, mp);
3421+
} else {
3422+
if (rt6_nexthop_info(skb, rt, &rtm->rtm_flags) < 0)
3423+
goto nla_put_failure;
3424+
}
3425+
33463426
expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
33473427

33483428
if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
@@ -3351,8 +3431,6 @@ static int rt6_fill_node(struct net *net,
33513431
if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
33523432
goto nla_put_failure;
33533433

3354-
if (lwtunnel_fill_encap(skb, rt->dst.lwtstate) < 0)
3355-
goto nla_put_failure;
33563434

33573435
nlmsg_end(skb, nlh);
33583436
return 0;

0 commit comments

Comments
 (0)