1 // SPDX-License-Identifier: GPL-2.0 2 3 /* Create 3 namespaces with 3 veth peers, and forward packets in-between using 4 * native XDP 5 * 6 * XDP_TX 7 * NS1(veth11) NS2(veth22) NS3(veth33) 8 * | | | 9 * | | | 10 * (veth1, (veth2, (veth3, 11 * id:111) id:122) id:133) 12 * ^ | ^ | ^ | 13 * | | XDP_REDIRECT | | XDP_REDIRECT | | 14 * | ------------------ ------------------ | 15 * ----------------------------------------- 16 * XDP_REDIRECT 17 */ 18 19 #define _GNU_SOURCE 20 #include <net/if.h> 21 #include "test_progs.h" 22 #include "network_helpers.h" 23 #include "xdp_dummy.skel.h" 24 #include "xdp_redirect_map.skel.h" 25 #include "xdp_tx.skel.h" 26 27 #define VETH_PAIRS_COUNT 3 28 #define NS_SUFFIX_LEN 6 29 #define VETH_NAME_MAX_LEN 16 30 #define IP_SRC "10.1.1.11" 31 #define IP_DST "10.1.1.33" 32 #define IP_CMD_MAX_LEN 128 33 34 struct skeletons { 35 struct xdp_dummy *xdp_dummy; 36 struct xdp_tx *xdp_tx; 37 struct xdp_redirect_map *xdp_redirect_maps; 38 }; 39 40 struct veth_configuration { 41 char local_veth[VETH_NAME_MAX_LEN]; /* Interface in main namespace */ 42 char remote_veth[VETH_NAME_MAX_LEN]; /* Peer interface in dedicated namespace*/ 43 const char *namespace; /* Namespace for the remote veth */ 44 char next_veth[VETH_NAME_MAX_LEN]; /* Local interface to redirect traffic to */ 45 char *remote_addr; /* IP address of the remote veth */ 46 }; 47 48 static struct veth_configuration config[VETH_PAIRS_COUNT] = { 49 { 50 .local_veth = "veth1", 51 .remote_veth = "veth11", 52 .next_veth = "veth2", 53 .remote_addr = IP_SRC, 54 .namespace = "ns-veth11" 55 }, 56 { 57 .local_veth = "veth2", 58 .remote_veth = "veth22", 59 .next_veth = "veth3", 60 .remote_addr = NULL, 61 .namespace = "ns-veth22" 62 }, 63 { 64 .local_veth = "veth3", 65 .remote_veth = "veth33", 66 .next_veth = "veth1", 67 .remote_addr = IP_DST, 68 .namespace = "ns-veth33" 69 } 70 }; 71 72 static int attach_programs_to_veth_pair(struct skeletons *skeletons, int index) 73 { 74 struct bpf_program *local_prog, *remote_prog; 75 struct bpf_link **local_link, **remote_link; 76 struct nstoken *nstoken; 77 struct bpf_link *link; 78 int interface; 79 80 switch (index) { 81 case 0: 82 local_prog = skeletons->xdp_redirect_maps->progs.xdp_redirect_map_0; 83 local_link = &skeletons->xdp_redirect_maps->links.xdp_redirect_map_0; 84 remote_prog = skeletons->xdp_dummy->progs.xdp_dummy_prog; 85 remote_link = &skeletons->xdp_dummy->links.xdp_dummy_prog; 86 break; 87 case 1: 88 local_prog = skeletons->xdp_redirect_maps->progs.xdp_redirect_map_1; 89 local_link = &skeletons->xdp_redirect_maps->links.xdp_redirect_map_1; 90 remote_prog = skeletons->xdp_tx->progs.xdp_tx; 91 remote_link = &skeletons->xdp_tx->links.xdp_tx; 92 break; 93 case 2: 94 local_prog = skeletons->xdp_redirect_maps->progs.xdp_redirect_map_2; 95 local_link = &skeletons->xdp_redirect_maps->links.xdp_redirect_map_2; 96 remote_prog = skeletons->xdp_dummy->progs.xdp_dummy_prog; 97 remote_link = &skeletons->xdp_dummy->links.xdp_dummy_prog; 98 break; 99 } 100 interface = if_nametoindex(config[index].local_veth); 101 if (!ASSERT_NEQ(interface, 0, "non zero interface index")) 102 return -1; 103 link = bpf_program__attach_xdp(local_prog, interface); 104 if (!ASSERT_OK_PTR(link, "attach xdp program to local veth")) 105 return -1; 106 *local_link = link; 107 nstoken = open_netns(config[index].namespace); 108 if (!ASSERT_OK_PTR(nstoken, "switch to remote veth namespace")) 109 return -1; 110 interface = if_nametoindex(config[index].remote_veth); 111 if (!ASSERT_NEQ(interface, 0, "non zero interface index")) { 112 close_netns(nstoken); 113 return -1; 114 } 115 link = bpf_program__attach_xdp(remote_prog, interface); 116 *remote_link = link; 117 close_netns(nstoken); 118 if (!ASSERT_OK_PTR(link, "attach xdp program to remote veth")) 119 return -1; 120 121 return 0; 122 } 123 124 static int configure_network(struct skeletons *skeletons) 125 { 126 int interface_id; 127 int map_fd; 128 int err; 129 int i = 0; 130 131 /* First create and configure all interfaces */ 132 for (i = 0; i < VETH_PAIRS_COUNT; i++) { 133 SYS(fail, "ip netns add %s", config[i].namespace); 134 SYS(fail, "ip link add %s type veth peer name %s netns %s", 135 config[i].local_veth, config[i].remote_veth, config[i].namespace); 136 SYS(fail, "ip link set dev %s up", config[i].local_veth); 137 if (config[i].remote_addr) 138 SYS(fail, "ip -n %s addr add %s/24 dev %s", config[i].namespace, 139 config[i].remote_addr, config[i].remote_veth); 140 SYS(fail, "ip -n %s link set dev %s up", config[i].namespace, 141 config[i].remote_veth); 142 } 143 144 /* Then configure the redirect map and attach programs to interfaces */ 145 map_fd = bpf_map__fd(skeletons->xdp_redirect_maps->maps.tx_port); 146 if (!ASSERT_GE(map_fd, 0, "open redirect map")) 147 goto fail; 148 for (i = 0; i < VETH_PAIRS_COUNT; i++) { 149 interface_id = if_nametoindex(config[i].next_veth); 150 if (!ASSERT_NEQ(interface_id, 0, "non zero interface index")) 151 goto fail; 152 err = bpf_map_update_elem(map_fd, &i, &interface_id, BPF_ANY); 153 if (!ASSERT_OK(err, "configure interface redirection through map")) 154 goto fail; 155 if (attach_programs_to_veth_pair(skeletons, i)) 156 goto fail; 157 } 158 159 return 0; 160 161 fail: 162 return -1; 163 } 164 165 static void cleanup_network(void) 166 { 167 int i; 168 169 /* Deleting namespaces is enough to automatically remove veth pairs as well 170 */ 171 for (i = 0; i < VETH_PAIRS_COUNT; i++) 172 SYS_NOFAIL("ip netns del %s", config[i].namespace); 173 } 174 175 static int check_ping(struct skeletons *skeletons) 176 { 177 /* Test: if all interfaces are properly configured, we must be able to ping 178 * veth33 from veth11 179 */ 180 return SYS_NOFAIL("ip netns exec %s ping -c 1 -W 1 %s > /dev/null", 181 config[0].namespace, IP_DST); 182 } 183 184 void test_xdp_veth_redirect(void) 185 { 186 struct skeletons skeletons = {}; 187 188 skeletons.xdp_dummy = xdp_dummy__open_and_load(); 189 if (!ASSERT_OK_PTR(skeletons.xdp_dummy, "xdp_dummy__open_and_load")) 190 return; 191 192 skeletons.xdp_tx = xdp_tx__open_and_load(); 193 if (!ASSERT_OK_PTR(skeletons.xdp_tx, "xdp_tx__open_and_load")) 194 goto destroy_xdp_dummy; 195 196 skeletons.xdp_redirect_maps = xdp_redirect_map__open_and_load(); 197 if (!ASSERT_OK_PTR(skeletons.xdp_redirect_maps, "xdp_redirect_map__open_and_load")) 198 goto destroy_xdp_tx; 199 200 if (configure_network(&skeletons)) 201 goto destroy_xdp_redirect_map; 202 203 ASSERT_OK(check_ping(&skeletons), "ping"); 204 205 destroy_xdp_redirect_map: 206 xdp_redirect_map__destroy(skeletons.xdp_redirect_maps); 207 destroy_xdp_tx: 208 xdp_tx__destroy(skeletons.xdp_tx); 209 destroy_xdp_dummy: 210 xdp_dummy__destroy(skeletons.xdp_dummy); 211 212 cleanup_network(); 213 } 214