nexthop: Add support for IPv6 gateways
authorDavid Ahern <dsahern@gmail.com>
Fri, 24 May 2019 21:43:06 +0000 (14:43 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 29 May 2019 04:37:30 +0000 (21:37 -0700)
Handle IPv6 gateway in a nexthop spec. If nh_family is set to AF_INET6,
NHA_GATEWAY is expected to be an IPv6 address. Add ipv6 option to gw in
nh_config to hold the address, add fib6_nh to nh_info to leverage the
ipv6 initialization and cleanup code. Update nh_fill_node to dump the v6
address.

Signed-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/nexthop.h
net/ipv4/nexthop.c

index c0e4b0d92c3987be24b3670a146bc8ccf84d4df9..d188f16c0c4f646dfdc04e7e1cffd46d2032905c 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/netdevice.h>
 #include <linux/types.h>
 #include <net/ip_fib.h>
+#include <net/ip6_fib.h>
 #include <net/netlink.h>
 
 #define NEXTHOP_VALID_USER_FLAGS RTNH_F_ONLINK
@@ -31,6 +32,7 @@ struct nh_config {
 
        union {
                __be32          ipv4;
+               struct in6_addr ipv6;
        } gw;
 
        u32             nlflags;
@@ -47,6 +49,7 @@ struct nh_info {
        union {
                struct fib_nh_common    fib_nhc;
                struct fib_nh           fib_nh;
+               struct fib6_nh          fib6_nh;
        };
 };
 
index 79c7b3461e19f99e4a40e79ab23c9a14834b2402..f2b237a6735cef0fc5beabfe720e86cc9ed5b230 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/nexthop.h>
 #include <linux/rtnetlink.h>
 #include <linux/slab.h>
+#include <net/ipv6_stubs.h>
 #include <net/nexthop.h>
 #include <net/route.h>
 #include <net/sock.h>
@@ -61,6 +62,9 @@ void nexthop_free_rcu(struct rcu_head *head)
        case AF_INET:
                fib_nh_release(nh->net, &nhi->fib_nh);
                break;
+       case AF_INET6:
+               ipv6_stub->fib6_nh_release(&nhi->fib6_nh);
+               break;
        }
        kfree(nhi);
 
@@ -127,6 +131,7 @@ static u32 nh_find_unused_id(struct net *net)
 static int nh_fill_node(struct sk_buff *skb, struct nexthop *nh,
                        int event, u32 portid, u32 seq, unsigned int nlflags)
 {
+       struct fib6_nh *fib6_nh;
        struct fib_nh *fib_nh;
        struct nlmsghdr *nlh;
        struct nh_info *nhi;
@@ -168,6 +173,13 @@ static int nh_fill_node(struct sk_buff *skb, struct nexthop *nh,
                    nla_put_u32(skb, NHA_GATEWAY, fib_nh->fib_nh_gw4))
                        goto nla_put_failure;
                break;
+
+       case AF_INET6:
+               fib6_nh = &nhi->fib6_nh;
+               if (fib6_nh->fib_nh_gw_family &&
+                   nla_put_in6_addr(skb, NHA_GATEWAY, &fib6_nh->fib_nh_gw6))
+                       goto nla_put_failure;
+               break;
        }
 
 out:
@@ -193,6 +205,12 @@ static size_t nh_nlmsg_size(struct nexthop *nh)
                if (nhi->fib_nh.fib_nh_gw_family)
                        sz += nla_total_size(4);  /* NHA_GATEWAY */
                break;
+
+       case AF_INET6:
+               /* NHA_GATEWAY */
+               if (nhi->fib6_nh.fib_nh_gw_family)
+                       sz += nla_total_size(sizeof(const struct in6_addr));
+               break;
        }
 
        return sz;
@@ -374,6 +392,33 @@ out:
        return err;
 }
 
+static int nh_create_ipv6(struct net *net,  struct nexthop *nh,
+                         struct nh_info *nhi, struct nh_config *cfg,
+                         struct netlink_ext_ack *extack)
+{
+       struct fib6_nh *fib6_nh = &nhi->fib6_nh;
+       struct fib6_config fib6_cfg = {
+               .fc_table = l3mdev_fib_table(cfg->dev),
+               .fc_ifindex = cfg->nh_ifindex,
+               .fc_gateway = cfg->gw.ipv6,
+               .fc_flags = cfg->nh_flags,
+       };
+       int err = -EINVAL;
+
+       if (!ipv6_addr_any(&cfg->gw.ipv6))
+               fib6_cfg.fc_flags |= RTF_GATEWAY;
+
+       /* sets nh_dev if successful */
+       err = ipv6_stub->fib6_nh_init(net, fib6_nh, &fib6_cfg, GFP_KERNEL,
+                                     extack);
+       if (err)
+               ipv6_stub->fib6_nh_release(fib6_nh);
+       else
+               nh->nh_flags = fib6_nh->fib_nh_flags;
+
+       return err;
+}
+
 static struct nexthop *nexthop_create(struct net *net, struct nh_config *cfg,
                                      struct netlink_ext_ack *extack)
 {
@@ -407,6 +452,9 @@ static struct nexthop *nexthop_create(struct net *net, struct nh_config *cfg,
        case AF_INET:
                err = nh_create_ipv4(net, nh, nhi, cfg, extack);
                break;
+       case AF_INET6:
+               err = nh_create_ipv6(net, nh, nhi, cfg, extack);
+               break;
        }
 
        if (err) {
@@ -487,6 +535,7 @@ static int rtm_to_nh_config(struct net *net, struct sk_buff *skb,
 
        switch (nhm->nh_family) {
        case AF_INET:
+       case AF_INET6:
                break;
        default:
                NL_SET_ERR_MSG(extack, "Invalid address family");
@@ -556,6 +605,13 @@ static int rtm_to_nh_config(struct net *net, struct sk_buff *skb,
                        }
                        cfg->gw.ipv4 = nla_get_be32(gwa);
                        break;
+               case AF_INET6:
+                       if (nla_len(gwa) != sizeof(struct in6_addr)) {
+                               NL_SET_ERR_MSG(extack, "Invalid gateway");
+                               goto out;
+                       }
+                       cfg->gw.ipv6 = nla_get_in6_addr(gwa);
+                       break;
                default:
                        NL_SET_ERR_MSG(extack,
                                       "Unknown address family for gateway");