|
@@ -3195,8 +3195,20 @@ static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
|
|
return ip6_route_add(&cfg);
|
|
return ip6_route_add(&cfg);
|
|
}
|
|
}
|
|
|
|
|
|
-static inline size_t rt6_nlmsg_size(struct rt6_info *rt)
|
|
|
|
|
|
+static size_t rt6_nlmsg_size(struct rt6_info *rt)
|
|
{
|
|
{
|
|
|
|
+ int nexthop_len = 0;
|
|
|
|
+
|
|
|
|
+ if (rt->rt6i_nsiblings) {
|
|
|
|
+ nexthop_len = nla_total_size(0) /* RTA_MULTIPATH */
|
|
|
|
+ + NLA_ALIGN(sizeof(struct rtnexthop))
|
|
|
|
+ + nla_total_size(16) /* RTA_GATEWAY */
|
|
|
|
+ + nla_total_size(4) /* RTA_OIF */
|
|
|
|
+ + lwtunnel_get_encap_size(rt->dst.lwtstate);
|
|
|
|
+
|
|
|
|
+ nexthop_len *= rt->rt6i_nsiblings;
|
|
|
|
+ }
|
|
|
|
+
|
|
return NLMSG_ALIGN(sizeof(struct rtmsg))
|
|
return NLMSG_ALIGN(sizeof(struct rtmsg))
|
|
+ nla_total_size(16) /* RTA_SRC */
|
|
+ nla_total_size(16) /* RTA_SRC */
|
|
+ nla_total_size(16) /* RTA_DST */
|
|
+ nla_total_size(16) /* RTA_DST */
|
|
@@ -3210,7 +3222,62 @@ static inline size_t rt6_nlmsg_size(struct rt6_info *rt)
|
|
+ nla_total_size(sizeof(struct rta_cacheinfo))
|
|
+ nla_total_size(sizeof(struct rta_cacheinfo))
|
|
+ nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
|
|
+ nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
|
|
+ nla_total_size(1) /* RTA_PREF */
|
|
+ nla_total_size(1) /* RTA_PREF */
|
|
- + lwtunnel_get_encap_size(rt->dst.lwtstate);
|
|
|
|
|
|
+ + lwtunnel_get_encap_size(rt->dst.lwtstate)
|
|
|
|
+ + nexthop_len;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int rt6_nexthop_info(struct sk_buff *skb, struct rt6_info *rt,
|
|
|
|
+ unsigned int *flags)
|
|
|
|
+{
|
|
|
|
+ if (!netif_running(rt->dst.dev) || !netif_carrier_ok(rt->dst.dev)) {
|
|
|
|
+ *flags |= RTNH_F_LINKDOWN;
|
|
|
|
+ if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
|
|
|
|
+ *flags |= RTNH_F_DEAD;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (rt->rt6i_flags & RTF_GATEWAY) {
|
|
|
|
+ if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0)
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (rt->dst.dev &&
|
|
|
|
+ nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+
|
|
|
|
+ if (rt->dst.lwtstate &&
|
|
|
|
+ lwtunnel_fill_encap(skb, rt->dst.lwtstate) < 0)
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+nla_put_failure:
|
|
|
|
+ return -EMSGSIZE;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int rt6_add_nexthop(struct sk_buff *skb, struct rt6_info *rt)
|
|
|
|
+{
|
|
|
|
+ struct rtnexthop *rtnh;
|
|
|
|
+ unsigned int flags = 0;
|
|
|
|
+
|
|
|
|
+ rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh));
|
|
|
|
+ if (!rtnh)
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+
|
|
|
|
+ rtnh->rtnh_hops = 0;
|
|
|
|
+ rtnh->rtnh_ifindex = rt->dst.dev ? rt->dst.dev->ifindex : 0;
|
|
|
|
+
|
|
|
|
+ if (rt6_nexthop_info(skb, rt, &flags) < 0)
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+
|
|
|
|
+ rtnh->rtnh_flags = flags;
|
|
|
|
+
|
|
|
|
+ /* length of rtnetlink header + attributes */
|
|
|
|
+ rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *)rtnh;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+nla_put_failure:
|
|
|
|
+ return -EMSGSIZE;
|
|
}
|
|
}
|
|
|
|
|
|
static int rt6_fill_node(struct net *net,
|
|
static int rt6_fill_node(struct net *net,
|
|
@@ -3264,11 +3331,6 @@ static int rt6_fill_node(struct net *net,
|
|
else
|
|
else
|
|
rtm->rtm_type = RTN_UNICAST;
|
|
rtm->rtm_type = RTN_UNICAST;
|
|
rtm->rtm_flags = 0;
|
|
rtm->rtm_flags = 0;
|
|
- if (!netif_running(rt->dst.dev) || !netif_carrier_ok(rt->dst.dev)) {
|
|
|
|
- rtm->rtm_flags |= RTNH_F_LINKDOWN;
|
|
|
|
- if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
|
|
|
|
- rtm->rtm_flags |= RTNH_F_DEAD;
|
|
|
|
- }
|
|
|
|
rtm->rtm_scope = RT_SCOPE_UNIVERSE;
|
|
rtm->rtm_scope = RT_SCOPE_UNIVERSE;
|
|
rtm->rtm_protocol = rt->rt6i_protocol;
|
|
rtm->rtm_protocol = rt->rt6i_protocol;
|
|
if (rt->rt6i_flags & RTF_DYNAMIC)
|
|
if (rt->rt6i_flags & RTF_DYNAMIC)
|
|
@@ -3332,17 +3394,35 @@ static int rt6_fill_node(struct net *net,
|
|
if (rtnetlink_put_metrics(skb, metrics) < 0)
|
|
if (rtnetlink_put_metrics(skb, metrics) < 0)
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
|
|
|
|
- if (rt->rt6i_flags & RTF_GATEWAY) {
|
|
|
|
- if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0)
|
|
|
|
- goto nla_put_failure;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (rt->dst.dev &&
|
|
|
|
- nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
|
|
if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
+ /* For multipath routes, walk the siblings list and add
|
|
|
|
+ * each as a nexthop within RTA_MULTIPATH.
|
|
|
|
+ */
|
|
|
|
+ if (rt->rt6i_nsiblings) {
|
|
|
|
+ struct rt6_info *sibling, *next_sibling;
|
|
|
|
+ struct nlattr *mp;
|
|
|
|
+
|
|
|
|
+ mp = nla_nest_start(skb, RTA_MULTIPATH);
|
|
|
|
+ if (!mp)
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+
|
|
|
|
+ if (rt6_add_nexthop(skb, rt) < 0)
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+
|
|
|
|
+ list_for_each_entry_safe(sibling, next_sibling,
|
|
|
|
+ &rt->rt6i_siblings, rt6i_siblings) {
|
|
|
|
+ if (rt6_add_nexthop(skb, sibling) < 0)
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ nla_nest_end(skb, mp);
|
|
|
|
+ } else {
|
|
|
|
+ if (rt6_nexthop_info(skb, rt, &rtm->rtm_flags) < 0)
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ }
|
|
|
|
+
|
|
expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
|
|
expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0;
|
|
|
|
|
|
if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0)
|
|
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,
|
|
if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
|
|
if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
|
|
|
|
- if (lwtunnel_fill_encap(skb, rt->dst.lwtstate) < 0)
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
|
|
nlmsg_end(skb, nlh);
|
|
nlmsg_end(skb, nlh);
|
|
return 0;
|
|
return 0;
|