]> err.no Git - linux-2.6/blobdiff - net/ipv4/tcp.c
[netdrvr] via-velocity: misc. cleanups
[linux-2.6] / net / ipv4 / tcp.c
index 0e029c4e2903e4353fa46568bdedc599ca3e2209..f6a2d9223d07ca1503aecfdaa9124e09b8e22247 100644 (file)
  *     TCP_CLOSE               socket is finished
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/fcntl.h>
@@ -643,7 +642,7 @@ static inline int select_size(struct sock *sk, struct tcp_sock *tp)
        int tmp = tp->mss_cache;
 
        if (sk->sk_route_caps & NETIF_F_SG) {
-               if (sk->sk_route_caps & NETIF_F_TSO)
+               if (sk_can_gso(sk))
                        tmp = 0;
                else {
                        int pgbreak = SKB_MAX_HEAD(MAX_TCP_HEADER);
@@ -2145,7 +2144,7 @@ int compat_tcp_getsockopt(struct sock *sk, int level, int optname,
 EXPORT_SYMBOL(compat_tcp_getsockopt);
 #endif
 
-struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg)
+struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features)
 {
        struct sk_buff *segs = ERR_PTR(-EINVAL);
        struct tcphdr *th;
@@ -2166,15 +2165,36 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg)
        if (!pskb_may_pull(skb, thlen))
                goto out;
 
-       oldlen = ~htonl(skb->len);
+       oldlen = (u16)~skb->len;
        __skb_pull(skb, thlen);
 
-       segs = skb_segment(skb, sg);
+       if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
+               /* Packet is from an untrusted source, reset gso_segs. */
+               int type = skb_shinfo(skb)->gso_type;
+               int mss;
+
+               if (unlikely(type &
+                            ~(SKB_GSO_TCPV4 |
+                              SKB_GSO_DODGY |
+                              SKB_GSO_TCP_ECN |
+                              SKB_GSO_TCPV6 |
+                              0) ||
+                            !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))))
+                       goto out;
+
+               mss = skb_shinfo(skb)->gso_size;
+               skb_shinfo(skb)->gso_segs = (skb->len + mss - 1) / mss;
+
+               segs = NULL;
+               goto out;
+       }
+
+       segs = skb_segment(skb, features);
        if (IS_ERR(segs))
                goto out;
 
        len = skb_shinfo(skb)->gso_size;
-       delta = csum_add(oldlen, htonl(thlen + len));
+       delta = htonl(oldlen + (thlen + len));
 
        skb = segs;
        th = skb->h.th;
@@ -2183,10 +2203,10 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg)
        do {
                th->fin = th->psh = 0;
 
-               if (skb->ip_summed == CHECKSUM_NONE) {
-                       th->check = csum_fold(csum_partial(
-                               skb->h.raw, thlen, csum_add(skb->csum, delta)));
-               }
+               th->check = ~csum_fold(th->check + delta);
+               if (skb->ip_summed != CHECKSUM_HW)
+                       th->check = csum_fold(csum_partial(skb->h.raw, thlen,
+                                                          skb->csum));
 
                seq += len;
                skb = skb->next;
@@ -2196,15 +2216,16 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg)
                th->cwr = 0;
        } while (skb->next);
 
-       if (skb->ip_summed == CHECKSUM_NONE) {
-               delta = csum_add(oldlen, htonl(skb->tail - skb->h.raw));
-               th->check = csum_fold(csum_partial(
-                       skb->h.raw, thlen, csum_add(skb->csum, delta)));
-       }
+       delta = htonl(oldlen + (skb->tail - skb->h.raw) + skb->data_len);
+       th->check = ~csum_fold(th->check + delta);
+       if (skb->ip_summed != CHECKSUM_HW)
+               th->check = csum_fold(csum_partial(skb->h.raw, thlen,
+                                                  skb->csum));
 
 out:
        return segs;
 }
+EXPORT_SYMBOL(tcp_tso_segment);
 
 extern void __skb_cb_too_small_for_tcp(int, int);
 extern struct tcp_congestion_ops tcp_reno;