18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci#include <linux/module.h>
38c2ecf20Sopenharmony_ci#include <linux/errno.h>
48c2ecf20Sopenharmony_ci#include <linux/socket.h>
58c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
68c2ecf20Sopenharmony_ci#include <linux/ip.h>
78c2ecf20Sopenharmony_ci#include <linux/icmp.h>
88c2ecf20Sopenharmony_ci#include <linux/udp.h>
98c2ecf20Sopenharmony_ci#include <linux/types.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <net/genetlink.h>
128c2ecf20Sopenharmony_ci#include <net/gue.h>
138c2ecf20Sopenharmony_ci#include <net/fou.h>
148c2ecf20Sopenharmony_ci#include <net/ip.h>
158c2ecf20Sopenharmony_ci#include <net/protocol.h>
168c2ecf20Sopenharmony_ci#include <net/udp.h>
178c2ecf20Sopenharmony_ci#include <net/udp_tunnel.h>
188c2ecf20Sopenharmony_ci#include <net/xfrm.h>
198c2ecf20Sopenharmony_ci#include <uapi/linux/fou.h>
208c2ecf20Sopenharmony_ci#include <uapi/linux/genetlink.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistruct fou {
238c2ecf20Sopenharmony_ci	struct socket *sock;
248c2ecf20Sopenharmony_ci	u8 protocol;
258c2ecf20Sopenharmony_ci	u8 flags;
268c2ecf20Sopenharmony_ci	__be16 port;
278c2ecf20Sopenharmony_ci	u8 family;
288c2ecf20Sopenharmony_ci	u16 type;
298c2ecf20Sopenharmony_ci	struct list_head list;
308c2ecf20Sopenharmony_ci	struct rcu_head rcu;
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define FOU_F_REMCSUM_NOPARTIAL BIT(0)
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct fou_cfg {
368c2ecf20Sopenharmony_ci	u16 type;
378c2ecf20Sopenharmony_ci	u8 protocol;
388c2ecf20Sopenharmony_ci	u8 flags;
398c2ecf20Sopenharmony_ci	struct udp_port_cfg udp_config;
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic unsigned int fou_net_id;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistruct fou_net {
458c2ecf20Sopenharmony_ci	struct list_head fou_list;
468c2ecf20Sopenharmony_ci	struct mutex fou_lock;
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic inline struct fou *fou_from_sock(struct sock *sk)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	return sk->sk_user_data;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic int fou_recv_pull(struct sk_buff *skb, struct fou *fou, size_t len)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	/* Remove 'len' bytes from the packet (UDP header and
578c2ecf20Sopenharmony_ci	 * FOU header if present).
588c2ecf20Sopenharmony_ci	 */
598c2ecf20Sopenharmony_ci	if (fou->family == AF_INET)
608c2ecf20Sopenharmony_ci		ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len);
618c2ecf20Sopenharmony_ci	else
628c2ecf20Sopenharmony_ci		ipv6_hdr(skb)->payload_len =
638c2ecf20Sopenharmony_ci		    htons(ntohs(ipv6_hdr(skb)->payload_len) - len);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	__skb_pull(skb, len);
668c2ecf20Sopenharmony_ci	skb_postpull_rcsum(skb, udp_hdr(skb), len);
678c2ecf20Sopenharmony_ci	skb_reset_transport_header(skb);
688c2ecf20Sopenharmony_ci	return iptunnel_pull_offloads(skb);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct fou *fou = fou_from_sock(sk);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (!fou)
768c2ecf20Sopenharmony_ci		return 1;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (fou_recv_pull(skb, fou, sizeof(struct udphdr)))
798c2ecf20Sopenharmony_ci		goto drop;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return -fou->protocol;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cidrop:
848c2ecf20Sopenharmony_ci	kfree_skb(skb);
858c2ecf20Sopenharmony_ci	return 0;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr,
898c2ecf20Sopenharmony_ci				  void *data, size_t hdrlen, u8 ipproto,
908c2ecf20Sopenharmony_ci				  bool nopartial)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	__be16 *pd = data;
938c2ecf20Sopenharmony_ci	size_t start = ntohs(pd[0]);
948c2ecf20Sopenharmony_ci	size_t offset = ntohs(pd[1]);
958c2ecf20Sopenharmony_ci	size_t plen = sizeof(struct udphdr) + hdrlen +
968c2ecf20Sopenharmony_ci	    max_t(size_t, offset + sizeof(u16), start);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (skb->remcsum_offload)
998c2ecf20Sopenharmony_ci		return guehdr;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, plen))
1028c2ecf20Sopenharmony_ci		return NULL;
1038c2ecf20Sopenharmony_ci	guehdr = (struct guehdr *)&udp_hdr(skb)[1];
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	skb_remcsum_process(skb, (void *)guehdr + hdrlen,
1068c2ecf20Sopenharmony_ci			    start, offset, nopartial);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return guehdr;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic int gue_control_message(struct sk_buff *skb, struct guehdr *guehdr)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	/* No support yet */
1148c2ecf20Sopenharmony_ci	kfree_skb(skb);
1158c2ecf20Sopenharmony_ci	return 0;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct fou *fou = fou_from_sock(sk);
1218c2ecf20Sopenharmony_ci	size_t len, optlen, hdrlen;
1228c2ecf20Sopenharmony_ci	struct guehdr *guehdr;
1238c2ecf20Sopenharmony_ci	void *data;
1248c2ecf20Sopenharmony_ci	u16 doffset = 0;
1258c2ecf20Sopenharmony_ci	u8 proto_ctype;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (!fou)
1288c2ecf20Sopenharmony_ci		return 1;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	len = sizeof(struct udphdr) + sizeof(struct guehdr);
1318c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, len))
1328c2ecf20Sopenharmony_ci		goto drop;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	guehdr = (struct guehdr *)&udp_hdr(skb)[1];
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	switch (guehdr->version) {
1378c2ecf20Sopenharmony_ci	case 0: /* Full GUE header present */
1388c2ecf20Sopenharmony_ci		break;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	case 1: {
1418c2ecf20Sopenharmony_ci		/* Direct encapsulation of IPv4 or IPv6 */
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		int prot;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci		switch (((struct iphdr *)guehdr)->version) {
1468c2ecf20Sopenharmony_ci		case 4:
1478c2ecf20Sopenharmony_ci			prot = IPPROTO_IPIP;
1488c2ecf20Sopenharmony_ci			break;
1498c2ecf20Sopenharmony_ci		case 6:
1508c2ecf20Sopenharmony_ci			prot = IPPROTO_IPV6;
1518c2ecf20Sopenharmony_ci			break;
1528c2ecf20Sopenharmony_ci		default:
1538c2ecf20Sopenharmony_ci			goto drop;
1548c2ecf20Sopenharmony_ci		}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		if (fou_recv_pull(skb, fou, sizeof(struct udphdr)))
1578c2ecf20Sopenharmony_ci			goto drop;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		return -prot;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	default: /* Undefined version */
1638c2ecf20Sopenharmony_ci		goto drop;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	optlen = guehdr->hlen << 2;
1678c2ecf20Sopenharmony_ci	len += optlen;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, len))
1708c2ecf20Sopenharmony_ci		goto drop;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	/* guehdr may change after pull */
1738c2ecf20Sopenharmony_ci	guehdr = (struct guehdr *)&udp_hdr(skb)[1];
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (validate_gue_flags(guehdr, optlen))
1768c2ecf20Sopenharmony_ci		goto drop;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	hdrlen = sizeof(struct guehdr) + optlen;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	if (fou->family == AF_INET)
1818c2ecf20Sopenharmony_ci		ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len);
1828c2ecf20Sopenharmony_ci	else
1838c2ecf20Sopenharmony_ci		ipv6_hdr(skb)->payload_len =
1848c2ecf20Sopenharmony_ci		    htons(ntohs(ipv6_hdr(skb)->payload_len) - len);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* Pull csum through the guehdr now . This can be used if
1878c2ecf20Sopenharmony_ci	 * there is a remote checksum offload.
1888c2ecf20Sopenharmony_ci	 */
1898c2ecf20Sopenharmony_ci	skb_postpull_rcsum(skb, udp_hdr(skb), len);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	data = &guehdr[1];
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (guehdr->flags & GUE_FLAG_PRIV) {
1948c2ecf20Sopenharmony_ci		__be32 flags = *(__be32 *)(data + doffset);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci		doffset += GUE_LEN_PRIV;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		if (flags & GUE_PFLAG_REMCSUM) {
1998c2ecf20Sopenharmony_ci			guehdr = gue_remcsum(skb, guehdr, data + doffset,
2008c2ecf20Sopenharmony_ci					     hdrlen, guehdr->proto_ctype,
2018c2ecf20Sopenharmony_ci					     !!(fou->flags &
2028c2ecf20Sopenharmony_ci						FOU_F_REMCSUM_NOPARTIAL));
2038c2ecf20Sopenharmony_ci			if (!guehdr)
2048c2ecf20Sopenharmony_ci				goto drop;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci			data = &guehdr[1];
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci			doffset += GUE_PLEN_REMCSUM;
2098c2ecf20Sopenharmony_ci		}
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (unlikely(guehdr->control))
2138c2ecf20Sopenharmony_ci		return gue_control_message(skb, guehdr);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	proto_ctype = guehdr->proto_ctype;
2168c2ecf20Sopenharmony_ci	__skb_pull(skb, sizeof(struct udphdr) + hdrlen);
2178c2ecf20Sopenharmony_ci	skb_reset_transport_header(skb);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (iptunnel_pull_offloads(skb))
2208c2ecf20Sopenharmony_ci		goto drop;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return -proto_ctype;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cidrop:
2258c2ecf20Sopenharmony_ci	kfree_skb(skb);
2268c2ecf20Sopenharmony_ci	return 0;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic struct sk_buff *fou_gro_receive(struct sock *sk,
2308c2ecf20Sopenharmony_ci				       struct list_head *head,
2318c2ecf20Sopenharmony_ci				       struct sk_buff *skb)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	u8 proto = fou_from_sock(sk)->protocol;
2348c2ecf20Sopenharmony_ci	const struct net_offload **offloads;
2358c2ecf20Sopenharmony_ci	const struct net_offload *ops;
2368c2ecf20Sopenharmony_ci	struct sk_buff *pp = NULL;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	/* We can clear the encap_mark for FOU as we are essentially doing
2398c2ecf20Sopenharmony_ci	 * one of two possible things.  We are either adding an L4 tunnel
2408c2ecf20Sopenharmony_ci	 * header to the outer L3 tunnel header, or we are simply
2418c2ecf20Sopenharmony_ci	 * treating the GRE tunnel header as though it is a UDP protocol
2428c2ecf20Sopenharmony_ci	 * specific header such as VXLAN or GENEVE.
2438c2ecf20Sopenharmony_ci	 */
2448c2ecf20Sopenharmony_ci	NAPI_GRO_CB(skb)->encap_mark = 0;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/* Flag this frame as already having an outer encap header */
2478c2ecf20Sopenharmony_ci	NAPI_GRO_CB(skb)->is_fou = 1;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	rcu_read_lock();
2508c2ecf20Sopenharmony_ci	offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
2518c2ecf20Sopenharmony_ci	ops = rcu_dereference(offloads[proto]);
2528c2ecf20Sopenharmony_ci	if (!ops || !ops->callbacks.gro_receive)
2538c2ecf20Sopenharmony_ci		goto out_unlock;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ciout_unlock:
2588c2ecf20Sopenharmony_ci	rcu_read_unlock();
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	return pp;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic int fou_gro_complete(struct sock *sk, struct sk_buff *skb,
2648c2ecf20Sopenharmony_ci			    int nhoff)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	const struct net_offload *ops;
2678c2ecf20Sopenharmony_ci	u8 proto = fou_from_sock(sk)->protocol;
2688c2ecf20Sopenharmony_ci	int err = -ENOSYS;
2698c2ecf20Sopenharmony_ci	const struct net_offload **offloads;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	rcu_read_lock();
2728c2ecf20Sopenharmony_ci	offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
2738c2ecf20Sopenharmony_ci	ops = rcu_dereference(offloads[proto]);
2748c2ecf20Sopenharmony_ci	if (WARN_ON(!ops || !ops->callbacks.gro_complete))
2758c2ecf20Sopenharmony_ci		goto out_unlock;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	err = ops->callbacks.gro_complete(skb, nhoff);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	skb_set_inner_mac_header(skb, nhoff);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ciout_unlock:
2828c2ecf20Sopenharmony_ci	rcu_read_unlock();
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	return err;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistatic struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
2888c2ecf20Sopenharmony_ci				      struct guehdr *guehdr, void *data,
2898c2ecf20Sopenharmony_ci				      size_t hdrlen, struct gro_remcsum *grc,
2908c2ecf20Sopenharmony_ci				      bool nopartial)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	__be16 *pd = data;
2938c2ecf20Sopenharmony_ci	size_t start = ntohs(pd[0]);
2948c2ecf20Sopenharmony_ci	size_t offset = ntohs(pd[1]);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	if (skb->remcsum_offload)
2978c2ecf20Sopenharmony_ci		return guehdr;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (!NAPI_GRO_CB(skb)->csum_valid)
3008c2ecf20Sopenharmony_ci		return NULL;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	guehdr = skb_gro_remcsum_process(skb, (void *)guehdr, off, hdrlen,
3038c2ecf20Sopenharmony_ci					 start, offset, grc, nopartial);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	skb->remcsum_offload = 1;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	return guehdr;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic struct sk_buff *gue_gro_receive(struct sock *sk,
3118c2ecf20Sopenharmony_ci				       struct list_head *head,
3128c2ecf20Sopenharmony_ci				       struct sk_buff *skb)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	const struct net_offload **offloads;
3158c2ecf20Sopenharmony_ci	const struct net_offload *ops;
3168c2ecf20Sopenharmony_ci	struct sk_buff *pp = NULL;
3178c2ecf20Sopenharmony_ci	struct sk_buff *p;
3188c2ecf20Sopenharmony_ci	struct guehdr *guehdr;
3198c2ecf20Sopenharmony_ci	size_t len, optlen, hdrlen, off;
3208c2ecf20Sopenharmony_ci	void *data;
3218c2ecf20Sopenharmony_ci	u16 doffset = 0;
3228c2ecf20Sopenharmony_ci	int flush = 1;
3238c2ecf20Sopenharmony_ci	struct fou *fou = fou_from_sock(sk);
3248c2ecf20Sopenharmony_ci	struct gro_remcsum grc;
3258c2ecf20Sopenharmony_ci	u8 proto;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	skb_gro_remcsum_init(&grc);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	off = skb_gro_offset(skb);
3308c2ecf20Sopenharmony_ci	len = off + sizeof(*guehdr);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	guehdr = skb_gro_header_fast(skb, off);
3338c2ecf20Sopenharmony_ci	if (skb_gro_header_hard(skb, len)) {
3348c2ecf20Sopenharmony_ci		guehdr = skb_gro_header_slow(skb, len, off);
3358c2ecf20Sopenharmony_ci		if (unlikely(!guehdr))
3368c2ecf20Sopenharmony_ci			goto out;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	switch (guehdr->version) {
3408c2ecf20Sopenharmony_ci	case 0:
3418c2ecf20Sopenharmony_ci		break;
3428c2ecf20Sopenharmony_ci	case 1:
3438c2ecf20Sopenharmony_ci		switch (((struct iphdr *)guehdr)->version) {
3448c2ecf20Sopenharmony_ci		case 4:
3458c2ecf20Sopenharmony_ci			proto = IPPROTO_IPIP;
3468c2ecf20Sopenharmony_ci			break;
3478c2ecf20Sopenharmony_ci		case 6:
3488c2ecf20Sopenharmony_ci			proto = IPPROTO_IPV6;
3498c2ecf20Sopenharmony_ci			break;
3508c2ecf20Sopenharmony_ci		default:
3518c2ecf20Sopenharmony_ci			goto out;
3528c2ecf20Sopenharmony_ci		}
3538c2ecf20Sopenharmony_ci		goto next_proto;
3548c2ecf20Sopenharmony_ci	default:
3558c2ecf20Sopenharmony_ci		goto out;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	optlen = guehdr->hlen << 2;
3598c2ecf20Sopenharmony_ci	len += optlen;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (skb_gro_header_hard(skb, len)) {
3628c2ecf20Sopenharmony_ci		guehdr = skb_gro_header_slow(skb, len, off);
3638c2ecf20Sopenharmony_ci		if (unlikely(!guehdr))
3648c2ecf20Sopenharmony_ci			goto out;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	if (unlikely(guehdr->control) || guehdr->version != 0 ||
3688c2ecf20Sopenharmony_ci	    validate_gue_flags(guehdr, optlen))
3698c2ecf20Sopenharmony_ci		goto out;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	hdrlen = sizeof(*guehdr) + optlen;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	/* Adjust NAPI_GRO_CB(skb)->csum to account for guehdr,
3748c2ecf20Sopenharmony_ci	 * this is needed if there is a remote checkcsum offload.
3758c2ecf20Sopenharmony_ci	 */
3768c2ecf20Sopenharmony_ci	skb_gro_postpull_rcsum(skb, guehdr, hdrlen);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	data = &guehdr[1];
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	if (guehdr->flags & GUE_FLAG_PRIV) {
3818c2ecf20Sopenharmony_ci		__be32 flags = *(__be32 *)(data + doffset);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci		doffset += GUE_LEN_PRIV;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci		if (flags & GUE_PFLAG_REMCSUM) {
3868c2ecf20Sopenharmony_ci			guehdr = gue_gro_remcsum(skb, off, guehdr,
3878c2ecf20Sopenharmony_ci						 data + doffset, hdrlen, &grc,
3888c2ecf20Sopenharmony_ci						 !!(fou->flags &
3898c2ecf20Sopenharmony_ci						    FOU_F_REMCSUM_NOPARTIAL));
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci			if (!guehdr)
3928c2ecf20Sopenharmony_ci				goto out;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci			data = &guehdr[1];
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci			doffset += GUE_PLEN_REMCSUM;
3978c2ecf20Sopenharmony_ci		}
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	skb_gro_pull(skb, hdrlen);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	list_for_each_entry(p, head, list) {
4038c2ecf20Sopenharmony_ci		const struct guehdr *guehdr2;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		if (!NAPI_GRO_CB(p)->same_flow)
4068c2ecf20Sopenharmony_ci			continue;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci		guehdr2 = (struct guehdr *)(p->data + off);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci		/* Compare base GUE header to be equal (covers
4118c2ecf20Sopenharmony_ci		 * hlen, version, proto_ctype, and flags.
4128c2ecf20Sopenharmony_ci		 */
4138c2ecf20Sopenharmony_ci		if (guehdr->word != guehdr2->word) {
4148c2ecf20Sopenharmony_ci			NAPI_GRO_CB(p)->same_flow = 0;
4158c2ecf20Sopenharmony_ci			continue;
4168c2ecf20Sopenharmony_ci		}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		/* Compare optional fields are the same. */
4198c2ecf20Sopenharmony_ci		if (guehdr->hlen && memcmp(&guehdr[1], &guehdr2[1],
4208c2ecf20Sopenharmony_ci					   guehdr->hlen << 2)) {
4218c2ecf20Sopenharmony_ci			NAPI_GRO_CB(p)->same_flow = 0;
4228c2ecf20Sopenharmony_ci			continue;
4238c2ecf20Sopenharmony_ci		}
4248c2ecf20Sopenharmony_ci	}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	proto = guehdr->proto_ctype;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cinext_proto:
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	/* We can clear the encap_mark for GUE as we are essentially doing
4318c2ecf20Sopenharmony_ci	 * one of two possible things.  We are either adding an L4 tunnel
4328c2ecf20Sopenharmony_ci	 * header to the outer L3 tunnel header, or we are simply
4338c2ecf20Sopenharmony_ci	 * treating the GRE tunnel header as though it is a UDP protocol
4348c2ecf20Sopenharmony_ci	 * specific header such as VXLAN or GENEVE.
4358c2ecf20Sopenharmony_ci	 */
4368c2ecf20Sopenharmony_ci	NAPI_GRO_CB(skb)->encap_mark = 0;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	/* Flag this frame as already having an outer encap header */
4398c2ecf20Sopenharmony_ci	NAPI_GRO_CB(skb)->is_fou = 1;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	rcu_read_lock();
4428c2ecf20Sopenharmony_ci	offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
4438c2ecf20Sopenharmony_ci	ops = rcu_dereference(offloads[proto]);
4448c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(!ops || !ops->callbacks.gro_receive))
4458c2ecf20Sopenharmony_ci		goto out_unlock;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
4488c2ecf20Sopenharmony_ci	flush = 0;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ciout_unlock:
4518c2ecf20Sopenharmony_ci	rcu_read_unlock();
4528c2ecf20Sopenharmony_ciout:
4538c2ecf20Sopenharmony_ci	skb_gro_flush_final_remcsum(skb, pp, flush, &grc);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	return pp;
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cistatic int gue_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	const struct net_offload **offloads;
4618c2ecf20Sopenharmony_ci	struct guehdr *guehdr = (struct guehdr *)(skb->data + nhoff);
4628c2ecf20Sopenharmony_ci	const struct net_offload *ops;
4638c2ecf20Sopenharmony_ci	unsigned int guehlen = 0;
4648c2ecf20Sopenharmony_ci	u8 proto;
4658c2ecf20Sopenharmony_ci	int err = -ENOENT;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	switch (guehdr->version) {
4688c2ecf20Sopenharmony_ci	case 0:
4698c2ecf20Sopenharmony_ci		proto = guehdr->proto_ctype;
4708c2ecf20Sopenharmony_ci		guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
4718c2ecf20Sopenharmony_ci		break;
4728c2ecf20Sopenharmony_ci	case 1:
4738c2ecf20Sopenharmony_ci		switch (((struct iphdr *)guehdr)->version) {
4748c2ecf20Sopenharmony_ci		case 4:
4758c2ecf20Sopenharmony_ci			proto = IPPROTO_IPIP;
4768c2ecf20Sopenharmony_ci			break;
4778c2ecf20Sopenharmony_ci		case 6:
4788c2ecf20Sopenharmony_ci			proto = IPPROTO_IPV6;
4798c2ecf20Sopenharmony_ci			break;
4808c2ecf20Sopenharmony_ci		default:
4818c2ecf20Sopenharmony_ci			return err;
4828c2ecf20Sopenharmony_ci		}
4838c2ecf20Sopenharmony_ci		break;
4848c2ecf20Sopenharmony_ci	default:
4858c2ecf20Sopenharmony_ci		return err;
4868c2ecf20Sopenharmony_ci	}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	rcu_read_lock();
4898c2ecf20Sopenharmony_ci	offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
4908c2ecf20Sopenharmony_ci	ops = rcu_dereference(offloads[proto]);
4918c2ecf20Sopenharmony_ci	if (WARN_ON(!ops || !ops->callbacks.gro_complete))
4928c2ecf20Sopenharmony_ci		goto out_unlock;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	err = ops->callbacks.gro_complete(skb, nhoff + guehlen);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	skb_set_inner_mac_header(skb, nhoff + guehlen);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ciout_unlock:
4998c2ecf20Sopenharmony_ci	rcu_read_unlock();
5008c2ecf20Sopenharmony_ci	return err;
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_cistatic bool fou_cfg_cmp(struct fou *fou, struct fou_cfg *cfg)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	struct sock *sk = fou->sock->sk;
5068c2ecf20Sopenharmony_ci	struct udp_port_cfg *udp_cfg = &cfg->udp_config;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	if (fou->family != udp_cfg->family ||
5098c2ecf20Sopenharmony_ci	    fou->port != udp_cfg->local_udp_port ||
5108c2ecf20Sopenharmony_ci	    sk->sk_dport != udp_cfg->peer_udp_port ||
5118c2ecf20Sopenharmony_ci	    sk->sk_bound_dev_if != udp_cfg->bind_ifindex)
5128c2ecf20Sopenharmony_ci		return false;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	if (fou->family == AF_INET) {
5158c2ecf20Sopenharmony_ci		if (sk->sk_rcv_saddr != udp_cfg->local_ip.s_addr ||
5168c2ecf20Sopenharmony_ci		    sk->sk_daddr != udp_cfg->peer_ip.s_addr)
5178c2ecf20Sopenharmony_ci			return false;
5188c2ecf20Sopenharmony_ci		else
5198c2ecf20Sopenharmony_ci			return true;
5208c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
5218c2ecf20Sopenharmony_ci	} else {
5228c2ecf20Sopenharmony_ci		if (ipv6_addr_cmp(&sk->sk_v6_rcv_saddr, &udp_cfg->local_ip6) ||
5238c2ecf20Sopenharmony_ci		    ipv6_addr_cmp(&sk->sk_v6_daddr, &udp_cfg->peer_ip6))
5248c2ecf20Sopenharmony_ci			return false;
5258c2ecf20Sopenharmony_ci		else
5268c2ecf20Sopenharmony_ci			return true;
5278c2ecf20Sopenharmony_ci#endif
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	return false;
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_cistatic int fou_add_to_port_list(struct net *net, struct fou *fou,
5348c2ecf20Sopenharmony_ci				struct fou_cfg *cfg)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	struct fou_net *fn = net_generic(net, fou_net_id);
5378c2ecf20Sopenharmony_ci	struct fou *fout;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	mutex_lock(&fn->fou_lock);
5408c2ecf20Sopenharmony_ci	list_for_each_entry(fout, &fn->fou_list, list) {
5418c2ecf20Sopenharmony_ci		if (fou_cfg_cmp(fout, cfg)) {
5428c2ecf20Sopenharmony_ci			mutex_unlock(&fn->fou_lock);
5438c2ecf20Sopenharmony_ci			return -EALREADY;
5448c2ecf20Sopenharmony_ci		}
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	list_add(&fou->list, &fn->fou_list);
5488c2ecf20Sopenharmony_ci	mutex_unlock(&fn->fou_lock);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	return 0;
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_cistatic void fou_release(struct fou *fou)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	struct socket *sock = fou->sock;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	list_del(&fou->list);
5588c2ecf20Sopenharmony_ci	udp_tunnel_sock_release(sock);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	kfree_rcu(fou, rcu);
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_cistatic int fou_create(struct net *net, struct fou_cfg *cfg,
5648c2ecf20Sopenharmony_ci		      struct socket **sockp)
5658c2ecf20Sopenharmony_ci{
5668c2ecf20Sopenharmony_ci	struct socket *sock = NULL;
5678c2ecf20Sopenharmony_ci	struct fou *fou = NULL;
5688c2ecf20Sopenharmony_ci	struct sock *sk;
5698c2ecf20Sopenharmony_ci	struct udp_tunnel_sock_cfg tunnel_cfg;
5708c2ecf20Sopenharmony_ci	int err;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	/* Open UDP socket */
5738c2ecf20Sopenharmony_ci	err = udp_sock_create(net, &cfg->udp_config, &sock);
5748c2ecf20Sopenharmony_ci	if (err < 0)
5758c2ecf20Sopenharmony_ci		goto error;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	/* Allocate FOU port structure */
5788c2ecf20Sopenharmony_ci	fou = kzalloc(sizeof(*fou), GFP_KERNEL);
5798c2ecf20Sopenharmony_ci	if (!fou) {
5808c2ecf20Sopenharmony_ci		err = -ENOMEM;
5818c2ecf20Sopenharmony_ci		goto error;
5828c2ecf20Sopenharmony_ci	}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	sk = sock->sk;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	fou->port = cfg->udp_config.local_udp_port;
5878c2ecf20Sopenharmony_ci	fou->family = cfg->udp_config.family;
5888c2ecf20Sopenharmony_ci	fou->flags = cfg->flags;
5898c2ecf20Sopenharmony_ci	fou->type = cfg->type;
5908c2ecf20Sopenharmony_ci	fou->sock = sock;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	memset(&tunnel_cfg, 0, sizeof(tunnel_cfg));
5938c2ecf20Sopenharmony_ci	tunnel_cfg.encap_type = 1;
5948c2ecf20Sopenharmony_ci	tunnel_cfg.sk_user_data = fou;
5958c2ecf20Sopenharmony_ci	tunnel_cfg.encap_destroy = NULL;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	/* Initial for fou type */
5988c2ecf20Sopenharmony_ci	switch (cfg->type) {
5998c2ecf20Sopenharmony_ci	case FOU_ENCAP_DIRECT:
6008c2ecf20Sopenharmony_ci		tunnel_cfg.encap_rcv = fou_udp_recv;
6018c2ecf20Sopenharmony_ci		tunnel_cfg.gro_receive = fou_gro_receive;
6028c2ecf20Sopenharmony_ci		tunnel_cfg.gro_complete = fou_gro_complete;
6038c2ecf20Sopenharmony_ci		fou->protocol = cfg->protocol;
6048c2ecf20Sopenharmony_ci		break;
6058c2ecf20Sopenharmony_ci	case FOU_ENCAP_GUE:
6068c2ecf20Sopenharmony_ci		tunnel_cfg.encap_rcv = gue_udp_recv;
6078c2ecf20Sopenharmony_ci		tunnel_cfg.gro_receive = gue_gro_receive;
6088c2ecf20Sopenharmony_ci		tunnel_cfg.gro_complete = gue_gro_complete;
6098c2ecf20Sopenharmony_ci		break;
6108c2ecf20Sopenharmony_ci	default:
6118c2ecf20Sopenharmony_ci		err = -EINVAL;
6128c2ecf20Sopenharmony_ci		goto error;
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	setup_udp_tunnel_sock(net, sock, &tunnel_cfg);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	sk->sk_allocation = GFP_ATOMIC;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	err = fou_add_to_port_list(net, fou, cfg);
6208c2ecf20Sopenharmony_ci	if (err)
6218c2ecf20Sopenharmony_ci		goto error;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	if (sockp)
6248c2ecf20Sopenharmony_ci		*sockp = sock;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	return 0;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_cierror:
6298c2ecf20Sopenharmony_ci	kfree(fou);
6308c2ecf20Sopenharmony_ci	if (sock)
6318c2ecf20Sopenharmony_ci		udp_tunnel_sock_release(sock);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	return err;
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistatic int fou_destroy(struct net *net, struct fou_cfg *cfg)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	struct fou_net *fn = net_generic(net, fou_net_id);
6398c2ecf20Sopenharmony_ci	int err = -EINVAL;
6408c2ecf20Sopenharmony_ci	struct fou *fou;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	mutex_lock(&fn->fou_lock);
6438c2ecf20Sopenharmony_ci	list_for_each_entry(fou, &fn->fou_list, list) {
6448c2ecf20Sopenharmony_ci		if (fou_cfg_cmp(fou, cfg)) {
6458c2ecf20Sopenharmony_ci			fou_release(fou);
6468c2ecf20Sopenharmony_ci			err = 0;
6478c2ecf20Sopenharmony_ci			break;
6488c2ecf20Sopenharmony_ci		}
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci	mutex_unlock(&fn->fou_lock);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	return err;
6538c2ecf20Sopenharmony_ci}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_cistatic struct genl_family fou_nl_family;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_cistatic const struct nla_policy fou_nl_policy[FOU_ATTR_MAX + 1] = {
6588c2ecf20Sopenharmony_ci	[FOU_ATTR_PORT]			= { .type = NLA_U16, },
6598c2ecf20Sopenharmony_ci	[FOU_ATTR_AF]			= { .type = NLA_U8, },
6608c2ecf20Sopenharmony_ci	[FOU_ATTR_IPPROTO]		= { .type = NLA_U8, },
6618c2ecf20Sopenharmony_ci	[FOU_ATTR_TYPE]			= { .type = NLA_U8, },
6628c2ecf20Sopenharmony_ci	[FOU_ATTR_REMCSUM_NOPARTIAL]	= { .type = NLA_FLAG, },
6638c2ecf20Sopenharmony_ci	[FOU_ATTR_LOCAL_V4]		= { .type = NLA_U32, },
6648c2ecf20Sopenharmony_ci	[FOU_ATTR_PEER_V4]		= { .type = NLA_U32, },
6658c2ecf20Sopenharmony_ci	[FOU_ATTR_LOCAL_V6]		= { .len = sizeof(struct in6_addr), },
6668c2ecf20Sopenharmony_ci	[FOU_ATTR_PEER_V6]		= { .len = sizeof(struct in6_addr), },
6678c2ecf20Sopenharmony_ci	[FOU_ATTR_PEER_PORT]		= { .type = NLA_U16, },
6688c2ecf20Sopenharmony_ci	[FOU_ATTR_IFINDEX]		= { .type = NLA_S32, },
6698c2ecf20Sopenharmony_ci};
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_cistatic int parse_nl_config(struct genl_info *info,
6728c2ecf20Sopenharmony_ci			   struct fou_cfg *cfg)
6738c2ecf20Sopenharmony_ci{
6748c2ecf20Sopenharmony_ci	bool has_local = false, has_peer = false;
6758c2ecf20Sopenharmony_ci	struct nlattr *attr;
6768c2ecf20Sopenharmony_ci	int ifindex;
6778c2ecf20Sopenharmony_ci	__be16 port;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	memset(cfg, 0, sizeof(*cfg));
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	cfg->udp_config.family = AF_INET;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	if (info->attrs[FOU_ATTR_AF]) {
6848c2ecf20Sopenharmony_ci		u8 family = nla_get_u8(info->attrs[FOU_ATTR_AF]);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci		switch (family) {
6878c2ecf20Sopenharmony_ci		case AF_INET:
6888c2ecf20Sopenharmony_ci			break;
6898c2ecf20Sopenharmony_ci		case AF_INET6:
6908c2ecf20Sopenharmony_ci			cfg->udp_config.ipv6_v6only = 1;
6918c2ecf20Sopenharmony_ci			break;
6928c2ecf20Sopenharmony_ci		default:
6938c2ecf20Sopenharmony_ci			return -EAFNOSUPPORT;
6948c2ecf20Sopenharmony_ci		}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci		cfg->udp_config.family = family;
6978c2ecf20Sopenharmony_ci	}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	if (info->attrs[FOU_ATTR_PORT]) {
7008c2ecf20Sopenharmony_ci		port = nla_get_be16(info->attrs[FOU_ATTR_PORT]);
7018c2ecf20Sopenharmony_ci		cfg->udp_config.local_udp_port = port;
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	if (info->attrs[FOU_ATTR_IPPROTO])
7058c2ecf20Sopenharmony_ci		cfg->protocol = nla_get_u8(info->attrs[FOU_ATTR_IPPROTO]);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	if (info->attrs[FOU_ATTR_TYPE])
7088c2ecf20Sopenharmony_ci		cfg->type = nla_get_u8(info->attrs[FOU_ATTR_TYPE]);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	if (info->attrs[FOU_ATTR_REMCSUM_NOPARTIAL])
7118c2ecf20Sopenharmony_ci		cfg->flags |= FOU_F_REMCSUM_NOPARTIAL;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	if (cfg->udp_config.family == AF_INET) {
7148c2ecf20Sopenharmony_ci		if (info->attrs[FOU_ATTR_LOCAL_V4]) {
7158c2ecf20Sopenharmony_ci			attr = info->attrs[FOU_ATTR_LOCAL_V4];
7168c2ecf20Sopenharmony_ci			cfg->udp_config.local_ip.s_addr = nla_get_in_addr(attr);
7178c2ecf20Sopenharmony_ci			has_local = true;
7188c2ecf20Sopenharmony_ci		}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci		if (info->attrs[FOU_ATTR_PEER_V4]) {
7218c2ecf20Sopenharmony_ci			attr = info->attrs[FOU_ATTR_PEER_V4];
7228c2ecf20Sopenharmony_ci			cfg->udp_config.peer_ip.s_addr = nla_get_in_addr(attr);
7238c2ecf20Sopenharmony_ci			has_peer = true;
7248c2ecf20Sopenharmony_ci		}
7258c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
7268c2ecf20Sopenharmony_ci	} else {
7278c2ecf20Sopenharmony_ci		if (info->attrs[FOU_ATTR_LOCAL_V6]) {
7288c2ecf20Sopenharmony_ci			attr = info->attrs[FOU_ATTR_LOCAL_V6];
7298c2ecf20Sopenharmony_ci			cfg->udp_config.local_ip6 = nla_get_in6_addr(attr);
7308c2ecf20Sopenharmony_ci			has_local = true;
7318c2ecf20Sopenharmony_ci		}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci		if (info->attrs[FOU_ATTR_PEER_V6]) {
7348c2ecf20Sopenharmony_ci			attr = info->attrs[FOU_ATTR_PEER_V6];
7358c2ecf20Sopenharmony_ci			cfg->udp_config.peer_ip6 = nla_get_in6_addr(attr);
7368c2ecf20Sopenharmony_ci			has_peer = true;
7378c2ecf20Sopenharmony_ci		}
7388c2ecf20Sopenharmony_ci#endif
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	if (has_peer) {
7428c2ecf20Sopenharmony_ci		if (info->attrs[FOU_ATTR_PEER_PORT]) {
7438c2ecf20Sopenharmony_ci			port = nla_get_be16(info->attrs[FOU_ATTR_PEER_PORT]);
7448c2ecf20Sopenharmony_ci			cfg->udp_config.peer_udp_port = port;
7458c2ecf20Sopenharmony_ci		} else {
7468c2ecf20Sopenharmony_ci			return -EINVAL;
7478c2ecf20Sopenharmony_ci		}
7488c2ecf20Sopenharmony_ci	}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	if (info->attrs[FOU_ATTR_IFINDEX]) {
7518c2ecf20Sopenharmony_ci		if (!has_local)
7528c2ecf20Sopenharmony_ci			return -EINVAL;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci		ifindex = nla_get_s32(info->attrs[FOU_ATTR_IFINDEX]);
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci		cfg->udp_config.bind_ifindex = ifindex;
7578c2ecf20Sopenharmony_ci	}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	return 0;
7608c2ecf20Sopenharmony_ci}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_cistatic int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info)
7638c2ecf20Sopenharmony_ci{
7648c2ecf20Sopenharmony_ci	struct net *net = genl_info_net(info);
7658c2ecf20Sopenharmony_ci	struct fou_cfg cfg;
7668c2ecf20Sopenharmony_ci	int err;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	err = parse_nl_config(info, &cfg);
7698c2ecf20Sopenharmony_ci	if (err)
7708c2ecf20Sopenharmony_ci		return err;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	return fou_create(net, &cfg, NULL);
7738c2ecf20Sopenharmony_ci}
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_cistatic int fou_nl_cmd_rm_port(struct sk_buff *skb, struct genl_info *info)
7768c2ecf20Sopenharmony_ci{
7778c2ecf20Sopenharmony_ci	struct net *net = genl_info_net(info);
7788c2ecf20Sopenharmony_ci	struct fou_cfg cfg;
7798c2ecf20Sopenharmony_ci	int err;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	err = parse_nl_config(info, &cfg);
7828c2ecf20Sopenharmony_ci	if (err)
7838c2ecf20Sopenharmony_ci		return err;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	return fou_destroy(net, &cfg);
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_cistatic int fou_fill_info(struct fou *fou, struct sk_buff *msg)
7898c2ecf20Sopenharmony_ci{
7908c2ecf20Sopenharmony_ci	struct sock *sk = fou->sock->sk;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	if (nla_put_u8(msg, FOU_ATTR_AF, fou->sock->sk->sk_family) ||
7938c2ecf20Sopenharmony_ci	    nla_put_be16(msg, FOU_ATTR_PORT, fou->port) ||
7948c2ecf20Sopenharmony_ci	    nla_put_be16(msg, FOU_ATTR_PEER_PORT, sk->sk_dport) ||
7958c2ecf20Sopenharmony_ci	    nla_put_u8(msg, FOU_ATTR_IPPROTO, fou->protocol) ||
7968c2ecf20Sopenharmony_ci	    nla_put_u8(msg, FOU_ATTR_TYPE, fou->type) ||
7978c2ecf20Sopenharmony_ci	    nla_put_s32(msg, FOU_ATTR_IFINDEX, sk->sk_bound_dev_if))
7988c2ecf20Sopenharmony_ci		return -1;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	if (fou->flags & FOU_F_REMCSUM_NOPARTIAL)
8018c2ecf20Sopenharmony_ci		if (nla_put_flag(msg, FOU_ATTR_REMCSUM_NOPARTIAL))
8028c2ecf20Sopenharmony_ci			return -1;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	if (fou->sock->sk->sk_family == AF_INET) {
8058c2ecf20Sopenharmony_ci		if (nla_put_in_addr(msg, FOU_ATTR_LOCAL_V4, sk->sk_rcv_saddr))
8068c2ecf20Sopenharmony_ci			return -1;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci		if (nla_put_in_addr(msg, FOU_ATTR_PEER_V4, sk->sk_daddr))
8098c2ecf20Sopenharmony_ci			return -1;
8108c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
8118c2ecf20Sopenharmony_ci	} else {
8128c2ecf20Sopenharmony_ci		if (nla_put_in6_addr(msg, FOU_ATTR_LOCAL_V6,
8138c2ecf20Sopenharmony_ci				     &sk->sk_v6_rcv_saddr))
8148c2ecf20Sopenharmony_ci			return -1;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci		if (nla_put_in6_addr(msg, FOU_ATTR_PEER_V6, &sk->sk_v6_daddr))
8178c2ecf20Sopenharmony_ci			return -1;
8188c2ecf20Sopenharmony_ci#endif
8198c2ecf20Sopenharmony_ci	}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	return 0;
8228c2ecf20Sopenharmony_ci}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_cistatic int fou_dump_info(struct fou *fou, u32 portid, u32 seq,
8258c2ecf20Sopenharmony_ci			 u32 flags, struct sk_buff *skb, u8 cmd)
8268c2ecf20Sopenharmony_ci{
8278c2ecf20Sopenharmony_ci	void *hdr;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	hdr = genlmsg_put(skb, portid, seq, &fou_nl_family, flags, cmd);
8308c2ecf20Sopenharmony_ci	if (!hdr)
8318c2ecf20Sopenharmony_ci		return -ENOMEM;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	if (fou_fill_info(fou, skb) < 0)
8348c2ecf20Sopenharmony_ci		goto nla_put_failure;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	genlmsg_end(skb, hdr);
8378c2ecf20Sopenharmony_ci	return 0;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_cinla_put_failure:
8408c2ecf20Sopenharmony_ci	genlmsg_cancel(skb, hdr);
8418c2ecf20Sopenharmony_ci	return -EMSGSIZE;
8428c2ecf20Sopenharmony_ci}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_cistatic int fou_nl_cmd_get_port(struct sk_buff *skb, struct genl_info *info)
8458c2ecf20Sopenharmony_ci{
8468c2ecf20Sopenharmony_ci	struct net *net = genl_info_net(info);
8478c2ecf20Sopenharmony_ci	struct fou_net *fn = net_generic(net, fou_net_id);
8488c2ecf20Sopenharmony_ci	struct sk_buff *msg;
8498c2ecf20Sopenharmony_ci	struct fou_cfg cfg;
8508c2ecf20Sopenharmony_ci	struct fou *fout;
8518c2ecf20Sopenharmony_ci	__be16 port;
8528c2ecf20Sopenharmony_ci	u8 family;
8538c2ecf20Sopenharmony_ci	int ret;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	ret = parse_nl_config(info, &cfg);
8568c2ecf20Sopenharmony_ci	if (ret)
8578c2ecf20Sopenharmony_ci		return ret;
8588c2ecf20Sopenharmony_ci	port = cfg.udp_config.local_udp_port;
8598c2ecf20Sopenharmony_ci	if (port == 0)
8608c2ecf20Sopenharmony_ci		return -EINVAL;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	family = cfg.udp_config.family;
8638c2ecf20Sopenharmony_ci	if (family != AF_INET && family != AF_INET6)
8648c2ecf20Sopenharmony_ci		return -EINVAL;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
8678c2ecf20Sopenharmony_ci	if (!msg)
8688c2ecf20Sopenharmony_ci		return -ENOMEM;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	ret = -ESRCH;
8718c2ecf20Sopenharmony_ci	mutex_lock(&fn->fou_lock);
8728c2ecf20Sopenharmony_ci	list_for_each_entry(fout, &fn->fou_list, list) {
8738c2ecf20Sopenharmony_ci		if (fou_cfg_cmp(fout, &cfg)) {
8748c2ecf20Sopenharmony_ci			ret = fou_dump_info(fout, info->snd_portid,
8758c2ecf20Sopenharmony_ci					    info->snd_seq, 0, msg,
8768c2ecf20Sopenharmony_ci					    info->genlhdr->cmd);
8778c2ecf20Sopenharmony_ci			break;
8788c2ecf20Sopenharmony_ci		}
8798c2ecf20Sopenharmony_ci	}
8808c2ecf20Sopenharmony_ci	mutex_unlock(&fn->fou_lock);
8818c2ecf20Sopenharmony_ci	if (ret < 0)
8828c2ecf20Sopenharmony_ci		goto out_free;
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	return genlmsg_reply(msg, info);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ciout_free:
8878c2ecf20Sopenharmony_ci	nlmsg_free(msg);
8888c2ecf20Sopenharmony_ci	return ret;
8898c2ecf20Sopenharmony_ci}
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_cistatic int fou_nl_dump(struct sk_buff *skb, struct netlink_callback *cb)
8928c2ecf20Sopenharmony_ci{
8938c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
8948c2ecf20Sopenharmony_ci	struct fou_net *fn = net_generic(net, fou_net_id);
8958c2ecf20Sopenharmony_ci	struct fou *fout;
8968c2ecf20Sopenharmony_ci	int idx = 0, ret;
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	mutex_lock(&fn->fou_lock);
8998c2ecf20Sopenharmony_ci	list_for_each_entry(fout, &fn->fou_list, list) {
9008c2ecf20Sopenharmony_ci		if (idx++ < cb->args[0])
9018c2ecf20Sopenharmony_ci			continue;
9028c2ecf20Sopenharmony_ci		ret = fou_dump_info(fout, NETLINK_CB(cb->skb).portid,
9038c2ecf20Sopenharmony_ci				    cb->nlh->nlmsg_seq, NLM_F_MULTI,
9048c2ecf20Sopenharmony_ci				    skb, FOU_CMD_GET);
9058c2ecf20Sopenharmony_ci		if (ret)
9068c2ecf20Sopenharmony_ci			break;
9078c2ecf20Sopenharmony_ci	}
9088c2ecf20Sopenharmony_ci	mutex_unlock(&fn->fou_lock);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	cb->args[0] = idx;
9118c2ecf20Sopenharmony_ci	return skb->len;
9128c2ecf20Sopenharmony_ci}
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_cistatic const struct genl_small_ops fou_nl_ops[] = {
9158c2ecf20Sopenharmony_ci	{
9168c2ecf20Sopenharmony_ci		.cmd = FOU_CMD_ADD,
9178c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
9188c2ecf20Sopenharmony_ci		.doit = fou_nl_cmd_add_port,
9198c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
9208c2ecf20Sopenharmony_ci	},
9218c2ecf20Sopenharmony_ci	{
9228c2ecf20Sopenharmony_ci		.cmd = FOU_CMD_DEL,
9238c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
9248c2ecf20Sopenharmony_ci		.doit = fou_nl_cmd_rm_port,
9258c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
9268c2ecf20Sopenharmony_ci	},
9278c2ecf20Sopenharmony_ci	{
9288c2ecf20Sopenharmony_ci		.cmd = FOU_CMD_GET,
9298c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
9308c2ecf20Sopenharmony_ci		.doit = fou_nl_cmd_get_port,
9318c2ecf20Sopenharmony_ci		.dumpit = fou_nl_dump,
9328c2ecf20Sopenharmony_ci	},
9338c2ecf20Sopenharmony_ci};
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_cistatic struct genl_family fou_nl_family __ro_after_init = {
9368c2ecf20Sopenharmony_ci	.hdrsize	= 0,
9378c2ecf20Sopenharmony_ci	.name		= FOU_GENL_NAME,
9388c2ecf20Sopenharmony_ci	.version	= FOU_GENL_VERSION,
9398c2ecf20Sopenharmony_ci	.maxattr	= FOU_ATTR_MAX,
9408c2ecf20Sopenharmony_ci	.policy = fou_nl_policy,
9418c2ecf20Sopenharmony_ci	.netnsok	= true,
9428c2ecf20Sopenharmony_ci	.module		= THIS_MODULE,
9438c2ecf20Sopenharmony_ci	.small_ops	= fou_nl_ops,
9448c2ecf20Sopenharmony_ci	.n_small_ops	= ARRAY_SIZE(fou_nl_ops),
9458c2ecf20Sopenharmony_ci};
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_cisize_t fou_encap_hlen(struct ip_tunnel_encap *e)
9488c2ecf20Sopenharmony_ci{
9498c2ecf20Sopenharmony_ci	return sizeof(struct udphdr);
9508c2ecf20Sopenharmony_ci}
9518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fou_encap_hlen);
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_cisize_t gue_encap_hlen(struct ip_tunnel_encap *e)
9548c2ecf20Sopenharmony_ci{
9558c2ecf20Sopenharmony_ci	size_t len;
9568c2ecf20Sopenharmony_ci	bool need_priv = false;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	len = sizeof(struct udphdr) + sizeof(struct guehdr);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	if (e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) {
9618c2ecf20Sopenharmony_ci		len += GUE_PLEN_REMCSUM;
9628c2ecf20Sopenharmony_ci		need_priv = true;
9638c2ecf20Sopenharmony_ci	}
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	len += need_priv ? GUE_LEN_PRIV : 0;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	return len;
9688c2ecf20Sopenharmony_ci}
9698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(gue_encap_hlen);
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ciint __fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
9728c2ecf20Sopenharmony_ci		       u8 *protocol, __be16 *sport, int type)
9738c2ecf20Sopenharmony_ci{
9748c2ecf20Sopenharmony_ci	int err;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	err = iptunnel_handle_offloads(skb, type);
9778c2ecf20Sopenharmony_ci	if (err)
9788c2ecf20Sopenharmony_ci		return err;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	*sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
9818c2ecf20Sopenharmony_ci						skb, 0, 0, false);
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	return 0;
9848c2ecf20Sopenharmony_ci}
9858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__fou_build_header);
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ciint __gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
9888c2ecf20Sopenharmony_ci		       u8 *protocol, __be16 *sport, int type)
9898c2ecf20Sopenharmony_ci{
9908c2ecf20Sopenharmony_ci	struct guehdr *guehdr;
9918c2ecf20Sopenharmony_ci	size_t hdrlen, optlen = 0;
9928c2ecf20Sopenharmony_ci	void *data;
9938c2ecf20Sopenharmony_ci	bool need_priv = false;
9948c2ecf20Sopenharmony_ci	int err;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	if ((e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) &&
9978c2ecf20Sopenharmony_ci	    skb->ip_summed == CHECKSUM_PARTIAL) {
9988c2ecf20Sopenharmony_ci		optlen += GUE_PLEN_REMCSUM;
9998c2ecf20Sopenharmony_ci		type |= SKB_GSO_TUNNEL_REMCSUM;
10008c2ecf20Sopenharmony_ci		need_priv = true;
10018c2ecf20Sopenharmony_ci	}
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	optlen += need_priv ? GUE_LEN_PRIV : 0;
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	err = iptunnel_handle_offloads(skb, type);
10068c2ecf20Sopenharmony_ci	if (err)
10078c2ecf20Sopenharmony_ci		return err;
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	/* Get source port (based on flow hash) before skb_push */
10108c2ecf20Sopenharmony_ci	*sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
10118c2ecf20Sopenharmony_ci						skb, 0, 0, false);
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	hdrlen = sizeof(struct guehdr) + optlen;
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	skb_push(skb, hdrlen);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	guehdr = (struct guehdr *)skb->data;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	guehdr->control = 0;
10208c2ecf20Sopenharmony_ci	guehdr->version = 0;
10218c2ecf20Sopenharmony_ci	guehdr->hlen = optlen >> 2;
10228c2ecf20Sopenharmony_ci	guehdr->flags = 0;
10238c2ecf20Sopenharmony_ci	guehdr->proto_ctype = *protocol;
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	data = &guehdr[1];
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	if (need_priv) {
10288c2ecf20Sopenharmony_ci		__be32 *flags = data;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci		guehdr->flags |= GUE_FLAG_PRIV;
10318c2ecf20Sopenharmony_ci		*flags = 0;
10328c2ecf20Sopenharmony_ci		data += GUE_LEN_PRIV;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci		if (type & SKB_GSO_TUNNEL_REMCSUM) {
10358c2ecf20Sopenharmony_ci			u16 csum_start = skb_checksum_start_offset(skb);
10368c2ecf20Sopenharmony_ci			__be16 *pd = data;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci			if (csum_start < hdrlen)
10398c2ecf20Sopenharmony_ci				return -EINVAL;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci			csum_start -= hdrlen;
10428c2ecf20Sopenharmony_ci			pd[0] = htons(csum_start);
10438c2ecf20Sopenharmony_ci			pd[1] = htons(csum_start + skb->csum_offset);
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci			if (!skb_is_gso(skb)) {
10468c2ecf20Sopenharmony_ci				skb->ip_summed = CHECKSUM_NONE;
10478c2ecf20Sopenharmony_ci				skb->encapsulation = 0;
10488c2ecf20Sopenharmony_ci			}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci			*flags |= GUE_PFLAG_REMCSUM;
10518c2ecf20Sopenharmony_ci			data += GUE_PLEN_REMCSUM;
10528c2ecf20Sopenharmony_ci		}
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	}
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	return 0;
10578c2ecf20Sopenharmony_ci}
10588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__gue_build_header);
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_FOU_IP_TUNNELS
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_cistatic void fou_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e,
10638c2ecf20Sopenharmony_ci			  struct flowi4 *fl4, u8 *protocol, __be16 sport)
10648c2ecf20Sopenharmony_ci{
10658c2ecf20Sopenharmony_ci	struct udphdr *uh;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	skb_push(skb, sizeof(struct udphdr));
10688c2ecf20Sopenharmony_ci	skb_reset_transport_header(skb);
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	uh = udp_hdr(skb);
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	uh->dest = e->dport;
10738c2ecf20Sopenharmony_ci	uh->source = sport;
10748c2ecf20Sopenharmony_ci	uh->len = htons(skb->len);
10758c2ecf20Sopenharmony_ci	udp_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM), skb,
10768c2ecf20Sopenharmony_ci		     fl4->saddr, fl4->daddr, skb->len);
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	*protocol = IPPROTO_UDP;
10798c2ecf20Sopenharmony_ci}
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_cistatic int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
10828c2ecf20Sopenharmony_ci			    u8 *protocol, struct flowi4 *fl4)
10838c2ecf20Sopenharmony_ci{
10848c2ecf20Sopenharmony_ci	int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM ? SKB_GSO_UDP_TUNNEL_CSUM :
10858c2ecf20Sopenharmony_ci						       SKB_GSO_UDP_TUNNEL;
10868c2ecf20Sopenharmony_ci	__be16 sport;
10878c2ecf20Sopenharmony_ci	int err;
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	err = __fou_build_header(skb, e, protocol, &sport, type);
10908c2ecf20Sopenharmony_ci	if (err)
10918c2ecf20Sopenharmony_ci		return err;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	fou_build_udp(skb, e, fl4, protocol, sport);
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	return 0;
10968c2ecf20Sopenharmony_ci}
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_cistatic int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
10998c2ecf20Sopenharmony_ci			    u8 *protocol, struct flowi4 *fl4)
11008c2ecf20Sopenharmony_ci{
11018c2ecf20Sopenharmony_ci	int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM ? SKB_GSO_UDP_TUNNEL_CSUM :
11028c2ecf20Sopenharmony_ci						       SKB_GSO_UDP_TUNNEL;
11038c2ecf20Sopenharmony_ci	__be16 sport;
11048c2ecf20Sopenharmony_ci	int err;
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	err = __gue_build_header(skb, e, protocol, &sport, type);
11078c2ecf20Sopenharmony_ci	if (err)
11088c2ecf20Sopenharmony_ci		return err;
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	fou_build_udp(skb, e, fl4, protocol, sport);
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	return 0;
11138c2ecf20Sopenharmony_ci}
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_cistatic int gue_err_proto_handler(int proto, struct sk_buff *skb, u32 info)
11168c2ecf20Sopenharmony_ci{
11178c2ecf20Sopenharmony_ci	const struct net_protocol *ipprot = rcu_dereference(inet_protos[proto]);
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	if (ipprot && ipprot->err_handler) {
11208c2ecf20Sopenharmony_ci		if (!ipprot->err_handler(skb, info))
11218c2ecf20Sopenharmony_ci			return 0;
11228c2ecf20Sopenharmony_ci	}
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	return -ENOENT;
11258c2ecf20Sopenharmony_ci}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_cistatic int gue_err(struct sk_buff *skb, u32 info)
11288c2ecf20Sopenharmony_ci{
11298c2ecf20Sopenharmony_ci	int transport_offset = skb_transport_offset(skb);
11308c2ecf20Sopenharmony_ci	struct guehdr *guehdr;
11318c2ecf20Sopenharmony_ci	size_t len, optlen;
11328c2ecf20Sopenharmony_ci	int ret;
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	len = sizeof(struct udphdr) + sizeof(struct guehdr);
11358c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, transport_offset + len))
11368c2ecf20Sopenharmony_ci		return -EINVAL;
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	guehdr = (struct guehdr *)&udp_hdr(skb)[1];
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	switch (guehdr->version) {
11418c2ecf20Sopenharmony_ci	case 0: /* Full GUE header present */
11428c2ecf20Sopenharmony_ci		break;
11438c2ecf20Sopenharmony_ci	case 1: {
11448c2ecf20Sopenharmony_ci		/* Direct encapsulation of IPv4 or IPv6 */
11458c2ecf20Sopenharmony_ci		skb_set_transport_header(skb, -(int)sizeof(struct icmphdr));
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci		switch (((struct iphdr *)guehdr)->version) {
11488c2ecf20Sopenharmony_ci		case 4:
11498c2ecf20Sopenharmony_ci			ret = gue_err_proto_handler(IPPROTO_IPIP, skb, info);
11508c2ecf20Sopenharmony_ci			goto out;
11518c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
11528c2ecf20Sopenharmony_ci		case 6:
11538c2ecf20Sopenharmony_ci			ret = gue_err_proto_handler(IPPROTO_IPV6, skb, info);
11548c2ecf20Sopenharmony_ci			goto out;
11558c2ecf20Sopenharmony_ci#endif
11568c2ecf20Sopenharmony_ci		default:
11578c2ecf20Sopenharmony_ci			ret = -EOPNOTSUPP;
11588c2ecf20Sopenharmony_ci			goto out;
11598c2ecf20Sopenharmony_ci		}
11608c2ecf20Sopenharmony_ci	}
11618c2ecf20Sopenharmony_ci	default: /* Undefined version */
11628c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
11638c2ecf20Sopenharmony_ci	}
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	if (guehdr->control)
11668c2ecf20Sopenharmony_ci		return -ENOENT;
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	optlen = guehdr->hlen << 2;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, transport_offset + len + optlen))
11718c2ecf20Sopenharmony_ci		return -EINVAL;
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	guehdr = (struct guehdr *)&udp_hdr(skb)[1];
11748c2ecf20Sopenharmony_ci	if (validate_gue_flags(guehdr, optlen))
11758c2ecf20Sopenharmony_ci		return -EINVAL;
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	/* Handling exceptions for direct UDP encapsulation in GUE would lead to
11788c2ecf20Sopenharmony_ci	 * recursion. Besides, this kind of encapsulation can't even be
11798c2ecf20Sopenharmony_ci	 * configured currently. Discard this.
11808c2ecf20Sopenharmony_ci	 */
11818c2ecf20Sopenharmony_ci	if (guehdr->proto_ctype == IPPROTO_UDP ||
11828c2ecf20Sopenharmony_ci	    guehdr->proto_ctype == IPPROTO_UDPLITE)
11838c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	skb_set_transport_header(skb, -(int)sizeof(struct icmphdr));
11868c2ecf20Sopenharmony_ci	ret = gue_err_proto_handler(guehdr->proto_ctype, skb, info);
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ciout:
11898c2ecf20Sopenharmony_ci	skb_set_transport_header(skb, transport_offset);
11908c2ecf20Sopenharmony_ci	return ret;
11918c2ecf20Sopenharmony_ci}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_cistatic const struct ip_tunnel_encap_ops fou_iptun_ops = {
11958c2ecf20Sopenharmony_ci	.encap_hlen = fou_encap_hlen,
11968c2ecf20Sopenharmony_ci	.build_header = fou_build_header,
11978c2ecf20Sopenharmony_ci	.err_handler = gue_err,
11988c2ecf20Sopenharmony_ci};
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_cistatic const struct ip_tunnel_encap_ops gue_iptun_ops = {
12018c2ecf20Sopenharmony_ci	.encap_hlen = gue_encap_hlen,
12028c2ecf20Sopenharmony_ci	.build_header = gue_build_header,
12038c2ecf20Sopenharmony_ci	.err_handler = gue_err,
12048c2ecf20Sopenharmony_ci};
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_cistatic int ip_tunnel_encap_add_fou_ops(void)
12078c2ecf20Sopenharmony_ci{
12088c2ecf20Sopenharmony_ci	int ret;
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	ret = ip_tunnel_encap_add_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
12118c2ecf20Sopenharmony_ci	if (ret < 0) {
12128c2ecf20Sopenharmony_ci		pr_err("can't add fou ops\n");
12138c2ecf20Sopenharmony_ci		return ret;
12148c2ecf20Sopenharmony_ci	}
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	ret = ip_tunnel_encap_add_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE);
12178c2ecf20Sopenharmony_ci	if (ret < 0) {
12188c2ecf20Sopenharmony_ci		pr_err("can't add gue ops\n");
12198c2ecf20Sopenharmony_ci		ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
12208c2ecf20Sopenharmony_ci		return ret;
12218c2ecf20Sopenharmony_ci	}
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	return 0;
12248c2ecf20Sopenharmony_ci}
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_cistatic void ip_tunnel_encap_del_fou_ops(void)
12278c2ecf20Sopenharmony_ci{
12288c2ecf20Sopenharmony_ci	ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
12298c2ecf20Sopenharmony_ci	ip_tunnel_encap_del_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE);
12308c2ecf20Sopenharmony_ci}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci#else
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_cistatic int ip_tunnel_encap_add_fou_ops(void)
12358c2ecf20Sopenharmony_ci{
12368c2ecf20Sopenharmony_ci	return 0;
12378c2ecf20Sopenharmony_ci}
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_cistatic void ip_tunnel_encap_del_fou_ops(void)
12408c2ecf20Sopenharmony_ci{
12418c2ecf20Sopenharmony_ci}
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci#endif
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_cistatic __net_init int fou_init_net(struct net *net)
12468c2ecf20Sopenharmony_ci{
12478c2ecf20Sopenharmony_ci	struct fou_net *fn = net_generic(net, fou_net_id);
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&fn->fou_list);
12508c2ecf20Sopenharmony_ci	mutex_init(&fn->fou_lock);
12518c2ecf20Sopenharmony_ci	return 0;
12528c2ecf20Sopenharmony_ci}
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_cistatic __net_exit void fou_exit_net(struct net *net)
12558c2ecf20Sopenharmony_ci{
12568c2ecf20Sopenharmony_ci	struct fou_net *fn = net_generic(net, fou_net_id);
12578c2ecf20Sopenharmony_ci	struct fou *fou, *next;
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	/* Close all the FOU sockets */
12608c2ecf20Sopenharmony_ci	mutex_lock(&fn->fou_lock);
12618c2ecf20Sopenharmony_ci	list_for_each_entry_safe(fou, next, &fn->fou_list, list)
12628c2ecf20Sopenharmony_ci		fou_release(fou);
12638c2ecf20Sopenharmony_ci	mutex_unlock(&fn->fou_lock);
12648c2ecf20Sopenharmony_ci}
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_cistatic struct pernet_operations fou_net_ops = {
12678c2ecf20Sopenharmony_ci	.init = fou_init_net,
12688c2ecf20Sopenharmony_ci	.exit = fou_exit_net,
12698c2ecf20Sopenharmony_ci	.id   = &fou_net_id,
12708c2ecf20Sopenharmony_ci	.size = sizeof(struct fou_net),
12718c2ecf20Sopenharmony_ci};
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_cistatic int __init fou_init(void)
12748c2ecf20Sopenharmony_ci{
12758c2ecf20Sopenharmony_ci	int ret;
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	ret = register_pernet_device(&fou_net_ops);
12788c2ecf20Sopenharmony_ci	if (ret)
12798c2ecf20Sopenharmony_ci		goto exit;
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci	ret = genl_register_family(&fou_nl_family);
12828c2ecf20Sopenharmony_ci	if (ret < 0)
12838c2ecf20Sopenharmony_ci		goto unregister;
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	ret = ip_tunnel_encap_add_fou_ops();
12868c2ecf20Sopenharmony_ci	if (ret == 0)
12878c2ecf20Sopenharmony_ci		return 0;
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	genl_unregister_family(&fou_nl_family);
12908c2ecf20Sopenharmony_ciunregister:
12918c2ecf20Sopenharmony_ci	unregister_pernet_device(&fou_net_ops);
12928c2ecf20Sopenharmony_ciexit:
12938c2ecf20Sopenharmony_ci	return ret;
12948c2ecf20Sopenharmony_ci}
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_cistatic void __exit fou_fini(void)
12978c2ecf20Sopenharmony_ci{
12988c2ecf20Sopenharmony_ci	ip_tunnel_encap_del_fou_ops();
12998c2ecf20Sopenharmony_ci	genl_unregister_family(&fou_nl_family);
13008c2ecf20Sopenharmony_ci	unregister_pernet_device(&fou_net_ops);
13018c2ecf20Sopenharmony_ci}
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_cimodule_init(fou_init);
13048c2ecf20Sopenharmony_cimodule_exit(fou_fini);
13058c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tom Herbert <therbert@google.com>");
13068c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
13078c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Foo over UDP");
1308