1*a36b38aaSBjörn Töpel // SPDX-License-Identifier: GPL-2.0 2*a36b38aaSBjörn Töpel /* XDP sockets monitoring support 3*a36b38aaSBjörn Töpel * 4*a36b38aaSBjörn Töpel * Copyright(c) 2019 Intel Corporation. 5*a36b38aaSBjörn Töpel * 6*a36b38aaSBjörn Töpel * Author: Björn Töpel <bjorn.topel@intel.com> 7*a36b38aaSBjörn Töpel */ 8*a36b38aaSBjörn Töpel 9*a36b38aaSBjörn Töpel #include <linux/module.h> 10*a36b38aaSBjörn Töpel #include <net/xdp_sock.h> 11*a36b38aaSBjörn Töpel #include <linux/xdp_diag.h> 12*a36b38aaSBjörn Töpel #include <linux/sock_diag.h> 13*a36b38aaSBjörn Töpel 14*a36b38aaSBjörn Töpel #include "xsk_queue.h" 15*a36b38aaSBjörn Töpel #include "xsk.h" 16*a36b38aaSBjörn Töpel 17*a36b38aaSBjörn Töpel static int xsk_diag_put_info(const struct xdp_sock *xs, struct sk_buff *nlskb) 18*a36b38aaSBjörn Töpel { 19*a36b38aaSBjörn Töpel struct xdp_diag_info di = {}; 20*a36b38aaSBjörn Töpel 21*a36b38aaSBjörn Töpel di.ifindex = xs->dev ? xs->dev->ifindex : 0; 22*a36b38aaSBjörn Töpel di.queue_id = xs->queue_id; 23*a36b38aaSBjörn Töpel return nla_put(nlskb, XDP_DIAG_INFO, sizeof(di), &di); 24*a36b38aaSBjörn Töpel } 25*a36b38aaSBjörn Töpel 26*a36b38aaSBjörn Töpel static int xsk_diag_put_ring(const struct xsk_queue *queue, int nl_type, 27*a36b38aaSBjörn Töpel struct sk_buff *nlskb) 28*a36b38aaSBjörn Töpel { 29*a36b38aaSBjörn Töpel struct xdp_diag_ring dr = {}; 30*a36b38aaSBjörn Töpel 31*a36b38aaSBjörn Töpel dr.entries = queue->nentries; 32*a36b38aaSBjörn Töpel return nla_put(nlskb, nl_type, sizeof(dr), &dr); 33*a36b38aaSBjörn Töpel } 34*a36b38aaSBjörn Töpel 35*a36b38aaSBjörn Töpel static int xsk_diag_put_rings_cfg(const struct xdp_sock *xs, 36*a36b38aaSBjörn Töpel struct sk_buff *nlskb) 37*a36b38aaSBjörn Töpel { 38*a36b38aaSBjörn Töpel int err = 0; 39*a36b38aaSBjörn Töpel 40*a36b38aaSBjörn Töpel if (xs->rx) 41*a36b38aaSBjörn Töpel err = xsk_diag_put_ring(xs->rx, XDP_DIAG_RX_RING, nlskb); 42*a36b38aaSBjörn Töpel if (!err && xs->tx) 43*a36b38aaSBjörn Töpel err = xsk_diag_put_ring(xs->tx, XDP_DIAG_TX_RING, nlskb); 44*a36b38aaSBjörn Töpel return err; 45*a36b38aaSBjörn Töpel } 46*a36b38aaSBjörn Töpel 47*a36b38aaSBjörn Töpel static int xsk_diag_put_umem(const struct xdp_sock *xs, struct sk_buff *nlskb) 48*a36b38aaSBjörn Töpel { 49*a36b38aaSBjörn Töpel struct xdp_umem *umem = xs->umem; 50*a36b38aaSBjörn Töpel struct xdp_diag_umem du = {}; 51*a36b38aaSBjörn Töpel int err; 52*a36b38aaSBjörn Töpel 53*a36b38aaSBjörn Töpel if (!umem) 54*a36b38aaSBjörn Töpel return 0; 55*a36b38aaSBjörn Töpel 56*a36b38aaSBjörn Töpel du.id = umem->id; 57*a36b38aaSBjörn Töpel du.size = umem->size; 58*a36b38aaSBjörn Töpel du.num_pages = umem->npgs; 59*a36b38aaSBjörn Töpel du.chunk_size = (__u32)(~umem->chunk_mask + 1); 60*a36b38aaSBjörn Töpel du.headroom = umem->headroom; 61*a36b38aaSBjörn Töpel du.ifindex = umem->dev ? umem->dev->ifindex : 0; 62*a36b38aaSBjörn Töpel du.queue_id = umem->queue_id; 63*a36b38aaSBjörn Töpel du.flags = 0; 64*a36b38aaSBjörn Töpel if (umem->zc) 65*a36b38aaSBjörn Töpel du.flags |= XDP_DU_F_ZEROCOPY; 66*a36b38aaSBjörn Töpel du.refs = refcount_read(&umem->users); 67*a36b38aaSBjörn Töpel 68*a36b38aaSBjörn Töpel err = nla_put(nlskb, XDP_DIAG_UMEM, sizeof(du), &du); 69*a36b38aaSBjörn Töpel 70*a36b38aaSBjörn Töpel if (!err && umem->fq) 71*a36b38aaSBjörn Töpel err = xsk_diag_put_ring(xs->tx, XDP_DIAG_UMEM_FILL_RING, nlskb); 72*a36b38aaSBjörn Töpel if (!err && umem->cq) { 73*a36b38aaSBjörn Töpel err = xsk_diag_put_ring(xs->tx, XDP_DIAG_UMEM_COMPLETION_RING, 74*a36b38aaSBjörn Töpel nlskb); 75*a36b38aaSBjörn Töpel } 76*a36b38aaSBjörn Töpel return err; 77*a36b38aaSBjörn Töpel } 78*a36b38aaSBjörn Töpel 79*a36b38aaSBjörn Töpel static int xsk_diag_fill(struct sock *sk, struct sk_buff *nlskb, 80*a36b38aaSBjörn Töpel struct xdp_diag_req *req, 81*a36b38aaSBjörn Töpel struct user_namespace *user_ns, 82*a36b38aaSBjörn Töpel u32 portid, u32 seq, u32 flags, int sk_ino) 83*a36b38aaSBjörn Töpel { 84*a36b38aaSBjörn Töpel struct xdp_sock *xs = xdp_sk(sk); 85*a36b38aaSBjörn Töpel struct xdp_diag_msg *msg; 86*a36b38aaSBjörn Töpel struct nlmsghdr *nlh; 87*a36b38aaSBjörn Töpel 88*a36b38aaSBjörn Töpel nlh = nlmsg_put(nlskb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*msg), 89*a36b38aaSBjörn Töpel flags); 90*a36b38aaSBjörn Töpel if (!nlh) 91*a36b38aaSBjörn Töpel return -EMSGSIZE; 92*a36b38aaSBjörn Töpel 93*a36b38aaSBjörn Töpel msg = nlmsg_data(nlh); 94*a36b38aaSBjörn Töpel memset(msg, 0, sizeof(*msg)); 95*a36b38aaSBjörn Töpel msg->xdiag_family = AF_XDP; 96*a36b38aaSBjörn Töpel msg->xdiag_type = sk->sk_type; 97*a36b38aaSBjörn Töpel msg->xdiag_ino = sk_ino; 98*a36b38aaSBjörn Töpel sock_diag_save_cookie(sk, msg->xdiag_cookie); 99*a36b38aaSBjörn Töpel 100*a36b38aaSBjörn Töpel if ((req->xdiag_show & XDP_SHOW_INFO) && xsk_diag_put_info(xs, nlskb)) 101*a36b38aaSBjörn Töpel goto out_nlmsg_trim; 102*a36b38aaSBjörn Töpel 103*a36b38aaSBjörn Töpel if ((req->xdiag_show & XDP_SHOW_INFO) && 104*a36b38aaSBjörn Töpel nla_put_u32(nlskb, XDP_DIAG_UID, 105*a36b38aaSBjörn Töpel from_kuid_munged(user_ns, sock_i_uid(sk)))) 106*a36b38aaSBjörn Töpel goto out_nlmsg_trim; 107*a36b38aaSBjörn Töpel 108*a36b38aaSBjörn Töpel if ((req->xdiag_show & XDP_SHOW_RING_CFG) && 109*a36b38aaSBjörn Töpel xsk_diag_put_rings_cfg(xs, nlskb)) 110*a36b38aaSBjörn Töpel goto out_nlmsg_trim; 111*a36b38aaSBjörn Töpel 112*a36b38aaSBjörn Töpel if ((req->xdiag_show & XDP_SHOW_UMEM) && 113*a36b38aaSBjörn Töpel xsk_diag_put_umem(xs, nlskb)) 114*a36b38aaSBjörn Töpel goto out_nlmsg_trim; 115*a36b38aaSBjörn Töpel 116*a36b38aaSBjörn Töpel if ((req->xdiag_show & XDP_SHOW_MEMINFO) && 117*a36b38aaSBjörn Töpel sock_diag_put_meminfo(sk, nlskb, XDP_DIAG_MEMINFO)) 118*a36b38aaSBjörn Töpel goto out_nlmsg_trim; 119*a36b38aaSBjörn Töpel 120*a36b38aaSBjörn Töpel nlmsg_end(nlskb, nlh); 121*a36b38aaSBjörn Töpel return 0; 122*a36b38aaSBjörn Töpel 123*a36b38aaSBjörn Töpel out_nlmsg_trim: 124*a36b38aaSBjörn Töpel nlmsg_cancel(nlskb, nlh); 125*a36b38aaSBjörn Töpel return -EMSGSIZE; 126*a36b38aaSBjörn Töpel } 127*a36b38aaSBjörn Töpel 128*a36b38aaSBjörn Töpel static int xsk_diag_dump(struct sk_buff *nlskb, struct netlink_callback *cb) 129*a36b38aaSBjörn Töpel { 130*a36b38aaSBjörn Töpel struct xdp_diag_req *req = nlmsg_data(cb->nlh); 131*a36b38aaSBjörn Töpel struct net *net = sock_net(nlskb->sk); 132*a36b38aaSBjörn Töpel int num = 0, s_num = cb->args[0]; 133*a36b38aaSBjörn Töpel struct sock *sk; 134*a36b38aaSBjörn Töpel 135*a36b38aaSBjörn Töpel mutex_lock(&net->xdp.lock); 136*a36b38aaSBjörn Töpel 137*a36b38aaSBjörn Töpel sk_for_each(sk, &net->xdp.list) { 138*a36b38aaSBjörn Töpel if (!net_eq(sock_net(sk), net)) 139*a36b38aaSBjörn Töpel continue; 140*a36b38aaSBjörn Töpel if (num++ < s_num) 141*a36b38aaSBjörn Töpel continue; 142*a36b38aaSBjörn Töpel 143*a36b38aaSBjörn Töpel if (xsk_diag_fill(sk, nlskb, req, 144*a36b38aaSBjörn Töpel sk_user_ns(NETLINK_CB(cb->skb).sk), 145*a36b38aaSBjörn Töpel NETLINK_CB(cb->skb).portid, 146*a36b38aaSBjörn Töpel cb->nlh->nlmsg_seq, NLM_F_MULTI, 147*a36b38aaSBjörn Töpel sock_i_ino(sk)) < 0) { 148*a36b38aaSBjörn Töpel num--; 149*a36b38aaSBjörn Töpel break; 150*a36b38aaSBjörn Töpel } 151*a36b38aaSBjörn Töpel } 152*a36b38aaSBjörn Töpel 153*a36b38aaSBjörn Töpel mutex_unlock(&net->xdp.lock); 154*a36b38aaSBjörn Töpel cb->args[0] = num; 155*a36b38aaSBjörn Töpel return nlskb->len; 156*a36b38aaSBjörn Töpel } 157*a36b38aaSBjörn Töpel 158*a36b38aaSBjörn Töpel static int xsk_diag_handler_dump(struct sk_buff *nlskb, struct nlmsghdr *hdr) 159*a36b38aaSBjörn Töpel { 160*a36b38aaSBjörn Töpel struct netlink_dump_control c = { .dump = xsk_diag_dump }; 161*a36b38aaSBjörn Töpel int hdrlen = sizeof(struct xdp_diag_req); 162*a36b38aaSBjörn Töpel struct net *net = sock_net(nlskb->sk); 163*a36b38aaSBjörn Töpel 164*a36b38aaSBjörn Töpel if (nlmsg_len(hdr) < hdrlen) 165*a36b38aaSBjörn Töpel return -EINVAL; 166*a36b38aaSBjörn Töpel 167*a36b38aaSBjörn Töpel if (!(hdr->nlmsg_flags & NLM_F_DUMP)) 168*a36b38aaSBjörn Töpel return -EOPNOTSUPP; 169*a36b38aaSBjörn Töpel 170*a36b38aaSBjörn Töpel return netlink_dump_start(net->diag_nlsk, nlskb, hdr, &c); 171*a36b38aaSBjörn Töpel } 172*a36b38aaSBjörn Töpel 173*a36b38aaSBjörn Töpel static const struct sock_diag_handler xsk_diag_handler = { 174*a36b38aaSBjörn Töpel .family = AF_XDP, 175*a36b38aaSBjörn Töpel .dump = xsk_diag_handler_dump, 176*a36b38aaSBjörn Töpel }; 177*a36b38aaSBjörn Töpel 178*a36b38aaSBjörn Töpel static int __init xsk_diag_init(void) 179*a36b38aaSBjörn Töpel { 180*a36b38aaSBjörn Töpel return sock_diag_register(&xsk_diag_handler); 181*a36b38aaSBjörn Töpel } 182*a36b38aaSBjörn Töpel 183*a36b38aaSBjörn Töpel static void __exit xsk_diag_exit(void) 184*a36b38aaSBjörn Töpel { 185*a36b38aaSBjörn Töpel sock_diag_unregister(&xsk_diag_handler); 186*a36b38aaSBjörn Töpel } 187*a36b38aaSBjörn Töpel 188*a36b38aaSBjörn Töpel module_init(xsk_diag_init); 189*a36b38aaSBjörn Töpel module_exit(xsk_diag_exit); 190*a36b38aaSBjörn Töpel MODULE_LICENSE("GPL"); 191*a36b38aaSBjörn Töpel MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, AF_XDP); 192