fou6.c (5188B)
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 21static 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 40static 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 57static 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 74static 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 89static 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 152out: 153 skb_set_transport_header(skb, transport_offset); 154 return ret; 155} 156 157 158static 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 164static 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 170static 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 190static 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 198static int ip6_tnl_encap_add_fou_ops(void) 199{ 200 return 0; 201} 202 203static void ip6_tnl_encap_del_fou_ops(void) 204{ 205} 206 207#endif 208 209static int __init fou6_init(void) 210{ 211 int ret; 212 213 ret = ip6_tnl_encap_add_fou_ops(); 214 215 return ret; 216} 217 218static void __exit fou6_fini(void) 219{ 220 ip6_tnl_encap_del_fou_ops(); 221} 222 223module_init(fou6_init); 224module_exit(fou6_fini); 225MODULE_AUTHOR("Tom Herbert <therbert@google.com>"); 226MODULE_LICENSE("GPL"); 227MODULE_DESCRIPTION("Foo over UDP (IPv6)");