aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4
diff options
context:
space:
mode:
authorDaniel Borkmann <dborkman@redhat.com>2013-07-01 13:24:00 -0400
committerDavid S. Miller <davem@davemloft.net>2013-07-03 17:37:39 -0400
commitc50cd357887acf9fd7af3a5d492911bd825555a2 (patch)
tree8b2c00e46461e9959b4ecc339d8fcdef1ca69f03 /net/ipv4
parent419076f59fc5916bd200b45225c83437b37ab189 (diff)
net: gre: move GSO functions to gre_offload
Similarly to TCP/UDP offloading, move all related GRE functions to gre_offload.c to make things more explicit and similar to the rest of the code. Suggested-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: Daniel Borkmann <dborkman@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r--net/ipv4/Makefile1
-rw-r--r--net/ipv4/gre_demux.c (renamed from net/ipv4/gre.c)106
-rw-r--r--net/ipv4/gre_offload.c127
3 files changed, 131 insertions, 103 deletions
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 86ded0bac9c7..4b81e91c80fe 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_PROC_FS) += proc.o
19obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o 19obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o
20obj-$(CONFIG_IP_MROUTE) += ipmr.o 20obj-$(CONFIG_IP_MROUTE) += ipmr.o
21obj-$(CONFIG_NET_IPIP) += ipip.o 21obj-$(CONFIG_NET_IPIP) += ipip.o
22gre-y := gre_demux.o gre_offload.o
22obj-$(CONFIG_NET_IPGRE_DEMUX) += gre.o 23obj-$(CONFIG_NET_IPGRE_DEMUX) += gre.o
23obj-$(CONFIG_NET_IPGRE) += ip_gre.o 24obj-$(CONFIG_NET_IPGRE) += ip_gre.o
24obj-$(CONFIG_NET_IPVTI) += ip_vti.o 25obj-$(CONFIG_NET_IPVTI) += ip_vti.o
diff --git a/net/ipv4/gre.c b/net/ipv4/gre_demux.c
index ba4803e609b5..736c9fc3ef93 100644
--- a/net/ipv4/gre.c
+++ b/net/ipv4/gre_demux.c
@@ -334,113 +334,12 @@ static void gre_err(struct sk_buff *skb, u32 info)
334 rcu_read_unlock(); 334 rcu_read_unlock();
335} 335}
336 336
337static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
338 netdev_features_t features)
339{
340 struct sk_buff *segs = ERR_PTR(-EINVAL);
341 netdev_features_t enc_features;
342 int ghl = GRE_HEADER_SECTION;
343 struct gre_base_hdr *greh;
344 int mac_len = skb->mac_len;
345 __be16 protocol = skb->protocol;
346 int tnl_hlen;
347 bool csum;
348
349 if (unlikely(skb_shinfo(skb)->gso_type &
350 ~(SKB_GSO_TCPV4 |
351 SKB_GSO_TCPV6 |
352 SKB_GSO_UDP |
353 SKB_GSO_DODGY |
354 SKB_GSO_TCP_ECN |
355 SKB_GSO_GRE)))
356 goto out;
357
358 if (unlikely(!pskb_may_pull(skb, sizeof(*greh))))
359 goto out;
360
361 greh = (struct gre_base_hdr *)skb_transport_header(skb);
362
363 if (greh->flags & GRE_KEY)
364 ghl += GRE_HEADER_SECTION;
365 if (greh->flags & GRE_SEQ)
366 ghl += GRE_HEADER_SECTION;
367 if (greh->flags & GRE_CSUM) {
368 ghl += GRE_HEADER_SECTION;
369 csum = true;
370 } else
371 csum = false;
372
373 /* setup inner skb. */
374 skb->protocol = greh->protocol;
375 skb->encapsulation = 0;
376
377 if (unlikely(!pskb_may_pull(skb, ghl)))
378 goto out;
379 __skb_pull(skb, ghl);
380 skb_reset_mac_header(skb);
381 skb_set_network_header(skb, skb_inner_network_offset(skb));
382 skb->mac_len = skb_inner_network_offset(skb);
383
384 /* segment inner packet. */
385 enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
386 segs = skb_mac_gso_segment(skb, enc_features);
387 if (!segs || IS_ERR(segs))
388 goto out;
389
390 skb = segs;
391 tnl_hlen = skb_tnl_header_len(skb);
392 do {
393 __skb_push(skb, ghl);
394 if (csum) {
395 __be32 *pcsum;
396
397 if (skb_has_shared_frag(skb)) {
398 int err;
399
400 err = __skb_linearize(skb);
401 if (err) {
402 kfree_skb(segs);
403 segs = ERR_PTR(err);
404 goto out;
405 }
406 }
407
408 greh = (struct gre_base_hdr *)(skb->data);
409 pcsum = (__be32 *)(greh + 1);
410 *pcsum = 0;
411 *(__sum16 *)pcsum = csum_fold(skb_checksum(skb, 0, skb->len, 0));
412 }
413 __skb_push(skb, tnl_hlen - ghl);
414
415 skb_reset_mac_header(skb);
416 skb_set_network_header(skb, mac_len);
417 skb->mac_len = mac_len;
418 skb->protocol = protocol;
419 } while ((skb = skb->next));
420out:
421 return segs;
422}
423
424static int gre_gso_send_check(struct sk_buff *skb)
425{
426 if (!skb->encapsulation)
427 return -EINVAL;
428 return 0;
429}
430
431static const struct net_protocol net_gre_protocol = { 337static const struct net_protocol net_gre_protocol = {
432 .handler = gre_rcv, 338 .handler = gre_rcv,
433 .err_handler = gre_err, 339 .err_handler = gre_err,
434 .netns_ok = 1, 340 .netns_ok = 1,
435}; 341};
436 342
437static const struct net_offload gre_offload = {
438 .callbacks = {
439 .gso_send_check = gre_gso_send_check,
440 .gso_segment = gre_gso_segment,
441 },
442};
443
444static const struct gre_protocol ipgre_protocol = { 343static const struct gre_protocol ipgre_protocol = {
445 .handler = gre_cisco_rcv, 344 .handler = gre_cisco_rcv,
446 .err_handler = gre_cisco_err, 345 .err_handler = gre_cisco_err,
@@ -485,7 +384,7 @@ static int __init gre_init(void)
485 goto err_gre; 384 goto err_gre;
486 } 385 }
487 386
488 if (inet_add_offload(&gre_offload, IPPROTO_GRE)) { 387 if (gre_offload_init()) {
489 pr_err("can't add protocol offload\n"); 388 pr_err("can't add protocol offload\n");
490 goto err_gso; 389 goto err_gso;
491 } 390 }
@@ -501,7 +400,8 @@ err:
501 400
502static void __exit gre_exit(void) 401static void __exit gre_exit(void)
503{ 402{
504 inet_del_offload(&gre_offload, IPPROTO_GRE); 403 gre_offload_exit();
404
505 gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO); 405 gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
506 inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); 406 inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
507} 407}
diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c
new file mode 100644
index 000000000000..a9d8cd2bff4b
--- /dev/null
+++ b/net/ipv4/gre_offload.c
@@ -0,0 +1,127 @@
1/*
2 * IPV4 GSO/GRO offload support
3 * Linux INET implementation
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version
8 * 2 of the License, or (at your option) any later version.
9 *
10 * GRE GSO support
11 */
12
13#include <linux/skbuff.h>
14#include <net/protocol.h>
15#include <net/gre.h>
16
17static int gre_gso_send_check(struct sk_buff *skb)
18{
19 if (!skb->encapsulation)
20 return -EINVAL;
21 return 0;
22}
23
24static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
25 netdev_features_t features)
26{
27 struct sk_buff *segs = ERR_PTR(-EINVAL);
28 netdev_features_t enc_features;
29 int ghl = GRE_HEADER_SECTION;
30 struct gre_base_hdr *greh;
31 int mac_len = skb->mac_len;
32 __be16 protocol = skb->protocol;
33 int tnl_hlen;
34 bool csum;
35
36 if (unlikely(skb_shinfo(skb)->gso_type &
37 ~(SKB_GSO_TCPV4 |
38 SKB_GSO_TCPV6 |
39 SKB_GSO_UDP |
40 SKB_GSO_DODGY |
41 SKB_GSO_TCP_ECN |
42 SKB_GSO_GRE)))
43 goto out;
44
45 if (unlikely(!pskb_may_pull(skb, sizeof(*greh))))
46 goto out;
47
48 greh = (struct gre_base_hdr *)skb_transport_header(skb);
49
50 if (greh->flags & GRE_KEY)
51 ghl += GRE_HEADER_SECTION;
52 if (greh->flags & GRE_SEQ)
53 ghl += GRE_HEADER_SECTION;
54 if (greh->flags & GRE_CSUM) {
55 ghl += GRE_HEADER_SECTION;
56 csum = true;
57 } else
58 csum = false;
59
60 /* setup inner skb. */
61 skb->protocol = greh->protocol;
62 skb->encapsulation = 0;
63
64 if (unlikely(!pskb_may_pull(skb, ghl)))
65 goto out;
66
67 __skb_pull(skb, ghl);
68 skb_reset_mac_header(skb);
69 skb_set_network_header(skb, skb_inner_network_offset(skb));
70 skb->mac_len = skb_inner_network_offset(skb);
71
72 /* segment inner packet. */
73 enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
74 segs = skb_mac_gso_segment(skb, enc_features);
75 if (!segs || IS_ERR(segs))
76 goto out;
77
78 skb = segs;
79 tnl_hlen = skb_tnl_header_len(skb);
80 do {
81 __skb_push(skb, ghl);
82 if (csum) {
83 __be32 *pcsum;
84
85 if (skb_has_shared_frag(skb)) {
86 int err;
87
88 err = __skb_linearize(skb);
89 if (err) {
90 kfree_skb(segs);
91 segs = ERR_PTR(err);
92 goto out;
93 }
94 }
95
96 greh = (struct gre_base_hdr *)(skb->data);
97 pcsum = (__be32 *)(greh + 1);
98 *pcsum = 0;
99 *(__sum16 *)pcsum = csum_fold(skb_checksum(skb, 0, skb->len, 0));
100 }
101 __skb_push(skb, tnl_hlen - ghl);
102
103 skb_reset_mac_header(skb);
104 skb_set_network_header(skb, mac_len);
105 skb->mac_len = mac_len;
106 skb->protocol = protocol;
107 } while ((skb = skb->next));
108out:
109 return segs;
110}
111
112static const struct net_offload gre_offload = {
113 .callbacks = {
114 .gso_send_check = gre_gso_send_check,
115 .gso_segment = gre_gso_segment,
116 },
117};
118
119int __init gre_offload_init(void)
120{
121 return inet_add_offload(&gre_offload, IPPROTO_GRE);
122}
123
124void __exit gre_offload_exit(void)
125{
126 inet_del_offload(&gre_offload, IPPROTO_GRE);
127}