162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	Linux NET3:	GRE over IP protocol decoder.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *	Authors: Alexey Kuznetsov (kuznet@ms2.inr.ac.ru)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/capability.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/types.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/uaccess.h>
1662306a36Sopenharmony_ci#include <linux/skbuff.h>
1762306a36Sopenharmony_ci#include <linux/netdevice.h>
1862306a36Sopenharmony_ci#include <linux/in.h>
1962306a36Sopenharmony_ci#include <linux/tcp.h>
2062306a36Sopenharmony_ci#include <linux/udp.h>
2162306a36Sopenharmony_ci#include <linux/if_arp.h>
2262306a36Sopenharmony_ci#include <linux/if_vlan.h>
2362306a36Sopenharmony_ci#include <linux/init.h>
2462306a36Sopenharmony_ci#include <linux/in6.h>
2562306a36Sopenharmony_ci#include <linux/inetdevice.h>
2662306a36Sopenharmony_ci#include <linux/igmp.h>
2762306a36Sopenharmony_ci#include <linux/netfilter_ipv4.h>
2862306a36Sopenharmony_ci#include <linux/etherdevice.h>
2962306a36Sopenharmony_ci#include <linux/if_ether.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <net/sock.h>
3262306a36Sopenharmony_ci#include <net/ip.h>
3362306a36Sopenharmony_ci#include <net/icmp.h>
3462306a36Sopenharmony_ci#include <net/protocol.h>
3562306a36Sopenharmony_ci#include <net/ip_tunnels.h>
3662306a36Sopenharmony_ci#include <net/arp.h>
3762306a36Sopenharmony_ci#include <net/checksum.h>
3862306a36Sopenharmony_ci#include <net/dsfield.h>
3962306a36Sopenharmony_ci#include <net/inet_ecn.h>
4062306a36Sopenharmony_ci#include <net/xfrm.h>
4162306a36Sopenharmony_ci#include <net/net_namespace.h>
4262306a36Sopenharmony_ci#include <net/netns/generic.h>
4362306a36Sopenharmony_ci#include <net/rtnetlink.h>
4462306a36Sopenharmony_ci#include <net/gre.h>
4562306a36Sopenharmony_ci#include <net/dst_metadata.h>
4662306a36Sopenharmony_ci#include <net/erspan.h>
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci   Problems & solutions
5062306a36Sopenharmony_ci   --------------------
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci   1. The most important issue is detecting local dead loops.
5362306a36Sopenharmony_ci   They would cause complete host lockup in transmit, which
5462306a36Sopenharmony_ci   would be "resolved" by stack overflow or, if queueing is enabled,
5562306a36Sopenharmony_ci   with infinite looping in net_bh.
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci   We cannot track such dead loops during route installation,
5862306a36Sopenharmony_ci   it is infeasible task. The most general solutions would be
5962306a36Sopenharmony_ci   to keep skb->encapsulation counter (sort of local ttl),
6062306a36Sopenharmony_ci   and silently drop packet when it expires. It is a good
6162306a36Sopenharmony_ci   solution, but it supposes maintaining new variable in ALL
6262306a36Sopenharmony_ci   skb, even if no tunneling is used.
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci   Current solution: xmit_recursion breaks dead loops. This is a percpu
6562306a36Sopenharmony_ci   counter, since when we enter the first ndo_xmit(), cpu migration is
6662306a36Sopenharmony_ci   forbidden. We force an exit if this counter reaches RECURSION_LIMIT
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci   2. Networking dead loops would not kill routers, but would really
6962306a36Sopenharmony_ci   kill network. IP hop limit plays role of "t->recursion" in this case,
7062306a36Sopenharmony_ci   if we copy it from packet being encapsulated to upper header.
7162306a36Sopenharmony_ci   It is very good solution, but it introduces two problems:
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci   - Routing protocols, using packets with ttl=1 (OSPF, RIP2),
7462306a36Sopenharmony_ci     do not work over tunnels.
7562306a36Sopenharmony_ci   - traceroute does not work. I planned to relay ICMP from tunnel,
7662306a36Sopenharmony_ci     so that this problem would be solved and traceroute output
7762306a36Sopenharmony_ci     would even more informative. This idea appeared to be wrong:
7862306a36Sopenharmony_ci     only Linux complies to rfc1812 now (yes, guys, Linux is the only
7962306a36Sopenharmony_ci     true router now :-)), all routers (at least, in neighbourhood of mine)
8062306a36Sopenharmony_ci     return only 8 bytes of payload. It is the end.
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci   Hence, if we want that OSPF worked or traceroute said something reasonable,
8362306a36Sopenharmony_ci   we should search for another solution.
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci   One of them is to parse packet trying to detect inner encapsulation
8662306a36Sopenharmony_ci   made by our node. It is difficult or even impossible, especially,
8762306a36Sopenharmony_ci   taking into account fragmentation. TO be short, ttl is not solution at all.
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci   Current solution: The solution was UNEXPECTEDLY SIMPLE.
9062306a36Sopenharmony_ci   We force DF flag on tunnels with preconfigured hop limit,
9162306a36Sopenharmony_ci   that is ALL. :-) Well, it does not remove the problem completely,
9262306a36Sopenharmony_ci   but exponential growth of network traffic is changed to linear
9362306a36Sopenharmony_ci   (branches, that exceed pmtu are pruned) and tunnel mtu
9462306a36Sopenharmony_ci   rapidly degrades to value <68, where looping stops.
9562306a36Sopenharmony_ci   Yes, it is not good if there exists a router in the loop,
9662306a36Sopenharmony_ci   which does not force DF, even when encapsulating packets have DF set.
9762306a36Sopenharmony_ci   But it is not our problem! Nobody could accuse us, we made
9862306a36Sopenharmony_ci   all that we could make. Even if it is your gated who injected
9962306a36Sopenharmony_ci   fatal route to network, even if it were you who configured
10062306a36Sopenharmony_ci   fatal static route: you are innocent. :-)
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci   Alexey Kuznetsov.
10362306a36Sopenharmony_ci */
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic bool log_ecn_error = true;
10662306a36Sopenharmony_cimodule_param(log_ecn_error, bool, 0644);
10762306a36Sopenharmony_ciMODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic struct rtnl_link_ops ipgre_link_ops __read_mostly;
11062306a36Sopenharmony_cistatic const struct header_ops ipgre_header_ops;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic int ipgre_tunnel_init(struct net_device *dev);
11362306a36Sopenharmony_cistatic void erspan_build_header(struct sk_buff *skb,
11462306a36Sopenharmony_ci				u32 id, u32 index,
11562306a36Sopenharmony_ci				bool truncate, bool is_ipv4);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic unsigned int ipgre_net_id __read_mostly;
11862306a36Sopenharmony_cistatic unsigned int gre_tap_net_id __read_mostly;
11962306a36Sopenharmony_cistatic unsigned int erspan_net_id __read_mostly;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic int ipgre_err(struct sk_buff *skb, u32 info,
12262306a36Sopenharmony_ci		     const struct tnl_ptk_info *tpi)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* All the routers (except for Linux) return only
12662306a36Sopenharmony_ci	   8 bytes of packet payload. It means, that precise relaying of
12762306a36Sopenharmony_ci	   ICMP in the real Internet is absolutely infeasible.
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	   Moreover, Cisco "wise men" put GRE key to the third word
13062306a36Sopenharmony_ci	   in GRE header. It makes impossible maintaining even soft
13162306a36Sopenharmony_ci	   state for keyed GRE tunnels with enabled checksum. Tell
13262306a36Sopenharmony_ci	   them "thank you".
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	   Well, I wonder, rfc1812 was written by Cisco employee,
13562306a36Sopenharmony_ci	   what the hell these idiots break standards established
13662306a36Sopenharmony_ci	   by themselves???
13762306a36Sopenharmony_ci	   */
13862306a36Sopenharmony_ci	struct net *net = dev_net(skb->dev);
13962306a36Sopenharmony_ci	struct ip_tunnel_net *itn;
14062306a36Sopenharmony_ci	const struct iphdr *iph;
14162306a36Sopenharmony_ci	const int type = icmp_hdr(skb)->type;
14262306a36Sopenharmony_ci	const int code = icmp_hdr(skb)->code;
14362306a36Sopenharmony_ci	unsigned int data_len = 0;
14462306a36Sopenharmony_ci	struct ip_tunnel *t;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (tpi->proto == htons(ETH_P_TEB))
14762306a36Sopenharmony_ci		itn = net_generic(net, gre_tap_net_id);
14862306a36Sopenharmony_ci	else if (tpi->proto == htons(ETH_P_ERSPAN) ||
14962306a36Sopenharmony_ci		 tpi->proto == htons(ETH_P_ERSPAN2))
15062306a36Sopenharmony_ci		itn = net_generic(net, erspan_net_id);
15162306a36Sopenharmony_ci	else
15262306a36Sopenharmony_ci		itn = net_generic(net, ipgre_net_id);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	iph = (const struct iphdr *)(icmp_hdr(skb) + 1);
15562306a36Sopenharmony_ci	t = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi->flags,
15662306a36Sopenharmony_ci			     iph->daddr, iph->saddr, tpi->key);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (!t)
15962306a36Sopenharmony_ci		return -ENOENT;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	switch (type) {
16262306a36Sopenharmony_ci	default:
16362306a36Sopenharmony_ci	case ICMP_PARAMETERPROB:
16462306a36Sopenharmony_ci		return 0;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	case ICMP_DEST_UNREACH:
16762306a36Sopenharmony_ci		switch (code) {
16862306a36Sopenharmony_ci		case ICMP_SR_FAILED:
16962306a36Sopenharmony_ci		case ICMP_PORT_UNREACH:
17062306a36Sopenharmony_ci			/* Impossible event. */
17162306a36Sopenharmony_ci			return 0;
17262306a36Sopenharmony_ci		default:
17362306a36Sopenharmony_ci			/* All others are translated to HOST_UNREACH.
17462306a36Sopenharmony_ci			   rfc2003 contains "deep thoughts" about NET_UNREACH,
17562306a36Sopenharmony_ci			   I believe they are just ether pollution. --ANK
17662306a36Sopenharmony_ci			 */
17762306a36Sopenharmony_ci			break;
17862306a36Sopenharmony_ci		}
17962306a36Sopenharmony_ci		break;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	case ICMP_TIME_EXCEEDED:
18262306a36Sopenharmony_ci		if (code != ICMP_EXC_TTL)
18362306a36Sopenharmony_ci			return 0;
18462306a36Sopenharmony_ci		data_len = icmp_hdr(skb)->un.reserved[1] * 4; /* RFC 4884 4.1 */
18562306a36Sopenharmony_ci		break;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	case ICMP_REDIRECT:
18862306a36Sopenharmony_ci		break;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
19262306a36Sopenharmony_ci	if (tpi->proto == htons(ETH_P_IPV6) &&
19362306a36Sopenharmony_ci	    !ip6_err_gen_icmpv6_unreach(skb, iph->ihl * 4 + tpi->hdr_len,
19462306a36Sopenharmony_ci					type, data_len))
19562306a36Sopenharmony_ci		return 0;
19662306a36Sopenharmony_ci#endif
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (t->parms.iph.daddr == 0 ||
19962306a36Sopenharmony_ci	    ipv4_is_multicast(t->parms.iph.daddr))
20062306a36Sopenharmony_ci		return 0;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
20362306a36Sopenharmony_ci		return 0;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO))
20662306a36Sopenharmony_ci		t->err_count++;
20762306a36Sopenharmony_ci	else
20862306a36Sopenharmony_ci		t->err_count = 1;
20962306a36Sopenharmony_ci	t->err_time = jiffies;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	return 0;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic void gre_err(struct sk_buff *skb, u32 info)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	/* All the routers (except for Linux) return only
21762306a36Sopenharmony_ci	 * 8 bytes of packet payload. It means, that precise relaying of
21862306a36Sopenharmony_ci	 * ICMP in the real Internet is absolutely infeasible.
21962306a36Sopenharmony_ci	 *
22062306a36Sopenharmony_ci	 * Moreover, Cisco "wise men" put GRE key to the third word
22162306a36Sopenharmony_ci	 * in GRE header. It makes impossible maintaining even soft
22262306a36Sopenharmony_ci	 * state for keyed
22362306a36Sopenharmony_ci	 * GRE tunnels with enabled checksum. Tell them "thank you".
22462306a36Sopenharmony_ci	 *
22562306a36Sopenharmony_ci	 * Well, I wonder, rfc1812 was written by Cisco employee,
22662306a36Sopenharmony_ci	 * what the hell these idiots break standards established
22762306a36Sopenharmony_ci	 * by themselves???
22862306a36Sopenharmony_ci	 */
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	const struct iphdr *iph = (struct iphdr *)skb->data;
23162306a36Sopenharmony_ci	const int type = icmp_hdr(skb)->type;
23262306a36Sopenharmony_ci	const int code = icmp_hdr(skb)->code;
23362306a36Sopenharmony_ci	struct tnl_ptk_info tpi;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (gre_parse_header(skb, &tpi, NULL, htons(ETH_P_IP),
23662306a36Sopenharmony_ci			     iph->ihl * 4) < 0)
23762306a36Sopenharmony_ci		return;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
24062306a36Sopenharmony_ci		ipv4_update_pmtu(skb, dev_net(skb->dev), info,
24162306a36Sopenharmony_ci				 skb->dev->ifindex, IPPROTO_GRE);
24262306a36Sopenharmony_ci		return;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci	if (type == ICMP_REDIRECT) {
24562306a36Sopenharmony_ci		ipv4_redirect(skb, dev_net(skb->dev), skb->dev->ifindex,
24662306a36Sopenharmony_ci			      IPPROTO_GRE);
24762306a36Sopenharmony_ci		return;
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	ipgre_err(skb, info, &tpi);
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic bool is_erspan_type1(int gre_hdr_len)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	/* Both ERSPAN type I (version 0) and type II (version 1) use
25662306a36Sopenharmony_ci	 * protocol 0x88BE, but the type I has only 4-byte GRE header,
25762306a36Sopenharmony_ci	 * while type II has 8-byte.
25862306a36Sopenharmony_ci	 */
25962306a36Sopenharmony_ci	return gre_hdr_len == 4;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
26362306a36Sopenharmony_ci		      int gre_hdr_len)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct net *net = dev_net(skb->dev);
26662306a36Sopenharmony_ci	struct metadata_dst *tun_dst = NULL;
26762306a36Sopenharmony_ci	struct erspan_base_hdr *ershdr;
26862306a36Sopenharmony_ci	struct ip_tunnel_net *itn;
26962306a36Sopenharmony_ci	struct ip_tunnel *tunnel;
27062306a36Sopenharmony_ci	const struct iphdr *iph;
27162306a36Sopenharmony_ci	struct erspan_md2 *md2;
27262306a36Sopenharmony_ci	int ver;
27362306a36Sopenharmony_ci	int len;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	itn = net_generic(net, erspan_net_id);
27662306a36Sopenharmony_ci	iph = ip_hdr(skb);
27762306a36Sopenharmony_ci	if (is_erspan_type1(gre_hdr_len)) {
27862306a36Sopenharmony_ci		ver = 0;
27962306a36Sopenharmony_ci		tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex,
28062306a36Sopenharmony_ci					  tpi->flags | TUNNEL_NO_KEY,
28162306a36Sopenharmony_ci					  iph->saddr, iph->daddr, 0);
28262306a36Sopenharmony_ci	} else {
28362306a36Sopenharmony_ci		ershdr = (struct erspan_base_hdr *)(skb->data + gre_hdr_len);
28462306a36Sopenharmony_ci		ver = ershdr->ver;
28562306a36Sopenharmony_ci		tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex,
28662306a36Sopenharmony_ci					  tpi->flags | TUNNEL_KEY,
28762306a36Sopenharmony_ci					  iph->saddr, iph->daddr, tpi->key);
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (tunnel) {
29162306a36Sopenharmony_ci		if (is_erspan_type1(gre_hdr_len))
29262306a36Sopenharmony_ci			len = gre_hdr_len;
29362306a36Sopenharmony_ci		else
29462306a36Sopenharmony_ci			len = gre_hdr_len + erspan_hdr_len(ver);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci		if (unlikely(!pskb_may_pull(skb, len)))
29762306a36Sopenharmony_ci			return PACKET_REJECT;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci		if (__iptunnel_pull_header(skb,
30062306a36Sopenharmony_ci					   len,
30162306a36Sopenharmony_ci					   htons(ETH_P_TEB),
30262306a36Sopenharmony_ci					   false, false) < 0)
30362306a36Sopenharmony_ci			goto drop;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci		if (tunnel->collect_md) {
30662306a36Sopenharmony_ci			struct erspan_metadata *pkt_md, *md;
30762306a36Sopenharmony_ci			struct ip_tunnel_info *info;
30862306a36Sopenharmony_ci			unsigned char *gh;
30962306a36Sopenharmony_ci			__be64 tun_id;
31062306a36Sopenharmony_ci			__be16 flags;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci			tpi->flags |= TUNNEL_KEY;
31362306a36Sopenharmony_ci			flags = tpi->flags;
31462306a36Sopenharmony_ci			tun_id = key32_to_tunnel_id(tpi->key);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci			tun_dst = ip_tun_rx_dst(skb, flags,
31762306a36Sopenharmony_ci						tun_id, sizeof(*md));
31862306a36Sopenharmony_ci			if (!tun_dst)
31962306a36Sopenharmony_ci				return PACKET_REJECT;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci			/* skb can be uncloned in __iptunnel_pull_header, so
32262306a36Sopenharmony_ci			 * old pkt_md is no longer valid and we need to reset
32362306a36Sopenharmony_ci			 * it
32462306a36Sopenharmony_ci			 */
32562306a36Sopenharmony_ci			gh = skb_network_header(skb) +
32662306a36Sopenharmony_ci			     skb_network_header_len(skb);
32762306a36Sopenharmony_ci			pkt_md = (struct erspan_metadata *)(gh + gre_hdr_len +
32862306a36Sopenharmony_ci							    sizeof(*ershdr));
32962306a36Sopenharmony_ci			md = ip_tunnel_info_opts(&tun_dst->u.tun_info);
33062306a36Sopenharmony_ci			md->version = ver;
33162306a36Sopenharmony_ci			md2 = &md->u.md2;
33262306a36Sopenharmony_ci			memcpy(md2, pkt_md, ver == 1 ? ERSPAN_V1_MDSIZE :
33362306a36Sopenharmony_ci						       ERSPAN_V2_MDSIZE);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci			info = &tun_dst->u.tun_info;
33662306a36Sopenharmony_ci			info->key.tun_flags |= TUNNEL_ERSPAN_OPT;
33762306a36Sopenharmony_ci			info->options_len = sizeof(*md);
33862306a36Sopenharmony_ci		}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci		skb_reset_mac_header(skb);
34162306a36Sopenharmony_ci		ip_tunnel_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
34262306a36Sopenharmony_ci		return PACKET_RCVD;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci	return PACKET_REJECT;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cidrop:
34762306a36Sopenharmony_ci	kfree_skb(skb);
34862306a36Sopenharmony_ci	return PACKET_RCVD;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic int __ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
35262306a36Sopenharmony_ci		       struct ip_tunnel_net *itn, int hdr_len, bool raw_proto)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct metadata_dst *tun_dst = NULL;
35562306a36Sopenharmony_ci	const struct iphdr *iph;
35662306a36Sopenharmony_ci	struct ip_tunnel *tunnel;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	iph = ip_hdr(skb);
35962306a36Sopenharmony_ci	tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi->flags,
36062306a36Sopenharmony_ci				  iph->saddr, iph->daddr, tpi->key);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (tunnel) {
36362306a36Sopenharmony_ci		const struct iphdr *tnl_params;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		if (__iptunnel_pull_header(skb, hdr_len, tpi->proto,
36662306a36Sopenharmony_ci					   raw_proto, false) < 0)
36762306a36Sopenharmony_ci			goto drop;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci		/* Special case for ipgre_header_parse(), which expects the
37062306a36Sopenharmony_ci		 * mac_header to point to the outer IP header.
37162306a36Sopenharmony_ci		 */
37262306a36Sopenharmony_ci		if (tunnel->dev->header_ops == &ipgre_header_ops)
37362306a36Sopenharmony_ci			skb_pop_mac_header(skb);
37462306a36Sopenharmony_ci		else
37562306a36Sopenharmony_ci			skb_reset_mac_header(skb);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		tnl_params = &tunnel->parms.iph;
37862306a36Sopenharmony_ci		if (tunnel->collect_md || tnl_params->daddr == 0) {
37962306a36Sopenharmony_ci			__be16 flags;
38062306a36Sopenharmony_ci			__be64 tun_id;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci			flags = tpi->flags & (TUNNEL_CSUM | TUNNEL_KEY);
38362306a36Sopenharmony_ci			tun_id = key32_to_tunnel_id(tpi->key);
38462306a36Sopenharmony_ci			tun_dst = ip_tun_rx_dst(skb, flags, tun_id, 0);
38562306a36Sopenharmony_ci			if (!tun_dst)
38662306a36Sopenharmony_ci				return PACKET_REJECT;
38762306a36Sopenharmony_ci		}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		ip_tunnel_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
39062306a36Sopenharmony_ci		return PACKET_RCVD;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci	return PACKET_NEXT;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cidrop:
39562306a36Sopenharmony_ci	kfree_skb(skb);
39662306a36Sopenharmony_ci	return PACKET_RCVD;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic int ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
40062306a36Sopenharmony_ci		     int hdr_len)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct net *net = dev_net(skb->dev);
40362306a36Sopenharmony_ci	struct ip_tunnel_net *itn;
40462306a36Sopenharmony_ci	int res;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (tpi->proto == htons(ETH_P_TEB))
40762306a36Sopenharmony_ci		itn = net_generic(net, gre_tap_net_id);
40862306a36Sopenharmony_ci	else
40962306a36Sopenharmony_ci		itn = net_generic(net, ipgre_net_id);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	res = __ipgre_rcv(skb, tpi, itn, hdr_len, false);
41262306a36Sopenharmony_ci	if (res == PACKET_NEXT && tpi->proto == htons(ETH_P_TEB)) {
41362306a36Sopenharmony_ci		/* ipgre tunnels in collect metadata mode should receive
41462306a36Sopenharmony_ci		 * also ETH_P_TEB traffic.
41562306a36Sopenharmony_ci		 */
41662306a36Sopenharmony_ci		itn = net_generic(net, ipgre_net_id);
41762306a36Sopenharmony_ci		res = __ipgre_rcv(skb, tpi, itn, hdr_len, true);
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci	return res;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic int gre_rcv(struct sk_buff *skb)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct tnl_ptk_info tpi;
42562306a36Sopenharmony_ci	bool csum_err = false;
42662306a36Sopenharmony_ci	int hdr_len;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci#ifdef CONFIG_NET_IPGRE_BROADCAST
42962306a36Sopenharmony_ci	if (ipv4_is_multicast(ip_hdr(skb)->daddr)) {
43062306a36Sopenharmony_ci		/* Looped back packet, drop it! */
43162306a36Sopenharmony_ci		if (rt_is_output_route(skb_rtable(skb)))
43262306a36Sopenharmony_ci			goto drop;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci#endif
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	hdr_len = gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IP), 0);
43762306a36Sopenharmony_ci	if (hdr_len < 0)
43862306a36Sopenharmony_ci		goto drop;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (unlikely(tpi.proto == htons(ETH_P_ERSPAN) ||
44162306a36Sopenharmony_ci		     tpi.proto == htons(ETH_P_ERSPAN2))) {
44262306a36Sopenharmony_ci		if (erspan_rcv(skb, &tpi, hdr_len) == PACKET_RCVD)
44362306a36Sopenharmony_ci			return 0;
44462306a36Sopenharmony_ci		goto out;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (ipgre_rcv(skb, &tpi, hdr_len) == PACKET_RCVD)
44862306a36Sopenharmony_ci		return 0;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ciout:
45162306a36Sopenharmony_ci	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
45262306a36Sopenharmony_cidrop:
45362306a36Sopenharmony_ci	kfree_skb(skb);
45462306a36Sopenharmony_ci	return 0;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic void __gre_xmit(struct sk_buff *skb, struct net_device *dev,
45862306a36Sopenharmony_ci		       const struct iphdr *tnl_params,
45962306a36Sopenharmony_ci		       __be16 proto)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	struct ip_tunnel *tunnel = netdev_priv(dev);
46262306a36Sopenharmony_ci	__be16 flags = tunnel->parms.o_flags;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	/* Push GRE header. */
46562306a36Sopenharmony_ci	gre_build_header(skb, tunnel->tun_hlen,
46662306a36Sopenharmony_ci			 flags, proto, tunnel->parms.o_key,
46762306a36Sopenharmony_ci			 (flags & TUNNEL_SEQ) ? htonl(atomic_fetch_inc(&tunnel->o_seqno)) : 0);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	ip_tunnel_xmit(skb, dev, tnl_params, tnl_params->protocol);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic int gre_handle_offloads(struct sk_buff *skb, bool csum)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	return iptunnel_handle_offloads(skb, csum ? SKB_GSO_GRE_CSUM : SKB_GSO_GRE);
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev,
47862306a36Sopenharmony_ci			__be16 proto)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	struct ip_tunnel *tunnel = netdev_priv(dev);
48162306a36Sopenharmony_ci	struct ip_tunnel_info *tun_info;
48262306a36Sopenharmony_ci	const struct ip_tunnel_key *key;
48362306a36Sopenharmony_ci	int tunnel_hlen;
48462306a36Sopenharmony_ci	__be16 flags;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	tun_info = skb_tunnel_info(skb);
48762306a36Sopenharmony_ci	if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX) ||
48862306a36Sopenharmony_ci		     ip_tunnel_info_af(tun_info) != AF_INET))
48962306a36Sopenharmony_ci		goto err_free_skb;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	key = &tun_info->key;
49262306a36Sopenharmony_ci	tunnel_hlen = gre_calc_hlen(key->tun_flags);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (skb_cow_head(skb, dev->needed_headroom))
49562306a36Sopenharmony_ci		goto err_free_skb;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	/* Push Tunnel header. */
49862306a36Sopenharmony_ci	if (gre_handle_offloads(skb, !!(tun_info->key.tun_flags & TUNNEL_CSUM)))
49962306a36Sopenharmony_ci		goto err_free_skb;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	flags = tun_info->key.tun_flags &
50262306a36Sopenharmony_ci		(TUNNEL_CSUM | TUNNEL_KEY | TUNNEL_SEQ);
50362306a36Sopenharmony_ci	gre_build_header(skb, tunnel_hlen, flags, proto,
50462306a36Sopenharmony_ci			 tunnel_id_to_key32(tun_info->key.tun_id),
50562306a36Sopenharmony_ci			 (flags & TUNNEL_SEQ) ? htonl(atomic_fetch_inc(&tunnel->o_seqno)) : 0);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	ip_md_tunnel_xmit(skb, dev, IPPROTO_GRE, tunnel_hlen);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	return;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cierr_free_skb:
51262306a36Sopenharmony_ci	kfree_skb(skb);
51362306a36Sopenharmony_ci	DEV_STATS_INC(dev, tx_dropped);
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	struct ip_tunnel *tunnel = netdev_priv(dev);
51962306a36Sopenharmony_ci	struct ip_tunnel_info *tun_info;
52062306a36Sopenharmony_ci	const struct ip_tunnel_key *key;
52162306a36Sopenharmony_ci	struct erspan_metadata *md;
52262306a36Sopenharmony_ci	bool truncate = false;
52362306a36Sopenharmony_ci	__be16 proto;
52462306a36Sopenharmony_ci	int tunnel_hlen;
52562306a36Sopenharmony_ci	int version;
52662306a36Sopenharmony_ci	int nhoff;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	tun_info = skb_tunnel_info(skb);
52962306a36Sopenharmony_ci	if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX) ||
53062306a36Sopenharmony_ci		     ip_tunnel_info_af(tun_info) != AF_INET))
53162306a36Sopenharmony_ci		goto err_free_skb;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	key = &tun_info->key;
53462306a36Sopenharmony_ci	if (!(tun_info->key.tun_flags & TUNNEL_ERSPAN_OPT))
53562306a36Sopenharmony_ci		goto err_free_skb;
53662306a36Sopenharmony_ci	if (tun_info->options_len < sizeof(*md))
53762306a36Sopenharmony_ci		goto err_free_skb;
53862306a36Sopenharmony_ci	md = ip_tunnel_info_opts(tun_info);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	/* ERSPAN has fixed 8 byte GRE header */
54162306a36Sopenharmony_ci	version = md->version;
54262306a36Sopenharmony_ci	tunnel_hlen = 8 + erspan_hdr_len(version);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (skb_cow_head(skb, dev->needed_headroom))
54562306a36Sopenharmony_ci		goto err_free_skb;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (gre_handle_offloads(skb, false))
54862306a36Sopenharmony_ci		goto err_free_skb;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if (skb->len > dev->mtu + dev->hard_header_len) {
55162306a36Sopenharmony_ci		if (pskb_trim(skb, dev->mtu + dev->hard_header_len))
55262306a36Sopenharmony_ci			goto err_free_skb;
55362306a36Sopenharmony_ci		truncate = true;
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	nhoff = skb_network_offset(skb);
55762306a36Sopenharmony_ci	if (skb->protocol == htons(ETH_P_IP) &&
55862306a36Sopenharmony_ci	    (ntohs(ip_hdr(skb)->tot_len) > skb->len - nhoff))
55962306a36Sopenharmony_ci		truncate = true;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	if (skb->protocol == htons(ETH_P_IPV6)) {
56262306a36Sopenharmony_ci		int thoff;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		if (skb_transport_header_was_set(skb))
56562306a36Sopenharmony_ci			thoff = skb_transport_offset(skb);
56662306a36Sopenharmony_ci		else
56762306a36Sopenharmony_ci			thoff = nhoff + sizeof(struct ipv6hdr);
56862306a36Sopenharmony_ci		if (ntohs(ipv6_hdr(skb)->payload_len) > skb->len - thoff)
56962306a36Sopenharmony_ci			truncate = true;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (version == 1) {
57362306a36Sopenharmony_ci		erspan_build_header(skb, ntohl(tunnel_id_to_key32(key->tun_id)),
57462306a36Sopenharmony_ci				    ntohl(md->u.index), truncate, true);
57562306a36Sopenharmony_ci		proto = htons(ETH_P_ERSPAN);
57662306a36Sopenharmony_ci	} else if (version == 2) {
57762306a36Sopenharmony_ci		erspan_build_header_v2(skb,
57862306a36Sopenharmony_ci				       ntohl(tunnel_id_to_key32(key->tun_id)),
57962306a36Sopenharmony_ci				       md->u.md2.dir,
58062306a36Sopenharmony_ci				       get_hwid(&md->u.md2),
58162306a36Sopenharmony_ci				       truncate, true);
58262306a36Sopenharmony_ci		proto = htons(ETH_P_ERSPAN2);
58362306a36Sopenharmony_ci	} else {
58462306a36Sopenharmony_ci		goto err_free_skb;
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	gre_build_header(skb, 8, TUNNEL_SEQ,
58862306a36Sopenharmony_ci			 proto, 0, htonl(atomic_fetch_inc(&tunnel->o_seqno)));
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	ip_md_tunnel_xmit(skb, dev, IPPROTO_GRE, tunnel_hlen);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	return;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_cierr_free_skb:
59562306a36Sopenharmony_ci	kfree_skb(skb);
59662306a36Sopenharmony_ci	DEV_STATS_INC(dev, tx_dropped);
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic int gre_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	struct ip_tunnel_info *info = skb_tunnel_info(skb);
60262306a36Sopenharmony_ci	const struct ip_tunnel_key *key;
60362306a36Sopenharmony_ci	struct rtable *rt;
60462306a36Sopenharmony_ci	struct flowi4 fl4;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	if (ip_tunnel_info_af(info) != AF_INET)
60762306a36Sopenharmony_ci		return -EINVAL;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	key = &info->key;
61062306a36Sopenharmony_ci	ip_tunnel_init_flow(&fl4, IPPROTO_GRE, key->u.ipv4.dst, key->u.ipv4.src,
61162306a36Sopenharmony_ci			    tunnel_id_to_key32(key->tun_id),
61262306a36Sopenharmony_ci			    key->tos & ~INET_ECN_MASK, dev_net(dev), 0,
61362306a36Sopenharmony_ci			    skb->mark, skb_get_hash(skb), key->flow_flags);
61462306a36Sopenharmony_ci	rt = ip_route_output_key(dev_net(dev), &fl4);
61562306a36Sopenharmony_ci	if (IS_ERR(rt))
61662306a36Sopenharmony_ci		return PTR_ERR(rt);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	ip_rt_put(rt);
61962306a36Sopenharmony_ci	info->key.u.ipv4.src = fl4.saddr;
62062306a36Sopenharmony_ci	return 0;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic netdev_tx_t ipgre_xmit(struct sk_buff *skb,
62462306a36Sopenharmony_ci			      struct net_device *dev)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	struct ip_tunnel *tunnel = netdev_priv(dev);
62762306a36Sopenharmony_ci	const struct iphdr *tnl_params;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	if (!pskb_inet_may_pull(skb))
63062306a36Sopenharmony_ci		goto free_skb;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	if (tunnel->collect_md) {
63362306a36Sopenharmony_ci		gre_fb_xmit(skb, dev, skb->protocol);
63462306a36Sopenharmony_ci		return NETDEV_TX_OK;
63562306a36Sopenharmony_ci	}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	if (dev->header_ops) {
63862306a36Sopenharmony_ci		int pull_len = tunnel->hlen + sizeof(struct iphdr);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci		if (skb_cow_head(skb, 0))
64162306a36Sopenharmony_ci			goto free_skb;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci		tnl_params = (const struct iphdr *)skb->data;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci		if (!pskb_network_may_pull(skb, pull_len))
64662306a36Sopenharmony_ci			goto free_skb;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci		/* ip_tunnel_xmit() needs skb->data pointing to gre header. */
64962306a36Sopenharmony_ci		skb_pull(skb, pull_len);
65062306a36Sopenharmony_ci		skb_reset_mac_header(skb);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci		if (skb->ip_summed == CHECKSUM_PARTIAL &&
65362306a36Sopenharmony_ci		    skb_checksum_start(skb) < skb->data)
65462306a36Sopenharmony_ci			goto free_skb;
65562306a36Sopenharmony_ci	} else {
65662306a36Sopenharmony_ci		if (skb_cow_head(skb, dev->needed_headroom))
65762306a36Sopenharmony_ci			goto free_skb;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci		tnl_params = &tunnel->parms.iph;
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (gre_handle_offloads(skb, !!(tunnel->parms.o_flags & TUNNEL_CSUM)))
66362306a36Sopenharmony_ci		goto free_skb;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	__gre_xmit(skb, dev, tnl_params, skb->protocol);
66662306a36Sopenharmony_ci	return NETDEV_TX_OK;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cifree_skb:
66962306a36Sopenharmony_ci	kfree_skb(skb);
67062306a36Sopenharmony_ci	DEV_STATS_INC(dev, tx_dropped);
67162306a36Sopenharmony_ci	return NETDEV_TX_OK;
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic netdev_tx_t erspan_xmit(struct sk_buff *skb,
67562306a36Sopenharmony_ci			       struct net_device *dev)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	struct ip_tunnel *tunnel = netdev_priv(dev);
67862306a36Sopenharmony_ci	bool truncate = false;
67962306a36Sopenharmony_ci	__be16 proto;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (!pskb_inet_may_pull(skb))
68262306a36Sopenharmony_ci		goto free_skb;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	if (tunnel->collect_md) {
68562306a36Sopenharmony_ci		erspan_fb_xmit(skb, dev);
68662306a36Sopenharmony_ci		return NETDEV_TX_OK;
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (gre_handle_offloads(skb, false))
69062306a36Sopenharmony_ci		goto free_skb;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	if (skb_cow_head(skb, dev->needed_headroom))
69362306a36Sopenharmony_ci		goto free_skb;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	if (skb->len > dev->mtu + dev->hard_header_len) {
69662306a36Sopenharmony_ci		if (pskb_trim(skb, dev->mtu + dev->hard_header_len))
69762306a36Sopenharmony_ci			goto free_skb;
69862306a36Sopenharmony_ci		truncate = true;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	/* Push ERSPAN header */
70262306a36Sopenharmony_ci	if (tunnel->erspan_ver == 0) {
70362306a36Sopenharmony_ci		proto = htons(ETH_P_ERSPAN);
70462306a36Sopenharmony_ci		tunnel->parms.o_flags &= ~TUNNEL_SEQ;
70562306a36Sopenharmony_ci	} else if (tunnel->erspan_ver == 1) {
70662306a36Sopenharmony_ci		erspan_build_header(skb, ntohl(tunnel->parms.o_key),
70762306a36Sopenharmony_ci				    tunnel->index,
70862306a36Sopenharmony_ci				    truncate, true);
70962306a36Sopenharmony_ci		proto = htons(ETH_P_ERSPAN);
71062306a36Sopenharmony_ci	} else if (tunnel->erspan_ver == 2) {
71162306a36Sopenharmony_ci		erspan_build_header_v2(skb, ntohl(tunnel->parms.o_key),
71262306a36Sopenharmony_ci				       tunnel->dir, tunnel->hwid,
71362306a36Sopenharmony_ci				       truncate, true);
71462306a36Sopenharmony_ci		proto = htons(ETH_P_ERSPAN2);
71562306a36Sopenharmony_ci	} else {
71662306a36Sopenharmony_ci		goto free_skb;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	tunnel->parms.o_flags &= ~TUNNEL_KEY;
72062306a36Sopenharmony_ci	__gre_xmit(skb, dev, &tunnel->parms.iph, proto);
72162306a36Sopenharmony_ci	return NETDEV_TX_OK;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_cifree_skb:
72462306a36Sopenharmony_ci	kfree_skb(skb);
72562306a36Sopenharmony_ci	DEV_STATS_INC(dev, tx_dropped);
72662306a36Sopenharmony_ci	return NETDEV_TX_OK;
72762306a36Sopenharmony_ci}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_cistatic netdev_tx_t gre_tap_xmit(struct sk_buff *skb,
73062306a36Sopenharmony_ci				struct net_device *dev)
73162306a36Sopenharmony_ci{
73262306a36Sopenharmony_ci	struct ip_tunnel *tunnel = netdev_priv(dev);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	if (!pskb_inet_may_pull(skb))
73562306a36Sopenharmony_ci		goto free_skb;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	if (tunnel->collect_md) {
73862306a36Sopenharmony_ci		gre_fb_xmit(skb, dev, htons(ETH_P_TEB));
73962306a36Sopenharmony_ci		return NETDEV_TX_OK;
74062306a36Sopenharmony_ci	}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	if (gre_handle_offloads(skb, !!(tunnel->parms.o_flags & TUNNEL_CSUM)))
74362306a36Sopenharmony_ci		goto free_skb;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	if (skb_cow_head(skb, dev->needed_headroom))
74662306a36Sopenharmony_ci		goto free_skb;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	__gre_xmit(skb, dev, &tunnel->parms.iph, htons(ETH_P_TEB));
74962306a36Sopenharmony_ci	return NETDEV_TX_OK;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_cifree_skb:
75262306a36Sopenharmony_ci	kfree_skb(skb);
75362306a36Sopenharmony_ci	DEV_STATS_INC(dev, tx_dropped);
75462306a36Sopenharmony_ci	return NETDEV_TX_OK;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic void ipgre_link_update(struct net_device *dev, bool set_mtu)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	struct ip_tunnel *tunnel = netdev_priv(dev);
76062306a36Sopenharmony_ci	__be16 flags;
76162306a36Sopenharmony_ci	int len;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	len = tunnel->tun_hlen;
76462306a36Sopenharmony_ci	tunnel->tun_hlen = gre_calc_hlen(tunnel->parms.o_flags);
76562306a36Sopenharmony_ci	len = tunnel->tun_hlen - len;
76662306a36Sopenharmony_ci	tunnel->hlen = tunnel->hlen + len;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	if (dev->header_ops)
76962306a36Sopenharmony_ci		dev->hard_header_len += len;
77062306a36Sopenharmony_ci	else
77162306a36Sopenharmony_ci		dev->needed_headroom += len;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	if (set_mtu)
77462306a36Sopenharmony_ci		dev->mtu = max_t(int, dev->mtu - len, 68);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	flags = tunnel->parms.o_flags;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	if (flags & TUNNEL_SEQ ||
77962306a36Sopenharmony_ci	    (flags & TUNNEL_CSUM && tunnel->encap.type != TUNNEL_ENCAP_NONE)) {
78062306a36Sopenharmony_ci		dev->features &= ~NETIF_F_GSO_SOFTWARE;
78162306a36Sopenharmony_ci		dev->hw_features &= ~NETIF_F_GSO_SOFTWARE;
78262306a36Sopenharmony_ci	} else {
78362306a36Sopenharmony_ci		dev->features |= NETIF_F_GSO_SOFTWARE;
78462306a36Sopenharmony_ci		dev->hw_features |= NETIF_F_GSO_SOFTWARE;
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistatic int ipgre_tunnel_ctl(struct net_device *dev, struct ip_tunnel_parm *p,
78962306a36Sopenharmony_ci			    int cmd)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	int err;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	if (cmd == SIOCADDTUNNEL || cmd == SIOCCHGTUNNEL) {
79462306a36Sopenharmony_ci		if (p->iph.version != 4 || p->iph.protocol != IPPROTO_GRE ||
79562306a36Sopenharmony_ci		    p->iph.ihl != 5 || (p->iph.frag_off & htons(~IP_DF)) ||
79662306a36Sopenharmony_ci		    ((p->i_flags | p->o_flags) & (GRE_VERSION | GRE_ROUTING)))
79762306a36Sopenharmony_ci			return -EINVAL;
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	p->i_flags = gre_flags_to_tnl_flags(p->i_flags);
80162306a36Sopenharmony_ci	p->o_flags = gre_flags_to_tnl_flags(p->o_flags);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	err = ip_tunnel_ctl(dev, p, cmd);
80462306a36Sopenharmony_ci	if (err)
80562306a36Sopenharmony_ci		return err;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	if (cmd == SIOCCHGTUNNEL) {
80862306a36Sopenharmony_ci		struct ip_tunnel *t = netdev_priv(dev);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci		t->parms.i_flags = p->i_flags;
81162306a36Sopenharmony_ci		t->parms.o_flags = p->o_flags;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci		if (strcmp(dev->rtnl_link_ops->kind, "erspan"))
81462306a36Sopenharmony_ci			ipgre_link_update(dev, true);
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	p->i_flags = gre_tnl_flags_to_gre_flags(p->i_flags);
81862306a36Sopenharmony_ci	p->o_flags = gre_tnl_flags_to_gre_flags(p->o_flags);
81962306a36Sopenharmony_ci	return 0;
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci/* Nice toy. Unfortunately, useless in real life :-)
82362306a36Sopenharmony_ci   It allows to construct virtual multiprotocol broadcast "LAN"
82462306a36Sopenharmony_ci   over the Internet, provided multicast routing is tuned.
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci   I have no idea was this bicycle invented before me,
82862306a36Sopenharmony_ci   so that I had to set ARPHRD_IPGRE to a random value.
82962306a36Sopenharmony_ci   I have an impression, that Cisco could make something similar,
83062306a36Sopenharmony_ci   but this feature is apparently missing in IOS<=11.2(8).
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci   I set up 10.66.66/24 and fec0:6666:6666::0/96 as virtual networks
83362306a36Sopenharmony_ci   with broadcast 224.66.66.66. If you have access to mbone, play with me :-)
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci   ping -t 255 224.66.66.66
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci   If nobody answers, mbone does not work.
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci   ip tunnel add Universe mode gre remote 224.66.66.66 local <Your_real_addr> ttl 255
84062306a36Sopenharmony_ci   ip addr add 10.66.66.<somewhat>/24 dev Universe
84162306a36Sopenharmony_ci   ifconfig Universe up
84262306a36Sopenharmony_ci   ifconfig Universe add fe80::<Your_real_addr>/10
84362306a36Sopenharmony_ci   ifconfig Universe add fec0:6666:6666::<Your_real_addr>/96
84462306a36Sopenharmony_ci   ftp 10.66.66.66
84562306a36Sopenharmony_ci   ...
84662306a36Sopenharmony_ci   ftp fec0:6666:6666::193.233.7.65
84762306a36Sopenharmony_ci   ...
84862306a36Sopenharmony_ci */
84962306a36Sopenharmony_cistatic int ipgre_header(struct sk_buff *skb, struct net_device *dev,
85062306a36Sopenharmony_ci			unsigned short type,
85162306a36Sopenharmony_ci			const void *daddr, const void *saddr, unsigned int len)
85262306a36Sopenharmony_ci{
85362306a36Sopenharmony_ci	struct ip_tunnel *t = netdev_priv(dev);
85462306a36Sopenharmony_ci	struct iphdr *iph;
85562306a36Sopenharmony_ci	struct gre_base_hdr *greh;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	iph = skb_push(skb, t->hlen + sizeof(*iph));
85862306a36Sopenharmony_ci	greh = (struct gre_base_hdr *)(iph+1);
85962306a36Sopenharmony_ci	greh->flags = gre_tnl_flags_to_gre_flags(t->parms.o_flags);
86062306a36Sopenharmony_ci	greh->protocol = htons(type);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	memcpy(iph, &t->parms.iph, sizeof(struct iphdr));
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	/* Set the source hardware address. */
86562306a36Sopenharmony_ci	if (saddr)
86662306a36Sopenharmony_ci		memcpy(&iph->saddr, saddr, 4);
86762306a36Sopenharmony_ci	if (daddr)
86862306a36Sopenharmony_ci		memcpy(&iph->daddr, daddr, 4);
86962306a36Sopenharmony_ci	if (iph->daddr)
87062306a36Sopenharmony_ci		return t->hlen + sizeof(*iph);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	return -(t->hlen + sizeof(*iph));
87362306a36Sopenharmony_ci}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_cistatic int ipgre_header_parse(const struct sk_buff *skb, unsigned char *haddr)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	const struct iphdr *iph = (const struct iphdr *) skb_mac_header(skb);
87862306a36Sopenharmony_ci	memcpy(haddr, &iph->saddr, 4);
87962306a36Sopenharmony_ci	return 4;
88062306a36Sopenharmony_ci}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_cistatic const struct header_ops ipgre_header_ops = {
88362306a36Sopenharmony_ci	.create	= ipgre_header,
88462306a36Sopenharmony_ci	.parse	= ipgre_header_parse,
88562306a36Sopenharmony_ci};
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci#ifdef CONFIG_NET_IPGRE_BROADCAST
88862306a36Sopenharmony_cistatic int ipgre_open(struct net_device *dev)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	struct ip_tunnel *t = netdev_priv(dev);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	if (ipv4_is_multicast(t->parms.iph.daddr)) {
89362306a36Sopenharmony_ci		struct flowi4 fl4;
89462306a36Sopenharmony_ci		struct rtable *rt;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci		rt = ip_route_output_gre(t->net, &fl4,
89762306a36Sopenharmony_ci					 t->parms.iph.daddr,
89862306a36Sopenharmony_ci					 t->parms.iph.saddr,
89962306a36Sopenharmony_ci					 t->parms.o_key,
90062306a36Sopenharmony_ci					 RT_TOS(t->parms.iph.tos),
90162306a36Sopenharmony_ci					 t->parms.link);
90262306a36Sopenharmony_ci		if (IS_ERR(rt))
90362306a36Sopenharmony_ci			return -EADDRNOTAVAIL;
90462306a36Sopenharmony_ci		dev = rt->dst.dev;
90562306a36Sopenharmony_ci		ip_rt_put(rt);
90662306a36Sopenharmony_ci		if (!__in_dev_get_rtnl(dev))
90762306a36Sopenharmony_ci			return -EADDRNOTAVAIL;
90862306a36Sopenharmony_ci		t->mlink = dev->ifindex;
90962306a36Sopenharmony_ci		ip_mc_inc_group(__in_dev_get_rtnl(dev), t->parms.iph.daddr);
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci	return 0;
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cistatic int ipgre_close(struct net_device *dev)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	struct ip_tunnel *t = netdev_priv(dev);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	if (ipv4_is_multicast(t->parms.iph.daddr) && t->mlink) {
91962306a36Sopenharmony_ci		struct in_device *in_dev;
92062306a36Sopenharmony_ci		in_dev = inetdev_by_index(t->net, t->mlink);
92162306a36Sopenharmony_ci		if (in_dev)
92262306a36Sopenharmony_ci			ip_mc_dec_group(in_dev, t->parms.iph.daddr);
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci	return 0;
92562306a36Sopenharmony_ci}
92662306a36Sopenharmony_ci#endif
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_cistatic const struct net_device_ops ipgre_netdev_ops = {
92962306a36Sopenharmony_ci	.ndo_init		= ipgre_tunnel_init,
93062306a36Sopenharmony_ci	.ndo_uninit		= ip_tunnel_uninit,
93162306a36Sopenharmony_ci#ifdef CONFIG_NET_IPGRE_BROADCAST
93262306a36Sopenharmony_ci	.ndo_open		= ipgre_open,
93362306a36Sopenharmony_ci	.ndo_stop		= ipgre_close,
93462306a36Sopenharmony_ci#endif
93562306a36Sopenharmony_ci	.ndo_start_xmit		= ipgre_xmit,
93662306a36Sopenharmony_ci	.ndo_siocdevprivate	= ip_tunnel_siocdevprivate,
93762306a36Sopenharmony_ci	.ndo_change_mtu		= ip_tunnel_change_mtu,
93862306a36Sopenharmony_ci	.ndo_get_stats64	= dev_get_tstats64,
93962306a36Sopenharmony_ci	.ndo_get_iflink		= ip_tunnel_get_iflink,
94062306a36Sopenharmony_ci	.ndo_tunnel_ctl		= ipgre_tunnel_ctl,
94162306a36Sopenharmony_ci};
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci#define GRE_FEATURES (NETIF_F_SG |		\
94462306a36Sopenharmony_ci		      NETIF_F_FRAGLIST |	\
94562306a36Sopenharmony_ci		      NETIF_F_HIGHDMA |		\
94662306a36Sopenharmony_ci		      NETIF_F_HW_CSUM)
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_cistatic void ipgre_tunnel_setup(struct net_device *dev)
94962306a36Sopenharmony_ci{
95062306a36Sopenharmony_ci	dev->netdev_ops		= &ipgre_netdev_ops;
95162306a36Sopenharmony_ci	dev->type		= ARPHRD_IPGRE;
95262306a36Sopenharmony_ci	ip_tunnel_setup(dev, ipgre_net_id);
95362306a36Sopenharmony_ci}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_cistatic void __gre_tunnel_init(struct net_device *dev)
95662306a36Sopenharmony_ci{
95762306a36Sopenharmony_ci	struct ip_tunnel *tunnel;
95862306a36Sopenharmony_ci	__be16 flags;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	tunnel = netdev_priv(dev);
96162306a36Sopenharmony_ci	tunnel->tun_hlen = gre_calc_hlen(tunnel->parms.o_flags);
96262306a36Sopenharmony_ci	tunnel->parms.iph.protocol = IPPROTO_GRE;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen;
96562306a36Sopenharmony_ci	dev->needed_headroom = tunnel->hlen + sizeof(tunnel->parms.iph);
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	dev->features		|= GRE_FEATURES | NETIF_F_LLTX;
96862306a36Sopenharmony_ci	dev->hw_features	|= GRE_FEATURES;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	flags = tunnel->parms.o_flags;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	/* TCP offload with GRE SEQ is not supported, nor can we support 2
97362306a36Sopenharmony_ci	 * levels of outer headers requiring an update.
97462306a36Sopenharmony_ci	 */
97562306a36Sopenharmony_ci	if (flags & TUNNEL_SEQ)
97662306a36Sopenharmony_ci		return;
97762306a36Sopenharmony_ci	if (flags & TUNNEL_CSUM && tunnel->encap.type != TUNNEL_ENCAP_NONE)
97862306a36Sopenharmony_ci		return;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	dev->features |= NETIF_F_GSO_SOFTWARE;
98162306a36Sopenharmony_ci	dev->hw_features |= NETIF_F_GSO_SOFTWARE;
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_cistatic int ipgre_tunnel_init(struct net_device *dev)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	struct ip_tunnel *tunnel = netdev_priv(dev);
98762306a36Sopenharmony_ci	struct iphdr *iph = &tunnel->parms.iph;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	__gre_tunnel_init(dev);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	__dev_addr_set(dev, &iph->saddr, 4);
99262306a36Sopenharmony_ci	memcpy(dev->broadcast, &iph->daddr, 4);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	dev->flags		= IFF_NOARP;
99562306a36Sopenharmony_ci	netif_keep_dst(dev);
99662306a36Sopenharmony_ci	dev->addr_len		= 4;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	if (iph->daddr && !tunnel->collect_md) {
99962306a36Sopenharmony_ci#ifdef CONFIG_NET_IPGRE_BROADCAST
100062306a36Sopenharmony_ci		if (ipv4_is_multicast(iph->daddr)) {
100162306a36Sopenharmony_ci			if (!iph->saddr)
100262306a36Sopenharmony_ci				return -EINVAL;
100362306a36Sopenharmony_ci			dev->flags = IFF_BROADCAST;
100462306a36Sopenharmony_ci			dev->header_ops = &ipgre_header_ops;
100562306a36Sopenharmony_ci			dev->hard_header_len = tunnel->hlen + sizeof(*iph);
100662306a36Sopenharmony_ci			dev->needed_headroom = 0;
100762306a36Sopenharmony_ci		}
100862306a36Sopenharmony_ci#endif
100962306a36Sopenharmony_ci	} else if (!tunnel->collect_md) {
101062306a36Sopenharmony_ci		dev->header_ops = &ipgre_header_ops;
101162306a36Sopenharmony_ci		dev->hard_header_len = tunnel->hlen + sizeof(*iph);
101262306a36Sopenharmony_ci		dev->needed_headroom = 0;
101362306a36Sopenharmony_ci	}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	return ip_tunnel_init(dev);
101662306a36Sopenharmony_ci}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_cistatic const struct gre_protocol ipgre_protocol = {
101962306a36Sopenharmony_ci	.handler     = gre_rcv,
102062306a36Sopenharmony_ci	.err_handler = gre_err,
102162306a36Sopenharmony_ci};
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_cistatic int __net_init ipgre_init_net(struct net *net)
102462306a36Sopenharmony_ci{
102562306a36Sopenharmony_ci	return ip_tunnel_init_net(net, ipgre_net_id, &ipgre_link_ops, NULL);
102662306a36Sopenharmony_ci}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_cistatic void __net_exit ipgre_exit_batch_net(struct list_head *list_net)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	ip_tunnel_delete_nets(list_net, ipgre_net_id, &ipgre_link_ops);
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_cistatic struct pernet_operations ipgre_net_ops = {
103462306a36Sopenharmony_ci	.init = ipgre_init_net,
103562306a36Sopenharmony_ci	.exit_batch = ipgre_exit_batch_net,
103662306a36Sopenharmony_ci	.id   = &ipgre_net_id,
103762306a36Sopenharmony_ci	.size = sizeof(struct ip_tunnel_net),
103862306a36Sopenharmony_ci};
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_cistatic int ipgre_tunnel_validate(struct nlattr *tb[], struct nlattr *data[],
104162306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
104262306a36Sopenharmony_ci{
104362306a36Sopenharmony_ci	__be16 flags;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	if (!data)
104662306a36Sopenharmony_ci		return 0;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	flags = 0;
104962306a36Sopenharmony_ci	if (data[IFLA_GRE_IFLAGS])
105062306a36Sopenharmony_ci		flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]);
105162306a36Sopenharmony_ci	if (data[IFLA_GRE_OFLAGS])
105262306a36Sopenharmony_ci		flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]);
105362306a36Sopenharmony_ci	if (flags & (GRE_VERSION|GRE_ROUTING))
105462306a36Sopenharmony_ci		return -EINVAL;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	if (data[IFLA_GRE_COLLECT_METADATA] &&
105762306a36Sopenharmony_ci	    data[IFLA_GRE_ENCAP_TYPE] &&
105862306a36Sopenharmony_ci	    nla_get_u16(data[IFLA_GRE_ENCAP_TYPE]) != TUNNEL_ENCAP_NONE)
105962306a36Sopenharmony_ci		return -EINVAL;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	return 0;
106262306a36Sopenharmony_ci}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_cistatic int ipgre_tap_validate(struct nlattr *tb[], struct nlattr *data[],
106562306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
106662306a36Sopenharmony_ci{
106762306a36Sopenharmony_ci	__be32 daddr;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	if (tb[IFLA_ADDRESS]) {
107062306a36Sopenharmony_ci		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
107162306a36Sopenharmony_ci			return -EINVAL;
107262306a36Sopenharmony_ci		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
107362306a36Sopenharmony_ci			return -EADDRNOTAVAIL;
107462306a36Sopenharmony_ci	}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if (!data)
107762306a36Sopenharmony_ci		goto out;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	if (data[IFLA_GRE_REMOTE]) {
108062306a36Sopenharmony_ci		memcpy(&daddr, nla_data(data[IFLA_GRE_REMOTE]), 4);
108162306a36Sopenharmony_ci		if (!daddr)
108262306a36Sopenharmony_ci			return -EINVAL;
108362306a36Sopenharmony_ci	}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ciout:
108662306a36Sopenharmony_ci	return ipgre_tunnel_validate(tb, data, extack);
108762306a36Sopenharmony_ci}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_cistatic int erspan_validate(struct nlattr *tb[], struct nlattr *data[],
109062306a36Sopenharmony_ci			   struct netlink_ext_ack *extack)
109162306a36Sopenharmony_ci{
109262306a36Sopenharmony_ci	__be16 flags = 0;
109362306a36Sopenharmony_ci	int ret;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	if (!data)
109662306a36Sopenharmony_ci		return 0;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	ret = ipgre_tap_validate(tb, data, extack);
109962306a36Sopenharmony_ci	if (ret)
110062306a36Sopenharmony_ci		return ret;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	if (data[IFLA_GRE_ERSPAN_VER] &&
110362306a36Sopenharmony_ci	    nla_get_u8(data[IFLA_GRE_ERSPAN_VER]) == 0)
110462306a36Sopenharmony_ci		return 0;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	/* ERSPAN type II/III should only have GRE sequence and key flag */
110762306a36Sopenharmony_ci	if (data[IFLA_GRE_OFLAGS])
110862306a36Sopenharmony_ci		flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]);
110962306a36Sopenharmony_ci	if (data[IFLA_GRE_IFLAGS])
111062306a36Sopenharmony_ci		flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]);
111162306a36Sopenharmony_ci	if (!data[IFLA_GRE_COLLECT_METADATA] &&
111262306a36Sopenharmony_ci	    flags != (GRE_SEQ | GRE_KEY))
111362306a36Sopenharmony_ci		return -EINVAL;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	/* ERSPAN Session ID only has 10-bit. Since we reuse
111662306a36Sopenharmony_ci	 * 32-bit key field as ID, check it's range.
111762306a36Sopenharmony_ci	 */
111862306a36Sopenharmony_ci	if (data[IFLA_GRE_IKEY] &&
111962306a36Sopenharmony_ci	    (ntohl(nla_get_be32(data[IFLA_GRE_IKEY])) & ~ID_MASK))
112062306a36Sopenharmony_ci		return -EINVAL;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	if (data[IFLA_GRE_OKEY] &&
112362306a36Sopenharmony_ci	    (ntohl(nla_get_be32(data[IFLA_GRE_OKEY])) & ~ID_MASK))
112462306a36Sopenharmony_ci		return -EINVAL;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	return 0;
112762306a36Sopenharmony_ci}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_cistatic int ipgre_netlink_parms(struct net_device *dev,
113062306a36Sopenharmony_ci				struct nlattr *data[],
113162306a36Sopenharmony_ci				struct nlattr *tb[],
113262306a36Sopenharmony_ci				struct ip_tunnel_parm *parms,
113362306a36Sopenharmony_ci				__u32 *fwmark)
113462306a36Sopenharmony_ci{
113562306a36Sopenharmony_ci	struct ip_tunnel *t = netdev_priv(dev);
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	memset(parms, 0, sizeof(*parms));
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	parms->iph.protocol = IPPROTO_GRE;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	if (!data)
114262306a36Sopenharmony_ci		return 0;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	if (data[IFLA_GRE_LINK])
114562306a36Sopenharmony_ci		parms->link = nla_get_u32(data[IFLA_GRE_LINK]);
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	if (data[IFLA_GRE_IFLAGS])
114862306a36Sopenharmony_ci		parms->i_flags = gre_flags_to_tnl_flags(nla_get_be16(data[IFLA_GRE_IFLAGS]));
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	if (data[IFLA_GRE_OFLAGS])
115162306a36Sopenharmony_ci		parms->o_flags = gre_flags_to_tnl_flags(nla_get_be16(data[IFLA_GRE_OFLAGS]));
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	if (data[IFLA_GRE_IKEY])
115462306a36Sopenharmony_ci		parms->i_key = nla_get_be32(data[IFLA_GRE_IKEY]);
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	if (data[IFLA_GRE_OKEY])
115762306a36Sopenharmony_ci		parms->o_key = nla_get_be32(data[IFLA_GRE_OKEY]);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	if (data[IFLA_GRE_LOCAL])
116062306a36Sopenharmony_ci		parms->iph.saddr = nla_get_in_addr(data[IFLA_GRE_LOCAL]);
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	if (data[IFLA_GRE_REMOTE])
116362306a36Sopenharmony_ci		parms->iph.daddr = nla_get_in_addr(data[IFLA_GRE_REMOTE]);
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	if (data[IFLA_GRE_TTL])
116662306a36Sopenharmony_ci		parms->iph.ttl = nla_get_u8(data[IFLA_GRE_TTL]);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	if (data[IFLA_GRE_TOS])
116962306a36Sopenharmony_ci		parms->iph.tos = nla_get_u8(data[IFLA_GRE_TOS]);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	if (!data[IFLA_GRE_PMTUDISC] || nla_get_u8(data[IFLA_GRE_PMTUDISC])) {
117262306a36Sopenharmony_ci		if (t->ignore_df)
117362306a36Sopenharmony_ci			return -EINVAL;
117462306a36Sopenharmony_ci		parms->iph.frag_off = htons(IP_DF);
117562306a36Sopenharmony_ci	}
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	if (data[IFLA_GRE_COLLECT_METADATA]) {
117862306a36Sopenharmony_ci		t->collect_md = true;
117962306a36Sopenharmony_ci		if (dev->type == ARPHRD_IPGRE)
118062306a36Sopenharmony_ci			dev->type = ARPHRD_NONE;
118162306a36Sopenharmony_ci	}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	if (data[IFLA_GRE_IGNORE_DF]) {
118462306a36Sopenharmony_ci		if (nla_get_u8(data[IFLA_GRE_IGNORE_DF])
118562306a36Sopenharmony_ci		  && (parms->iph.frag_off & htons(IP_DF)))
118662306a36Sopenharmony_ci			return -EINVAL;
118762306a36Sopenharmony_ci		t->ignore_df = !!nla_get_u8(data[IFLA_GRE_IGNORE_DF]);
118862306a36Sopenharmony_ci	}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	if (data[IFLA_GRE_FWMARK])
119162306a36Sopenharmony_ci		*fwmark = nla_get_u32(data[IFLA_GRE_FWMARK]);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	return 0;
119462306a36Sopenharmony_ci}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_cistatic int erspan_netlink_parms(struct net_device *dev,
119762306a36Sopenharmony_ci				struct nlattr *data[],
119862306a36Sopenharmony_ci				struct nlattr *tb[],
119962306a36Sopenharmony_ci				struct ip_tunnel_parm *parms,
120062306a36Sopenharmony_ci				__u32 *fwmark)
120162306a36Sopenharmony_ci{
120262306a36Sopenharmony_ci	struct ip_tunnel *t = netdev_priv(dev);
120362306a36Sopenharmony_ci	int err;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	err = ipgre_netlink_parms(dev, data, tb, parms, fwmark);
120662306a36Sopenharmony_ci	if (err)
120762306a36Sopenharmony_ci		return err;
120862306a36Sopenharmony_ci	if (!data)
120962306a36Sopenharmony_ci		return 0;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	if (data[IFLA_GRE_ERSPAN_VER]) {
121262306a36Sopenharmony_ci		t->erspan_ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]);
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci		if (t->erspan_ver > 2)
121562306a36Sopenharmony_ci			return -EINVAL;
121662306a36Sopenharmony_ci	}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	if (t->erspan_ver == 1) {
121962306a36Sopenharmony_ci		if (data[IFLA_GRE_ERSPAN_INDEX]) {
122062306a36Sopenharmony_ci			t->index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]);
122162306a36Sopenharmony_ci			if (t->index & ~INDEX_MASK)
122262306a36Sopenharmony_ci				return -EINVAL;
122362306a36Sopenharmony_ci		}
122462306a36Sopenharmony_ci	} else if (t->erspan_ver == 2) {
122562306a36Sopenharmony_ci		if (data[IFLA_GRE_ERSPAN_DIR]) {
122662306a36Sopenharmony_ci			t->dir = nla_get_u8(data[IFLA_GRE_ERSPAN_DIR]);
122762306a36Sopenharmony_ci			if (t->dir & ~(DIR_MASK >> DIR_OFFSET))
122862306a36Sopenharmony_ci				return -EINVAL;
122962306a36Sopenharmony_ci		}
123062306a36Sopenharmony_ci		if (data[IFLA_GRE_ERSPAN_HWID]) {
123162306a36Sopenharmony_ci			t->hwid = nla_get_u16(data[IFLA_GRE_ERSPAN_HWID]);
123262306a36Sopenharmony_ci			if (t->hwid & ~(HWID_MASK >> HWID_OFFSET))
123362306a36Sopenharmony_ci				return -EINVAL;
123462306a36Sopenharmony_ci		}
123562306a36Sopenharmony_ci	}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	return 0;
123862306a36Sopenharmony_ci}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci/* This function returns true when ENCAP attributes are present in the nl msg */
124162306a36Sopenharmony_cistatic bool ipgre_netlink_encap_parms(struct nlattr *data[],
124262306a36Sopenharmony_ci				      struct ip_tunnel_encap *ipencap)
124362306a36Sopenharmony_ci{
124462306a36Sopenharmony_ci	bool ret = false;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	memset(ipencap, 0, sizeof(*ipencap));
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	if (!data)
124962306a36Sopenharmony_ci		return ret;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	if (data[IFLA_GRE_ENCAP_TYPE]) {
125262306a36Sopenharmony_ci		ret = true;
125362306a36Sopenharmony_ci		ipencap->type = nla_get_u16(data[IFLA_GRE_ENCAP_TYPE]);
125462306a36Sopenharmony_ci	}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	if (data[IFLA_GRE_ENCAP_FLAGS]) {
125762306a36Sopenharmony_ci		ret = true;
125862306a36Sopenharmony_ci		ipencap->flags = nla_get_u16(data[IFLA_GRE_ENCAP_FLAGS]);
125962306a36Sopenharmony_ci	}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	if (data[IFLA_GRE_ENCAP_SPORT]) {
126262306a36Sopenharmony_ci		ret = true;
126362306a36Sopenharmony_ci		ipencap->sport = nla_get_be16(data[IFLA_GRE_ENCAP_SPORT]);
126462306a36Sopenharmony_ci	}
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	if (data[IFLA_GRE_ENCAP_DPORT]) {
126762306a36Sopenharmony_ci		ret = true;
126862306a36Sopenharmony_ci		ipencap->dport = nla_get_be16(data[IFLA_GRE_ENCAP_DPORT]);
126962306a36Sopenharmony_ci	}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	return ret;
127262306a36Sopenharmony_ci}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_cistatic int gre_tap_init(struct net_device *dev)
127562306a36Sopenharmony_ci{
127662306a36Sopenharmony_ci	__gre_tunnel_init(dev);
127762306a36Sopenharmony_ci	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
127862306a36Sopenharmony_ci	netif_keep_dst(dev);
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	return ip_tunnel_init(dev);
128162306a36Sopenharmony_ci}
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_cistatic const struct net_device_ops gre_tap_netdev_ops = {
128462306a36Sopenharmony_ci	.ndo_init		= gre_tap_init,
128562306a36Sopenharmony_ci	.ndo_uninit		= ip_tunnel_uninit,
128662306a36Sopenharmony_ci	.ndo_start_xmit		= gre_tap_xmit,
128762306a36Sopenharmony_ci	.ndo_set_mac_address 	= eth_mac_addr,
128862306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
128962306a36Sopenharmony_ci	.ndo_change_mtu		= ip_tunnel_change_mtu,
129062306a36Sopenharmony_ci	.ndo_get_stats64	= dev_get_tstats64,
129162306a36Sopenharmony_ci	.ndo_get_iflink		= ip_tunnel_get_iflink,
129262306a36Sopenharmony_ci	.ndo_fill_metadata_dst	= gre_fill_metadata_dst,
129362306a36Sopenharmony_ci};
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_cistatic int erspan_tunnel_init(struct net_device *dev)
129662306a36Sopenharmony_ci{
129762306a36Sopenharmony_ci	struct ip_tunnel *tunnel = netdev_priv(dev);
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	if (tunnel->erspan_ver == 0)
130062306a36Sopenharmony_ci		tunnel->tun_hlen = 4; /* 4-byte GRE hdr. */
130162306a36Sopenharmony_ci	else
130262306a36Sopenharmony_ci		tunnel->tun_hlen = 8; /* 8-byte GRE hdr. */
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	tunnel->parms.iph.protocol = IPPROTO_GRE;
130562306a36Sopenharmony_ci	tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen +
130662306a36Sopenharmony_ci		       erspan_hdr_len(tunnel->erspan_ver);
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	dev->features		|= GRE_FEATURES;
130962306a36Sopenharmony_ci	dev->hw_features	|= GRE_FEATURES;
131062306a36Sopenharmony_ci	dev->priv_flags		|= IFF_LIVE_ADDR_CHANGE;
131162306a36Sopenharmony_ci	netif_keep_dst(dev);
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	return ip_tunnel_init(dev);
131462306a36Sopenharmony_ci}
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_cistatic const struct net_device_ops erspan_netdev_ops = {
131762306a36Sopenharmony_ci	.ndo_init		= erspan_tunnel_init,
131862306a36Sopenharmony_ci	.ndo_uninit		= ip_tunnel_uninit,
131962306a36Sopenharmony_ci	.ndo_start_xmit		= erspan_xmit,
132062306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
132162306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
132262306a36Sopenharmony_ci	.ndo_change_mtu		= ip_tunnel_change_mtu,
132362306a36Sopenharmony_ci	.ndo_get_stats64	= dev_get_tstats64,
132462306a36Sopenharmony_ci	.ndo_get_iflink		= ip_tunnel_get_iflink,
132562306a36Sopenharmony_ci	.ndo_fill_metadata_dst	= gre_fill_metadata_dst,
132662306a36Sopenharmony_ci};
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_cistatic void ipgre_tap_setup(struct net_device *dev)
132962306a36Sopenharmony_ci{
133062306a36Sopenharmony_ci	ether_setup(dev);
133162306a36Sopenharmony_ci	dev->max_mtu = 0;
133262306a36Sopenharmony_ci	dev->netdev_ops	= &gre_tap_netdev_ops;
133362306a36Sopenharmony_ci	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
133462306a36Sopenharmony_ci	dev->priv_flags	|= IFF_LIVE_ADDR_CHANGE;
133562306a36Sopenharmony_ci	ip_tunnel_setup(dev, gre_tap_net_id);
133662306a36Sopenharmony_ci}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_cistatic int
133962306a36Sopenharmony_ciipgre_newlink_encap_setup(struct net_device *dev, struct nlattr *data[])
134062306a36Sopenharmony_ci{
134162306a36Sopenharmony_ci	struct ip_tunnel_encap ipencap;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	if (ipgre_netlink_encap_parms(data, &ipencap)) {
134462306a36Sopenharmony_ci		struct ip_tunnel *t = netdev_priv(dev);
134562306a36Sopenharmony_ci		int err = ip_tunnel_encap_setup(t, &ipencap);
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci		if (err < 0)
134862306a36Sopenharmony_ci			return err;
134962306a36Sopenharmony_ci	}
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	return 0;
135262306a36Sopenharmony_ci}
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_cistatic int ipgre_newlink(struct net *src_net, struct net_device *dev,
135562306a36Sopenharmony_ci			 struct nlattr *tb[], struct nlattr *data[],
135662306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
135762306a36Sopenharmony_ci{
135862306a36Sopenharmony_ci	struct ip_tunnel_parm p;
135962306a36Sopenharmony_ci	__u32 fwmark = 0;
136062306a36Sopenharmony_ci	int err;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	err = ipgre_newlink_encap_setup(dev, data);
136362306a36Sopenharmony_ci	if (err)
136462306a36Sopenharmony_ci		return err;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	err = ipgre_netlink_parms(dev, data, tb, &p, &fwmark);
136762306a36Sopenharmony_ci	if (err < 0)
136862306a36Sopenharmony_ci		return err;
136962306a36Sopenharmony_ci	return ip_tunnel_newlink(dev, tb, &p, fwmark);
137062306a36Sopenharmony_ci}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_cistatic int erspan_newlink(struct net *src_net, struct net_device *dev,
137362306a36Sopenharmony_ci			  struct nlattr *tb[], struct nlattr *data[],
137462306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
137562306a36Sopenharmony_ci{
137662306a36Sopenharmony_ci	struct ip_tunnel_parm p;
137762306a36Sopenharmony_ci	__u32 fwmark = 0;
137862306a36Sopenharmony_ci	int err;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	err = ipgre_newlink_encap_setup(dev, data);
138162306a36Sopenharmony_ci	if (err)
138262306a36Sopenharmony_ci		return err;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	err = erspan_netlink_parms(dev, data, tb, &p, &fwmark);
138562306a36Sopenharmony_ci	if (err)
138662306a36Sopenharmony_ci		return err;
138762306a36Sopenharmony_ci	return ip_tunnel_newlink(dev, tb, &p, fwmark);
138862306a36Sopenharmony_ci}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_cistatic int ipgre_changelink(struct net_device *dev, struct nlattr *tb[],
139162306a36Sopenharmony_ci			    struct nlattr *data[],
139262306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
139362306a36Sopenharmony_ci{
139462306a36Sopenharmony_ci	struct ip_tunnel *t = netdev_priv(dev);
139562306a36Sopenharmony_ci	__u32 fwmark = t->fwmark;
139662306a36Sopenharmony_ci	struct ip_tunnel_parm p;
139762306a36Sopenharmony_ci	int err;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	err = ipgre_newlink_encap_setup(dev, data);
140062306a36Sopenharmony_ci	if (err)
140162306a36Sopenharmony_ci		return err;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	err = ipgre_netlink_parms(dev, data, tb, &p, &fwmark);
140462306a36Sopenharmony_ci	if (err < 0)
140562306a36Sopenharmony_ci		return err;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	err = ip_tunnel_changelink(dev, tb, &p, fwmark);
140862306a36Sopenharmony_ci	if (err < 0)
140962306a36Sopenharmony_ci		return err;
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	t->parms.i_flags = p.i_flags;
141262306a36Sopenharmony_ci	t->parms.o_flags = p.o_flags;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	ipgre_link_update(dev, !tb[IFLA_MTU]);
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	return 0;
141762306a36Sopenharmony_ci}
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_cistatic int erspan_changelink(struct net_device *dev, struct nlattr *tb[],
142062306a36Sopenharmony_ci			     struct nlattr *data[],
142162306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
142262306a36Sopenharmony_ci{
142362306a36Sopenharmony_ci	struct ip_tunnel *t = netdev_priv(dev);
142462306a36Sopenharmony_ci	__u32 fwmark = t->fwmark;
142562306a36Sopenharmony_ci	struct ip_tunnel_parm p;
142662306a36Sopenharmony_ci	int err;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	err = ipgre_newlink_encap_setup(dev, data);
142962306a36Sopenharmony_ci	if (err)
143062306a36Sopenharmony_ci		return err;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	err = erspan_netlink_parms(dev, data, tb, &p, &fwmark);
143362306a36Sopenharmony_ci	if (err < 0)
143462306a36Sopenharmony_ci		return err;
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	err = ip_tunnel_changelink(dev, tb, &p, fwmark);
143762306a36Sopenharmony_ci	if (err < 0)
143862306a36Sopenharmony_ci		return err;
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	t->parms.i_flags = p.i_flags;
144162306a36Sopenharmony_ci	t->parms.o_flags = p.o_flags;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	return 0;
144462306a36Sopenharmony_ci}
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_cistatic size_t ipgre_get_size(const struct net_device *dev)
144762306a36Sopenharmony_ci{
144862306a36Sopenharmony_ci	return
144962306a36Sopenharmony_ci		/* IFLA_GRE_LINK */
145062306a36Sopenharmony_ci		nla_total_size(4) +
145162306a36Sopenharmony_ci		/* IFLA_GRE_IFLAGS */
145262306a36Sopenharmony_ci		nla_total_size(2) +
145362306a36Sopenharmony_ci		/* IFLA_GRE_OFLAGS */
145462306a36Sopenharmony_ci		nla_total_size(2) +
145562306a36Sopenharmony_ci		/* IFLA_GRE_IKEY */
145662306a36Sopenharmony_ci		nla_total_size(4) +
145762306a36Sopenharmony_ci		/* IFLA_GRE_OKEY */
145862306a36Sopenharmony_ci		nla_total_size(4) +
145962306a36Sopenharmony_ci		/* IFLA_GRE_LOCAL */
146062306a36Sopenharmony_ci		nla_total_size(4) +
146162306a36Sopenharmony_ci		/* IFLA_GRE_REMOTE */
146262306a36Sopenharmony_ci		nla_total_size(4) +
146362306a36Sopenharmony_ci		/* IFLA_GRE_TTL */
146462306a36Sopenharmony_ci		nla_total_size(1) +
146562306a36Sopenharmony_ci		/* IFLA_GRE_TOS */
146662306a36Sopenharmony_ci		nla_total_size(1) +
146762306a36Sopenharmony_ci		/* IFLA_GRE_PMTUDISC */
146862306a36Sopenharmony_ci		nla_total_size(1) +
146962306a36Sopenharmony_ci		/* IFLA_GRE_ENCAP_TYPE */
147062306a36Sopenharmony_ci		nla_total_size(2) +
147162306a36Sopenharmony_ci		/* IFLA_GRE_ENCAP_FLAGS */
147262306a36Sopenharmony_ci		nla_total_size(2) +
147362306a36Sopenharmony_ci		/* IFLA_GRE_ENCAP_SPORT */
147462306a36Sopenharmony_ci		nla_total_size(2) +
147562306a36Sopenharmony_ci		/* IFLA_GRE_ENCAP_DPORT */
147662306a36Sopenharmony_ci		nla_total_size(2) +
147762306a36Sopenharmony_ci		/* IFLA_GRE_COLLECT_METADATA */
147862306a36Sopenharmony_ci		nla_total_size(0) +
147962306a36Sopenharmony_ci		/* IFLA_GRE_IGNORE_DF */
148062306a36Sopenharmony_ci		nla_total_size(1) +
148162306a36Sopenharmony_ci		/* IFLA_GRE_FWMARK */
148262306a36Sopenharmony_ci		nla_total_size(4) +
148362306a36Sopenharmony_ci		/* IFLA_GRE_ERSPAN_INDEX */
148462306a36Sopenharmony_ci		nla_total_size(4) +
148562306a36Sopenharmony_ci		/* IFLA_GRE_ERSPAN_VER */
148662306a36Sopenharmony_ci		nla_total_size(1) +
148762306a36Sopenharmony_ci		/* IFLA_GRE_ERSPAN_DIR */
148862306a36Sopenharmony_ci		nla_total_size(1) +
148962306a36Sopenharmony_ci		/* IFLA_GRE_ERSPAN_HWID */
149062306a36Sopenharmony_ci		nla_total_size(2) +
149162306a36Sopenharmony_ci		0;
149262306a36Sopenharmony_ci}
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_cistatic int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev)
149562306a36Sopenharmony_ci{
149662306a36Sopenharmony_ci	struct ip_tunnel *t = netdev_priv(dev);
149762306a36Sopenharmony_ci	struct ip_tunnel_parm *p = &t->parms;
149862306a36Sopenharmony_ci	__be16 o_flags = p->o_flags;
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) ||
150162306a36Sopenharmony_ci	    nla_put_be16(skb, IFLA_GRE_IFLAGS,
150262306a36Sopenharmony_ci			 gre_tnl_flags_to_gre_flags(p->i_flags)) ||
150362306a36Sopenharmony_ci	    nla_put_be16(skb, IFLA_GRE_OFLAGS,
150462306a36Sopenharmony_ci			 gre_tnl_flags_to_gre_flags(o_flags)) ||
150562306a36Sopenharmony_ci	    nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) ||
150662306a36Sopenharmony_ci	    nla_put_be32(skb, IFLA_GRE_OKEY, p->o_key) ||
150762306a36Sopenharmony_ci	    nla_put_in_addr(skb, IFLA_GRE_LOCAL, p->iph.saddr) ||
150862306a36Sopenharmony_ci	    nla_put_in_addr(skb, IFLA_GRE_REMOTE, p->iph.daddr) ||
150962306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_GRE_TTL, p->iph.ttl) ||
151062306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_GRE_TOS, p->iph.tos) ||
151162306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_GRE_PMTUDISC,
151262306a36Sopenharmony_ci		       !!(p->iph.frag_off & htons(IP_DF))) ||
151362306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_GRE_FWMARK, t->fwmark))
151462306a36Sopenharmony_ci		goto nla_put_failure;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	if (nla_put_u16(skb, IFLA_GRE_ENCAP_TYPE,
151762306a36Sopenharmony_ci			t->encap.type) ||
151862306a36Sopenharmony_ci	    nla_put_be16(skb, IFLA_GRE_ENCAP_SPORT,
151962306a36Sopenharmony_ci			 t->encap.sport) ||
152062306a36Sopenharmony_ci	    nla_put_be16(skb, IFLA_GRE_ENCAP_DPORT,
152162306a36Sopenharmony_ci			 t->encap.dport) ||
152262306a36Sopenharmony_ci	    nla_put_u16(skb, IFLA_GRE_ENCAP_FLAGS,
152362306a36Sopenharmony_ci			t->encap.flags))
152462306a36Sopenharmony_ci		goto nla_put_failure;
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	if (nla_put_u8(skb, IFLA_GRE_IGNORE_DF, t->ignore_df))
152762306a36Sopenharmony_ci		goto nla_put_failure;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	if (t->collect_md) {
153062306a36Sopenharmony_ci		if (nla_put_flag(skb, IFLA_GRE_COLLECT_METADATA))
153162306a36Sopenharmony_ci			goto nla_put_failure;
153262306a36Sopenharmony_ci	}
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	return 0;
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_cinla_put_failure:
153762306a36Sopenharmony_ci	return -EMSGSIZE;
153862306a36Sopenharmony_ci}
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_cistatic int erspan_fill_info(struct sk_buff *skb, const struct net_device *dev)
154162306a36Sopenharmony_ci{
154262306a36Sopenharmony_ci	struct ip_tunnel *t = netdev_priv(dev);
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	if (t->erspan_ver <= 2) {
154562306a36Sopenharmony_ci		if (t->erspan_ver != 0 && !t->collect_md)
154662306a36Sopenharmony_ci			t->parms.o_flags |= TUNNEL_KEY;
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci		if (nla_put_u8(skb, IFLA_GRE_ERSPAN_VER, t->erspan_ver))
154962306a36Sopenharmony_ci			goto nla_put_failure;
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci		if (t->erspan_ver == 1) {
155262306a36Sopenharmony_ci			if (nla_put_u32(skb, IFLA_GRE_ERSPAN_INDEX, t->index))
155362306a36Sopenharmony_ci				goto nla_put_failure;
155462306a36Sopenharmony_ci		} else if (t->erspan_ver == 2) {
155562306a36Sopenharmony_ci			if (nla_put_u8(skb, IFLA_GRE_ERSPAN_DIR, t->dir))
155662306a36Sopenharmony_ci				goto nla_put_failure;
155762306a36Sopenharmony_ci			if (nla_put_u16(skb, IFLA_GRE_ERSPAN_HWID, t->hwid))
155862306a36Sopenharmony_ci				goto nla_put_failure;
155962306a36Sopenharmony_ci		}
156062306a36Sopenharmony_ci	}
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	return ipgre_fill_info(skb, dev);
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_cinla_put_failure:
156562306a36Sopenharmony_ci	return -EMSGSIZE;
156662306a36Sopenharmony_ci}
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_cistatic void erspan_setup(struct net_device *dev)
156962306a36Sopenharmony_ci{
157062306a36Sopenharmony_ci	struct ip_tunnel *t = netdev_priv(dev);
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	ether_setup(dev);
157362306a36Sopenharmony_ci	dev->max_mtu = 0;
157462306a36Sopenharmony_ci	dev->netdev_ops = &erspan_netdev_ops;
157562306a36Sopenharmony_ci	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
157662306a36Sopenharmony_ci	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
157762306a36Sopenharmony_ci	ip_tunnel_setup(dev, erspan_net_id);
157862306a36Sopenharmony_ci	t->erspan_ver = 1;
157962306a36Sopenharmony_ci}
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_cistatic const struct nla_policy ipgre_policy[IFLA_GRE_MAX + 1] = {
158262306a36Sopenharmony_ci	[IFLA_GRE_LINK]		= { .type = NLA_U32 },
158362306a36Sopenharmony_ci	[IFLA_GRE_IFLAGS]	= { .type = NLA_U16 },
158462306a36Sopenharmony_ci	[IFLA_GRE_OFLAGS]	= { .type = NLA_U16 },
158562306a36Sopenharmony_ci	[IFLA_GRE_IKEY]		= { .type = NLA_U32 },
158662306a36Sopenharmony_ci	[IFLA_GRE_OKEY]		= { .type = NLA_U32 },
158762306a36Sopenharmony_ci	[IFLA_GRE_LOCAL]	= { .len = sizeof_field(struct iphdr, saddr) },
158862306a36Sopenharmony_ci	[IFLA_GRE_REMOTE]	= { .len = sizeof_field(struct iphdr, daddr) },
158962306a36Sopenharmony_ci	[IFLA_GRE_TTL]		= { .type = NLA_U8 },
159062306a36Sopenharmony_ci	[IFLA_GRE_TOS]		= { .type = NLA_U8 },
159162306a36Sopenharmony_ci	[IFLA_GRE_PMTUDISC]	= { .type = NLA_U8 },
159262306a36Sopenharmony_ci	[IFLA_GRE_ENCAP_TYPE]	= { .type = NLA_U16 },
159362306a36Sopenharmony_ci	[IFLA_GRE_ENCAP_FLAGS]	= { .type = NLA_U16 },
159462306a36Sopenharmony_ci	[IFLA_GRE_ENCAP_SPORT]	= { .type = NLA_U16 },
159562306a36Sopenharmony_ci	[IFLA_GRE_ENCAP_DPORT]	= { .type = NLA_U16 },
159662306a36Sopenharmony_ci	[IFLA_GRE_COLLECT_METADATA]	= { .type = NLA_FLAG },
159762306a36Sopenharmony_ci	[IFLA_GRE_IGNORE_DF]	= { .type = NLA_U8 },
159862306a36Sopenharmony_ci	[IFLA_GRE_FWMARK]	= { .type = NLA_U32 },
159962306a36Sopenharmony_ci	[IFLA_GRE_ERSPAN_INDEX]	= { .type = NLA_U32 },
160062306a36Sopenharmony_ci	[IFLA_GRE_ERSPAN_VER]	= { .type = NLA_U8 },
160162306a36Sopenharmony_ci	[IFLA_GRE_ERSPAN_DIR]	= { .type = NLA_U8 },
160262306a36Sopenharmony_ci	[IFLA_GRE_ERSPAN_HWID]	= { .type = NLA_U16 },
160362306a36Sopenharmony_ci};
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_cistatic struct rtnl_link_ops ipgre_link_ops __read_mostly = {
160662306a36Sopenharmony_ci	.kind		= "gre",
160762306a36Sopenharmony_ci	.maxtype	= IFLA_GRE_MAX,
160862306a36Sopenharmony_ci	.policy		= ipgre_policy,
160962306a36Sopenharmony_ci	.priv_size	= sizeof(struct ip_tunnel),
161062306a36Sopenharmony_ci	.setup		= ipgre_tunnel_setup,
161162306a36Sopenharmony_ci	.validate	= ipgre_tunnel_validate,
161262306a36Sopenharmony_ci	.newlink	= ipgre_newlink,
161362306a36Sopenharmony_ci	.changelink	= ipgre_changelink,
161462306a36Sopenharmony_ci	.dellink	= ip_tunnel_dellink,
161562306a36Sopenharmony_ci	.get_size	= ipgre_get_size,
161662306a36Sopenharmony_ci	.fill_info	= ipgre_fill_info,
161762306a36Sopenharmony_ci	.get_link_net	= ip_tunnel_get_link_net,
161862306a36Sopenharmony_ci};
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_cistatic struct rtnl_link_ops ipgre_tap_ops __read_mostly = {
162162306a36Sopenharmony_ci	.kind		= "gretap",
162262306a36Sopenharmony_ci	.maxtype	= IFLA_GRE_MAX,
162362306a36Sopenharmony_ci	.policy		= ipgre_policy,
162462306a36Sopenharmony_ci	.priv_size	= sizeof(struct ip_tunnel),
162562306a36Sopenharmony_ci	.setup		= ipgre_tap_setup,
162662306a36Sopenharmony_ci	.validate	= ipgre_tap_validate,
162762306a36Sopenharmony_ci	.newlink	= ipgre_newlink,
162862306a36Sopenharmony_ci	.changelink	= ipgre_changelink,
162962306a36Sopenharmony_ci	.dellink	= ip_tunnel_dellink,
163062306a36Sopenharmony_ci	.get_size	= ipgre_get_size,
163162306a36Sopenharmony_ci	.fill_info	= ipgre_fill_info,
163262306a36Sopenharmony_ci	.get_link_net	= ip_tunnel_get_link_net,
163362306a36Sopenharmony_ci};
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_cistatic struct rtnl_link_ops erspan_link_ops __read_mostly = {
163662306a36Sopenharmony_ci	.kind		= "erspan",
163762306a36Sopenharmony_ci	.maxtype	= IFLA_GRE_MAX,
163862306a36Sopenharmony_ci	.policy		= ipgre_policy,
163962306a36Sopenharmony_ci	.priv_size	= sizeof(struct ip_tunnel),
164062306a36Sopenharmony_ci	.setup		= erspan_setup,
164162306a36Sopenharmony_ci	.validate	= erspan_validate,
164262306a36Sopenharmony_ci	.newlink	= erspan_newlink,
164362306a36Sopenharmony_ci	.changelink	= erspan_changelink,
164462306a36Sopenharmony_ci	.dellink	= ip_tunnel_dellink,
164562306a36Sopenharmony_ci	.get_size	= ipgre_get_size,
164662306a36Sopenharmony_ci	.fill_info	= erspan_fill_info,
164762306a36Sopenharmony_ci	.get_link_net	= ip_tunnel_get_link_net,
164862306a36Sopenharmony_ci};
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_cistruct net_device *gretap_fb_dev_create(struct net *net, const char *name,
165162306a36Sopenharmony_ci					u8 name_assign_type)
165262306a36Sopenharmony_ci{
165362306a36Sopenharmony_ci	struct nlattr *tb[IFLA_MAX + 1];
165462306a36Sopenharmony_ci	struct net_device *dev;
165562306a36Sopenharmony_ci	LIST_HEAD(list_kill);
165662306a36Sopenharmony_ci	struct ip_tunnel *t;
165762306a36Sopenharmony_ci	int err;
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	memset(&tb, 0, sizeof(tb));
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	dev = rtnl_create_link(net, name, name_assign_type,
166262306a36Sopenharmony_ci			       &ipgre_tap_ops, tb, NULL);
166362306a36Sopenharmony_ci	if (IS_ERR(dev))
166462306a36Sopenharmony_ci		return dev;
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	/* Configure flow based GRE device. */
166762306a36Sopenharmony_ci	t = netdev_priv(dev);
166862306a36Sopenharmony_ci	t->collect_md = true;
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	err = ipgre_newlink(net, dev, tb, NULL, NULL);
167162306a36Sopenharmony_ci	if (err < 0) {
167262306a36Sopenharmony_ci		free_netdev(dev);
167362306a36Sopenharmony_ci		return ERR_PTR(err);
167462306a36Sopenharmony_ci	}
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	/* openvswitch users expect packet sizes to be unrestricted,
167762306a36Sopenharmony_ci	 * so set the largest MTU we can.
167862306a36Sopenharmony_ci	 */
167962306a36Sopenharmony_ci	err = __ip_tunnel_change_mtu(dev, IP_MAX_MTU, false);
168062306a36Sopenharmony_ci	if (err)
168162306a36Sopenharmony_ci		goto out;
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	err = rtnl_configure_link(dev, NULL, 0, NULL);
168462306a36Sopenharmony_ci	if (err < 0)
168562306a36Sopenharmony_ci		goto out;
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	return dev;
168862306a36Sopenharmony_ciout:
168962306a36Sopenharmony_ci	ip_tunnel_dellink(dev, &list_kill);
169062306a36Sopenharmony_ci	unregister_netdevice_many(&list_kill);
169162306a36Sopenharmony_ci	return ERR_PTR(err);
169262306a36Sopenharmony_ci}
169362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(gretap_fb_dev_create);
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_cistatic int __net_init ipgre_tap_init_net(struct net *net)
169662306a36Sopenharmony_ci{
169762306a36Sopenharmony_ci	return ip_tunnel_init_net(net, gre_tap_net_id, &ipgre_tap_ops, "gretap0");
169862306a36Sopenharmony_ci}
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_cistatic void __net_exit ipgre_tap_exit_batch_net(struct list_head *list_net)
170162306a36Sopenharmony_ci{
170262306a36Sopenharmony_ci	ip_tunnel_delete_nets(list_net, gre_tap_net_id, &ipgre_tap_ops);
170362306a36Sopenharmony_ci}
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_cistatic struct pernet_operations ipgre_tap_net_ops = {
170662306a36Sopenharmony_ci	.init = ipgre_tap_init_net,
170762306a36Sopenharmony_ci	.exit_batch = ipgre_tap_exit_batch_net,
170862306a36Sopenharmony_ci	.id   = &gre_tap_net_id,
170962306a36Sopenharmony_ci	.size = sizeof(struct ip_tunnel_net),
171062306a36Sopenharmony_ci};
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_cistatic int __net_init erspan_init_net(struct net *net)
171362306a36Sopenharmony_ci{
171462306a36Sopenharmony_ci	return ip_tunnel_init_net(net, erspan_net_id,
171562306a36Sopenharmony_ci				  &erspan_link_ops, "erspan0");
171662306a36Sopenharmony_ci}
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_cistatic void __net_exit erspan_exit_batch_net(struct list_head *net_list)
171962306a36Sopenharmony_ci{
172062306a36Sopenharmony_ci	ip_tunnel_delete_nets(net_list, erspan_net_id, &erspan_link_ops);
172162306a36Sopenharmony_ci}
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_cistatic struct pernet_operations erspan_net_ops = {
172462306a36Sopenharmony_ci	.init = erspan_init_net,
172562306a36Sopenharmony_ci	.exit_batch = erspan_exit_batch_net,
172662306a36Sopenharmony_ci	.id   = &erspan_net_id,
172762306a36Sopenharmony_ci	.size = sizeof(struct ip_tunnel_net),
172862306a36Sopenharmony_ci};
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_cistatic int __init ipgre_init(void)
173162306a36Sopenharmony_ci{
173262306a36Sopenharmony_ci	int err;
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	pr_info("GRE over IPv4 tunneling driver\n");
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	err = register_pernet_device(&ipgre_net_ops);
173762306a36Sopenharmony_ci	if (err < 0)
173862306a36Sopenharmony_ci		return err;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	err = register_pernet_device(&ipgre_tap_net_ops);
174162306a36Sopenharmony_ci	if (err < 0)
174262306a36Sopenharmony_ci		goto pnet_tap_failed;
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	err = register_pernet_device(&erspan_net_ops);
174562306a36Sopenharmony_ci	if (err < 0)
174662306a36Sopenharmony_ci		goto pnet_erspan_failed;
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci	err = gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO);
174962306a36Sopenharmony_ci	if (err < 0) {
175062306a36Sopenharmony_ci		pr_info("%s: can't add protocol\n", __func__);
175162306a36Sopenharmony_ci		goto add_proto_failed;
175262306a36Sopenharmony_ci	}
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	err = rtnl_link_register(&ipgre_link_ops);
175562306a36Sopenharmony_ci	if (err < 0)
175662306a36Sopenharmony_ci		goto rtnl_link_failed;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	err = rtnl_link_register(&ipgre_tap_ops);
175962306a36Sopenharmony_ci	if (err < 0)
176062306a36Sopenharmony_ci		goto tap_ops_failed;
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	err = rtnl_link_register(&erspan_link_ops);
176362306a36Sopenharmony_ci	if (err < 0)
176462306a36Sopenharmony_ci		goto erspan_link_failed;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	return 0;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_cierspan_link_failed:
176962306a36Sopenharmony_ci	rtnl_link_unregister(&ipgre_tap_ops);
177062306a36Sopenharmony_citap_ops_failed:
177162306a36Sopenharmony_ci	rtnl_link_unregister(&ipgre_link_ops);
177262306a36Sopenharmony_cirtnl_link_failed:
177362306a36Sopenharmony_ci	gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
177462306a36Sopenharmony_ciadd_proto_failed:
177562306a36Sopenharmony_ci	unregister_pernet_device(&erspan_net_ops);
177662306a36Sopenharmony_cipnet_erspan_failed:
177762306a36Sopenharmony_ci	unregister_pernet_device(&ipgre_tap_net_ops);
177862306a36Sopenharmony_cipnet_tap_failed:
177962306a36Sopenharmony_ci	unregister_pernet_device(&ipgre_net_ops);
178062306a36Sopenharmony_ci	return err;
178162306a36Sopenharmony_ci}
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_cistatic void __exit ipgre_fini(void)
178462306a36Sopenharmony_ci{
178562306a36Sopenharmony_ci	rtnl_link_unregister(&ipgre_tap_ops);
178662306a36Sopenharmony_ci	rtnl_link_unregister(&ipgre_link_ops);
178762306a36Sopenharmony_ci	rtnl_link_unregister(&erspan_link_ops);
178862306a36Sopenharmony_ci	gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
178962306a36Sopenharmony_ci	unregister_pernet_device(&ipgre_tap_net_ops);
179062306a36Sopenharmony_ci	unregister_pernet_device(&ipgre_net_ops);
179162306a36Sopenharmony_ci	unregister_pernet_device(&erspan_net_ops);
179262306a36Sopenharmony_ci}
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_cimodule_init(ipgre_init);
179562306a36Sopenharmony_cimodule_exit(ipgre_fini);
179662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
179762306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("gre");
179862306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("gretap");
179962306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("erspan");
180062306a36Sopenharmony_ciMODULE_ALIAS_NETDEV("gre0");
180162306a36Sopenharmony_ciMODULE_ALIAS_NETDEV("gretap0");
180262306a36Sopenharmony_ciMODULE_ALIAS_NETDEV("erspan0");
1803