]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/ipv4/tcp_ipv4.c
tcp: fix length used for checksum in a reset
[linux-2.6-omap-h63xx.git] / net / ipv4 / tcp_ipv4.c
index a2b06d0cc26b70075a6cd649db7cb7c19451f76c..ba46769c6e97ead6e0c24cf127b20d72b87dc461 100644 (file)
@@ -589,8 +589,9 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
 #endif
        arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
                                      ip_hdr(skb)->saddr, /* XXX */
-                                     sizeof(struct tcphdr), IPPROTO_TCP, 0);
+                                     arg.iov[0].iov_len, IPPROTO_TCP, 0);
        arg.csumoffset = offsetof(struct tcphdr, check) / 2;
+       arg.flags = (sk && inet_sk(sk)->transparent) ? IP_REPLY_ARG_NOSRCCHECK : 0;
 
        net = dev_net(skb->dst->dev);
        ip_send_reply(net->ipv4.tcp_sock, skb,
@@ -606,7 +607,8 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
 
 static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack,
                            u32 win, u32 ts, int oif,
-                           struct tcp_md5sig_key *key)
+                           struct tcp_md5sig_key *key,
+                           int reply_flags)
 {
        struct tcphdr *th = tcp_hdr(skb);
        struct {
@@ -618,7 +620,7 @@ static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack,
                        ];
        } rep;
        struct ip_reply_arg arg;
-       struct net *net = dev_net(skb->dev);
+       struct net *net = dev_net(skb->dst->dev);
 
        memset(&rep.th, 0, sizeof(struct tcphdr));
        memset(&arg, 0, sizeof(arg));
@@ -655,10 +657,11 @@ static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack,
                rep.th.doff = arg.iov[0].iov_len/4;
 
                tcp_v4_md5_hash_hdr((__u8 *) &rep.opt[offset],
-                                   key, ip_hdr(skb)->daddr,
-                                   ip_hdr(skb)->saddr, &rep.th);
+                                   key, ip_hdr(skb)->saddr,
+                                   ip_hdr(skb)->daddr, &rep.th);
        }
 #endif
+       arg.flags = reply_flags;
        arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
                                      ip_hdr(skb)->saddr, /* XXX */
                                      arg.iov[0].iov_len, IPPROTO_TCP, 0);
@@ -681,20 +684,22 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb)
                        tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
                        tcptw->tw_ts_recent,
                        tw->tw_bound_dev_if,
-                       tcp_twsk_md5_key(tcptw)
+                       tcp_twsk_md5_key(tcptw),
+                       tw->tw_transparent ? IP_REPLY_ARG_NOSRCCHECK : 0
                        );
 
        inet_twsk_put(tw);
 }
 
-static void tcp_v4_reqsk_send_ack(struct sk_buff *skb,
+static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
                                  struct request_sock *req)
 {
        tcp_v4_send_ack(skb, tcp_rsk(req)->snt_isn + 1,
                        tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd,
                        req->ts_recent,
                        0,
-                       tcp_v4_md5_do_lookup(skb->sk, ip_hdr(skb)->daddr));
+                       tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->daddr),
+                       inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0);
 }
 
 /*
@@ -1116,18 +1121,12 @@ static int tcp_v4_inbound_md5_hash(struct sock *sk, struct sk_buff *skb)
                return 0;
 
        if (hash_expected && !hash_location) {
-               LIMIT_NETDEBUG(KERN_INFO "MD5 Hash expected but NOT found "
-                              "(" NIPQUAD_FMT ", %d)->(" NIPQUAD_FMT ", %d)\n",
-                              NIPQUAD(iph->saddr), ntohs(th->source),
-                              NIPQUAD(iph->daddr), ntohs(th->dest));
+               NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND);
                return 1;
        }
 
        if (!hash_expected && hash_location) {
-               LIMIT_NETDEBUG(KERN_INFO "MD5 Hash NOT expected but found "
-                              "(" NIPQUAD_FMT ", %d)->(" NIPQUAD_FMT ", %d)\n",
-                              NIPQUAD(iph->saddr), ntohs(th->source),
-                              NIPQUAD(iph->daddr), ntohs(th->dest));
+               NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED);
                return 1;
        }
 
@@ -1250,6 +1249,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        ireq = inet_rsk(req);
        ireq->loc_addr = daddr;
        ireq->rmt_addr = saddr;
+       ireq->no_srccheck = inet_sk(sk)->transparent;
        ireq->opt = tcp_v4_save_options(sk, skb);
        if (!want_cookie)
                TCP_ECN_create_request(req, tcp_hdr(skb));
@@ -1370,6 +1370,10 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        tcp_mtup_init(newsk);
        tcp_sync_mss(newsk, dst_mtu(dst));
        newtp->advmss = dst_metric(dst, RTAX_ADVMSS);
+       if (tcp_sk(sk)->rx_opt.user_mss &&
+           tcp_sk(sk)->rx_opt.user_mss < newtp->advmss)
+               newtp->advmss = tcp_sk(sk)->rx_opt.user_mss;
+
        tcp_initialize_rcv_mss(newsk);
 
 #ifdef CONFIG_TCP_MD5SIG
@@ -1573,8 +1577,7 @@ int tcp_v4_rcv(struct sk_buff *skb)
        TCP_SKB_CB(skb)->flags   = iph->tos;
        TCP_SKB_CB(skb)->sacked  = 0;
 
-       sk = __inet_lookup(net, &tcp_hashinfo, iph->saddr,
-                       th->source, iph->daddr, th->dest, inet_iif(skb));
+       sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
        if (!sk)
                goto no_tcp_socket;
 
@@ -1952,6 +1955,12 @@ static void *listening_get_idx(struct seq_file *seq, loff_t *pos)
        return rc;
 }
 
+static inline int empty_bucket(struct tcp_iter_state *st)
+{
+       return hlist_empty(&tcp_hashinfo.ehash[st->bucket].chain) &&
+               hlist_empty(&tcp_hashinfo.ehash[st->bucket].twchain);
+}
+
 static void *established_get_first(struct seq_file *seq)
 {
        struct tcp_iter_state* st = seq->private;
@@ -1964,6 +1973,10 @@ static void *established_get_first(struct seq_file *seq)
                struct inet_timewait_sock *tw;
                rwlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, st->bucket);
 
+               /* Lockless fast path for the common case of empty buckets */
+               if (empty_bucket(st))
+                       continue;
+
                read_lock_bh(lock);
                sk_for_each(sk, node, &tcp_hashinfo.ehash[st->bucket].chain) {
                        if (sk->sk_family != st->family ||
@@ -2014,13 +2027,15 @@ get_tw:
                read_unlock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket));
                st->state = TCP_SEQ_STATE_ESTABLISHED;
 
-               if (++st->bucket < tcp_hashinfo.ehash_size) {
-                       read_lock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket));
-                       sk = sk_head(&tcp_hashinfo.ehash[st->bucket].chain);
-               } else {
-                       cur = NULL;
-                       goto out;
-               }
+               /* Look for next non empty bucket */
+               while (++st->bucket < tcp_hashinfo.ehash_size &&
+                               empty_bucket(st))
+                       ;
+               if (st->bucket >= tcp_hashinfo.ehash_size)
+                       return NULL;
+
+               read_lock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket));
+               sk = sk_head(&tcp_hashinfo.ehash[st->bucket].chain);
        } else
                sk = sk_next(sk);
 
@@ -2382,6 +2397,7 @@ static int __net_init tcp_sk_init(struct net *net)
 static void __net_exit tcp_sk_exit(struct net *net)
 {
        inet_ctl_sock_destroy(net->ipv4.tcp_sock);
+       inet_twsk_purge(net, &tcp_hashinfo, &tcp_death_row, AF_INET);
 }
 
 static struct pernet_operations __net_initdata tcp_sk_ops = {