1953 lines · 45.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* *	IPv6 over IPv4 tunnel device - Simple Internet Transition (SIT) *	Linux INET6 implementation * *	Authors: *	Pedro Roque		<roque@di.fc.ul.pt> *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru> * *	Changes: * Roger Venning <r.venning@telstra.com>:	6to4 support * Nate Thompson <nate@thebog.net>:		6to4 support * Fred Templin <fred.l.templin@boeing.com>:	isatap support */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h>#include <linux/capability.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/string.h>#include <linux/net.h>#include <linux/in6.h>#include <linux/netdevice.h>#include <linux/if_arp.h>#include <linux/icmp.h>#include <linux/slab.h>#include <linux/uaccess.h>#include <linux/init.h>#include <linux/netfilter_ipv4.h>#include <linux/if_ether.h> #include <net/sock.h>#include <net/snmp.h> #include <net/ipv6.h>#include <net/protocol.h>#include <net/transp_v6.h>#include <net/ip6_fib.h>#include <net/ip6_route.h>#include <net/ndisc.h>#include <net/addrconf.h>#include <net/ip.h>#include <net/udp.h>#include <net/icmp.h>#include <net/ip_tunnels.h>#include <net/inet_ecn.h>#include <net/xfrm.h>#include <net/dsfield.h>#include <net/net_namespace.h>#include <net/netns/generic.h>#include <net/netdev_lock.h>#include <net/inet_dscp.h> /*   This version of net/ipv6/sit.c is cloned of net/ipv4/ip_gre.c    For comments look at net/ipv4/ip_gre.c --ANK */ #define IP6_SIT_HASH_SIZE  16#define HASH(addr) (((__force u32)addr^((__force u32)addr>>4))&0xF) 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"); static int ipip6_tunnel_init(struct net_device *dev);static void ipip6_tunnel_setup(struct net_device *dev);static void ipip6_dev_free(struct net_device *dev);static bool check_6rd(struct ip_tunnel *tunnel, const struct in6_addr *v6dst,		      __be32 *v4dst);static struct rtnl_link_ops sit_link_ops __read_mostly; static unsigned int sit_net_id __read_mostly;struct sit_net {	struct ip_tunnel __rcu *tunnels_r_l[IP6_SIT_HASH_SIZE];	struct ip_tunnel __rcu *tunnels_r[IP6_SIT_HASH_SIZE];	struct ip_tunnel __rcu *tunnels_l[IP6_SIT_HASH_SIZE];	struct ip_tunnel __rcu *tunnels_wc[1];	struct ip_tunnel __rcu **tunnels[4]; 	struct net_device *fb_tunnel_dev;}; static inline struct sit_net *dev_to_sit_net(struct net_device *dev){	struct ip_tunnel *t = netdev_priv(dev); 	return net_generic(t->net, sit_net_id);} /* * Must be invoked with rcu_read_lock */static struct ip_tunnel *ipip6_tunnel_lookup(struct net *net,					     struct net_device *dev,					     __be32 remote, __be32 local,					     int sifindex){	unsigned int h0 = HASH(remote);	unsigned int h1 = HASH(local);	struct ip_tunnel *t;	struct sit_net *sitn = net_generic(net, sit_net_id);	int ifindex = dev ? dev->ifindex : 0; 	for_each_ip_tunnel_rcu(t, sitn->tunnels_r_l[h0 ^ h1]) {		if (local == t->parms.iph.saddr &&		    remote == t->parms.iph.daddr &&		    (!dev || !t->parms.link || ifindex == t->parms.link ||		     sifindex == t->parms.link) &&		    (t->dev->flags & IFF_UP))			return t;	}	for_each_ip_tunnel_rcu(t, sitn->tunnels_r[h0]) {		if (remote == t->parms.iph.daddr &&		    (!dev || !t->parms.link || ifindex == t->parms.link ||		     sifindex == t->parms.link) &&		    (t->dev->flags & IFF_UP))			return t;	}	for_each_ip_tunnel_rcu(t, sitn->tunnels_l[h1]) {		if (local == t->parms.iph.saddr &&		    (!dev || !t->parms.link || ifindex == t->parms.link ||		     sifindex == t->parms.link) &&		    (t->dev->flags & IFF_UP))			return t;	}	t = rcu_dereference(sitn->tunnels_wc[0]);	if (t && (t->dev->flags & IFF_UP))		return t;	return NULL;} static struct ip_tunnel __rcu **__ipip6_bucket(struct sit_net *sitn, struct ip_tunnel_parm_kern *parms){	__be32 remote = parms->iph.daddr;	__be32 local = parms->iph.saddr;	unsigned int h = 0;	int prio = 0; 	if (remote) {		prio |= 2;		h ^= HASH(remote);	}	if (local) {		prio |= 1;		h ^= HASH(local);	}	return &sitn->tunnels[prio][h];} static inline struct ip_tunnel __rcu **ipip6_bucket(struct sit_net *sitn,		struct ip_tunnel *t){	return __ipip6_bucket(sitn, &t->parms);} static void ipip6_tunnel_unlink(struct sit_net *sitn, struct ip_tunnel *t){	struct ip_tunnel __rcu **tp;	struct ip_tunnel *iter; 	for (tp = ipip6_bucket(sitn, t);	     (iter = rtnl_dereference(*tp)) != NULL;	     tp = &iter->next) {		if (t == iter) {			rcu_assign_pointer(*tp, t->next);			break;		}	}} static void ipip6_tunnel_link(struct sit_net *sitn, struct ip_tunnel *t){	struct ip_tunnel __rcu **tp = ipip6_bucket(sitn, t); 	rcu_assign_pointer(t->next, rtnl_dereference(*tp));	rcu_assign_pointer(*tp, t);} static void ipip6_tunnel_clone_6rd(struct net_device *dev, struct sit_net *sitn){#ifdef CONFIG_IPV6_SIT_6RD	struct ip_tunnel *t = netdev_priv(dev); 	if (dev == sitn->fb_tunnel_dev || !sitn->fb_tunnel_dev) {		ipv6_addr_set(&t->ip6rd.prefix, htonl(0x20020000), 0, 0, 0);		t->ip6rd.relay_prefix = 0;		t->ip6rd.prefixlen = 16;		t->ip6rd.relay_prefixlen = 0;	} else {		struct ip_tunnel *t0 = netdev_priv(sitn->fb_tunnel_dev);		memcpy(&t->ip6rd, &t0->ip6rd, sizeof(t->ip6rd));	}#endif} static int ipip6_tunnel_create(struct net_device *dev){	struct ip_tunnel *t = netdev_priv(dev);	struct sit_net *sitn = net_generic(t->net, sit_net_id);	int err; 	__dev_addr_set(dev, &t->parms.iph.saddr, 4);	memcpy(dev->broadcast, &t->parms.iph.daddr, 4); 	if (test_bit(IP_TUNNEL_SIT_ISATAP_BIT, t->parms.i_flags))		dev->priv_flags |= IFF_ISATAP; 	dev->rtnl_link_ops = &sit_link_ops; 	err = register_netdevice(dev);	if (err < 0)		goto out; 	ipip6_tunnel_clone_6rd(dev, sitn); 	ipip6_tunnel_link(sitn, t);	return 0; out:	return err;} static struct ip_tunnel *ipip6_tunnel_locate(struct net *net,					     struct ip_tunnel_parm_kern *parms,					     int create){	__be32 remote = parms->iph.daddr;	__be32 local = parms->iph.saddr;	struct ip_tunnel *t, *nt;	struct ip_tunnel __rcu **tp;	struct net_device *dev;	char name[IFNAMSIZ];	struct sit_net *sitn = net_generic(net, sit_net_id); 	for (tp = __ipip6_bucket(sitn, parms);	    (t = rtnl_dereference(*tp)) != NULL;	     tp = &t->next) {		if (local == t->parms.iph.saddr &&		    remote == t->parms.iph.daddr &&		    parms->link == t->parms.link) {			if (create)				return NULL;			else				return t;		}	}	if (!create)		goto failed; 	if (parms->name[0]) {		if (!dev_valid_name(parms->name))			goto failed;		strscpy(name, parms->name);	} else {		strscpy(name, "sit%d");	}	dev = alloc_netdev(sizeof(*t), name, NET_NAME_UNKNOWN,			   ipip6_tunnel_setup);	if (!dev)		return NULL; 	dev_net_set(dev, net); 	nt = netdev_priv(dev); 	nt->net = net;	nt->parms = *parms;	if (ipip6_tunnel_create(dev) < 0)		goto failed_free; 	if (!parms->name[0])		strscpy(parms->name, dev->name); 	return nt; failed_free:	free_netdev(dev);failed:	return NULL;} #define for_each_prl_rcu(start)			\	for (prl = rcu_dereference(start);	\	     prl;				\	     prl = rcu_dereference(prl->next)) static struct ip_tunnel_prl_entry *__ipip6_tunnel_locate_prl(struct ip_tunnel *t, __be32 addr){	struct ip_tunnel_prl_entry *prl; 	for_each_prl_rcu(t->prl)		if (prl->addr == addr)			break;	return prl; } static int ipip6_tunnel_get_prl(struct net_device *dev, struct ip_tunnel_prl __user *a){	struct ip_tunnel *t = netdev_priv(dev);	struct ip_tunnel_prl kprl, *kp;	struct ip_tunnel_prl_entry *prl;	unsigned int cmax, c = 0, ca, len;	int ret; 	if (dev == dev_to_sit_net(dev)->fb_tunnel_dev)		return -EINVAL; 	if (copy_from_user(&kprl, a, sizeof(kprl)))		return -EFAULT;	cmax = kprl.datalen / sizeof(kprl);	if (cmax > 1 && kprl.addr != htonl(INADDR_ANY))		cmax = 1; 	/* For simple GET or for root users,	 * we try harder to allocate.	 */	kp = (cmax <= 1 || capable(CAP_NET_ADMIN)) ?		kzalloc_objs(*kp, cmax, GFP_KERNEL_ACCOUNT | __GFP_NOWARN) :		NULL; 	ca = min(t->prl_count, cmax); 	if (!kp) {		/* We don't try hard to allocate much memory for		 * non-root users.		 * For root users, retry allocating enough memory for		 * the answer.		 */		kp = kzalloc_objs(*kp, ca,				  GFP_ATOMIC | __GFP_ACCOUNT | __GFP_NOWARN);		if (!kp) {			ret = -ENOMEM;			goto out;		}	} 	rcu_read_lock();	for_each_prl_rcu(t->prl) {		if (c >= cmax)			break;		if (kprl.addr != htonl(INADDR_ANY) && prl->addr != kprl.addr)			continue;		kp[c].addr = prl->addr;		kp[c].flags = prl->flags;		c++;		if (kprl.addr != htonl(INADDR_ANY))			break;	} 	rcu_read_unlock(); 	len = sizeof(*kp) * c;	ret = 0;	if ((len && copy_to_user(a + 1, kp, len)) || put_user(len, &a->datalen))		ret = -EFAULT; 	kfree(kp);out:	return ret;} static intipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg){	struct ip_tunnel_prl_entry *p;	int err = 0; 	if (a->addr == htonl(INADDR_ANY))		return -EINVAL; 	ASSERT_RTNL(); 	for (p = rtnl_dereference(t->prl); p; p = rtnl_dereference(p->next)) {		if (p->addr == a->addr) {			if (chg) {				p->flags = a->flags;				goto out;			}			err = -EEXIST;			goto out;		}	} 	if (chg) {		err = -ENXIO;		goto out;	} 	p = kzalloc_obj(struct ip_tunnel_prl_entry);	if (!p) {		err = -ENOBUFS;		goto out;	} 	p->next = t->prl;	p->addr = a->addr;	p->flags = a->flags;	t->prl_count++;	rcu_assign_pointer(t->prl, p);out:	return err;} static void prl_list_destroy_rcu(struct rcu_head *head){	struct ip_tunnel_prl_entry *p, *n; 	p = container_of(head, struct ip_tunnel_prl_entry, rcu_head);	do {		n = rcu_dereference_protected(p->next, 1);		kfree(p);		p = n;	} while (p);} static intipip6_tunnel_del_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a){	struct ip_tunnel_prl_entry *x;	struct ip_tunnel_prl_entry __rcu **p;	int err = 0; 	ASSERT_RTNL(); 	if (a && a->addr != htonl(INADDR_ANY)) {		for (p = &t->prl;		     (x = rtnl_dereference(*p)) != NULL;		     p = &x->next) {			if (x->addr == a->addr) {				*p = x->next;				kfree_rcu(x, rcu_head);				t->prl_count--;				goto out;			}		}		err = -ENXIO;	} else {		x = rtnl_dereference(t->prl);		if (x) {			t->prl_count = 0;			call_rcu(&x->rcu_head, prl_list_destroy_rcu);			t->prl = NULL;		}	}out:	return err;} static int ipip6_tunnel_prl_ctl(struct net_device *dev,				struct ip_tunnel_prl __user *data, int cmd){	struct ip_tunnel *t = netdev_priv(dev);	struct ip_tunnel_prl prl;	int err; 	if (!ns_capable(t->net->user_ns, CAP_NET_ADMIN))		return -EPERM;	if (dev == dev_to_sit_net(dev)->fb_tunnel_dev)		return -EINVAL; 	if (copy_from_user(&prl, data, sizeof(prl)))		return -EFAULT; 	switch (cmd) {	case SIOCDELPRL:		err = ipip6_tunnel_del_prl(t, &prl);		break;	case SIOCADDPRL:	case SIOCCHGPRL:		err = ipip6_tunnel_add_prl(t, &prl, cmd == SIOCCHGPRL);		break;	}	dst_cache_reset(&t->dst_cache);	netdev_state_change(dev);	return err;} static intisatap_chksrc(struct sk_buff *skb, const struct iphdr *iph, struct ip_tunnel *t){	struct ip_tunnel_prl_entry *p;	int ok = 1; 	rcu_read_lock();	p = __ipip6_tunnel_locate_prl(t, iph->saddr);	if (p) {		if (p->flags & PRL_DEFAULT)			skb->ndisc_nodetype = NDISC_NODETYPE_DEFAULT;		else			skb->ndisc_nodetype = NDISC_NODETYPE_NODEFAULT;	} else {

Lines 1–500 of 1953.