net: Compute protocol sequence numbers and fragment IDs using MD5, CVE-2011-3188
[linux-flexiantxendom0-natty.git] / net / ipv4 / tcp_ipv4.c
index 7f9515c..531597d 100644 (file)
@@ -72,6 +72,7 @@
 #include <net/timewait_sock.h>
 #include <net/xfrm.h>
 #include <net/netdma.h>
+#include <net/secure_seq.h>
 
 #include <linux/inet.h>
 #include <linux/ipv6.h>
@@ -84,6 +85,7 @@
 
 int sysctl_tcp_tw_reuse __read_mostly;
 int sysctl_tcp_low_latency __read_mostly;
+EXPORT_SYMBOL(sysctl_tcp_low_latency);
 
 
 #ifdef CONFIG_TCP_MD5SIG
@@ -100,6 +102,7 @@ struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr)
 #endif
 
 struct inet_hashinfo tcp_hashinfo;
+EXPORT_SYMBOL(tcp_hashinfo);
 
 static inline __u32 tcp_v4_init_sequence(struct sk_buff *skb)
 {
@@ -139,7 +142,6 @@ int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
 
        return 0;
 }
-
 EXPORT_SYMBOL_GPL(tcp_twsk_unique);
 
 /* This will initiate an outgoing connection. */
@@ -204,10 +206,12 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
                 * TIME-WAIT * and initialize rx_opt.ts_recent from it,
                 * when trying new connection.
                 */
-               if (peer != NULL &&
-                   (u32)get_seconds() - peer->tcp_ts_stamp <= TCP_PAWS_MSL) {
-                       tp->rx_opt.ts_recent_stamp = peer->tcp_ts_stamp;
-                       tp->rx_opt.ts_recent = peer->tcp_ts;
+               if (peer) {
+                       inet_peer_refcheck(peer);
+                       if ((u32)get_seconds() - peer->tcp_ts_stamp <= TCP_PAWS_MSL) {
+                               tp->rx_opt.ts_recent_stamp = peer->tcp_ts_stamp;
+                               tp->rx_opt.ts_recent = peer->tcp_ts;
+                       }
                }
        }
 
@@ -265,6 +269,7 @@ failure:
        inet->inet_dport = 0;
        return err;
 }
+EXPORT_SYMBOL(tcp_v4_connect);
 
 /*
  * This routine does path mtu discovery as defined in RFC1191.
@@ -411,6 +416,9 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
                    !icsk->icsk_backoff)
                        break;
 
+               if (sock_owned_by_user(sk))
+                       break;
+
                icsk->icsk_backoff--;
                inet_csk(sk)->icsk_rto = __tcp_set_rto(tp) <<
                                         icsk->icsk_backoff;
@@ -425,11 +433,6 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
                if (remaining) {
                        inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
                                                  remaining, TCP_RTO_MAX);
-               } else if (sock_owned_by_user(sk)) {
-                       /* RTO revert clocked out retransmission,
-                        * but socket is locked. Will defer. */
-                       inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
-                                                 HZ/20, TCP_RTO_MAX);
                } else {
                        /* RTO revert clocked out retransmission.
                         * Will retransmit now */
@@ -543,6 +546,7 @@ void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb)
 
        __tcp_v4_send_check(skb, inet->inet_saddr, inet->inet_daddr);
 }
+EXPORT_SYMBOL(tcp_v4_send_check);
 
 int tcp_v4_gso_send_check(struct sk_buff *skb)
 {
@@ -858,7 +862,6 @@ struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk,
 {
        return tcp_v4_md5_do_lookup(sk, inet_sk(addr_sk)->inet_daddr);
 }
-
 EXPORT_SYMBOL(tcp_v4_md5_lookup);
 
 static struct tcp_md5sig_key *tcp_v4_reqsk_md5_lookup(struct sock *sk,
@@ -925,7 +928,6 @@ int tcp_v4_md5_do_add(struct sock *sk, __be32 addr,
        }
        return 0;
 }
-
 EXPORT_SYMBOL(tcp_v4_md5_do_add);
 
 static int tcp_v4_md5_add_func(struct sock *sk, struct sock *addr_sk,
@@ -963,7 +965,6 @@ int tcp_v4_md5_do_del(struct sock *sk, __be32 addr)
        }
        return -ENOENT;
 }
-
 EXPORT_SYMBOL(tcp_v4_md5_do_del);
 
 static void tcp_v4_clear_md5_list(struct sock *sk)
@@ -1136,7 +1137,6 @@ clear_hash_noput:
        memset(md5_hash, 0, 16);
        return 1;
 }
-
 EXPORT_SYMBOL(tcp_v4_md5_hash_skb);
 
 static int tcp_v4_inbound_md5_hash(struct sock *sk, struct sk_buff *skb)
@@ -1211,12 +1211,6 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
 };
 #endif
 
-static struct timewait_sock_ops tcp_timewait_sock_ops = {
-       .twsk_obj_size  = sizeof(struct tcp_timewait_sock),
-       .twsk_unique    = tcp_twsk_unique,
-       .twsk_destructor= tcp_twsk_destructor,
-};
-
 int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
 {
        struct tcp_extend_values tmp_ext;
@@ -1326,14 +1320,12 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        if (security_inet_conn_request(sk, skb, req))
                goto drop_and_free;
 
-       if (!want_cookie)
+       if (!want_cookie || tmp_opt.tstamp_ok)
                TCP_ECN_create_request(req, tcp_hdr(skb));
 
        if (want_cookie) {
-#ifdef CONFIG_SYN_COOKIES
-               req->cookie_ts = tmp_opt.tstamp_ok;
-#endif
                isn = cookie_v4_init_sequence(sk, skb, &req->mss);
+               req->cookie_ts = tmp_opt.tstamp_ok;
        } else if (!isn) {
                struct inet_peer *peer = NULL;
 
@@ -1350,7 +1342,8 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
                    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) {
+                   peer->daddr.a4 == saddr) {
+                       inet_peer_refcheck(peer);
                        if ((u32)get_seconds() - peer->tcp_ts_stamp < TCP_PAWS_MSL &&
                            (s32)(peer->tcp_ts - req->ts_recent) >
                                                        TCP_PAWS_WINDOW) {
@@ -1395,6 +1388,7 @@ drop_and_free:
 drop:
        return 0;
 }
+EXPORT_SYMBOL(tcp_v4_conn_request);
 
 
 /*
@@ -1421,7 +1415,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
 
        newsk = tcp_create_openreq_child(sk, req, skb);
        if (!newsk)
-               goto exit;
+               goto exit_nonewsk;
 
        newsk->sk_gso_type = SKB_GSO_TCPV4;
        sk_setup_caps(newsk, dst);
@@ -1443,7 +1437,7 @@ 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);
+       newtp->advmss = dst_metric_advmss(dst);
        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;
@@ -1468,18 +1462,23 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        }
 #endif
 
+       if (__inet_inherit_port(sk, newsk) < 0) {
+               sock_put(newsk);
+               goto exit;
+       }
        __inet_hash_nolisten(newsk, NULL);
-       __inet_inherit_port(sk, newsk);
 
        return newsk;
 
 exit_overflow:
        NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
+exit_nonewsk:
+       dst_release(dst);
 exit:
        NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
-       dst_release(dst);
        return NULL;
 }
+EXPORT_SYMBOL(tcp_v4_syn_recv_sock);
 
 static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
 {
@@ -1609,6 +1608,7 @@ csum_err:
        TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
        goto discard;
 }
+EXPORT_SYMBOL(tcp_v4_do_rcv);
 
 /*
  *     From tcp_input.c
@@ -1758,63 +1758,40 @@ do_time_wait:
        goto discard_it;
 }
 
-/* VJ's idea. Save last timestamp seen from this destination
- * and hold it at least for normal timewait interval to use for duplicate
- * segment detection in subsequent connections, before they enter synchronized
- * state.
- */
-
-int tcp_v4_remember_stamp(struct sock *sk)
+struct inet_peer *tcp_v4_get_peer(struct sock *sk, bool *release_it)
 {
+       struct rtable *rt = (struct rtable *) __sk_dst_get(sk);
        struct inet_sock *inet = inet_sk(sk);
-       struct tcp_sock *tp = tcp_sk(sk);
-       struct rtable *rt = (struct rtable *)__sk_dst_get(sk);
-       struct inet_peer *peer = NULL;
-       int release_it = 0;
+       struct inet_peer *peer;
 
        if (!rt || rt->rt_dst != inet->inet_daddr) {
-               peer = inet_getpeer(inet->inet_daddr, 1);
-               release_it = 1;
+               peer = inet_getpeer_v4(inet->inet_daddr, 1);
+               *release_it = true;
        } else {
                if (!rt->peer)
                        rt_bind_peer(rt, 1);
                peer = rt->peer;
+               *release_it = false;
        }
 
-       if (peer) {
-               if ((s32)(peer->tcp_ts - tp->rx_opt.ts_recent) <= 0 ||
-                   ((u32)get_seconds() - peer->tcp_ts_stamp > TCP_PAWS_MSL &&
-                    peer->tcp_ts_stamp <= (u32)tp->rx_opt.ts_recent_stamp)) {
-                       peer->tcp_ts_stamp = (u32)tp->rx_opt.ts_recent_stamp;
-                       peer->tcp_ts = tp->rx_opt.ts_recent;
-               }
-               if (release_it)
-                       inet_putpeer(peer);
-               return 1;
-       }
-
-       return 0;
+       return peer;
 }
+EXPORT_SYMBOL(tcp_v4_get_peer);
 
-int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw)
+void *tcp_v4_tw_get_peer(struct sock *sk)
 {
-       struct inet_peer *peer = inet_getpeer(tw->tw_daddr, 1);
-
-       if (peer) {
-               const struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
-
-               if ((s32)(peer->tcp_ts - tcptw->tw_ts_recent) <= 0 ||
-                   ((u32)get_seconds() - peer->tcp_ts_stamp > TCP_PAWS_MSL &&
-                    peer->tcp_ts_stamp <= (u32)tcptw->tw_ts_recent_stamp)) {
-                       peer->tcp_ts_stamp = (u32)tcptw->tw_ts_recent_stamp;
-                       peer->tcp_ts       = tcptw->tw_ts_recent;
-               }
-               inet_putpeer(peer);
-               return 1;
-       }
+       struct inet_timewait_sock *tw = inet_twsk(sk);
 
-       return 0;
+       return inet_getpeer_v4(tw->tw_daddr, 1);
 }
+EXPORT_SYMBOL(tcp_v4_tw_get_peer);
+
+static struct timewait_sock_ops tcp_timewait_sock_ops = {
+       .twsk_obj_size  = sizeof(struct tcp_timewait_sock),
+       .twsk_unique    = tcp_twsk_unique,
+       .twsk_destructor= tcp_twsk_destructor,
+       .twsk_getpeer   = tcp_v4_tw_get_peer,
+};
 
 const struct inet_connection_sock_af_ops ipv4_specific = {
        .queue_xmit        = ip_queue_xmit,
@@ -1822,7 +1799,7 @@ const struct inet_connection_sock_af_ops ipv4_specific = {
        .rebuild_header    = inet_sk_rebuild_header,
        .conn_request      = tcp_v4_conn_request,
        .syn_recv_sock     = tcp_v4_syn_recv_sock,
-       .remember_stamp    = tcp_v4_remember_stamp,
+       .get_peer          = tcp_v4_get_peer,
        .net_header_len    = sizeof(struct iphdr),
        .setsockopt        = ip_setsockopt,
        .getsockopt        = ip_getsockopt,
@@ -1834,6 +1811,7 @@ const struct inet_connection_sock_af_ops ipv4_specific = {
        .compat_getsockopt = compat_ip_getsockopt,
 #endif
 };
+EXPORT_SYMBOL(ipv4_specific);
 
 #ifdef CONFIG_TCP_MD5SIG
 static const struct tcp_sock_af_ops tcp_sock_ipv4_specific = {
@@ -1962,7 +1940,6 @@ void tcp_v4_destroy_sock(struct sock *sk)
 
        percpu_counter_dec(&tcp_sockets_allocated);
 }
-
 EXPORT_SYMBOL(tcp_v4_destroy_sock);
 
 #ifdef CONFIG_PROC_FS
@@ -2018,13 +1995,12 @@ static void *listening_get_next(struct seq_file *seq, void *cur)
                                }
                                req = req->dl_next;
                        }
-                       st->offset = 0;
                        if (++st->sbucket >= icsk->icsk_accept_queue.listen_opt->nr_table_entries)
                                break;
 get_req:
                        req = icsk->icsk_accept_queue.listen_opt->syn_table[st->sbucket];
                }
-               sk        = sk_next(st->syn_wait_sk);
+               sk        = sk_nulls_next(st->syn_wait_sk);
                st->state = TCP_SEQ_STATE_LISTENING;
                read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
        } else {
@@ -2033,11 +2009,13 @@ get_req:
                if (reqsk_queue_len(&icsk->icsk_accept_queue))
                        goto start_req;
                read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
-               sk = sk_next(sk);
+               sk = sk_nulls_next(sk);
        }
 get_sk:
        sk_nulls_for_each_from(sk, node) {
-               if (sk->sk_family == st->family && net_eq(sock_net(sk), net)) {
+               if (!net_eq(sock_net(sk), net))
+                       continue;
+               if (sk->sk_family == st->family) {
                        cur = sk;
                        goto out;
                }
@@ -2366,11 +2344,13 @@ int tcp_proc_register(struct net *net, struct tcp_seq_afinfo *afinfo)
                rc = -ENOMEM;
        return rc;
 }
+EXPORT_SYMBOL(tcp_proc_register);
 
 void tcp_proc_unregister(struct net *net, struct tcp_seq_afinfo *afinfo)
 {
        proc_net_remove(net, afinfo->name);
 }
+EXPORT_SYMBOL(tcp_proc_unregister);
 
 static void get_openreq4(struct sock *sk, struct request_sock *req,
                         struct seq_file *f, int i, int uid, int *len)
@@ -2379,7 +2359,7 @@ static void get_openreq4(struct sock *sk, struct request_sock *req,
        int ttd = req->expires - jiffies;
 
        seq_printf(f, "%4d: %08X:%04X %08X:%04X"
-               " %02X %08X:%08X %02X:%08lX %08X %5d %8d %u %d %p%n",
+               " %02X %08X:%08X %02X:%08lX %08X %5d %8d %u %d %pK%n",
                i,
                ireq->loc_addr,
                ntohs(inet_sk(sk)->inet_sport),
@@ -2434,7 +2414,7 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len)
                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",
+                       "%08X %5d %8d %lu %d %pK %lu %lu %u %u %d%n",
                i, src, srcp, dest, destp, sk->sk_state,
                tp->write_seq - tp->snd_una,
                rx_queue,
@@ -2469,7 +2449,7 @@ static void get_timewait4_sock(struct inet_timewait_sock *tw,
        srcp  = ntohs(tw->tw_sport);
 
        seq_printf(f, "%4d: %08X:%04X %08X:%04X"
-               " %02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %p%n",
+               " %02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %pK%n",
                i, src, srcp, dest, destp, tw->tw_substate, 0, 0,
                3, jiffies_to_clock_t(ttd), 0, 0, 0, 0,
                atomic_read(&tw->tw_refcnt), tw, len);
@@ -2565,7 +2545,6 @@ struct sk_buff **tcp4_gro_receive(struct sk_buff **head, struct sk_buff *skb)
 
        return tcp_gro_receive(head, skb);
 }
-EXPORT_SYMBOL(tcp4_gro_receive);
 
 int tcp4_gro_complete(struct sk_buff *skb)
 {
@@ -2578,7 +2557,6 @@ int tcp4_gro_complete(struct sk_buff *skb)
 
        return tcp_gro_complete(skb);
 }
-EXPORT_SYMBOL(tcp4_gro_complete);
 
 struct proto tcp_prot = {
        .name                   = "TCP",
@@ -2594,6 +2572,8 @@ struct proto tcp_prot = {
        .setsockopt             = tcp_setsockopt,
        .getsockopt             = tcp_getsockopt,
        .recvmsg                = tcp_recvmsg,
+       .sendmsg                = tcp_sendmsg,
+       .sendpage               = tcp_sendpage,
        .backlog_rcv            = tcp_v4_do_rcv,
        .hash                   = inet_hash,
        .unhash                 = inet_unhash,
@@ -2612,11 +2592,13 @@ struct proto tcp_prot = {
        .twsk_prot              = &tcp_timewait_sock_ops,
        .rsk_prot               = &tcp_request_sock_ops,
        .h.hashinfo             = &tcp_hashinfo,
+       .no_autobind            = true,
 #ifdef CONFIG_COMPAT
        .compat_setsockopt      = compat_tcp_setsockopt,
        .compat_getsockopt      = compat_tcp_getsockopt,
 #endif
 };
+EXPORT_SYMBOL(tcp_prot);
 
 
 static int __net_init tcp_sk_init(struct net *net)
@@ -2647,20 +2629,3 @@ void __init tcp_v4_init(void)
        if (register_pernet_subsys(&tcp_sk_ops))
                panic("Failed to create the TCP control socket.\n");
 }
-
-EXPORT_SYMBOL(ipv4_specific);
-EXPORT_SYMBOL(tcp_hashinfo);
-EXPORT_SYMBOL(tcp_prot);
-EXPORT_SYMBOL(tcp_v4_conn_request);
-EXPORT_SYMBOL(tcp_v4_connect);
-EXPORT_SYMBOL(tcp_v4_do_rcv);
-EXPORT_SYMBOL(tcp_v4_remember_stamp);
-EXPORT_SYMBOL(tcp_v4_send_check);
-EXPORT_SYMBOL(tcp_v4_syn_recv_sock);
-
-#ifdef CONFIG_PROC_FS
-EXPORT_SYMBOL(tcp_proc_register);
-EXPORT_SYMBOL(tcp_proc_unregister);
-#endif
-EXPORT_SYMBOL(sysctl_tcp_low_latency);
-