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/udp.h> 88c2ecf20Sopenharmony_ci#include <linux/icmpv6.h> 98c2ecf20Sopenharmony_ci#include <linux/types.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <net/fou.h> 128c2ecf20Sopenharmony_ci#include <net/ip.h> 138c2ecf20Sopenharmony_ci#include <net/ip6_tunnel.h> 148c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h> 158c2ecf20Sopenharmony_ci#include <net/protocol.h> 168c2ecf20Sopenharmony_ci#include <net/udp.h> 178c2ecf20Sopenharmony_ci#include <net/udp_tunnel.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_FOU_TUNNEL) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic void fou6_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e, 228c2ecf20Sopenharmony_ci struct flowi6 *fl6, u8 *protocol, __be16 sport) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct udphdr *uh; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci skb_push(skb, sizeof(struct udphdr)); 278c2ecf20Sopenharmony_ci skb_reset_transport_header(skb); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci uh = udp_hdr(skb); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci uh->dest = e->dport; 328c2ecf20Sopenharmony_ci uh->source = sport; 338c2ecf20Sopenharmony_ci uh->len = htons(skb->len); 348c2ecf20Sopenharmony_ci udp6_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM6), skb, 358c2ecf20Sopenharmony_ci &fl6->saddr, &fl6->daddr, skb->len); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci *protocol = IPPROTO_UDP; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int fou6_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, 418c2ecf20Sopenharmony_ci u8 *protocol, struct flowi6 *fl6) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci __be16 sport; 448c2ecf20Sopenharmony_ci int err; 458c2ecf20Sopenharmony_ci int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM6 ? 468c2ecf20Sopenharmony_ci SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci err = __fou_build_header(skb, e, protocol, &sport, type); 498c2ecf20Sopenharmony_ci if (err) 508c2ecf20Sopenharmony_ci return err; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci fou6_build_udp(skb, e, fl6, protocol, sport); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci return 0; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic int gue6_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, 588c2ecf20Sopenharmony_ci u8 *protocol, struct flowi6 *fl6) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci __be16 sport; 618c2ecf20Sopenharmony_ci int err; 628c2ecf20Sopenharmony_ci int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM6 ? 638c2ecf20Sopenharmony_ci SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci err = __gue_build_header(skb, e, protocol, &sport, type); 668c2ecf20Sopenharmony_ci if (err) 678c2ecf20Sopenharmony_ci return err; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci fou6_build_udp(skb, e, fl6, protocol, sport); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return 0; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int gue6_err_proto_handler(int proto, struct sk_buff *skb, 758c2ecf20Sopenharmony_ci struct inet6_skb_parm *opt, 768c2ecf20Sopenharmony_ci u8 type, u8 code, int offset, __be32 info) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci const struct inet6_protocol *ipprot; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci ipprot = rcu_dereference(inet6_protos[proto]); 818c2ecf20Sopenharmony_ci if (ipprot && ipprot->err_handler) { 828c2ecf20Sopenharmony_ci if (!ipprot->err_handler(skb, opt, type, code, offset, info)) 838c2ecf20Sopenharmony_ci return 0; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return -ENOENT; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int gue6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 908c2ecf20Sopenharmony_ci u8 type, u8 code, int offset, __be32 info) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci int transport_offset = skb_transport_offset(skb); 938c2ecf20Sopenharmony_ci struct guehdr *guehdr; 948c2ecf20Sopenharmony_ci size_t len, optlen; 958c2ecf20Sopenharmony_ci int ret; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci len = sizeof(struct udphdr) + sizeof(struct guehdr); 988c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, transport_offset + len)) 998c2ecf20Sopenharmony_ci return -EINVAL; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci guehdr = (struct guehdr *)&udp_hdr(skb)[1]; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci switch (guehdr->version) { 1048c2ecf20Sopenharmony_ci case 0: /* Full GUE header present */ 1058c2ecf20Sopenharmony_ci break; 1068c2ecf20Sopenharmony_ci case 1: { 1078c2ecf20Sopenharmony_ci /* Direct encasulation of IPv4 or IPv6 */ 1088c2ecf20Sopenharmony_ci skb_set_transport_header(skb, -(int)sizeof(struct icmp6hdr)); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci switch (((struct iphdr *)guehdr)->version) { 1118c2ecf20Sopenharmony_ci case 4: 1128c2ecf20Sopenharmony_ci ret = gue6_err_proto_handler(IPPROTO_IPIP, skb, opt, 1138c2ecf20Sopenharmony_ci type, code, offset, info); 1148c2ecf20Sopenharmony_ci goto out; 1158c2ecf20Sopenharmony_ci case 6: 1168c2ecf20Sopenharmony_ci ret = gue6_err_proto_handler(IPPROTO_IPV6, skb, opt, 1178c2ecf20Sopenharmony_ci type, code, offset, info); 1188c2ecf20Sopenharmony_ci goto out; 1198c2ecf20Sopenharmony_ci default: 1208c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 1218c2ecf20Sopenharmony_ci goto out; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci default: /* Undefined version */ 1258c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (guehdr->control) 1298c2ecf20Sopenharmony_ci return -ENOENT; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci optlen = guehdr->hlen << 2; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, transport_offset + len + optlen)) 1348c2ecf20Sopenharmony_ci return -EINVAL; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci guehdr = (struct guehdr *)&udp_hdr(skb)[1]; 1378c2ecf20Sopenharmony_ci if (validate_gue_flags(guehdr, optlen)) 1388c2ecf20Sopenharmony_ci return -EINVAL; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Handling exceptions for direct UDP encapsulation in GUE would lead to 1418c2ecf20Sopenharmony_ci * recursion. Besides, this kind of encapsulation can't even be 1428c2ecf20Sopenharmony_ci * configured currently. Discard this. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci if (guehdr->proto_ctype == IPPROTO_UDP || 1458c2ecf20Sopenharmony_ci guehdr->proto_ctype == IPPROTO_UDPLITE) 1468c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci skb_set_transport_header(skb, -(int)sizeof(struct icmp6hdr)); 1498c2ecf20Sopenharmony_ci ret = gue6_err_proto_handler(guehdr->proto_ctype, skb, 1508c2ecf20Sopenharmony_ci opt, type, code, offset, info); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ciout: 1538c2ecf20Sopenharmony_ci skb_set_transport_header(skb, transport_offset); 1548c2ecf20Sopenharmony_ci return ret; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic const struct ip6_tnl_encap_ops fou_ip6tun_ops = { 1598c2ecf20Sopenharmony_ci .encap_hlen = fou_encap_hlen, 1608c2ecf20Sopenharmony_ci .build_header = fou6_build_header, 1618c2ecf20Sopenharmony_ci .err_handler = gue6_err, 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic const struct ip6_tnl_encap_ops gue_ip6tun_ops = { 1658c2ecf20Sopenharmony_ci .encap_hlen = gue_encap_hlen, 1668c2ecf20Sopenharmony_ci .build_header = gue6_build_header, 1678c2ecf20Sopenharmony_ci .err_handler = gue6_err, 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int ip6_tnl_encap_add_fou_ops(void) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci int ret; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci ret = ip6_tnl_encap_add_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU); 1758c2ecf20Sopenharmony_ci if (ret < 0) { 1768c2ecf20Sopenharmony_ci pr_err("can't add fou6 ops\n"); 1778c2ecf20Sopenharmony_ci return ret; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci ret = ip6_tnl_encap_add_ops(&gue_ip6tun_ops, TUNNEL_ENCAP_GUE); 1818c2ecf20Sopenharmony_ci if (ret < 0) { 1828c2ecf20Sopenharmony_ci pr_err("can't add gue6 ops\n"); 1838c2ecf20Sopenharmony_ci ip6_tnl_encap_del_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU); 1848c2ecf20Sopenharmony_ci return ret; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void ip6_tnl_encap_del_fou_ops(void) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci ip6_tnl_encap_del_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU); 1938c2ecf20Sopenharmony_ci ip6_tnl_encap_del_ops(&gue_ip6tun_ops, TUNNEL_ENCAP_GUE); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci#else 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int ip6_tnl_encap_add_fou_ops(void) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic void ip6_tnl_encap_del_fou_ops(void) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci#endif 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic int __init fou6_init(void) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci int ret; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci ret = ip6_tnl_encap_add_fou_ops(); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return ret; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic void __exit fou6_fini(void) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci ip6_tnl_encap_del_fou_ops(); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cimodule_init(fou6_init); 2248c2ecf20Sopenharmony_cimodule_exit(fou6_fini); 2258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tom Herbert <therbert@google.com>"); 2268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Foo over UDP (IPv6)"); 228