mmc: s3c6410: enable ADMA feature in 6410 sdhci controller
[safe/jmp/linux-2.6] / net / ipv4 / tcp_ipv4.c
index fee9aab..202cf09 100644 (file)
@@ -60,6 +60,7 @@
 #include <linux/jhash.h>
 #include <linux/init.h>
 #include <linux/times.h>
+#include <linux/slab.h>
 
 #include <net/net_namespace.h>
 #include <net/icmp.h>
@@ -370,6 +371,11 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
        if (sk->sk_state == TCP_CLOSE)
                goto out;
 
+       if (unlikely(iph->ttl < inet_sk(sk)->min_ttl)) {
+               NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP);
+               goto out;
+       }
+
        icsk = inet_csk(sk);
        tp = tcp_sk(sk);
        seq = ntohl(th->seq);
@@ -513,26 +519,31 @@ out:
        sock_put(sk);
 }
 
-/* This routine computes an IPv4 TCP checksum. */
-void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb)
+static void __tcp_v4_send_check(struct sk_buff *skb,
+                               __be32 saddr, __be32 daddr)
 {
-       struct inet_sock *inet = inet_sk(sk);
        struct tcphdr *th = tcp_hdr(skb);
 
        if (skb->ip_summed == CHECKSUM_PARTIAL) {
-               th->check = ~tcp_v4_check(len, inet->inet_saddr,
-                                         inet->inet_daddr, 0);
+               th->check = ~tcp_v4_check(skb->len, saddr, daddr, 0);
                skb->csum_start = skb_transport_header(skb) - skb->head;
                skb->csum_offset = offsetof(struct tcphdr, check);
        } else {
-               th->check = tcp_v4_check(len, inet->inet_saddr,
-                                        inet->inet_daddr,
+               th->check = tcp_v4_check(skb->len, saddr, daddr,
                                         csum_partial(th,
                                                      th->doff << 2,
                                                      skb->csum));
        }
 }
 
+/* This routine computes an IPv4 TCP checksum. */
+void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb)
+{
+       struct inet_sock *inet = inet_sk(sk);
+
+       __tcp_v4_send_check(skb, inet->inet_saddr, inet->inet_daddr);
+}
+
 int tcp_v4_gso_send_check(struct sk_buff *skb)
 {
        const struct iphdr *iph;
@@ -545,10 +556,8 @@ int tcp_v4_gso_send_check(struct sk_buff *skb)
        th = tcp_hdr(skb);
 
        th->check = 0;
-       th->check = ~tcp_v4_check(skb->len, iph->saddr, iph->daddr, 0);
-       skb->csum_start = skb_transport_header(skb) - skb->head;
-       skb->csum_offset = offsetof(struct tcphdr, check);
        skb->ip_summed = CHECKSUM_PARTIAL;
+       __tcp_v4_send_check(skb, iph->saddr, iph->daddr);
        return 0;
 }
 
@@ -742,9 +751,9 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
  *     This still operates on a request_sock only, not on a big
  *     socket.
  */
-static int __tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
-                               struct request_sock *req,
-                               struct request_values *rvp)
+static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
+                             struct request_sock *req,
+                             struct request_values *rvp)
 {
        const struct inet_request_sock *ireq = inet_rsk(req);
        int err = -1;
@@ -757,13 +766,7 @@ static int __tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
        skb = tcp_make_synack(sk, dst, req, rvp);
 
        if (skb) {
-               struct tcphdr *th = tcp_hdr(skb);
-
-               th->check = tcp_v4_check(skb->len,
-                                        ireq->loc_addr,
-                                        ireq->rmt_addr,
-                                        csum_partial(th, skb->len,
-                                                     skb->csum));
+               __tcp_v4_send_check(skb, ireq->loc_addr, ireq->rmt_addr);
 
                err = ip_build_and_send_pkt(skb, sk, ireq->loc_addr,
                                            ireq->rmt_addr,
@@ -775,10 +778,11 @@ static int __tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
        return err;
 }
 
-static int tcp_v4_send_synack(struct sock *sk, struct request_sock *req,
+static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req,
                              struct request_values *rvp)
 {
-       return __tcp_v4_send_synack(sk, NULL, req, rvp);
+       TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
+       return tcp_v4_send_synack(sk, NULL, req, rvp);
 }
 
 /*
@@ -887,7 +891,7 @@ int tcp_v4_md5_do_add(struct sock *sk, __be32 addr,
                                kfree(newkey);
                                return -ENOMEM;
                        }
-                       sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
+                       sk_nocaps_add(sk, NETIF_F_GSO_MASK);
                }
                if (tcp_alloc_md5sig_pool(sk) == NULL) {
                        kfree(newkey);
@@ -1017,7 +1021,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
                        return -EINVAL;
 
                tp->md5sig_info = p;
-               sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
+               sk_nocaps_add(sk, NETIF_F_GSO_MASK);
        }
 
        newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, sk->sk_allocation);
@@ -1192,10 +1196,11 @@ static int tcp_v4_inbound_md5_hash(struct sock *sk, struct sk_buff *skb)
 struct request_sock_ops tcp_request_sock_ops __read_mostly = {
        .family         =       PF_INET,
        .obj_size       =       sizeof(struct tcp_request_sock),
-       .rtx_syn_ack    =       tcp_v4_send_synack,
+       .rtx_syn_ack    =       tcp_v4_rtx_synack,
        .send_ack       =       tcp_v4_reqsk_send_ack,
        .destructor     =       tcp_v4_reqsk_destructor,
        .send_reset     =       tcp_v4_send_reset,
+       .syn_ack_timeout =      tcp_syn_ack_timeout,
 };
 
 #ifdef CONFIG_TCP_MD5SIG
@@ -1262,20 +1267,10 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        tcp_rsk(req)->af_specific = &tcp_request_sock_ipv4_ops;
 #endif
 
-       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);
-
-       dst = inet_csk_route_req(sk, req);
-       if(!dst)
-               goto drop_and_free;
-
        tcp_clear_options(&tmp_opt);
        tmp_opt.mss_clamp = TCP_MSS_DEFAULT;
        tmp_opt.user_mss  = tp->rx_opt.user_mss;
-       tcp_parse_options(skb, &tmp_opt, &hash_location, 0, dst);
+       tcp_parse_options(skb, &tmp_opt, &hash_location, 0);
 
        if (tmp_opt.cookie_plus > 0 &&
            tmp_opt.saw_tstamp &&
@@ -1291,8 +1286,8 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
                        goto drop_and_release;
 
                /* Secret recipe starts with IP addresses */
-               *mess++ ^= daddr;
-               *mess++ ^= saddr;
+               *mess++ ^= (__force u32)daddr;
+               *mess++ ^= (__force u32)saddr;
 
                /* plus variable length Initiator Cookie */
                c = (u8 *)mess;
@@ -1319,8 +1314,14 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
        tcp_openreq_init(req, &tmp_opt, 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 (security_inet_conn_request(sk, skb, req))
-               goto drop_and_release;
+               goto drop_and_free;
 
        if (!want_cookie)
                TCP_ECN_create_request(req, tcp_hdr(skb));
@@ -1345,6 +1346,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
                 */
                if (tmp_opt.saw_tstamp &&
                    tcp_death_row.sysctl_tw_recycle &&
+                   (dst = inet_csk_route_req(sk, req)) != NULL &&
                    (peer = rt_get_peer((struct rtable *)dst)) != NULL &&
                    peer->v4daddr == saddr) {
                        if ((u32)get_seconds() - peer->tcp_ts_stamp < TCP_PAWS_MSL &&
@@ -1376,8 +1378,8 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        }
        tcp_rsk(req)->snt_isn = isn;
 
-       if (__tcp_v4_send_synack(sk, dst, req,
-                                (struct request_values *)&tmp_ext) ||
+       if (tcp_v4_send_synack(sk, dst, req,
+                              (struct request_values *)&tmp_ext) ||
            want_cookie)
                goto drop_and_free;
 
@@ -1460,11 +1462,11 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                if (newkey != NULL)
                        tcp_v4_md5_do_add(newsk, newinet->inet_daddr,
                                          newkey, key->keylen);
-               newsk->sk_route_caps &= ~NETIF_F_GSO_MASK;
+               sk_nocaps_add(newsk, NETIF_F_GSO_MASK);
        }
 #endif
 
-       __inet_hash_nolisten(newsk);
+       __inet_hash_nolisten(newsk, NULL);
        __inet_inherit_port(sk, newsk);
 
        return newsk;
@@ -1656,6 +1658,11 @@ process:
        if (sk->sk_state == TCP_TIME_WAIT)
                goto do_time_wait;
 
+       if (unlikely(iph->ttl < inet_sk(sk)->min_ttl)) {
+               NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP);
+               goto discard_and_relse;
+       }
+
        if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
                goto discard_and_relse;
        nf_reset(skb);
@@ -1665,6 +1672,8 @@ process:
 
        skb->dev = NULL;
 
+       sock_rps_save_rxhash(sk, skb->rxhash);
+
        bh_lock_sock_nested(sk);
        ret = 0;
        if (!sock_owned_by_user(sk)) {
@@ -1680,8 +1689,11 @@ process:
                        if (!tcp_prequeue(sk, skb))
                                ret = tcp_v4_do_rcv(sk, skb);
                }
-       } else
-               sk_add_backlog(sk, skb);
+       } else if (unlikely(sk_add_backlog(sk, skb))) {
+               bh_unlock_sock(sk);
+               NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP);
+               goto discard_and_relse;
+       }
        bh_unlock_sock(sk);
 
        sock_put(sk);
@@ -2318,6 +2330,7 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len)
        __be32 src = inet->inet_rcv_saddr;
        __u16 destp = ntohs(inet->inet_dport);
        __u16 srcp = ntohs(inet->inet_sport);
+       int rx_queue;
 
        if (icsk->icsk_pending == ICSK_TIME_RETRANS) {
                timer_active    = 1;
@@ -2333,12 +2346,19 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len)
                timer_expires = jiffies;
        }
 
+       if (sk->sk_state == TCP_LISTEN)
+               rx_queue = sk->sk_ack_backlog;
+       else
+               /*
+                * because we dont lock socket, we might find a transient negative value
+                */
+               rx_queue = max_t(int, tp->rcv_nxt - tp->copied_seq, 0);
+
        seq_printf(f, "%4d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08lX "
                        "%08X %5d %8d %lu %d %p %lu %lu %u %u %d%n",
                i, src, srcp, dest, destp, sk->sk_state,
                tp->write_seq - tp->snd_una,
-               sk->sk_state == TCP_LISTEN ? sk->sk_ack_backlog :
-                                            (tp->rcv_nxt - tp->copied_seq),
+               rx_queue,
                timer_active,
                jiffies_to_clock_t(timer_expires - jiffies),
                icsk->icsk_retransmits,
@@ -2420,12 +2440,12 @@ static struct tcp_seq_afinfo tcp4_seq_afinfo = {
        },
 };
 
-static int tcp4_proc_init_net(struct net *net)
+static int __net_init tcp4_proc_init_net(struct net *net)
 {
        return tcp_proc_register(net, &tcp4_seq_afinfo);
 }
 
-static void tcp4_proc_exit_net(struct net *net)
+static void __net_exit tcp4_proc_exit_net(struct net *net)
 {
        tcp_proc_unregister(net, &tcp4_seq_afinfo);
 }