aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/protocol.h6
-rw-r--r--net/ipv4/tcp.c1
-rw-r--r--net/ipv6/exthdrs.c4
-rw-r--r--net/ipv6/ipv6_sockglue.c62
-rw-r--r--net/ipv6/tcp_ipv6.c1
5 files changed, 72 insertions, 2 deletions
diff --git a/include/net/protocol.h b/include/net/protocol.h
index 40b6b9c9973f..a225d6371cb1 100644
--- a/include/net/protocol.h
+++ b/include/net/protocol.h
@@ -50,11 +50,17 @@ struct inet6_protocol
50 struct inet6_skb_parm *opt, 50 struct inet6_skb_parm *opt,
51 int type, int code, int offset, 51 int type, int code, int offset,
52 __u32 info); 52 __u32 info);
53
54 struct sk_buff *(*gso_segment)(struct sk_buff *skb,
55 int features);
56
53 unsigned int flags; /* INET6_PROTO_xxx */ 57 unsigned int flags; /* INET6_PROTO_xxx */
54}; 58};
55 59
56#define INET6_PROTO_NOPOLICY 0x1 60#define INET6_PROTO_NOPOLICY 0x1
57#define INET6_PROTO_FINAL 0x2 61#define INET6_PROTO_FINAL 0x2
62/* This should be set for any extension header which is compatible with GSO. */
63#define INET6_PROTO_GSO_EXTHDR 0x4
58#endif 64#endif
59 65
60/* This is used to register socket interfaces for IP protocols. */ 66/* This is used to register socket interfaces for IP protocols. */
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 0bb0ac96d675..b7cf26e0e5c2 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2215,6 +2215,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features)
2215out: 2215out:
2216 return segs; 2216 return segs;
2217} 2217}
2218EXPORT_SYMBOL(tcp_tso_segment);
2218 2219
2219extern void __skb_cb_too_small_for_tcp(int, int); 2220extern void __skb_cb_too_small_for_tcp(int, int);
2220extern struct tcp_congestion_ops tcp_reno; 2221extern struct tcp_congestion_ops tcp_reno;
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index a18d4256372c..9d0ee7f0eeb5 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -179,7 +179,7 @@ static int ipv6_destopt_rcv(struct sk_buff **skbp)
179 179
180static struct inet6_protocol destopt_protocol = { 180static struct inet6_protocol destopt_protocol = {
181 .handler = ipv6_destopt_rcv, 181 .handler = ipv6_destopt_rcv,
182 .flags = INET6_PROTO_NOPOLICY, 182 .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
183}; 183};
184 184
185void __init ipv6_destopt_init(void) 185void __init ipv6_destopt_init(void)
@@ -340,7 +340,7 @@ looped_back:
340 340
341static struct inet6_protocol rthdr_protocol = { 341static struct inet6_protocol rthdr_protocol = {
342 .handler = ipv6_rthdr_rcv, 342 .handler = ipv6_rthdr_rcv,
343 .flags = INET6_PROTO_NOPOLICY, 343 .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
344}; 344};
345 345
346void __init ipv6_rthdr_init(void) 346void __init ipv6_rthdr_init(void)
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 4c20eeb3d568..25f8bf8ac49b 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -58,9 +58,71 @@
58 58
59DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly; 59DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly;
60 60
61static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
62{
63 struct sk_buff *segs = ERR_PTR(-EINVAL);
64 struct ipv6hdr *ipv6h;
65 struct inet6_protocol *ops;
66 int proto;
67
68 if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
69 goto out;
70
71 ipv6h = skb->nh.ipv6h;
72 proto = ipv6h->nexthdr;
73 __skb_pull(skb, sizeof(*ipv6h));
74
75 rcu_read_lock();
76 for (;;) {
77 struct ipv6_opt_hdr *opth;
78 int len;
79
80 if (proto != NEXTHDR_HOP) {
81 ops = rcu_dereference(inet6_protos[proto]);
82
83 if (unlikely(!ops))
84 goto unlock;
85
86 if (!(ops->flags & INET6_PROTO_GSO_EXTHDR))
87 break;
88 }
89
90 if (unlikely(!pskb_may_pull(skb, 8)))
91 goto unlock;
92
93 opth = (void *)skb->data;
94 len = opth->hdrlen * 8 + 8;
95
96 if (unlikely(!pskb_may_pull(skb, len)))
97 goto unlock;
98
99 proto = opth->nexthdr;
100 __skb_pull(skb, len);
101 }
102
103 skb->h.raw = skb->data;
104 if (likely(ops->gso_segment))
105 segs = ops->gso_segment(skb, features);
106
107unlock:
108 rcu_read_unlock();
109
110 if (unlikely(IS_ERR(segs)))
111 goto out;
112
113 for (skb = segs; skb; skb = skb->next) {
114 ipv6h = skb->nh.ipv6h;
115 ipv6h->payload_len = htons(skb->len - skb->mac_len);
116 }
117
118out:
119 return segs;
120}
121
61static struct packet_type ipv6_packet_type = { 122static struct packet_type ipv6_packet_type = {
62 .type = __constant_htons(ETH_P_IPV6), 123 .type = __constant_htons(ETH_P_IPV6),
63 .func = ipv6_rcv, 124 .func = ipv6_rcv,
125 .gso_segment = ipv6_gso_segment,
64}; 126};
65 127
66struct ip6_ra_chain *ip6_ra_chain; 128struct ip6_ra_chain *ip6_ra_chain;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index b36d5b2e7c30..bf7f8c26c488 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1606,6 +1606,7 @@ struct proto tcpv6_prot = {
1606static struct inet6_protocol tcpv6_protocol = { 1606static struct inet6_protocol tcpv6_protocol = {
1607 .handler = tcp_v6_rcv, 1607 .handler = tcp_v6_rcv,
1608 .err_handler = tcp_v6_err, 1608 .err_handler = tcp_v6_err,
1609 .gso_segment = tcp_tso_segment,
1609 .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, 1610 .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
1610}; 1611};
1611 1612