if (get_integer(&mtu, *argv, 0))
invarg("Invalid \"mtu\" value\n", *argv);
addattr_l(&req->n, sizeof(*req), IFLA_MTU, &mtu, 4);
- } else if (strcmp(*argv, "xdp") == 0) {
+ } else if (strcmp(*argv, "xdpgeneric") == 0 ||
+ strcmp(*argv, "xdp") == 0) {
+ bool generic = strcmp(*argv, "xdpgeneric") == 0;
+
NEXT_ARG();
- if (xdp_parse(&argc, &argv, req))
+ if (xdp_parse(&argc, &argv, req, generic))
exit(-1);
} else if (strcmp(*argv, "netns") == 0) {
NEXT_ARG();
extern int force;
+struct xdp_req {
+ struct iplink_req *req;
+ __u32 flags;
+};
+
static void xdp_ebpf_cb(void *raw, int fd, const char *annotation)
{
- __u32 flags = !force ? XDP_FLAGS_UPDATE_IF_NOEXIST : 0;
- struct iplink_req *req = raw;
- struct rtattr *xdp;
+ struct xdp_req *xdp = raw;
+ struct iplink_req *req = xdp->req;
+ struct rtattr *xdp_attr;
- xdp = addattr_nest(&req->n, sizeof(*req), IFLA_XDP);
+ xdp_attr = addattr_nest(&req->n, sizeof(*req), IFLA_XDP);
addattr32(&req->n, sizeof(*req), IFLA_XDP_FD, fd);
- addattr32(&req->n, sizeof(*req), IFLA_XDP_FLAGS, flags);
- addattr_nest_end(&req->n, xdp);
+ 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 iplink_req *req)
+static int xdp_delete(struct xdp_req *xdp)
{
- xdp_ebpf_cb(req, -1, NULL);
+ xdp_ebpf_cb(xdp, -1, NULL);
return 0;
}
-int xdp_parse(int *argc, char ***argv, struct iplink_req *req)
+int xdp_parse(int *argc, char ***argv, struct iplink_req *req, bool generic)
{
struct bpf_cfg_in cfg = {
.argc = *argc,
.argv = *argv,
};
+ struct xdp_req xdp = {
+ .req = req,
+ };
+
+ if (!force)
+ xdp.flags |= XDP_FLAGS_UPDATE_IF_NOEXIST;
+ if (generic)
+ xdp.flags |= XDP_FLAGS_SKB_MODE;
if (*argc == 1) {
if (strcmp(**argv, "none") == 0 ||
strcmp(**argv, "off") == 0)
- return xdp_delete(req);
+ return xdp_delete(&xdp);
}
- if (bpf_parse_common(BPF_PROG_TYPE_XDP, &cfg, &bpf_cb_ops, req))
+
+ if (bpf_parse_common(BPF_PROG_TYPE_XDP, &cfg, &bpf_cb_ops, &xdp))
return -1;
*argc = cfg.argc;
void xdp_dump(FILE *fp, struct rtattr *xdp)
{
struct rtattr *tb[IFLA_XDP_MAX + 1];
+ __u32 flags = 0;
parse_rtattr_nested(tb, IFLA_XDP_MAX, xdp);
+
if (!tb[IFLA_XDP_ATTACHED] ||
!rta_getattr_u8(tb[IFLA_XDP_ATTACHED]))
return;
- fprintf(fp, "xdp ");
- /* More to come here in future for 'ip -d link' (digest, etc) ... */
+ if (tb[IFLA_XDP_FLAGS])
+ flags = rta_getattr_u32(tb[IFLA_XDP_FLAGS]);
+
+ fprintf(fp, "xdp%s ",
+ flags & XDP_FLAGS_SKB_MODE ? "generic" : "");
}
.RB "[ " port_guid " eui64 ] ]"
.br
.in -9
-.RB "[ " xdp " { " off " | "
+.RB "[ { " xdp " | " xdpgeneric " } { " off " | "
.br
.in +8
.BR object
.TP
.B xdp object "|" pinned "|" off
-set (or unset) a XDP ("express data path") BPF program to run on every
+set (or unset) a XDP ("eXpress Data Path") BPF program to run on every
packet at driver level.
+.B ip link
+output will indicate a
+.B xdp
+flag for the networking device. If the driver does not have native XDP
+support, the kernel will fall back to a slower, driver-independent "generic"
+XDP variant. The
+.B ip link
+output will in that case indicate
+.B xdpgeneric
+instead of
+.B xdp
+only. If the driver does have native XDP support, but the program is
+loaded under
+.B xdpgeneric object "|" pinned
+then the kernel will use the generic XDP variant instead of the native one.
.B off
(or