Commit 5fdcce21 authored by William Dauchy's avatar William Dauchy Committed by David S. Miller
Browse files

net, ip6_tunnel: enhance tunnel locate with link check



With ipip, it is possible to create an extra interface explicitly
attached to a given physical interface:

  # ip link show tunl0
  4: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0
  # ip link add tunl1 type ipip dev eth0
  # ip link show tunl1
  6: tunl1@eth0: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0

But it is not possible with ip6tnl:

  # ip link show ip6tnl0
  5: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN mode DEFAULT group default qlen 1000
      link/tunnel6 :: brd ::
  # ip link add ip6tnl1 type ip6tnl dev eth0
  RTNETLINK answers: File exists

This patch aims to make it possible by adding link comparaison in both
tunnel locate and lookup functions; we also modify mtu calculation when
attached to an interface with a lower mtu.

This permits to make use of x-netns communication by moving the newly
created tunnel in a given netns.

Signed-off-by: default avatarWilliam Dauchy <w.dauchy@criteo.com>
Reviewed-by: default avatarNicolas Dichtel <nicolas.dichtel@6wind.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b32cb6fc
Loading
Loading
Loading
Loading
+47 −21
Original line number Diff line number Diff line
@@ -121,6 +121,7 @@ static struct net_device_stats *ip6_get_stats(struct net_device *dev)

/**
 * ip6_tnl_lookup - fetch tunnel matching the end-point addresses
 *   @link: ifindex of underlying interface
 *   @remote: the address of the tunnel exit-point
 *   @local: the address of the tunnel entry-point
 *
@@ -134,37 +135,56 @@ static struct net_device_stats *ip6_get_stats(struct net_device *dev)
	for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))

static struct ip6_tnl *
ip6_tnl_lookup(struct net *net, const struct in6_addr *remote, const struct in6_addr *local)
ip6_tnl_lookup(struct net *net, int link,
	       const struct in6_addr *remote, const struct in6_addr *local)
{
	unsigned int hash = HASH(remote, local);
	struct ip6_tnl *t;
	struct ip6_tnl *t, *cand = NULL;
	struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
	struct in6_addr any;

	for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
		if (ipv6_addr_equal(local, &t->parms.laddr) &&
		    ipv6_addr_equal(remote, &t->parms.raddr) &&
		    (t->dev->flags & IFF_UP))
		if (!ipv6_addr_equal(local, &t->parms.laddr) ||
		    !ipv6_addr_equal(remote, &t->parms.raddr) ||
		    !(t->dev->flags & IFF_UP))
			continue;

		if (link == t->parms.link)
			return t;
		else
			cand = t;
	}

	memset(&any, 0, sizeof(any));
	hash = HASH(&any, local);
	for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
		if (ipv6_addr_equal(local, &t->parms.laddr) &&
		    ipv6_addr_any(&t->parms.raddr) &&
		    (t->dev->flags & IFF_UP))
		if (!ipv6_addr_equal(local, &t->parms.laddr) ||
		    !ipv6_addr_any(&t->parms.raddr) ||
		    !(t->dev->flags & IFF_UP))
			continue;

		if (link == t->parms.link)
			return t;
		else if (!cand)
			cand = t;
	}

	hash = HASH(remote, &any);
	for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
		if (ipv6_addr_equal(remote, &t->parms.raddr) &&
		    ipv6_addr_any(&t->parms.laddr) &&
		    (t->dev->flags & IFF_UP))
		if (!ipv6_addr_equal(remote, &t->parms.raddr) ||
		    !ipv6_addr_any(&t->parms.laddr) ||
		    !(t->dev->flags & IFF_UP))
			continue;

		if (link == t->parms.link)
			return t;
		else if (!cand)
			cand = t;
	}

	if (cand)
		return cand;

	t = rcu_dereference(ip6n->collect_md_tun);
	if (t && t->dev->flags & IFF_UP)
		return t;
@@ -351,7 +371,8 @@ static struct ip6_tnl *ip6_tnl_locate(struct net *net,
	     (t = rtnl_dereference(*tp)) != NULL;
	     tp = &t->next) {
		if (ipv6_addr_equal(local, &t->parms.laddr) &&
		    ipv6_addr_equal(remote, &t->parms.raddr)) {
		    ipv6_addr_equal(remote, &t->parms.raddr) &&
		    p->link == t->parms.link) {
			if (create)
				return ERR_PTR(-EEXIST);

@@ -485,7 +506,7 @@ ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt,
	   processing of the error. */

	rcu_read_lock();
	t = ip6_tnl_lookup(dev_net(skb->dev), &ipv6h->daddr, &ipv6h->saddr);
	t = ip6_tnl_lookup(dev_net(skb->dev), skb->dev->ifindex, &ipv6h->daddr, &ipv6h->saddr);
	if (!t)
		goto out;

@@ -887,7 +908,7 @@ static int ipxip6_rcv(struct sk_buff *skb, u8 ipproto,
	int ret = -1;

	rcu_read_lock();
	t = ip6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr, &ipv6h->daddr);
	t = ip6_tnl_lookup(dev_net(skb->dev), skb->dev->ifindex, &ipv6h->saddr, &ipv6h->daddr);

	if (t) {
		u8 tproto = READ_ONCE(t->parms.proto);
@@ -1420,8 +1441,10 @@ tx_err:
static void ip6_tnl_link_config(struct ip6_tnl *t)
{
	struct net_device *dev = t->dev;
	struct net_device *tdev = NULL;
	struct __ip6_tnl_parm *p = &t->parms;
	struct flowi6 *fl6 = &t->fl.u.ip6;
	unsigned int mtu;
	int t_hlen;

	memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr));
@@ -1457,22 +1480,25 @@ static void ip6_tnl_link_config(struct ip6_tnl *t)
		struct rt6_info *rt = rt6_lookup(t->net,
						 &p->raddr, &p->laddr,
						 p->link, NULL, strict);
		if (rt) {
			tdev = rt->dst.dev;
			ip6_rt_put(rt);
		}

		if (!rt)
			return;
		if (!tdev && p->link)
			tdev = __dev_get_by_index(t->net, p->link);

		if (rt->dst.dev) {
			dev->hard_header_len = rt->dst.dev->hard_header_len +
				t_hlen;
		if (tdev) {
			dev->hard_header_len = tdev->hard_header_len + t_hlen;
			mtu = min_t(unsigned int, tdev->mtu, IP6_MAX_MTU);

			dev->mtu = rt->dst.dev->mtu - t_hlen;
			dev->mtu = mtu - t_hlen;
			if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
				dev->mtu -= 8;

			if (dev->mtu < IPV6_MIN_MTU)
				dev->mtu = IPV6_MIN_MTU;
		}
		ip6_rt_put(rt);
	}
}