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 skb_set_transport_header(skb, -(int)sizeof(struct icmp6hdr)); 135 ret = gue6_err_proto_handler(guehdr->proto_ctype, skb, 136 opt, type, code, offset, info); 137 138 out: 139 skb_set_transport_header(skb, transport_offset); 140 return ret; 141 } 142 143 144 static const struct ip6_tnl_encap_ops fou_ip6tun_ops = { 145 .encap_hlen = fou_encap_hlen, 146 .build_header = fou6_build_header, 147 .err_handler = gue6_err, 148 }; 149 150 static const struct ip6_tnl_encap_ops gue_ip6tun_ops = { 151 .encap_hlen = gue_encap_hlen, 152 .build_header = gue6_build_header, 153 .err_handler = gue6_err, 154 }; 155 156 static int ip6_tnl_encap_add_fou_ops(void) 157 { 158 int ret; 159 160 ret = ip6_tnl_encap_add_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU); 161 if (ret < 0) { 162 pr_err("can't add fou6 ops\n"); 163 return ret; 164 } 165 166 ret = ip6_tnl_encap_add_ops(&gue_ip6tun_ops, TUNNEL_ENCAP_GUE); 167 if (ret < 0) { 168 pr_err("can't add gue6 ops\n"); 169 ip6_tnl_encap_del_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU); 170 return ret; 171 } 172 173 return 0; 174 } 175 176 static void ip6_tnl_encap_del_fou_ops(void) 177 { 178 ip6_tnl_encap_del_ops(&fou_ip6tun_ops, TUNNEL_ENCAP_FOU); 179 ip6_tnl_encap_del_ops(&gue_ip6tun_ops, TUNNEL_ENCAP_GUE); 180 } 181 182 #else 183 184 static int ip6_tnl_encap_add_fou_ops(void) 185 { 186 return 0; 187 } 188 189 static void ip6_tnl_encap_del_fou_ops(void) 190 { 191 } 192 193 #endif 194 195 static int __init fou6_init(void) 196 { 197 int ret; 198 199 ret = ip6_tnl_encap_add_fou_ops(); 200 201 return ret; 202 } 203 204 static void __exit fou6_fini(void) 205 { 206 ip6_tnl_encap_del_fou_ops(); 207 } 208 209 module_init(fou6_init); 210 module_exit(fou6_fini); 211 MODULE_AUTHOR("Tom Herbert <therbert@google.com>"); 212 MODULE_LICENSE("GPL"); 213