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 optlen; 94 int ret; 95 96 if (skb->len < sizeof(struct udphdr) + sizeof(struct guehdr)) 97 return -EINVAL; 98 99 guehdr = (struct guehdr *)&udp_hdr(skb)[1]; 100 101 switch (guehdr->version) { 102 case 0: /* Full GUE header present */ 103 break; 104 case 1: { 105 /* Direct encasulation of IPv4 or IPv6 */ 106 skb_set_transport_header(skb, -(int)sizeof(struct icmp6hdr)); 107 108 switch (((struct iphdr *)guehdr)->version) { 109 case 4: 110 ret = gue6_err_proto_handler(IPPROTO_IPIP, skb, opt, 111 type, code, offset, info); 112 goto out; 113 case 6: 114 ret = gue6_err_proto_handler(IPPROTO_IPV6, skb, opt, 115 type, code, offset, info); 116 goto out; 117 default: 118 ret = -EOPNOTSUPP; 119 goto out; 120 } 121 } 122 default: /* Undefined version */ 123 return -EOPNOTSUPP; 124 } 125 126 if (guehdr->control) 127 return -ENOENT; 128 129 optlen = guehdr->hlen << 2; 130 131 if (validate_gue_flags(guehdr, optlen)) 132 return -EINVAL; 133 134 /* Handling exceptions for direct UDP encapsulation in GUE would lead to 135 * recursion. Besides, this kind of encapsulation can't even be 136 * configured currently. Discard this. 137 */ 138 if (guehdr->proto_ctype == IPPROTO_UDP || 139 guehdr->proto_ctype == IPPROTO_UDPLITE) 140 return -EOPNOTSUPP; 141 142 skb_set_transport_header(skb, -(int)sizeof(struct icmp6hdr)); 143 ret = gue6_err_proto_handler(guehdr->proto_ctype, skb, 144 opt, type, code, offset, info); 145 146 out: 147 skb_set_transport_header(skb, transport_offset); 148 return ret; 149 } 150 151 152 static const struct ip6_tnl_encap_ops fou_ip6tun_ops = { 153 .encap_hlen = fou_encap_hlen, 154 .build_header = fou6_build_header, 155 .err_handler = gue6_err, 156 }; 157 158 static const struct ip6_tnl_encap_ops gue_ip6tun_ops = { 159 .encap_hlen = gue_encap_hlen, 160 .build_header = gue6_build_header, 161 .err_handler = gue6_err, 162 }; 163 164 static int ip6_tnl_encap_add_fou_ops(void) 165 { 166 int ret; 167 168 ret = ip6_tnl_encap_add_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU); 169 if (ret < 0) { 170 pr_err("can't add fou6 ops\n"); 171 return ret; 172 } 173 174 ret = ip6_tnl_encap_add_ops(&gue_ip6tun_ops, TUNNEL_ENCAP_GUE); 175 if (ret < 0) { 176 pr_err("can't add gue6 ops\n"); 177 ip6_tnl_encap_del_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU); 178 return ret; 179 } 180 181 return 0; 182 } 183 184 static void ip6_tnl_encap_del_fou_ops(void) 185 { 186 ip6_tnl_encap_del_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU); 187 ip6_tnl_encap_del_ops(&gue_ip6tun_ops, TUNNEL_ENCAP_GUE); 188 } 189 190 #else 191 192 static int ip6_tnl_encap_add_fou_ops(void) 193 { 194 return 0; 195 } 196 197 static void ip6_tnl_encap_del_fou_ops(void) 198 { 199 } 200 201 #endif 202 203 static int __init fou6_init(void) 204 { 205 int ret; 206 207 ret = ip6_tnl_encap_add_fou_ops(); 208 209 return ret; 210 } 211 212 static void __exit fou6_fini(void) 213 { 214 ip6_tnl_encap_del_fou_ops(); 215 } 216 217 module_init(fou6_init); 218 module_exit(fou6_fini); 219 MODULE_AUTHOR("Tom Herbert <therbert@google.com>"); 220 MODULE_LICENSE("GPL"); 221