Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 53010f99 authored by David Ahern's avatar David Ahern Committed by David S. Miller
Browse files

nexthop: Add support for IPv6 gateways



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: default avatarDavid Ahern <dsahern@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 597cfe4f
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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;
	};
};

+56 −0
Original line number Diff line number Diff line
@@ -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 @@ static int nh_create_ipv4(struct net *net, struct nexthop *nh,
	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");