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