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