xref: /linux/net/ipv4/ipip.c (revision 00d066a4d4edbe559ba6c35153da71d4b2b8a383)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *	Linux NET3:	IP/IP protocol decoder.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *	Authors:
61da177e4SLinus Torvalds  *		Sam Lantinga (slouken@cs.ucdavis.edu)  02/01/95
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  *	Fixes:
91da177e4SLinus Torvalds  *		Alan Cox	:	Merged and made usable non modular (its so tiny its silly as
101da177e4SLinus Torvalds  *					a module taking up 2 pages).
111da177e4SLinus Torvalds  *		Alan Cox	: 	Fixed bug with 1.3.18 and IPIP not working (now needs to set skb->h.iph)
121da177e4SLinus Torvalds  *					to keep ip_forward happy.
131da177e4SLinus Torvalds  *		Alan Cox	:	More fixes for 1.3.21, and firewall fix. Maybe this will work soon 8).
141da177e4SLinus Torvalds  *		Kai Schulte	:	Fixed #defines for IP_FIREWALL->FIREWALL
151da177e4SLinus Torvalds  *              David Woodhouse :       Perform some basic ICMP handling.
161da177e4SLinus Torvalds  *                                      IPIP Routing without decapsulation.
171da177e4SLinus Torvalds  *              Carlos Picoto   :       GRE over IP support
181da177e4SLinus Torvalds  *		Alexey Kuznetsov:	Reworked. Really, now it is truncated version of ipv4/ip_gre.c.
191da177e4SLinus Torvalds  *					I do not want to merge them together.
201da177e4SLinus Torvalds  */
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds /* tunnel.c: an IP tunnel driver
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds 	The purpose of this driver is to provide an IP tunnel through
251da177e4SLinus Torvalds 	which you can tunnel network traffic transparently across subnets.
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds 	This was written by looking at Nick Holloway's dummy driver
281da177e4SLinus Torvalds 	Thanks for the great code!
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds 		-Sam Lantinga	(slouken@cs.ucdavis.edu)  02/01/95
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds 	Minor tweaks:
331da177e4SLinus Torvalds 		Cleaned up the code a little and added some pre-1.3.0 tweaks.
341da177e4SLinus Torvalds 		dev->hard_header/hard_header_len changed to use no headers.
351da177e4SLinus Torvalds 		Comments/bracketing tweaked.
361da177e4SLinus Torvalds 		Made the tunnels use dev->name not tunnel: when error reporting.
371da177e4SLinus Torvalds 		Added tx_dropped stat
381da177e4SLinus Torvalds 
39113aa838SAlan Cox 		-Alan Cox	(alan@lxorguk.ukuu.org.uk) 21 March 95
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds 	Reworked:
421da177e4SLinus Torvalds 		Changed to tunnel to destination gateway in addition to the
431da177e4SLinus Torvalds 			tunnel's pointopoint address
441da177e4SLinus Torvalds 		Almost completely rewritten
451da177e4SLinus Torvalds 		Note:  There is currently no firewall or ICMP handling done.
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds 		-Sam Lantinga	(slouken@cs.ucdavis.edu) 02/13/96
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds */
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds /* Things I wish I had known when writing the tunnel driver:
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds 	When the tunnel_xmit() function is called, the skb contains the
541da177e4SLinus Torvalds 	packet to be sent (plus a great deal of extra info), and dev
551da177e4SLinus Torvalds 	contains the tunnel device that _we_ are.
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds 	When we are passed a packet, we are expected to fill in the
581da177e4SLinus Torvalds 	source address with our source IP address.
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 	What is the proper way to allocate, copy and free a buffer?
611da177e4SLinus Torvalds 	After you allocate it, it is a "0 length" chunk of memory
621da177e4SLinus Torvalds 	starting at zero.  If you want to add headers to the buffer
631da177e4SLinus Torvalds 	later, you'll have to call "skb_reserve(skb, amount)" with
641da177e4SLinus Torvalds 	the amount of memory you want reserved.  Then, you call
651da177e4SLinus Torvalds 	"skb_put(skb, amount)" with the amount of space you want in
661da177e4SLinus Torvalds 	the buffer.  skb_put() returns a pointer to the top (#0) of
671da177e4SLinus Torvalds 	that buffer.  skb->len is set to the amount of space you have
681da177e4SLinus Torvalds 	"allocated" with skb_put().  You can then write up to skb->len
691da177e4SLinus Torvalds 	bytes to that buffer.  If you need more, you can call skb_put()
701da177e4SLinus Torvalds 	again with the additional amount of space you need.  You can
711da177e4SLinus Torvalds 	find out how much more space you can allocate by calling
721da177e4SLinus Torvalds 	"skb_tailroom(skb)".
731da177e4SLinus Torvalds 	Now, to add header space, call "skb_push(skb, header_len)".
741da177e4SLinus Torvalds 	This creates space at the beginning of the buffer and returns
751da177e4SLinus Torvalds 	a pointer to this new space.  If later you need to strip a
761da177e4SLinus Torvalds 	header from a buffer, call "skb_pull(skb, header_len)".
771da177e4SLinus Torvalds 	skb_headroom() will return how much space is left at the top
781da177e4SLinus Torvalds 	of the buffer (before the main data).  Remember, this headroom
791da177e4SLinus Torvalds 	space must be reserved before the skb_put() function is called.
801da177e4SLinus Torvalds 	*/
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds /*
831da177e4SLinus Torvalds    This version of net/ipv4/ipip.c is cloned of net/ipv4/ip_gre.c
841da177e4SLinus Torvalds 
851da177e4SLinus Torvalds    For comments look at net/ipv4/ip_gre.c --ANK
861da177e4SLinus Torvalds  */
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 
894fc268d2SRandy Dunlap #include <linux/capability.h>
901da177e4SLinus Torvalds #include <linux/module.h>
911da177e4SLinus Torvalds #include <linux/types.h>
921da177e4SLinus Torvalds #include <linux/kernel.h>
935a0e3ad6STejun Heo #include <linux/slab.h>
947c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
951da177e4SLinus Torvalds #include <linux/skbuff.h>
961da177e4SLinus Torvalds #include <linux/netdevice.h>
971da177e4SLinus Torvalds #include <linux/in.h>
981da177e4SLinus Torvalds #include <linux/tcp.h>
991da177e4SLinus Torvalds #include <linux/udp.h>
1001da177e4SLinus Torvalds #include <linux/if_arp.h>
1011da177e4SLinus Torvalds #include <linux/init.h>
1021da177e4SLinus Torvalds #include <linux/netfilter_ipv4.h>
10346f25dffSKris Katterjohn #include <linux/if_ether.h>
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds #include <net/sock.h>
1061da177e4SLinus Torvalds #include <net/ip.h>
1071da177e4SLinus Torvalds #include <net/icmp.h>
108c5441932SPravin B Shelar #include <net/ip_tunnels.h>
1091da177e4SLinus Torvalds #include <net/inet_ecn.h>
1101da177e4SLinus Torvalds #include <net/xfrm.h>
11110dc4c7bSPavel Emelyanov #include <net/net_namespace.h>
11210dc4c7bSPavel Emelyanov #include <net/netns/generic.h>
113cfc7381bSAlexei Starovoitov #include <net/dst_metadata.h>
1141da177e4SLinus Torvalds 
115eccc1bb8Sstephen hemminger static bool log_ecn_error = true;
116eccc1bb8Sstephen hemminger module_param(log_ecn_error, bool, 0644);
117eccc1bb8Sstephen hemminger MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
118eccc1bb8Sstephen hemminger 
119c7d03a00SAlexey Dobriyan static unsigned int ipip_net_id __read_mostly;
12010dc4c7bSPavel Emelyanov 
1213c97af99SEric Dumazet static int ipip_tunnel_init(struct net_device *dev);
1220974658dSNicolas Dichtel static struct rtnl_link_ops ipip_link_ops __read_mostly;
1231da177e4SLinus Torvalds 
124d2acc347SHerbert Xu static int ipip_err(struct sk_buff *skb, u32 info)
1251da177e4SLinus Torvalds {
126071f92d0SRami Rosen 	/* All the routers (except for Linux) return only
127f3594f0aSXin Long 	 * 8 bytes of packet payload. It means, that precise relaying of
128f3594f0aSXin Long 	 * ICMP in the real Internet is absolutely infeasible.
1291da177e4SLinus Torvalds 	 */
130fd58156eSPravin B Shelar 	struct net *net = dev_net(skb->dev);
131fd58156eSPravin B Shelar 	struct ip_tunnel_net *itn = net_generic(net, ipip_net_id);
132b71d1d42SEric Dumazet 	const struct iphdr *iph = (const struct iphdr *)skb->data;
1335832c4a7SAlexander Lobakin 	IP_TUNNEL_DECLARE_FLAGS(flags) = { };
134fd58156eSPravin B Shelar 	const int type = icmp_hdr(skb)->type;
135fd58156eSPravin B Shelar 	const int code = icmp_hdr(skb)->code;
136f3594f0aSXin Long 	struct ip_tunnel *t;
137f3594f0aSXin Long 	int err = 0;
1381da177e4SLinus Torvalds 
1395832c4a7SAlexander Lobakin 	__set_bit(IP_TUNNEL_NO_KEY_BIT, flags);
1405832c4a7SAlexander Lobakin 
1415832c4a7SAlexander Lobakin 	t = ip_tunnel_lookup(itn, skb->dev->ifindex, flags, iph->daddr,
1425832c4a7SAlexander Lobakin 			     iph->saddr, 0);
14332bbd879SStefano Brivio 	if (!t) {
14432bbd879SStefano Brivio 		err = -ENOENT;
14532bbd879SStefano Brivio 		goto out;
14632bbd879SStefano Brivio 	}
14732bbd879SStefano Brivio 
148f3594f0aSXin Long 	switch (type) {
149f3594f0aSXin Long 	case ICMP_DEST_UNREACH:
150f3594f0aSXin Long 		switch (code) {
151f3594f0aSXin Long 		case ICMP_SR_FAILED:
152f3594f0aSXin Long 			/* Impossible event. */
153f3594f0aSXin Long 			goto out;
154f3594f0aSXin Long 		default:
155f3594f0aSXin Long 			/* All others are translated to HOST_UNREACH.
156f3594f0aSXin Long 			 * rfc2003 contains "deep thoughts" about NET_UNREACH,
157f3594f0aSXin Long 			 * I believe they are just ether pollution. --ANK
158f3594f0aSXin Long 			 */
159f3594f0aSXin Long 			break;
160f3594f0aSXin Long 		}
161f3594f0aSXin Long 		break;
162f3594f0aSXin Long 
163f3594f0aSXin Long 	case ICMP_TIME_EXCEEDED:
164f3594f0aSXin Long 		if (code != ICMP_EXC_TTL)
165f3594f0aSXin Long 			goto out;
166f3594f0aSXin Long 		break;
167f3594f0aSXin Long 
168f3594f0aSXin Long 	case ICMP_REDIRECT:
169f3594f0aSXin Long 		break;
170f3594f0aSXin Long 
171f3594f0aSXin Long 	default:
172f3594f0aSXin Long 		goto out;
173f3594f0aSXin Long 	}
174f3594f0aSXin Long 
17536393395SDavid S. Miller 	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
176d888f396SMaciej Żenczykowski 		ipv4_update_pmtu(skb, net, info, t->parms.link, iph->protocol);
17736393395SDavid S. Miller 		goto out;
17836393395SDavid S. Miller 	}
17936393395SDavid S. Miller 
18055be7a9cSDavid S. Miller 	if (type == ICMP_REDIRECT) {
1811042caa7SMaciej Żenczykowski 		ipv4_redirect(skb, net, t->parms.link, iph->protocol);
18255be7a9cSDavid S. Miller 		goto out;
18355be7a9cSDavid S. Miller 	}
18455be7a9cSDavid S. Miller 
185f3594f0aSXin Long 	if (t->parms.iph.daddr == 0) {
186f3594f0aSXin Long 		err = -ENOENT;
1871da177e4SLinus Torvalds 		goto out;
188f3594f0aSXin Long 	}
189d2acc347SHerbert Xu 
1901da177e4SLinus Torvalds 	if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
1911da177e4SLinus Torvalds 		goto out;
1921da177e4SLinus Torvalds 
19326d94b46SWei Yongjun 	if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO))
1941da177e4SLinus Torvalds 		t->err_count++;
1951da177e4SLinus Torvalds 	else
1961da177e4SLinus Torvalds 		t->err_count = 1;
1971da177e4SLinus Torvalds 	t->err_time = jiffies;
198b0558ef2Sstephen hemminger 
199fd58156eSPravin B Shelar out:
200d2acc347SHerbert Xu 	return err;
2011da177e4SLinus Torvalds }
2021da177e4SLinus Torvalds 
2031b69e7e6SSimon Horman static const struct tnl_ptk_info ipip_tpi = {
204fd58156eSPravin B Shelar 	/* no tunnel info required for ipip. */
205fd58156eSPravin B Shelar 	.proto = htons(ETH_P_IP),
206fd58156eSPravin B Shelar };
207fd58156eSPravin B Shelar 
2081b69e7e6SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
2091b69e7e6SSimon Horman static const struct tnl_ptk_info mplsip_tpi = {
2101b69e7e6SSimon Horman 	/* no tunnel info required for mplsip. */
2111b69e7e6SSimon Horman 	.proto = htons(ETH_P_MPLS_UC),
2121b69e7e6SSimon Horman };
2131b69e7e6SSimon Horman #endif
2141b69e7e6SSimon Horman 
2151b69e7e6SSimon Horman static int ipip_tunnel_rcv(struct sk_buff *skb, u8 ipproto)
2161da177e4SLinus Torvalds {
217fd58156eSPravin B Shelar 	struct net *net = dev_net(skb->dev);
218fd58156eSPravin B Shelar 	struct ip_tunnel_net *itn = net_generic(net, ipip_net_id);
2195832c4a7SAlexander Lobakin 	IP_TUNNEL_DECLARE_FLAGS(flags) = { };
220cfc7381bSAlexei Starovoitov 	struct metadata_dst *tun_dst = NULL;
2211da177e4SLinus Torvalds 	struct ip_tunnel *tunnel;
2223d7b46cdSPravin B Shelar 	const struct iphdr *iph;
2231da177e4SLinus Torvalds 
2245832c4a7SAlexander Lobakin 	__set_bit(IP_TUNNEL_NO_KEY_BIT, flags);
2255832c4a7SAlexander Lobakin 
2263d7b46cdSPravin B Shelar 	iph = ip_hdr(skb);
2275832c4a7SAlexander Lobakin 	tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, flags, iph->saddr,
2285832c4a7SAlexander Lobakin 				  iph->daddr, 0);
229fd58156eSPravin B Shelar 	if (tunnel) {
2301b69e7e6SSimon Horman 		const struct tnl_ptk_info *tpi;
2311b69e7e6SSimon Horman 
2321b69e7e6SSimon Horman 		if (tunnel->parms.iph.protocol != ipproto &&
2331b69e7e6SSimon Horman 		    tunnel->parms.iph.protocol != 0)
2341b69e7e6SSimon Horman 			goto drop;
2351b69e7e6SSimon Horman 
236eccc1bb8Sstephen hemminger 		if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
237eccc1bb8Sstephen hemminger 			goto drop;
2381b69e7e6SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
2391b69e7e6SSimon Horman 		if (ipproto == IPPROTO_MPLS)
2401b69e7e6SSimon Horman 			tpi = &mplsip_tpi;
2411b69e7e6SSimon Horman 		else
2421b69e7e6SSimon Horman #endif
2431b69e7e6SSimon Horman 			tpi = &ipip_tpi;
2441b69e7e6SSimon Horman 		if (iptunnel_pull_header(skb, 0, tpi->proto, false))
245737e828bSLi Hongjun 			goto drop;
246cfc7381bSAlexei Starovoitov 		if (tunnel->collect_md) {
2475832c4a7SAlexander Lobakin 			ip_tunnel_flags_zero(flags);
2485832c4a7SAlexander Lobakin 
2495832c4a7SAlexander Lobakin 			tun_dst = ip_tun_rx_dst(skb, flags, 0, 0);
250cfc7381bSAlexei Starovoitov 			if (!tun_dst)
251cfc7381bSAlexei Starovoitov 				return 0;
252ac931d4cSChristian Ehrig 			ip_tunnel_md_udp_encap(skb, &tun_dst->u.tun_info);
253cfc7381bSAlexei Starovoitov 		}
2547ad136fdSGuillaume Nault 		skb_reset_mac_header(skb);
2557ad136fdSGuillaume Nault 
256cfc7381bSAlexei Starovoitov 		return ip_tunnel_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
2571da177e4SLinus Torvalds 	}
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 	return -1;
260eccc1bb8Sstephen hemminger 
261eccc1bb8Sstephen hemminger drop:
262eccc1bb8Sstephen hemminger 	kfree_skb(skb);
263eccc1bb8Sstephen hemminger 	return 0;
2641da177e4SLinus Torvalds }
2651da177e4SLinus Torvalds 
2661b69e7e6SSimon Horman static int ipip_rcv(struct sk_buff *skb)
2671b69e7e6SSimon Horman {
2681b69e7e6SSimon Horman 	return ipip_tunnel_rcv(skb, IPPROTO_IPIP);
2691b69e7e6SSimon Horman }
2701b69e7e6SSimon Horman 
2711b69e7e6SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
2721b69e7e6SSimon Horman static int mplsip_rcv(struct sk_buff *skb)
2731b69e7e6SSimon Horman {
2741b69e7e6SSimon Horman 	return ipip_tunnel_rcv(skb, IPPROTO_MPLS);
2751b69e7e6SSimon Horman }
2761b69e7e6SSimon Horman #endif
2771b69e7e6SSimon Horman 
2781da177e4SLinus Torvalds /*
2791da177e4SLinus Torvalds  *	This function assumes it is being called from dev_queue_xmit()
2801da177e4SLinus Torvalds  *	and that skb is filled properly by that function.
2811da177e4SLinus Torvalds  */
2821b69e7e6SSimon Horman static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb,
2831b69e7e6SSimon Horman 				    struct net_device *dev)
2841da177e4SLinus Torvalds {
2852941a486SPatrick McHardy 	struct ip_tunnel *tunnel = netdev_priv(dev);
286b71d1d42SEric Dumazet 	const struct iphdr  *tiph = &tunnel->parms.iph;
2871b69e7e6SSimon Horman 	u8 ipproto;
2881da177e4SLinus Torvalds 
28947d858d0SHaishuang Yan 	if (!pskb_inet_may_pull(skb))
29047d858d0SHaishuang Yan 		goto tx_error;
29147d858d0SHaishuang Yan 
2921b69e7e6SSimon Horman 	switch (skb->protocol) {
2931b69e7e6SSimon Horman 	case htons(ETH_P_IP):
2941b69e7e6SSimon Horman 		ipproto = IPPROTO_IPIP;
2951b69e7e6SSimon Horman 		break;
2961b69e7e6SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
2971b69e7e6SSimon Horman 	case htons(ETH_P_MPLS_UC):
2981b69e7e6SSimon Horman 		ipproto = IPPROTO_MPLS;
2991b69e7e6SSimon Horman 		break;
3001b69e7e6SSimon Horman #endif
3011b69e7e6SSimon Horman 	default:
3021b69e7e6SSimon Horman 		goto tx_error;
3031b69e7e6SSimon Horman 	}
3041b69e7e6SSimon Horman 
3051b69e7e6SSimon Horman 	if (tiph->protocol != ipproto && tiph->protocol != 0)
3061da177e4SLinus Torvalds 		goto tx_error;
307cef401deSEric Dumazet 
3087e13318dSTom Herbert 	if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP4))
309aed069dfSAlexander Duyck 		goto tx_error;
3104f3ed920SPravin B Shelar 
3111b69e7e6SSimon Horman 	skb_set_inner_ipproto(skb, ipproto);
312077c5a09STom Herbert 
313cfc7381bSAlexei Starovoitov 	if (tunnel->collect_md)
314c8b34e68Swenxu 		ip_md_tunnel_xmit(skb, dev, ipproto, 0);
315cfc7381bSAlexei Starovoitov 	else
3161b69e7e6SSimon Horman 		ip_tunnel_xmit(skb, dev, tiph, ipproto);
3176ed10654SPatrick McHardy 	return NETDEV_TX_OK;
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds tx_error:
3203acfa1e7SEric Dumazet 	kfree_skb(skb);
321aed069dfSAlexander Duyck 
322c4794d22SEric Dumazet 	DEV_STATS_INC(dev, tx_errors);
3236ed10654SPatrick McHardy 	return NETDEV_TX_OK;
3241da177e4SLinus Torvalds }
3251da177e4SLinus Torvalds 
3261b69e7e6SSimon Horman static bool ipip_tunnel_ioctl_verify_protocol(u8 ipproto)
3271b69e7e6SSimon Horman {
3281b69e7e6SSimon Horman 	switch (ipproto) {
3291b69e7e6SSimon Horman 	case 0:
3301b69e7e6SSimon Horman 	case IPPROTO_IPIP:
3311b69e7e6SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
3321b69e7e6SSimon Horman 	case IPPROTO_MPLS:
3331b69e7e6SSimon Horman #endif
3341b69e7e6SSimon Horman 		return true;
3351b69e7e6SSimon Horman 	}
3361b69e7e6SSimon Horman 
3371b69e7e6SSimon Horman 	return false;
3381b69e7e6SSimon Horman }
3391b69e7e6SSimon Horman 
3401da177e4SLinus Torvalds static int
341117aef12SAlexander Lobakin ipip_tunnel_ctl(struct net_device *dev, struct ip_tunnel_parm_kern *p, int cmd)
3421da177e4SLinus Torvalds {
3433b7b514fSCong Wang 	if (cmd == SIOCADDTUNNEL || cmd == SIOCCHGTUNNEL) {
344607259a6SChristoph Hellwig 		if (p->iph.version != 4 ||
345607259a6SChristoph Hellwig 		    !ipip_tunnel_ioctl_verify_protocol(p->iph.protocol) ||
346607259a6SChristoph Hellwig 		    p->iph.ihl != 5 || (p->iph.frag_off & htons(~IP_DF)))
347fd58156eSPravin B Shelar 			return -EINVAL;
3483b7b514fSCong Wang 	}
3493b7b514fSCong Wang 
350607259a6SChristoph Hellwig 	p->i_key = p->o_key = 0;
3515832c4a7SAlexander Lobakin 	ip_tunnel_flags_zero(p->i_flags);
3525832c4a7SAlexander Lobakin 	ip_tunnel_flags_zero(p->o_flags);
353607259a6SChristoph Hellwig 	return ip_tunnel_ctl(dev, p, cmd);
3541da177e4SLinus Torvalds }
3551da177e4SLinus Torvalds 
35623a12b14SStephen Hemminger static const struct net_device_ops ipip_netdev_ops = {
357fd58156eSPravin B Shelar 	.ndo_init       = ipip_tunnel_init,
358fd58156eSPravin B Shelar 	.ndo_uninit     = ip_tunnel_uninit,
35923a12b14SStephen Hemminger 	.ndo_start_xmit	= ipip_tunnel_xmit,
3603e7a1c7cSArnd Bergmann 	.ndo_siocdevprivate = ip_tunnel_siocdevprivate,
361fd58156eSPravin B Shelar 	.ndo_change_mtu = ip_tunnel_change_mtu,
36298d7fc46SHeiner Kallweit 	.ndo_get_stats64 = dev_get_tstats64,
3631e99584bSNicolas Dichtel 	.ndo_get_iflink = ip_tunnel_get_iflink,
364607259a6SChristoph Hellwig 	.ndo_tunnel_ctl	= ipip_tunnel_ctl,
36523a12b14SStephen Hemminger };
36623a12b14SStephen Hemminger 
367c3b89fbbSEric Dumazet #define IPIP_FEATURES (NETIF_F_SG |		\
368c3b89fbbSEric Dumazet 		       NETIF_F_FRAGLIST |	\
369c3b89fbbSEric Dumazet 		       NETIF_F_HIGHDMA |	\
370cb32f511SEric Dumazet 		       NETIF_F_GSO_SOFTWARE |	\
371c3b89fbbSEric Dumazet 		       NETIF_F_HW_CSUM)
372c3b89fbbSEric Dumazet 
3731da177e4SLinus Torvalds static void ipip_tunnel_setup(struct net_device *dev)
3741da177e4SLinus Torvalds {
37523a12b14SStephen Hemminger 	dev->netdev_ops		= &ipip_netdev_ops;
376e53ac932SJason A. Donenfeld 	dev->header_ops		= &ip_tunnel_header_ops;
3771da177e4SLinus Torvalds 
3781da177e4SLinus Torvalds 	dev->type		= ARPHRD_TUNNEL;
3791da177e4SLinus Torvalds 	dev->flags		= IFF_NOARP;
3801da177e4SLinus Torvalds 	dev->addr_len		= 4;
381*00d066a4SAlexander Lobakin 	dev->lltx		= true;
38202875878SEric Dumazet 	netif_keep_dst(dev);
383c3b89fbbSEric Dumazet 
384c3b89fbbSEric Dumazet 	dev->features		|= IPIP_FEATURES;
385c3b89fbbSEric Dumazet 	dev->hw_features	|= IPIP_FEATURES;
386fd58156eSPravin B Shelar 	ip_tunnel_setup(dev, ipip_net_id);
3871da177e4SLinus Torvalds }
3881da177e4SLinus Torvalds 
3893c97af99SEric Dumazet static int ipip_tunnel_init(struct net_device *dev)
3901da177e4SLinus Torvalds {
39123a12b14SStephen Hemminger 	struct ip_tunnel *tunnel = netdev_priv(dev);
3921da177e4SLinus Torvalds 
3935a1b7e1aSJakub Kicinski 	__dev_addr_set(dev, &tunnel->parms.iph.saddr, 4);
3941da177e4SLinus Torvalds 	memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
3951da177e4SLinus Torvalds 
396473ab820STom Herbert 	tunnel->tun_hlen = 0;
397473ab820STom Herbert 	tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen;
398fd58156eSPravin B Shelar 	return ip_tunnel_init(dev);
3991da177e4SLinus Torvalds }
4001da177e4SLinus Torvalds 
401a8b8a889SMatthias Schiffer static int ipip_tunnel_validate(struct nlattr *tb[], struct nlattr *data[],
402a8b8a889SMatthias Schiffer 				struct netlink_ext_ack *extack)
4031b69e7e6SSimon Horman {
4041b69e7e6SSimon Horman 	u8 proto;
4051b69e7e6SSimon Horman 
4061b69e7e6SSimon Horman 	if (!data || !data[IFLA_IPTUN_PROTO])
4071b69e7e6SSimon Horman 		return 0;
4081b69e7e6SSimon Horman 
4091b69e7e6SSimon Horman 	proto = nla_get_u8(data[IFLA_IPTUN_PROTO]);
4101b69e7e6SSimon Horman 	if (proto != IPPROTO_IPIP && proto != IPPROTO_MPLS && proto != 0)
4111b69e7e6SSimon Horman 		return -EINVAL;
4121b69e7e6SSimon Horman 
4131b69e7e6SSimon Horman 	return 0;
4141b69e7e6SSimon Horman }
4151b69e7e6SSimon Horman 
416be42da0eSNicolas Dichtel static void ipip_netlink_parms(struct nlattr *data[],
417117aef12SAlexander Lobakin 			       struct ip_tunnel_parm_kern *parms,
418117aef12SAlexander Lobakin 			       bool *collect_md, __u32 *fwmark)
419be42da0eSNicolas Dichtel {
420be42da0eSNicolas Dichtel 	memset(parms, 0, sizeof(*parms));
421be42da0eSNicolas Dichtel 
422be42da0eSNicolas Dichtel 	parms->iph.version = 4;
423be42da0eSNicolas Dichtel 	parms->iph.protocol = IPPROTO_IPIP;
424be42da0eSNicolas Dichtel 	parms->iph.ihl = 5;
425cfc7381bSAlexei Starovoitov 	*collect_md = false;
426be42da0eSNicolas Dichtel 
427be42da0eSNicolas Dichtel 	if (!data)
428be42da0eSNicolas Dichtel 		return;
429be42da0eSNicolas Dichtel 
430b86fca80SLiu Jian 	ip_tunnel_netlink_parms(data, parms);
431cfc7381bSAlexei Starovoitov 
432cfc7381bSAlexei Starovoitov 	if (data[IFLA_IPTUN_COLLECT_METADATA])
433cfc7381bSAlexei Starovoitov 		*collect_md = true;
4349830ad4cSCraig Gallek 
4359830ad4cSCraig Gallek 	if (data[IFLA_IPTUN_FWMARK])
4369830ad4cSCraig Gallek 		*fwmark = nla_get_u32(data[IFLA_IPTUN_FWMARK]);
437be42da0eSNicolas Dichtel }
438be42da0eSNicolas Dichtel 
439be42da0eSNicolas Dichtel static int ipip_newlink(struct net *src_net, struct net_device *dev,
4407a3f4a18SMatthias Schiffer 			struct nlattr *tb[], struct nlattr *data[],
4417a3f4a18SMatthias Schiffer 			struct netlink_ext_ack *extack)
442be42da0eSNicolas Dichtel {
443cfc7381bSAlexei Starovoitov 	struct ip_tunnel *t = netdev_priv(dev);
444473ab820STom Herbert 	struct ip_tunnel_encap ipencap;
445117aef12SAlexander Lobakin 	struct ip_tunnel_parm_kern p;
4469830ad4cSCraig Gallek 	__u32 fwmark = 0;
447473ab820STom Herbert 
448537dd2d9SLiu Jian 	if (ip_tunnel_netlink_encap_parms(data, &ipencap)) {
449473ab820STom Herbert 		int err = ip_tunnel_encap_setup(t, &ipencap);
450473ab820STom Herbert 
451473ab820STom Herbert 		if (err < 0)
452473ab820STom Herbert 			return err;
453473ab820STom Herbert 	}
454be42da0eSNicolas Dichtel 
4559830ad4cSCraig Gallek 	ipip_netlink_parms(data, &p, &t->collect_md, &fwmark);
4569830ad4cSCraig Gallek 	return ip_tunnel_newlink(dev, tb, &p, fwmark);
457be42da0eSNicolas Dichtel }
458be42da0eSNicolas Dichtel 
459be42da0eSNicolas Dichtel static int ipip_changelink(struct net_device *dev, struct nlattr *tb[],
460ad744b22SMatthias Schiffer 			   struct nlattr *data[],
461ad744b22SMatthias Schiffer 			   struct netlink_ext_ack *extack)
462be42da0eSNicolas Dichtel {
4639830ad4cSCraig Gallek 	struct ip_tunnel *t = netdev_priv(dev);
464473ab820STom Herbert 	struct ip_tunnel_encap ipencap;
465117aef12SAlexander Lobakin 	struct ip_tunnel_parm_kern p;
466cfc7381bSAlexei Starovoitov 	bool collect_md;
4679830ad4cSCraig Gallek 	__u32 fwmark = t->fwmark;
468473ab820STom Herbert 
469537dd2d9SLiu Jian 	if (ip_tunnel_netlink_encap_parms(data, &ipencap)) {
470473ab820STom Herbert 		int err = ip_tunnel_encap_setup(t, &ipencap);
471473ab820STom Herbert 
472473ab820STom Herbert 		if (err < 0)
473473ab820STom Herbert 			return err;
474473ab820STom Herbert 	}
475be42da0eSNicolas Dichtel 
4769830ad4cSCraig Gallek 	ipip_netlink_parms(data, &p, &collect_md, &fwmark);
477cfc7381bSAlexei Starovoitov 	if (collect_md)
478cfc7381bSAlexei Starovoitov 		return -EINVAL;
479be42da0eSNicolas Dichtel 
480be42da0eSNicolas Dichtel 	if (((dev->flags & IFF_POINTOPOINT) && !p.iph.daddr) ||
481be42da0eSNicolas Dichtel 	    (!(dev->flags & IFF_POINTOPOINT) && p.iph.daddr))
482be42da0eSNicolas Dichtel 		return -EINVAL;
483be42da0eSNicolas Dichtel 
4849830ad4cSCraig Gallek 	return ip_tunnel_changelink(dev, tb, &p, fwmark);
485be42da0eSNicolas Dichtel }
486be42da0eSNicolas Dichtel 
4870974658dSNicolas Dichtel static size_t ipip_get_size(const struct net_device *dev)
4880974658dSNicolas Dichtel {
4890974658dSNicolas Dichtel 	return
4900974658dSNicolas Dichtel 		/* IFLA_IPTUN_LINK */
4910974658dSNicolas Dichtel 		nla_total_size(4) +
4920974658dSNicolas Dichtel 		/* IFLA_IPTUN_LOCAL */
4930974658dSNicolas Dichtel 		nla_total_size(4) +
4940974658dSNicolas Dichtel 		/* IFLA_IPTUN_REMOTE */
4950974658dSNicolas Dichtel 		nla_total_size(4) +
4960974658dSNicolas Dichtel 		/* IFLA_IPTUN_TTL */
4970974658dSNicolas Dichtel 		nla_total_size(1) +
4980974658dSNicolas Dichtel 		/* IFLA_IPTUN_TOS */
4990974658dSNicolas Dichtel 		nla_total_size(1) +
5001b69e7e6SSimon Horman 		/* IFLA_IPTUN_PROTO */
5011b69e7e6SSimon Horman 		nla_total_size(1) +
502befe2aa1SNicolas Dichtel 		/* IFLA_IPTUN_PMTUDISC */
503befe2aa1SNicolas Dichtel 		nla_total_size(1) +
504473ab820STom Herbert 		/* IFLA_IPTUN_ENCAP_TYPE */
505473ab820STom Herbert 		nla_total_size(2) +
506473ab820STom Herbert 		/* IFLA_IPTUN_ENCAP_FLAGS */
507473ab820STom Herbert 		nla_total_size(2) +
508473ab820STom Herbert 		/* IFLA_IPTUN_ENCAP_SPORT */
509473ab820STom Herbert 		nla_total_size(2) +
510473ab820STom Herbert 		/* IFLA_IPTUN_ENCAP_DPORT */
511473ab820STom Herbert 		nla_total_size(2) +
512cfc7381bSAlexei Starovoitov 		/* IFLA_IPTUN_COLLECT_METADATA */
513cfc7381bSAlexei Starovoitov 		nla_total_size(0) +
5149830ad4cSCraig Gallek 		/* IFLA_IPTUN_FWMARK */
5159830ad4cSCraig Gallek 		nla_total_size(4) +
5160974658dSNicolas Dichtel 		0;
5170974658dSNicolas Dichtel }
5180974658dSNicolas Dichtel 
5190974658dSNicolas Dichtel static int ipip_fill_info(struct sk_buff *skb, const struct net_device *dev)
5200974658dSNicolas Dichtel {
5210974658dSNicolas Dichtel 	struct ip_tunnel *tunnel = netdev_priv(dev);
522117aef12SAlexander Lobakin 	struct ip_tunnel_parm_kern *parm = &tunnel->parms;
5230974658dSNicolas Dichtel 
5240974658dSNicolas Dichtel 	if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) ||
525930345eaSJiri Benc 	    nla_put_in_addr(skb, IFLA_IPTUN_LOCAL, parm->iph.saddr) ||
526930345eaSJiri Benc 	    nla_put_in_addr(skb, IFLA_IPTUN_REMOTE, parm->iph.daddr) ||
5270974658dSNicolas Dichtel 	    nla_put_u8(skb, IFLA_IPTUN_TTL, parm->iph.ttl) ||
528befe2aa1SNicolas Dichtel 	    nla_put_u8(skb, IFLA_IPTUN_TOS, parm->iph.tos) ||
5291b69e7e6SSimon Horman 	    nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->iph.protocol) ||
530befe2aa1SNicolas Dichtel 	    nla_put_u8(skb, IFLA_IPTUN_PMTUDISC,
5319830ad4cSCraig Gallek 		       !!(parm->iph.frag_off & htons(IP_DF))) ||
5329830ad4cSCraig Gallek 	    nla_put_u32(skb, IFLA_IPTUN_FWMARK, tunnel->fwmark))
5330974658dSNicolas Dichtel 		goto nla_put_failure;
534473ab820STom Herbert 
535473ab820STom Herbert 	if (nla_put_u16(skb, IFLA_IPTUN_ENCAP_TYPE,
536473ab820STom Herbert 			tunnel->encap.type) ||
5373e97fa70SSabrina Dubroca 	    nla_put_be16(skb, IFLA_IPTUN_ENCAP_SPORT,
538473ab820STom Herbert 			 tunnel->encap.sport) ||
5393e97fa70SSabrina Dubroca 	    nla_put_be16(skb, IFLA_IPTUN_ENCAP_DPORT,
540473ab820STom Herbert 			 tunnel->encap.dport) ||
541473ab820STom Herbert 	    nla_put_u16(skb, IFLA_IPTUN_ENCAP_FLAGS,
542e1b2cb65STom Herbert 			tunnel->encap.flags))
543473ab820STom Herbert 		goto nla_put_failure;
544473ab820STom Herbert 
545cfc7381bSAlexei Starovoitov 	if (tunnel->collect_md)
546cfc7381bSAlexei Starovoitov 		if (nla_put_flag(skb, IFLA_IPTUN_COLLECT_METADATA))
547cfc7381bSAlexei Starovoitov 			goto nla_put_failure;
5480974658dSNicolas Dichtel 	return 0;
5490974658dSNicolas Dichtel 
5500974658dSNicolas Dichtel nla_put_failure:
5510974658dSNicolas Dichtel 	return -EMSGSIZE;
5520974658dSNicolas Dichtel }
5530974658dSNicolas Dichtel 
554be42da0eSNicolas Dichtel static const struct nla_policy ipip_policy[IFLA_IPTUN_MAX + 1] = {
555be42da0eSNicolas Dichtel 	[IFLA_IPTUN_LINK]		= { .type = NLA_U32 },
556be42da0eSNicolas Dichtel 	[IFLA_IPTUN_LOCAL]		= { .type = NLA_U32 },
557be42da0eSNicolas Dichtel 	[IFLA_IPTUN_REMOTE]		= { .type = NLA_U32 },
558be42da0eSNicolas Dichtel 	[IFLA_IPTUN_TTL]		= { .type = NLA_U8 },
559be42da0eSNicolas Dichtel 	[IFLA_IPTUN_TOS]		= { .type = NLA_U8 },
5601b69e7e6SSimon Horman 	[IFLA_IPTUN_PROTO]		= { .type = NLA_U8 },
561be42da0eSNicolas Dichtel 	[IFLA_IPTUN_PMTUDISC]		= { .type = NLA_U8 },
562473ab820STom Herbert 	[IFLA_IPTUN_ENCAP_TYPE]		= { .type = NLA_U16 },
563473ab820STom Herbert 	[IFLA_IPTUN_ENCAP_FLAGS]	= { .type = NLA_U16 },
564473ab820STom Herbert 	[IFLA_IPTUN_ENCAP_SPORT]	= { .type = NLA_U16 },
565473ab820STom Herbert 	[IFLA_IPTUN_ENCAP_DPORT]	= { .type = NLA_U16 },
566cfc7381bSAlexei Starovoitov 	[IFLA_IPTUN_COLLECT_METADATA]	= { .type = NLA_FLAG },
5679830ad4cSCraig Gallek 	[IFLA_IPTUN_FWMARK]		= { .type = NLA_U32 },
568be42da0eSNicolas Dichtel };
569be42da0eSNicolas Dichtel 
5700974658dSNicolas Dichtel static struct rtnl_link_ops ipip_link_ops __read_mostly = {
5710974658dSNicolas Dichtel 	.kind		= "ipip",
5720974658dSNicolas Dichtel 	.maxtype	= IFLA_IPTUN_MAX,
573be42da0eSNicolas Dichtel 	.policy		= ipip_policy,
5740974658dSNicolas Dichtel 	.priv_size	= sizeof(struct ip_tunnel),
575be42da0eSNicolas Dichtel 	.setup		= ipip_tunnel_setup,
5761b69e7e6SSimon Horman 	.validate	= ipip_tunnel_validate,
577be42da0eSNicolas Dichtel 	.newlink	= ipip_newlink,
578be42da0eSNicolas Dichtel 	.changelink	= ipip_changelink,
579fd58156eSPravin B Shelar 	.dellink	= ip_tunnel_dellink,
5800974658dSNicolas Dichtel 	.get_size	= ipip_get_size,
5810974658dSNicolas Dichtel 	.fill_info	= ipip_fill_info,
5821728d4faSNicolas Dichtel 	.get_link_net	= ip_tunnel_get_link_net,
5830974658dSNicolas Dichtel };
5840974658dSNicolas Dichtel 
5856dcd814bSEric Dumazet static struct xfrm_tunnel ipip_handler __read_mostly = {
5861da177e4SLinus Torvalds 	.handler	=	ipip_rcv,
5871da177e4SLinus Torvalds 	.err_handler	=	ipip_err,
588d2acc347SHerbert Xu 	.priority	=	1,
5891da177e4SLinus Torvalds };
5901da177e4SLinus Torvalds 
5911b69e7e6SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
5921b69e7e6SSimon Horman static struct xfrm_tunnel mplsip_handler __read_mostly = {
5931b69e7e6SSimon Horman 	.handler	=	mplsip_rcv,
5941b69e7e6SSimon Horman 	.err_handler	=	ipip_err,
5951b69e7e6SSimon Horman 	.priority	=	1,
5961b69e7e6SSimon Horman };
5971b69e7e6SSimon Horman #endif
5981b69e7e6SSimon Horman 
5992c8c1e72SAlexey Dobriyan static int __net_init ipip_init_net(struct net *net)
60010dc4c7bSPavel Emelyanov {
601fd58156eSPravin B Shelar 	return ip_tunnel_init_net(net, ipip_net_id, &ipip_link_ops, "tunl0");
60210dc4c7bSPavel Emelyanov }
60310dc4c7bSPavel Emelyanov 
6049b5b3637SEric Dumazet static void __net_exit ipip_exit_batch_rtnl(struct list_head *list_net,
6059b5b3637SEric Dumazet 					    struct list_head *dev_to_kill)
60610dc4c7bSPavel Emelyanov {
6079b5b3637SEric Dumazet 	ip_tunnel_delete_nets(list_net, ipip_net_id, &ipip_link_ops,
6089b5b3637SEric Dumazet 			      dev_to_kill);
60910dc4c7bSPavel Emelyanov }
61010dc4c7bSPavel Emelyanov 
61110dc4c7bSPavel Emelyanov static struct pernet_operations ipip_net_ops = {
61210dc4c7bSPavel Emelyanov 	.init = ipip_init_net,
6139b5b3637SEric Dumazet 	.exit_batch_rtnl = ipip_exit_batch_rtnl,
61486de8a63SEric W. Biederman 	.id   = &ipip_net_id,
615fd58156eSPravin B Shelar 	.size = sizeof(struct ip_tunnel_net),
61610dc4c7bSPavel Emelyanov };
61710dc4c7bSPavel Emelyanov 
6181da177e4SLinus Torvalds static int __init ipip_init(void)
6191da177e4SLinus Torvalds {
6201da177e4SLinus Torvalds 	int err;
6211da177e4SLinus Torvalds 
6221b69e7e6SSimon Horman 	pr_info("ipip: IPv4 and MPLS over IPv4 tunneling driver\n");
6231da177e4SLinus Torvalds 
62486de8a63SEric W. Biederman 	err = register_pernet_device(&ipip_net_ops);
625d5aa407fSAlexey Dobriyan 	if (err < 0)
626d5aa407fSAlexey Dobriyan 		return err;
627d5aa407fSAlexey Dobriyan 	err = xfrm4_tunnel_register(&ipip_handler, AF_INET);
628d5aa407fSAlexey Dobriyan 	if (err < 0) {
629058bd4d2SJoe Perches 		pr_info("%s: can't register tunnel\n", __func__);
6301b69e7e6SSimon Horman 		goto xfrm_tunnel_ipip_failed;
631d5aa407fSAlexey Dobriyan 	}
6321b69e7e6SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
6331b69e7e6SSimon Horman 	err = xfrm4_tunnel_register(&mplsip_handler, AF_MPLS);
6341b69e7e6SSimon Horman 	if (err < 0) {
6351b69e7e6SSimon Horman 		pr_info("%s: can't register tunnel\n", __func__);
6361b69e7e6SSimon Horman 		goto xfrm_tunnel_mplsip_failed;
6371b69e7e6SSimon Horman 	}
6381b69e7e6SSimon Horman #endif
6390974658dSNicolas Dichtel 	err = rtnl_link_register(&ipip_link_ops);
6400974658dSNicolas Dichtel 	if (err < 0)
6410974658dSNicolas Dichtel 		goto rtnl_link_failed;
6420974658dSNicolas Dichtel 
6430974658dSNicolas Dichtel out:
644b9855c54SPavel Emelyanov 	return err;
6450974658dSNicolas Dichtel 
6460974658dSNicolas Dichtel rtnl_link_failed:
6471b69e7e6SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
64857ebc8f0SVadim Fedorenko 	xfrm4_tunnel_deregister(&mplsip_handler, AF_MPLS);
6491b69e7e6SSimon Horman xfrm_tunnel_mplsip_failed:
6501b69e7e6SSimon Horman 
6511b69e7e6SSimon Horman #endif
6520974658dSNicolas Dichtel 	xfrm4_tunnel_deregister(&ipip_handler, AF_INET);
6531b69e7e6SSimon Horman xfrm_tunnel_ipip_failed:
6540974658dSNicolas Dichtel 	unregister_pernet_device(&ipip_net_ops);
6550974658dSNicolas Dichtel 	goto out;
6561da177e4SLinus Torvalds }
6571da177e4SLinus Torvalds 
6581da177e4SLinus Torvalds static void __exit ipip_fini(void)
6591da177e4SLinus Torvalds {
6600974658dSNicolas Dichtel 	rtnl_link_unregister(&ipip_link_ops);
661c0d56408SKazunori MIYAZAWA 	if (xfrm4_tunnel_deregister(&ipip_handler, AF_INET))
662058bd4d2SJoe Perches 		pr_info("%s: can't deregister tunnel\n", __func__);
6631b69e7e6SSimon Horman #if IS_ENABLED(CONFIG_MPLS)
6641b69e7e6SSimon Horman 	if (xfrm4_tunnel_deregister(&mplsip_handler, AF_MPLS))
6651b69e7e6SSimon Horman 		pr_info("%s: can't deregister tunnel\n", __func__);
6661b69e7e6SSimon Horman #endif
66786de8a63SEric W. Biederman 	unregister_pernet_device(&ipip_net_ops);
6681da177e4SLinus Torvalds }
6691da177e4SLinus Torvalds 
6701da177e4SLinus Torvalds module_init(ipip_init);
6711da177e4SLinus Torvalds module_exit(ipip_fini);
672b058a5d2SBreno Leitao MODULE_DESCRIPTION("IP/IP protocol decoder library");
6731da177e4SLinus Torvalds MODULE_LICENSE("GPL");
674f98f89a0STom Gundersen MODULE_ALIAS_RTNL_LINK("ipip");
6758909c9adSVasiliy Kulikov MODULE_ALIAS_NETDEV("tunl0");
676