sock_diag: implement a get_info handler for inet
[cascardo/linux.git] / net / ipv4 / inet_diag.c
index b1f0117..21985d8 100644 (file)
@@ -1078,14 +1078,60 @@ 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);
+       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)