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