18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * vsock sock_diag(7) module 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Red Hat, Inc. 68c2ecf20Sopenharmony_ci * Author: Stefan Hajnoczi <stefanha@redhat.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/sock_diag.h> 118c2ecf20Sopenharmony_ci#include <linux/vm_sockets_diag.h> 128c2ecf20Sopenharmony_ci#include <net/af_vsock.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic int sk_diag_fill(struct sock *sk, struct sk_buff *skb, 158c2ecf20Sopenharmony_ci u32 portid, u32 seq, u32 flags) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci struct vsock_sock *vsk = vsock_sk(sk); 188c2ecf20Sopenharmony_ci struct vsock_diag_msg *rep; 198c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep), 228c2ecf20Sopenharmony_ci flags); 238c2ecf20Sopenharmony_ci if (!nlh) 248c2ecf20Sopenharmony_ci return -EMSGSIZE; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci rep = nlmsg_data(nlh); 278c2ecf20Sopenharmony_ci rep->vdiag_family = AF_VSOCK; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci /* Lock order dictates that sk_lock is acquired before 308c2ecf20Sopenharmony_ci * vsock_table_lock, so we cannot lock here. Simply don't take 318c2ecf20Sopenharmony_ci * sk_lock; sk is guaranteed to stay alive since vsock_table_lock is 328c2ecf20Sopenharmony_ci * held. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci rep->vdiag_type = sk->sk_type; 358c2ecf20Sopenharmony_ci rep->vdiag_state = sk->sk_state; 368c2ecf20Sopenharmony_ci rep->vdiag_shutdown = sk->sk_shutdown; 378c2ecf20Sopenharmony_ci rep->vdiag_src_cid = vsk->local_addr.svm_cid; 388c2ecf20Sopenharmony_ci rep->vdiag_src_port = vsk->local_addr.svm_port; 398c2ecf20Sopenharmony_ci rep->vdiag_dst_cid = vsk->remote_addr.svm_cid; 408c2ecf20Sopenharmony_ci rep->vdiag_dst_port = vsk->remote_addr.svm_port; 418c2ecf20Sopenharmony_ci rep->vdiag_ino = sock_i_ino(sk); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci sock_diag_save_cookie(sk, rep->vdiag_cookie); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci return 0; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int vsock_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct vsock_diag_req *req; 518c2ecf20Sopenharmony_ci struct vsock_sock *vsk; 528c2ecf20Sopenharmony_ci unsigned int bucket; 538c2ecf20Sopenharmony_ci unsigned int last_i; 548c2ecf20Sopenharmony_ci unsigned int table; 558c2ecf20Sopenharmony_ci struct net *net; 568c2ecf20Sopenharmony_ci unsigned int i; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci req = nlmsg_data(cb->nlh); 598c2ecf20Sopenharmony_ci net = sock_net(skb->sk); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* State saved between calls: */ 628c2ecf20Sopenharmony_ci table = cb->args[0]; 638c2ecf20Sopenharmony_ci bucket = cb->args[1]; 648c2ecf20Sopenharmony_ci i = last_i = cb->args[2]; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* TODO VMCI pending sockets? */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci spin_lock_bh(&vsock_table_lock); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* Bind table (locally created sockets) */ 718c2ecf20Sopenharmony_ci if (table == 0) { 728c2ecf20Sopenharmony_ci while (bucket < ARRAY_SIZE(vsock_bind_table)) { 738c2ecf20Sopenharmony_ci struct list_head *head = &vsock_bind_table[bucket]; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci i = 0; 768c2ecf20Sopenharmony_ci list_for_each_entry(vsk, head, bound_table) { 778c2ecf20Sopenharmony_ci struct sock *sk = sk_vsock(vsk); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (!net_eq(sock_net(sk), net)) 808c2ecf20Sopenharmony_ci continue; 818c2ecf20Sopenharmony_ci if (i < last_i) 828c2ecf20Sopenharmony_ci goto next_bind; 838c2ecf20Sopenharmony_ci if (!(req->vdiag_states & (1 << sk->sk_state))) 848c2ecf20Sopenharmony_ci goto next_bind; 858c2ecf20Sopenharmony_ci if (sk_diag_fill(sk, skb, 868c2ecf20Sopenharmony_ci NETLINK_CB(cb->skb).portid, 878c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, 888c2ecf20Sopenharmony_ci NLM_F_MULTI) < 0) 898c2ecf20Sopenharmony_ci goto done; 908c2ecf20Sopenharmony_cinext_bind: 918c2ecf20Sopenharmony_ci i++; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci last_i = 0; 948c2ecf20Sopenharmony_ci bucket++; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci table++; 988c2ecf20Sopenharmony_ci bucket = 0; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* Connected table (accepted connections) */ 1028c2ecf20Sopenharmony_ci while (bucket < ARRAY_SIZE(vsock_connected_table)) { 1038c2ecf20Sopenharmony_ci struct list_head *head = &vsock_connected_table[bucket]; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci i = 0; 1068c2ecf20Sopenharmony_ci list_for_each_entry(vsk, head, connected_table) { 1078c2ecf20Sopenharmony_ci struct sock *sk = sk_vsock(vsk); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Skip sockets we've already seen above */ 1108c2ecf20Sopenharmony_ci if (__vsock_in_bound_table(vsk)) 1118c2ecf20Sopenharmony_ci continue; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (!net_eq(sock_net(sk), net)) 1148c2ecf20Sopenharmony_ci continue; 1158c2ecf20Sopenharmony_ci if (i < last_i) 1168c2ecf20Sopenharmony_ci goto next_connected; 1178c2ecf20Sopenharmony_ci if (!(req->vdiag_states & (1 << sk->sk_state))) 1188c2ecf20Sopenharmony_ci goto next_connected; 1198c2ecf20Sopenharmony_ci if (sk_diag_fill(sk, skb, 1208c2ecf20Sopenharmony_ci NETLINK_CB(cb->skb).portid, 1218c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, 1228c2ecf20Sopenharmony_ci NLM_F_MULTI) < 0) 1238c2ecf20Sopenharmony_ci goto done; 1248c2ecf20Sopenharmony_cinext_connected: 1258c2ecf20Sopenharmony_ci i++; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci last_i = 0; 1288c2ecf20Sopenharmony_ci bucket++; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cidone: 1328c2ecf20Sopenharmony_ci spin_unlock_bh(&vsock_table_lock); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci cb->args[0] = table; 1358c2ecf20Sopenharmony_ci cb->args[1] = bucket; 1368c2ecf20Sopenharmony_ci cb->args[2] = i; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return skb->len; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int vsock_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci int hdrlen = sizeof(struct vsock_diag_req); 1448c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (nlmsg_len(h) < hdrlen) 1478c2ecf20Sopenharmony_ci return -EINVAL; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (h->nlmsg_flags & NLM_F_DUMP) { 1508c2ecf20Sopenharmony_ci struct netlink_dump_control c = { 1518c2ecf20Sopenharmony_ci .dump = vsock_diag_dump, 1528c2ecf20Sopenharmony_ci }; 1538c2ecf20Sopenharmony_ci return netlink_dump_start(net->diag_nlsk, skb, h, &c); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic const struct sock_diag_handler vsock_diag_handler = { 1608c2ecf20Sopenharmony_ci .family = AF_VSOCK, 1618c2ecf20Sopenharmony_ci .dump = vsock_diag_handler_dump, 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int __init vsock_diag_init(void) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci return sock_diag_register(&vsock_diag_handler); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void __exit vsock_diag_exit(void) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci sock_diag_unregister(&vsock_diag_handler); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cimodule_init(vsock_diag_init); 1758c2ecf20Sopenharmony_cimodule_exit(vsock_diag_exit); 1768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1778c2ecf20Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 1788c2ecf20Sopenharmony_ci 40 /* AF_VSOCK */); 179