On 1/14/21 6:27 AM, Hangbin Liu wrote:
This patch add a xdp program on egress to show that we can modify
the packet on egress. In this sample we will set the pkt's src
mac to egress's mac address. The xdp_prog will be attached when
-X option supplied.
Signed-off-by: Hangbin Liu <liuhangbin@xxxxxxxxx>
---
v6: no code update, only rebase the code on latest bpf-next
v5:
a) close fd when err out in get_mac_addr()
b) exit program when both -S and -X supplied.
v4:
a) Update get_mac_addr socket create
b) Load dummy prog regardless of 2nd xdp prog on egress
v3:
a) modify the src mac address based on egress mac
v2:
a) use pkt counter instead of IP ttl modification on egress program
b) make the egress program selectable by option -X
---
samples/bpf/xdp_redirect_map_kern.c | 75 ++++++++++++++--
samples/bpf/xdp_redirect_map_user.c | 135 +++++++++++++++++++++++-----
2 files changed, 184 insertions(+), 26 deletions(-)
diff --git a/samples/bpf/xdp_redirect_map_kern.c b/samples/bpf/xdp_redirect_map_kern.c
index 6489352ab7a4..8b8e73d25ad6 100644
--- a/samples/bpf/xdp_redirect_map_kern.c
+++ b/samples/bpf/xdp_redirect_map_kern.c
@@ -19,12 +19,22 @@
#include <linux/ipv6.h>
#include <bpf/bpf_helpers.h>
+/* The 2nd xdp prog on egress does not support skb mode, so we define two
+ * maps, tx_port_general and tx_port_native.
+ */
struct {
__uint(type, BPF_MAP_TYPE_DEVMAP);
__uint(key_size, sizeof(int));
__uint(value_size, sizeof(int));
__uint(max_entries, 100);
-} tx_port SEC(".maps");
+} tx_port_general SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_DEVMAP);
+ __uint(key_size, sizeof(int));
+ __uint(value_size, sizeof(struct bpf_devmap_val));
+ __uint(max_entries, 100);
+} tx_port_native SEC(".maps");
/* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
* feedback. Redirect TX errors can be caught via a tracepoint.
@@ -36,6 +46,14 @@ struct {
__uint(max_entries, 1);
} rxcnt SEC(".maps");
+/* map to stroe egress interface mac address */
s/stroe/store
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __type(key, u32);
+ __type(value, __be64);
+ __uint(max_entries, 1);
+} tx_mac SEC(".maps");
+
static void swap_src_dst_mac(void *data)
{
unsigned short *p = data;
@@ -52,17 +70,16 @@ static void swap_src_dst_mac(void *data)
p[5] = dst[2];
}
[...]
int main(int argc, char **argv)
{
struct bpf_prog_load_attr prog_load_attr = {
- .prog_type = BPF_PROG_TYPE_XDP,
+ .prog_type = BPF_PROG_TYPE_UNSPEC,
};
- struct bpf_program *prog, *dummy_prog;
+ struct bpf_program *prog, *dummy_prog, *devmap_prog;
+ int devmap_prog_fd_0 = -1, devmap_prog_fd_1 = -1;
The default value is -1 here. I remembered there was a discussion
about the default value here, does default value 0 work here?
+ int prog_fd, dummy_prog_fd;
+ int tx_port_map_fd, tx_mac_map_fd;
+ struct bpf_devmap_val devmap_val;
struct bpf_prog_info info = {};
__u32 info_len = sizeof(info);
- int prog_fd, dummy_prog_fd;
- const char *optstr = "FSN";
+ const char *optstr = "FSNX";
struct bpf_object *obj;
int ret, opt, key = 0;
char filename[256];
- int tx_port_map_fd;
while ((opt = getopt(argc, argv, optstr)) != -1) {
switch (opt) {
@@ -120,14 +154,21 @@ int main(int argc, char **argv)
case 'F':
xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
break;
+ case 'X':
+ xdp_devmap_attached = true;
+ break;
default:
usage(basename(argv[0]));
return 1;
}
}
- if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
+ if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) {
xdp_flags |= XDP_FLAGS_DRV_MODE;
+ } else if (xdp_devmap_attached) {
+ printf("Load xdp program on egress with SKB mode not supported yet\n");
+ return 1;
+ }
if (optind == argc) {
printf("usage: %s <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n", argv[0]);
@@ -150,24 +191,28 @@ int main(int argc, char **argv)
if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
return 1;
- prog = bpf_program__next(NULL, obj);
- dummy_prog = bpf_program__next(prog, obj);
- if (!prog || !dummy_prog) {
- printf("finding a prog in obj file failed\n");
- return 1;
+ if (xdp_flags & XDP_FLAGS_SKB_MODE) {
+ prog = bpf_object__find_program_by_title(obj, "xdp_redirect_general");
libbpf supports each section having multiple programs, so
bpf_object__find_program_by_title() is not recommended.
Could you change to bpf_object__find_program_by_name()?
+ tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_general");
+ } else {
+ prog = bpf_object__find_program_by_title(obj, "xdp_redirect_native");
+ tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_native");
+ }
+ dummy_prog = bpf_object__find_program_by_title(obj, "xdp_redirect_dummy");
+ if (!prog || dummy_prog < 0 || tx_port_map_fd < 0) {
+ printf("finding prog/tx_port_map in obj file failed\n");
+ goto out;
}
- /* bpf_prog_load_xattr gives us the pointer to first prog's fd,
- * so we're missing only the fd for dummy prog
- */
+ prog_fd = bpf_program__fd(prog);
dummy_prog_fd = bpf_program__fd(dummy_prog);
- if (prog_fd < 0 || dummy_prog_fd < 0) {
+ if (prog_fd < 0 || dummy_prog_fd < 0 || tx_port_map_fd < 0) {
printf("bpf_prog_load_xattr: %s\n", strerror(errno));
return 1;
}
- tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port");
+ tx_mac_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_mac");
rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
- if (tx_port_map_fd < 0 || rxcnt_map_fd < 0) {
+ if (tx_mac_map_fd < 0 || rxcnt_map_fd < 0) {
printf("bpf_object__find_map_fd_by_name failed\n");
return 1;
}
[...]