1a36b38aaSBjörn Töpel // SPDX-License-Identifier: GPL-2.0 2a36b38aaSBjörn Töpel /* XDP sockets monitoring support 3a36b38aaSBjörn Töpel * 4a36b38aaSBjörn Töpel * Copyright(c) 2019 Intel Corporation. 5a36b38aaSBjörn Töpel * 6a36b38aaSBjörn Töpel * Author: Björn Töpel <bjorn.topel@intel.com> 7a36b38aaSBjörn Töpel */ 8a36b38aaSBjörn Töpel 9a36b38aaSBjörn Töpel #include <linux/module.h> 10a36b38aaSBjörn Töpel #include <net/xdp_sock.h> 11a36b38aaSBjörn Töpel #include <linux/xdp_diag.h> 12a36b38aaSBjörn Töpel #include <linux/sock_diag.h> 13a36b38aaSBjörn Töpel 14a36b38aaSBjörn Töpel #include "xsk_queue.h" 15a36b38aaSBjörn Töpel #include "xsk.h" 16a36b38aaSBjörn Töpel 17a36b38aaSBjörn Töpel static int xsk_diag_put_info(const struct xdp_sock *xs, struct sk_buff *nlskb) 18a36b38aaSBjörn Töpel { 19a36b38aaSBjörn Töpel struct xdp_diag_info di = {}; 20a36b38aaSBjörn Töpel 21a36b38aaSBjörn Töpel di.ifindex = xs->dev ? xs->dev->ifindex : 0; 22a36b38aaSBjörn Töpel di.queue_id = xs->queue_id; 23a36b38aaSBjörn Töpel return nla_put(nlskb, XDP_DIAG_INFO, sizeof(di), &di); 24a36b38aaSBjörn Töpel } 25a36b38aaSBjörn Töpel 26a36b38aaSBjörn Töpel static int xsk_diag_put_ring(const struct xsk_queue *queue, int nl_type, 27a36b38aaSBjörn Töpel struct sk_buff *nlskb) 28a36b38aaSBjörn Töpel { 29a36b38aaSBjörn Töpel struct xdp_diag_ring dr = {}; 30a36b38aaSBjörn Töpel 31a36b38aaSBjörn Töpel dr.entries = queue->nentries; 32a36b38aaSBjörn Töpel return nla_put(nlskb, nl_type, sizeof(dr), &dr); 33a36b38aaSBjörn Töpel } 34a36b38aaSBjörn Töpel 35a36b38aaSBjörn Töpel static int xsk_diag_put_rings_cfg(const struct xdp_sock *xs, 36a36b38aaSBjörn Töpel struct sk_buff *nlskb) 37a36b38aaSBjörn Töpel { 38a36b38aaSBjörn Töpel int err = 0; 39a36b38aaSBjörn Töpel 40a36b38aaSBjörn Töpel if (xs->rx) 41a36b38aaSBjörn Töpel err = xsk_diag_put_ring(xs->rx, XDP_DIAG_RX_RING, nlskb); 42a36b38aaSBjörn Töpel if (!err && xs->tx) 43a36b38aaSBjörn Töpel err = xsk_diag_put_ring(xs->tx, XDP_DIAG_TX_RING, nlskb); 44a36b38aaSBjörn Töpel return err; 45a36b38aaSBjörn Töpel } 46a36b38aaSBjörn Töpel 47a36b38aaSBjörn Töpel static int xsk_diag_put_umem(const struct xdp_sock *xs, struct sk_buff *nlskb) 48a36b38aaSBjörn Töpel { 497361f9c3SMagnus Karlsson struct xsk_buff_pool *pool = xs->pool; 50a36b38aaSBjörn Töpel struct xdp_umem *umem = xs->umem; 51a36b38aaSBjörn Töpel struct xdp_diag_umem du = {}; 52a36b38aaSBjörn Töpel int err; 53a36b38aaSBjörn Töpel 54a36b38aaSBjörn Töpel if (!umem) 55a36b38aaSBjörn Töpel return 0; 56a36b38aaSBjörn Töpel 57a36b38aaSBjörn Töpel du.id = umem->id; 58a36b38aaSBjörn Töpel du.size = umem->size; 59a36b38aaSBjörn Töpel du.num_pages = umem->npgs; 602b43470aSBjörn Töpel du.chunk_size = umem->chunk_size; 61a36b38aaSBjörn Töpel du.headroom = umem->headroom; 6253ea2076SMagnus Karlsson du.ifindex = (pool && pool->netdev) ? pool->netdev->ifindex : 0; 6353ea2076SMagnus Karlsson du.queue_id = pool ? pool->queue_id : 0; 64a36b38aaSBjörn Töpel du.flags = 0; 65a36b38aaSBjörn Töpel if (umem->zc) 66a36b38aaSBjörn Töpel du.flags |= XDP_DU_F_ZEROCOPY; 67a36b38aaSBjörn Töpel du.refs = refcount_read(&umem->users); 68a36b38aaSBjörn Töpel 69a36b38aaSBjörn Töpel err = nla_put(nlskb, XDP_DIAG_UMEM, sizeof(du), &du); 7053ea2076SMagnus Karlsson if (!err && pool && pool->fq) 717361f9c3SMagnus Karlsson err = xsk_diag_put_ring(pool->fq, 727361f9c3SMagnus Karlsson XDP_DIAG_UMEM_FILL_RING, nlskb); 7353ea2076SMagnus Karlsson if (!err && pool && pool->cq) 7453ea2076SMagnus Karlsson err = xsk_diag_put_ring(pool->cq, 7553ea2076SMagnus Karlsson XDP_DIAG_UMEM_COMPLETION_RING, nlskb); 76a36b38aaSBjörn Töpel return err; 77a36b38aaSBjörn Töpel } 78a36b38aaSBjörn Töpel 790d80cb46SCiara Loftus static int xsk_diag_put_stats(const struct xdp_sock *xs, struct sk_buff *nlskb) 800d80cb46SCiara Loftus { 810d80cb46SCiara Loftus struct xdp_diag_stats du = {}; 820d80cb46SCiara Loftus 830d80cb46SCiara Loftus du.n_rx_dropped = xs->rx_dropped; 840d80cb46SCiara Loftus du.n_rx_invalid = xskq_nb_invalid_descs(xs->rx); 850d80cb46SCiara Loftus du.n_rx_full = xs->rx_queue_full; 867361f9c3SMagnus Karlsson du.n_fill_ring_empty = xs->pool ? xskq_nb_queue_empty_descs(xs->pool->fq) : 0; 870d80cb46SCiara Loftus du.n_tx_invalid = xskq_nb_invalid_descs(xs->tx); 880d80cb46SCiara Loftus du.n_tx_ring_empty = xskq_nb_queue_empty_descs(xs->tx); 890d80cb46SCiara Loftus return nla_put(nlskb, XDP_DIAG_STATS, sizeof(du), &du); 900d80cb46SCiara Loftus } 910d80cb46SCiara Loftus 92a36b38aaSBjörn Töpel static int xsk_diag_fill(struct sock *sk, struct sk_buff *nlskb, 93a36b38aaSBjörn Töpel struct xdp_diag_req *req, 94a36b38aaSBjörn Töpel struct user_namespace *user_ns, 95a36b38aaSBjörn Töpel u32 portid, u32 seq, u32 flags, int sk_ino) 96a36b38aaSBjörn Töpel { 97a36b38aaSBjörn Töpel struct xdp_sock *xs = xdp_sk(sk); 98a36b38aaSBjörn Töpel struct xdp_diag_msg *msg; 99a36b38aaSBjörn Töpel struct nlmsghdr *nlh; 100a36b38aaSBjörn Töpel 101a36b38aaSBjörn Töpel nlh = nlmsg_put(nlskb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*msg), 102a36b38aaSBjörn Töpel flags); 103a36b38aaSBjörn Töpel if (!nlh) 104a36b38aaSBjörn Töpel return -EMSGSIZE; 105a36b38aaSBjörn Töpel 106a36b38aaSBjörn Töpel msg = nlmsg_data(nlh); 107a36b38aaSBjörn Töpel memset(msg, 0, sizeof(*msg)); 108a36b38aaSBjörn Töpel msg->xdiag_family = AF_XDP; 109a36b38aaSBjörn Töpel msg->xdiag_type = sk->sk_type; 110a36b38aaSBjörn Töpel msg->xdiag_ino = sk_ino; 111a36b38aaSBjörn Töpel sock_diag_save_cookie(sk, msg->xdiag_cookie); 112a36b38aaSBjörn Töpel 11325dc18ffSBjörn Töpel mutex_lock(&xs->mutex); 1143e019d8aSMagnus Karlsson if (READ_ONCE(xs->state) == XSK_UNBOUND) 1153e019d8aSMagnus Karlsson goto out_nlmsg_trim; 1163e019d8aSMagnus Karlsson 117a36b38aaSBjörn Töpel if ((req->xdiag_show & XDP_SHOW_INFO) && xsk_diag_put_info(xs, nlskb)) 118a36b38aaSBjörn Töpel goto out_nlmsg_trim; 119a36b38aaSBjörn Töpel 120a36b38aaSBjörn Töpel if ((req->xdiag_show & XDP_SHOW_INFO) && 121a36b38aaSBjörn Töpel nla_put_u32(nlskb, XDP_DIAG_UID, 122a36b38aaSBjörn Töpel from_kuid_munged(user_ns, sock_i_uid(sk)))) 123a36b38aaSBjörn Töpel goto out_nlmsg_trim; 124a36b38aaSBjörn Töpel 125a36b38aaSBjörn Töpel if ((req->xdiag_show & XDP_SHOW_RING_CFG) && 126a36b38aaSBjörn Töpel xsk_diag_put_rings_cfg(xs, nlskb)) 127a36b38aaSBjörn Töpel goto out_nlmsg_trim; 128a36b38aaSBjörn Töpel 129a36b38aaSBjörn Töpel if ((req->xdiag_show & XDP_SHOW_UMEM) && 130a36b38aaSBjörn Töpel xsk_diag_put_umem(xs, nlskb)) 131a36b38aaSBjörn Töpel goto out_nlmsg_trim; 132a36b38aaSBjörn Töpel 133a36b38aaSBjörn Töpel if ((req->xdiag_show & XDP_SHOW_MEMINFO) && 134a36b38aaSBjörn Töpel sock_diag_put_meminfo(sk, nlskb, XDP_DIAG_MEMINFO)) 135a36b38aaSBjörn Töpel goto out_nlmsg_trim; 136a36b38aaSBjörn Töpel 1370d80cb46SCiara Loftus if ((req->xdiag_show & XDP_SHOW_STATS) && 1380d80cb46SCiara Loftus xsk_diag_put_stats(xs, nlskb)) 1390d80cb46SCiara Loftus goto out_nlmsg_trim; 1400d80cb46SCiara Loftus 14125dc18ffSBjörn Töpel mutex_unlock(&xs->mutex); 142a36b38aaSBjörn Töpel nlmsg_end(nlskb, nlh); 143a36b38aaSBjörn Töpel return 0; 144a36b38aaSBjörn Töpel 145a36b38aaSBjörn Töpel out_nlmsg_trim: 14625dc18ffSBjörn Töpel mutex_unlock(&xs->mutex); 147a36b38aaSBjörn Töpel nlmsg_cancel(nlskb, nlh); 148a36b38aaSBjörn Töpel return -EMSGSIZE; 149a36b38aaSBjörn Töpel } 150a36b38aaSBjörn Töpel 151a36b38aaSBjörn Töpel static int xsk_diag_dump(struct sk_buff *nlskb, struct netlink_callback *cb) 152a36b38aaSBjörn Töpel { 153a36b38aaSBjörn Töpel struct xdp_diag_req *req = nlmsg_data(cb->nlh); 154a36b38aaSBjörn Töpel struct net *net = sock_net(nlskb->sk); 155a36b38aaSBjörn Töpel int num = 0, s_num = cb->args[0]; 156a36b38aaSBjörn Töpel struct sock *sk; 157a36b38aaSBjörn Töpel 158a36b38aaSBjörn Töpel mutex_lock(&net->xdp.lock); 159a36b38aaSBjörn Töpel 160a36b38aaSBjörn Töpel sk_for_each(sk, &net->xdp.list) { 161a36b38aaSBjörn Töpel if (!net_eq(sock_net(sk), net)) 162a36b38aaSBjörn Töpel continue; 163a36b38aaSBjörn Töpel if (num++ < s_num) 164a36b38aaSBjörn Töpel continue; 165a36b38aaSBjörn Töpel 166a36b38aaSBjörn Töpel if (xsk_diag_fill(sk, nlskb, req, 167a36b38aaSBjörn Töpel sk_user_ns(NETLINK_CB(cb->skb).sk), 168a36b38aaSBjörn Töpel NETLINK_CB(cb->skb).portid, 169a36b38aaSBjörn Töpel cb->nlh->nlmsg_seq, NLM_F_MULTI, 170a36b38aaSBjörn Töpel sock_i_ino(sk)) < 0) { 171a36b38aaSBjörn Töpel num--; 172a36b38aaSBjörn Töpel break; 173a36b38aaSBjörn Töpel } 174a36b38aaSBjörn Töpel } 175a36b38aaSBjörn Töpel 176a36b38aaSBjörn Töpel mutex_unlock(&net->xdp.lock); 177a36b38aaSBjörn Töpel cb->args[0] = num; 178a36b38aaSBjörn Töpel return nlskb->len; 179a36b38aaSBjörn Töpel } 180a36b38aaSBjörn Töpel 181a36b38aaSBjörn Töpel static int xsk_diag_handler_dump(struct sk_buff *nlskb, struct nlmsghdr *hdr) 182a36b38aaSBjörn Töpel { 183a36b38aaSBjörn Töpel struct netlink_dump_control c = { .dump = xsk_diag_dump }; 184a36b38aaSBjörn Töpel int hdrlen = sizeof(struct xdp_diag_req); 185a36b38aaSBjörn Töpel struct net *net = sock_net(nlskb->sk); 186a36b38aaSBjörn Töpel 187a36b38aaSBjörn Töpel if (nlmsg_len(hdr) < hdrlen) 188a36b38aaSBjörn Töpel return -EINVAL; 189a36b38aaSBjörn Töpel 190a36b38aaSBjörn Töpel if (!(hdr->nlmsg_flags & NLM_F_DUMP)) 191a36b38aaSBjörn Töpel return -EOPNOTSUPP; 192a36b38aaSBjörn Töpel 193a36b38aaSBjörn Töpel return netlink_dump_start(net->diag_nlsk, nlskb, hdr, &c); 194a36b38aaSBjörn Töpel } 195a36b38aaSBjörn Töpel 196a36b38aaSBjörn Töpel static const struct sock_diag_handler xsk_diag_handler = { 197*114b4bb1SEric Dumazet .owner = THIS_MODULE, 198a36b38aaSBjörn Töpel .family = AF_XDP, 199a36b38aaSBjörn Töpel .dump = xsk_diag_handler_dump, 200a36b38aaSBjörn Töpel }; 201a36b38aaSBjörn Töpel 202a36b38aaSBjörn Töpel static int __init xsk_diag_init(void) 203a36b38aaSBjörn Töpel { 204a36b38aaSBjörn Töpel return sock_diag_register(&xsk_diag_handler); 205a36b38aaSBjörn Töpel } 206a36b38aaSBjörn Töpel 207a36b38aaSBjörn Töpel static void __exit xsk_diag_exit(void) 208a36b38aaSBjörn Töpel { 209a36b38aaSBjörn Töpel sock_diag_unregister(&xsk_diag_handler); 210a36b38aaSBjörn Töpel } 211a36b38aaSBjörn Töpel 212a36b38aaSBjörn Töpel module_init(xsk_diag_init); 213a36b38aaSBjörn Töpel module_exit(xsk_diag_exit); 214a36b38aaSBjörn Töpel MODULE_LICENSE("GPL"); 215938dbeadSJakub Kicinski MODULE_DESCRIPTION("XDP socket monitoring via SOCK_DIAG"); 216a36b38aaSBjörn Töpel MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, AF_XDP); 217