inet_diag: fix inet_diag_dump_icsk() timewait socket state logic
[pandora-kernel.git] / net / ipv4 / inet_diag.c
index 68e8ac5..6be5e8e 100644 (file)
@@ -108,9 +108,6 @@ static int inet_csk_diag_fill(struct sock *sk,
                       icsk->icsk_ca_ops->name);
        }
 
-       if ((ext & (1 << (INET_DIAG_TOS - 1))) && (sk->sk_family != AF_INET6))
-               RTA_PUT_U8(skb, INET_DIAG_TOS, inet->tos);
-
        r->idiag_family = sk->sk_family;
        r->idiag_state = sk->sk_state;
        r->idiag_timer = 0;
@@ -122,19 +119,30 @@ static int inet_csk_diag_fill(struct sock *sk,
 
        r->id.idiag_sport = inet->inet_sport;
        r->id.idiag_dport = inet->inet_dport;
+
+       memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src));
+       memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst));
+
        r->id.idiag_src[0] = inet->inet_rcv_saddr;
        r->id.idiag_dst[0] = inet->inet_daddr;
 
+       /* IPv6 dual-stack sockets use inet->tos for IPv4 connections,
+        * hence this needs to be included regardless of socket family.
+        */
+       if (ext & (1 << (INET_DIAG_TOS - 1)))
+               RTA_PUT_U8(skb, INET_DIAG_TOS, inet->tos);
+
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
        if (r->idiag_family == AF_INET6) {
                const struct ipv6_pinfo *np = inet6_sk(sk);
 
+               if (ext & (1 << (INET_DIAG_TCLASS - 1)))
+                       RTA_PUT_U8(skb, INET_DIAG_TCLASS, np->tclass);
+
                ipv6_addr_copy((struct in6_addr *)r->id.idiag_src,
                               &np->rcv_saddr);
                ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst,
                               &np->daddr);
-               if (ext & (1 << (INET_DIAG_TCLASS - 1)))
-                       RTA_PUT_U8(skb, INET_DIAG_TCLASS, np->tclass);
        }
 #endif
 
@@ -205,13 +213,20 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw,
 
        r->idiag_family       = tw->tw_family;
        r->idiag_retrans      = 0;
+
        r->id.idiag_if        = tw->tw_bound_dev_if;
        r->id.idiag_cookie[0] = (u32)(unsigned long)tw;
        r->id.idiag_cookie[1] = (u32)(((unsigned long)tw >> 31) >> 1);
+
        r->id.idiag_sport     = tw->tw_sport;
        r->id.idiag_dport     = tw->tw_dport;
+
+       memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src));
+       memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst));
+
        r->id.idiag_src[0]    = tw->tw_rcv_saddr;
        r->id.idiag_dst[0]    = tw->tw_daddr;
+
        r->idiag_state        = tw->tw_substate;
        r->idiag_timer        = 3;
        r->idiag_expires      = DIV_ROUND_UP(tmo * 1000, HZ);
@@ -594,8 +609,13 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
 
        r->id.idiag_sport = inet->inet_sport;
        r->id.idiag_dport = ireq->rmt_port;
+
+       memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src));
+       memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst));
+
        r->id.idiag_src[0] = ireq->loc_addr;
        r->id.idiag_dst[0] = ireq->rmt_addr;
+
        r->idiag_expires = jiffies_to_msecs(tmo);
        r->idiag_rqueue = 0;
        r->idiag_wqueue = 0;
@@ -820,7 +840,7 @@ next_normal:
                        ++num;
                }
 
-               if (r->idiag_states & TCPF_TIME_WAIT) {
+               if (r->idiag_states & (TCPF_TIME_WAIT | TCPF_FIN_WAIT2)) {
                        struct inet_timewait_sock *tw;
 
                        inet_twsk_for_each(tw, node,
@@ -828,6 +848,8 @@ next_normal:
 
                                if (num < s_num)
                                        goto next_dying;
+                               if (!(r->idiag_states & (1 << tw->tw_substate)))
+                                       goto next_dying;
                                if (r->id.idiag_sport != tw->tw_sport &&
                                    r->id.idiag_sport)
                                        goto next_dying;