net: check the length of the socket address passed to connect(2)
[safe/jmp/linux-2.6] / net / ipv6 / tcp_ipv6.c
index da6e244..9b6dbba 100644 (file)
@@ -96,7 +96,7 @@ static void tcp_v6_hash(struct sock *sk)
                        return;
                }
                local_bh_disable();
-               __inet6_hash(sk);
+               __inet6_hash(sk, NULL);
                local_bh_enable();
        }
 }
@@ -520,6 +520,13 @@ done:
        return err;
 }
 
+static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req,
+                            struct request_values *rvp)
+{
+       TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
+       return tcp_v6_send_synack(sk, req, rvp);
+}
+
 static inline void syn_flood_warning(struct sk_buff *skb)
 {
 #ifdef CONFIG_SYN_COOKIES
@@ -876,7 +883,7 @@ static int tcp_v6_inbound_md5_hash (struct sock *sk, struct sk_buff *skb)
 
        if (genhash || memcmp(hash_location, newhash, 16) != 0) {
                if (net_ratelimit()) {
-                       printk(KERN_INFO "MD5 Hash %s for (%pI6, %u)->(%pI6, %u)\n",
+                       printk(KERN_INFO "MD5 Hash %s for [%pI6c]:%u->[%pI6c]:%u\n",
                               genhash ? "failed" : "mismatch",
                               &ip6h->saddr, ntohs(th->source),
                               &ip6h->daddr, ntohs(th->dest));
@@ -890,10 +897,11 @@ static int tcp_v6_inbound_md5_hash (struct sock *sk, struct sk_buff *skb)
 struct request_sock_ops tcp6_request_sock_ops __read_mostly = {
        .family         =       AF_INET6,
        .obj_size       =       sizeof(struct tcp6_request_sock),
-       .rtx_syn_ack    =       tcp_v6_send_synack,
+       .rtx_syn_ack    =       tcp_v6_rtx_synack,
        .send_ack       =       tcp_v6_reqsk_send_ack,
        .destructor     =       tcp_v6_reqsk_destructor,
-       .send_reset     =       tcp_v6_send_reset
+       .send_reset     =       tcp_v6_send_reset,
+       .syn_ack_timeout =      tcp_syn_ack_timeout,
 };
 
 #ifdef CONFIG_TCP_MD5SIG
@@ -1162,12 +1170,13 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
  */
 static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
 {
+       struct tcp_extend_values tmp_ext;
        struct tcp_options_received tmp_opt;
+       u8 *hash_location;
        struct request_sock *req;
        struct inet6_request_sock *treq;
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
-       struct dst_entry *dst = __sk_dst_get(sk);
        __u32 isn = TCP_SKB_CB(skb)->when;
 #ifdef CONFIG_SYN_COOKIES
        int want_cookie = 0;
@@ -1206,8 +1215,52 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        tcp_clear_options(&tmp_opt);
        tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
        tmp_opt.user_mss = tp->rx_opt.user_mss;
+       tcp_parse_options(skb, &tmp_opt, &hash_location, 0);
+
+       if (tmp_opt.cookie_plus > 0 &&
+           tmp_opt.saw_tstamp &&
+           !tp->rx_opt.cookie_out_never &&
+           (sysctl_tcp_cookie_size > 0 ||
+            (tp->cookie_values != NULL &&
+             tp->cookie_values->cookie_desired > 0))) {
+               u8 *c;
+               u32 *d;
+               u32 *mess = &tmp_ext.cookie_bakery[COOKIE_DIGEST_WORDS];
+               int l = tmp_opt.cookie_plus - TCPOLEN_COOKIE_BASE;
+
+               if (tcp_cookie_generator(&tmp_ext.cookie_bakery[0]) != 0)
+                       goto drop_and_free;
+
+               /* Secret recipe starts with IP addresses */
+               d = &ipv6_hdr(skb)->daddr.s6_addr32[0];
+               *mess++ ^= *d++;
+               *mess++ ^= *d++;
+               *mess++ ^= *d++;
+               *mess++ ^= *d++;
+               d = &ipv6_hdr(skb)->saddr.s6_addr32[0];
+               *mess++ ^= *d++;
+               *mess++ ^= *d++;
+               *mess++ ^= *d++;
+               *mess++ ^= *d++;
+
+               /* plus variable length Initiator Cookie */
+               c = (u8 *)mess;
+               while (l-- > 0)
+                       *c++ ^= *hash_location++;
 
-       tcp_parse_options(skb, &tmp_opt, 0, dst);
+#ifdef CONFIG_SYN_COOKIES
+               want_cookie = 0;        /* not our kind of cookie */
+#endif
+               tmp_ext.cookie_out_never = 0; /* false */
+               tmp_ext.cookie_plus = tmp_opt.cookie_plus;
+       } else if (!tp->rx_opt.cookie_in_always) {
+               /* redundant indications, but ensure initialization. */
+               tmp_ext.cookie_out_never = 1; /* true */
+               tmp_ext.cookie_plus = 0;
+       } else {
+               goto drop_and_free;
+       }
+       tmp_ext.cookie_in_always = tp->rx_opt.cookie_in_always;
 
        if (want_cookie && !tmp_opt.saw_tstamp)
                tcp_clear_options(&tmp_opt);
@@ -1244,7 +1297,9 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
 
        security_inet_conn_request(sk, skb, req);
 
-       if (tcp_v6_send_synack(sk, req, NULL) || want_cookie)
+       if (tcp_v6_send_synack(sk, req,
+                              (struct request_values *)&tmp_ext) ||
+           want_cookie)
                goto drop_and_free;
 
        inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
@@ -1448,7 +1503,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        }
 #endif
 
-       __inet6_hash(newsk);
+       __inet6_hash(newsk, NULL);
        __inet_inherit_port(sk, newsk);
 
        return newsk;
@@ -1685,8 +1740,11 @@ process:
                        if (!tcp_prequeue(sk, skb))
                                ret = tcp_v6_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);
@@ -1864,6 +1922,19 @@ static int tcp_v6_init_sock(struct sock *sk)
        tp->af_specific = &tcp_sock_ipv6_specific;
 #endif
 
+       /* TCP Cookie Transactions */
+       if (sysctl_tcp_cookie_size > 0) {
+               /* Default, cookies without s_data_payload. */
+               tp->cookie_values =
+                       kzalloc(sizeof(*tp->cookie_values),
+                               sk->sk_allocation);
+               if (tp->cookie_values != NULL)
+                       kref_init(&tp->cookie_values->kref);
+       }
+       /* Presumed zeroed, in order of appearance:
+        *      cookie_in_always, cookie_out_never,
+        *      s_data_constant, s_data_in, s_data_out
+        */
        sk->sk_sndbuf = sysctl_tcp_wmem[1];
        sk->sk_rcvbuf = sysctl_tcp_rmem[1];
 
@@ -2045,7 +2116,7 @@ static struct tcp_seq_afinfo tcp6_seq_afinfo = {
        },
 };
 
-int tcp6_proc_init(struct net *net)
+int __net_init tcp6_proc_init(struct net *net)
 {
        return tcp_proc_register(net, &tcp6_seq_afinfo);
 }
@@ -2114,21 +2185,26 @@ static struct inet_protosw tcpv6_protosw = {
                                INET_PROTOSW_ICSK,
 };
 
-static int tcpv6_net_init(struct net *net)
+static int __net_init tcpv6_net_init(struct net *net)
 {
        return inet_ctl_sock_create(&net->ipv6.tcp_sk, PF_INET6,
                                    SOCK_RAW, IPPROTO_TCP, net);
 }
 
-static void tcpv6_net_exit(struct net *net)
+static void __net_exit tcpv6_net_exit(struct net *net)
 {
        inet_ctl_sock_destroy(net->ipv6.tcp_sk);
-       inet_twsk_purge(net, &tcp_hashinfo, &tcp_death_row, AF_INET6);
+}
+
+static void __net_exit tcpv6_net_exit_batch(struct list_head *net_exit_list)
+{
+       inet_twsk_purge(&tcp_hashinfo, &tcp_death_row, AF_INET6);
 }
 
 static struct pernet_operations tcpv6_net_ops = {
-       .init = tcpv6_net_init,
-       .exit = tcpv6_net_exit,
+       .init       = tcpv6_net_init,
+       .exit       = tcpv6_net_exit,
+       .exit_batch = tcpv6_net_exit_batch,
 };
 
 int __init tcpv6_init(void)