summaryrefslogtreecommitdiffstats
path: root/ip/iplink_xdp.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--ip/iplink_xdp.c199
1 files changed, 199 insertions, 0 deletions
diff --git a/ip/iplink_xdp.c b/ip/iplink_xdp.c
new file mode 100644
index 0000000..4a490bc
--- /dev/null
+++ b/ip/iplink_xdp.c
@@ -0,0 +1,199 @@
+/*
+ * iplink_xdp.c XDP program loader
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Daniel Borkmann <daniel@iogearbox.net>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <linux/bpf.h>
+
+#include "bpf_util.h"
+#include "utils.h"
+#include "ip_common.h"
+
+extern int force;
+
+struct xdp_req {
+ struct iplink_req *req;
+ __u32 flags;
+};
+
+static void xdp_ebpf_cb(void *raw, int fd, const char *annotation)
+{
+ struct xdp_req *xdp = raw;
+ struct iplink_req *req = xdp->req;
+ struct rtattr *xdp_attr;
+
+ xdp_attr = addattr_nest(&req->n, sizeof(*req), IFLA_XDP);
+ addattr32(&req->n, sizeof(*req), IFLA_XDP_FD, fd);
+ if (xdp->flags)
+ addattr32(&req->n, sizeof(*req), IFLA_XDP_FLAGS, xdp->flags);
+ addattr_nest_end(&req->n, xdp_attr);
+}
+
+static const struct bpf_cfg_ops bpf_cb_ops = {
+ .ebpf_cb = xdp_ebpf_cb,
+};
+
+static int xdp_delete(struct xdp_req *xdp)
+{
+ xdp_ebpf_cb(xdp, -1, NULL);
+ return 0;
+}
+
+int xdp_parse(int *argc, char ***argv, struct iplink_req *req,
+ const char *ifname, bool generic, bool drv, bool offload)
+{
+ struct bpf_cfg_in cfg = {
+ .type = BPF_PROG_TYPE_XDP,
+ .argc = *argc,
+ .argv = *argv,
+ };
+ struct xdp_req xdp = {
+ .req = req,
+ };
+
+ if (offload) {
+ int ifindex = ll_name_to_index(ifname);
+
+ if (!ifindex)
+ incomplete_command();
+ cfg.ifindex = ifindex;
+ }
+
+ if (!force)
+ xdp.flags |= XDP_FLAGS_UPDATE_IF_NOEXIST;
+ if (generic)
+ xdp.flags |= XDP_FLAGS_SKB_MODE;
+ if (drv)
+ xdp.flags |= XDP_FLAGS_DRV_MODE;
+ if (offload)
+ xdp.flags |= XDP_FLAGS_HW_MODE;
+
+ if (*argc == 1) {
+ if (strcmp(**argv, "none") == 0 ||
+ strcmp(**argv, "off") == 0)
+ return xdp_delete(&xdp);
+ }
+
+ if (bpf_parse_and_load_common(&cfg, &bpf_cb_ops, &xdp))
+ return -1;
+
+ *argc = cfg.argc;
+ *argv = cfg.argv;
+ return 0;
+}
+
+static void xdp_dump_json_one(struct rtattr *tb[IFLA_XDP_MAX + 1], __u32 attr,
+ __u8 mode)
+{
+ if (!tb[attr])
+ return;
+
+ open_json_object(NULL);
+ print_uint(PRINT_JSON, "mode", NULL, mode);
+ bpf_dump_prog_info(NULL, rta_getattr_u32(tb[attr]));
+ close_json_object();
+}
+
+static void xdp_dump_json(struct rtattr *tb[IFLA_XDP_MAX + 1])
+{
+ __u32 prog_id = 0;
+ __u8 mode;
+
+ mode = rta_getattr_u8(tb[IFLA_XDP_ATTACHED]);
+ if (tb[IFLA_XDP_PROG_ID])
+ prog_id = rta_getattr_u32(tb[IFLA_XDP_PROG_ID]);
+
+ open_json_object("xdp");
+ print_uint(PRINT_JSON, "mode", NULL, mode);
+ if (prog_id)
+ bpf_dump_prog_info(NULL, prog_id);
+
+ open_json_array(PRINT_JSON, "attached");
+ if (tb[IFLA_XDP_SKB_PROG_ID] ||
+ tb[IFLA_XDP_DRV_PROG_ID] ||
+ tb[IFLA_XDP_HW_PROG_ID]) {
+ xdp_dump_json_one(tb, IFLA_XDP_SKB_PROG_ID, XDP_ATTACHED_SKB);
+ xdp_dump_json_one(tb, IFLA_XDP_DRV_PROG_ID, XDP_ATTACHED_DRV);
+ xdp_dump_json_one(tb, IFLA_XDP_HW_PROG_ID, XDP_ATTACHED_HW);
+ } else if (tb[IFLA_XDP_PROG_ID]) {
+ /* Older kernel - use IFLA_XDP_PROG_ID */
+ xdp_dump_json_one(tb, IFLA_XDP_PROG_ID, mode);
+ }
+ close_json_array(PRINT_JSON, NULL);
+
+ close_json_object();
+}
+
+static void xdp_dump_prog_one(FILE *fp, struct rtattr *tb[IFLA_XDP_MAX + 1],
+ __u32 attr, bool link, bool details,
+ const char *pfx)
+{
+ __u32 prog_id;
+
+ if (!tb[attr])
+ return;
+
+ prog_id = rta_getattr_u32(tb[attr]);
+ if (!details) {
+ if (prog_id && !link && attr == IFLA_XDP_PROG_ID)
+ fprintf(fp, "/id:%u", prog_id);
+ return;
+ }
+
+ if (prog_id) {
+ fprintf(fp, "%s prog/xdp%s ", _SL_, pfx);
+ bpf_dump_prog_info(fp, prog_id);
+ }
+}
+
+void xdp_dump(FILE *fp, struct rtattr *xdp, bool link, bool details)
+{
+ struct rtattr *tb[IFLA_XDP_MAX + 1];
+ __u8 mode;
+
+ parse_rtattr_nested(tb, IFLA_XDP_MAX, xdp);
+
+ if (!tb[IFLA_XDP_ATTACHED])
+ return;
+
+ mode = rta_getattr_u8(tb[IFLA_XDP_ATTACHED]);
+ if (mode == XDP_ATTACHED_NONE)
+ return;
+ else if (is_json_context())
+ return details ? (void)0 : xdp_dump_json(tb);
+ else if (details && link)
+ /* don't print mode */;
+ else if (mode == XDP_ATTACHED_DRV)
+ fprintf(fp, "xdp");
+ else if (mode == XDP_ATTACHED_SKB)
+ fprintf(fp, "xdpgeneric");
+ else if (mode == XDP_ATTACHED_HW)
+ fprintf(fp, "xdpoffload");
+ else if (mode == XDP_ATTACHED_MULTI)
+ fprintf(fp, "xdpmulti");
+ else
+ fprintf(fp, "xdp[%u]", mode);
+
+ xdp_dump_prog_one(fp, tb, IFLA_XDP_PROG_ID, link, details, "");
+
+ if (mode == XDP_ATTACHED_MULTI) {
+ xdp_dump_prog_one(fp, tb, IFLA_XDP_SKB_PROG_ID, link, details,
+ "generic");
+ xdp_dump_prog_one(fp, tb, IFLA_XDP_DRV_PROG_ID, link, details,
+ "drv");
+ xdp_dump_prog_one(fp, tb, IFLA_XDP_HW_PROG_ID, link, details,
+ "offload");
+ }
+
+ if (!details || !link)
+ fprintf(fp, " ");
+}