@@ -26,7 +26,8 @@ NET COMMANDS
| **bpftool** **net help**
|
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
-| *ATTACH_TYPE* := { **xdp** | **xdpgeneric** | **xdpdrv** | **xdpoffload** }
+| *ATTACH_TYPE* :=
+| { **xdp** | **xdpgeneric** | **xdpdrv** | **xdpoffload** | **xdp_egress** }
DESCRIPTION
===========
@@ -63,6 +64,7 @@ DESCRIPTION
**xdpgeneric** - Generic XDP. runs at generic XDP hook when packet already enters receive path as skb;
**xdpdrv** - Native XDP. runs earliest point in driver's receive path;
**xdpoffload** - Offload XDP. runs directly on NIC on each packet reception;
+ **xdp_egress** - XDP in egress path. runs at core networking level;
**bpftool** **net detach** *ATTACH_TYPE* **dev** *NAME*
Detach bpf program attached to network interface *NAME* with
@@ -1003,7 +1003,7 @@ _bpftool()
;;
net)
local PROG_TYPE='id pinned tag name'
- local ATTACH_TYPES='xdp xdpgeneric xdpdrv xdpoffload'
+ local ATTACH_TYPES='xdp xdpgeneric xdpdrv xdpoffload xdp_egress'
case $command in
show|list)
[[ $prev != "$command" ]] && return 0
@@ -230,7 +230,7 @@ void btf_dump_linfo_json(const struct btf *btf,
struct nlattr;
struct ifinfomsg;
struct tcmsg;
-int do_xdp_dump(struct ifinfomsg *ifinfo, struct nlattr **tb);
+int do_xdp_dump(struct ifinfomsg *ifinfo, struct nlattr **tb, bool egress);
int do_filter_dump(struct tcmsg *ifinfo, struct nlattr **tb, const char *kind,
const char *devname, int ifindex);
@@ -32,6 +32,7 @@ struct bpf_netdev_t {
int used_len;
int array_len;
int filter_idx;
+ bool egress;
};
struct tc_kind_handle {
@@ -61,6 +62,7 @@ enum net_attach_type {
NET_ATTACH_TYPE_XDP_GENERIC,
NET_ATTACH_TYPE_XDP_DRIVER,
NET_ATTACH_TYPE_XDP_OFFLOAD,
+ NET_ATTACH_TYPE_XDP_EGRESS,
};
static const char * const attach_type_strings[] = {
@@ -68,6 +70,7 @@ static const char * const attach_type_strings[] = {
[NET_ATTACH_TYPE_XDP_GENERIC] = "xdpgeneric",
[NET_ATTACH_TYPE_XDP_DRIVER] = "xdpdrv",
[NET_ATTACH_TYPE_XDP_OFFLOAD] = "xdpoffload",
+ [NET_ATTACH_TYPE_XDP_EGRESS] = "xdp_egress",
};
const size_t net_attach_type_size = ARRAY_SIZE(attach_type_strings);
@@ -111,7 +114,7 @@ static int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb)
: "");
netinfo->used_len++;
- return do_xdp_dump(ifinfo, tb);
+ return do_xdp_dump(ifinfo, tb, netinfo->egress);
}
static int dump_class_qdisc_nlmsg(void *cookie, void *msg, struct nlattr **tb)
@@ -276,10 +279,20 @@ static int net_parse_dev(int *argc, char ***argv)
static int do_attach_detach_xdp(int progfd, enum net_attach_type attach_type,
int ifindex, bool overwrite)
{
- __u32 flags = 0;
+ struct bpf_xdp_set_link_opts opts;
+ __u32 flags = 0, id = 0;
+ int rc;
+
+ memset(&opts, 0, sizeof(opts));
+ opts.sz = sizeof(opts);
+ opts.old_fd = -1;
if (!overwrite)
flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
+
+ if (attach_type == NET_ATTACH_TYPE_XDP_EGRESS)
+ opts.egress = 1;
+
if (attach_type == NET_ATTACH_TYPE_XDP_GENERIC)
flags |= XDP_FLAGS_SKB_MODE;
if (attach_type == NET_ATTACH_TYPE_XDP_DRIVER)
@@ -287,7 +300,25 @@ static int do_attach_detach_xdp(int progfd, enum net_attach_type attach_type,
if (attach_type == NET_ATTACH_TYPE_XDP_OFFLOAD)
flags |= XDP_FLAGS_HW_MODE;
- return bpf_set_link_xdp_fd(ifindex, progfd, flags);
+ if (opts.egress)
+ rc = bpf_get_link_xdp_egress_id(ifindex, &id, flags);
+ else
+ rc = bpf_get_link_xdp_id(ifindex, &id, flags);
+
+ if (rc) {
+ p_err("Failed to get existing prog id for device");
+ return rc;
+ }
+
+ if (id)
+ opts.old_fd = bpf_prog_get_fd_by_id(id);
+
+ rc = bpf_set_link_xdp_fd_opts(ifindex, progfd, flags, &opts);
+
+ if (opts.old_fd != -1)
+ close(opts.old_fd);
+
+ return rc;
}
static int do_attach(int argc, char **argv)
@@ -411,6 +442,7 @@ static int do_show(int argc, char **argv)
dev_array.used_len = 0;
dev_array.array_len = 0;
dev_array.filter_idx = filter_idx;
+ dev_array.egress = 0;
if (json_output)
jsonw_start_array(json_wtr);
@@ -419,6 +451,14 @@ static int do_show(int argc, char **argv)
ret = libbpf_nl_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array);
NET_END_ARRAY("\n");
+ if (!ret) {
+ dev_array.egress = true;
+ NET_START_ARRAY("xdp_egress", "%s:\n");
+ ret = libbpf_nl_get_link(sock, nl_pid, dump_link_nlmsg,
+ &dev_array);
+ NET_END_ARRAY("\n");
+ }
+
if (!ret) {
NET_START_ARRAY("tc", "%s:\n");
for (i = 0; i < dev_array.used_len; i++) {
@@ -464,7 +504,7 @@ static int do_help(int argc, char **argv)
" %s %s help\n"
"\n"
" " HELP_SPEC_PROGRAM "\n"
- " ATTACH_TYPE := { xdp | xdpgeneric | xdpdrv | xdpoffload }\n"
+ " ATTACH_TYPE := { xdp | xdpgeneric | xdpdrv | xdpoffload | xdp_egress}\n"
"\n"
"Note: Only xdp and tc attachments are supported now.\n"
" For progs attached to cgroups, use \"bpftool cgroup\"\n"
@@ -55,6 +55,7 @@ static int do_xdp_dump_one(struct nlattr *attr, unsigned int ifindex,
xdp_dump_prog_id(tb, IFLA_XDP_SKB_PROG_ID, "generic", true);
xdp_dump_prog_id(tb, IFLA_XDP_DRV_PROG_ID, "driver", true);
xdp_dump_prog_id(tb, IFLA_XDP_HW_PROG_ID, "offload", true);
+ xdp_dump_prog_id(tb, IFLA_XDP_EGRESS_CORE_PROG_ID, "core", true);
if (json_output)
jsonw_end_array(json_wtr);
} else if (mode == XDP_ATTACHED_DRV) {
@@ -63,18 +64,23 @@ static int do_xdp_dump_one(struct nlattr *attr, unsigned int ifindex,
xdp_dump_prog_id(tb, IFLA_XDP_PROG_ID, "generic", false);
} else if (mode == XDP_ATTACHED_HW) {
xdp_dump_prog_id(tb, IFLA_XDP_PROG_ID, "offload", false);
+ } else if (mode == XDP_ATTACHED_EGRESS_CORE) {
+ xdp_dump_prog_id(tb, IFLA_XDP_EGRESS_CORE_PROG_ID, "core",
+ false);
}
NET_END_OBJECT_FINAL;
return 0;
}
-int do_xdp_dump(struct ifinfomsg *ifinfo, struct nlattr **tb)
+int do_xdp_dump(struct ifinfomsg *ifinfo, struct nlattr **tb, bool egress)
{
- if (!tb[IFLA_XDP])
+ __u16 atype = egress ? IFLA_XDP_EGRESS : IFLA_XDP;
+
+ if (!tb[atype])
return 0;
- return do_xdp_dump_one(tb[IFLA_XDP], ifinfo->ifi_index,
+ return do_xdp_dump_one(tb[atype], ifinfo->ifi_index,
libbpf_nla_getattr_str(tb[IFLA_IFNAME]));
}