xref: /linux/net/ipv4/ipip.c (revision 1e99584b911cb6f3d2a681e2532d8dc3f9339c9c)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *	Linux NET3:	IP/IP protocol decoder.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *	Authors:
51da177e4SLinus Torvalds  *		Sam Lantinga (slouken@cs.ucdavis.edu)  02/01/95
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *	Fixes:
81da177e4SLinus Torvalds  *		Alan Cox	:	Merged and made usable non modular (its so tiny its silly as
91da177e4SLinus Torvalds  *					a module taking up 2 pages).
101da177e4SLinus Torvalds  *		Alan Cox	: 	Fixed bug with 1.3.18 and IPIP not working (now needs to set skb->h.iph)
111da177e4SLinus Torvalds  *					to keep ip_forward happy.
121da177e4SLinus Torvalds  *		Alan Cox	:	More fixes for 1.3.21, and firewall fix. Maybe this will work soon 8).
131da177e4SLinus Torvalds  *		Kai Schulte	:	Fixed #defines for IP_FIREWALL->FIREWALL
141da177e4SLinus Torvalds  *              David Woodhouse :       Perform some basic ICMP handling.
151da177e4SLinus Torvalds  *                                      IPIP Routing without decapsulation.
161da177e4SLinus Torvalds  *              Carlos Picoto   :       GRE over IP support
171da177e4SLinus Torvalds  *		Alexey Kuznetsov:	Reworked. Really, now it is truncated version of ipv4/ip_gre.c.
181da177e4SLinus Torvalds  *					I do not want to merge them together.
191da177e4SLinus Torvalds  *
201da177e4SLinus Torvalds  *	This program is free software; you can redistribute it and/or
211da177e4SLinus Torvalds  *	modify it under the terms of the GNU General Public License
221da177e4SLinus Torvalds  *	as published by the Free Software Foundation; either version
231da177e4SLinus Torvalds  *	2 of the License, or (at your option) any later version.
241da177e4SLinus Torvalds  *
251da177e4SLinus Torvalds  */
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds /* tunnel.c: an IP tunnel driver
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds 	The purpose of this driver is to provide an IP tunnel through
301da177e4SLinus Torvalds 	which you can tunnel network traffic transparently across subnets.
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds 	This was written by looking at Nick Holloway's dummy driver
331da177e4SLinus Torvalds 	Thanks for the great code!
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds 		-Sam Lantinga	(slouken@cs.ucdavis.edu)  02/01/95
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds 	Minor tweaks:
381da177e4SLinus Torvalds 		Cleaned up the code a little and added some pre-1.3.0 tweaks.
391da177e4SLinus Torvalds 		dev->hard_header/hard_header_len changed to use no headers.
401da177e4SLinus Torvalds 		Comments/bracketing tweaked.
411da177e4SLinus Torvalds 		Made the tunnels use dev->name not tunnel: when error reporting.
421da177e4SLinus Torvalds 		Added tx_dropped stat
431da177e4SLinus Torvalds 
44113aa838SAlan Cox 		-Alan Cox	(alan@lxorguk.ukuu.org.uk) 21 March 95
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds 	Reworked:
471da177e4SLinus Torvalds 		Changed to tunnel to destination gateway in addition to the
481da177e4SLinus Torvalds 			tunnel's pointopoint address
491da177e4SLinus Torvalds 		Almost completely rewritten
501da177e4SLinus Torvalds 		Note:  There is currently no firewall or ICMP handling done.
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds 		-Sam Lantinga	(slouken@cs.ucdavis.edu) 02/13/96
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds */
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds /* Things I wish I had known when writing the tunnel driver:
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds 	When the tunnel_xmit() function is called, the skb contains the
591da177e4SLinus Torvalds 	packet to be sent (plus a great deal of extra info), and dev
601da177e4SLinus Torvalds 	contains the tunnel device that _we_ are.
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds 	When we are passed a packet, we are expected to fill in the
631da177e4SLinus Torvalds 	source address with our source IP address.
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds 	What is the proper way to allocate, copy and free a buffer?
661da177e4SLinus Torvalds 	After you allocate it, it is a "0 length" chunk of memory
671da177e4SLinus Torvalds 	starting at zero.  If you want to add headers to the buffer
681da177e4SLinus Torvalds 	later, you'll have to call "skb_reserve(skb, amount)" with
691da177e4SLinus Torvalds 	the amount of memory you want reserved.  Then, you call
701da177e4SLinus Torvalds 	"skb_put(skb, amount)" with the amount of space you want in
711da177e4SLinus Torvalds 	the buffer.  skb_put() returns a pointer to the top (#0) of
721da177e4SLinus Torvalds 	that buffer.  skb->len is set to the amount of space you have
731da177e4SLinus Torvalds 	"allocated" with skb_put().  You can then write up to skb->len
741da177e4SLinus Torvalds 	bytes to that buffer.  If you need more, you can call skb_put()
751da177e4SLinus Torvalds 	again with the additional amount of space you need.  You can
761da177e4SLinus Torvalds 	find out how much more space you can allocate by calling
771da177e4SLinus Torvalds 	"skb_tailroom(skb)".
781da177e4SLinus Torvalds 	Now, to add header space, call "skb_push(skb, header_len)".
791da177e4SLinus Torvalds 	This creates space at the beginning of the buffer and returns
801da177e4SLinus Torvalds 	a pointer to this new space.  If later you need to strip a
811da177e4SLinus Torvalds 	header from a buffer, call "skb_pull(skb, header_len)".
821da177e4SLinus Torvalds 	skb_headroom() will return how much space is left at the top
831da177e4SLinus Torvalds 	of the buffer (before the main data).  Remember, this headroom
841da177e4SLinus Torvalds 	space must be reserved before the skb_put() function is called.
851da177e4SLinus Torvalds 	*/
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds /*
881da177e4SLinus Torvalds    This version of net/ipv4/ipip.c is cloned of net/ipv4/ip_gre.c
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds    For comments look at net/ipv4/ip_gre.c --ANK
911da177e4SLinus Torvalds  */
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds 
944fc268d2SRandy Dunlap #include <linux/capability.h>
951da177e4SLinus Torvalds #include <linux/module.h>
961da177e4SLinus Torvalds #include <linux/types.h>
971da177e4SLinus Torvalds #include <linux/kernel.h>
985a0e3ad6STejun Heo #include <linux/slab.h>
991da177e4SLinus Torvalds #include <asm/uaccess.h>
1001da177e4SLinus Torvalds #include <linux/skbuff.h>
1011da177e4SLinus Torvalds #include <linux/netdevice.h>
1021da177e4SLinus Torvalds #include <linux/in.h>
1031da177e4SLinus Torvalds #include <linux/tcp.h>
1041da177e4SLinus Torvalds #include <linux/udp.h>
1051da177e4SLinus Torvalds #include <linux/if_arp.h>
1061da177e4SLinus Torvalds #include <linux/mroute.h>
1071da177e4SLinus Torvalds #include <linux/init.h>
1081da177e4SLinus Torvalds #include <linux/netfilter_ipv4.h>
10946f25dffSKris Katterjohn #include <linux/if_ether.h>
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds #include <net/sock.h>
1121da177e4SLinus Torvalds #include <net/ip.h>
1131da177e4SLinus Torvalds #include <net/icmp.h>
114c5441932SPravin B Shelar #include <net/ip_tunnels.h>
1151da177e4SLinus Torvalds #include <net/inet_ecn.h>
1161da177e4SLinus Torvalds #include <net/xfrm.h>
11710dc4c7bSPavel Emelyanov #include <net/net_namespace.h>
11810dc4c7bSPavel Emelyanov #include <net/netns/generic.h>
1191da177e4SLinus Torvalds 
120eccc1bb8Sstephen hemminger static bool log_ecn_error = true;
121eccc1bb8Sstephen hemminger module_param(log_ecn_error, bool, 0644);
122eccc1bb8Sstephen hemminger MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
123eccc1bb8Sstephen hemminger 
124f99189b1SEric Dumazet static int ipip_net_id __read_mostly;
12510dc4c7bSPavel Emelyanov 
1263c97af99SEric Dumazet static int ipip_tunnel_init(struct net_device *dev);
1270974658dSNicolas Dichtel static struct rtnl_link_ops ipip_link_ops __read_mostly;
1281da177e4SLinus Torvalds 
129d2acc347SHerbert Xu static int ipip_err(struct sk_buff *skb, u32 info)
1301da177e4SLinus Torvalds {
1311da177e4SLinus Torvalds 
132071f92d0SRami Rosen /* All the routers (except for Linux) return only
1331da177e4SLinus Torvalds    8 bytes of packet payload. It means, that precise relaying of
1341da177e4SLinus Torvalds    ICMP in the real Internet is absolutely infeasible.
1351da177e4SLinus Torvalds  */
136fd58156eSPravin B Shelar 	struct net *net = dev_net(skb->dev);
137fd58156eSPravin B Shelar 	struct ip_tunnel_net *itn = net_generic(net, ipip_net_id);
138b71d1d42SEric Dumazet 	const struct iphdr *iph = (const struct iphdr *)skb->data;
1391da177e4SLinus Torvalds 	struct ip_tunnel *t;
140d2acc347SHerbert Xu 	int err;
141fd58156eSPravin B Shelar 	const int type = icmp_hdr(skb)->type;
142fd58156eSPravin B Shelar 	const int code = icmp_hdr(skb)->code;
1431da177e4SLinus Torvalds 
144d2acc347SHerbert Xu 	err = -ENOENT;
145fd58156eSPravin B Shelar 	t = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
146fd58156eSPravin B Shelar 			     iph->daddr, iph->saddr, 0);
14736393395SDavid S. Miller 	if (t == NULL)
14836393395SDavid S. Miller 		goto out;
14936393395SDavid S. Miller 
15036393395SDavid S. Miller 	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
15136393395SDavid S. Miller 		ipv4_update_pmtu(skb, dev_net(skb->dev), info,
1522346829eSDmitry Popov 				 t->parms.link, 0, IPPROTO_IPIP, 0);
15336393395SDavid S. Miller 		err = 0;
15436393395SDavid S. Miller 		goto out;
15536393395SDavid S. Miller 	}
15636393395SDavid S. Miller 
15755be7a9cSDavid S. Miller 	if (type == ICMP_REDIRECT) {
1582346829eSDmitry Popov 		ipv4_redirect(skb, dev_net(skb->dev), t->parms.link, 0,
15955be7a9cSDavid S. Miller 			      IPPROTO_IPIP, 0);
16055be7a9cSDavid S. Miller 		err = 0;
16155be7a9cSDavid S. Miller 		goto out;
16255be7a9cSDavid S. Miller 	}
16355be7a9cSDavid S. Miller 
16436393395SDavid S. Miller 	if (t->parms.iph.daddr == 0)
1651da177e4SLinus Torvalds 		goto out;
166d2acc347SHerbert Xu 
167d2acc347SHerbert Xu 	err = 0;
1681da177e4SLinus Torvalds 	if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
1691da177e4SLinus Torvalds 		goto out;
1701da177e4SLinus Torvalds 
17126d94b46SWei Yongjun 	if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO))
1721da177e4SLinus Torvalds 		t->err_count++;
1731da177e4SLinus Torvalds 	else
1741da177e4SLinus Torvalds 		t->err_count = 1;
1751da177e4SLinus Torvalds 	t->err_time = jiffies;
176b0558ef2Sstephen hemminger 
177fd58156eSPravin B Shelar out:
178d2acc347SHerbert Xu 	return err;
1791da177e4SLinus Torvalds }
1801da177e4SLinus Torvalds 
181fd58156eSPravin B Shelar static const struct tnl_ptk_info tpi = {
182fd58156eSPravin B Shelar 	/* no tunnel info required for ipip. */
183fd58156eSPravin B Shelar 	.proto = htons(ETH_P_IP),
184fd58156eSPravin B Shelar };
185fd58156eSPravin B Shelar 
1861da177e4SLinus Torvalds static int ipip_rcv(struct sk_buff *skb)
1871da177e4SLinus Torvalds {
188fd58156eSPravin B Shelar 	struct net *net = dev_net(skb->dev);
189fd58156eSPravin B Shelar 	struct ip_tunnel_net *itn = net_generic(net, ipip_net_id);
1901da177e4SLinus Torvalds 	struct ip_tunnel *tunnel;
1913d7b46cdSPravin B Shelar 	const struct iphdr *iph;
1921da177e4SLinus Torvalds 
1933d7b46cdSPravin B Shelar 	iph = ip_hdr(skb);
194fd58156eSPravin B Shelar 	tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
195fd58156eSPravin B Shelar 			iph->saddr, iph->daddr, 0);
196fd58156eSPravin B Shelar 	if (tunnel) {
197eccc1bb8Sstephen hemminger 		if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
198eccc1bb8Sstephen hemminger 			goto drop;
199737e828bSLi Hongjun 		if (iptunnel_pull_header(skb, 0, tpi.proto))
200737e828bSLi Hongjun 			goto drop;
201fd58156eSPravin B Shelar 		return ip_tunnel_rcv(tunnel, skb, &tpi, log_ecn_error);
2021da177e4SLinus Torvalds 	}
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 	return -1;
205eccc1bb8Sstephen hemminger 
206eccc1bb8Sstephen hemminger drop:
207eccc1bb8Sstephen hemminger 	kfree_skb(skb);
208eccc1bb8Sstephen hemminger 	return 0;
2091da177e4SLinus Torvalds }
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds /*
2121da177e4SLinus Torvalds  *	This function assumes it is being called from dev_queue_xmit()
2131da177e4SLinus Torvalds  *	and that skb is filled properly by that function.
2141da177e4SLinus Torvalds  */
2156fef4c0cSStephen Hemminger static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
2161da177e4SLinus Torvalds {
2172941a486SPatrick McHardy 	struct ip_tunnel *tunnel = netdev_priv(dev);
218b71d1d42SEric Dumazet 	const struct iphdr  *tiph = &tunnel->parms.iph;
2191da177e4SLinus Torvalds 
220fd58156eSPravin B Shelar 	if (unlikely(skb->protocol != htons(ETH_P_IP)))
2211da177e4SLinus Torvalds 		goto tx_error;
222cef401deSEric Dumazet 
223cb32f511SEric Dumazet 	skb = iptunnel_handle_offloads(skb, false, SKB_GSO_IPIP);
224cb32f511SEric Dumazet 	if (IS_ERR(skb))
225cb32f511SEric Dumazet 		goto out;
2264f3ed920SPravin B Shelar 
227077c5a09STom Herbert 	skb_set_inner_ipproto(skb, IPPROTO_IPIP);
228077c5a09STom Herbert 
229bf3d6a8fSNicolas Dichtel 	ip_tunnel_xmit(skb, dev, tiph, tiph->protocol);
2306ed10654SPatrick McHardy 	return NETDEV_TX_OK;
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds tx_error:
2333acfa1e7SEric Dumazet 	kfree_skb(skb);
234cb32f511SEric Dumazet out:
235cb32f511SEric Dumazet 	dev->stats.tx_errors++;
2366ed10654SPatrick McHardy 	return NETDEV_TX_OK;
2371da177e4SLinus Torvalds }
2381da177e4SLinus Torvalds 
2391da177e4SLinus Torvalds static int
2401da177e4SLinus Torvalds ipip_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
2411da177e4SLinus Torvalds {
2421da177e4SLinus Torvalds 	int err = 0;
2431da177e4SLinus Torvalds 	struct ip_tunnel_parm p;
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 	if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
246fd58156eSPravin B Shelar 		return -EFAULT;
2471da177e4SLinus Torvalds 
2483b7b514fSCong Wang 	if (cmd == SIOCADDTUNNEL || cmd == SIOCCHGTUNNEL) {
2491da177e4SLinus Torvalds 		if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPIP ||
2501da177e4SLinus Torvalds 		    p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)))
251fd58156eSPravin B Shelar 			return -EINVAL;
2523b7b514fSCong Wang 	}
2533b7b514fSCong Wang 
2543b7b514fSCong Wang 	p.i_key = p.o_key = p.i_flags = p.o_flags = 0;
2551da177e4SLinus Torvalds 	if (p.iph.ttl)
2561da177e4SLinus Torvalds 		p.iph.frag_off |= htons(IP_DF);
2571da177e4SLinus Torvalds 
258fd58156eSPravin B Shelar 	err = ip_tunnel_ioctl(dev, &p, cmd);
259fd58156eSPravin B Shelar 	if (err)
2601da177e4SLinus Torvalds 		return err;
2611da177e4SLinus Torvalds 
262fd58156eSPravin B Shelar 	if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
263fd58156eSPravin B Shelar 		return -EFAULT;
264fd58156eSPravin B Shelar 
2651da177e4SLinus Torvalds 	return 0;
2661da177e4SLinus Torvalds }
2671da177e4SLinus Torvalds 
26823a12b14SStephen Hemminger static const struct net_device_ops ipip_netdev_ops = {
269fd58156eSPravin B Shelar 	.ndo_init       = ipip_tunnel_init,
270fd58156eSPravin B Shelar 	.ndo_uninit     = ip_tunnel_uninit,
27123a12b14SStephen Hemminger 	.ndo_start_xmit	= ipip_tunnel_xmit,
27223a12b14SStephen Hemminger 	.ndo_do_ioctl	= ipip_tunnel_ioctl,
273fd58156eSPravin B Shelar 	.ndo_change_mtu = ip_tunnel_change_mtu,
274fd58156eSPravin B Shelar 	.ndo_get_stats64 = ip_tunnel_get_stats64,
275*1e99584bSNicolas Dichtel 	.ndo_get_iflink = ip_tunnel_get_iflink,
27623a12b14SStephen Hemminger };
27723a12b14SStephen Hemminger 
278c3b89fbbSEric Dumazet #define IPIP_FEATURES (NETIF_F_SG |		\
279c3b89fbbSEric Dumazet 		       NETIF_F_FRAGLIST |	\
280c3b89fbbSEric Dumazet 		       NETIF_F_HIGHDMA |	\
281cb32f511SEric Dumazet 		       NETIF_F_GSO_SOFTWARE |	\
282c3b89fbbSEric Dumazet 		       NETIF_F_HW_CSUM)
283c3b89fbbSEric Dumazet 
2841da177e4SLinus Torvalds static void ipip_tunnel_setup(struct net_device *dev)
2851da177e4SLinus Torvalds {
28623a12b14SStephen Hemminger 	dev->netdev_ops		= &ipip_netdev_ops;
2871da177e4SLinus Torvalds 
2881da177e4SLinus Torvalds 	dev->type		= ARPHRD_TUNNEL;
2891da177e4SLinus Torvalds 	dev->flags		= IFF_NOARP;
2901da177e4SLinus Torvalds 	dev->addr_len		= 4;
291153f0943SEric Dumazet 	dev->features		|= NETIF_F_LLTX;
29202875878SEric Dumazet 	netif_keep_dst(dev);
293c3b89fbbSEric Dumazet 
294c3b89fbbSEric Dumazet 	dev->features		|= IPIP_FEATURES;
295c3b89fbbSEric Dumazet 	dev->hw_features	|= IPIP_FEATURES;
296fd58156eSPravin B Shelar 	ip_tunnel_setup(dev, ipip_net_id);
2971da177e4SLinus Torvalds }
2981da177e4SLinus Torvalds 
2993c97af99SEric Dumazet static int ipip_tunnel_init(struct net_device *dev)
3001da177e4SLinus Torvalds {
30123a12b14SStephen Hemminger 	struct ip_tunnel *tunnel = netdev_priv(dev);
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds 	memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
3041da177e4SLinus Torvalds 	memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
3051da177e4SLinus Torvalds 
306473ab820STom Herbert 	tunnel->tun_hlen = 0;
307473ab820STom Herbert 	tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen;
308fd58156eSPravin B Shelar 	tunnel->parms.iph.protocol = IPPROTO_IPIP;
309fd58156eSPravin B Shelar 	return ip_tunnel_init(dev);
3101da177e4SLinus Torvalds }
3111da177e4SLinus Torvalds 
312be42da0eSNicolas Dichtel static void ipip_netlink_parms(struct nlattr *data[],
313be42da0eSNicolas Dichtel 			       struct ip_tunnel_parm *parms)
314be42da0eSNicolas Dichtel {
315be42da0eSNicolas Dichtel 	memset(parms, 0, sizeof(*parms));
316be42da0eSNicolas Dichtel 
317be42da0eSNicolas Dichtel 	parms->iph.version = 4;
318be42da0eSNicolas Dichtel 	parms->iph.protocol = IPPROTO_IPIP;
319be42da0eSNicolas Dichtel 	parms->iph.ihl = 5;
320be42da0eSNicolas Dichtel 
321be42da0eSNicolas Dichtel 	if (!data)
322be42da0eSNicolas Dichtel 		return;
323be42da0eSNicolas Dichtel 
324be42da0eSNicolas Dichtel 	if (data[IFLA_IPTUN_LINK])
325be42da0eSNicolas Dichtel 		parms->link = nla_get_u32(data[IFLA_IPTUN_LINK]);
326be42da0eSNicolas Dichtel 
327be42da0eSNicolas Dichtel 	if (data[IFLA_IPTUN_LOCAL])
32867b61f6cSJiri Benc 		parms->iph.saddr = nla_get_in_addr(data[IFLA_IPTUN_LOCAL]);
329be42da0eSNicolas Dichtel 
330be42da0eSNicolas Dichtel 	if (data[IFLA_IPTUN_REMOTE])
33167b61f6cSJiri Benc 		parms->iph.daddr = nla_get_in_addr(data[IFLA_IPTUN_REMOTE]);
332be42da0eSNicolas Dichtel 
333be42da0eSNicolas Dichtel 	if (data[IFLA_IPTUN_TTL]) {
334be42da0eSNicolas Dichtel 		parms->iph.ttl = nla_get_u8(data[IFLA_IPTUN_TTL]);
335be42da0eSNicolas Dichtel 		if (parms->iph.ttl)
336be42da0eSNicolas Dichtel 			parms->iph.frag_off = htons(IP_DF);
337be42da0eSNicolas Dichtel 	}
338be42da0eSNicolas Dichtel 
339be42da0eSNicolas Dichtel 	if (data[IFLA_IPTUN_TOS])
340be42da0eSNicolas Dichtel 		parms->iph.tos = nla_get_u8(data[IFLA_IPTUN_TOS]);
341be42da0eSNicolas Dichtel 
342be42da0eSNicolas Dichtel 	if (!data[IFLA_IPTUN_PMTUDISC] || nla_get_u8(data[IFLA_IPTUN_PMTUDISC]))
343be42da0eSNicolas Dichtel 		parms->iph.frag_off = htons(IP_DF);
344be42da0eSNicolas Dichtel }
345be42da0eSNicolas Dichtel 
346473ab820STom Herbert /* This function returns true when ENCAP attributes are present in the nl msg */
347473ab820STom Herbert static bool ipip_netlink_encap_parms(struct nlattr *data[],
348473ab820STom Herbert 				     struct ip_tunnel_encap *ipencap)
349473ab820STom Herbert {
350473ab820STom Herbert 	bool ret = false;
351473ab820STom Herbert 
352473ab820STom Herbert 	memset(ipencap, 0, sizeof(*ipencap));
353473ab820STom Herbert 
354473ab820STom Herbert 	if (!data)
355473ab820STom Herbert 		return ret;
356473ab820STom Herbert 
357473ab820STom Herbert 	if (data[IFLA_IPTUN_ENCAP_TYPE]) {
358473ab820STom Herbert 		ret = true;
359473ab820STom Herbert 		ipencap->type = nla_get_u16(data[IFLA_IPTUN_ENCAP_TYPE]);
360473ab820STom Herbert 	}
361473ab820STom Herbert 
362473ab820STom Herbert 	if (data[IFLA_IPTUN_ENCAP_FLAGS]) {
363473ab820STom Herbert 		ret = true;
364473ab820STom Herbert 		ipencap->flags = nla_get_u16(data[IFLA_IPTUN_ENCAP_FLAGS]);
365473ab820STom Herbert 	}
366473ab820STom Herbert 
367473ab820STom Herbert 	if (data[IFLA_IPTUN_ENCAP_SPORT]) {
368473ab820STom Herbert 		ret = true;
3693e97fa70SSabrina Dubroca 		ipencap->sport = nla_get_be16(data[IFLA_IPTUN_ENCAP_SPORT]);
370473ab820STom Herbert 	}
371473ab820STom Herbert 
372473ab820STom Herbert 	if (data[IFLA_IPTUN_ENCAP_DPORT]) {
373473ab820STom Herbert 		ret = true;
3743e97fa70SSabrina Dubroca 		ipencap->dport = nla_get_be16(data[IFLA_IPTUN_ENCAP_DPORT]);
375473ab820STom Herbert 	}
376473ab820STom Herbert 
377473ab820STom Herbert 	return ret;
378473ab820STom Herbert }
379473ab820STom Herbert 
380be42da0eSNicolas Dichtel static int ipip_newlink(struct net *src_net, struct net_device *dev,
381be42da0eSNicolas Dichtel 			struct nlattr *tb[], struct nlattr *data[])
382be42da0eSNicolas Dichtel {
383fd58156eSPravin B Shelar 	struct ip_tunnel_parm p;
384473ab820STom Herbert 	struct ip_tunnel_encap ipencap;
385473ab820STom Herbert 
386473ab820STom Herbert 	if (ipip_netlink_encap_parms(data, &ipencap)) {
387473ab820STom Herbert 		struct ip_tunnel *t = netdev_priv(dev);
388473ab820STom Herbert 		int err = ip_tunnel_encap_setup(t, &ipencap);
389473ab820STom Herbert 
390473ab820STom Herbert 		if (err < 0)
391473ab820STom Herbert 			return err;
392473ab820STom Herbert 	}
393be42da0eSNicolas Dichtel 
394fd58156eSPravin B Shelar 	ipip_netlink_parms(data, &p);
395fd58156eSPravin B Shelar 	return ip_tunnel_newlink(dev, tb, &p);
396be42da0eSNicolas Dichtel }
397be42da0eSNicolas Dichtel 
398be42da0eSNicolas Dichtel static int ipip_changelink(struct net_device *dev, struct nlattr *tb[],
399be42da0eSNicolas Dichtel 			   struct nlattr *data[])
400be42da0eSNicolas Dichtel {
401be42da0eSNicolas Dichtel 	struct ip_tunnel_parm p;
402473ab820STom Herbert 	struct ip_tunnel_encap ipencap;
403473ab820STom Herbert 
404473ab820STom Herbert 	if (ipip_netlink_encap_parms(data, &ipencap)) {
405473ab820STom Herbert 		struct ip_tunnel *t = netdev_priv(dev);
406473ab820STom Herbert 		int err = ip_tunnel_encap_setup(t, &ipencap);
407473ab820STom Herbert 
408473ab820STom Herbert 		if (err < 0)
409473ab820STom Herbert 			return err;
410473ab820STom Herbert 	}
411be42da0eSNicolas Dichtel 
412be42da0eSNicolas Dichtel 	ipip_netlink_parms(data, &p);
413be42da0eSNicolas Dichtel 
414be42da0eSNicolas Dichtel 	if (((dev->flags & IFF_POINTOPOINT) && !p.iph.daddr) ||
415be42da0eSNicolas Dichtel 	    (!(dev->flags & IFF_POINTOPOINT) && p.iph.daddr))
416be42da0eSNicolas Dichtel 		return -EINVAL;
417be42da0eSNicolas Dichtel 
418fd58156eSPravin B Shelar 	return ip_tunnel_changelink(dev, tb, &p);
419be42da0eSNicolas Dichtel }
420be42da0eSNicolas Dichtel 
4210974658dSNicolas Dichtel static size_t ipip_get_size(const struct net_device *dev)
4220974658dSNicolas Dichtel {
4230974658dSNicolas Dichtel 	return
4240974658dSNicolas Dichtel 		/* IFLA_IPTUN_LINK */
4250974658dSNicolas Dichtel 		nla_total_size(4) +
4260974658dSNicolas Dichtel 		/* IFLA_IPTUN_LOCAL */
4270974658dSNicolas Dichtel 		nla_total_size(4) +
4280974658dSNicolas Dichtel 		/* IFLA_IPTUN_REMOTE */
4290974658dSNicolas Dichtel 		nla_total_size(4) +
4300974658dSNicolas Dichtel 		/* IFLA_IPTUN_TTL */
4310974658dSNicolas Dichtel 		nla_total_size(1) +
4320974658dSNicolas Dichtel 		/* IFLA_IPTUN_TOS */
4330974658dSNicolas Dichtel 		nla_total_size(1) +
434befe2aa1SNicolas Dichtel 		/* IFLA_IPTUN_PMTUDISC */
435befe2aa1SNicolas Dichtel 		nla_total_size(1) +
436473ab820STom Herbert 		/* IFLA_IPTUN_ENCAP_TYPE */
437473ab820STom Herbert 		nla_total_size(2) +
438473ab820STom Herbert 		/* IFLA_IPTUN_ENCAP_FLAGS */
439473ab820STom Herbert 		nla_total_size(2) +
440473ab820STom Herbert 		/* IFLA_IPTUN_ENCAP_SPORT */
441473ab820STom Herbert 		nla_total_size(2) +
442473ab820STom Herbert 		/* IFLA_IPTUN_ENCAP_DPORT */
443473ab820STom Herbert 		nla_total_size(2) +
4440974658dSNicolas Dichtel 		0;
4450974658dSNicolas Dichtel }
4460974658dSNicolas Dichtel 
4470974658dSNicolas Dichtel static int ipip_fill_info(struct sk_buff *skb, const struct net_device *dev)
4480974658dSNicolas Dichtel {
4490974658dSNicolas Dichtel 	struct ip_tunnel *tunnel = netdev_priv(dev);
4500974658dSNicolas Dichtel 	struct ip_tunnel_parm *parm = &tunnel->parms;
4510974658dSNicolas Dichtel 
4520974658dSNicolas Dichtel 	if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) ||
453930345eaSJiri Benc 	    nla_put_in_addr(skb, IFLA_IPTUN_LOCAL, parm->iph.saddr) ||
454930345eaSJiri Benc 	    nla_put_in_addr(skb, IFLA_IPTUN_REMOTE, parm->iph.daddr) ||
4550974658dSNicolas Dichtel 	    nla_put_u8(skb, IFLA_IPTUN_TTL, parm->iph.ttl) ||
456befe2aa1SNicolas Dichtel 	    nla_put_u8(skb, IFLA_IPTUN_TOS, parm->iph.tos) ||
457befe2aa1SNicolas Dichtel 	    nla_put_u8(skb, IFLA_IPTUN_PMTUDISC,
458befe2aa1SNicolas Dichtel 		       !!(parm->iph.frag_off & htons(IP_DF))))
4590974658dSNicolas Dichtel 		goto nla_put_failure;
460473ab820STom Herbert 
461473ab820STom Herbert 	if (nla_put_u16(skb, IFLA_IPTUN_ENCAP_TYPE,
462473ab820STom Herbert 			tunnel->encap.type) ||
4633e97fa70SSabrina Dubroca 	    nla_put_be16(skb, IFLA_IPTUN_ENCAP_SPORT,
464473ab820STom Herbert 			 tunnel->encap.sport) ||
4653e97fa70SSabrina Dubroca 	    nla_put_be16(skb, IFLA_IPTUN_ENCAP_DPORT,
466473ab820STom Herbert 			 tunnel->encap.dport) ||
467473ab820STom Herbert 	    nla_put_u16(skb, IFLA_IPTUN_ENCAP_FLAGS,
468e1b2cb65STom Herbert 			tunnel->encap.flags))
469473ab820STom Herbert 		goto nla_put_failure;
470473ab820STom Herbert 
4710974658dSNicolas Dichtel 	return 0;
4720974658dSNicolas Dichtel 
4730974658dSNicolas Dichtel nla_put_failure:
4740974658dSNicolas Dichtel 	return -EMSGSIZE;
4750974658dSNicolas Dichtel }
4760974658dSNicolas Dichtel 
477be42da0eSNicolas Dichtel static const struct nla_policy ipip_policy[IFLA_IPTUN_MAX + 1] = {
478be42da0eSNicolas Dichtel 	[IFLA_IPTUN_LINK]		= { .type = NLA_U32 },
479be42da0eSNicolas Dichtel 	[IFLA_IPTUN_LOCAL]		= { .type = NLA_U32 },
480be42da0eSNicolas Dichtel 	[IFLA_IPTUN_REMOTE]		= { .type = NLA_U32 },
481be42da0eSNicolas Dichtel 	[IFLA_IPTUN_TTL]		= { .type = NLA_U8 },
482be42da0eSNicolas Dichtel 	[IFLA_IPTUN_TOS]		= { .type = NLA_U8 },
483be42da0eSNicolas Dichtel 	[IFLA_IPTUN_PMTUDISC]		= { .type = NLA_U8 },
484473ab820STom Herbert 	[IFLA_IPTUN_ENCAP_TYPE]		= { .type = NLA_U16 },
485473ab820STom Herbert 	[IFLA_IPTUN_ENCAP_FLAGS]	= { .type = NLA_U16 },
486473ab820STom Herbert 	[IFLA_IPTUN_ENCAP_SPORT]	= { .type = NLA_U16 },
487473ab820STom Herbert 	[IFLA_IPTUN_ENCAP_DPORT]	= { .type = NLA_U16 },
488be42da0eSNicolas Dichtel };
489be42da0eSNicolas Dichtel 
4900974658dSNicolas Dichtel static struct rtnl_link_ops ipip_link_ops __read_mostly = {
4910974658dSNicolas Dichtel 	.kind		= "ipip",
4920974658dSNicolas Dichtel 	.maxtype	= IFLA_IPTUN_MAX,
493be42da0eSNicolas Dichtel 	.policy		= ipip_policy,
4940974658dSNicolas Dichtel 	.priv_size	= sizeof(struct ip_tunnel),
495be42da0eSNicolas Dichtel 	.setup		= ipip_tunnel_setup,
496be42da0eSNicolas Dichtel 	.newlink	= ipip_newlink,
497be42da0eSNicolas Dichtel 	.changelink	= ipip_changelink,
498fd58156eSPravin B Shelar 	.dellink	= ip_tunnel_dellink,
4990974658dSNicolas Dichtel 	.get_size	= ipip_get_size,
5000974658dSNicolas Dichtel 	.fill_info	= ipip_fill_info,
5011728d4faSNicolas Dichtel 	.get_link_net	= ip_tunnel_get_link_net,
5020974658dSNicolas Dichtel };
5030974658dSNicolas Dichtel 
5046dcd814bSEric Dumazet static struct xfrm_tunnel ipip_handler __read_mostly = {
5051da177e4SLinus Torvalds 	.handler	=	ipip_rcv,
5061da177e4SLinus Torvalds 	.err_handler	=	ipip_err,
507d2acc347SHerbert Xu 	.priority	=	1,
5081da177e4SLinus Torvalds };
5091da177e4SLinus Torvalds 
5102c8c1e72SAlexey Dobriyan static int __net_init ipip_init_net(struct net *net)
51110dc4c7bSPavel Emelyanov {
512fd58156eSPravin B Shelar 	return ip_tunnel_init_net(net, ipip_net_id, &ipip_link_ops, "tunl0");
51310dc4c7bSPavel Emelyanov }
51410dc4c7bSPavel Emelyanov 
5152c8c1e72SAlexey Dobriyan static void __net_exit ipip_exit_net(struct net *net)
51610dc4c7bSPavel Emelyanov {
517fd58156eSPravin B Shelar 	struct ip_tunnel_net *itn = net_generic(net, ipip_net_id);
5186c742e71SNicolas Dichtel 	ip_tunnel_delete_net(itn, &ipip_link_ops);
51910dc4c7bSPavel Emelyanov }
52010dc4c7bSPavel Emelyanov 
52110dc4c7bSPavel Emelyanov static struct pernet_operations ipip_net_ops = {
52210dc4c7bSPavel Emelyanov 	.init = ipip_init_net,
52310dc4c7bSPavel Emelyanov 	.exit = ipip_exit_net,
52486de8a63SEric W. Biederman 	.id   = &ipip_net_id,
525fd58156eSPravin B Shelar 	.size = sizeof(struct ip_tunnel_net),
52610dc4c7bSPavel Emelyanov };
52710dc4c7bSPavel Emelyanov 
5281da177e4SLinus Torvalds static int __init ipip_init(void)
5291da177e4SLinus Torvalds {
5301da177e4SLinus Torvalds 	int err;
5311da177e4SLinus Torvalds 
532fd58156eSPravin B Shelar 	pr_info("ipip: IPv4 over IPv4 tunneling driver\n");
5331da177e4SLinus Torvalds 
53486de8a63SEric W. Biederman 	err = register_pernet_device(&ipip_net_ops);
535d5aa407fSAlexey Dobriyan 	if (err < 0)
536d5aa407fSAlexey Dobriyan 		return err;
537d5aa407fSAlexey Dobriyan 	err = xfrm4_tunnel_register(&ipip_handler, AF_INET);
538d5aa407fSAlexey Dobriyan 	if (err < 0) {
539058bd4d2SJoe Perches 		pr_info("%s: can't register tunnel\n", __func__);
5400974658dSNicolas Dichtel 		goto xfrm_tunnel_failed;
541d5aa407fSAlexey Dobriyan 	}
5420974658dSNicolas Dichtel 	err = rtnl_link_register(&ipip_link_ops);
5430974658dSNicolas Dichtel 	if (err < 0)
5440974658dSNicolas Dichtel 		goto rtnl_link_failed;
5450974658dSNicolas Dichtel 
5460974658dSNicolas Dichtel out:
547b9855c54SPavel Emelyanov 	return err;
5480974658dSNicolas Dichtel 
5490974658dSNicolas Dichtel rtnl_link_failed:
5500974658dSNicolas Dichtel 	xfrm4_tunnel_deregister(&ipip_handler, AF_INET);
5510974658dSNicolas Dichtel xfrm_tunnel_failed:
5520974658dSNicolas Dichtel 	unregister_pernet_device(&ipip_net_ops);
5530974658dSNicolas Dichtel 	goto out;
5541da177e4SLinus Torvalds }
5551da177e4SLinus Torvalds 
5561da177e4SLinus Torvalds static void __exit ipip_fini(void)
5571da177e4SLinus Torvalds {
5580974658dSNicolas Dichtel 	rtnl_link_unregister(&ipip_link_ops);
559c0d56408SKazunori MIYAZAWA 	if (xfrm4_tunnel_deregister(&ipip_handler, AF_INET))
560058bd4d2SJoe Perches 		pr_info("%s: can't deregister tunnel\n", __func__);
5611da177e4SLinus Torvalds 
56286de8a63SEric W. Biederman 	unregister_pernet_device(&ipip_net_ops);
5631da177e4SLinus Torvalds }
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds module_init(ipip_init);
5661da177e4SLinus Torvalds module_exit(ipip_fini);
5671da177e4SLinus Torvalds MODULE_LICENSE("GPL");
568f98f89a0STom Gundersen MODULE_ALIAS_RTNL_LINK("ipip");
5698909c9adSVasiliy Kulikov MODULE_ALIAS_NETDEV("tunl0");
570