162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci#include <linux/module.h>
362306a36Sopenharmony_ci#include <linux/errno.h>
462306a36Sopenharmony_ci#include <linux/socket.h>
562306a36Sopenharmony_ci#include <linux/skbuff.h>
662306a36Sopenharmony_ci#include <linux/ip.h>
762306a36Sopenharmony_ci#include <linux/icmp.h>
862306a36Sopenharmony_ci#include <linux/udp.h>
962306a36Sopenharmony_ci#include <linux/types.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <net/genetlink.h>
1262306a36Sopenharmony_ci#include <net/gro.h>
1362306a36Sopenharmony_ci#include <net/gue.h>
1462306a36Sopenharmony_ci#include <net/fou.h>
1562306a36Sopenharmony_ci#include <net/ip.h>
1662306a36Sopenharmony_ci#include <net/protocol.h>
1762306a36Sopenharmony_ci#include <net/udp.h>
1862306a36Sopenharmony_ci#include <net/udp_tunnel.h>
1962306a36Sopenharmony_ci#include <uapi/linux/fou.h>
2062306a36Sopenharmony_ci#include <uapi/linux/genetlink.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "fou_nl.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct fou {
2562306a36Sopenharmony_ci	struct socket *sock;
2662306a36Sopenharmony_ci	u8 protocol;
2762306a36Sopenharmony_ci	u8 flags;
2862306a36Sopenharmony_ci	__be16 port;
2962306a36Sopenharmony_ci	u8 family;
3062306a36Sopenharmony_ci	u16 type;
3162306a36Sopenharmony_ci	struct list_head list;
3262306a36Sopenharmony_ci	struct rcu_head rcu;
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define FOU_F_REMCSUM_NOPARTIAL BIT(0)
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistruct fou_cfg {
3862306a36Sopenharmony_ci	u16 type;
3962306a36Sopenharmony_ci	u8 protocol;
4062306a36Sopenharmony_ci	u8 flags;
4162306a36Sopenharmony_ci	struct udp_port_cfg udp_config;
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic unsigned int fou_net_id;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistruct fou_net {
4762306a36Sopenharmony_ci	struct list_head fou_list;
4862306a36Sopenharmony_ci	struct mutex fou_lock;
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic inline struct fou *fou_from_sock(struct sock *sk)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	return sk->sk_user_data;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int fou_recv_pull(struct sk_buff *skb, struct fou *fou, size_t len)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	/* Remove 'len' bytes from the packet (UDP header and
5962306a36Sopenharmony_ci	 * FOU header if present).
6062306a36Sopenharmony_ci	 */
6162306a36Sopenharmony_ci	if (fou->family == AF_INET)
6262306a36Sopenharmony_ci		ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len);
6362306a36Sopenharmony_ci	else
6462306a36Sopenharmony_ci		ipv6_hdr(skb)->payload_len =
6562306a36Sopenharmony_ci		    htons(ntohs(ipv6_hdr(skb)->payload_len) - len);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	__skb_pull(skb, len);
6862306a36Sopenharmony_ci	skb_postpull_rcsum(skb, udp_hdr(skb), len);
6962306a36Sopenharmony_ci	skb_reset_transport_header(skb);
7062306a36Sopenharmony_ci	return iptunnel_pull_offloads(skb);
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct fou *fou = fou_from_sock(sk);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (!fou)
7862306a36Sopenharmony_ci		return 1;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (fou_recv_pull(skb, fou, sizeof(struct udphdr)))
8162306a36Sopenharmony_ci		goto drop;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return -fou->protocol;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cidrop:
8662306a36Sopenharmony_ci	kfree_skb(skb);
8762306a36Sopenharmony_ci	return 0;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr,
9162306a36Sopenharmony_ci				  void *data, size_t hdrlen, u8 ipproto,
9262306a36Sopenharmony_ci				  bool nopartial)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	__be16 *pd = data;
9562306a36Sopenharmony_ci	size_t start = ntohs(pd[0]);
9662306a36Sopenharmony_ci	size_t offset = ntohs(pd[1]);
9762306a36Sopenharmony_ci	size_t plen = sizeof(struct udphdr) + hdrlen +
9862306a36Sopenharmony_ci	    max_t(size_t, offset + sizeof(u16), start);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (skb->remcsum_offload)
10162306a36Sopenharmony_ci		return guehdr;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (!pskb_may_pull(skb, plen))
10462306a36Sopenharmony_ci		return NULL;
10562306a36Sopenharmony_ci	guehdr = (struct guehdr *)&udp_hdr(skb)[1];
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	skb_remcsum_process(skb, (void *)guehdr + hdrlen,
10862306a36Sopenharmony_ci			    start, offset, nopartial);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return guehdr;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int gue_control_message(struct sk_buff *skb, struct guehdr *guehdr)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	/* No support yet */
11662306a36Sopenharmony_ci	kfree_skb(skb);
11762306a36Sopenharmony_ci	return 0;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct fou *fou = fou_from_sock(sk);
12362306a36Sopenharmony_ci	size_t len, optlen, hdrlen;
12462306a36Sopenharmony_ci	struct guehdr *guehdr;
12562306a36Sopenharmony_ci	void *data;
12662306a36Sopenharmony_ci	u16 doffset = 0;
12762306a36Sopenharmony_ci	u8 proto_ctype;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	if (!fou)
13062306a36Sopenharmony_ci		return 1;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	len = sizeof(struct udphdr) + sizeof(struct guehdr);
13362306a36Sopenharmony_ci	if (!pskb_may_pull(skb, len))
13462306a36Sopenharmony_ci		goto drop;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	guehdr = (struct guehdr *)&udp_hdr(skb)[1];
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	switch (guehdr->version) {
13962306a36Sopenharmony_ci	case 0: /* Full GUE header present */
14062306a36Sopenharmony_ci		break;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	case 1: {
14362306a36Sopenharmony_ci		/* Direct encapsulation of IPv4 or IPv6 */
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		int prot;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci		switch (((struct iphdr *)guehdr)->version) {
14862306a36Sopenharmony_ci		case 4:
14962306a36Sopenharmony_ci			prot = IPPROTO_IPIP;
15062306a36Sopenharmony_ci			break;
15162306a36Sopenharmony_ci		case 6:
15262306a36Sopenharmony_ci			prot = IPPROTO_IPV6;
15362306a36Sopenharmony_ci			break;
15462306a36Sopenharmony_ci		default:
15562306a36Sopenharmony_ci			goto drop;
15662306a36Sopenharmony_ci		}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		if (fou_recv_pull(skb, fou, sizeof(struct udphdr)))
15962306a36Sopenharmony_ci			goto drop;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		return -prot;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	default: /* Undefined version */
16562306a36Sopenharmony_ci		goto drop;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	optlen = guehdr->hlen << 2;
16962306a36Sopenharmony_ci	len += optlen;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (!pskb_may_pull(skb, len))
17262306a36Sopenharmony_ci		goto drop;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* guehdr may change after pull */
17562306a36Sopenharmony_ci	guehdr = (struct guehdr *)&udp_hdr(skb)[1];
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if (validate_gue_flags(guehdr, optlen))
17862306a36Sopenharmony_ci		goto drop;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	hdrlen = sizeof(struct guehdr) + optlen;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (fou->family == AF_INET)
18362306a36Sopenharmony_ci		ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len);
18462306a36Sopenharmony_ci	else
18562306a36Sopenharmony_ci		ipv6_hdr(skb)->payload_len =
18662306a36Sopenharmony_ci		    htons(ntohs(ipv6_hdr(skb)->payload_len) - len);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* Pull csum through the guehdr now . This can be used if
18962306a36Sopenharmony_ci	 * there is a remote checksum offload.
19062306a36Sopenharmony_ci	 */
19162306a36Sopenharmony_ci	skb_postpull_rcsum(skb, udp_hdr(skb), len);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	data = &guehdr[1];
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (guehdr->flags & GUE_FLAG_PRIV) {
19662306a36Sopenharmony_ci		__be32 flags = *(__be32 *)(data + doffset);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		doffset += GUE_LEN_PRIV;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci		if (flags & GUE_PFLAG_REMCSUM) {
20162306a36Sopenharmony_ci			guehdr = gue_remcsum(skb, guehdr, data + doffset,
20262306a36Sopenharmony_ci					     hdrlen, guehdr->proto_ctype,
20362306a36Sopenharmony_ci					     !!(fou->flags &
20462306a36Sopenharmony_ci						FOU_F_REMCSUM_NOPARTIAL));
20562306a36Sopenharmony_ci			if (!guehdr)
20662306a36Sopenharmony_ci				goto drop;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci			data = &guehdr[1];
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci			doffset += GUE_PLEN_REMCSUM;
21162306a36Sopenharmony_ci		}
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (unlikely(guehdr->control))
21562306a36Sopenharmony_ci		return gue_control_message(skb, guehdr);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	proto_ctype = guehdr->proto_ctype;
21862306a36Sopenharmony_ci	__skb_pull(skb, sizeof(struct udphdr) + hdrlen);
21962306a36Sopenharmony_ci	skb_reset_transport_header(skb);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (iptunnel_pull_offloads(skb))
22262306a36Sopenharmony_ci		goto drop;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	return -proto_ctype;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cidrop:
22762306a36Sopenharmony_ci	kfree_skb(skb);
22862306a36Sopenharmony_ci	return 0;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic struct sk_buff *fou_gro_receive(struct sock *sk,
23262306a36Sopenharmony_ci				       struct list_head *head,
23362306a36Sopenharmony_ci				       struct sk_buff *skb)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	const struct net_offload __rcu **offloads;
23662306a36Sopenharmony_ci	u8 proto = fou_from_sock(sk)->protocol;
23762306a36Sopenharmony_ci	const struct net_offload *ops;
23862306a36Sopenharmony_ci	struct sk_buff *pp = NULL;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	/* We can clear the encap_mark for FOU as we are essentially doing
24162306a36Sopenharmony_ci	 * one of two possible things.  We are either adding an L4 tunnel
24262306a36Sopenharmony_ci	 * header to the outer L3 tunnel header, or we are simply
24362306a36Sopenharmony_ci	 * treating the GRE tunnel header as though it is a UDP protocol
24462306a36Sopenharmony_ci	 * specific header such as VXLAN or GENEVE.
24562306a36Sopenharmony_ci	 */
24662306a36Sopenharmony_ci	NAPI_GRO_CB(skb)->encap_mark = 0;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* Flag this frame as already having an outer encap header */
24962306a36Sopenharmony_ci	NAPI_GRO_CB(skb)->is_fou = 1;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
25262306a36Sopenharmony_ci	ops = rcu_dereference(offloads[proto]);
25362306a36Sopenharmony_ci	if (!ops || !ops->callbacks.gro_receive)
25462306a36Sopenharmony_ci		goto out;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ciout:
25962306a36Sopenharmony_ci	return pp;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic int fou_gro_complete(struct sock *sk, struct sk_buff *skb,
26362306a36Sopenharmony_ci			    int nhoff)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	const struct net_offload __rcu **offloads;
26662306a36Sopenharmony_ci	u8 proto = fou_from_sock(sk)->protocol;
26762306a36Sopenharmony_ci	const struct net_offload *ops;
26862306a36Sopenharmony_ci	int err = -ENOSYS;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
27162306a36Sopenharmony_ci	ops = rcu_dereference(offloads[proto]);
27262306a36Sopenharmony_ci	if (WARN_ON(!ops || !ops->callbacks.gro_complete))
27362306a36Sopenharmony_ci		goto out;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	err = ops->callbacks.gro_complete(skb, nhoff);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	skb_set_inner_mac_header(skb, nhoff);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ciout:
28062306a36Sopenharmony_ci	return err;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
28462306a36Sopenharmony_ci				      struct guehdr *guehdr, void *data,
28562306a36Sopenharmony_ci				      size_t hdrlen, struct gro_remcsum *grc,
28662306a36Sopenharmony_ci				      bool nopartial)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	__be16 *pd = data;
28962306a36Sopenharmony_ci	size_t start = ntohs(pd[0]);
29062306a36Sopenharmony_ci	size_t offset = ntohs(pd[1]);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (skb->remcsum_offload)
29362306a36Sopenharmony_ci		return guehdr;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (!NAPI_GRO_CB(skb)->csum_valid)
29662306a36Sopenharmony_ci		return NULL;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	guehdr = skb_gro_remcsum_process(skb, (void *)guehdr, off, hdrlen,
29962306a36Sopenharmony_ci					 start, offset, grc, nopartial);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	skb->remcsum_offload = 1;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	return guehdr;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic struct sk_buff *gue_gro_receive(struct sock *sk,
30762306a36Sopenharmony_ci				       struct list_head *head,
30862306a36Sopenharmony_ci				       struct sk_buff *skb)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	const struct net_offload __rcu **offloads;
31162306a36Sopenharmony_ci	const struct net_offload *ops;
31262306a36Sopenharmony_ci	struct sk_buff *pp = NULL;
31362306a36Sopenharmony_ci	struct sk_buff *p;
31462306a36Sopenharmony_ci	struct guehdr *guehdr;
31562306a36Sopenharmony_ci	size_t len, optlen, hdrlen, off;
31662306a36Sopenharmony_ci	void *data;
31762306a36Sopenharmony_ci	u16 doffset = 0;
31862306a36Sopenharmony_ci	int flush = 1;
31962306a36Sopenharmony_ci	struct fou *fou = fou_from_sock(sk);
32062306a36Sopenharmony_ci	struct gro_remcsum grc;
32162306a36Sopenharmony_ci	u8 proto;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	skb_gro_remcsum_init(&grc);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	off = skb_gro_offset(skb);
32662306a36Sopenharmony_ci	len = off + sizeof(*guehdr);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	guehdr = skb_gro_header(skb, len, off);
32962306a36Sopenharmony_ci	if (unlikely(!guehdr))
33062306a36Sopenharmony_ci		goto out;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	switch (guehdr->version) {
33362306a36Sopenharmony_ci	case 0:
33462306a36Sopenharmony_ci		break;
33562306a36Sopenharmony_ci	case 1:
33662306a36Sopenharmony_ci		switch (((struct iphdr *)guehdr)->version) {
33762306a36Sopenharmony_ci		case 4:
33862306a36Sopenharmony_ci			proto = IPPROTO_IPIP;
33962306a36Sopenharmony_ci			break;
34062306a36Sopenharmony_ci		case 6:
34162306a36Sopenharmony_ci			proto = IPPROTO_IPV6;
34262306a36Sopenharmony_ci			break;
34362306a36Sopenharmony_ci		default:
34462306a36Sopenharmony_ci			goto out;
34562306a36Sopenharmony_ci		}
34662306a36Sopenharmony_ci		goto next_proto;
34762306a36Sopenharmony_ci	default:
34862306a36Sopenharmony_ci		goto out;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	optlen = guehdr->hlen << 2;
35262306a36Sopenharmony_ci	len += optlen;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if (skb_gro_header_hard(skb, len)) {
35562306a36Sopenharmony_ci		guehdr = skb_gro_header_slow(skb, len, off);
35662306a36Sopenharmony_ci		if (unlikely(!guehdr))
35762306a36Sopenharmony_ci			goto out;
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	if (unlikely(guehdr->control) || guehdr->version != 0 ||
36162306a36Sopenharmony_ci	    validate_gue_flags(guehdr, optlen))
36262306a36Sopenharmony_ci		goto out;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	hdrlen = sizeof(*guehdr) + optlen;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	/* Adjust NAPI_GRO_CB(skb)->csum to account for guehdr,
36762306a36Sopenharmony_ci	 * this is needed if there is a remote checkcsum offload.
36862306a36Sopenharmony_ci	 */
36962306a36Sopenharmony_ci	skb_gro_postpull_rcsum(skb, guehdr, hdrlen);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	data = &guehdr[1];
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (guehdr->flags & GUE_FLAG_PRIV) {
37462306a36Sopenharmony_ci		__be32 flags = *(__be32 *)(data + doffset);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci		doffset += GUE_LEN_PRIV;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci		if (flags & GUE_PFLAG_REMCSUM) {
37962306a36Sopenharmony_ci			guehdr = gue_gro_remcsum(skb, off, guehdr,
38062306a36Sopenharmony_ci						 data + doffset, hdrlen, &grc,
38162306a36Sopenharmony_ci						 !!(fou->flags &
38262306a36Sopenharmony_ci						    FOU_F_REMCSUM_NOPARTIAL));
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci			if (!guehdr)
38562306a36Sopenharmony_ci				goto out;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci			data = &guehdr[1];
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci			doffset += GUE_PLEN_REMCSUM;
39062306a36Sopenharmony_ci		}
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	skb_gro_pull(skb, hdrlen);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	list_for_each_entry(p, head, list) {
39662306a36Sopenharmony_ci		const struct guehdr *guehdr2;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		if (!NAPI_GRO_CB(p)->same_flow)
39962306a36Sopenharmony_ci			continue;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci		guehdr2 = (struct guehdr *)(p->data + off);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci		/* Compare base GUE header to be equal (covers
40462306a36Sopenharmony_ci		 * hlen, version, proto_ctype, and flags.
40562306a36Sopenharmony_ci		 */
40662306a36Sopenharmony_ci		if (guehdr->word != guehdr2->word) {
40762306a36Sopenharmony_ci			NAPI_GRO_CB(p)->same_flow = 0;
40862306a36Sopenharmony_ci			continue;
40962306a36Sopenharmony_ci		}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci		/* Compare optional fields are the same. */
41262306a36Sopenharmony_ci		if (guehdr->hlen && memcmp(&guehdr[1], &guehdr2[1],
41362306a36Sopenharmony_ci					   guehdr->hlen << 2)) {
41462306a36Sopenharmony_ci			NAPI_GRO_CB(p)->same_flow = 0;
41562306a36Sopenharmony_ci			continue;
41662306a36Sopenharmony_ci		}
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	proto = guehdr->proto_ctype;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cinext_proto:
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	/* We can clear the encap_mark for GUE as we are essentially doing
42462306a36Sopenharmony_ci	 * one of two possible things.  We are either adding an L4 tunnel
42562306a36Sopenharmony_ci	 * header to the outer L3 tunnel header, or we are simply
42662306a36Sopenharmony_ci	 * treating the GRE tunnel header as though it is a UDP protocol
42762306a36Sopenharmony_ci	 * specific header such as VXLAN or GENEVE.
42862306a36Sopenharmony_ci	 */
42962306a36Sopenharmony_ci	NAPI_GRO_CB(skb)->encap_mark = 0;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	/* Flag this frame as already having an outer encap header */
43262306a36Sopenharmony_ci	NAPI_GRO_CB(skb)->is_fou = 1;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
43562306a36Sopenharmony_ci	ops = rcu_dereference(offloads[proto]);
43662306a36Sopenharmony_ci	if (WARN_ON_ONCE(!ops || !ops->callbacks.gro_receive))
43762306a36Sopenharmony_ci		goto out;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
44062306a36Sopenharmony_ci	flush = 0;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ciout:
44362306a36Sopenharmony_ci	skb_gro_flush_final_remcsum(skb, pp, flush, &grc);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	return pp;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic int gue_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct guehdr *guehdr = (struct guehdr *)(skb->data + nhoff);
45162306a36Sopenharmony_ci	const struct net_offload __rcu **offloads;
45262306a36Sopenharmony_ci	const struct net_offload *ops;
45362306a36Sopenharmony_ci	unsigned int guehlen = 0;
45462306a36Sopenharmony_ci	u8 proto;
45562306a36Sopenharmony_ci	int err = -ENOENT;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	switch (guehdr->version) {
45862306a36Sopenharmony_ci	case 0:
45962306a36Sopenharmony_ci		proto = guehdr->proto_ctype;
46062306a36Sopenharmony_ci		guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
46162306a36Sopenharmony_ci		break;
46262306a36Sopenharmony_ci	case 1:
46362306a36Sopenharmony_ci		switch (((struct iphdr *)guehdr)->version) {
46462306a36Sopenharmony_ci		case 4:
46562306a36Sopenharmony_ci			proto = IPPROTO_IPIP;
46662306a36Sopenharmony_ci			break;
46762306a36Sopenharmony_ci		case 6:
46862306a36Sopenharmony_ci			proto = IPPROTO_IPV6;
46962306a36Sopenharmony_ci			break;
47062306a36Sopenharmony_ci		default:
47162306a36Sopenharmony_ci			return err;
47262306a36Sopenharmony_ci		}
47362306a36Sopenharmony_ci		break;
47462306a36Sopenharmony_ci	default:
47562306a36Sopenharmony_ci		return err;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
47962306a36Sopenharmony_ci	ops = rcu_dereference(offloads[proto]);
48062306a36Sopenharmony_ci	if (WARN_ON(!ops || !ops->callbacks.gro_complete))
48162306a36Sopenharmony_ci		goto out;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	err = ops->callbacks.gro_complete(skb, nhoff + guehlen);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	skb_set_inner_mac_header(skb, nhoff + guehlen);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ciout:
48862306a36Sopenharmony_ci	return err;
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic bool fou_cfg_cmp(struct fou *fou, struct fou_cfg *cfg)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	struct sock *sk = fou->sock->sk;
49462306a36Sopenharmony_ci	struct udp_port_cfg *udp_cfg = &cfg->udp_config;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (fou->family != udp_cfg->family ||
49762306a36Sopenharmony_ci	    fou->port != udp_cfg->local_udp_port ||
49862306a36Sopenharmony_ci	    sk->sk_dport != udp_cfg->peer_udp_port ||
49962306a36Sopenharmony_ci	    sk->sk_bound_dev_if != udp_cfg->bind_ifindex)
50062306a36Sopenharmony_ci		return false;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (fou->family == AF_INET) {
50362306a36Sopenharmony_ci		if (sk->sk_rcv_saddr != udp_cfg->local_ip.s_addr ||
50462306a36Sopenharmony_ci		    sk->sk_daddr != udp_cfg->peer_ip.s_addr)
50562306a36Sopenharmony_ci			return false;
50662306a36Sopenharmony_ci		else
50762306a36Sopenharmony_ci			return true;
50862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
50962306a36Sopenharmony_ci	} else {
51062306a36Sopenharmony_ci		if (ipv6_addr_cmp(&sk->sk_v6_rcv_saddr, &udp_cfg->local_ip6) ||
51162306a36Sopenharmony_ci		    ipv6_addr_cmp(&sk->sk_v6_daddr, &udp_cfg->peer_ip6))
51262306a36Sopenharmony_ci			return false;
51362306a36Sopenharmony_ci		else
51462306a36Sopenharmony_ci			return true;
51562306a36Sopenharmony_ci#endif
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	return false;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic int fou_add_to_port_list(struct net *net, struct fou *fou,
52262306a36Sopenharmony_ci				struct fou_cfg *cfg)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	struct fou_net *fn = net_generic(net, fou_net_id);
52562306a36Sopenharmony_ci	struct fou *fout;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	mutex_lock(&fn->fou_lock);
52862306a36Sopenharmony_ci	list_for_each_entry(fout, &fn->fou_list, list) {
52962306a36Sopenharmony_ci		if (fou_cfg_cmp(fout, cfg)) {
53062306a36Sopenharmony_ci			mutex_unlock(&fn->fou_lock);
53162306a36Sopenharmony_ci			return -EALREADY;
53262306a36Sopenharmony_ci		}
53362306a36Sopenharmony_ci	}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	list_add(&fou->list, &fn->fou_list);
53662306a36Sopenharmony_ci	mutex_unlock(&fn->fou_lock);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	return 0;
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_cistatic void fou_release(struct fou *fou)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	struct socket *sock = fou->sock;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	list_del(&fou->list);
54662306a36Sopenharmony_ci	udp_tunnel_sock_release(sock);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	kfree_rcu(fou, rcu);
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic int fou_create(struct net *net, struct fou_cfg *cfg,
55262306a36Sopenharmony_ci		      struct socket **sockp)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	struct socket *sock = NULL;
55562306a36Sopenharmony_ci	struct fou *fou = NULL;
55662306a36Sopenharmony_ci	struct sock *sk;
55762306a36Sopenharmony_ci	struct udp_tunnel_sock_cfg tunnel_cfg;
55862306a36Sopenharmony_ci	int err;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	/* Open UDP socket */
56162306a36Sopenharmony_ci	err = udp_sock_create(net, &cfg->udp_config, &sock);
56262306a36Sopenharmony_ci	if (err < 0)
56362306a36Sopenharmony_ci		goto error;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	/* Allocate FOU port structure */
56662306a36Sopenharmony_ci	fou = kzalloc(sizeof(*fou), GFP_KERNEL);
56762306a36Sopenharmony_ci	if (!fou) {
56862306a36Sopenharmony_ci		err = -ENOMEM;
56962306a36Sopenharmony_ci		goto error;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	sk = sock->sk;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	fou->port = cfg->udp_config.local_udp_port;
57562306a36Sopenharmony_ci	fou->family = cfg->udp_config.family;
57662306a36Sopenharmony_ci	fou->flags = cfg->flags;
57762306a36Sopenharmony_ci	fou->type = cfg->type;
57862306a36Sopenharmony_ci	fou->sock = sock;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	memset(&tunnel_cfg, 0, sizeof(tunnel_cfg));
58162306a36Sopenharmony_ci	tunnel_cfg.encap_type = 1;
58262306a36Sopenharmony_ci	tunnel_cfg.sk_user_data = fou;
58362306a36Sopenharmony_ci	tunnel_cfg.encap_destroy = NULL;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	/* Initial for fou type */
58662306a36Sopenharmony_ci	switch (cfg->type) {
58762306a36Sopenharmony_ci	case FOU_ENCAP_DIRECT:
58862306a36Sopenharmony_ci		tunnel_cfg.encap_rcv = fou_udp_recv;
58962306a36Sopenharmony_ci		tunnel_cfg.gro_receive = fou_gro_receive;
59062306a36Sopenharmony_ci		tunnel_cfg.gro_complete = fou_gro_complete;
59162306a36Sopenharmony_ci		fou->protocol = cfg->protocol;
59262306a36Sopenharmony_ci		break;
59362306a36Sopenharmony_ci	case FOU_ENCAP_GUE:
59462306a36Sopenharmony_ci		tunnel_cfg.encap_rcv = gue_udp_recv;
59562306a36Sopenharmony_ci		tunnel_cfg.gro_receive = gue_gro_receive;
59662306a36Sopenharmony_ci		tunnel_cfg.gro_complete = gue_gro_complete;
59762306a36Sopenharmony_ci		break;
59862306a36Sopenharmony_ci	default:
59962306a36Sopenharmony_ci		err = -EINVAL;
60062306a36Sopenharmony_ci		goto error;
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	setup_udp_tunnel_sock(net, sock, &tunnel_cfg);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	sk->sk_allocation = GFP_ATOMIC;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	err = fou_add_to_port_list(net, fou, cfg);
60862306a36Sopenharmony_ci	if (err)
60962306a36Sopenharmony_ci		goto error;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	if (sockp)
61262306a36Sopenharmony_ci		*sockp = sock;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	return 0;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_cierror:
61762306a36Sopenharmony_ci	kfree(fou);
61862306a36Sopenharmony_ci	if (sock)
61962306a36Sopenharmony_ci		udp_tunnel_sock_release(sock);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	return err;
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic int fou_destroy(struct net *net, struct fou_cfg *cfg)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	struct fou_net *fn = net_generic(net, fou_net_id);
62762306a36Sopenharmony_ci	int err = -EINVAL;
62862306a36Sopenharmony_ci	struct fou *fou;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	mutex_lock(&fn->fou_lock);
63162306a36Sopenharmony_ci	list_for_each_entry(fou, &fn->fou_list, list) {
63262306a36Sopenharmony_ci		if (fou_cfg_cmp(fou, cfg)) {
63362306a36Sopenharmony_ci			fou_release(fou);
63462306a36Sopenharmony_ci			err = 0;
63562306a36Sopenharmony_ci			break;
63662306a36Sopenharmony_ci		}
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci	mutex_unlock(&fn->fou_lock);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	return err;
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic struct genl_family fou_nl_family;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_cistatic int parse_nl_config(struct genl_info *info,
64662306a36Sopenharmony_ci			   struct fou_cfg *cfg)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	bool has_local = false, has_peer = false;
64962306a36Sopenharmony_ci	struct nlattr *attr;
65062306a36Sopenharmony_ci	int ifindex;
65162306a36Sopenharmony_ci	__be16 port;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	memset(cfg, 0, sizeof(*cfg));
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	cfg->udp_config.family = AF_INET;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	if (info->attrs[FOU_ATTR_AF]) {
65862306a36Sopenharmony_ci		u8 family = nla_get_u8(info->attrs[FOU_ATTR_AF]);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		switch (family) {
66162306a36Sopenharmony_ci		case AF_INET:
66262306a36Sopenharmony_ci			break;
66362306a36Sopenharmony_ci		case AF_INET6:
66462306a36Sopenharmony_ci			cfg->udp_config.ipv6_v6only = 1;
66562306a36Sopenharmony_ci			break;
66662306a36Sopenharmony_ci		default:
66762306a36Sopenharmony_ci			return -EAFNOSUPPORT;
66862306a36Sopenharmony_ci		}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci		cfg->udp_config.family = family;
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	if (info->attrs[FOU_ATTR_PORT]) {
67462306a36Sopenharmony_ci		port = nla_get_be16(info->attrs[FOU_ATTR_PORT]);
67562306a36Sopenharmony_ci		cfg->udp_config.local_udp_port = port;
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	if (info->attrs[FOU_ATTR_IPPROTO])
67962306a36Sopenharmony_ci		cfg->protocol = nla_get_u8(info->attrs[FOU_ATTR_IPPROTO]);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (info->attrs[FOU_ATTR_TYPE])
68262306a36Sopenharmony_ci		cfg->type = nla_get_u8(info->attrs[FOU_ATTR_TYPE]);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	if (info->attrs[FOU_ATTR_REMCSUM_NOPARTIAL])
68562306a36Sopenharmony_ci		cfg->flags |= FOU_F_REMCSUM_NOPARTIAL;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	if (cfg->udp_config.family == AF_INET) {
68862306a36Sopenharmony_ci		if (info->attrs[FOU_ATTR_LOCAL_V4]) {
68962306a36Sopenharmony_ci			attr = info->attrs[FOU_ATTR_LOCAL_V4];
69062306a36Sopenharmony_ci			cfg->udp_config.local_ip.s_addr = nla_get_in_addr(attr);
69162306a36Sopenharmony_ci			has_local = true;
69262306a36Sopenharmony_ci		}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci		if (info->attrs[FOU_ATTR_PEER_V4]) {
69562306a36Sopenharmony_ci			attr = info->attrs[FOU_ATTR_PEER_V4];
69662306a36Sopenharmony_ci			cfg->udp_config.peer_ip.s_addr = nla_get_in_addr(attr);
69762306a36Sopenharmony_ci			has_peer = true;
69862306a36Sopenharmony_ci		}
69962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
70062306a36Sopenharmony_ci	} else {
70162306a36Sopenharmony_ci		if (info->attrs[FOU_ATTR_LOCAL_V6]) {
70262306a36Sopenharmony_ci			attr = info->attrs[FOU_ATTR_LOCAL_V6];
70362306a36Sopenharmony_ci			cfg->udp_config.local_ip6 = nla_get_in6_addr(attr);
70462306a36Sopenharmony_ci			has_local = true;
70562306a36Sopenharmony_ci		}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci		if (info->attrs[FOU_ATTR_PEER_V6]) {
70862306a36Sopenharmony_ci			attr = info->attrs[FOU_ATTR_PEER_V6];
70962306a36Sopenharmony_ci			cfg->udp_config.peer_ip6 = nla_get_in6_addr(attr);
71062306a36Sopenharmony_ci			has_peer = true;
71162306a36Sopenharmony_ci		}
71262306a36Sopenharmony_ci#endif
71362306a36Sopenharmony_ci	}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	if (has_peer) {
71662306a36Sopenharmony_ci		if (info->attrs[FOU_ATTR_PEER_PORT]) {
71762306a36Sopenharmony_ci			port = nla_get_be16(info->attrs[FOU_ATTR_PEER_PORT]);
71862306a36Sopenharmony_ci			cfg->udp_config.peer_udp_port = port;
71962306a36Sopenharmony_ci		} else {
72062306a36Sopenharmony_ci			return -EINVAL;
72162306a36Sopenharmony_ci		}
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	if (info->attrs[FOU_ATTR_IFINDEX]) {
72562306a36Sopenharmony_ci		if (!has_local)
72662306a36Sopenharmony_ci			return -EINVAL;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci		ifindex = nla_get_s32(info->attrs[FOU_ATTR_IFINDEX]);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci		cfg->udp_config.bind_ifindex = ifindex;
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	return 0;
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ciint fou_nl_add_doit(struct sk_buff *skb, struct genl_info *info)
73762306a36Sopenharmony_ci{
73862306a36Sopenharmony_ci	struct net *net = genl_info_net(info);
73962306a36Sopenharmony_ci	struct fou_cfg cfg;
74062306a36Sopenharmony_ci	int err;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	err = parse_nl_config(info, &cfg);
74362306a36Sopenharmony_ci	if (err)
74462306a36Sopenharmony_ci		return err;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	return fou_create(net, &cfg, NULL);
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ciint fou_nl_del_doit(struct sk_buff *skb, struct genl_info *info)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	struct net *net = genl_info_net(info);
75262306a36Sopenharmony_ci	struct fou_cfg cfg;
75362306a36Sopenharmony_ci	int err;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	err = parse_nl_config(info, &cfg);
75662306a36Sopenharmony_ci	if (err)
75762306a36Sopenharmony_ci		return err;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	return fou_destroy(net, &cfg);
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic int fou_fill_info(struct fou *fou, struct sk_buff *msg)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	struct sock *sk = fou->sock->sk;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	if (nla_put_u8(msg, FOU_ATTR_AF, fou->sock->sk->sk_family) ||
76762306a36Sopenharmony_ci	    nla_put_be16(msg, FOU_ATTR_PORT, fou->port) ||
76862306a36Sopenharmony_ci	    nla_put_be16(msg, FOU_ATTR_PEER_PORT, sk->sk_dport) ||
76962306a36Sopenharmony_ci	    nla_put_u8(msg, FOU_ATTR_IPPROTO, fou->protocol) ||
77062306a36Sopenharmony_ci	    nla_put_u8(msg, FOU_ATTR_TYPE, fou->type) ||
77162306a36Sopenharmony_ci	    nla_put_s32(msg, FOU_ATTR_IFINDEX, sk->sk_bound_dev_if))
77262306a36Sopenharmony_ci		return -1;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	if (fou->flags & FOU_F_REMCSUM_NOPARTIAL)
77562306a36Sopenharmony_ci		if (nla_put_flag(msg, FOU_ATTR_REMCSUM_NOPARTIAL))
77662306a36Sopenharmony_ci			return -1;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	if (fou->sock->sk->sk_family == AF_INET) {
77962306a36Sopenharmony_ci		if (nla_put_in_addr(msg, FOU_ATTR_LOCAL_V4, sk->sk_rcv_saddr))
78062306a36Sopenharmony_ci			return -1;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci		if (nla_put_in_addr(msg, FOU_ATTR_PEER_V4, sk->sk_daddr))
78362306a36Sopenharmony_ci			return -1;
78462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
78562306a36Sopenharmony_ci	} else {
78662306a36Sopenharmony_ci		if (nla_put_in6_addr(msg, FOU_ATTR_LOCAL_V6,
78762306a36Sopenharmony_ci				     &sk->sk_v6_rcv_saddr))
78862306a36Sopenharmony_ci			return -1;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci		if (nla_put_in6_addr(msg, FOU_ATTR_PEER_V6, &sk->sk_v6_daddr))
79162306a36Sopenharmony_ci			return -1;
79262306a36Sopenharmony_ci#endif
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	return 0;
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_cistatic int fou_dump_info(struct fou *fou, u32 portid, u32 seq,
79962306a36Sopenharmony_ci			 u32 flags, struct sk_buff *skb, u8 cmd)
80062306a36Sopenharmony_ci{
80162306a36Sopenharmony_ci	void *hdr;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	hdr = genlmsg_put(skb, portid, seq, &fou_nl_family, flags, cmd);
80462306a36Sopenharmony_ci	if (!hdr)
80562306a36Sopenharmony_ci		return -ENOMEM;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	if (fou_fill_info(fou, skb) < 0)
80862306a36Sopenharmony_ci		goto nla_put_failure;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	genlmsg_end(skb, hdr);
81162306a36Sopenharmony_ci	return 0;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_cinla_put_failure:
81462306a36Sopenharmony_ci	genlmsg_cancel(skb, hdr);
81562306a36Sopenharmony_ci	return -EMSGSIZE;
81662306a36Sopenharmony_ci}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ciint fou_nl_get_doit(struct sk_buff *skb, struct genl_info *info)
81962306a36Sopenharmony_ci{
82062306a36Sopenharmony_ci	struct net *net = genl_info_net(info);
82162306a36Sopenharmony_ci	struct fou_net *fn = net_generic(net, fou_net_id);
82262306a36Sopenharmony_ci	struct sk_buff *msg;
82362306a36Sopenharmony_ci	struct fou_cfg cfg;
82462306a36Sopenharmony_ci	struct fou *fout;
82562306a36Sopenharmony_ci	__be16 port;
82662306a36Sopenharmony_ci	u8 family;
82762306a36Sopenharmony_ci	int ret;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	ret = parse_nl_config(info, &cfg);
83062306a36Sopenharmony_ci	if (ret)
83162306a36Sopenharmony_ci		return ret;
83262306a36Sopenharmony_ci	port = cfg.udp_config.local_udp_port;
83362306a36Sopenharmony_ci	if (port == 0)
83462306a36Sopenharmony_ci		return -EINVAL;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	family = cfg.udp_config.family;
83762306a36Sopenharmony_ci	if (family != AF_INET && family != AF_INET6)
83862306a36Sopenharmony_ci		return -EINVAL;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
84162306a36Sopenharmony_ci	if (!msg)
84262306a36Sopenharmony_ci		return -ENOMEM;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	ret = -ESRCH;
84562306a36Sopenharmony_ci	mutex_lock(&fn->fou_lock);
84662306a36Sopenharmony_ci	list_for_each_entry(fout, &fn->fou_list, list) {
84762306a36Sopenharmony_ci		if (fou_cfg_cmp(fout, &cfg)) {
84862306a36Sopenharmony_ci			ret = fou_dump_info(fout, info->snd_portid,
84962306a36Sopenharmony_ci					    info->snd_seq, 0, msg,
85062306a36Sopenharmony_ci					    info->genlhdr->cmd);
85162306a36Sopenharmony_ci			break;
85262306a36Sopenharmony_ci		}
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci	mutex_unlock(&fn->fou_lock);
85562306a36Sopenharmony_ci	if (ret < 0)
85662306a36Sopenharmony_ci		goto out_free;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	return genlmsg_reply(msg, info);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ciout_free:
86162306a36Sopenharmony_ci	nlmsg_free(msg);
86262306a36Sopenharmony_ci	return ret;
86362306a36Sopenharmony_ci}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ciint fou_nl_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
86662306a36Sopenharmony_ci{
86762306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
86862306a36Sopenharmony_ci	struct fou_net *fn = net_generic(net, fou_net_id);
86962306a36Sopenharmony_ci	struct fou *fout;
87062306a36Sopenharmony_ci	int idx = 0, ret;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	mutex_lock(&fn->fou_lock);
87362306a36Sopenharmony_ci	list_for_each_entry(fout, &fn->fou_list, list) {
87462306a36Sopenharmony_ci		if (idx++ < cb->args[0])
87562306a36Sopenharmony_ci			continue;
87662306a36Sopenharmony_ci		ret = fou_dump_info(fout, NETLINK_CB(cb->skb).portid,
87762306a36Sopenharmony_ci				    cb->nlh->nlmsg_seq, NLM_F_MULTI,
87862306a36Sopenharmony_ci				    skb, FOU_CMD_GET);
87962306a36Sopenharmony_ci		if (ret)
88062306a36Sopenharmony_ci			break;
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci	mutex_unlock(&fn->fou_lock);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	cb->args[0] = idx;
88562306a36Sopenharmony_ci	return skb->len;
88662306a36Sopenharmony_ci}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_cistatic struct genl_family fou_nl_family __ro_after_init = {
88962306a36Sopenharmony_ci	.hdrsize	= 0,
89062306a36Sopenharmony_ci	.name		= FOU_GENL_NAME,
89162306a36Sopenharmony_ci	.version	= FOU_GENL_VERSION,
89262306a36Sopenharmony_ci	.maxattr	= FOU_ATTR_MAX,
89362306a36Sopenharmony_ci	.policy		= fou_nl_policy,
89462306a36Sopenharmony_ci	.netnsok	= true,
89562306a36Sopenharmony_ci	.module		= THIS_MODULE,
89662306a36Sopenharmony_ci	.small_ops	= fou_nl_ops,
89762306a36Sopenharmony_ci	.n_small_ops	= ARRAY_SIZE(fou_nl_ops),
89862306a36Sopenharmony_ci	.resv_start_op	= FOU_CMD_GET + 1,
89962306a36Sopenharmony_ci};
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_cisize_t fou_encap_hlen(struct ip_tunnel_encap *e)
90262306a36Sopenharmony_ci{
90362306a36Sopenharmony_ci	return sizeof(struct udphdr);
90462306a36Sopenharmony_ci}
90562306a36Sopenharmony_ciEXPORT_SYMBOL(fou_encap_hlen);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_cisize_t gue_encap_hlen(struct ip_tunnel_encap *e)
90862306a36Sopenharmony_ci{
90962306a36Sopenharmony_ci	size_t len;
91062306a36Sopenharmony_ci	bool need_priv = false;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	len = sizeof(struct udphdr) + sizeof(struct guehdr);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	if (e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) {
91562306a36Sopenharmony_ci		len += GUE_PLEN_REMCSUM;
91662306a36Sopenharmony_ci		need_priv = true;
91762306a36Sopenharmony_ci	}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	len += need_priv ? GUE_LEN_PRIV : 0;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	return len;
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ciEXPORT_SYMBOL(gue_encap_hlen);
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ciint __fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
92662306a36Sopenharmony_ci		       u8 *protocol, __be16 *sport, int type)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	int err;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	err = iptunnel_handle_offloads(skb, type);
93162306a36Sopenharmony_ci	if (err)
93262306a36Sopenharmony_ci		return err;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	*sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
93562306a36Sopenharmony_ci						skb, 0, 0, false);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	return 0;
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ciEXPORT_SYMBOL(__fou_build_header);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ciint __gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
94262306a36Sopenharmony_ci		       u8 *protocol, __be16 *sport, int type)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	struct guehdr *guehdr;
94562306a36Sopenharmony_ci	size_t hdrlen, optlen = 0;
94662306a36Sopenharmony_ci	void *data;
94762306a36Sopenharmony_ci	bool need_priv = false;
94862306a36Sopenharmony_ci	int err;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	if ((e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) &&
95162306a36Sopenharmony_ci	    skb->ip_summed == CHECKSUM_PARTIAL) {
95262306a36Sopenharmony_ci		optlen += GUE_PLEN_REMCSUM;
95362306a36Sopenharmony_ci		type |= SKB_GSO_TUNNEL_REMCSUM;
95462306a36Sopenharmony_ci		need_priv = true;
95562306a36Sopenharmony_ci	}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	optlen += need_priv ? GUE_LEN_PRIV : 0;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	err = iptunnel_handle_offloads(skb, type);
96062306a36Sopenharmony_ci	if (err)
96162306a36Sopenharmony_ci		return err;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	/* Get source port (based on flow hash) before skb_push */
96462306a36Sopenharmony_ci	*sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
96562306a36Sopenharmony_ci						skb, 0, 0, false);
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	hdrlen = sizeof(struct guehdr) + optlen;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	skb_push(skb, hdrlen);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	guehdr = (struct guehdr *)skb->data;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	guehdr->control = 0;
97462306a36Sopenharmony_ci	guehdr->version = 0;
97562306a36Sopenharmony_ci	guehdr->hlen = optlen >> 2;
97662306a36Sopenharmony_ci	guehdr->flags = 0;
97762306a36Sopenharmony_ci	guehdr->proto_ctype = *protocol;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	data = &guehdr[1];
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	if (need_priv) {
98262306a36Sopenharmony_ci		__be32 *flags = data;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci		guehdr->flags |= GUE_FLAG_PRIV;
98562306a36Sopenharmony_ci		*flags = 0;
98662306a36Sopenharmony_ci		data += GUE_LEN_PRIV;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci		if (type & SKB_GSO_TUNNEL_REMCSUM) {
98962306a36Sopenharmony_ci			u16 csum_start = skb_checksum_start_offset(skb);
99062306a36Sopenharmony_ci			__be16 *pd = data;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci			if (csum_start < hdrlen)
99362306a36Sopenharmony_ci				return -EINVAL;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci			csum_start -= hdrlen;
99662306a36Sopenharmony_ci			pd[0] = htons(csum_start);
99762306a36Sopenharmony_ci			pd[1] = htons(csum_start + skb->csum_offset);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci			if (!skb_is_gso(skb)) {
100062306a36Sopenharmony_ci				skb->ip_summed = CHECKSUM_NONE;
100162306a36Sopenharmony_ci				skb->encapsulation = 0;
100262306a36Sopenharmony_ci			}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci			*flags |= GUE_PFLAG_REMCSUM;
100562306a36Sopenharmony_ci			data += GUE_PLEN_REMCSUM;
100662306a36Sopenharmony_ci		}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	return 0;
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ciEXPORT_SYMBOL(__gue_build_header);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci#ifdef CONFIG_NET_FOU_IP_TUNNELS
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_cistatic void fou_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e,
101762306a36Sopenharmony_ci			  struct flowi4 *fl4, u8 *protocol, __be16 sport)
101862306a36Sopenharmony_ci{
101962306a36Sopenharmony_ci	struct udphdr *uh;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	skb_push(skb, sizeof(struct udphdr));
102262306a36Sopenharmony_ci	skb_reset_transport_header(skb);
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	uh = udp_hdr(skb);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	uh->dest = e->dport;
102762306a36Sopenharmony_ci	uh->source = sport;
102862306a36Sopenharmony_ci	uh->len = htons(skb->len);
102962306a36Sopenharmony_ci	udp_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM), skb,
103062306a36Sopenharmony_ci		     fl4->saddr, fl4->daddr, skb->len);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	*protocol = IPPROTO_UDP;
103362306a36Sopenharmony_ci}
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_cistatic int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
103662306a36Sopenharmony_ci			    u8 *protocol, struct flowi4 *fl4)
103762306a36Sopenharmony_ci{
103862306a36Sopenharmony_ci	int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM ? SKB_GSO_UDP_TUNNEL_CSUM :
103962306a36Sopenharmony_ci						       SKB_GSO_UDP_TUNNEL;
104062306a36Sopenharmony_ci	__be16 sport;
104162306a36Sopenharmony_ci	int err;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	err = __fou_build_header(skb, e, protocol, &sport, type);
104462306a36Sopenharmony_ci	if (err)
104562306a36Sopenharmony_ci		return err;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	fou_build_udp(skb, e, fl4, protocol, sport);
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	return 0;
105062306a36Sopenharmony_ci}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_cistatic int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
105362306a36Sopenharmony_ci			    u8 *protocol, struct flowi4 *fl4)
105462306a36Sopenharmony_ci{
105562306a36Sopenharmony_ci	int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM ? SKB_GSO_UDP_TUNNEL_CSUM :
105662306a36Sopenharmony_ci						       SKB_GSO_UDP_TUNNEL;
105762306a36Sopenharmony_ci	__be16 sport;
105862306a36Sopenharmony_ci	int err;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	err = __gue_build_header(skb, e, protocol, &sport, type);
106162306a36Sopenharmony_ci	if (err)
106262306a36Sopenharmony_ci		return err;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	fou_build_udp(skb, e, fl4, protocol, sport);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	return 0;
106762306a36Sopenharmony_ci}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_cistatic int gue_err_proto_handler(int proto, struct sk_buff *skb, u32 info)
107062306a36Sopenharmony_ci{
107162306a36Sopenharmony_ci	const struct net_protocol *ipprot = rcu_dereference(inet_protos[proto]);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	if (ipprot && ipprot->err_handler) {
107462306a36Sopenharmony_ci		if (!ipprot->err_handler(skb, info))
107562306a36Sopenharmony_ci			return 0;
107662306a36Sopenharmony_ci	}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	return -ENOENT;
107962306a36Sopenharmony_ci}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_cistatic int gue_err(struct sk_buff *skb, u32 info)
108262306a36Sopenharmony_ci{
108362306a36Sopenharmony_ci	int transport_offset = skb_transport_offset(skb);
108462306a36Sopenharmony_ci	struct guehdr *guehdr;
108562306a36Sopenharmony_ci	size_t len, optlen;
108662306a36Sopenharmony_ci	int ret;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	len = sizeof(struct udphdr) + sizeof(struct guehdr);
108962306a36Sopenharmony_ci	if (!pskb_may_pull(skb, transport_offset + len))
109062306a36Sopenharmony_ci		return -EINVAL;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	guehdr = (struct guehdr *)&udp_hdr(skb)[1];
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	switch (guehdr->version) {
109562306a36Sopenharmony_ci	case 0: /* Full GUE header present */
109662306a36Sopenharmony_ci		break;
109762306a36Sopenharmony_ci	case 1: {
109862306a36Sopenharmony_ci		/* Direct encapsulation of IPv4 or IPv6 */
109962306a36Sopenharmony_ci		skb_set_transport_header(skb, -(int)sizeof(struct icmphdr));
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci		switch (((struct iphdr *)guehdr)->version) {
110262306a36Sopenharmony_ci		case 4:
110362306a36Sopenharmony_ci			ret = gue_err_proto_handler(IPPROTO_IPIP, skb, info);
110462306a36Sopenharmony_ci			goto out;
110562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
110662306a36Sopenharmony_ci		case 6:
110762306a36Sopenharmony_ci			ret = gue_err_proto_handler(IPPROTO_IPV6, skb, info);
110862306a36Sopenharmony_ci			goto out;
110962306a36Sopenharmony_ci#endif
111062306a36Sopenharmony_ci		default:
111162306a36Sopenharmony_ci			ret = -EOPNOTSUPP;
111262306a36Sopenharmony_ci			goto out;
111362306a36Sopenharmony_ci		}
111462306a36Sopenharmony_ci	}
111562306a36Sopenharmony_ci	default: /* Undefined version */
111662306a36Sopenharmony_ci		return -EOPNOTSUPP;
111762306a36Sopenharmony_ci	}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	if (guehdr->control)
112062306a36Sopenharmony_ci		return -ENOENT;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	optlen = guehdr->hlen << 2;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	if (!pskb_may_pull(skb, transport_offset + len + optlen))
112562306a36Sopenharmony_ci		return -EINVAL;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	guehdr = (struct guehdr *)&udp_hdr(skb)[1];
112862306a36Sopenharmony_ci	if (validate_gue_flags(guehdr, optlen))
112962306a36Sopenharmony_ci		return -EINVAL;
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	/* Handling exceptions for direct UDP encapsulation in GUE would lead to
113262306a36Sopenharmony_ci	 * recursion. Besides, this kind of encapsulation can't even be
113362306a36Sopenharmony_ci	 * configured currently. Discard this.
113462306a36Sopenharmony_ci	 */
113562306a36Sopenharmony_ci	if (guehdr->proto_ctype == IPPROTO_UDP ||
113662306a36Sopenharmony_ci	    guehdr->proto_ctype == IPPROTO_UDPLITE)
113762306a36Sopenharmony_ci		return -EOPNOTSUPP;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	skb_set_transport_header(skb, -(int)sizeof(struct icmphdr));
114062306a36Sopenharmony_ci	ret = gue_err_proto_handler(guehdr->proto_ctype, skb, info);
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ciout:
114362306a36Sopenharmony_ci	skb_set_transport_header(skb, transport_offset);
114462306a36Sopenharmony_ci	return ret;
114562306a36Sopenharmony_ci}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_cistatic const struct ip_tunnel_encap_ops fou_iptun_ops = {
114962306a36Sopenharmony_ci	.encap_hlen = fou_encap_hlen,
115062306a36Sopenharmony_ci	.build_header = fou_build_header,
115162306a36Sopenharmony_ci	.err_handler = gue_err,
115262306a36Sopenharmony_ci};
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cistatic const struct ip_tunnel_encap_ops gue_iptun_ops = {
115562306a36Sopenharmony_ci	.encap_hlen = gue_encap_hlen,
115662306a36Sopenharmony_ci	.build_header = gue_build_header,
115762306a36Sopenharmony_ci	.err_handler = gue_err,
115862306a36Sopenharmony_ci};
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_cistatic int ip_tunnel_encap_add_fou_ops(void)
116162306a36Sopenharmony_ci{
116262306a36Sopenharmony_ci	int ret;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	ret = ip_tunnel_encap_add_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
116562306a36Sopenharmony_ci	if (ret < 0) {
116662306a36Sopenharmony_ci		pr_err("can't add fou ops\n");
116762306a36Sopenharmony_ci		return ret;
116862306a36Sopenharmony_ci	}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	ret = ip_tunnel_encap_add_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE);
117162306a36Sopenharmony_ci	if (ret < 0) {
117262306a36Sopenharmony_ci		pr_err("can't add gue ops\n");
117362306a36Sopenharmony_ci		ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
117462306a36Sopenharmony_ci		return ret;
117562306a36Sopenharmony_ci	}
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	return 0;
117862306a36Sopenharmony_ci}
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_cistatic void ip_tunnel_encap_del_fou_ops(void)
118162306a36Sopenharmony_ci{
118262306a36Sopenharmony_ci	ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
118362306a36Sopenharmony_ci	ip_tunnel_encap_del_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE);
118462306a36Sopenharmony_ci}
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci#else
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_cistatic int ip_tunnel_encap_add_fou_ops(void)
118962306a36Sopenharmony_ci{
119062306a36Sopenharmony_ci	return 0;
119162306a36Sopenharmony_ci}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_cistatic void ip_tunnel_encap_del_fou_ops(void)
119462306a36Sopenharmony_ci{
119562306a36Sopenharmony_ci}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci#endif
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_cistatic __net_init int fou_init_net(struct net *net)
120062306a36Sopenharmony_ci{
120162306a36Sopenharmony_ci	struct fou_net *fn = net_generic(net, fou_net_id);
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	INIT_LIST_HEAD(&fn->fou_list);
120462306a36Sopenharmony_ci	mutex_init(&fn->fou_lock);
120562306a36Sopenharmony_ci	return 0;
120662306a36Sopenharmony_ci}
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_cistatic __net_exit void fou_exit_net(struct net *net)
120962306a36Sopenharmony_ci{
121062306a36Sopenharmony_ci	struct fou_net *fn = net_generic(net, fou_net_id);
121162306a36Sopenharmony_ci	struct fou *fou, *next;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	/* Close all the FOU sockets */
121462306a36Sopenharmony_ci	mutex_lock(&fn->fou_lock);
121562306a36Sopenharmony_ci	list_for_each_entry_safe(fou, next, &fn->fou_list, list)
121662306a36Sopenharmony_ci		fou_release(fou);
121762306a36Sopenharmony_ci	mutex_unlock(&fn->fou_lock);
121862306a36Sopenharmony_ci}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_cistatic struct pernet_operations fou_net_ops = {
122162306a36Sopenharmony_ci	.init = fou_init_net,
122262306a36Sopenharmony_ci	.exit = fou_exit_net,
122362306a36Sopenharmony_ci	.id   = &fou_net_id,
122462306a36Sopenharmony_ci	.size = sizeof(struct fou_net),
122562306a36Sopenharmony_ci};
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_cistatic int __init fou_init(void)
122862306a36Sopenharmony_ci{
122962306a36Sopenharmony_ci	int ret;
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	ret = register_pernet_device(&fou_net_ops);
123262306a36Sopenharmony_ci	if (ret)
123362306a36Sopenharmony_ci		goto exit;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	ret = genl_register_family(&fou_nl_family);
123662306a36Sopenharmony_ci	if (ret < 0)
123762306a36Sopenharmony_ci		goto unregister;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	ret = register_fou_bpf();
124062306a36Sopenharmony_ci	if (ret < 0)
124162306a36Sopenharmony_ci		goto kfunc_failed;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	ret = ip_tunnel_encap_add_fou_ops();
124462306a36Sopenharmony_ci	if (ret == 0)
124562306a36Sopenharmony_ci		return 0;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_cikfunc_failed:
124862306a36Sopenharmony_ci	genl_unregister_family(&fou_nl_family);
124962306a36Sopenharmony_ciunregister:
125062306a36Sopenharmony_ci	unregister_pernet_device(&fou_net_ops);
125162306a36Sopenharmony_ciexit:
125262306a36Sopenharmony_ci	return ret;
125362306a36Sopenharmony_ci}
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_cistatic void __exit fou_fini(void)
125662306a36Sopenharmony_ci{
125762306a36Sopenharmony_ci	ip_tunnel_encap_del_fou_ops();
125862306a36Sopenharmony_ci	genl_unregister_family(&fou_nl_family);
125962306a36Sopenharmony_ci	unregister_pernet_device(&fou_net_ops);
126062306a36Sopenharmony_ci}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_cimodule_init(fou_init);
126362306a36Sopenharmony_cimodule_exit(fou_fini);
126462306a36Sopenharmony_ciMODULE_AUTHOR("Tom Herbert <therbert@google.com>");
126562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
126662306a36Sopenharmony_ciMODULE_DESCRIPTION("Foo over UDP");
1267