iproute2/ip/iplink_xdp.c
Jakub Kicinski da083b5a48 iplink: add support for reporting multiple XDP programs
Kernel now supports attaching XDP programs in the driver
and hardware at the same time.  Print that information
correctly.

In case there are multiple programs attached kernel will
not provide IFLA_XDP_PROG_ID, so don't expect it to be
there (this also improves the printing for very old kernels
slightly, as it avoids unnecessary "prog/xdp" line).

In short mode preserve the current outputs but don't print
IDs if there are multiple.

6: netdevsim0: <BROADCAST,NOARP> mtu 1500 xdpoffload/id:11 qdisc [...]

and:

6: netdevsim0: <BROADCAST,NOARP> mtu 1500 xdpmulti qdisc [...]

ip link output will keep using prog/xdp prefix if only one program
is attached, but can also print multiple program lines:

    prog/xdp id 8 tag fc7a51d1a693a99e jited

vs:

    prog/xdpdrv id 8 tag fc7a51d1a693a99e jited
    prog/xdpoffload id 9 tag fc7a51d1a693a99e

JSON output gains a new array called "attached" which will
contain the full list of attached programs along with their
attachment modes:

        "xdp": {
            "mode": 3,
            "prog": {
                "id": 11,
                "tag": "fc7a51d1a693a99e",
                "jited": 0
            },
            "attached": [ {
                    "mode": 3,
                    "prog": {
                        "id": 11,
                        "tag": "fc7a51d1a693a99e",
                        "jited": 0
                    }
                } ]
        },

In case there are multiple programs attached the general "xdp"
section will not contain program information:

        "xdp": {
            "mode": 4,
            "attached": [ {
                    "mode": 1,
                    "prog": {
                        "id": 10,
                        "tag": "fc7a51d1a693a99e",
                        "jited": 1
                    }
                },{
                    "mode": 3,
                    "prog": {
                        "id": 11,
                        "tag": "fc7a51d1a693a99e",
                        "jited": 0
                    }
                } ]
        },

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David Ahern <dsahern@gmail.com>
2018-07-15 13:10:03 -07:00

200 lines
4.6 KiB
C

/*
* 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, " ");
}