nf_conntrack: avoid kernel pointer value leak in slab name
[pandora-kernel.git] / net / ipv4 / tcp_ipv4.c
index c34f015..b4e0eb4 100644 (file)
@@ -92,7 +92,7 @@ EXPORT_SYMBOL(sysctl_tcp_low_latency);
 static struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk,
                                                   __be32 addr);
 static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
-                              __be32 daddr, __be32 saddr, struct tcphdr *th);
+                              __be32 daddr, __be32 saddr, const struct tcphdr *th);
 #else
 static inline
 struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr)
@@ -104,7 +104,7 @@ struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr)
 struct inet_hashinfo tcp_hashinfo;
 EXPORT_SYMBOL(tcp_hashinfo);
 
-static inline __u32 tcp_v4_init_sequence(struct sk_buff *skb)
+static inline __u32 tcp_v4_init_sequence(const struct sk_buff *skb)
 {
        return secure_tcp_sequence_number(ip_hdr(skb)->daddr,
                                          ip_hdr(skb)->saddr,
@@ -182,7 +182,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
        if (IS_ERR(rt)) {
                err = PTR_ERR(rt);
                if (err == -ENETUNREACH)
-                       IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
+                       IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
                return err;
        }
 
@@ -552,7 +552,7 @@ static void __tcp_v4_send_check(struct sk_buff *skb,
 /* 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);
+       const struct inet_sock *inet = inet_sk(sk);
 
        __tcp_v4_send_check(skb, inet->inet_saddr, inet->inet_daddr);
 }
@@ -590,7 +590,7 @@ int tcp_v4_gso_send_check(struct sk_buff *skb)
 
 static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
 {
-       struct tcphdr *th = tcp_hdr(skb);
+       const struct tcphdr *th = tcp_hdr(skb);
        struct {
                struct tcphdr th;
 #ifdef CONFIG_TCP_MD5SIG
@@ -630,7 +630,7 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
        arg.iov[0].iov_len  = sizeof(rep.th);
 
 #ifdef CONFIG_TCP_MD5SIG
-       key = sk ? tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->daddr) : NULL;
+       key = sk ? tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->saddr) : NULL;
        if (key) {
                rep.opt[0] = htonl((TCPOPT_NOP << 24) |
                                   (TCPOPT_NOP << 16) |
@@ -650,8 +650,15 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
                                      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;
+       /* When socket is gone, all binding information is lost.
+        * routing might fail in this case. No choice here, if we choose to force
+        * input interface, we will misroute in case of asymmetric route.
+        */
+       if (sk)
+               arg.bound_dev_if = sk->sk_bound_dev_if;
 
        net = dev_net(skb_dst(skb)->dev);
+       arg.tos = ip_hdr(skb)->tos;
        ip_send_reply(net->ipv4.tcp_sock, skb, ip_hdr(skb)->saddr,
                      &arg, arg.iov[0].iov_len);
 
@@ -666,9 +673,9 @@ 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,
-                           int reply_flags)
+                           int reply_flags, u8 tos)
 {
-       struct tcphdr *th = tcp_hdr(skb);
+       const struct tcphdr *th = tcp_hdr(skb);
        struct {
                struct tcphdr th;
                __be32 opt[(TCPOLEN_TSTAMP_ALIGNED >> 2)
@@ -726,7 +733,7 @@ static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack,
        arg.csumoffset = offsetof(struct tcphdr, check) / 2;
        if (oif)
                arg.bound_dev_if = oif;
-
+       arg.tos = tos;
        ip_send_reply(net->ipv4.tcp_sock, skb, ip_hdr(skb)->saddr,
                      &arg, arg.iov[0].iov_len);
 
@@ -743,7 +750,8 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb)
                        tcptw->tw_ts_recent,
                        tw->tw_bound_dev_if,
                        tcp_twsk_md5_key(tcptw),
-                       tw->tw_transparent ? IP_REPLY_ARG_NOSRCCHECK : 0
+                       tw->tw_transparent ? IP_REPLY_ARG_NOSRCCHECK : 0,
+                       tw->tw_tos
                        );
 
        inet_twsk_put(tw);
@@ -757,7 +765,8 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
                        req->ts_recent,
                        0,
                        tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->daddr),
-                       inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0);
+                       inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0,
+                       ip_hdr(skb)->tos);
 }
 
 /*
@@ -927,18 +936,18 @@ int tcp_v4_md5_do_add(struct sock *sk, __be32 addr,
                        }
                        sk_nocaps_add(sk, NETIF_F_GSO_MASK);
                }
-               if (tcp_alloc_md5sig_pool(sk) == NULL) {
+
+               md5sig = tp->md5sig_info;
+               if (md5sig->entries4 == 0 && !tcp_alloc_md5sig_pool()) {
                        kfree(newkey);
                        return -ENOMEM;
                }
-               md5sig = tp->md5sig_info;
 
                if (md5sig->alloced4 == md5sig->entries4) {
                        keys = kmalloc((sizeof(*keys) *
                                        (md5sig->entries4 + 1)), GFP_ATOMIC);
                        if (!keys) {
                                kfree(newkey);
-                               tcp_free_md5sig_pool();
                                return -ENOMEM;
                        }
 
@@ -989,7 +998,6 @@ int tcp_v4_md5_do_del(struct sock *sk, __be32 addr)
                                        (tp->md5sig_info->entries4 - i) *
                                         sizeof(struct tcp4_md5sig_key));
                        }
-                       tcp_free_md5sig_pool();
                        return 0;
                }
        }
@@ -1010,7 +1018,6 @@ static void tcp_v4_clear_md5_list(struct sock *sk)
                for (i = 0; i < tp->md5sig_info->entries4; i++)
                        kfree(tp->md5sig_info->keys4[i].base.key);
                tp->md5sig_info->entries4 = 0;
-               tcp_free_md5sig_pool();
        }
        if (tp->md5sig_info->keys4) {
                kfree(tp->md5sig_info->keys4);
@@ -1087,7 +1094,7 @@ static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
 }
 
 static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
-                              __be32 daddr, __be32 saddr, struct tcphdr *th)
+                              __be32 daddr, __be32 saddr, const struct tcphdr *th)
 {
        struct tcp_md5sig_pool *hp;
        struct hash_desc *desc;
@@ -1119,12 +1126,12 @@ clear_hash_noput:
 }
 
 int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
-                       struct sock *sk, struct request_sock *req,
-                       struct sk_buff *skb)
+                       const struct sock *sk, const struct request_sock *req,
+                       const struct sk_buff *skb)
 {
        struct tcp_md5sig_pool *hp;
        struct hash_desc *desc;
-       struct tcphdr *th = tcp_hdr(skb);
+       const struct tcphdr *th = tcp_hdr(skb);
        __be32 saddr, daddr;
 
        if (sk) {
@@ -1169,7 +1176,7 @@ clear_hash_noput:
 }
 EXPORT_SYMBOL(tcp_v4_md5_hash_skb);
 
-static int tcp_v4_inbound_md5_hash(struct sock *sk, struct sk_buff *skb)
+static int tcp_v4_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb)
 {
        /*
         * This gets called for each TCP segment that arrives
@@ -1179,10 +1186,10 @@ static int tcp_v4_inbound_md5_hash(struct sock *sk, struct sk_buff *skb)
         * o MD5 hash and we're not expecting one.
         * o MD5 hash and its wrong.
         */
-       __u8 *hash_location = NULL;
+       const __u8 *hash_location = NULL;
        struct tcp_md5sig_key *hash_expected;
        const struct iphdr *iph = ip_hdr(skb);
-       struct tcphdr *th = tcp_hdr(skb);
+       const struct tcphdr *th = tcp_hdr(skb);
        int genhash;
        unsigned char newhash[16];
 
@@ -1245,7 +1252,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
 {
        struct tcp_extend_values tmp_ext;
        struct tcp_options_received tmp_opt;
-       u8 *hash_location;
+       const u8 *hash_location;
        struct request_sock *req;
        struct inet_request_sock *ireq;
        struct tcp_sock *tp = tcp_sk(sk);
@@ -1340,7 +1347,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
                goto drop_and_free;
 
        if (!want_cookie || tmp_opt.tstamp_ok)
-               TCP_ECN_create_request(req, tcp_hdr(skb));
+               TCP_ECN_create_request(req, skb);
 
        if (want_cookie) {
                isn = cookie_v4_init_sequence(sk, skb, &req->mss);
@@ -1454,9 +1461,13 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                inet_csk(newsk)->icsk_ext_hdr_len = inet_opt->opt.optlen;
        newinet->inet_id = newtp->write_seq ^ jiffies;
 
-       if (!dst && (dst = inet_csk_route_child_sock(sk, newsk, req)) == NULL)
-               goto put_and_exit;
-
+       if (!dst) {
+               dst = inet_csk_route_child_sock(sk, newsk, req);
+               if (!dst)
+                       goto put_and_exit;
+       } else {
+               /* syncookie case : see end of cookie_v4_check() */
+       }
        sk_setup_caps(newsk, dst);
 
        tcp_mtup_init(newsk);
@@ -1504,7 +1515,8 @@ exit:
        NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
        return NULL;
 put_and_exit:
-       sock_put(newsk);
+       inet_csk_prepare_forced_close(newsk);
+       tcp_done(newsk);
        goto exit;
 }
 EXPORT_SYMBOL(tcp_v4_syn_recv_sock);
@@ -1585,7 +1597,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
 #endif
 
        if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
-               sock_rps_save_rxhash(sk, skb->rxhash);
+               sock_rps_save_rxhash(sk, skb);
                if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {
                        rsk = sk;
                        goto reset;
@@ -1602,7 +1614,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
                        goto discard;
 
                if (nsk != sk) {
-                       sock_rps_save_rxhash(nsk, skb->rxhash);
+                       sock_rps_save_rxhash(nsk, skb);
                        if (tcp_child_process(sk, nsk, skb)) {
                                rsk = nsk;
                                goto reset;
@@ -1610,7 +1622,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
                        return 0;
                }
        } else
-               sock_rps_save_rxhash(sk, skb->rxhash);
+               sock_rps_save_rxhash(sk, skb);
 
        if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {
                rsk = sk;
@@ -1642,7 +1654,7 @@ EXPORT_SYMBOL(tcp_v4_do_rcv);
 int tcp_v4_rcv(struct sk_buff *skb)
 {
        const struct iphdr *iph;
-       struct tcphdr *th;
+       const struct tcphdr *th;
        struct sock *sk;
        int ret;
        struct net *net = dev_net(skb->dev);
@@ -1677,7 +1689,7 @@ int tcp_v4_rcv(struct sk_buff *skb)
                                    skb->len - th->doff * 4);
        TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
        TCP_SKB_CB(skb)->when    = 0;
-       TCP_SKB_CB(skb)->flags   = iph->tos;
+       TCP_SKB_CB(skb)->ip_dsfield = ipv4_get_dsfield(iph);
        TCP_SKB_CB(skb)->sacked  = 0;
 
        sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
@@ -1806,7 +1818,7 @@ EXPORT_SYMBOL(tcp_v4_get_peer);
 
 void *tcp_v4_tw_get_peer(struct sock *sk)
 {
-       struct inet_timewait_sock *tw = inet_twsk(sk);
+       const struct inet_timewait_sock *tw = inet_twsk(sk);
 
        return inet_getpeer_v4(tw->tw_daddr, 1);
 }
@@ -2333,7 +2345,7 @@ static void tcp_seq_stop(struct seq_file *seq, void *v)
        }
 }
 
-static int tcp_seq_open(struct inode *inode, struct file *file)
+int tcp_seq_open(struct inode *inode, struct file *file)
 {
        struct tcp_seq_afinfo *afinfo = PDE(inode)->data;
        struct tcp_iter_state *s;
@@ -2349,23 +2361,19 @@ static int tcp_seq_open(struct inode *inode, struct file *file)
        s->last_pos             = 0;
        return 0;
 }
+EXPORT_SYMBOL(tcp_seq_open);
 
 int tcp_proc_register(struct net *net, struct tcp_seq_afinfo *afinfo)
 {
        int rc = 0;
        struct proc_dir_entry *p;
 
-       afinfo->seq_fops.open           = tcp_seq_open;
-       afinfo->seq_fops.read           = seq_read;
-       afinfo->seq_fops.llseek         = seq_lseek;
-       afinfo->seq_fops.release        = seq_release_net;
-
        afinfo->seq_ops.start           = tcp_seq_start;
        afinfo->seq_ops.next            = tcp_seq_next;
        afinfo->seq_ops.stop            = tcp_seq_stop;
 
        p = proc_create_data(afinfo->name, S_IRUGO, net->proc_net,
-                            &afinfo->seq_fops, afinfo);
+                            afinfo->seq_fops, afinfo);
        if (!p)
                rc = -ENOMEM;
        return rc;
@@ -2378,7 +2386,7 @@ void tcp_proc_unregister(struct net *net, struct tcp_seq_afinfo *afinfo)
 }
 EXPORT_SYMBOL(tcp_proc_unregister);
 
-static void get_openreq4(struct sock *sk, struct request_sock *req,
+static void get_openreq4(const struct sock *sk, const struct request_sock *req,
                         struct seq_file *f, int i, int uid, int *len)
 {
        const struct inet_request_sock *ireq = inet_rsk(req);
@@ -2408,9 +2416,9 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len)
 {
        int timer_active;
        unsigned long timer_expires;
-       struct tcp_sock *tp = tcp_sk(sk);
+       const struct tcp_sock *tp = tcp_sk(sk);
        const struct inet_connection_sock *icsk = inet_csk(sk);
-       struct inet_sock *inet = inet_sk(sk);
+       const struct inet_sock *inet = inet_sk(sk);
        __be32 dest = inet->inet_daddr;
        __be32 src = inet->inet_rcv_saddr;
        __u16 destp = ntohs(inet->inet_dport);
@@ -2459,7 +2467,7 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len)
                len);
 }
 
-static void get_timewait4_sock(struct inet_timewait_sock *tw,
+static void get_timewait4_sock(const struct inet_timewait_sock *tw,
                               struct seq_file *f, int i, int *len)
 {
        __be32 dest, src;
@@ -2514,12 +2522,18 @@ out:
        return 0;
 }
 
+static const struct file_operations tcp_afinfo_seq_fops = {
+       .owner   = THIS_MODULE,
+       .open    = tcp_seq_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = seq_release_net
+};
+
 static struct tcp_seq_afinfo tcp4_seq_afinfo = {
        .name           = "tcp",
        .family         = AF_INET,
-       .seq_fops       = {
-               .owner          = THIS_MODULE,
-       },
+       .seq_fops       = &tcp_afinfo_seq_fops,
        .seq_ops        = {
                .show           = tcp4_seq_show,
        },