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