Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: Properly update v4 routes with v6 nexthop

When creating a v4 route that uses a v6 nexthop from a nexthop group.
Allow the kernel to properly send the nexthop as v6 via the RTA_VIA
attribute.

Broken behavior:

$ ip nexthop add via fe80::9 dev eth0
$ ip nexthop show
id 1 via fe80::9 dev eth0 scope link
$ ip route add 4.5.6.7/32 nhid 1
$ ip route show
default via 10.0.2.2 dev eth0
4.5.6.7 nhid 1 via 254.128.0.0 dev eth0
10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15
$

Fixed behavior:

$ ip nexthop add via fe80::9 dev eth0
$ ip nexthop show
id 1 via fe80::9 dev eth0 scope link
$ ip route add 4.5.6.7/32 nhid 1
$ ip route show
default via 10.0.2.2 dev eth0
4.5.6.7 nhid 1 via inet6 fe80::9 dev eth0
10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15
$

v2, v3: Addresses code review comments from David Ahern

Fixes: dcb1ecb50edf (“ipv4: Prepare for fib6_nh from a nexthop object”)
Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
Reviewed-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Donald Sharp and committed by
David S. Miller
7bdf4de1 e9752c83

+19 -16
+2 -2
include/net/ip_fib.h
··· 513 513 struct netlink_callback *cb); 514 514 515 515 int fib_nexthop_info(struct sk_buff *skb, const struct fib_nh_common *nh, 516 - unsigned char *flags, bool skip_oif); 516 + u8 rt_family, unsigned char *flags, bool skip_oif); 517 517 int fib_add_nexthop(struct sk_buff *skb, const struct fib_nh_common *nh, 518 - int nh_weight); 518 + int nh_weight, u8 rt_family); 519 519 #endif /* _NET_FIB_H */
+3 -2
include/net/nexthop.h
··· 161 161 } 162 162 163 163 static inline 164 - int nexthop_mpath_fill_node(struct sk_buff *skb, struct nexthop *nh) 164 + int nexthop_mpath_fill_node(struct sk_buff *skb, struct nexthop *nh, 165 + u8 rt_family) 165 166 { 166 167 struct nh_group *nhg = rtnl_dereference(nh->nh_grp); 167 168 int i; ··· 173 172 struct fib_nh_common *nhc = &nhi->fib_nhc; 174 173 int weight = nhg->nh_entries[i].weight; 175 174 176 - if (fib_add_nexthop(skb, nhc, weight) < 0) 175 + if (fib_add_nexthop(skb, nhc, weight, rt_family) < 0) 177 176 return -EMSGSIZE; 178 177 } 179 178
+8 -7
net/ipv4/fib_semantics.c
··· 1582 1582 } 1583 1583 1584 1584 int fib_nexthop_info(struct sk_buff *skb, const struct fib_nh_common *nhc, 1585 - unsigned char *flags, bool skip_oif) 1585 + u8 rt_family, unsigned char *flags, bool skip_oif) 1586 1586 { 1587 1587 if (nhc->nhc_flags & RTNH_F_DEAD) 1588 1588 *flags |= RTNH_F_DEAD; ··· 1613 1613 /* if gateway family does not match nexthop family 1614 1614 * gateway is encoded as RTA_VIA 1615 1615 */ 1616 - if (nhc->nhc_gw_family != nhc->nhc_family) { 1616 + if (rt_family != nhc->nhc_gw_family) { 1617 1617 int alen = sizeof(struct in6_addr); 1618 1618 struct nlattr *nla; 1619 1619 struct rtvia *via; ··· 1654 1654 1655 1655 #if IS_ENABLED(CONFIG_IP_ROUTE_MULTIPATH) || IS_ENABLED(CONFIG_IPV6) 1656 1656 int fib_add_nexthop(struct sk_buff *skb, const struct fib_nh_common *nhc, 1657 - int nh_weight) 1657 + int nh_weight, u8 rt_family) 1658 1658 { 1659 1659 const struct net_device *dev = nhc->nhc_dev; 1660 1660 struct rtnexthop *rtnh; ··· 1667 1667 rtnh->rtnh_hops = nh_weight - 1; 1668 1668 rtnh->rtnh_ifindex = dev ? dev->ifindex : 0; 1669 1669 1670 - if (fib_nexthop_info(skb, nhc, &flags, true) < 0) 1670 + if (fib_nexthop_info(skb, nhc, rt_family, &flags, true) < 0) 1671 1671 goto nla_put_failure; 1672 1672 1673 1673 rtnh->rtnh_flags = flags; ··· 1693 1693 goto nla_put_failure; 1694 1694 1695 1695 if (unlikely(fi->nh)) { 1696 - if (nexthop_mpath_fill_node(skb, fi->nh) < 0) 1696 + if (nexthop_mpath_fill_node(skb, fi->nh, AF_INET) < 0) 1697 1697 goto nla_put_failure; 1698 1698 goto mp_end; 1699 1699 } 1700 1700 1701 1701 for_nexthops(fi) { 1702 - if (fib_add_nexthop(skb, &nh->nh_common, nh->fib_nh_weight) < 0) 1702 + if (fib_add_nexthop(skb, &nh->nh_common, nh->fib_nh_weight, 1703 + AF_INET) < 0) 1703 1704 goto nla_put_failure; 1704 1705 #ifdef CONFIG_IP_ROUTE_CLASSID 1705 1706 if (nh->nh_tclassid && ··· 1776 1775 const struct fib_nh_common *nhc = fib_info_nhc(fi, 0); 1777 1776 unsigned char flags = 0; 1778 1777 1779 - if (fib_nexthop_info(skb, nhc, &flags, false) < 0) 1778 + if (fib_nexthop_info(skb, nhc, AF_INET, &flags, false) < 0) 1780 1779 goto nla_put_failure; 1781 1780 1782 1781 rtm->rtm_flags = flags;
+6 -5
net/ipv6/route.c
··· 5333 5333 if (!mp) 5334 5334 goto nla_put_failure; 5335 5335 5336 - if (nexthop_mpath_fill_node(skb, nh)) 5336 + if (nexthop_mpath_fill_node(skb, nh, AF_INET6)) 5337 5337 goto nla_put_failure; 5338 5338 5339 5339 nla_nest_end(skb, mp); ··· 5341 5341 struct fib6_nh *fib6_nh; 5342 5342 5343 5343 fib6_nh = nexthop_fib6_nh(nh); 5344 - if (fib_nexthop_info(skb, &fib6_nh->nh_common, 5344 + if (fib_nexthop_info(skb, &fib6_nh->nh_common, AF_INET6, 5345 5345 flags, false) < 0) 5346 5346 goto nla_put_failure; 5347 5347 } ··· 5470 5470 goto nla_put_failure; 5471 5471 5472 5472 if (fib_add_nexthop(skb, &rt->fib6_nh->nh_common, 5473 - rt->fib6_nh->fib_nh_weight) < 0) 5473 + rt->fib6_nh->fib_nh_weight, AF_INET6) < 0) 5474 5474 goto nla_put_failure; 5475 5475 5476 5476 list_for_each_entry_safe(sibling, next_sibling, 5477 5477 &rt->fib6_siblings, fib6_siblings) { 5478 5478 if (fib_add_nexthop(skb, &sibling->fib6_nh->nh_common, 5479 - sibling->fib6_nh->fib_nh_weight) < 0) 5479 + sibling->fib6_nh->fib_nh_weight, 5480 + AF_INET6) < 0) 5480 5481 goto nla_put_failure; 5481 5482 } 5482 5483 ··· 5494 5493 5495 5494 rtm->rtm_flags |= nh_flags; 5496 5495 } else { 5497 - if (fib_nexthop_info(skb, &rt->fib6_nh->nh_common, 5496 + if (fib_nexthop_info(skb, &rt->fib6_nh->nh_common, AF_INET6, 5498 5497 &nh_flags, false) < 0) 5499 5498 goto nla_put_failure; 5500 5499