| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame] | 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/types.h> | 
|  | 8 | #include <linux/kernel.h> | 
|  | 9 | #include <net/genetlink.h> | 
|  | 10 | #include <net/gue.h> | 
|  | 11 | #include <net/fou.h> | 
|  | 12 | #include <net/ip.h> | 
|  | 13 | #include <net/protocol.h> | 
|  | 14 | #include <net/udp.h> | 
|  | 15 | #include <net/udp_tunnel.h> | 
|  | 16 | #include <net/xfrm.h> | 
|  | 17 | #include <uapi/linux/fou.h> | 
|  | 18 | #include <uapi/linux/genetlink.h> | 
|  | 19 |  | 
|  | 20 | struct fou { | 
|  | 21 | struct socket *sock; | 
|  | 22 | u8 protocol; | 
|  | 23 | u8 flags; | 
|  | 24 | __be16 port; | 
|  | 25 | u8 family; | 
|  | 26 | u16 type; | 
|  | 27 | struct list_head list; | 
|  | 28 | struct rcu_head rcu; | 
|  | 29 | }; | 
|  | 30 |  | 
|  | 31 | #define FOU_F_REMCSUM_NOPARTIAL BIT(0) | 
|  | 32 |  | 
|  | 33 | struct fou_cfg { | 
|  | 34 | u16 type; | 
|  | 35 | u8 protocol; | 
|  | 36 | u8 flags; | 
|  | 37 | struct udp_port_cfg udp_config; | 
|  | 38 | }; | 
|  | 39 |  | 
|  | 40 | static unsigned int fou_net_id; | 
|  | 41 |  | 
|  | 42 | struct fou_net { | 
|  | 43 | struct list_head fou_list; | 
|  | 44 | struct mutex fou_lock; | 
|  | 45 | }; | 
|  | 46 |  | 
|  | 47 | static inline struct fou *fou_from_sock(struct sock *sk) | 
|  | 48 | { | 
|  | 49 | return sk->sk_user_data; | 
|  | 50 | } | 
|  | 51 |  | 
|  | 52 | static int fou_recv_pull(struct sk_buff *skb, struct fou *fou, size_t len) | 
|  | 53 | { | 
|  | 54 | /* Remove 'len' bytes from the packet (UDP header and | 
|  | 55 | * FOU header if present). | 
|  | 56 | */ | 
|  | 57 | if (fou->family == AF_INET) | 
|  | 58 | ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len); | 
|  | 59 | else | 
|  | 60 | ipv6_hdr(skb)->payload_len = | 
|  | 61 | htons(ntohs(ipv6_hdr(skb)->payload_len) - len); | 
|  | 62 |  | 
|  | 63 | __skb_pull(skb, len); | 
|  | 64 | skb_postpull_rcsum(skb, udp_hdr(skb), len); | 
|  | 65 | skb_reset_transport_header(skb); | 
|  | 66 | return iptunnel_pull_offloads(skb); | 
|  | 67 | } | 
|  | 68 |  | 
|  | 69 | static int fou_udp_recv(struct sock *sk, struct sk_buff *skb) | 
|  | 70 | { | 
|  | 71 | struct fou *fou = fou_from_sock(sk); | 
|  | 72 |  | 
|  | 73 | if (!fou) | 
|  | 74 | return 1; | 
|  | 75 |  | 
|  | 76 | if (fou_recv_pull(skb, fou, sizeof(struct udphdr))) | 
|  | 77 | goto drop; | 
|  | 78 |  | 
|  | 79 | return -fou->protocol; | 
|  | 80 |  | 
|  | 81 | drop: | 
|  | 82 | kfree_skb(skb); | 
|  | 83 | return 0; | 
|  | 84 | } | 
|  | 85 |  | 
|  | 86 | static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr, | 
|  | 87 | void *data, size_t hdrlen, u8 ipproto, | 
|  | 88 | bool nopartial) | 
|  | 89 | { | 
|  | 90 | __be16 *pd = data; | 
|  | 91 | size_t start = ntohs(pd[0]); | 
|  | 92 | size_t offset = ntohs(pd[1]); | 
|  | 93 | size_t plen = sizeof(struct udphdr) + hdrlen + | 
|  | 94 | max_t(size_t, offset + sizeof(u16), start); | 
|  | 95 |  | 
|  | 96 | if (skb->remcsum_offload) | 
|  | 97 | return guehdr; | 
|  | 98 |  | 
|  | 99 | if (!pskb_may_pull(skb, plen)) | 
|  | 100 | return NULL; | 
|  | 101 | guehdr = (struct guehdr *)&udp_hdr(skb)[1]; | 
|  | 102 |  | 
|  | 103 | skb_remcsum_process(skb, (void *)guehdr + hdrlen, | 
|  | 104 | start, offset, nopartial); | 
|  | 105 |  | 
|  | 106 | return guehdr; | 
|  | 107 | } | 
|  | 108 |  | 
|  | 109 | static int gue_control_message(struct sk_buff *skb, struct guehdr *guehdr) | 
|  | 110 | { | 
|  | 111 | /* No support yet */ | 
|  | 112 | kfree_skb(skb); | 
|  | 113 | return 0; | 
|  | 114 | } | 
|  | 115 |  | 
|  | 116 | static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) | 
|  | 117 | { | 
|  | 118 | struct fou *fou = fou_from_sock(sk); | 
|  | 119 | size_t len, optlen, hdrlen; | 
|  | 120 | struct guehdr *guehdr; | 
|  | 121 | void *data; | 
|  | 122 | u16 doffset = 0; | 
|  | 123 | u8 proto_ctype; | 
|  | 124 |  | 
|  | 125 | if (!fou) | 
|  | 126 | return 1; | 
|  | 127 |  | 
|  | 128 | len = sizeof(struct udphdr) + sizeof(struct guehdr); | 
|  | 129 | if (!pskb_may_pull(skb, len)) | 
|  | 130 | goto drop; | 
|  | 131 |  | 
|  | 132 | guehdr = (struct guehdr *)&udp_hdr(skb)[1]; | 
|  | 133 |  | 
|  | 134 | switch (guehdr->version) { | 
|  | 135 | case 0: /* Full GUE header present */ | 
|  | 136 | break; | 
|  | 137 |  | 
|  | 138 | case 1: { | 
|  | 139 | /* Direct encasulation of IPv4 or IPv6 */ | 
|  | 140 |  | 
|  | 141 | int prot; | 
|  | 142 |  | 
|  | 143 | switch (((struct iphdr *)guehdr)->version) { | 
|  | 144 | case 4: | 
|  | 145 | prot = IPPROTO_IPIP; | 
|  | 146 | break; | 
|  | 147 | case 6: | 
|  | 148 | prot = IPPROTO_IPV6; | 
|  | 149 | break; | 
|  | 150 | default: | 
|  | 151 | goto drop; | 
|  | 152 | } | 
|  | 153 |  | 
|  | 154 | if (fou_recv_pull(skb, fou, sizeof(struct udphdr))) | 
|  | 155 | goto drop; | 
|  | 156 |  | 
|  | 157 | return -prot; | 
|  | 158 | } | 
|  | 159 |  | 
|  | 160 | default: /* Undefined version */ | 
|  | 161 | goto drop; | 
|  | 162 | } | 
|  | 163 |  | 
|  | 164 | optlen = guehdr->hlen << 2; | 
|  | 165 | len += optlen; | 
|  | 166 |  | 
|  | 167 | if (!pskb_may_pull(skb, len)) | 
|  | 168 | goto drop; | 
|  | 169 |  | 
|  | 170 | /* guehdr may change after pull */ | 
|  | 171 | guehdr = (struct guehdr *)&udp_hdr(skb)[1]; | 
|  | 172 |  | 
|  | 173 | hdrlen = sizeof(struct guehdr) + optlen; | 
|  | 174 |  | 
|  | 175 | if (guehdr->version != 0 || validate_gue_flags(guehdr, optlen)) | 
|  | 176 | goto drop; | 
|  | 177 |  | 
|  | 178 | hdrlen = sizeof(struct guehdr) + optlen; | 
|  | 179 |  | 
|  | 180 | if (fou->family == AF_INET) | 
|  | 181 | ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len); | 
|  | 182 | else | 
|  | 183 | ipv6_hdr(skb)->payload_len = | 
|  | 184 | htons(ntohs(ipv6_hdr(skb)->payload_len) - len); | 
|  | 185 |  | 
|  | 186 | /* Pull csum through the guehdr now . This can be used if | 
|  | 187 | * there is a remote checksum offload. | 
|  | 188 | */ | 
|  | 189 | skb_postpull_rcsum(skb, udp_hdr(skb), len); | 
|  | 190 |  | 
|  | 191 | data = &guehdr[1]; | 
|  | 192 |  | 
|  | 193 | if (guehdr->flags & GUE_FLAG_PRIV) { | 
|  | 194 | __be32 flags = *(__be32 *)(data + doffset); | 
|  | 195 |  | 
|  | 196 | doffset += GUE_LEN_PRIV; | 
|  | 197 |  | 
|  | 198 | if (flags & GUE_PFLAG_REMCSUM) { | 
|  | 199 | guehdr = gue_remcsum(skb, guehdr, data + doffset, | 
|  | 200 | hdrlen, guehdr->proto_ctype, | 
|  | 201 | !!(fou->flags & | 
|  | 202 | FOU_F_REMCSUM_NOPARTIAL)); | 
|  | 203 | if (!guehdr) | 
|  | 204 | goto drop; | 
|  | 205 |  | 
|  | 206 | data = &guehdr[1]; | 
|  | 207 |  | 
|  | 208 | doffset += GUE_PLEN_REMCSUM; | 
|  | 209 | } | 
|  | 210 | } | 
|  | 211 |  | 
|  | 212 | if (unlikely(guehdr->control)) | 
|  | 213 | return gue_control_message(skb, guehdr); | 
|  | 214 |  | 
|  | 215 | proto_ctype = guehdr->proto_ctype; | 
|  | 216 | __skb_pull(skb, sizeof(struct udphdr) + hdrlen); | 
|  | 217 | skb_reset_transport_header(skb); | 
|  | 218 |  | 
|  | 219 | if (iptunnel_pull_offloads(skb)) | 
|  | 220 | goto drop; | 
|  | 221 |  | 
|  | 222 | return -proto_ctype; | 
|  | 223 |  | 
|  | 224 | drop: | 
|  | 225 | kfree_skb(skb); | 
|  | 226 | return 0; | 
|  | 227 | } | 
|  | 228 |  | 
|  | 229 | static struct sk_buff *fou_gro_receive(struct sock *sk, | 
|  | 230 | struct list_head *head, | 
|  | 231 | struct sk_buff *skb) | 
|  | 232 | { | 
|  | 233 | u8 proto = fou_from_sock(sk)->protocol; | 
|  | 234 | const struct net_offload **offloads; | 
|  | 235 | const struct net_offload *ops; | 
|  | 236 | struct sk_buff *pp = NULL; | 
|  | 237 |  | 
|  | 238 | /* We can clear the encap_mark for FOU as we are essentially doing | 
|  | 239 | * one of two possible things.  We are either adding an L4 tunnel | 
|  | 240 | * header to the outer L3 tunnel header, or we are are simply | 
|  | 241 | * treating the GRE tunnel header as though it is a UDP protocol | 
|  | 242 | * specific header such as VXLAN or GENEVE. | 
|  | 243 | */ | 
|  | 244 | NAPI_GRO_CB(skb)->encap_mark = 0; | 
|  | 245 |  | 
|  | 246 | /* Flag this frame as already having an outer encap header */ | 
|  | 247 | NAPI_GRO_CB(skb)->is_fou = 1; | 
|  | 248 |  | 
|  | 249 | rcu_read_lock(); | 
|  | 250 | offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; | 
|  | 251 | ops = rcu_dereference(offloads[proto]); | 
|  | 252 | if (!ops || !ops->callbacks.gro_receive) | 
|  | 253 | goto out_unlock; | 
|  | 254 |  | 
|  | 255 | pp = call_gro_receive(ops->callbacks.gro_receive, head, skb); | 
|  | 256 |  | 
|  | 257 | out_unlock: | 
|  | 258 | rcu_read_unlock(); | 
|  | 259 |  | 
|  | 260 | return pp; | 
|  | 261 | } | 
|  | 262 |  | 
|  | 263 | static int fou_gro_complete(struct sock *sk, struct sk_buff *skb, | 
|  | 264 | int nhoff) | 
|  | 265 | { | 
|  | 266 | const struct net_offload *ops; | 
|  | 267 | u8 proto = fou_from_sock(sk)->protocol; | 
|  | 268 | int err = -ENOSYS; | 
|  | 269 | const struct net_offload **offloads; | 
|  | 270 |  | 
|  | 271 | rcu_read_lock(); | 
|  | 272 | offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; | 
|  | 273 | ops = rcu_dereference(offloads[proto]); | 
|  | 274 | if (WARN_ON(!ops || !ops->callbacks.gro_complete)) | 
|  | 275 | goto out_unlock; | 
|  | 276 |  | 
|  | 277 | err = ops->callbacks.gro_complete(skb, nhoff); | 
|  | 278 |  | 
|  | 279 | skb_set_inner_mac_header(skb, nhoff); | 
|  | 280 |  | 
|  | 281 | out_unlock: | 
|  | 282 | rcu_read_unlock(); | 
|  | 283 |  | 
|  | 284 | return err; | 
|  | 285 | } | 
|  | 286 |  | 
|  | 287 | static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off, | 
|  | 288 | struct guehdr *guehdr, void *data, | 
|  | 289 | size_t hdrlen, struct gro_remcsum *grc, | 
|  | 290 | bool nopartial) | 
|  | 291 | { | 
|  | 292 | __be16 *pd = data; | 
|  | 293 | size_t start = ntohs(pd[0]); | 
|  | 294 | size_t offset = ntohs(pd[1]); | 
|  | 295 |  | 
|  | 296 | if (skb->remcsum_offload) | 
|  | 297 | return guehdr; | 
|  | 298 |  | 
|  | 299 | if (!NAPI_GRO_CB(skb)->csum_valid) | 
|  | 300 | return NULL; | 
|  | 301 |  | 
|  | 302 | guehdr = skb_gro_remcsum_process(skb, (void *)guehdr, off, hdrlen, | 
|  | 303 | start, offset, grc, nopartial); | 
|  | 304 |  | 
|  | 305 | skb->remcsum_offload = 1; | 
|  | 306 |  | 
|  | 307 | return guehdr; | 
|  | 308 | } | 
|  | 309 |  | 
|  | 310 | static struct sk_buff *gue_gro_receive(struct sock *sk, | 
|  | 311 | struct list_head *head, | 
|  | 312 | struct sk_buff *skb) | 
|  | 313 | { | 
|  | 314 | const struct net_offload **offloads; | 
|  | 315 | const struct net_offload *ops; | 
|  | 316 | struct sk_buff *pp = NULL; | 
|  | 317 | struct sk_buff *p; | 
|  | 318 | struct guehdr *guehdr; | 
|  | 319 | size_t len, optlen, hdrlen, off; | 
|  | 320 | void *data; | 
|  | 321 | u16 doffset = 0; | 
|  | 322 | int flush = 1; | 
|  | 323 | struct fou *fou = fou_from_sock(sk); | 
|  | 324 | struct gro_remcsum grc; | 
|  | 325 | u8 proto; | 
|  | 326 |  | 
|  | 327 | skb_gro_remcsum_init(&grc); | 
|  | 328 |  | 
|  | 329 | off = skb_gro_offset(skb); | 
|  | 330 | len = off + sizeof(*guehdr); | 
|  | 331 |  | 
|  | 332 | guehdr = skb_gro_header_fast(skb, off); | 
|  | 333 | if (skb_gro_header_hard(skb, len)) { | 
|  | 334 | guehdr = skb_gro_header_slow(skb, len, off); | 
|  | 335 | if (unlikely(!guehdr)) | 
|  | 336 | goto out; | 
|  | 337 | } | 
|  | 338 |  | 
|  | 339 | switch (guehdr->version) { | 
|  | 340 | case 0: | 
|  | 341 | break; | 
|  | 342 | case 1: | 
|  | 343 | switch (((struct iphdr *)guehdr)->version) { | 
|  | 344 | case 4: | 
|  | 345 | proto = IPPROTO_IPIP; | 
|  | 346 | break; | 
|  | 347 | case 6: | 
|  | 348 | proto = IPPROTO_IPV6; | 
|  | 349 | break; | 
|  | 350 | default: | 
|  | 351 | goto out; | 
|  | 352 | } | 
|  | 353 | goto next_proto; | 
|  | 354 | default: | 
|  | 355 | goto out; | 
|  | 356 | } | 
|  | 357 |  | 
|  | 358 | optlen = guehdr->hlen << 2; | 
|  | 359 | len += optlen; | 
|  | 360 |  | 
|  | 361 | if (skb_gro_header_hard(skb, len)) { | 
|  | 362 | guehdr = skb_gro_header_slow(skb, len, off); | 
|  | 363 | if (unlikely(!guehdr)) | 
|  | 364 | goto out; | 
|  | 365 | } | 
|  | 366 |  | 
|  | 367 | if (unlikely(guehdr->control) || guehdr->version != 0 || | 
|  | 368 | validate_gue_flags(guehdr, optlen)) | 
|  | 369 | goto out; | 
|  | 370 |  | 
|  | 371 | hdrlen = sizeof(*guehdr) + optlen; | 
|  | 372 |  | 
|  | 373 | /* Adjust NAPI_GRO_CB(skb)->csum to account for guehdr, | 
|  | 374 | * this is needed if there is a remote checkcsum offload. | 
|  | 375 | */ | 
|  | 376 | skb_gro_postpull_rcsum(skb, guehdr, hdrlen); | 
|  | 377 |  | 
|  | 378 | data = &guehdr[1]; | 
|  | 379 |  | 
|  | 380 | if (guehdr->flags & GUE_FLAG_PRIV) { | 
|  | 381 | __be32 flags = *(__be32 *)(data + doffset); | 
|  | 382 |  | 
|  | 383 | doffset += GUE_LEN_PRIV; | 
|  | 384 |  | 
|  | 385 | if (flags & GUE_PFLAG_REMCSUM) { | 
|  | 386 | guehdr = gue_gro_remcsum(skb, off, guehdr, | 
|  | 387 | data + doffset, hdrlen, &grc, | 
|  | 388 | !!(fou->flags & | 
|  | 389 | FOU_F_REMCSUM_NOPARTIAL)); | 
|  | 390 |  | 
|  | 391 | if (!guehdr) | 
|  | 392 | goto out; | 
|  | 393 |  | 
|  | 394 | data = &guehdr[1]; | 
|  | 395 |  | 
|  | 396 | doffset += GUE_PLEN_REMCSUM; | 
|  | 397 | } | 
|  | 398 | } | 
|  | 399 |  | 
|  | 400 | skb_gro_pull(skb, hdrlen); | 
|  | 401 |  | 
|  | 402 | list_for_each_entry(p, head, list) { | 
|  | 403 | const struct guehdr *guehdr2; | 
|  | 404 |  | 
|  | 405 | if (!NAPI_GRO_CB(p)->same_flow) | 
|  | 406 | continue; | 
|  | 407 |  | 
|  | 408 | guehdr2 = (struct guehdr *)(p->data + off); | 
|  | 409 |  | 
|  | 410 | /* Compare base GUE header to be equal (covers | 
|  | 411 | * hlen, version, proto_ctype, and flags. | 
|  | 412 | */ | 
|  | 413 | if (guehdr->word != guehdr2->word) { | 
|  | 414 | NAPI_GRO_CB(p)->same_flow = 0; | 
|  | 415 | continue; | 
|  | 416 | } | 
|  | 417 |  | 
|  | 418 | /* Compare optional fields are the same. */ | 
|  | 419 | if (guehdr->hlen && memcmp(&guehdr[1], &guehdr2[1], | 
|  | 420 | guehdr->hlen << 2)) { | 
|  | 421 | NAPI_GRO_CB(p)->same_flow = 0; | 
|  | 422 | continue; | 
|  | 423 | } | 
|  | 424 | } | 
|  | 425 |  | 
|  | 426 | proto = guehdr->proto_ctype; | 
|  | 427 |  | 
|  | 428 | next_proto: | 
|  | 429 |  | 
|  | 430 | /* We can clear the encap_mark for GUE as we are essentially doing | 
|  | 431 | * one of two possible things.  We are either adding an L4 tunnel | 
|  | 432 | * header to the outer L3 tunnel header, or we are are simply | 
|  | 433 | * treating the GRE tunnel header as though it is a UDP protocol | 
|  | 434 | * specific header such as VXLAN or GENEVE. | 
|  | 435 | */ | 
|  | 436 | NAPI_GRO_CB(skb)->encap_mark = 0; | 
|  | 437 |  | 
|  | 438 | /* Flag this frame as already having an outer encap header */ | 
|  | 439 | NAPI_GRO_CB(skb)->is_fou = 1; | 
|  | 440 |  | 
|  | 441 | rcu_read_lock(); | 
|  | 442 | offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; | 
|  | 443 | ops = rcu_dereference(offloads[proto]); | 
|  | 444 | if (WARN_ON_ONCE(!ops || !ops->callbacks.gro_receive)) | 
|  | 445 | goto out_unlock; | 
|  | 446 |  | 
|  | 447 | pp = call_gro_receive(ops->callbacks.gro_receive, head, skb); | 
|  | 448 | flush = 0; | 
|  | 449 |  | 
|  | 450 | out_unlock: | 
|  | 451 | rcu_read_unlock(); | 
|  | 452 | out: | 
|  | 453 | skb_gro_flush_final_remcsum(skb, pp, flush, &grc); | 
|  | 454 |  | 
|  | 455 | return pp; | 
|  | 456 | } | 
|  | 457 |  | 
|  | 458 | static int gue_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff) | 
|  | 459 | { | 
|  | 460 | const struct net_offload **offloads; | 
|  | 461 | struct guehdr *guehdr = (struct guehdr *)(skb->data + nhoff); | 
|  | 462 | const struct net_offload *ops; | 
|  | 463 | unsigned int guehlen = 0; | 
|  | 464 | u8 proto; | 
|  | 465 | int err = -ENOENT; | 
|  | 466 |  | 
|  | 467 | switch (guehdr->version) { | 
|  | 468 | case 0: | 
|  | 469 | proto = guehdr->proto_ctype; | 
|  | 470 | guehlen = sizeof(*guehdr) + (guehdr->hlen << 2); | 
|  | 471 | break; | 
|  | 472 | case 1: | 
|  | 473 | switch (((struct iphdr *)guehdr)->version) { | 
|  | 474 | case 4: | 
|  | 475 | proto = IPPROTO_IPIP; | 
|  | 476 | break; | 
|  | 477 | case 6: | 
|  | 478 | proto = IPPROTO_IPV6; | 
|  | 479 | break; | 
|  | 480 | default: | 
|  | 481 | return err; | 
|  | 482 | } | 
|  | 483 | break; | 
|  | 484 | default: | 
|  | 485 | return err; | 
|  | 486 | } | 
|  | 487 |  | 
|  | 488 | rcu_read_lock(); | 
|  | 489 | offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; | 
|  | 490 | ops = rcu_dereference(offloads[proto]); | 
|  | 491 | if (WARN_ON(!ops || !ops->callbacks.gro_complete)) | 
|  | 492 | goto out_unlock; | 
|  | 493 |  | 
|  | 494 | err = ops->callbacks.gro_complete(skb, nhoff + guehlen); | 
|  | 495 |  | 
|  | 496 | skb_set_inner_mac_header(skb, nhoff + guehlen); | 
|  | 497 |  | 
|  | 498 | out_unlock: | 
|  | 499 | rcu_read_unlock(); | 
|  | 500 | return err; | 
|  | 501 | } | 
|  | 502 |  | 
|  | 503 | static int fou_add_to_port_list(struct net *net, struct fou *fou) | 
|  | 504 | { | 
|  | 505 | struct fou_net *fn = net_generic(net, fou_net_id); | 
|  | 506 | struct fou *fout; | 
|  | 507 |  | 
|  | 508 | mutex_lock(&fn->fou_lock); | 
|  | 509 | list_for_each_entry(fout, &fn->fou_list, list) { | 
|  | 510 | if (fou->port == fout->port && | 
|  | 511 | fou->family == fout->family) { | 
|  | 512 | mutex_unlock(&fn->fou_lock); | 
|  | 513 | return -EALREADY; | 
|  | 514 | } | 
|  | 515 | } | 
|  | 516 |  | 
|  | 517 | list_add(&fou->list, &fn->fou_list); | 
|  | 518 | mutex_unlock(&fn->fou_lock); | 
|  | 519 |  | 
|  | 520 | return 0; | 
|  | 521 | } | 
|  | 522 |  | 
|  | 523 | static void fou_release(struct fou *fou) | 
|  | 524 | { | 
|  | 525 | struct socket *sock = fou->sock; | 
|  | 526 |  | 
|  | 527 | list_del(&fou->list); | 
|  | 528 | udp_tunnel_sock_release(sock); | 
|  | 529 |  | 
|  | 530 | kfree_rcu(fou, rcu); | 
|  | 531 | } | 
|  | 532 |  | 
|  | 533 | static int fou_create(struct net *net, struct fou_cfg *cfg, | 
|  | 534 | struct socket **sockp) | 
|  | 535 | { | 
|  | 536 | struct socket *sock = NULL; | 
|  | 537 | struct fou *fou = NULL; | 
|  | 538 | struct sock *sk; | 
|  | 539 | struct udp_tunnel_sock_cfg tunnel_cfg; | 
|  | 540 | int err; | 
|  | 541 |  | 
|  | 542 | /* Open UDP socket */ | 
|  | 543 | err = udp_sock_create(net, &cfg->udp_config, &sock); | 
|  | 544 | if (err < 0) | 
|  | 545 | goto error; | 
|  | 546 |  | 
|  | 547 | /* Allocate FOU port structure */ | 
|  | 548 | fou = kzalloc(sizeof(*fou), GFP_KERNEL); | 
|  | 549 | if (!fou) { | 
|  | 550 | err = -ENOMEM; | 
|  | 551 | goto error; | 
|  | 552 | } | 
|  | 553 |  | 
|  | 554 | sk = sock->sk; | 
|  | 555 |  | 
|  | 556 | fou->port = cfg->udp_config.local_udp_port; | 
|  | 557 | fou->family = cfg->udp_config.family; | 
|  | 558 | fou->flags = cfg->flags; | 
|  | 559 | fou->type = cfg->type; | 
|  | 560 | fou->sock = sock; | 
|  | 561 |  | 
|  | 562 | memset(&tunnel_cfg, 0, sizeof(tunnel_cfg)); | 
|  | 563 | tunnel_cfg.encap_type = 1; | 
|  | 564 | tunnel_cfg.sk_user_data = fou; | 
|  | 565 | tunnel_cfg.encap_destroy = NULL; | 
|  | 566 |  | 
|  | 567 | /* Initial for fou type */ | 
|  | 568 | switch (cfg->type) { | 
|  | 569 | case FOU_ENCAP_DIRECT: | 
|  | 570 | tunnel_cfg.encap_rcv = fou_udp_recv; | 
|  | 571 | tunnel_cfg.gro_receive = fou_gro_receive; | 
|  | 572 | tunnel_cfg.gro_complete = fou_gro_complete; | 
|  | 573 | fou->protocol = cfg->protocol; | 
|  | 574 | break; | 
|  | 575 | case FOU_ENCAP_GUE: | 
|  | 576 | tunnel_cfg.encap_rcv = gue_udp_recv; | 
|  | 577 | tunnel_cfg.gro_receive = gue_gro_receive; | 
|  | 578 | tunnel_cfg.gro_complete = gue_gro_complete; | 
|  | 579 | break; | 
|  | 580 | default: | 
|  | 581 | err = -EINVAL; | 
|  | 582 | goto error; | 
|  | 583 | } | 
|  | 584 |  | 
|  | 585 | setup_udp_tunnel_sock(net, sock, &tunnel_cfg); | 
|  | 586 |  | 
|  | 587 | sk->sk_allocation = GFP_ATOMIC; | 
|  | 588 |  | 
|  | 589 | err = fou_add_to_port_list(net, fou); | 
|  | 590 | if (err) | 
|  | 591 | goto error; | 
|  | 592 |  | 
|  | 593 | if (sockp) | 
|  | 594 | *sockp = sock; | 
|  | 595 |  | 
|  | 596 | return 0; | 
|  | 597 |  | 
|  | 598 | error: | 
|  | 599 | kfree(fou); | 
|  | 600 | if (sock) | 
|  | 601 | udp_tunnel_sock_release(sock); | 
|  | 602 |  | 
|  | 603 | return err; | 
|  | 604 | } | 
|  | 605 |  | 
|  | 606 | static int fou_destroy(struct net *net, struct fou_cfg *cfg) | 
|  | 607 | { | 
|  | 608 | struct fou_net *fn = net_generic(net, fou_net_id); | 
|  | 609 | __be16 port = cfg->udp_config.local_udp_port; | 
|  | 610 | u8 family = cfg->udp_config.family; | 
|  | 611 | int err = -EINVAL; | 
|  | 612 | struct fou *fou; | 
|  | 613 |  | 
|  | 614 | mutex_lock(&fn->fou_lock); | 
|  | 615 | list_for_each_entry(fou, &fn->fou_list, list) { | 
|  | 616 | if (fou->port == port && fou->family == family) { | 
|  | 617 | fou_release(fou); | 
|  | 618 | err = 0; | 
|  | 619 | break; | 
|  | 620 | } | 
|  | 621 | } | 
|  | 622 | mutex_unlock(&fn->fou_lock); | 
|  | 623 |  | 
|  | 624 | return err; | 
|  | 625 | } | 
|  | 626 |  | 
|  | 627 | static struct genl_family fou_nl_family; | 
|  | 628 |  | 
|  | 629 | static const struct nla_policy fou_nl_policy[FOU_ATTR_MAX + 1] = { | 
|  | 630 | [FOU_ATTR_PORT] = { .type = NLA_U16, }, | 
|  | 631 | [FOU_ATTR_AF] = { .type = NLA_U8, }, | 
|  | 632 | [FOU_ATTR_IPPROTO] = { .type = NLA_U8, }, | 
|  | 633 | [FOU_ATTR_TYPE] = { .type = NLA_U8, }, | 
|  | 634 | [FOU_ATTR_REMCSUM_NOPARTIAL] = { .type = NLA_FLAG, }, | 
|  | 635 | }; | 
|  | 636 |  | 
|  | 637 | static int parse_nl_config(struct genl_info *info, | 
|  | 638 | struct fou_cfg *cfg) | 
|  | 639 | { | 
|  | 640 | memset(cfg, 0, sizeof(*cfg)); | 
|  | 641 |  | 
|  | 642 | cfg->udp_config.family = AF_INET; | 
|  | 643 |  | 
|  | 644 | if (info->attrs[FOU_ATTR_AF]) { | 
|  | 645 | u8 family = nla_get_u8(info->attrs[FOU_ATTR_AF]); | 
|  | 646 |  | 
|  | 647 | switch (family) { | 
|  | 648 | case AF_INET: | 
|  | 649 | break; | 
|  | 650 | case AF_INET6: | 
|  | 651 | cfg->udp_config.ipv6_v6only = 1; | 
|  | 652 | break; | 
|  | 653 | default: | 
|  | 654 | return -EAFNOSUPPORT; | 
|  | 655 | } | 
|  | 656 |  | 
|  | 657 | cfg->udp_config.family = family; | 
|  | 658 | } | 
|  | 659 |  | 
|  | 660 | if (info->attrs[FOU_ATTR_PORT]) { | 
|  | 661 | __be16 port = nla_get_be16(info->attrs[FOU_ATTR_PORT]); | 
|  | 662 |  | 
|  | 663 | cfg->udp_config.local_udp_port = port; | 
|  | 664 | } | 
|  | 665 |  | 
|  | 666 | if (info->attrs[FOU_ATTR_IPPROTO]) | 
|  | 667 | cfg->protocol = nla_get_u8(info->attrs[FOU_ATTR_IPPROTO]); | 
|  | 668 |  | 
|  | 669 | if (info->attrs[FOU_ATTR_TYPE]) | 
|  | 670 | cfg->type = nla_get_u8(info->attrs[FOU_ATTR_TYPE]); | 
|  | 671 |  | 
|  | 672 | if (info->attrs[FOU_ATTR_REMCSUM_NOPARTIAL]) | 
|  | 673 | cfg->flags |= FOU_F_REMCSUM_NOPARTIAL; | 
|  | 674 |  | 
|  | 675 | return 0; | 
|  | 676 | } | 
|  | 677 |  | 
|  | 678 | static int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info) | 
|  | 679 | { | 
|  | 680 | struct net *net = genl_info_net(info); | 
|  | 681 | struct fou_cfg cfg; | 
|  | 682 | int err; | 
|  | 683 |  | 
|  | 684 | err = parse_nl_config(info, &cfg); | 
|  | 685 | if (err) | 
|  | 686 | return err; | 
|  | 687 |  | 
|  | 688 | return fou_create(net, &cfg, NULL); | 
|  | 689 | } | 
|  | 690 |  | 
|  | 691 | static int fou_nl_cmd_rm_port(struct sk_buff *skb, struct genl_info *info) | 
|  | 692 | { | 
|  | 693 | struct net *net = genl_info_net(info); | 
|  | 694 | struct fou_cfg cfg; | 
|  | 695 | int err; | 
|  | 696 |  | 
|  | 697 | err = parse_nl_config(info, &cfg); | 
|  | 698 | if (err) | 
|  | 699 | return err; | 
|  | 700 |  | 
|  | 701 | return fou_destroy(net, &cfg); | 
|  | 702 | } | 
|  | 703 |  | 
|  | 704 | static int fou_fill_info(struct fou *fou, struct sk_buff *msg) | 
|  | 705 | { | 
|  | 706 | if (nla_put_u8(msg, FOU_ATTR_AF, fou->sock->sk->sk_family) || | 
|  | 707 | nla_put_be16(msg, FOU_ATTR_PORT, fou->port) || | 
|  | 708 | nla_put_u8(msg, FOU_ATTR_IPPROTO, fou->protocol) || | 
|  | 709 | nla_put_u8(msg, FOU_ATTR_TYPE, fou->type)) | 
|  | 710 | return -1; | 
|  | 711 |  | 
|  | 712 | if (fou->flags & FOU_F_REMCSUM_NOPARTIAL) | 
|  | 713 | if (nla_put_flag(msg, FOU_ATTR_REMCSUM_NOPARTIAL)) | 
|  | 714 | return -1; | 
|  | 715 | return 0; | 
|  | 716 | } | 
|  | 717 |  | 
|  | 718 | static int fou_dump_info(struct fou *fou, u32 portid, u32 seq, | 
|  | 719 | u32 flags, struct sk_buff *skb, u8 cmd) | 
|  | 720 | { | 
|  | 721 | void *hdr; | 
|  | 722 |  | 
|  | 723 | hdr = genlmsg_put(skb, portid, seq, &fou_nl_family, flags, cmd); | 
|  | 724 | if (!hdr) | 
|  | 725 | return -ENOMEM; | 
|  | 726 |  | 
|  | 727 | if (fou_fill_info(fou, skb) < 0) | 
|  | 728 | goto nla_put_failure; | 
|  | 729 |  | 
|  | 730 | genlmsg_end(skb, hdr); | 
|  | 731 | return 0; | 
|  | 732 |  | 
|  | 733 | nla_put_failure: | 
|  | 734 | genlmsg_cancel(skb, hdr); | 
|  | 735 | return -EMSGSIZE; | 
|  | 736 | } | 
|  | 737 |  | 
|  | 738 | static int fou_nl_cmd_get_port(struct sk_buff *skb, struct genl_info *info) | 
|  | 739 | { | 
|  | 740 | struct net *net = genl_info_net(info); | 
|  | 741 | struct fou_net *fn = net_generic(net, fou_net_id); | 
|  | 742 | struct sk_buff *msg; | 
|  | 743 | struct fou_cfg cfg; | 
|  | 744 | struct fou *fout; | 
|  | 745 | __be16 port; | 
|  | 746 | u8 family; | 
|  | 747 | int ret; | 
|  | 748 |  | 
|  | 749 | ret = parse_nl_config(info, &cfg); | 
|  | 750 | if (ret) | 
|  | 751 | return ret; | 
|  | 752 | port = cfg.udp_config.local_udp_port; | 
|  | 753 | if (port == 0) | 
|  | 754 | return -EINVAL; | 
|  | 755 |  | 
|  | 756 | family = cfg.udp_config.family; | 
|  | 757 | if (family != AF_INET && family != AF_INET6) | 
|  | 758 | return -EINVAL; | 
|  | 759 |  | 
|  | 760 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|  | 761 | if (!msg) | 
|  | 762 | return -ENOMEM; | 
|  | 763 |  | 
|  | 764 | ret = -ESRCH; | 
|  | 765 | mutex_lock(&fn->fou_lock); | 
|  | 766 | list_for_each_entry(fout, &fn->fou_list, list) { | 
|  | 767 | if (port == fout->port && family == fout->family) { | 
|  | 768 | ret = fou_dump_info(fout, info->snd_portid, | 
|  | 769 | info->snd_seq, 0, msg, | 
|  | 770 | info->genlhdr->cmd); | 
|  | 771 | break; | 
|  | 772 | } | 
|  | 773 | } | 
|  | 774 | mutex_unlock(&fn->fou_lock); | 
|  | 775 | if (ret < 0) | 
|  | 776 | goto out_free; | 
|  | 777 |  | 
|  | 778 | return genlmsg_reply(msg, info); | 
|  | 779 |  | 
|  | 780 | out_free: | 
|  | 781 | nlmsg_free(msg); | 
|  | 782 | return ret; | 
|  | 783 | } | 
|  | 784 |  | 
|  | 785 | static int fou_nl_dump(struct sk_buff *skb, struct netlink_callback *cb) | 
|  | 786 | { | 
|  | 787 | struct net *net = sock_net(skb->sk); | 
|  | 788 | struct fou_net *fn = net_generic(net, fou_net_id); | 
|  | 789 | struct fou *fout; | 
|  | 790 | int idx = 0, ret; | 
|  | 791 |  | 
|  | 792 | mutex_lock(&fn->fou_lock); | 
|  | 793 | list_for_each_entry(fout, &fn->fou_list, list) { | 
|  | 794 | if (idx++ < cb->args[0]) | 
|  | 795 | continue; | 
|  | 796 | ret = fou_dump_info(fout, NETLINK_CB(cb->skb).portid, | 
|  | 797 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | 
|  | 798 | skb, FOU_CMD_GET); | 
|  | 799 | if (ret) | 
|  | 800 | break; | 
|  | 801 | } | 
|  | 802 | mutex_unlock(&fn->fou_lock); | 
|  | 803 |  | 
|  | 804 | cb->args[0] = idx; | 
|  | 805 | return skb->len; | 
|  | 806 | } | 
|  | 807 |  | 
|  | 808 | static const struct genl_ops fou_nl_ops[] = { | 
|  | 809 | { | 
|  | 810 | .cmd = FOU_CMD_ADD, | 
|  | 811 | .doit = fou_nl_cmd_add_port, | 
|  | 812 | .policy = fou_nl_policy, | 
|  | 813 | .flags = GENL_ADMIN_PERM, | 
|  | 814 | }, | 
|  | 815 | { | 
|  | 816 | .cmd = FOU_CMD_DEL, | 
|  | 817 | .doit = fou_nl_cmd_rm_port, | 
|  | 818 | .policy = fou_nl_policy, | 
|  | 819 | .flags = GENL_ADMIN_PERM, | 
|  | 820 | }, | 
|  | 821 | { | 
|  | 822 | .cmd = FOU_CMD_GET, | 
|  | 823 | .doit = fou_nl_cmd_get_port, | 
|  | 824 | .dumpit = fou_nl_dump, | 
|  | 825 | .policy = fou_nl_policy, | 
|  | 826 | }, | 
|  | 827 | }; | 
|  | 828 |  | 
|  | 829 | static struct genl_family fou_nl_family __ro_after_init = { | 
|  | 830 | .hdrsize	= 0, | 
|  | 831 | .name		= FOU_GENL_NAME, | 
|  | 832 | .version	= FOU_GENL_VERSION, | 
|  | 833 | .maxattr	= FOU_ATTR_MAX, | 
|  | 834 | .netnsok	= true, | 
|  | 835 | .module		= THIS_MODULE, | 
|  | 836 | .ops		= fou_nl_ops, | 
|  | 837 | .n_ops		= ARRAY_SIZE(fou_nl_ops), | 
|  | 838 | }; | 
|  | 839 |  | 
|  | 840 | size_t fou_encap_hlen(struct ip_tunnel_encap *e) | 
|  | 841 | { | 
|  | 842 | return sizeof(struct udphdr); | 
|  | 843 | } | 
|  | 844 | EXPORT_SYMBOL(fou_encap_hlen); | 
|  | 845 |  | 
|  | 846 | size_t gue_encap_hlen(struct ip_tunnel_encap *e) | 
|  | 847 | { | 
|  | 848 | size_t len; | 
|  | 849 | bool need_priv = false; | 
|  | 850 |  | 
|  | 851 | len = sizeof(struct udphdr) + sizeof(struct guehdr); | 
|  | 852 |  | 
|  | 853 | if (e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) { | 
|  | 854 | len += GUE_PLEN_REMCSUM; | 
|  | 855 | need_priv = true; | 
|  | 856 | } | 
|  | 857 |  | 
|  | 858 | len += need_priv ? GUE_LEN_PRIV : 0; | 
|  | 859 |  | 
|  | 860 | return len; | 
|  | 861 | } | 
|  | 862 | EXPORT_SYMBOL(gue_encap_hlen); | 
|  | 863 |  | 
|  | 864 | int __fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, | 
|  | 865 | u8 *protocol, __be16 *sport, int type) | 
|  | 866 | { | 
|  | 867 | int err; | 
|  | 868 |  | 
|  | 869 | err = iptunnel_handle_offloads(skb, type); | 
|  | 870 | if (err) | 
|  | 871 | return err; | 
|  | 872 |  | 
|  | 873 | *sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev), | 
|  | 874 | skb, 0, 0, false); | 
|  | 875 |  | 
|  | 876 | return 0; | 
|  | 877 | } | 
|  | 878 | EXPORT_SYMBOL(__fou_build_header); | 
|  | 879 |  | 
|  | 880 | int __gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, | 
|  | 881 | u8 *protocol, __be16 *sport, int type) | 
|  | 882 | { | 
|  | 883 | struct guehdr *guehdr; | 
|  | 884 | size_t hdrlen, optlen = 0; | 
|  | 885 | void *data; | 
|  | 886 | bool need_priv = false; | 
|  | 887 | int err; | 
|  | 888 |  | 
|  | 889 | if ((e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) && | 
|  | 890 | skb->ip_summed == CHECKSUM_PARTIAL) { | 
|  | 891 | optlen += GUE_PLEN_REMCSUM; | 
|  | 892 | type |= SKB_GSO_TUNNEL_REMCSUM; | 
|  | 893 | need_priv = true; | 
|  | 894 | } | 
|  | 895 |  | 
|  | 896 | optlen += need_priv ? GUE_LEN_PRIV : 0; | 
|  | 897 |  | 
|  | 898 | err = iptunnel_handle_offloads(skb, type); | 
|  | 899 | if (err) | 
|  | 900 | return err; | 
|  | 901 |  | 
|  | 902 | /* Get source port (based on flow hash) before skb_push */ | 
|  | 903 | *sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev), | 
|  | 904 | skb, 0, 0, false); | 
|  | 905 |  | 
|  | 906 | hdrlen = sizeof(struct guehdr) + optlen; | 
|  | 907 |  | 
|  | 908 | skb_push(skb, hdrlen); | 
|  | 909 |  | 
|  | 910 | guehdr = (struct guehdr *)skb->data; | 
|  | 911 |  | 
|  | 912 | guehdr->control = 0; | 
|  | 913 | guehdr->version = 0; | 
|  | 914 | guehdr->hlen = optlen >> 2; | 
|  | 915 | guehdr->flags = 0; | 
|  | 916 | guehdr->proto_ctype = *protocol; | 
|  | 917 |  | 
|  | 918 | data = &guehdr[1]; | 
|  | 919 |  | 
|  | 920 | if (need_priv) { | 
|  | 921 | __be32 *flags = data; | 
|  | 922 |  | 
|  | 923 | guehdr->flags |= GUE_FLAG_PRIV; | 
|  | 924 | *flags = 0; | 
|  | 925 | data += GUE_LEN_PRIV; | 
|  | 926 |  | 
|  | 927 | if (type & SKB_GSO_TUNNEL_REMCSUM) { | 
|  | 928 | u16 csum_start = skb_checksum_start_offset(skb); | 
|  | 929 | __be16 *pd = data; | 
|  | 930 |  | 
|  | 931 | if (csum_start < hdrlen) | 
|  | 932 | return -EINVAL; | 
|  | 933 |  | 
|  | 934 | csum_start -= hdrlen; | 
|  | 935 | pd[0] = htons(csum_start); | 
|  | 936 | pd[1] = htons(csum_start + skb->csum_offset); | 
|  | 937 |  | 
|  | 938 | if (!skb_is_gso(skb)) { | 
|  | 939 | skb->ip_summed = CHECKSUM_NONE; | 
|  | 940 | skb->encapsulation = 0; | 
|  | 941 | } | 
|  | 942 |  | 
|  | 943 | *flags |= GUE_PFLAG_REMCSUM; | 
|  | 944 | data += GUE_PLEN_REMCSUM; | 
|  | 945 | } | 
|  | 946 |  | 
|  | 947 | } | 
|  | 948 |  | 
|  | 949 | return 0; | 
|  | 950 | } | 
|  | 951 | EXPORT_SYMBOL(__gue_build_header); | 
|  | 952 |  | 
|  | 953 | #ifdef CONFIG_NET_FOU_IP_TUNNELS | 
|  | 954 |  | 
|  | 955 | static void fou_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e, | 
|  | 956 | struct flowi4 *fl4, u8 *protocol, __be16 sport) | 
|  | 957 | { | 
|  | 958 | struct udphdr *uh; | 
|  | 959 |  | 
|  | 960 | skb_push(skb, sizeof(struct udphdr)); | 
|  | 961 | skb_reset_transport_header(skb); | 
|  | 962 |  | 
|  | 963 | uh = udp_hdr(skb); | 
|  | 964 |  | 
|  | 965 | uh->dest = e->dport; | 
|  | 966 | uh->source = sport; | 
|  | 967 | uh->len = htons(skb->len); | 
|  | 968 | udp_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM), skb, | 
|  | 969 | fl4->saddr, fl4->daddr, skb->len); | 
|  | 970 |  | 
|  | 971 | *protocol = IPPROTO_UDP; | 
|  | 972 | } | 
|  | 973 |  | 
|  | 974 | static int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, | 
|  | 975 | u8 *protocol, struct flowi4 *fl4) | 
|  | 976 | { | 
|  | 977 | int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM ? SKB_GSO_UDP_TUNNEL_CSUM : | 
|  | 978 | SKB_GSO_UDP_TUNNEL; | 
|  | 979 | __be16 sport; | 
|  | 980 | int err; | 
|  | 981 |  | 
|  | 982 | err = __fou_build_header(skb, e, protocol, &sport, type); | 
|  | 983 | if (err) | 
|  | 984 | return err; | 
|  | 985 |  | 
|  | 986 | fou_build_udp(skb, e, fl4, protocol, sport); | 
|  | 987 |  | 
|  | 988 | return 0; | 
|  | 989 | } | 
|  | 990 |  | 
|  | 991 | static int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, | 
|  | 992 | u8 *protocol, struct flowi4 *fl4) | 
|  | 993 | { | 
|  | 994 | int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM ? SKB_GSO_UDP_TUNNEL_CSUM : | 
|  | 995 | SKB_GSO_UDP_TUNNEL; | 
|  | 996 | __be16 sport; | 
|  | 997 | int err; | 
|  | 998 |  | 
|  | 999 | err = __gue_build_header(skb, e, protocol, &sport, type); | 
|  | 1000 | if (err) | 
|  | 1001 | return err; | 
|  | 1002 |  | 
|  | 1003 | fou_build_udp(skb, e, fl4, protocol, sport); | 
|  | 1004 |  | 
|  | 1005 | return 0; | 
|  | 1006 | } | 
|  | 1007 |  | 
|  | 1008 |  | 
|  | 1009 | static const struct ip_tunnel_encap_ops fou_iptun_ops = { | 
|  | 1010 | .encap_hlen = fou_encap_hlen, | 
|  | 1011 | .build_header = fou_build_header, | 
|  | 1012 | }; | 
|  | 1013 |  | 
|  | 1014 | static const struct ip_tunnel_encap_ops gue_iptun_ops = { | 
|  | 1015 | .encap_hlen = gue_encap_hlen, | 
|  | 1016 | .build_header = gue_build_header, | 
|  | 1017 | }; | 
|  | 1018 |  | 
|  | 1019 | static int ip_tunnel_encap_add_fou_ops(void) | 
|  | 1020 | { | 
|  | 1021 | int ret; | 
|  | 1022 |  | 
|  | 1023 | ret = ip_tunnel_encap_add_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU); | 
|  | 1024 | if (ret < 0) { | 
|  | 1025 | pr_err("can't add fou ops\n"); | 
|  | 1026 | return ret; | 
|  | 1027 | } | 
|  | 1028 |  | 
|  | 1029 | ret = ip_tunnel_encap_add_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE); | 
|  | 1030 | if (ret < 0) { | 
|  | 1031 | pr_err("can't add gue ops\n"); | 
|  | 1032 | ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU); | 
|  | 1033 | return ret; | 
|  | 1034 | } | 
|  | 1035 |  | 
|  | 1036 | return 0; | 
|  | 1037 | } | 
|  | 1038 |  | 
|  | 1039 | static void ip_tunnel_encap_del_fou_ops(void) | 
|  | 1040 | { | 
|  | 1041 | ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU); | 
|  | 1042 | ip_tunnel_encap_del_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE); | 
|  | 1043 | } | 
|  | 1044 |  | 
|  | 1045 | #else | 
|  | 1046 |  | 
|  | 1047 | static int ip_tunnel_encap_add_fou_ops(void) | 
|  | 1048 | { | 
|  | 1049 | return 0; | 
|  | 1050 | } | 
|  | 1051 |  | 
|  | 1052 | static void ip_tunnel_encap_del_fou_ops(void) | 
|  | 1053 | { | 
|  | 1054 | } | 
|  | 1055 |  | 
|  | 1056 | #endif | 
|  | 1057 |  | 
|  | 1058 | static __net_init int fou_init_net(struct net *net) | 
|  | 1059 | { | 
|  | 1060 | struct fou_net *fn = net_generic(net, fou_net_id); | 
|  | 1061 |  | 
|  | 1062 | INIT_LIST_HEAD(&fn->fou_list); | 
|  | 1063 | mutex_init(&fn->fou_lock); | 
|  | 1064 | return 0; | 
|  | 1065 | } | 
|  | 1066 |  | 
|  | 1067 | static __net_exit void fou_exit_net(struct net *net) | 
|  | 1068 | { | 
|  | 1069 | struct fou_net *fn = net_generic(net, fou_net_id); | 
|  | 1070 | struct fou *fou, *next; | 
|  | 1071 |  | 
|  | 1072 | /* Close all the FOU sockets */ | 
|  | 1073 | mutex_lock(&fn->fou_lock); | 
|  | 1074 | list_for_each_entry_safe(fou, next, &fn->fou_list, list) | 
|  | 1075 | fou_release(fou); | 
|  | 1076 | mutex_unlock(&fn->fou_lock); | 
|  | 1077 | } | 
|  | 1078 |  | 
|  | 1079 | static struct pernet_operations fou_net_ops = { | 
|  | 1080 | .init = fou_init_net, | 
|  | 1081 | .exit = fou_exit_net, | 
|  | 1082 | .id   = &fou_net_id, | 
|  | 1083 | .size = sizeof(struct fou_net), | 
|  | 1084 | }; | 
|  | 1085 |  | 
|  | 1086 | static int __init fou_init(void) | 
|  | 1087 | { | 
|  | 1088 | int ret; | 
|  | 1089 |  | 
|  | 1090 | ret = register_pernet_device(&fou_net_ops); | 
|  | 1091 | if (ret) | 
|  | 1092 | goto exit; | 
|  | 1093 |  | 
|  | 1094 | ret = genl_register_family(&fou_nl_family); | 
|  | 1095 | if (ret < 0) | 
|  | 1096 | goto unregister; | 
|  | 1097 |  | 
|  | 1098 | ret = ip_tunnel_encap_add_fou_ops(); | 
|  | 1099 | if (ret == 0) | 
|  | 1100 | return 0; | 
|  | 1101 |  | 
|  | 1102 | genl_unregister_family(&fou_nl_family); | 
|  | 1103 | unregister: | 
|  | 1104 | unregister_pernet_device(&fou_net_ops); | 
|  | 1105 | exit: | 
|  | 1106 | return ret; | 
|  | 1107 | } | 
|  | 1108 |  | 
|  | 1109 | static void __exit fou_fini(void) | 
|  | 1110 | { | 
|  | 1111 | ip_tunnel_encap_del_fou_ops(); | 
|  | 1112 | genl_unregister_family(&fou_nl_family); | 
|  | 1113 | unregister_pernet_device(&fou_net_ops); | 
|  | 1114 | } | 
|  | 1115 |  | 
|  | 1116 | module_init(fou_init); | 
|  | 1117 | module_exit(fou_fini); | 
|  | 1118 | MODULE_AUTHOR("Tom Herbert <therbert@google.com>"); | 
|  | 1119 | MODULE_LICENSE("GPL"); |