162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * vsock sock_diag(7) module 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2017 Red Hat, Inc. 662306a36Sopenharmony_ci * Author: Stefan Hajnoczi <stefanha@redhat.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/sock_diag.h> 1162306a36Sopenharmony_ci#include <linux/vm_sockets_diag.h> 1262306a36Sopenharmony_ci#include <net/af_vsock.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic int sk_diag_fill(struct sock *sk, struct sk_buff *skb, 1562306a36Sopenharmony_ci u32 portid, u32 seq, u32 flags) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci struct vsock_sock *vsk = vsock_sk(sk); 1862306a36Sopenharmony_ci struct vsock_diag_msg *rep; 1962306a36Sopenharmony_ci struct nlmsghdr *nlh; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep), 2262306a36Sopenharmony_ci flags); 2362306a36Sopenharmony_ci if (!nlh) 2462306a36Sopenharmony_ci return -EMSGSIZE; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci rep = nlmsg_data(nlh); 2762306a36Sopenharmony_ci rep->vdiag_family = AF_VSOCK; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci /* Lock order dictates that sk_lock is acquired before 3062306a36Sopenharmony_ci * vsock_table_lock, so we cannot lock here. Simply don't take 3162306a36Sopenharmony_ci * sk_lock; sk is guaranteed to stay alive since vsock_table_lock is 3262306a36Sopenharmony_ci * held. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci rep->vdiag_type = sk->sk_type; 3562306a36Sopenharmony_ci rep->vdiag_state = sk->sk_state; 3662306a36Sopenharmony_ci rep->vdiag_shutdown = sk->sk_shutdown; 3762306a36Sopenharmony_ci rep->vdiag_src_cid = vsk->local_addr.svm_cid; 3862306a36Sopenharmony_ci rep->vdiag_src_port = vsk->local_addr.svm_port; 3962306a36Sopenharmony_ci rep->vdiag_dst_cid = vsk->remote_addr.svm_cid; 4062306a36Sopenharmony_ci rep->vdiag_dst_port = vsk->remote_addr.svm_port; 4162306a36Sopenharmony_ci rep->vdiag_ino = sock_i_ino(sk); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci sock_diag_save_cookie(sk, rep->vdiag_cookie); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci return 0; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int vsock_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct vsock_diag_req *req; 5162306a36Sopenharmony_ci struct vsock_sock *vsk; 5262306a36Sopenharmony_ci unsigned int bucket; 5362306a36Sopenharmony_ci unsigned int last_i; 5462306a36Sopenharmony_ci unsigned int table; 5562306a36Sopenharmony_ci struct net *net; 5662306a36Sopenharmony_ci unsigned int i; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci req = nlmsg_data(cb->nlh); 5962306a36Sopenharmony_ci net = sock_net(skb->sk); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* State saved between calls: */ 6262306a36Sopenharmony_ci table = cb->args[0]; 6362306a36Sopenharmony_ci bucket = cb->args[1]; 6462306a36Sopenharmony_ci i = last_i = cb->args[2]; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* TODO VMCI pending sockets? */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci spin_lock_bh(&vsock_table_lock); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* Bind table (locally created sockets) */ 7162306a36Sopenharmony_ci if (table == 0) { 7262306a36Sopenharmony_ci while (bucket < ARRAY_SIZE(vsock_bind_table)) { 7362306a36Sopenharmony_ci struct list_head *head = &vsock_bind_table[bucket]; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci i = 0; 7662306a36Sopenharmony_ci list_for_each_entry(vsk, head, bound_table) { 7762306a36Sopenharmony_ci struct sock *sk = sk_vsock(vsk); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (!net_eq(sock_net(sk), net)) 8062306a36Sopenharmony_ci continue; 8162306a36Sopenharmony_ci if (i < last_i) 8262306a36Sopenharmony_ci goto next_bind; 8362306a36Sopenharmony_ci if (!(req->vdiag_states & (1 << sk->sk_state))) 8462306a36Sopenharmony_ci goto next_bind; 8562306a36Sopenharmony_ci if (sk_diag_fill(sk, skb, 8662306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 8762306a36Sopenharmony_ci cb->nlh->nlmsg_seq, 8862306a36Sopenharmony_ci NLM_F_MULTI) < 0) 8962306a36Sopenharmony_ci goto done; 9062306a36Sopenharmony_cinext_bind: 9162306a36Sopenharmony_ci i++; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci last_i = 0; 9462306a36Sopenharmony_ci bucket++; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci table++; 9862306a36Sopenharmony_ci bucket = 0; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Connected table (accepted connections) */ 10262306a36Sopenharmony_ci while (bucket < ARRAY_SIZE(vsock_connected_table)) { 10362306a36Sopenharmony_ci struct list_head *head = &vsock_connected_table[bucket]; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci i = 0; 10662306a36Sopenharmony_ci list_for_each_entry(vsk, head, connected_table) { 10762306a36Sopenharmony_ci struct sock *sk = sk_vsock(vsk); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Skip sockets we've already seen above */ 11062306a36Sopenharmony_ci if (__vsock_in_bound_table(vsk)) 11162306a36Sopenharmony_ci continue; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (!net_eq(sock_net(sk), net)) 11462306a36Sopenharmony_ci continue; 11562306a36Sopenharmony_ci if (i < last_i) 11662306a36Sopenharmony_ci goto next_connected; 11762306a36Sopenharmony_ci if (!(req->vdiag_states & (1 << sk->sk_state))) 11862306a36Sopenharmony_ci goto next_connected; 11962306a36Sopenharmony_ci if (sk_diag_fill(sk, skb, 12062306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 12162306a36Sopenharmony_ci cb->nlh->nlmsg_seq, 12262306a36Sopenharmony_ci NLM_F_MULTI) < 0) 12362306a36Sopenharmony_ci goto done; 12462306a36Sopenharmony_cinext_connected: 12562306a36Sopenharmony_ci i++; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci last_i = 0; 12862306a36Sopenharmony_ci bucket++; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cidone: 13262306a36Sopenharmony_ci spin_unlock_bh(&vsock_table_lock); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci cb->args[0] = table; 13562306a36Sopenharmony_ci cb->args[1] = bucket; 13662306a36Sopenharmony_ci cb->args[2] = i; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return skb->len; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int vsock_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci int hdrlen = sizeof(struct vsock_diag_req); 14462306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (nlmsg_len(h) < hdrlen) 14762306a36Sopenharmony_ci return -EINVAL; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (h->nlmsg_flags & NLM_F_DUMP) { 15062306a36Sopenharmony_ci struct netlink_dump_control c = { 15162306a36Sopenharmony_ci .dump = vsock_diag_dump, 15262306a36Sopenharmony_ci }; 15362306a36Sopenharmony_ci return netlink_dump_start(net->diag_nlsk, skb, h, &c); 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return -EOPNOTSUPP; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic const struct sock_diag_handler vsock_diag_handler = { 16062306a36Sopenharmony_ci .family = AF_VSOCK, 16162306a36Sopenharmony_ci .dump = vsock_diag_handler_dump, 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic int __init vsock_diag_init(void) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci return sock_diag_register(&vsock_diag_handler); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic void __exit vsock_diag_exit(void) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci sock_diag_unregister(&vsock_diag_handler); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cimodule_init(vsock_diag_init); 17562306a36Sopenharmony_cimodule_exit(vsock_diag_exit); 17662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 17762306a36Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 17862306a36Sopenharmony_ci 40 /* AF_VSOCK */); 179