diff options
Diffstat (limited to 'net/ipv4/tcp.c')
-rw-r--r-- | net/ipv4/tcp.c | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 062dd1a0d8a8..0e029c4e2903 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c | |||
@@ -258,6 +258,7 @@ | |||
258 | #include <linux/random.h> | 258 | #include <linux/random.h> |
259 | #include <linux/bootmem.h> | 259 | #include <linux/bootmem.h> |
260 | #include <linux/cache.h> | 260 | #include <linux/cache.h> |
261 | #include <linux/err.h> | ||
261 | 262 | ||
262 | #include <net/icmp.h> | 263 | #include <net/icmp.h> |
263 | #include <net/tcp.h> | 264 | #include <net/tcp.h> |
@@ -2144,6 +2145,67 @@ int compat_tcp_getsockopt(struct sock *sk, int level, int optname, | |||
2144 | EXPORT_SYMBOL(compat_tcp_getsockopt); | 2145 | EXPORT_SYMBOL(compat_tcp_getsockopt); |
2145 | #endif | 2146 | #endif |
2146 | 2147 | ||
2148 | struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg) | ||
2149 | { | ||
2150 | struct sk_buff *segs = ERR_PTR(-EINVAL); | ||
2151 | struct tcphdr *th; | ||
2152 | unsigned thlen; | ||
2153 | unsigned int seq; | ||
2154 | unsigned int delta; | ||
2155 | unsigned int oldlen; | ||
2156 | unsigned int len; | ||
2157 | |||
2158 | if (!pskb_may_pull(skb, sizeof(*th))) | ||
2159 | goto out; | ||
2160 | |||
2161 | th = skb->h.th; | ||
2162 | thlen = th->doff * 4; | ||
2163 | if (thlen < sizeof(*th)) | ||
2164 | goto out; | ||
2165 | |||
2166 | if (!pskb_may_pull(skb, thlen)) | ||
2167 | goto out; | ||
2168 | |||
2169 | oldlen = ~htonl(skb->len); | ||
2170 | __skb_pull(skb, thlen); | ||
2171 | |||
2172 | segs = skb_segment(skb, sg); | ||
2173 | if (IS_ERR(segs)) | ||
2174 | goto out; | ||
2175 | |||
2176 | len = skb_shinfo(skb)->gso_size; | ||
2177 | delta = csum_add(oldlen, htonl(thlen + len)); | ||
2178 | |||
2179 | skb = segs; | ||
2180 | th = skb->h.th; | ||
2181 | seq = ntohl(th->seq); | ||
2182 | |||
2183 | do { | ||
2184 | th->fin = th->psh = 0; | ||
2185 | |||
2186 | if (skb->ip_summed == CHECKSUM_NONE) { | ||
2187 | th->check = csum_fold(csum_partial( | ||
2188 | skb->h.raw, thlen, csum_add(skb->csum, delta))); | ||
2189 | } | ||
2190 | |||
2191 | seq += len; | ||
2192 | skb = skb->next; | ||
2193 | th = skb->h.th; | ||
2194 | |||
2195 | th->seq = htonl(seq); | ||
2196 | th->cwr = 0; | ||
2197 | } while (skb->next); | ||
2198 | |||
2199 | if (skb->ip_summed == CHECKSUM_NONE) { | ||
2200 | delta = csum_add(oldlen, htonl(skb->tail - skb->h.raw)); | ||
2201 | th->check = csum_fold(csum_partial( | ||
2202 | skb->h.raw, thlen, csum_add(skb->csum, delta))); | ||
2203 | } | ||
2204 | |||
2205 | out: | ||
2206 | return segs; | ||
2207 | } | ||
2208 | |||
2147 | extern void __skb_cb_too_small_for_tcp(int, int); | 2209 | extern void __skb_cb_too_small_for_tcp(int, int); |
2148 | extern struct tcp_congestion_ops tcp_reno; | 2210 | extern struct tcp_congestion_ops tcp_reno; |
2149 | 2211 | ||