mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 08:33:17 +02:00
Merge branch 'selftests-bpf-migrate-test_xdp_redirect_multi-sh-to-test_progs'
Bastien Curutchet says:
====================
This patch series continues the work to migrate the *.sh tests into
prog_tests framework.
test_xdp_redirect_multi.sh tests the XDP redirections done through
bpf_redirect_map().
This is already partly covered by test_xdp_veth.c that already tests
map redirections at XDP level. What isn't covered yet by test_xdp_veth is
the use of the broadcast flags (BPF_F_BROADCAST or BPF_F_EXCLUDE_INGRESS)
and XDP egress programs.
Hence, this patch series add test cases to test_xdp_veth.c to get rid of
the test_xdp_redirect_multi.sh:
- PATCH 1 & 2 Rework test_xdp_veth.c to avoid using the root namespace
- PATCH 3 and 4 cover the broadcast flags
- PATCH 5 covers the XDP egress programs
NOTE: While working on this iteration I ran into a memory leak in
net/core/rtnetlink.c that leads to oom-kill when running ./test_progs in
a loop. This leak has been fixed by commit 1438f5d07b ("rtnetlink:
fix netns leak with rtnl_setlink()") in the net tree.
====================
Link: https://patch.msgid.link/20250212-redirect-multi-v5-0-fd0d39fca6e6@bootlin.com
Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
This commit is contained in:
commit
0fc6025c95
|
|
@ -100,7 +100,6 @@ TEST_FILES = xsk_prereqs.sh $(wildcard progs/btf_dump_test_case_*.c)
|
|||
|
||||
# Order correspond to 'make run_tests' order
|
||||
TEST_PROGS := test_kmod.sh \
|
||||
test_xdp_redirect_multi.sh \
|
||||
test_tunnel.sh \
|
||||
test_lwt_seg6local.sh \
|
||||
test_lirc_mode2.sh \
|
||||
|
|
@ -135,7 +134,6 @@ TEST_GEN_PROGS_EXTENDED = \
|
|||
veristat \
|
||||
xdp_features \
|
||||
xdp_hw_metadata \
|
||||
xdp_redirect_multi \
|
||||
xdp_synproxy \
|
||||
xdping \
|
||||
xskxceiver
|
||||
|
|
|
|||
|
|
@ -9,7 +9,11 @@
|
|||
* | veth11 | | veth22 | | veth33 |
|
||||
* ----|----- -----|---- -----|----
|
||||
* | | |
|
||||
* veth1 veth2 veth3
|
||||
* ----|------------------|----------------|----
|
||||
* | veth1 veth2 veth3 |
|
||||
* | |
|
||||
* | NSO |
|
||||
* ---------------------------------------------
|
||||
*
|
||||
* Test cases:
|
||||
* - [test_xdp_veth_redirect] : ping veth33 from veth11
|
||||
|
|
@ -24,6 +28,25 @@
|
|||
* | | | | | |
|
||||
* | ------------------ ------------------ |
|
||||
* -----------------------------------------
|
||||
*
|
||||
* - [test_xdp_veth_broadcast_redirect]: broadcast from veth11
|
||||
* - IPv4 ping : BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS
|
||||
* -> echo request received by all except veth11
|
||||
* - IPv4 ping : BPF_F_BROADCAST
|
||||
* -> echo request received by all veth
|
||||
* - [test_xdp_veth_egress]:
|
||||
* - all src mac should be the magic mac
|
||||
*
|
||||
* veth11 veth22 veth33
|
||||
* (XDP_PASS) (XDP_PASS) (XDP_PASS)
|
||||
* | | |
|
||||
* | | |
|
||||
* veth1 veth2 veth3
|
||||
* (XDP_REDIRECT) (XDP_REDIRECT) (XDP_REDIRECT)
|
||||
* | ^ ^
|
||||
* | | |
|
||||
* ----------------------------------------
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
|
@ -32,6 +55,7 @@
|
|||
#include "network_helpers.h"
|
||||
#include "xdp_dummy.skel.h"
|
||||
#include "xdp_redirect_map.skel.h"
|
||||
#include "xdp_redirect_multi_kern.skel.h"
|
||||
#include "xdp_tx.skel.h"
|
||||
#include <uapi/linux/if_link.h>
|
||||
|
||||
|
|
@ -40,6 +64,7 @@
|
|||
#define IP_MAX_LEN 16
|
||||
#define IP_SRC "10.1.1.11"
|
||||
#define IP_DST "10.1.1.33"
|
||||
#define IP_NEIGH "10.1.1.253"
|
||||
#define PROG_NAME_MAX_LEN 128
|
||||
#define NS_NAME_MAX_LEN 32
|
||||
|
||||
|
|
@ -51,27 +76,35 @@ struct veth_configuration {
|
|||
char remote_addr[IP_MAX_LEN]; /* IP address of the remote veth */
|
||||
};
|
||||
|
||||
static const struct veth_configuration default_config[VETH_PAIRS_COUNT] = {
|
||||
struct net_configuration {
|
||||
char ns0_name[NS_NAME_MAX_LEN];
|
||||
struct veth_configuration veth_cfg[VETH_PAIRS_COUNT];
|
||||
};
|
||||
|
||||
static const struct net_configuration default_config = {
|
||||
.ns0_name = "ns0-",
|
||||
{
|
||||
.local_veth = "veth1-",
|
||||
.remote_veth = "veth11",
|
||||
.next_veth = 1,
|
||||
.remote_addr = IP_SRC,
|
||||
.namespace = "ns-veth11-"
|
||||
},
|
||||
{
|
||||
.local_veth = "veth2-",
|
||||
.remote_veth = "veth22",
|
||||
.next_veth = 2,
|
||||
.remote_addr = "",
|
||||
.namespace = "ns-veth22-"
|
||||
},
|
||||
{
|
||||
.local_veth = "veth3-",
|
||||
.remote_veth = "veth33",
|
||||
.next_veth = 0,
|
||||
.remote_addr = IP_DST,
|
||||
.namespace = "ns-veth33-"
|
||||
{
|
||||
.local_veth = "veth1-",
|
||||
.remote_veth = "veth11",
|
||||
.next_veth = 1,
|
||||
.remote_addr = IP_SRC,
|
||||
.namespace = "ns-veth11-"
|
||||
},
|
||||
{
|
||||
.local_veth = "veth2-",
|
||||
.remote_veth = "veth22",
|
||||
.next_veth = 2,
|
||||
.remote_addr = "",
|
||||
.namespace = "ns-veth22-"
|
||||
},
|
||||
{
|
||||
.local_veth = "veth3-",
|
||||
.remote_veth = "veth33",
|
||||
.next_veth = 0,
|
||||
.remote_addr = IP_DST,
|
||||
.namespace = "ns-veth33-"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -83,7 +116,7 @@ struct prog_configuration {
|
|||
};
|
||||
|
||||
static int attach_programs_to_veth_pair(struct bpf_object **objs, size_t nb_obj,
|
||||
struct veth_configuration *net_config,
|
||||
struct net_configuration *net_config,
|
||||
struct prog_configuration *prog, int index)
|
||||
{
|
||||
struct bpf_program *local_prog, *remote_prog;
|
||||
|
|
@ -106,7 +139,7 @@ static int attach_programs_to_veth_pair(struct bpf_object **objs, size_t nb_obj,
|
|||
if (!ASSERT_OK_PTR(remote_prog, "find remote program"))
|
||||
return -1;
|
||||
|
||||
interface = if_nametoindex(net_config[index].local_veth);
|
||||
interface = if_nametoindex(net_config->veth_cfg[index].local_veth);
|
||||
if (!ASSERT_NEQ(interface, 0, "non zero interface index"))
|
||||
return -1;
|
||||
|
||||
|
|
@ -115,11 +148,11 @@ static int attach_programs_to_veth_pair(struct bpf_object **objs, size_t nb_obj,
|
|||
if (!ASSERT_OK(ret, "attach xdp program to local veth"))
|
||||
return -1;
|
||||
|
||||
nstoken = open_netns(net_config[index].namespace);
|
||||
nstoken = open_netns(net_config->veth_cfg[index].namespace);
|
||||
if (!ASSERT_OK_PTR(nstoken, "switch to remote veth namespace"))
|
||||
return -1;
|
||||
|
||||
interface = if_nametoindex(net_config[index].remote_veth);
|
||||
interface = if_nametoindex(net_config->veth_cfg[index].remote_veth);
|
||||
if (!ASSERT_NEQ(interface, 0, "non zero interface index")) {
|
||||
close_netns(nstoken);
|
||||
return -1;
|
||||
|
|
@ -136,55 +169,60 @@ static int attach_programs_to_veth_pair(struct bpf_object **objs, size_t nb_obj,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int create_network(struct veth_configuration *net_config)
|
||||
static int create_network(struct net_configuration *net_config)
|
||||
{
|
||||
struct nstoken *nstoken = NULL;
|
||||
int i, err;
|
||||
|
||||
memcpy(net_config, default_config, VETH_PAIRS_COUNT * sizeof(struct veth_configuration));
|
||||
memcpy(net_config, &default_config, sizeof(struct net_configuration));
|
||||
|
||||
/* Create unique namespaces */
|
||||
err = append_tid(net_config->ns0_name, NS_NAME_MAX_LEN);
|
||||
if (!ASSERT_OK(err, "append TID to ns0 name"))
|
||||
goto fail;
|
||||
SYS(fail, "ip netns add %s", net_config->ns0_name);
|
||||
|
||||
/* First create and configure all interfaces */
|
||||
for (i = 0; i < VETH_PAIRS_COUNT; i++) {
|
||||
err = append_tid(net_config[i].namespace, NS_NAME_MAX_LEN);
|
||||
err = append_tid(net_config->veth_cfg[i].namespace, NS_NAME_MAX_LEN);
|
||||
if (!ASSERT_OK(err, "append TID to ns name"))
|
||||
return -1;
|
||||
|
||||
err = append_tid(net_config[i].local_veth, VETH_NAME_MAX_LEN);
|
||||
if (!ASSERT_OK(err, "append TID to local veth name"))
|
||||
return -1;
|
||||
|
||||
SYS(fail, "ip netns add %s", net_config[i].namespace);
|
||||
SYS(fail, "ip link add %s type veth peer name %s netns %s",
|
||||
net_config[i].local_veth, net_config[i].remote_veth, net_config[i].namespace);
|
||||
SYS(fail, "ip link set dev %s up", net_config[i].local_veth);
|
||||
if (net_config[i].remote_addr[0])
|
||||
SYS(fail, "ip -n %s addr add %s/24 dev %s", net_config[i].namespace,
|
||||
net_config[i].remote_addr, net_config[i].remote_veth);
|
||||
SYS(fail, "ip -n %s link set dev %s up", net_config[i].namespace,
|
||||
net_config[i].remote_veth);
|
||||
goto fail;
|
||||
SYS(fail, "ip netns add %s", net_config->veth_cfg[i].namespace);
|
||||
}
|
||||
|
||||
/* Create interfaces */
|
||||
nstoken = open_netns(net_config->ns0_name);
|
||||
if (!nstoken)
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < VETH_PAIRS_COUNT; i++) {
|
||||
SYS(fail, "ip link add %s type veth peer name %s netns %s",
|
||||
net_config->veth_cfg[i].local_veth, net_config->veth_cfg[i].remote_veth,
|
||||
net_config->veth_cfg[i].namespace);
|
||||
SYS(fail, "ip link set dev %s up", net_config->veth_cfg[i].local_veth);
|
||||
if (net_config->veth_cfg[i].remote_addr[0])
|
||||
SYS(fail, "ip -n %s addr add %s/24 dev %s",
|
||||
net_config->veth_cfg[i].namespace,
|
||||
net_config->veth_cfg[i].remote_addr,
|
||||
net_config->veth_cfg[i].remote_veth);
|
||||
SYS(fail, "ip -n %s link set dev %s up", net_config->veth_cfg[i].namespace,
|
||||
net_config->veth_cfg[i].remote_veth);
|
||||
}
|
||||
|
||||
close_netns(nstoken);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
close_netns(nstoken);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void cleanup_network(struct veth_configuration *net_config)
|
||||
static void cleanup_network(struct net_configuration *net_config)
|
||||
{
|
||||
struct nstoken *nstoken;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < VETH_PAIRS_COUNT; i++) {
|
||||
bpf_xdp_detach(if_nametoindex(net_config[i].local_veth), 0, NULL);
|
||||
nstoken = open_netns(net_config[i].namespace);
|
||||
if (nstoken) {
|
||||
bpf_xdp_detach(if_nametoindex(net_config[i].remote_veth), 0, NULL);
|
||||
close_netns(nstoken);
|
||||
}
|
||||
/* in case the detach failed */
|
||||
SYS_NOFAIL("ip link del %s", net_config[i].local_veth);
|
||||
SYS_NOFAIL("ip netns del %s", net_config[i].namespace);
|
||||
}
|
||||
SYS_NOFAIL("ip netns del %s", net_config->ns0_name);
|
||||
for (i = 0; i < VETH_PAIRS_COUNT; i++)
|
||||
SYS_NOFAIL("ip netns del %s", net_config->veth_cfg[i].namespace);
|
||||
}
|
||||
|
||||
#define VETH_REDIRECT_SKEL_NB 3
|
||||
|
|
@ -210,9 +248,10 @@ static void xdp_veth_redirect(u32 flags)
|
|||
.remote_flags = flags,
|
||||
}
|
||||
};
|
||||
struct veth_configuration net_config[VETH_PAIRS_COUNT];
|
||||
struct bpf_object *bpf_objs[VETH_REDIRECT_SKEL_NB];
|
||||
struct xdp_redirect_map *xdp_redirect_map;
|
||||
struct net_configuration net_config;
|
||||
struct nstoken *nstoken = NULL;
|
||||
struct xdp_dummy *xdp_dummy;
|
||||
struct xdp_tx *xdp_tx;
|
||||
int map_fd;
|
||||
|
|
@ -230,7 +269,7 @@ static void xdp_veth_redirect(u32 flags)
|
|||
if (!ASSERT_OK_PTR(xdp_redirect_map, "xdp_redirect_map__open_and_load"))
|
||||
goto destroy_xdp_tx;
|
||||
|
||||
if (!ASSERT_OK(create_network(net_config), "create network"))
|
||||
if (!ASSERT_OK(create_network(&net_config), "create network"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
/* Then configure the redirect map and attach programs to interfaces */
|
||||
|
|
@ -241,19 +280,24 @@ static void xdp_veth_redirect(u32 flags)
|
|||
bpf_objs[0] = xdp_dummy->obj;
|
||||
bpf_objs[1] = xdp_tx->obj;
|
||||
bpf_objs[2] = xdp_redirect_map->obj;
|
||||
|
||||
nstoken = open_netns(net_config.ns0_name);
|
||||
if (!ASSERT_OK_PTR(nstoken, "open NS0"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
for (i = 0; i < VETH_PAIRS_COUNT; i++) {
|
||||
int next_veth = net_config[i].next_veth;
|
||||
int next_veth = net_config.veth_cfg[i].next_veth;
|
||||
int interface_id;
|
||||
int err;
|
||||
|
||||
interface_id = if_nametoindex(net_config[next_veth].local_veth);
|
||||
interface_id = if_nametoindex(net_config.veth_cfg[next_veth].local_veth);
|
||||
if (!ASSERT_NEQ(interface_id, 0, "non zero interface index"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
err = bpf_map_update_elem(map_fd, &i, &interface_id, BPF_ANY);
|
||||
if (!ASSERT_OK(err, "configure interface redirection through map"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
if (attach_programs_to_veth_pair(bpf_objs, VETH_REDIRECT_SKEL_NB,
|
||||
net_config, ping_config, i))
|
||||
&net_config, ping_config, i))
|
||||
goto destroy_xdp_redirect_map;
|
||||
}
|
||||
|
||||
|
|
@ -261,16 +305,250 @@ static void xdp_veth_redirect(u32 flags)
|
|||
* veth33 from veth11
|
||||
*/
|
||||
ASSERT_OK(SYS_NOFAIL("ip netns exec %s ping -c 1 -W 1 %s > /dev/null",
|
||||
net_config[0].namespace, IP_DST), "ping");
|
||||
net_config.veth_cfg[0].namespace, IP_DST), "ping");
|
||||
|
||||
destroy_xdp_redirect_map:
|
||||
close_netns(nstoken);
|
||||
xdp_redirect_map__destroy(xdp_redirect_map);
|
||||
destroy_xdp_tx:
|
||||
xdp_tx__destroy(xdp_tx);
|
||||
destroy_xdp_dummy:
|
||||
xdp_dummy__destroy(xdp_dummy);
|
||||
|
||||
cleanup_network(net_config);
|
||||
cleanup_network(&net_config);
|
||||
}
|
||||
|
||||
#define BROADCAST_REDIRECT_SKEL_NB 2
|
||||
static void xdp_veth_broadcast_redirect(u32 attach_flags, u64 redirect_flags)
|
||||
{
|
||||
struct prog_configuration prog_cfg[VETH_PAIRS_COUNT] = {
|
||||
{
|
||||
.local_name = "xdp_redirect_map_multi_prog",
|
||||
.remote_name = "xdp_count_0",
|
||||
.local_flags = attach_flags,
|
||||
.remote_flags = attach_flags,
|
||||
},
|
||||
{
|
||||
.local_name = "xdp_redirect_map_multi_prog",
|
||||
.remote_name = "xdp_count_1",
|
||||
.local_flags = attach_flags,
|
||||
.remote_flags = attach_flags,
|
||||
},
|
||||
{
|
||||
.local_name = "xdp_redirect_map_multi_prog",
|
||||
.remote_name = "xdp_count_2",
|
||||
.local_flags = attach_flags,
|
||||
.remote_flags = attach_flags,
|
||||
}
|
||||
};
|
||||
struct bpf_object *bpf_objs[BROADCAST_REDIRECT_SKEL_NB];
|
||||
struct xdp_redirect_multi_kern *xdp_redirect_multi_kern;
|
||||
struct xdp_redirect_map *xdp_redirect_map;
|
||||
struct bpf_devmap_val devmap_val = {};
|
||||
struct net_configuration net_config;
|
||||
struct nstoken *nstoken = NULL;
|
||||
u16 protocol = ETH_P_IP;
|
||||
int group_map;
|
||||
int flags_map;
|
||||
int cnt_map;
|
||||
u64 cnt = 0;
|
||||
int i, err;
|
||||
|
||||
xdp_redirect_multi_kern = xdp_redirect_multi_kern__open_and_load();
|
||||
if (!ASSERT_OK_PTR(xdp_redirect_multi_kern, "xdp_redirect_multi_kern__open_and_load"))
|
||||
return;
|
||||
|
||||
xdp_redirect_map = xdp_redirect_map__open_and_load();
|
||||
if (!ASSERT_OK_PTR(xdp_redirect_map, "xdp_redirect_map__open_and_load"))
|
||||
goto destroy_xdp_redirect_multi_kern;
|
||||
|
||||
if (!ASSERT_OK(create_network(&net_config), "create network"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
group_map = bpf_map__fd(xdp_redirect_multi_kern->maps.map_all);
|
||||
if (!ASSERT_OK_FD(group_map, "open map_all"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
flags_map = bpf_map__fd(xdp_redirect_multi_kern->maps.redirect_flags);
|
||||
if (!ASSERT_OK_FD(group_map, "open map_all"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
err = bpf_map_update_elem(flags_map, &protocol, &redirect_flags, BPF_NOEXIST);
|
||||
if (!ASSERT_OK(err, "init IP count"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
cnt_map = bpf_map__fd(xdp_redirect_map->maps.rxcnt);
|
||||
if (!ASSERT_OK_FD(cnt_map, "open rxcnt map"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
bpf_objs[0] = xdp_redirect_multi_kern->obj;
|
||||
bpf_objs[1] = xdp_redirect_map->obj;
|
||||
|
||||
nstoken = open_netns(net_config.ns0_name);
|
||||
if (!ASSERT_OK_PTR(nstoken, "open NS0"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
for (i = 0; i < VETH_PAIRS_COUNT; i++) {
|
||||
int ifindex = if_nametoindex(net_config.veth_cfg[i].local_veth);
|
||||
|
||||
if (attach_programs_to_veth_pair(bpf_objs, BROADCAST_REDIRECT_SKEL_NB,
|
||||
&net_config, prog_cfg, i))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
SYS(destroy_xdp_redirect_map,
|
||||
"ip -n %s neigh add %s lladdr 00:00:00:00:00:01 dev %s",
|
||||
net_config.veth_cfg[i].namespace, IP_NEIGH, net_config.veth_cfg[i].remote_veth);
|
||||
|
||||
devmap_val.ifindex = ifindex;
|
||||
err = bpf_map_update_elem(group_map, &ifindex, &devmap_val, 0);
|
||||
if (!ASSERT_OK(err, "bpf_map_update_elem"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
}
|
||||
|
||||
SYS_NOFAIL("ip netns exec %s ping %s -i 0.1 -c 4 -W1 > /dev/null ",
|
||||
net_config.veth_cfg[0].namespace, IP_NEIGH);
|
||||
|
||||
for (i = 0; i < VETH_PAIRS_COUNT; i++) {
|
||||
err = bpf_map_lookup_elem(cnt_map, &i, &cnt);
|
||||
if (!ASSERT_OK(err, "get IP cnt"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
if (redirect_flags & BPF_F_EXCLUDE_INGRESS)
|
||||
/* veth11 shouldn't receive the ICMP requests;
|
||||
* others should
|
||||
*/
|
||||
ASSERT_EQ(cnt, i ? 4 : 0, "compare IP cnt");
|
||||
else
|
||||
/* All remote veth should receive the ICMP requests */
|
||||
ASSERT_EQ(cnt, 4, "compare IP cnt");
|
||||
}
|
||||
|
||||
destroy_xdp_redirect_map:
|
||||
close_netns(nstoken);
|
||||
xdp_redirect_map__destroy(xdp_redirect_map);
|
||||
destroy_xdp_redirect_multi_kern:
|
||||
xdp_redirect_multi_kern__destroy(xdp_redirect_multi_kern);
|
||||
|
||||
cleanup_network(&net_config);
|
||||
}
|
||||
|
||||
#define VETH_EGRESS_SKEL_NB 3
|
||||
static void xdp_veth_egress(u32 flags)
|
||||
{
|
||||
struct prog_configuration prog_cfg[VETH_PAIRS_COUNT] = {
|
||||
{
|
||||
.local_name = "xdp_redirect_map_all_prog",
|
||||
.remote_name = "xdp_dummy_prog",
|
||||
.local_flags = flags,
|
||||
.remote_flags = flags,
|
||||
},
|
||||
{
|
||||
.local_name = "xdp_redirect_map_all_prog",
|
||||
.remote_name = "store_mac_1",
|
||||
.local_flags = flags,
|
||||
.remote_flags = flags,
|
||||
},
|
||||
{
|
||||
.local_name = "xdp_redirect_map_all_prog",
|
||||
.remote_name = "store_mac_2",
|
||||
.local_flags = flags,
|
||||
.remote_flags = flags,
|
||||
}
|
||||
};
|
||||
const char magic_mac[6] = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
|
||||
struct xdp_redirect_multi_kern *xdp_redirect_multi_kern;
|
||||
struct bpf_object *bpf_objs[VETH_EGRESS_SKEL_NB];
|
||||
struct xdp_redirect_map *xdp_redirect_map;
|
||||
struct bpf_devmap_val devmap_val = {};
|
||||
struct net_configuration net_config;
|
||||
int mac_map, egress_map, res_map;
|
||||
struct nstoken *nstoken = NULL;
|
||||
struct xdp_dummy *xdp_dummy;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
xdp_dummy = xdp_dummy__open_and_load();
|
||||
if (!ASSERT_OK_PTR(xdp_dummy, "xdp_dummy__open_and_load"))
|
||||
return;
|
||||
|
||||
xdp_redirect_multi_kern = xdp_redirect_multi_kern__open_and_load();
|
||||
if (!ASSERT_OK_PTR(xdp_redirect_multi_kern, "xdp_redirect_multi_kern__open_and_load"))
|
||||
goto destroy_xdp_dummy;
|
||||
|
||||
xdp_redirect_map = xdp_redirect_map__open_and_load();
|
||||
if (!ASSERT_OK_PTR(xdp_redirect_map, "xdp_redirect_map__open_and_load"))
|
||||
goto destroy_xdp_redirect_multi_kern;
|
||||
|
||||
if (!ASSERT_OK(create_network(&net_config), "create network"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
mac_map = bpf_map__fd(xdp_redirect_multi_kern->maps.mac_map);
|
||||
if (!ASSERT_OK_FD(mac_map, "open mac_map"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
egress_map = bpf_map__fd(xdp_redirect_multi_kern->maps.map_egress);
|
||||
if (!ASSERT_OK_FD(egress_map, "open map_egress"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
devmap_val.bpf_prog.fd = bpf_program__fd(xdp_redirect_multi_kern->progs.xdp_devmap_prog);
|
||||
|
||||
bpf_objs[0] = xdp_dummy->obj;
|
||||
bpf_objs[1] = xdp_redirect_multi_kern->obj;
|
||||
bpf_objs[2] = xdp_redirect_map->obj;
|
||||
|
||||
nstoken = open_netns(net_config.ns0_name);
|
||||
if (!ASSERT_OK_PTR(nstoken, "open NS0"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
for (i = 0; i < VETH_PAIRS_COUNT; i++) {
|
||||
int ifindex = if_nametoindex(net_config.veth_cfg[i].local_veth);
|
||||
|
||||
SYS(destroy_xdp_redirect_map,
|
||||
"ip -n %s neigh add %s lladdr 00:00:00:00:00:01 dev %s",
|
||||
net_config.veth_cfg[i].namespace, IP_NEIGH, net_config.veth_cfg[i].remote_veth);
|
||||
|
||||
if (attach_programs_to_veth_pair(bpf_objs, VETH_REDIRECT_SKEL_NB,
|
||||
&net_config, prog_cfg, i))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
err = bpf_map_update_elem(mac_map, &ifindex, magic_mac, 0);
|
||||
if (!ASSERT_OK(err, "bpf_map_update_elem"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
devmap_val.ifindex = ifindex;
|
||||
err = bpf_map_update_elem(egress_map, &ifindex, &devmap_val, 0);
|
||||
if (!ASSERT_OK(err, "bpf_map_update_elem"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
}
|
||||
|
||||
SYS_NOFAIL("ip netns exec %s ping %s -i 0.1 -c 4 -W1 > /dev/null ",
|
||||
net_config.veth_cfg[0].namespace, IP_NEIGH);
|
||||
|
||||
res_map = bpf_map__fd(xdp_redirect_map->maps.rx_mac);
|
||||
if (!ASSERT_OK_FD(res_map, "open rx_map"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
u32 key = i;
|
||||
u64 res;
|
||||
|
||||
err = bpf_map_lookup_elem(res_map, &key, &res);
|
||||
if (!ASSERT_OK(err, "get MAC res"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
ASSERT_STRNEQ((const char *)&res, magic_mac, ETH_ALEN, "compare mac");
|
||||
}
|
||||
|
||||
destroy_xdp_redirect_map:
|
||||
close_netns(nstoken);
|
||||
xdp_redirect_map__destroy(xdp_redirect_map);
|
||||
destroy_xdp_redirect_multi_kern:
|
||||
xdp_redirect_multi_kern__destroy(xdp_redirect_multi_kern);
|
||||
destroy_xdp_dummy:
|
||||
xdp_dummy__destroy(xdp_dummy);
|
||||
|
||||
cleanup_network(&net_config);
|
||||
}
|
||||
|
||||
void test_xdp_veth_redirect(void)
|
||||
|
|
@ -284,3 +562,38 @@ void test_xdp_veth_redirect(void)
|
|||
if (test__start_subtest("SKB_MODE"))
|
||||
xdp_veth_redirect(XDP_FLAGS_SKB_MODE);
|
||||
}
|
||||
|
||||
void test_xdp_veth_broadcast_redirect(void)
|
||||
{
|
||||
if (test__start_subtest("0/BROADCAST"))
|
||||
xdp_veth_broadcast_redirect(0, BPF_F_BROADCAST);
|
||||
|
||||
if (test__start_subtest("0/(BROADCAST | EXCLUDE_INGRESS)"))
|
||||
xdp_veth_broadcast_redirect(0, BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS);
|
||||
|
||||
if (test__start_subtest("DRV_MODE/BROADCAST"))
|
||||
xdp_veth_broadcast_redirect(XDP_FLAGS_DRV_MODE, BPF_F_BROADCAST);
|
||||
|
||||
if (test__start_subtest("DRV_MODE/(BROADCAST | EXCLUDE_INGRESS)"))
|
||||
xdp_veth_broadcast_redirect(XDP_FLAGS_DRV_MODE,
|
||||
BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS);
|
||||
|
||||
if (test__start_subtest("SKB_MODE/BROADCAST"))
|
||||
xdp_veth_broadcast_redirect(XDP_FLAGS_SKB_MODE, BPF_F_BROADCAST);
|
||||
|
||||
if (test__start_subtest("SKB_MODE/(BROADCAST | EXCLUDE_INGRESS)"))
|
||||
xdp_veth_broadcast_redirect(XDP_FLAGS_SKB_MODE,
|
||||
BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS);
|
||||
}
|
||||
|
||||
void test_xdp_veth_egress(void)
|
||||
{
|
||||
if (test__start_subtest("0/egress"))
|
||||
xdp_veth_egress(0);
|
||||
|
||||
if (test__start_subtest("DRV_MODE/egress"))
|
||||
xdp_veth_egress(XDP_FLAGS_DRV_MODE);
|
||||
|
||||
if (test__start_subtest("SKB_MODE/egress"))
|
||||
xdp_veth_egress(XDP_FLAGS_SKB_MODE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/if_ether.h>
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_endian.h>
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_DEVMAP);
|
||||
|
|
@ -28,4 +31,89 @@ int xdp_redirect_map_2(struct xdp_md *xdp)
|
|||
return bpf_redirect_map(&tx_port, 2, 0);
|
||||
}
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 3);
|
||||
__type(key, __u32);
|
||||
__type(value, __u64);
|
||||
} rxcnt SEC(".maps");
|
||||
|
||||
static int xdp_count(struct xdp_md *xdp, __u32 key)
|
||||
{
|
||||
void *data_end = (void *)(long)xdp->data_end;
|
||||
void *data = (void *)(long)xdp->data;
|
||||
struct ethhdr *eth = data;
|
||||
__u64 *count;
|
||||
|
||||
if (data + sizeof(*eth) > data_end)
|
||||
return XDP_DROP;
|
||||
|
||||
if (bpf_htons(eth->h_proto) == ETH_P_IP) {
|
||||
/* We only count IPv4 packets */
|
||||
count = bpf_map_lookup_elem(&rxcnt, &key);
|
||||
if (count)
|
||||
*count += 1;
|
||||
}
|
||||
|
||||
return XDP_PASS;
|
||||
}
|
||||
|
||||
SEC("xdp")
|
||||
int xdp_count_0(struct xdp_md *xdp)
|
||||
{
|
||||
return xdp_count(xdp, 0);
|
||||
}
|
||||
|
||||
SEC("xdp")
|
||||
int xdp_count_1(struct xdp_md *xdp)
|
||||
{
|
||||
return xdp_count(xdp, 1);
|
||||
}
|
||||
|
||||
SEC("xdp")
|
||||
int xdp_count_2(struct xdp_md *xdp)
|
||||
{
|
||||
return xdp_count(xdp, 2);
|
||||
}
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 2);
|
||||
__type(key, __u32);
|
||||
__type(value, __be64);
|
||||
} rx_mac SEC(".maps");
|
||||
|
||||
static int store_mac(struct xdp_md *xdp, __u32 id)
|
||||
{
|
||||
void *data_end = (void *)(long)xdp->data_end;
|
||||
void *data = (void *)(long)xdp->data;
|
||||
struct ethhdr *eth = data;
|
||||
__u32 key = id;
|
||||
__be64 mac = 0;
|
||||
|
||||
if (data + sizeof(*eth) > data_end)
|
||||
return XDP_DROP;
|
||||
|
||||
/* Only store IPv4 MAC to avoid being polluted by IPv6 packets */
|
||||
if (eth->h_proto == bpf_htons(ETH_P_IP)) {
|
||||
__builtin_memcpy(&mac, eth->h_source, ETH_ALEN);
|
||||
bpf_map_update_elem(&rx_mac, &key, &mac, 0);
|
||||
bpf_printk("%s - %x", __func__, mac);
|
||||
}
|
||||
|
||||
return XDP_PASS;
|
||||
}
|
||||
|
||||
SEC("xdp")
|
||||
int store_mac_1(struct xdp_md *xdp)
|
||||
{
|
||||
return store_mac(xdp, 0);
|
||||
}
|
||||
|
||||
SEC("xdp")
|
||||
int store_mac_2(struct xdp_md *xdp)
|
||||
{
|
||||
return store_mac(xdp, 1);
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
|
|
|||
|
|
@ -34,6 +34,14 @@ struct {
|
|||
__uint(max_entries, 128);
|
||||
} mac_map SEC(".maps");
|
||||
|
||||
/* map to store redirect flags for each protocol*/
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__type(key, __u16);
|
||||
__type(value, __u64);
|
||||
__uint(max_entries, 16);
|
||||
} redirect_flags SEC(".maps");
|
||||
|
||||
SEC("xdp")
|
||||
int xdp_redirect_map_multi_prog(struct xdp_md *ctx)
|
||||
{
|
||||
|
|
@ -41,25 +49,34 @@ int xdp_redirect_map_multi_prog(struct xdp_md *ctx)
|
|||
void *data = (void *)(long)ctx->data;
|
||||
int if_index = ctx->ingress_ifindex;
|
||||
struct ethhdr *eth = data;
|
||||
__u64 *flags_from_map;
|
||||
__u16 h_proto;
|
||||
__u64 nh_off;
|
||||
__u64 flags;
|
||||
|
||||
nh_off = sizeof(*eth);
|
||||
if (data + nh_off > data_end)
|
||||
return XDP_DROP;
|
||||
|
||||
h_proto = eth->h_proto;
|
||||
h_proto = bpf_htons(eth->h_proto);
|
||||
|
||||
/* Using IPv4 for (BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS) testing */
|
||||
if (h_proto == bpf_htons(ETH_P_IP))
|
||||
return bpf_redirect_map(&map_all, 0,
|
||||
BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS);
|
||||
/* Using IPv6 for none flag testing */
|
||||
else if (h_proto == bpf_htons(ETH_P_IPV6))
|
||||
return bpf_redirect_map(&map_all, if_index, 0);
|
||||
/* All others for BPF_F_BROADCAST testing */
|
||||
else
|
||||
return bpf_redirect_map(&map_all, 0, BPF_F_BROADCAST);
|
||||
flags_from_map = bpf_map_lookup_elem(&redirect_flags, &h_proto);
|
||||
|
||||
/* Default flags for IPv4 : (BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS) */
|
||||
if (h_proto == ETH_P_IP) {
|
||||
flags = flags_from_map ? *flags_from_map : BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS;
|
||||
return bpf_redirect_map(&map_all, 0, flags);
|
||||
}
|
||||
/* Default flags for IPv6 : 0 */
|
||||
if (h_proto == ETH_P_IPV6) {
|
||||
flags = flags_from_map ? *flags_from_map : 0;
|
||||
return bpf_redirect_map(&map_all, if_index, flags);
|
||||
}
|
||||
/* Default flags for others BPF_F_BROADCAST : 0 */
|
||||
else {
|
||||
flags = flags_from_map ? *flags_from_map : BPF_F_BROADCAST;
|
||||
return bpf_redirect_map(&map_all, 0, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/* The following 2 progs are for 2nd devmap prog testing */
|
||||
|
|
|
|||
|
|
@ -1,214 +0,0 @@
|
|||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Test topology:
|
||||
# - - - - - - - - - - - - - - - - - - -
|
||||
# | veth1 veth2 veth3 | ns0
|
||||
# - -| - - - - - - | - - - - - - | - -
|
||||
# --------- --------- ---------
|
||||
# | veth0 | | veth0 | | veth0 |
|
||||
# --------- --------- ---------
|
||||
# ns1 ns2 ns3
|
||||
#
|
||||
# Test modules:
|
||||
# XDP modes: generic, native, native + egress_prog
|
||||
#
|
||||
# Test cases:
|
||||
# ARP: Testing BPF_F_BROADCAST, the ingress interface also should receive
|
||||
# the redirects.
|
||||
# ns1 -> gw: ns1, ns2, ns3, should receive the arp request
|
||||
# IPv4: Testing BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS, the ingress
|
||||
# interface should not receive the redirects.
|
||||
# ns1 -> gw: ns1 should not receive, ns2, ns3 should receive redirects.
|
||||
# IPv6: Testing none flag, all the pkts should be redirected back
|
||||
# ping test: ns1 -> ns2 (block), echo requests will be redirect back
|
||||
# egress_prog:
|
||||
# all src mac should be egress interface's mac
|
||||
|
||||
# netns numbers
|
||||
NUM=3
|
||||
IFACES=""
|
||||
DRV_MODE="xdpgeneric xdpdrv xdpegress"
|
||||
PASS=0
|
||||
FAIL=0
|
||||
LOG_DIR=$(mktemp -d)
|
||||
declare -a NS
|
||||
NS[0]="ns0-$(mktemp -u XXXXXX)"
|
||||
NS[1]="ns1-$(mktemp -u XXXXXX)"
|
||||
NS[2]="ns2-$(mktemp -u XXXXXX)"
|
||||
NS[3]="ns3-$(mktemp -u XXXXXX)"
|
||||
|
||||
test_pass()
|
||||
{
|
||||
echo "Pass: $@"
|
||||
PASS=$((PASS + 1))
|
||||
}
|
||||
|
||||
test_fail()
|
||||
{
|
||||
echo "fail: $@"
|
||||
FAIL=$((FAIL + 1))
|
||||
}
|
||||
|
||||
clean_up()
|
||||
{
|
||||
for i in $(seq 0 $NUM); do
|
||||
ip netns del ${NS[$i]} 2> /dev/null
|
||||
done
|
||||
}
|
||||
|
||||
# Kselftest framework requirement - SKIP code is 4.
|
||||
check_env()
|
||||
{
|
||||
ip link set dev lo xdpgeneric off &>/dev/null
|
||||
if [ $? -ne 0 ];then
|
||||
echo "selftests: [SKIP] Could not run test without the ip xdpgeneric support"
|
||||
exit 4
|
||||
fi
|
||||
|
||||
which tcpdump &>/dev/null
|
||||
if [ $? -ne 0 ];then
|
||||
echo "selftests: [SKIP] Could not run test without tcpdump"
|
||||
exit 4
|
||||
fi
|
||||
}
|
||||
|
||||
setup_ns()
|
||||
{
|
||||
local mode=$1
|
||||
IFACES=""
|
||||
|
||||
if [ "$mode" = "xdpegress" ]; then
|
||||
mode="xdpdrv"
|
||||
fi
|
||||
|
||||
ip netns add ${NS[0]}
|
||||
for i in $(seq $NUM); do
|
||||
ip netns add ${NS[$i]}
|
||||
ip -n ${NS[$i]} link add veth0 type veth peer name veth$i netns ${NS[0]}
|
||||
ip -n ${NS[$i]} link set veth0 up
|
||||
ip -n ${NS[0]} link set veth$i up
|
||||
|
||||
ip -n ${NS[$i]} addr add 192.0.2.$i/24 dev veth0
|
||||
ip -n ${NS[$i]} addr add 2001:db8::$i/64 dev veth0
|
||||
# Add a neigh entry for IPv4 ping test
|
||||
ip -n ${NS[$i]} neigh add 192.0.2.253 lladdr 00:00:00:00:00:01 dev veth0
|
||||
ip -n ${NS[$i]} link set veth0 $mode obj \
|
||||
xdp_dummy.bpf.o sec xdp &> /dev/null || \
|
||||
{ test_fail "Unable to load dummy xdp" && exit 1; }
|
||||
IFACES="$IFACES veth$i"
|
||||
veth_mac[$i]=$(ip -n ${NS[0]} link show veth$i | awk '/link\/ether/ {print $2}')
|
||||
done
|
||||
}
|
||||
|
||||
do_egress_tests()
|
||||
{
|
||||
local mode=$1
|
||||
|
||||
# mac test
|
||||
ip netns exec ${NS[2]} tcpdump -e -i veth0 -nn -l -e &> ${LOG_DIR}/mac_ns1-2_${mode}.log &
|
||||
ip netns exec ${NS[3]} tcpdump -e -i veth0 -nn -l -e &> ${LOG_DIR}/mac_ns1-3_${mode}.log &
|
||||
sleep 0.5
|
||||
ip netns exec ${NS[1]} ping 192.0.2.254 -i 0.1 -c 4 &> /dev/null
|
||||
sleep 0.5
|
||||
pkill tcpdump
|
||||
|
||||
# mac check
|
||||
grep -q "${veth_mac[2]} > ff:ff:ff:ff:ff:ff" ${LOG_DIR}/mac_ns1-2_${mode}.log && \
|
||||
test_pass "$mode mac ns1-2" || test_fail "$mode mac ns1-2"
|
||||
grep -q "${veth_mac[3]} > ff:ff:ff:ff:ff:ff" ${LOG_DIR}/mac_ns1-3_${mode}.log && \
|
||||
test_pass "$mode mac ns1-3" || test_fail "$mode mac ns1-3"
|
||||
}
|
||||
|
||||
do_ping_tests()
|
||||
{
|
||||
local mode=$1
|
||||
|
||||
# ping6 test: echo request should be redirect back to itself, not others
|
||||
ip netns exec ${NS[1]} ip neigh add 2001:db8::2 dev veth0 lladdr 00:00:00:00:00:02
|
||||
|
||||
ip netns exec ${NS[1]} tcpdump -i veth0 -nn -l -e &> ${LOG_DIR}/ns1-1_${mode}.log &
|
||||
ip netns exec ${NS[2]} tcpdump -i veth0 -nn -l -e &> ${LOG_DIR}/ns1-2_${mode}.log &
|
||||
ip netns exec ${NS[3]} tcpdump -i veth0 -nn -l -e &> ${LOG_DIR}/ns1-3_${mode}.log &
|
||||
sleep 0.5
|
||||
# ARP test
|
||||
ip netns exec ${NS[1]} arping -q -c 2 -I veth0 192.0.2.254
|
||||
# IPv4 test
|
||||
ip netns exec ${NS[1]} ping 192.0.2.253 -i 0.1 -c 4 &> /dev/null
|
||||
# IPv6 test
|
||||
ip netns exec ${NS[1]} ping6 2001:db8::2 -i 0.1 -c 2 &> /dev/null
|
||||
sleep 0.5
|
||||
pkill tcpdump
|
||||
|
||||
# All netns should receive the redirect arp requests
|
||||
[ $(grep -cF "who-has 192.0.2.254" ${LOG_DIR}/ns1-1_${mode}.log) -eq 4 ] && \
|
||||
test_pass "$mode arp(F_BROADCAST) ns1-1" || \
|
||||
test_fail "$mode arp(F_BROADCAST) ns1-1"
|
||||
[ $(grep -cF "who-has 192.0.2.254" ${LOG_DIR}/ns1-2_${mode}.log) -eq 2 ] && \
|
||||
test_pass "$mode arp(F_BROADCAST) ns1-2" || \
|
||||
test_fail "$mode arp(F_BROADCAST) ns1-2"
|
||||
[ $(grep -cF "who-has 192.0.2.254" ${LOG_DIR}/ns1-3_${mode}.log) -eq 2 ] && \
|
||||
test_pass "$mode arp(F_BROADCAST) ns1-3" || \
|
||||
test_fail "$mode arp(F_BROADCAST) ns1-3"
|
||||
|
||||
# ns1 should not receive the redirect echo request, others should
|
||||
[ $(grep -c "ICMP echo request" ${LOG_DIR}/ns1-1_${mode}.log) -eq 4 ] && \
|
||||
test_pass "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-1" || \
|
||||
test_fail "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-1"
|
||||
[ $(grep -c "ICMP echo request" ${LOG_DIR}/ns1-2_${mode}.log) -eq 4 ] && \
|
||||
test_pass "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-2" || \
|
||||
test_fail "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-2"
|
||||
[ $(grep -c "ICMP echo request" ${LOG_DIR}/ns1-3_${mode}.log) -eq 4 ] && \
|
||||
test_pass "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-3" || \
|
||||
test_fail "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-3"
|
||||
|
||||
# ns1 should receive the echo request, ns2 should not
|
||||
[ $(grep -c "ICMP6, echo request" ${LOG_DIR}/ns1-1_${mode}.log) -eq 4 ] && \
|
||||
test_pass "$mode IPv6 (no flags) ns1-1" || \
|
||||
test_fail "$mode IPv6 (no flags) ns1-1"
|
||||
[ $(grep -c "ICMP6, echo request" ${LOG_DIR}/ns1-2_${mode}.log) -eq 0 ] && \
|
||||
test_pass "$mode IPv6 (no flags) ns1-2" || \
|
||||
test_fail "$mode IPv6 (no flags) ns1-2"
|
||||
}
|
||||
|
||||
do_tests()
|
||||
{
|
||||
local mode=$1
|
||||
local drv_p
|
||||
|
||||
case ${mode} in
|
||||
xdpdrv) drv_p="-N";;
|
||||
xdpegress) drv_p="-X";;
|
||||
xdpgeneric) drv_p="-S";;
|
||||
esac
|
||||
|
||||
ip netns exec ${NS[0]} ./xdp_redirect_multi $drv_p $IFACES &> ${LOG_DIR}/xdp_redirect_${mode}.log &
|
||||
xdp_pid=$!
|
||||
sleep 1
|
||||
if ! ps -p $xdp_pid > /dev/null; then
|
||||
test_fail "$mode xdp_redirect_multi start failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$mode" = "xdpegress" ]; then
|
||||
do_egress_tests $mode
|
||||
else
|
||||
do_ping_tests $mode
|
||||
fi
|
||||
|
||||
kill $xdp_pid
|
||||
}
|
||||
|
||||
check_env
|
||||
|
||||
trap clean_up EXIT
|
||||
|
||||
for mode in ${DRV_MODE}; do
|
||||
setup_ns $mode
|
||||
do_tests $mode
|
||||
clean_up
|
||||
done
|
||||
rm -rf ${LOG_DIR}
|
||||
|
||||
echo "Summary: PASS $PASS, FAIL $FAIL"
|
||||
[ $FAIL -eq 0 ] && exit 0 || exit 1
|
||||
|
|
@ -1,226 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/if_link.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <net/if.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "bpf_util.h"
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
#define MAX_IFACE_NUM 32
|
||||
#define MAX_INDEX_NUM 1024
|
||||
|
||||
static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
|
||||
static int ifaces[MAX_IFACE_NUM] = {};
|
||||
|
||||
static void int_exit(int sig)
|
||||
{
|
||||
__u32 prog_id = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; ifaces[i] > 0; i++) {
|
||||
if (bpf_xdp_query_id(ifaces[i], xdp_flags, &prog_id)) {
|
||||
printf("bpf_xdp_query_id failed\n");
|
||||
exit(1);
|
||||
}
|
||||
if (prog_id)
|
||||
bpf_xdp_detach(ifaces[i], xdp_flags, NULL);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static int get_mac_addr(unsigned int ifindex, void *mac_addr)
|
||||
{
|
||||
char ifname[IF_NAMESIZE];
|
||||
struct ifreq ifr;
|
||||
int fd, ret = -1;
|
||||
|
||||
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0)
|
||||
return ret;
|
||||
|
||||
if (!if_indextoname(ifindex, ifname))
|
||||
goto err_out;
|
||||
|
||||
strcpy(ifr.ifr_name, ifname);
|
||||
|
||||
if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
|
||||
goto err_out;
|
||||
|
||||
memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
|
||||
ret = 0;
|
||||
|
||||
err_out:
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usage(const char *prog)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: %s [OPTS] <IFNAME|IFINDEX> <IFNAME|IFINDEX> ...\n"
|
||||
"OPTS:\n"
|
||||
" -S use skb-mode\n"
|
||||
" -N enforce native mode\n"
|
||||
" -F force loading prog\n"
|
||||
" -X load xdp program on egress\n",
|
||||
prog);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int prog_fd, group_all, mac_map;
|
||||
struct bpf_program *ingress_prog, *egress_prog;
|
||||
int i, err, ret, opt, egress_prog_fd = 0;
|
||||
struct bpf_devmap_val devmap_val;
|
||||
bool attach_egress_prog = false;
|
||||
unsigned char mac_addr[6];
|
||||
char ifname[IF_NAMESIZE];
|
||||
struct bpf_object *obj;
|
||||
unsigned int ifindex;
|
||||
char filename[256];
|
||||
|
||||
while ((opt = getopt(argc, argv, "SNFX")) != -1) {
|
||||
switch (opt) {
|
||||
case 'S':
|
||||
xdp_flags |= XDP_FLAGS_SKB_MODE;
|
||||
break;
|
||||
case 'N':
|
||||
/* default, set below */
|
||||
break;
|
||||
case 'F':
|
||||
xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
|
||||
break;
|
||||
case 'X':
|
||||
attach_egress_prog = true;
|
||||
break;
|
||||
default:
|
||||
usage(basename(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) {
|
||||
xdp_flags |= XDP_FLAGS_DRV_MODE;
|
||||
} else if (attach_egress_prog) {
|
||||
printf("Load xdp program on egress with SKB mode not supported yet\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (optind == argc) {
|
||||
printf("usage: %s <IFNAME|IFINDEX> <IFNAME|IFINDEX> ...\n", argv[0]);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
printf("Get interfaces:");
|
||||
for (i = 0; i < MAX_IFACE_NUM && argv[optind + i]; i++) {
|
||||
ifaces[i] = if_nametoindex(argv[optind + i]);
|
||||
if (!ifaces[i])
|
||||
ifaces[i] = strtoul(argv[optind + i], NULL, 0);
|
||||
if (!if_indextoname(ifaces[i], ifname)) {
|
||||
perror("Invalid interface name or i");
|
||||
goto err_out;
|
||||
}
|
||||
if (ifaces[i] > MAX_INDEX_NUM) {
|
||||
printf(" interface index too large\n");
|
||||
goto err_out;
|
||||
}
|
||||
printf(" %d", ifaces[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
snprintf(filename, sizeof(filename), "%s_kern.bpf.o", argv[0]);
|
||||
obj = bpf_object__open_file(filename, NULL);
|
||||
err = libbpf_get_error(obj);
|
||||
if (err)
|
||||
goto err_out;
|
||||
err = bpf_object__load(obj);
|
||||
if (err)
|
||||
goto err_out;
|
||||
prog_fd = bpf_program__fd(bpf_object__next_program(obj, NULL));
|
||||
|
||||
if (attach_egress_prog)
|
||||
group_all = bpf_object__find_map_fd_by_name(obj, "map_egress");
|
||||
else
|
||||
group_all = bpf_object__find_map_fd_by_name(obj, "map_all");
|
||||
mac_map = bpf_object__find_map_fd_by_name(obj, "mac_map");
|
||||
|
||||
if (group_all < 0 || mac_map < 0) {
|
||||
printf("bpf_object__find_map_fd_by_name failed\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (attach_egress_prog) {
|
||||
/* Find ingress/egress prog for 2nd xdp prog */
|
||||
ingress_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_all_prog");
|
||||
egress_prog = bpf_object__find_program_by_name(obj, "xdp_devmap_prog");
|
||||
if (!ingress_prog || !egress_prog) {
|
||||
printf("finding ingress/egress_prog in obj file failed\n");
|
||||
goto err_out;
|
||||
}
|
||||
prog_fd = bpf_program__fd(ingress_prog);
|
||||
egress_prog_fd = bpf_program__fd(egress_prog);
|
||||
if (prog_fd < 0 || egress_prog_fd < 0) {
|
||||
printf("find egress_prog fd failed\n");
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
signal(SIGINT, int_exit);
|
||||
signal(SIGTERM, int_exit);
|
||||
|
||||
/* Init forward multicast groups and exclude group */
|
||||
for (i = 0; ifaces[i] > 0; i++) {
|
||||
ifindex = ifaces[i];
|
||||
|
||||
if (attach_egress_prog) {
|
||||
ret = get_mac_addr(ifindex, mac_addr);
|
||||
if (ret < 0) {
|
||||
printf("get interface %d mac failed\n", ifindex);
|
||||
goto err_out;
|
||||
}
|
||||
ret = bpf_map_update_elem(mac_map, &ifindex, mac_addr, 0);
|
||||
if (ret) {
|
||||
perror("bpf_update_elem mac_map failed\n");
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add all the interfaces to group all */
|
||||
devmap_val.ifindex = ifindex;
|
||||
devmap_val.bpf_prog.fd = egress_prog_fd;
|
||||
ret = bpf_map_update_elem(group_all, &ifindex, &devmap_val, 0);
|
||||
if (ret) {
|
||||
perror("bpf_map_update_elem");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* bind prog_fd to each interface */
|
||||
ret = bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL);
|
||||
if (ret) {
|
||||
printf("Set xdp fd failed on %d\n", ifindex);
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
/* sleep some time for testing */
|
||||
sleep(999);
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
return 1;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user