123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- /*
- * vsock sock_diag(7) module
- *
- * Copyright (C) 2017 Red Hat, Inc.
- * Author: Stefan Hajnoczi <stefanha@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation version 2 and no later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- */
- #include <linux/module.h>
- #include <linux/sock_diag.h>
- #include <linux/vm_sockets_diag.h>
- #include <net/af_vsock.h>
- static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
- u32 portid, u32 seq, u32 flags)
- {
- struct vsock_sock *vsk = vsock_sk(sk);
- struct vsock_diag_msg *rep;
- struct nlmsghdr *nlh;
- nlh = nlmsg_put(skb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep),
- flags);
- if (!nlh)
- return -EMSGSIZE;
- rep = nlmsg_data(nlh);
- rep->vdiag_family = AF_VSOCK;
- /* Lock order dictates that sk_lock is acquired before
- * vsock_table_lock, so we cannot lock here. Simply don't take
- * sk_lock; sk is guaranteed to stay alive since vsock_table_lock is
- * held.
- */
- rep->vdiag_type = sk->sk_type;
- rep->vdiag_state = sk->sk_state;
- rep->vdiag_shutdown = sk->sk_shutdown;
- rep->vdiag_src_cid = vsk->local_addr.svm_cid;
- rep->vdiag_src_port = vsk->local_addr.svm_port;
- rep->vdiag_dst_cid = vsk->remote_addr.svm_cid;
- rep->vdiag_dst_port = vsk->remote_addr.svm_port;
- rep->vdiag_ino = sock_i_ino(sk);
- sock_diag_save_cookie(sk, rep->vdiag_cookie);
- return 0;
- }
- static int vsock_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
- {
- struct vsock_diag_req *req;
- struct vsock_sock *vsk;
- unsigned int bucket;
- unsigned int last_i;
- unsigned int table;
- struct net *net;
- unsigned int i;
- req = nlmsg_data(cb->nlh);
- net = sock_net(skb->sk);
- /* State saved between calls: */
- table = cb->args[0];
- bucket = cb->args[1];
- i = last_i = cb->args[2];
- /* TODO VMCI pending sockets? */
- spin_lock_bh(&vsock_table_lock);
- /* Bind table (locally created sockets) */
- if (table == 0) {
- while (bucket < ARRAY_SIZE(vsock_bind_table)) {
- struct list_head *head = &vsock_bind_table[bucket];
- i = 0;
- list_for_each_entry(vsk, head, bound_table) {
- struct sock *sk = sk_vsock(vsk);
- if (!net_eq(sock_net(sk), net))
- continue;
- if (i < last_i)
- goto next_bind;
- if (!(req->vdiag_states & (1 << sk->sk_state)))
- goto next_bind;
- if (sk_diag_fill(sk, skb,
- NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq,
- NLM_F_MULTI) < 0)
- goto done;
- next_bind:
- i++;
- }
- last_i = 0;
- bucket++;
- }
- table++;
- bucket = 0;
- }
- /* Connected table (accepted connections) */
- while (bucket < ARRAY_SIZE(vsock_connected_table)) {
- struct list_head *head = &vsock_connected_table[bucket];
- i = 0;
- list_for_each_entry(vsk, head, connected_table) {
- struct sock *sk = sk_vsock(vsk);
- /* Skip sockets we've already seen above */
- if (__vsock_in_bound_table(vsk))
- continue;
- if (!net_eq(sock_net(sk), net))
- continue;
- if (i < last_i)
- goto next_connected;
- if (!(req->vdiag_states & (1 << sk->sk_state)))
- goto next_connected;
- if (sk_diag_fill(sk, skb,
- NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq,
- NLM_F_MULTI) < 0)
- goto done;
- next_connected:
- i++;
- }
- last_i = 0;
- bucket++;
- }
- done:
- spin_unlock_bh(&vsock_table_lock);
- cb->args[0] = table;
- cb->args[1] = bucket;
- cb->args[2] = i;
- return skb->len;
- }
- static int vsock_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
- {
- int hdrlen = sizeof(struct vsock_diag_req);
- struct net *net = sock_net(skb->sk);
- if (nlmsg_len(h) < hdrlen)
- return -EINVAL;
- if (h->nlmsg_flags & NLM_F_DUMP) {
- struct netlink_dump_control c = {
- .dump = vsock_diag_dump,
- };
- return netlink_dump_start(net->diag_nlsk, skb, h, &c);
- }
- return -EOPNOTSUPP;
- }
- static const struct sock_diag_handler vsock_diag_handler = {
- .family = AF_VSOCK,
- .dump = vsock_diag_handler_dump,
- };
- static int __init vsock_diag_init(void)
- {
- return sock_diag_register(&vsock_diag_handler);
- }
- static void __exit vsock_diag_exit(void)
- {
- sock_diag_unregister(&vsock_diag_handler);
- }
- module_init(vsock_diag_init);
- module_exit(vsock_diag_exit);
- MODULE_LICENSE("GPL");
- MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG,
- 40 /* AF_VSOCK */);
|