Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux...
[cascardo/linux.git] / net / ipv4 / fou.c
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/ip.h>
12 #include <net/protocol.h>
13 #include <net/udp.h>
14 #include <net/udp_tunnel.h>
15 #include <net/xfrm.h>
16 #include <uapi/linux/fou.h>
17 #include <uapi/linux/genetlink.h>
18
19 static DEFINE_SPINLOCK(fou_lock);
20 static LIST_HEAD(fou_list);
21
22 struct fou {
23         struct socket *sock;
24         u8 protocol;
25         u16 port;
26         struct udp_offload udp_offloads;
27         struct list_head list;
28 };
29
30 struct fou_cfg {
31         u16 type;
32         u8 protocol;
33         struct udp_port_cfg udp_config;
34 };
35
36 static inline struct fou *fou_from_sock(struct sock *sk)
37 {
38         return sk->sk_user_data;
39 }
40
41 static void fou_recv_pull(struct sk_buff *skb, size_t len)
42 {
43         struct iphdr *iph = ip_hdr(skb);
44
45         /* Remove 'len' bytes from the packet (UDP header and
46          * FOU header if present).
47          */
48         iph->tot_len = htons(ntohs(iph->tot_len) - len);
49         __skb_pull(skb, len);
50         skb_postpull_rcsum(skb, udp_hdr(skb), len);
51         skb_reset_transport_header(skb);
52 }
53
54 static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
55 {
56         struct fou *fou = fou_from_sock(sk);
57
58         if (!fou)
59                 return 1;
60
61         fou_recv_pull(skb, sizeof(struct udphdr));
62
63         return -fou->protocol;
64 }
65
66 static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr,
67                                   void *data, size_t hdrlen, u8 ipproto)
68 {
69         __be16 *pd = data;
70         size_t start = ntohs(pd[0]);
71         size_t offset = ntohs(pd[1]);
72         size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start);
73         __wsum delta;
74
75         if (skb->remcsum_offload) {
76                 /* Already processed in GRO path */
77                 skb->remcsum_offload = 0;
78                 return guehdr;
79         }
80
81         if (!pskb_may_pull(skb, plen))
82                 return NULL;
83         guehdr = (struct guehdr *)&udp_hdr(skb)[1];
84
85         if (unlikely(skb->ip_summed != CHECKSUM_COMPLETE))
86                 __skb_checksum_complete(skb);
87
88         delta = remcsum_adjust((void *)guehdr + hdrlen,
89                                skb->csum, start, offset);
90
91         /* Adjust skb->csum since we changed the packet */
92         skb->csum = csum_add(skb->csum, delta);
93
94         return guehdr;
95 }
96
97 static int gue_control_message(struct sk_buff *skb, struct guehdr *guehdr)
98 {
99         /* No support yet */
100         kfree_skb(skb);
101         return 0;
102 }
103
104 static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
105 {
106         struct fou *fou = fou_from_sock(sk);
107         size_t len, optlen, hdrlen;
108         struct guehdr *guehdr;
109         void *data;
110         u16 doffset = 0;
111
112         if (!fou)
113                 return 1;
114
115         len = sizeof(struct udphdr) + sizeof(struct guehdr);
116         if (!pskb_may_pull(skb, len))
117                 goto drop;
118
119         guehdr = (struct guehdr *)&udp_hdr(skb)[1];
120
121         optlen = guehdr->hlen << 2;
122         len += optlen;
123
124         if (!pskb_may_pull(skb, len))
125                 goto drop;
126
127         /* guehdr may change after pull */
128         guehdr = (struct guehdr *)&udp_hdr(skb)[1];
129
130         hdrlen = sizeof(struct guehdr) + optlen;
131
132         if (guehdr->version != 0 || validate_gue_flags(guehdr, optlen))
133                 goto drop;
134
135         hdrlen = sizeof(struct guehdr) + optlen;
136
137         ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len);
138
139         /* Pull csum through the guehdr now . This can be used if
140          * there is a remote checksum offload.
141          */
142         skb_postpull_rcsum(skb, udp_hdr(skb), len);
143
144         data = &guehdr[1];
145
146         if (guehdr->flags & GUE_FLAG_PRIV) {
147                 __be32 flags = *(__be32 *)(data + doffset);
148
149                 doffset += GUE_LEN_PRIV;
150
151                 if (flags & GUE_PFLAG_REMCSUM) {
152                         guehdr = gue_remcsum(skb, guehdr, data + doffset,
153                                              hdrlen, guehdr->proto_ctype);
154                         if (!guehdr)
155                                 goto drop;
156
157                         data = &guehdr[1];
158
159                         doffset += GUE_PLEN_REMCSUM;
160                 }
161         }
162
163         if (unlikely(guehdr->control))
164                 return gue_control_message(skb, guehdr);
165
166         __skb_pull(skb, sizeof(struct udphdr) + hdrlen);
167         skb_reset_transport_header(skb);
168
169         return -guehdr->proto_ctype;
170
171 drop:
172         kfree_skb(skb);
173         return 0;
174 }
175
176 static struct sk_buff **fou_gro_receive(struct sk_buff **head,
177                                         struct sk_buff *skb)
178 {
179         const struct net_offload *ops;
180         struct sk_buff **pp = NULL;
181         u8 proto = NAPI_GRO_CB(skb)->proto;
182         const struct net_offload **offloads;
183
184         rcu_read_lock();
185         offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
186         ops = rcu_dereference(offloads[proto]);
187         if (!ops || !ops->callbacks.gro_receive)
188                 goto out_unlock;
189
190         pp = ops->callbacks.gro_receive(head, skb);
191
192 out_unlock:
193         rcu_read_unlock();
194
195         return pp;
196 }
197
198 static int fou_gro_complete(struct sk_buff *skb, int nhoff)
199 {
200         const struct net_offload *ops;
201         u8 proto = NAPI_GRO_CB(skb)->proto;
202         int err = -ENOSYS;
203         const struct net_offload **offloads;
204
205         udp_tunnel_gro_complete(skb, nhoff);
206
207         rcu_read_lock();
208         offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
209         ops = rcu_dereference(offloads[proto]);
210         if (WARN_ON(!ops || !ops->callbacks.gro_complete))
211                 goto out_unlock;
212
213         err = ops->callbacks.gro_complete(skb, nhoff);
214
215 out_unlock:
216         rcu_read_unlock();
217
218         return err;
219 }
220
221 static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
222                                       struct guehdr *guehdr, void *data,
223                                       size_t hdrlen, u8 ipproto)
224 {
225         __be16 *pd = data;
226         size_t start = ntohs(pd[0]);
227         size_t offset = ntohs(pd[1]);
228         size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start);
229         __wsum delta;
230
231         if (skb->remcsum_offload)
232                 return guehdr;
233
234         if (!NAPI_GRO_CB(skb)->csum_valid)
235                 return NULL;
236
237         /* Pull checksum that will be written */
238         if (skb_gro_header_hard(skb, off + plen)) {
239                 guehdr = skb_gro_header_slow(skb, off + plen, off);
240                 if (!guehdr)
241                         return NULL;
242         }
243
244         delta = remcsum_adjust((void *)guehdr + hdrlen,
245                                NAPI_GRO_CB(skb)->csum, start, offset);
246
247         /* Adjust skb->csum since we changed the packet */
248         skb->csum = csum_add(skb->csum, delta);
249         NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta);
250
251         skb->remcsum_offload = 1;
252
253         return guehdr;
254 }
255
256 static struct sk_buff **gue_gro_receive(struct sk_buff **head,
257                                         struct sk_buff *skb)
258 {
259         const struct net_offload **offloads;
260         const struct net_offload *ops;
261         struct sk_buff **pp = NULL;
262         struct sk_buff *p;
263         struct guehdr *guehdr;
264         size_t len, optlen, hdrlen, off;
265         void *data;
266         u16 doffset = 0;
267         int flush = 1;
268
269         off = skb_gro_offset(skb);
270         len = off + sizeof(*guehdr);
271
272         guehdr = skb_gro_header_fast(skb, off);
273         if (skb_gro_header_hard(skb, len)) {
274                 guehdr = skb_gro_header_slow(skb, len, off);
275                 if (unlikely(!guehdr))
276                         goto out;
277         }
278
279         optlen = guehdr->hlen << 2;
280         len += optlen;
281
282         if (skb_gro_header_hard(skb, len)) {
283                 guehdr = skb_gro_header_slow(skb, len, off);
284                 if (unlikely(!guehdr))
285                         goto out;
286         }
287
288         if (unlikely(guehdr->control) || guehdr->version != 0 ||
289             validate_gue_flags(guehdr, optlen))
290                 goto out;
291
292         hdrlen = sizeof(*guehdr) + optlen;
293
294         /* Adjust NAPI_GRO_CB(skb)->csum to account for guehdr,
295          * this is needed if there is a remote checkcsum offload.
296          */
297         skb_gro_postpull_rcsum(skb, guehdr, hdrlen);
298
299         data = &guehdr[1];
300
301         if (guehdr->flags & GUE_FLAG_PRIV) {
302                 __be32 flags = *(__be32 *)(data + doffset);
303
304                 doffset += GUE_LEN_PRIV;
305
306                 if (flags & GUE_PFLAG_REMCSUM) {
307                         guehdr = gue_gro_remcsum(skb, off, guehdr,
308                                                  data + doffset, hdrlen,
309                                                  guehdr->proto_ctype);
310                         if (!guehdr)
311                                 goto out;
312
313                         data = &guehdr[1];
314
315                         doffset += GUE_PLEN_REMCSUM;
316                 }
317         }
318
319         skb_gro_pull(skb, hdrlen);
320
321         flush = 0;
322
323         for (p = *head; p; p = p->next) {
324                 const struct guehdr *guehdr2;
325
326                 if (!NAPI_GRO_CB(p)->same_flow)
327                         continue;
328
329                 guehdr2 = (struct guehdr *)(p->data + off);
330
331                 /* Compare base GUE header to be equal (covers
332                  * hlen, version, proto_ctype, and flags.
333                  */
334                 if (guehdr->word != guehdr2->word) {
335                         NAPI_GRO_CB(p)->same_flow = 0;
336                         continue;
337                 }
338
339                 /* Compare optional fields are the same. */
340                 if (guehdr->hlen && memcmp(&guehdr[1], &guehdr2[1],
341                                            guehdr->hlen << 2)) {
342                         NAPI_GRO_CB(p)->same_flow = 0;
343                         continue;
344                 }
345         }
346
347         rcu_read_lock();
348         offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
349         ops = rcu_dereference(offloads[guehdr->proto_ctype]);
350         if (WARN_ON(!ops || !ops->callbacks.gro_receive))
351                 goto out_unlock;
352
353         pp = ops->callbacks.gro_receive(head, skb);
354
355 out_unlock:
356         rcu_read_unlock();
357 out:
358         NAPI_GRO_CB(skb)->flush |= flush;
359
360         return pp;
361 }
362
363 static int gue_gro_complete(struct sk_buff *skb, int nhoff)
364 {
365         const struct net_offload **offloads;
366         struct guehdr *guehdr = (struct guehdr *)(skb->data + nhoff);
367         const struct net_offload *ops;
368         unsigned int guehlen;
369         u8 proto;
370         int err = -ENOENT;
371
372         proto = guehdr->proto_ctype;
373
374         guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
375
376         rcu_read_lock();
377         offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
378         ops = rcu_dereference(offloads[proto]);
379         if (WARN_ON(!ops || !ops->callbacks.gro_complete))
380                 goto out_unlock;
381
382         err = ops->callbacks.gro_complete(skb, nhoff + guehlen);
383
384 out_unlock:
385         rcu_read_unlock();
386         return err;
387 }
388
389 static int fou_add_to_port_list(struct fou *fou)
390 {
391         struct fou *fout;
392
393         spin_lock(&fou_lock);
394         list_for_each_entry(fout, &fou_list, list) {
395                 if (fou->port == fout->port) {
396                         spin_unlock(&fou_lock);
397                         return -EALREADY;
398                 }
399         }
400
401         list_add(&fou->list, &fou_list);
402         spin_unlock(&fou_lock);
403
404         return 0;
405 }
406
407 static void fou_release(struct fou *fou)
408 {
409         struct socket *sock = fou->sock;
410         struct sock *sk = sock->sk;
411
412         udp_del_offload(&fou->udp_offloads);
413
414         list_del(&fou->list);
415
416         /* Remove hooks into tunnel socket */
417         sk->sk_user_data = NULL;
418
419         sock_release(sock);
420
421         kfree(fou);
422 }
423
424 static int fou_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg)
425 {
426         udp_sk(sk)->encap_rcv = fou_udp_recv;
427         fou->protocol = cfg->protocol;
428         fou->udp_offloads.callbacks.gro_receive = fou_gro_receive;
429         fou->udp_offloads.callbacks.gro_complete = fou_gro_complete;
430         fou->udp_offloads.port = cfg->udp_config.local_udp_port;
431         fou->udp_offloads.ipproto = cfg->protocol;
432
433         return 0;
434 }
435
436 static int gue_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg)
437 {
438         udp_sk(sk)->encap_rcv = gue_udp_recv;
439         fou->udp_offloads.callbacks.gro_receive = gue_gro_receive;
440         fou->udp_offloads.callbacks.gro_complete = gue_gro_complete;
441         fou->udp_offloads.port = cfg->udp_config.local_udp_port;
442
443         return 0;
444 }
445
446 static int fou_create(struct net *net, struct fou_cfg *cfg,
447                       struct socket **sockp)
448 {
449         struct fou *fou = NULL;
450         int err;
451         struct socket *sock = NULL;
452         struct sock *sk;
453
454         /* Open UDP socket */
455         err = udp_sock_create(net, &cfg->udp_config, &sock);
456         if (err < 0)
457                 goto error;
458
459         /* Allocate FOU port structure */
460         fou = kzalloc(sizeof(*fou), GFP_KERNEL);
461         if (!fou) {
462                 err = -ENOMEM;
463                 goto error;
464         }
465
466         sk = sock->sk;
467
468         fou->port = cfg->udp_config.local_udp_port;
469
470         /* Initial for fou type */
471         switch (cfg->type) {
472         case FOU_ENCAP_DIRECT:
473                 err = fou_encap_init(sk, fou, cfg);
474                 if (err)
475                         goto error;
476                 break;
477         case FOU_ENCAP_GUE:
478                 err = gue_encap_init(sk, fou, cfg);
479                 if (err)
480                         goto error;
481                 break;
482         default:
483                 err = -EINVAL;
484                 goto error;
485         }
486
487         udp_sk(sk)->encap_type = 1;
488         udp_encap_enable();
489
490         sk->sk_user_data = fou;
491         fou->sock = sock;
492
493         udp_set_convert_csum(sk, true);
494
495         sk->sk_allocation = GFP_ATOMIC;
496
497         if (cfg->udp_config.family == AF_INET) {
498                 err = udp_add_offload(&fou->udp_offloads);
499                 if (err)
500                         goto error;
501         }
502
503         err = fou_add_to_port_list(fou);
504         if (err)
505                 goto error;
506
507         if (sockp)
508                 *sockp = sock;
509
510         return 0;
511
512 error:
513         kfree(fou);
514         if (sock)
515                 sock_release(sock);
516
517         return err;
518 }
519
520 static int fou_destroy(struct net *net, struct fou_cfg *cfg)
521 {
522         struct fou *fou;
523         u16 port = cfg->udp_config.local_udp_port;
524         int err = -EINVAL;
525
526         spin_lock(&fou_lock);
527         list_for_each_entry(fou, &fou_list, list) {
528                 if (fou->port == port) {
529                         udp_del_offload(&fou->udp_offloads);
530                         fou_release(fou);
531                         err = 0;
532                         break;
533                 }
534         }
535         spin_unlock(&fou_lock);
536
537         return err;
538 }
539
540 static struct genl_family fou_nl_family = {
541         .id             = GENL_ID_GENERATE,
542         .hdrsize        = 0,
543         .name           = FOU_GENL_NAME,
544         .version        = FOU_GENL_VERSION,
545         .maxattr        = FOU_ATTR_MAX,
546         .netnsok        = true,
547 };
548
549 static struct nla_policy fou_nl_policy[FOU_ATTR_MAX + 1] = {
550         [FOU_ATTR_PORT] = { .type = NLA_U16, },
551         [FOU_ATTR_AF] = { .type = NLA_U8, },
552         [FOU_ATTR_IPPROTO] = { .type = NLA_U8, },
553         [FOU_ATTR_TYPE] = { .type = NLA_U8, },
554 };
555
556 static int parse_nl_config(struct genl_info *info,
557                            struct fou_cfg *cfg)
558 {
559         memset(cfg, 0, sizeof(*cfg));
560
561         cfg->udp_config.family = AF_INET;
562
563         if (info->attrs[FOU_ATTR_AF]) {
564                 u8 family = nla_get_u8(info->attrs[FOU_ATTR_AF]);
565
566                 if (family != AF_INET && family != AF_INET6)
567                         return -EINVAL;
568
569                 cfg->udp_config.family = family;
570         }
571
572         if (info->attrs[FOU_ATTR_PORT]) {
573                 u16 port = nla_get_u16(info->attrs[FOU_ATTR_PORT]);
574
575                 cfg->udp_config.local_udp_port = port;
576         }
577
578         if (info->attrs[FOU_ATTR_IPPROTO])
579                 cfg->protocol = nla_get_u8(info->attrs[FOU_ATTR_IPPROTO]);
580
581         if (info->attrs[FOU_ATTR_TYPE])
582                 cfg->type = nla_get_u8(info->attrs[FOU_ATTR_TYPE]);
583
584         return 0;
585 }
586
587 static int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info)
588 {
589         struct fou_cfg cfg;
590         int err;
591
592         err = parse_nl_config(info, &cfg);
593         if (err)
594                 return err;
595
596         return fou_create(&init_net, &cfg, NULL);
597 }
598
599 static int fou_nl_cmd_rm_port(struct sk_buff *skb, struct genl_info *info)
600 {
601         struct fou_cfg cfg;
602
603         parse_nl_config(info, &cfg);
604
605         return fou_destroy(&init_net, &cfg);
606 }
607
608 static const struct genl_ops fou_nl_ops[] = {
609         {
610                 .cmd = FOU_CMD_ADD,
611                 .doit = fou_nl_cmd_add_port,
612                 .policy = fou_nl_policy,
613                 .flags = GENL_ADMIN_PERM,
614         },
615         {
616                 .cmd = FOU_CMD_DEL,
617                 .doit = fou_nl_cmd_rm_port,
618                 .policy = fou_nl_policy,
619                 .flags = GENL_ADMIN_PERM,
620         },
621 };
622
623 size_t fou_encap_hlen(struct ip_tunnel_encap *e)
624 {
625         return sizeof(struct udphdr);
626 }
627 EXPORT_SYMBOL(fou_encap_hlen);
628
629 size_t gue_encap_hlen(struct ip_tunnel_encap *e)
630 {
631         size_t len;
632         bool need_priv = false;
633
634         len = sizeof(struct udphdr) + sizeof(struct guehdr);
635
636         if (e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) {
637                 len += GUE_PLEN_REMCSUM;
638                 need_priv = true;
639         }
640
641         len += need_priv ? GUE_LEN_PRIV : 0;
642
643         return len;
644 }
645 EXPORT_SYMBOL(gue_encap_hlen);
646
647 static void fou_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e,
648                           struct flowi4 *fl4, u8 *protocol, __be16 sport)
649 {
650         struct udphdr *uh;
651
652         skb_push(skb, sizeof(struct udphdr));
653         skb_reset_transport_header(skb);
654
655         uh = udp_hdr(skb);
656
657         uh->dest = e->dport;
658         uh->source = sport;
659         uh->len = htons(skb->len);
660         uh->check = 0;
661         udp_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM), skb,
662                      fl4->saddr, fl4->daddr, skb->len);
663
664         *protocol = IPPROTO_UDP;
665 }
666
667 int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
668                      u8 *protocol, struct flowi4 *fl4)
669 {
670         bool csum = !!(e->flags & TUNNEL_ENCAP_FLAG_CSUM);
671         int type = csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
672         __be16 sport;
673
674         skb = iptunnel_handle_offloads(skb, csum, type);
675
676         if (IS_ERR(skb))
677                 return PTR_ERR(skb);
678
679         sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
680                                                skb, 0, 0, false);
681         fou_build_udp(skb, e, fl4, protocol, sport);
682
683         return 0;
684 }
685 EXPORT_SYMBOL(fou_build_header);
686
687 int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
688                      u8 *protocol, struct flowi4 *fl4)
689 {
690         bool csum = !!(e->flags & TUNNEL_ENCAP_FLAG_CSUM);
691         int type = csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
692         struct guehdr *guehdr;
693         size_t hdrlen, optlen = 0;
694         __be16 sport;
695         void *data;
696         bool need_priv = false;
697
698         if ((e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) &&
699             skb->ip_summed == CHECKSUM_PARTIAL) {
700                 csum = false;
701                 optlen += GUE_PLEN_REMCSUM;
702                 type |= SKB_GSO_TUNNEL_REMCSUM;
703                 need_priv = true;
704         }
705
706         optlen += need_priv ? GUE_LEN_PRIV : 0;
707
708         skb = iptunnel_handle_offloads(skb, csum, type);
709
710         if (IS_ERR(skb))
711                 return PTR_ERR(skb);
712
713         /* Get source port (based on flow hash) before skb_push */
714         sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
715                                                skb, 0, 0, false);
716
717         hdrlen = sizeof(struct guehdr) + optlen;
718
719         skb_push(skb, hdrlen);
720
721         guehdr = (struct guehdr *)skb->data;
722
723         guehdr->control = 0;
724         guehdr->version = 0;
725         guehdr->hlen = optlen >> 2;
726         guehdr->flags = 0;
727         guehdr->proto_ctype = *protocol;
728
729         data = &guehdr[1];
730
731         if (need_priv) {
732                 __be32 *flags = data;
733
734                 guehdr->flags |= GUE_FLAG_PRIV;
735                 *flags = 0;
736                 data += GUE_LEN_PRIV;
737
738                 if (type & SKB_GSO_TUNNEL_REMCSUM) {
739                         u16 csum_start = skb_checksum_start_offset(skb);
740                         __be16 *pd = data;
741
742                         if (csum_start < hdrlen)
743                                 return -EINVAL;
744
745                         csum_start -= hdrlen;
746                         pd[0] = htons(csum_start);
747                         pd[1] = htons(csum_start + skb->csum_offset);
748
749                         if (!skb_is_gso(skb)) {
750                                 skb->ip_summed = CHECKSUM_NONE;
751                                 skb->encapsulation = 0;
752                         }
753
754                         *flags |= GUE_PFLAG_REMCSUM;
755                         data += GUE_PLEN_REMCSUM;
756                 }
757
758         }
759
760         fou_build_udp(skb, e, fl4, protocol, sport);
761
762         return 0;
763 }
764 EXPORT_SYMBOL(gue_build_header);
765
766 #ifdef CONFIG_NET_FOU_IP_TUNNELS
767
768 static const struct ip_tunnel_encap_ops __read_mostly fou_iptun_ops = {
769         .encap_hlen = fou_encap_hlen,
770         .build_header = fou_build_header,
771 };
772
773 static const struct ip_tunnel_encap_ops __read_mostly gue_iptun_ops = {
774         .encap_hlen = gue_encap_hlen,
775         .build_header = gue_build_header,
776 };
777
778 static int ip_tunnel_encap_add_fou_ops(void)
779 {
780         int ret;
781
782         ret = ip_tunnel_encap_add_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
783         if (ret < 0) {
784                 pr_err("can't add fou ops\n");
785                 return ret;
786         }
787
788         ret = ip_tunnel_encap_add_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE);
789         if (ret < 0) {
790                 pr_err("can't add gue ops\n");
791                 ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
792                 return ret;
793         }
794
795         return 0;
796 }
797
798 static void ip_tunnel_encap_del_fou_ops(void)
799 {
800         ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
801         ip_tunnel_encap_del_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE);
802 }
803
804 #else
805
806 static int ip_tunnel_encap_add_fou_ops(void)
807 {
808         return 0;
809 }
810
811 static void ip_tunnel_encap_del_fou_ops(void)
812 {
813 }
814
815 #endif
816
817 static int __init fou_init(void)
818 {
819         int ret;
820
821         ret = genl_register_family_with_ops(&fou_nl_family,
822                                             fou_nl_ops);
823
824         if (ret < 0)
825                 goto exit;
826
827         ret = ip_tunnel_encap_add_fou_ops();
828         if (ret < 0)
829                 genl_unregister_family(&fou_nl_family);
830
831 exit:
832         return ret;
833 }
834
835 static void __exit fou_fini(void)
836 {
837         struct fou *fou, *next;
838
839         ip_tunnel_encap_del_fou_ops();
840
841         genl_unregister_family(&fou_nl_family);
842
843         /* Close all the FOU sockets */
844
845         spin_lock(&fou_lock);
846         list_for_each_entry_safe(fou, next, &fou_list, list)
847                 fou_release(fou);
848         spin_unlock(&fou_lock);
849 }
850
851 module_init(fou_init);
852 module_exit(fou_fini);
853 MODULE_AUTHOR("Tom Herbert <therbert@google.com>");
854 MODULE_LICENSE("GPL");