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
attach_programs_to_veth_pair(struct skeletons * skeletons,int index)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
configure_network(struct skeletons * skeletons)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
cleanup_network(void)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
check_ping(struct skeletons * skeletons)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
test_xdp_veth_redirect(void)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