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 20 static 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 39 static 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 56 static 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 73 static int gue6_err_proto_handler(int proto, struct sk_buff *skb, 74 struct inet6_skb_parm *opt, 75 u8 type, u8 code, int offset, u32 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 88 static 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, 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, 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 151 out: 152 skb_set_transport_header(skb, transport_offset); 153 return ret; 154 } 155 156 157 static 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 163 static 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 169 static 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 189 static 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 197 static int ip6_tnl_encap_add_fou_ops(void) 198 { 199 return 0; 200 } 201 202 static void ip6_tnl_encap_del_fou_ops(void) 203 { 204 } 205 206 #endif 207 208 static int __init fou6_init(void) 209 { 210 int ret; 211 212 ret = ip6_tnl_encap_add_fou_ops(); 213 214 return ret; 215 } 216 217 static void __exit fou6_fini(void) 218 { 219 ip6_tnl_encap_del_fou_ops(); 220 } 221 222 module_init(fou6_init); 223 module_exit(fou6_fini); 224 MODULE_AUTHOR("Tom Herbert <therbert@google.com>"); 225 MODULE_LICENSE("GPL"); 226