+static int xdp_stats_print(struct sockstat *s, const struct filter *f)
+{
+ const char *addr, *port;
+ char q_str[16];
+
+ s->local.family = s->remote.family = AF_XDP;
+
+ if (f->f) {
+ if (run_ssfilter(f->f, s) == 0)
+ return 1;
+ }
+
+ sock_state_print(s);
+
+ if (s->iface) {
+ addr = xll_index_to_name(s->iface);
+ snprintf(q_str, sizeof(q_str), "q%d", s->lport);
+ port = q_str;
+ sock_addr_print(addr, ":", port, NULL);
+ } else {
+ sock_addr_print("", "*", "", NULL);
+ }
+
+ sock_addr_print("", "*", "", NULL);
+
+ proc_ctx_print(s);
+
+ if (show_details)
+ sock_details_print(s);
+
+ return 0;
+}
+
+static void xdp_show_ring(const char *name, struct xdp_diag_ring *ring)
+{
+ if (oneline)
+ out(" %s(", name);
+ else
+ out("\n\t%s(", name);
+ out("entries:%u", ring->entries);
+ out(")");
+}
+
+static void xdp_show_umem(struct xdp_diag_umem *umem, struct xdp_diag_ring *fr,
+ struct xdp_diag_ring *cr)
+{
+ if (oneline)
+ out(" tumem(");
+ else
+ out("\n\tumem(");
+ out("id:%u", umem->id);
+ out(",size:%llu", umem->size);
+ out(",num_pages:%u", umem->num_pages);
+ out(",chunk_size:%u", umem->chunk_size);
+ out(",headroom:%u", umem->headroom);
+ out(",ifindex:%u", umem->ifindex);
+ out(",qid:%u", umem->queue_id);
+ out(",zc:%u", umem->flags & XDP_DU_F_ZEROCOPY);
+ out(",refs:%u", umem->refs);
+ out(")");
+
+ if (fr)
+ xdp_show_ring("fr", fr);
+ if (cr)
+ xdp_show_ring("cr", cr);
+}
+
+static void xdp_show_stats(struct xdp_diag_stats *stats)
+{
+ if (oneline)
+ out(" stats(");
+ else
+ out("\n\tstats(");
+ out("rx dropped:%llu", stats->n_rx_dropped);
+ out(",rx invalid:%llu", stats->n_rx_invalid);
+ out(",rx queue full:%llu", stats->n_rx_full);
+ out(",rx fill ring empty:%llu", stats->n_fill_ring_empty);
+ out(",tx invalid:%llu", stats->n_tx_invalid);
+ out(",tx ring empty:%llu", stats->n_tx_ring_empty);
+ out(")");
+}
+
+static int xdp_show_sock(struct nlmsghdr *nlh, void *arg)
+{
+ struct xdp_diag_ring *rx = NULL, *tx = NULL, *fr = NULL, *cr = NULL;
+ struct xdp_diag_msg *msg = NLMSG_DATA(nlh);
+ struct rtattr *tb[XDP_DIAG_MAX + 1];
+ struct xdp_diag_info *info = NULL;
+ struct xdp_diag_umem *umem = NULL;
+ struct xdp_diag_stats *stats = NULL;
+ const struct filter *f = arg;
+ struct sockstat stat = {};
+
+ parse_rtattr(tb, XDP_DIAG_MAX, (struct rtattr *)(msg + 1),
+ nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*msg)));
+
+ stat.type = msg->xdiag_type;
+ stat.ino = msg->xdiag_ino;
+ stat.state = SS_CLOSE;
+ stat.sk = cookie_sk_get(&msg->xdiag_cookie[0]);
+
+ if (tb[XDP_DIAG_INFO]) {
+ info = RTA_DATA(tb[XDP_DIAG_INFO]);
+ stat.iface = info->ifindex;
+ stat.lport = info->queue_id;
+ }
+
+ if (tb[XDP_DIAG_UID])
+ stat.uid = rta_getattr_u32(tb[XDP_DIAG_UID]);
+ if (tb[XDP_DIAG_RX_RING])
+ rx = RTA_DATA(tb[XDP_DIAG_RX_RING]);
+ if (tb[XDP_DIAG_TX_RING])
+ tx = RTA_DATA(tb[XDP_DIAG_TX_RING]);
+ if (tb[XDP_DIAG_UMEM])
+ umem = RTA_DATA(tb[XDP_DIAG_UMEM]);
+ if (tb[XDP_DIAG_UMEM_FILL_RING])
+ fr = RTA_DATA(tb[XDP_DIAG_UMEM_FILL_RING]);
+ if (tb[XDP_DIAG_UMEM_COMPLETION_RING])
+ cr = RTA_DATA(tb[XDP_DIAG_UMEM_COMPLETION_RING]);
+ if (tb[XDP_DIAG_MEMINFO]) {
+ __u32 *skmeminfo = RTA_DATA(tb[XDP_DIAG_MEMINFO]);
+
+ stat.rq = skmeminfo[SK_MEMINFO_RMEM_ALLOC];
+ }
+ if (tb[XDP_DIAG_STATS])
+ stats = RTA_DATA(tb[XDP_DIAG_STATS]);
+
+ if (xdp_stats_print(&stat, f))
+ return 0;
+
+ if (show_details) {
+ if (rx)
+ xdp_show_ring("rx", rx);
+ if (tx)
+ xdp_show_ring("tx", tx);
+ if (umem)
+ xdp_show_umem(umem, fr, cr);
+ if (stats)
+ xdp_show_stats(stats);
+ }
+
+ if (show_mem)
+ print_skmeminfo(tb, XDP_DIAG_MEMINFO); // really?
+
+
+ return 0;
+}
+
+static int xdp_show(struct filter *f)
+{
+ DIAG_REQUEST(req, struct xdp_diag_req r);
+
+ if (!filter_af_get(f, AF_XDP) || !(f->states & (1 << SS_CLOSE)))
+ return 0;
+
+ req.r.sdiag_family = AF_XDP;
+ req.r.xdiag_show = XDP_SHOW_INFO | XDP_SHOW_RING_CFG | XDP_SHOW_UMEM |
+ XDP_SHOW_MEMINFO | XDP_SHOW_STATS;
+
+ return handle_netlink_request(f, &req.nlh, sizeof(req), xdp_show_sock);
+}
+