These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / net / ipv4 / inet_diag.c
index 4d32262..ab9f8a6 100644 (file)
@@ -151,6 +151,10 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
                        if (nla_put_u8(skb, INET_DIAG_TCLASS,
                                       inet6_sk(sk)->tclass) < 0)
                                goto errout;
+
+               if (((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) &&
+                   nla_put_u8(skb, INET_DIAG_SKV6ONLY, ipv6_only_sock(sk)))
+                       goto errout;
        }
 #endif
 
@@ -200,9 +204,9 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
        }
 #undef EXPIRES_IN_MS
 
-       if (ext & (1 << (INET_DIAG_INFO - 1))) {
+       if ((ext & (1 << (INET_DIAG_INFO - 1))) && handler->idiag_info_size) {
                attr = nla_reserve(skb, INET_DIAG_INFO,
-                                  sizeof(struct tcp_info));
+                                  handler->idiag_info_size);
                if (!attr)
                        goto errout;
 
@@ -726,91 +730,21 @@ static void twsk_build_assert(void)
 #endif
 }
 
-static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk,
-                              struct netlink_callback *cb,
-                              const struct inet_diag_req_v2 *r,
-                              const struct nlattr *bc)
-{
-       struct inet_connection_sock *icsk = inet_csk(sk);
-       struct inet_sock *inet = inet_sk(sk);
-       struct inet_diag_entry entry;
-       int j, s_j, reqnum, s_reqnum;
-       struct listen_sock *lopt;
-       int err = 0;
-
-       s_j = cb->args[3];
-       s_reqnum = cb->args[4];
-
-       if (s_j > 0)
-               s_j--;
-
-       entry.family = sk->sk_family;
-
-       spin_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
-
-       lopt = icsk->icsk_accept_queue.listen_opt;
-       if (!lopt || !listen_sock_qlen(lopt))
-               goto out;
-
-       if (bc) {
-               entry.sport = inet->inet_num;
-               entry.userlocks = sk->sk_userlocks;
-       }
-
-       for (j = s_j; j < lopt->nr_table_entries; j++) {
-               struct request_sock *req, *head = lopt->syn_table[j];
-
-               reqnum = 0;
-               for (req = head; req; reqnum++, req = req->dl_next) {
-                       struct inet_request_sock *ireq = inet_rsk(req);
-
-                       if (reqnum < s_reqnum)
-                               continue;
-                       if (r->id.idiag_dport != ireq->ir_rmt_port &&
-                           r->id.idiag_dport)
-                               continue;
-
-                       if (bc) {
-                               /* Note: entry.sport and entry.userlocks are already set */
-                               entry_fill_addrs(&entry, req_to_sk(req));
-                               entry.dport = ntohs(ireq->ir_rmt_port);
-
-                               if (!inet_diag_bc_run(bc, &entry))
-                                       continue;
-                       }
-
-                       err = inet_req_diag_fill(req_to_sk(req), skb,
-                                                NETLINK_CB(cb->skb).portid,
-                                                cb->nlh->nlmsg_seq,
-                                                NLM_F_MULTI, cb->nlh);
-                       if (err < 0) {
-                               cb->args[3] = j + 1;
-                               cb->args[4] = reqnum;
-                               goto out;
-                       }
-               }
-
-               s_reqnum = 0;
-       }
-
-out:
-       spin_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
-
-       return err;
-}
-
 void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb,
                         struct netlink_callback *cb,
                         const struct inet_diag_req_v2 *r, struct nlattr *bc)
 {
        struct net *net = sock_net(skb->sk);
        int i, num, s_i, s_num;
+       u32 idiag_states = r->idiag_states;
 
+       if (idiag_states & TCPF_SYN_RECV)
+               idiag_states |= TCPF_NEW_SYN_RECV;
        s_i = cb->args[1];
        s_num = num = cb->args[2];
 
        if (cb->args[0] == 0) {
-               if (!(r->idiag_states & (TCPF_LISTEN | TCPF_SYN_RECV)))
+               if (!(idiag_states & TCPF_LISTEN))
                        goto skip_listen_ht;
 
                for (i = s_i; i < INET_LHTABLE_SIZE; i++) {
@@ -840,21 +774,11 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb,
                                    r->id.idiag_sport)
                                        goto next_listen;
 
-                               if (!(r->idiag_states & TCPF_LISTEN) ||
-                                   r->id.idiag_dport ||
+                               if (r->id.idiag_dport ||
                                    cb->args[3] > 0)
-                                       goto syn_recv;
-
-                               if (inet_csk_diag_dump(sk, skb, cb, r, bc) < 0) {
-                                       spin_unlock_bh(&ilb->lock);
-                                       goto done;
-                               }
-
-syn_recv:
-                               if (!(r->idiag_states & TCPF_SYN_RECV))
                                        goto next_listen;
 
-                               if (inet_diag_dump_reqs(skb, sk, cb, r, bc) < 0) {
+                               if (inet_csk_diag_dump(sk, skb, cb, r, bc) < 0) {
                                        spin_unlock_bh(&ilb->lock);
                                        goto done;
                                }
@@ -875,7 +799,7 @@ skip_listen_ht:
                s_i = num = s_num = 0;
        }
 
-       if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV)))
+       if (!(idiag_states & ~TCPF_LISTEN))
                goto out;
 
        for (i = s_i; i <= hashinfo->ehash_mask; i++) {
@@ -902,7 +826,7 @@ skip_listen_ht:
                                goto next_normal;
                        state = (sk->sk_state == TCP_TIME_WAIT) ?
                                inet_twsk(sk)->tw_substate : sk->sk_state;
-                       if (!(r->idiag_states & (1 << state)))
+                       if (!(idiag_states & (1 << state)))
                                goto next_normal;
                        if (r->sdiag_family != AF_UNSPEC &&
                            sk->sk_family != r->sdiag_family)
@@ -1078,14 +1002,62 @@ static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
        return inet_diag_get_exact(skb, h, nlmsg_data(h));
 }
 
+static
+int inet_diag_handler_get_info(struct sk_buff *skb, struct sock *sk)
+{
+       const struct inet_diag_handler *handler;
+       struct nlmsghdr *nlh;
+       struct nlattr *attr;
+       struct inet_diag_msg *r;
+       void *info = NULL;
+       int err = 0;
+
+       nlh = nlmsg_put(skb, 0, 0, SOCK_DIAG_BY_FAMILY, sizeof(*r), 0);
+       if (!nlh)
+               return -ENOMEM;
+
+       r = nlmsg_data(nlh);
+       memset(r, 0, sizeof(*r));
+       inet_diag_msg_common_fill(r, sk);
+       if (sk->sk_type == SOCK_DGRAM || sk->sk_type == SOCK_STREAM)
+               r->id.idiag_sport = inet_sk(sk)->inet_sport;
+       r->idiag_state = sk->sk_state;
+
+       if ((err = nla_put_u8(skb, INET_DIAG_PROTOCOL, sk->sk_protocol))) {
+               nlmsg_cancel(skb, nlh);
+               return err;
+       }
+
+       handler = inet_diag_lock_handler(sk->sk_protocol);
+       if (IS_ERR(handler)) {
+               inet_diag_unlock_handler(handler);
+               nlmsg_cancel(skb, nlh);
+               return PTR_ERR(handler);
+       }
+
+       attr = handler->idiag_info_size
+               ? nla_reserve(skb, INET_DIAG_INFO, handler->idiag_info_size)
+               : NULL;
+       if (attr)
+               info = nla_data(attr);
+
+       handler->idiag_get_info(sk, r, info);
+       inet_diag_unlock_handler(handler);
+
+       nlmsg_end(skb, nlh);
+       return 0;
+}
+
 static const struct sock_diag_handler inet_diag_handler = {
        .family = AF_INET,
        .dump = inet_diag_handler_dump,
+       .get_info = inet_diag_handler_get_info,
 };
 
 static const struct sock_diag_handler inet6_diag_handler = {
        .family = AF_INET6,
        .dump = inet_diag_handler_dump,
+       .get_info = inet_diag_handler_get_info,
 };
 
 int inet_diag_register(const struct inet_diag_handler *h)