diff options
Diffstat (limited to 'net/ipv6/ip6_gre.c')
| -rw-r--r-- | net/ipv6/ip6_gre.c | 96 | 
1 files changed, 56 insertions, 40 deletions
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 1ef1fa2b22a..3873181ed85 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -61,9 +61,6 @@ static bool log_ecn_error = true;  module_param(log_ecn_error, bool, 0644);  MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); -#define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK) -#define IPV6_TCLASS_SHIFT 20 -  #define HASH_SIZE_SHIFT  5  #define HASH_SIZE (1 << HASH_SIZE_SHIFT) @@ -75,6 +72,7 @@ struct ip6gre_net {  };  static struct rtnl_link_ops ip6gre_link_ops __read_mostly; +static struct rtnl_link_ops ip6gre_tap_ops __read_mostly;  static int ip6gre_tunnel_init(struct net_device *dev);  static void ip6gre_tunnel_setup(struct net_device *dev);  static void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t); @@ -356,10 +354,10 @@ failed_free:  static void ip6gre_tunnel_uninit(struct net_device *dev)  { -	struct net *net = dev_net(dev); -	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); +	struct ip6_tnl *t = netdev_priv(dev); +	struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id); -	ip6gre_tunnel_unlink(ign, netdev_priv(dev)); +	ip6gre_tunnel_unlink(ign, t);  	dev_put(dev);  } @@ -470,17 +468,7 @@ static int ip6gre_rcv(struct sk_buff *skb)  			goto drop;  		if (flags&GRE_CSUM) { -			switch (skb->ip_summed) { -			case CHECKSUM_COMPLETE: -				csum = csum_fold(skb->csum); -				if (!csum) -					break; -				/* fall through */ -			case CHECKSUM_NONE: -				skb->csum = 0; -				csum = __skb_checksum_complete(skb); -				skb->ip_summed = CHECKSUM_COMPLETE; -			} +			csum = skb_checksum_simple_validate(skb);  			offset += 4;  		}  		if (flags&GRE_KEY) { @@ -499,7 +487,7 @@ static int ip6gre_rcv(struct sk_buff *skb)  					  &ipv6h->saddr, &ipv6h->daddr, key,  					  gre_proto);  	if (tunnel) { -		struct pcpu_tstats *tstats; +		struct pcpu_sw_netstats *tstats;  		if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))  			goto drop; @@ -614,8 +602,8 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,  			 int encap_limit,  			 __u32 *pmtu)  { -	struct net *net = dev_net(dev);  	struct ip6_tnl *tunnel = netdev_priv(dev); +	struct net *net = tunnel->net;  	struct net_device *tdev;    /* Device to other host */  	struct ipv6hdr  *ipv6h;     /* Our new IP header */  	unsigned int max_headroom = 0; /* The extra header space needed */ @@ -846,7 +834,7 @@ static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)  	if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)  		fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_TCLASS_MASK);  	if (t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) -		fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_FLOWLABEL_MASK); +		fl6.flowlabel |= ip6_flowlabel(ipv6h);  	if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)  		fl6.flowi6_mark = skb->mark; @@ -976,12 +964,13 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)  		if (t->parms.o_flags&GRE_SEQ)  			addend += 4;  	} +	t->hlen = addend;  	if (p->flags & IP6_TNL_F_CAP_XMIT) {  		int strict = (ipv6_addr_type(&p->raddr) &  			      (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL)); -		struct rt6_info *rt = rt6_lookup(dev_net(dev), +		struct rt6_info *rt = rt6_lookup(t->net,  						 &p->raddr, &p->laddr,  						 p->link, strict); @@ -1002,8 +991,6 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)  		}  		ip6_rt_put(rt);  	} - -	t->hlen = addend;  }  static int ip6gre_tnl_change(struct ip6_tnl *t, @@ -1067,13 +1054,12 @@ static int ip6gre_tunnel_ioctl(struct net_device *dev,  	int err = 0;  	struct ip6_tnl_parm2 p;  	struct __ip6_tnl_parm p1; -	struct ip6_tnl *t; -	struct net *net = dev_net(dev); +	struct ip6_tnl *t = netdev_priv(dev); +	struct net *net = t->net;  	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);  	switch (cmd) {  	case SIOCGETTUNNEL: -		t = NULL;  		if (dev == ign->fb_tunnel_dev) {  			if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {  				err = -EFAULT; @@ -1081,9 +1067,9 @@ static int ip6gre_tunnel_ioctl(struct net_device *dev,  			}  			ip6gre_tnl_parm_from_user(&p1, &p);  			t = ip6gre_tunnel_locate(net, &p1, 0); +			if (t == NULL) +				t = netdev_priv(dev);  		} -		if (t == NULL) -			t = netdev_priv(dev);  		memset(&p, 0, sizeof(p));  		ip6gre_tnl_parm_to_user(&p, &t->parms);  		if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p))) @@ -1246,13 +1232,13 @@ static void ip6gre_tunnel_setup(struct net_device *dev)  	dev->flags |= IFF_NOARP;  	dev->iflink = 0;  	dev->addr_len = sizeof(struct in6_addr); -	dev->features |= NETIF_F_NETNS_LOCAL;  	dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;  }  static int ip6gre_tunnel_init(struct net_device *dev)  {  	struct ip6_tnl *tunnel; +	int i;  	tunnel = netdev_priv(dev); @@ -1266,10 +1252,17 @@ static int ip6gre_tunnel_init(struct net_device *dev)  	if (ipv6_addr_any(&tunnel->parms.raddr))  		dev->header_ops = &ip6gre_header_ops; -	dev->tstats = alloc_percpu(struct pcpu_tstats); +	dev->tstats = alloc_percpu(struct pcpu_sw_netstats);  	if (!dev->tstats)  		return -ENOMEM; +	for_each_possible_cpu(i) { +		struct pcpu_sw_netstats *ip6gre_tunnel_stats; +		ip6gre_tunnel_stats = per_cpu_ptr(dev->tstats, i); +		u64_stats_init(&ip6gre_tunnel_stats->syncp); +	} + +  	return 0;  } @@ -1293,11 +1286,17 @@ static struct inet6_protocol ip6gre_protocol __read_mostly = {  	.flags       = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,  }; -static void ip6gre_destroy_tunnels(struct ip6gre_net *ign, -	struct list_head *head) +static void ip6gre_destroy_tunnels(struct net *net, struct list_head *head)  { +	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); +	struct net_device *dev, *aux;  	int prio; +	for_each_netdev_safe(net, dev, aux) +		if (dev->rtnl_link_ops == &ip6gre_link_ops || +		    dev->rtnl_link_ops == &ip6gre_tap_ops) +			unregister_netdevice_queue(dev, head); +  	for (prio = 0; prio < 4; prio++) {  		int h;  		for (h = 0; h < HASH_SIZE; h++) { @@ -1306,7 +1305,12 @@ static void ip6gre_destroy_tunnels(struct ip6gre_net *ign,  			t = rtnl_dereference(ign->tunnels[prio][h]);  			while (t != NULL) { -				unregister_netdevice_queue(t->dev, head); +				/* If dev is in the same netns, it has already +				 * been added to the list by the previous loop. +				 */ +				if (!net_eq(dev_net(t->dev), net)) +					unregister_netdevice_queue(t->dev, +								   head);  				t = rtnl_dereference(t->next);  			}  		} @@ -1325,6 +1329,11 @@ static int __net_init ip6gre_init_net(struct net *net)  		goto err_alloc_dev;  	}  	dev_net_set(ign->fb_tunnel_dev, net); +	/* FB netdevice is special: we have one, and only one per netns. +	 * Allowing to move it to another netns is clearly unsafe. +	 */ +	ign->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL; +  	ip6gre_fb_tunnel_init(ign->fb_tunnel_dev);  	ign->fb_tunnel_dev->rtnl_link_ops = &ip6gre_link_ops; @@ -1345,12 +1354,10 @@ err_alloc_dev:  static void __net_exit ip6gre_exit_net(struct net *net)  { -	struct ip6gre_net *ign;  	LIST_HEAD(list); -	ign = net_generic(net, ip6gre_net_id);  	rtnl_lock(); -	ip6gre_destroy_tunnels(ign, &list); +	ip6gre_destroy_tunnels(net, &list);  	unregister_netdevice_many(&list);  	rtnl_unlock();  } @@ -1459,7 +1466,7 @@ static int ip6gre_tap_init(struct net_device *dev)  	ip6gre_tnl_link_config(tunnel, 1); -	dev->tstats = alloc_percpu(struct pcpu_tstats); +	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);  	if (!dev->tstats)  		return -ENOMEM; @@ -1527,15 +1534,14 @@ out:  static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[],  			    struct nlattr *data[])  { -	struct ip6_tnl *t, *nt; -	struct net *net = dev_net(dev); +	struct ip6_tnl *t, *nt = netdev_priv(dev); +	struct net *net = nt->net;  	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);  	struct __ip6_tnl_parm p;  	if (dev == ign->fb_tunnel_dev)  		return -EINVAL; -	nt = netdev_priv(dev);  	ip6gre_netlink_parms(data, &p);  	t = ip6gre_tunnel_locate(net, &p, 0); @@ -1555,6 +1561,15 @@ static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[],  	return 0;  } +static void ip6gre_dellink(struct net_device *dev, struct list_head *head) +{ +	struct net *net = dev_net(dev); +	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id); + +	if (dev != ign->fb_tunnel_dev) +		unregister_netdevice_queue(dev, head); +} +  static size_t ip6gre_get_size(const struct net_device *dev)  {  	return @@ -1632,6 +1647,7 @@ static struct rtnl_link_ops ip6gre_link_ops __read_mostly = {  	.validate	= ip6gre_tunnel_validate,  	.newlink	= ip6gre_newlink,  	.changelink	= ip6gre_changelink, +	.dellink	= ip6gre_dellink,  	.get_size	= ip6gre_get_size,  	.fill_info	= ip6gre_fill_info,  };  | 
