109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2aa3463d6STom Herbert #include <linux/module.h> 3aa3463d6STom Herbert #include <linux/errno.h> 4aa3463d6STom Herbert #include <linux/socket.h> 5aa3463d6STom Herbert #include <linux/skbuff.h> 6aa3463d6STom Herbert #include <linux/ip.h> 7aa3463d6STom Herbert #include <linux/udp.h> 8b8a51b38SStefano Brivio #include <linux/icmpv6.h> 9aa3463d6STom Herbert #include <linux/types.h> 10aa3463d6STom Herbert #include <linux/kernel.h> 11aa3463d6STom Herbert #include <net/fou.h> 12aa3463d6STom Herbert #include <net/ip.h> 13aa3463d6STom Herbert #include <net/ip6_tunnel.h> 14aa3463d6STom Herbert #include <net/ip6_checksum.h> 15aa3463d6STom Herbert #include <net/protocol.h> 16aa3463d6STom Herbert #include <net/udp.h> 17aa3463d6STom Herbert #include <net/udp_tunnel.h> 18aa3463d6STom Herbert 199dc621afSstephen hemminger #if IS_ENABLED(CONFIG_IPV6_FOU_TUNNEL) 209dc621afSstephen hemminger 21aa3463d6STom Herbert static void fou6_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e, 22aa3463d6STom Herbert struct flowi6 *fl6, u8 *protocol, __be16 sport) 23aa3463d6STom Herbert { 24aa3463d6STom Herbert struct udphdr *uh; 25aa3463d6STom Herbert 26aa3463d6STom Herbert skb_push(skb, sizeof(struct udphdr)); 27aa3463d6STom Herbert skb_reset_transport_header(skb); 28aa3463d6STom Herbert 29aa3463d6STom Herbert uh = udp_hdr(skb); 30aa3463d6STom Herbert 31aa3463d6STom Herbert uh->dest = e->dport; 32aa3463d6STom Herbert uh->source = sport; 33aa3463d6STom Herbert uh->len = htons(skb->len); 34aa3463d6STom Herbert udp6_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM6), skb, 35aa3463d6STom Herbert &fl6->saddr, &fl6->daddr, skb->len); 36aa3463d6STom Herbert 37aa3463d6STom Herbert *protocol = IPPROTO_UDP; 38aa3463d6STom Herbert } 39aa3463d6STom Herbert 409dc621afSstephen hemminger static int fou6_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, 41aa3463d6STom Herbert u8 *protocol, struct flowi6 *fl6) 42aa3463d6STom Herbert { 43aa3463d6STom Herbert __be16 sport; 44aa3463d6STom Herbert int err; 45aa3463d6STom Herbert int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM6 ? 46aa3463d6STom Herbert SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; 47aa3463d6STom Herbert 48aa3463d6STom Herbert err = __fou_build_header(skb, e, protocol, &sport, type); 49aa3463d6STom Herbert if (err) 50aa3463d6STom Herbert return err; 51aa3463d6STom Herbert 52aa3463d6STom Herbert fou6_build_udp(skb, e, fl6, protocol, sport); 53aa3463d6STom Herbert 54aa3463d6STom Herbert return 0; 55aa3463d6STom Herbert } 56aa3463d6STom Herbert 579dc621afSstephen hemminger static int gue6_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, 58aa3463d6STom Herbert u8 *protocol, struct flowi6 *fl6) 59aa3463d6STom Herbert { 60aa3463d6STom Herbert __be16 sport; 61aa3463d6STom Herbert int err; 62aa3463d6STom Herbert int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM6 ? 63aa3463d6STom Herbert SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; 64aa3463d6STom Herbert 65aa3463d6STom Herbert err = __gue_build_header(skb, e, protocol, &sport, type); 66aa3463d6STom Herbert if (err) 67aa3463d6STom Herbert return err; 68aa3463d6STom Herbert 69aa3463d6STom Herbert fou6_build_udp(skb, e, fl6, protocol, sport); 70aa3463d6STom Herbert 71aa3463d6STom Herbert return 0; 72aa3463d6STom Herbert } 73aa3463d6STom Herbert 74b8a51b38SStefano Brivio static int gue6_err_proto_handler(int proto, struct sk_buff *skb, 75b8a51b38SStefano Brivio struct inet6_skb_parm *opt, 765de362dfSPaolo Abeni u8 type, u8 code, int offset, __be32 info) 77b8a51b38SStefano Brivio { 78b8a51b38SStefano Brivio const struct inet6_protocol *ipprot; 79b8a51b38SStefano Brivio 80b8a51b38SStefano Brivio ipprot = rcu_dereference(inet6_protos[proto]); 81b8a51b38SStefano Brivio if (ipprot && ipprot->err_handler) { 82b8a51b38SStefano Brivio if (!ipprot->err_handler(skb, opt, type, code, offset, info)) 83b8a51b38SStefano Brivio return 0; 84b8a51b38SStefano Brivio } 85b8a51b38SStefano Brivio 86b8a51b38SStefano Brivio return -ENOENT; 87b8a51b38SStefano Brivio } 88b8a51b38SStefano Brivio 89b8a51b38SStefano Brivio static int gue6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 90b8a51b38SStefano Brivio u8 type, u8 code, int offset, __be32 info) 91b8a51b38SStefano Brivio { 92b8a51b38SStefano Brivio int transport_offset = skb_transport_offset(skb); 93b8a51b38SStefano Brivio struct guehdr *guehdr; 9426fc181eSEric Dumazet size_t len, optlen; 95b8a51b38SStefano Brivio int ret; 96b8a51b38SStefano Brivio 9726fc181eSEric Dumazet len = sizeof(struct udphdr) + sizeof(struct guehdr); 985355ed63SEric Dumazet if (!pskb_may_pull(skb, transport_offset + len)) 99b8a51b38SStefano Brivio return -EINVAL; 100b8a51b38SStefano Brivio 101b8a51b38SStefano Brivio guehdr = (struct guehdr *)&udp_hdr(skb)[1]; 102b8a51b38SStefano Brivio 103b8a51b38SStefano Brivio switch (guehdr->version) { 104b8a51b38SStefano Brivio case 0: /* Full GUE header present */ 105b8a51b38SStefano Brivio break; 106b8a51b38SStefano Brivio case 1: { 107b8a51b38SStefano Brivio /* Direct encasulation of IPv4 or IPv6 */ 108b8a51b38SStefano Brivio skb_set_transport_header(skb, -(int)sizeof(struct icmp6hdr)); 109b8a51b38SStefano Brivio 110b8a51b38SStefano Brivio switch (((struct iphdr *)guehdr)->version) { 111b8a51b38SStefano Brivio case 4: 112b8a51b38SStefano Brivio ret = gue6_err_proto_handler(IPPROTO_IPIP, skb, opt, 113b8a51b38SStefano Brivio type, code, offset, info); 114b8a51b38SStefano Brivio goto out; 115b8a51b38SStefano Brivio case 6: 116b8a51b38SStefano Brivio ret = gue6_err_proto_handler(IPPROTO_IPV6, skb, opt, 117b8a51b38SStefano Brivio type, code, offset, info); 118b8a51b38SStefano Brivio goto out; 119b8a51b38SStefano Brivio default: 120b8a51b38SStefano Brivio ret = -EOPNOTSUPP; 121b8a51b38SStefano Brivio goto out; 122b8a51b38SStefano Brivio } 123b8a51b38SStefano Brivio } 124b8a51b38SStefano Brivio default: /* Undefined version */ 125b8a51b38SStefano Brivio return -EOPNOTSUPP; 126b8a51b38SStefano Brivio } 127b8a51b38SStefano Brivio 128b8a51b38SStefano Brivio if (guehdr->control) 129b8a51b38SStefano Brivio return -ENOENT; 130b8a51b38SStefano Brivio 131b8a51b38SStefano Brivio optlen = guehdr->hlen << 2; 132b8a51b38SStefano Brivio 1335355ed63SEric Dumazet if (!pskb_may_pull(skb, transport_offset + len + optlen)) 13426fc181eSEric Dumazet return -EINVAL; 13526fc181eSEric Dumazet 13626fc181eSEric Dumazet guehdr = (struct guehdr *)&udp_hdr(skb)[1]; 137b8a51b38SStefano Brivio if (validate_gue_flags(guehdr, optlen)) 138b8a51b38SStefano Brivio return -EINVAL; 139b8a51b38SStefano Brivio 14044039e00SStefano Brivio /* Handling exceptions for direct UDP encapsulation in GUE would lead to 14144039e00SStefano Brivio * recursion. Besides, this kind of encapsulation can't even be 14244039e00SStefano Brivio * configured currently. Discard this. 14344039e00SStefano Brivio */ 14444039e00SStefano Brivio if (guehdr->proto_ctype == IPPROTO_UDP || 14544039e00SStefano Brivio guehdr->proto_ctype == IPPROTO_UDPLITE) 14644039e00SStefano Brivio return -EOPNOTSUPP; 14744039e00SStefano Brivio 148b8a51b38SStefano Brivio skb_set_transport_header(skb, -(int)sizeof(struct icmp6hdr)); 149b8a51b38SStefano Brivio ret = gue6_err_proto_handler(guehdr->proto_ctype, skb, 150b8a51b38SStefano Brivio opt, type, code, offset, info); 151b8a51b38SStefano Brivio 152b8a51b38SStefano Brivio out: 153b8a51b38SStefano Brivio skb_set_transport_header(skb, transport_offset); 154b8a51b38SStefano Brivio return ret; 155b8a51b38SStefano Brivio } 156b8a51b38SStefano Brivio 157b8a51b38SStefano Brivio 158aa3463d6STom Herbert static const struct ip6_tnl_encap_ops fou_ip6tun_ops = { 159aa3463d6STom Herbert .encap_hlen = fou_encap_hlen, 160aa3463d6STom Herbert .build_header = fou6_build_header, 161b8a51b38SStefano Brivio .err_handler = gue6_err, 162aa3463d6STom Herbert }; 163aa3463d6STom Herbert 164aa3463d6STom Herbert static const struct ip6_tnl_encap_ops gue_ip6tun_ops = { 165aa3463d6STom Herbert .encap_hlen = gue_encap_hlen, 166aa3463d6STom Herbert .build_header = gue6_build_header, 167b8a51b38SStefano Brivio .err_handler = gue6_err, 168aa3463d6STom Herbert }; 169aa3463d6STom Herbert 170aa3463d6STom Herbert static int ip6_tnl_encap_add_fou_ops(void) 171aa3463d6STom Herbert { 172aa3463d6STom Herbert int ret; 173aa3463d6STom Herbert 174aa3463d6STom Herbert ret = ip6_tnl_encap_add_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU); 175aa3463d6STom Herbert if (ret < 0) { 176aa3463d6STom Herbert pr_err("can't add fou6 ops\n"); 177aa3463d6STom Herbert return ret; 178aa3463d6STom Herbert } 179aa3463d6STom Herbert 180aa3463d6STom Herbert ret = ip6_tnl_encap_add_ops(&gue_ip6tun_ops, TUNNEL_ENCAP_GUE); 181aa3463d6STom Herbert if (ret < 0) { 182aa3463d6STom Herbert pr_err("can't add gue6 ops\n"); 183aa3463d6STom Herbert ip6_tnl_encap_del_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU); 184aa3463d6STom Herbert return ret; 185aa3463d6STom Herbert } 186aa3463d6STom Herbert 187aa3463d6STom Herbert return 0; 188aa3463d6STom Herbert } 189aa3463d6STom Herbert 190aa3463d6STom Herbert static void ip6_tnl_encap_del_fou_ops(void) 191aa3463d6STom Herbert { 192aa3463d6STom Herbert ip6_tnl_encap_del_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU); 193aa3463d6STom Herbert ip6_tnl_encap_del_ops(&gue_ip6tun_ops, TUNNEL_ENCAP_GUE); 194aa3463d6STom Herbert } 195aa3463d6STom Herbert 196aa3463d6STom Herbert #else 197aa3463d6STom Herbert 198aa3463d6STom Herbert static int ip6_tnl_encap_add_fou_ops(void) 199aa3463d6STom Herbert { 200aa3463d6STom Herbert return 0; 201aa3463d6STom Herbert } 202aa3463d6STom Herbert 203aa3463d6STom Herbert static void ip6_tnl_encap_del_fou_ops(void) 204aa3463d6STom Herbert { 205aa3463d6STom Herbert } 206aa3463d6STom Herbert 207aa3463d6STom Herbert #endif 208aa3463d6STom Herbert 209aa3463d6STom Herbert static int __init fou6_init(void) 210aa3463d6STom Herbert { 211aa3463d6STom Herbert int ret; 212aa3463d6STom Herbert 213aa3463d6STom Herbert ret = ip6_tnl_encap_add_fou_ops(); 214aa3463d6STom Herbert 215aa3463d6STom Herbert return ret; 216aa3463d6STom Herbert } 217aa3463d6STom Herbert 218aa3463d6STom Herbert static void __exit fou6_fini(void) 219aa3463d6STom Herbert { 220aa3463d6STom Herbert ip6_tnl_encap_del_fou_ops(); 221aa3463d6STom Herbert } 222aa3463d6STom Herbert 223aa3463d6STom Herbert module_init(fou6_init); 224aa3463d6STom Herbert module_exit(fou6_fini); 225aa3463d6STom Herbert MODULE_AUTHOR("Tom Herbert <therbert@google.com>"); 226aa3463d6STom Herbert MODULE_LICENSE("GPL"); 227