1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 3 /* 4 * Topology: 5 * --------- 6 * NS0 namespace | NS1 namespace | NS2 namespace 7 * | | 8 * +---------------+ | +---------------+ | 9 * | ipsec0 |---------| ipsec0 | | 10 * | 192.168.1.100 | | | 192.168.1.200 | | 11 * | if_id: bpf | | +---------------+ | 12 * +---------------+ | | 13 * | | | +---------------+ 14 * | | | | ipsec0 | 15 * \------------------------------------------| 192.168.1.200 | 16 * | | +---------------+ 17 * | | 18 * | | (overlay network) 19 * ------------------------------------------------------ 20 * | | (underlay network) 21 * +--------------+ | +--------------+ | 22 * | veth01 |----------| veth10 | | 23 * | 172.16.1.100 | | | 172.16.1.200 | | 24 * ---------------+ | +--------------+ | 25 * | | 26 * +--------------+ | | +--------------+ 27 * | veth02 |-----------------------------------| veth20 | 28 * | 172.16.2.100 | | | | 172.16.2.200 | 29 * +--------------+ | | +--------------+ 30 * 31 * 32 * Test Packet flow 33 * ----------- 34 * The tests perform 'ping 192.168.1.200' from the NS0 namespace: 35 * 1) request is routed to NS0 ipsec0 36 * 2) NS0 ipsec0 tc egress BPF program is triggered and sets the if_id based 37 * on the requested value. This makes the ipsec0 device in external mode 38 * select the destination tunnel 39 * 3) ping reaches the other namespace (NS1 or NS2 based on which if_id was 40 * used) and response is sent 41 * 4) response is received on NS0 ipsec0, tc ingress program is triggered and 42 * records the response if_id 43 * 5) requested if_id is compared with received if_id 44 */ 45 46 #include <net/if.h> 47 #include <linux/rtnetlink.h> 48 #include <linux/if_link.h> 49 50 #include "test_progs.h" 51 #include "network_helpers.h" 52 #include "xfrm_info.skel.h" 53 54 #define NS0 "xfrm_test_ns0" 55 #define NS1 "xfrm_test_ns1" 56 #define NS2 "xfrm_test_ns2" 57 58 #define IF_ID_0_TO_1 1 59 #define IF_ID_0_TO_2 2 60 #define IF_ID_1 3 61 #define IF_ID_2 4 62 63 #define IP4_ADDR_VETH01 "172.16.1.100" 64 #define IP4_ADDR_VETH10 "172.16.1.200" 65 #define IP4_ADDR_VETH02 "172.16.2.100" 66 #define IP4_ADDR_VETH20 "172.16.2.200" 67 68 #define ESP_DUMMY_PARAMS \ 69 "proto esp aead 'rfc4106(gcm(aes))' " \ 70 "0xe4d8f4b4da1df18a3510b3781496daa82488b713 128 mode tunnel " 71 72 #define SYS(fmt, ...) \ 73 ({ \ 74 char cmd[1024]; \ 75 snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \ 76 if (!ASSERT_OK(system(cmd), cmd)) \ 77 goto fail; \ 78 }) 79 80 #define SYS_NOFAIL(fmt, ...) \ 81 ({ \ 82 char cmd[1024]; \ 83 snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \ 84 system(cmd); \ 85 }) 86 87 static int attach_tc_prog(struct bpf_tc_hook *hook, int igr_fd, int egr_fd) 88 { 89 LIBBPF_OPTS(bpf_tc_opts, opts1, .handle = 1, .priority = 1, 90 .prog_fd = igr_fd); 91 LIBBPF_OPTS(bpf_tc_opts, opts2, .handle = 1, .priority = 1, 92 .prog_fd = egr_fd); 93 int ret; 94 95 ret = bpf_tc_hook_create(hook); 96 if (!ASSERT_OK(ret, "create tc hook")) 97 return ret; 98 99 if (igr_fd >= 0) { 100 hook->attach_point = BPF_TC_INGRESS; 101 ret = bpf_tc_attach(hook, &opts1); 102 if (!ASSERT_OK(ret, "bpf_tc_attach")) { 103 bpf_tc_hook_destroy(hook); 104 return ret; 105 } 106 } 107 108 if (egr_fd >= 0) { 109 hook->attach_point = BPF_TC_EGRESS; 110 ret = bpf_tc_attach(hook, &opts2); 111 if (!ASSERT_OK(ret, "bpf_tc_attach")) { 112 bpf_tc_hook_destroy(hook); 113 return ret; 114 } 115 } 116 117 return 0; 118 } 119 120 static void cleanup(void) 121 { 122 SYS_NOFAIL("test -f /var/run/netns/" NS0 " && ip netns delete " NS0); 123 SYS_NOFAIL("test -f /var/run/netns/" NS1 " && ip netns delete " NS1); 124 SYS_NOFAIL("test -f /var/run/netns/" NS2 " && ip netns delete " NS2); 125 } 126 127 static int config_underlay(void) 128 { 129 SYS("ip netns add " NS0); 130 SYS("ip netns add " NS1); 131 SYS("ip netns add " NS2); 132 133 /* NS0 <-> NS1 [veth01 <-> veth10] */ 134 SYS("ip link add veth01 netns " NS0 " type veth peer name veth10 netns " NS1); 135 SYS("ip -net " NS0 " addr add " IP4_ADDR_VETH01 "/24 dev veth01"); 136 SYS("ip -net " NS0 " link set dev veth01 up"); 137 SYS("ip -net " NS1 " addr add " IP4_ADDR_VETH10 "/24 dev veth10"); 138 SYS("ip -net " NS1 " link set dev veth10 up"); 139 140 /* NS0 <-> NS2 [veth02 <-> veth20] */ 141 SYS("ip link add veth02 netns " NS0 " type veth peer name veth20 netns " NS2); 142 SYS("ip -net " NS0 " addr add " IP4_ADDR_VETH02 "/24 dev veth02"); 143 SYS("ip -net " NS0 " link set dev veth02 up"); 144 SYS("ip -net " NS2 " addr add " IP4_ADDR_VETH20 "/24 dev veth20"); 145 SYS("ip -net " NS2 " link set dev veth20 up"); 146 147 return 0; 148 fail: 149 return -1; 150 } 151 152 static int setup_xfrm_tunnel_ns(const char *ns, const char *ipv4_local, 153 const char *ipv4_remote, int if_id) 154 { 155 /* State: local -> remote */ 156 SYS("ip -net %s xfrm state add src %s dst %s spi 1 " 157 ESP_DUMMY_PARAMS "if_id %d", ns, ipv4_local, ipv4_remote, if_id); 158 159 /* State: local <- remote */ 160 SYS("ip -net %s xfrm state add src %s dst %s spi 1 " 161 ESP_DUMMY_PARAMS "if_id %d", ns, ipv4_remote, ipv4_local, if_id); 162 163 /* Policy: local -> remote */ 164 SYS("ip -net %s xfrm policy add dir out src 0.0.0.0/0 dst 0.0.0.0/0 " 165 "if_id %d tmpl src %s dst %s proto esp mode tunnel if_id %d", ns, 166 if_id, ipv4_local, ipv4_remote, if_id); 167 168 /* Policy: local <- remote */ 169 SYS("ip -net %s xfrm policy add dir in src 0.0.0.0/0 dst 0.0.0.0/0 " 170 "if_id %d tmpl src %s dst %s proto esp mode tunnel if_id %d", ns, 171 if_id, ipv4_remote, ipv4_local, if_id); 172 173 return 0; 174 fail: 175 return -1; 176 } 177 178 static int setup_xfrm_tunnel(const char *ns_a, const char *ns_b, 179 const char *ipv4_a, const char *ipv4_b, 180 int if_id_a, int if_id_b) 181 { 182 return setup_xfrm_tunnel_ns(ns_a, ipv4_a, ipv4_b, if_id_a) || 183 setup_xfrm_tunnel_ns(ns_b, ipv4_b, ipv4_a, if_id_b); 184 } 185 186 static struct rtattr *rtattr_add(struct nlmsghdr *nh, unsigned short type, 187 unsigned short len) 188 { 189 struct rtattr *rta = 190 (struct rtattr *)((uint8_t *)nh + RTA_ALIGN(nh->nlmsg_len)); 191 rta->rta_type = type; 192 rta->rta_len = RTA_LENGTH(len); 193 nh->nlmsg_len = RTA_ALIGN(nh->nlmsg_len) + RTA_ALIGN(rta->rta_len); 194 return rta; 195 } 196 197 static struct rtattr *rtattr_add_str(struct nlmsghdr *nh, unsigned short type, 198 const char *s) 199 { 200 struct rtattr *rta = rtattr_add(nh, type, strlen(s)); 201 202 memcpy(RTA_DATA(rta), s, strlen(s)); 203 return rta; 204 } 205 206 static struct rtattr *rtattr_begin(struct nlmsghdr *nh, unsigned short type) 207 { 208 return rtattr_add(nh, type, 0); 209 } 210 211 static void rtattr_end(struct nlmsghdr *nh, struct rtattr *attr) 212 { 213 uint8_t *end = (uint8_t *)nh + nh->nlmsg_len; 214 215 attr->rta_len = end - (uint8_t *)attr; 216 } 217 218 static int setup_xfrmi_external_dev(const char *ns) 219 { 220 struct { 221 struct nlmsghdr nh; 222 struct ifinfomsg info; 223 unsigned char data[128]; 224 } req; 225 struct rtattr *link_info, *info_data; 226 struct nstoken *nstoken; 227 int ret = -1, sock = -1; 228 struct nlmsghdr *nh; 229 230 memset(&req, 0, sizeof(req)); 231 nh = &req.nh; 232 nh->nlmsg_len = NLMSG_LENGTH(sizeof(req.info)); 233 nh->nlmsg_type = RTM_NEWLINK; 234 nh->nlmsg_flags |= NLM_F_CREATE | NLM_F_REQUEST; 235 236 rtattr_add_str(nh, IFLA_IFNAME, "ipsec0"); 237 link_info = rtattr_begin(nh, IFLA_LINKINFO); 238 rtattr_add_str(nh, IFLA_INFO_KIND, "xfrm"); 239 info_data = rtattr_begin(nh, IFLA_INFO_DATA); 240 rtattr_add(nh, IFLA_XFRM_COLLECT_METADATA, 0); 241 rtattr_end(nh, info_data); 242 rtattr_end(nh, link_info); 243 244 nstoken = open_netns(ns); 245 if (!ASSERT_OK_PTR(nstoken, "setns")) 246 goto done; 247 248 sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); 249 if (!ASSERT_GE(sock, 0, "netlink socket")) 250 goto done; 251 ret = send(sock, nh, nh->nlmsg_len, 0); 252 if (!ASSERT_EQ(ret, nh->nlmsg_len, "netlink send length")) 253 goto done; 254 255 ret = 0; 256 done: 257 if (sock != -1) 258 close(sock); 259 if (nstoken) 260 close_netns(nstoken); 261 return ret; 262 } 263 264 static int config_overlay(void) 265 { 266 if (setup_xfrm_tunnel(NS0, NS1, IP4_ADDR_VETH01, IP4_ADDR_VETH10, 267 IF_ID_0_TO_1, IF_ID_1)) 268 goto fail; 269 if (setup_xfrm_tunnel(NS0, NS2, IP4_ADDR_VETH02, IP4_ADDR_VETH20, 270 IF_ID_0_TO_2, IF_ID_2)) 271 goto fail; 272 273 /* Older iproute2 doesn't support this option */ 274 if (!ASSERT_OK(setup_xfrmi_external_dev(NS0), "xfrmi")) 275 goto fail; 276 277 SYS("ip -net " NS0 " addr add 192.168.1.100/24 dev ipsec0"); 278 SYS("ip -net " NS0 " link set dev ipsec0 up"); 279 280 SYS("ip -net " NS1 " link add ipsec0 type xfrm if_id %d", IF_ID_1); 281 SYS("ip -net " NS1 " addr add 192.168.1.200/24 dev ipsec0"); 282 SYS("ip -net " NS1 " link set dev ipsec0 up"); 283 284 SYS("ip -net " NS2 " link add ipsec0 type xfrm if_id %d", IF_ID_2); 285 SYS("ip -net " NS2 " addr add 192.168.1.200/24 dev ipsec0"); 286 SYS("ip -net " NS2 " link set dev ipsec0 up"); 287 288 return 0; 289 fail: 290 return -1; 291 } 292 293 static int test_xfrm_ping(struct xfrm_info *skel, u32 if_id) 294 { 295 skel->bss->req_if_id = if_id; 296 297 SYS("ping -i 0.01 -c 3 -w 10 -q 192.168.1.200 > /dev/null"); 298 299 if (!ASSERT_EQ(skel->bss->resp_if_id, if_id, "if_id")) 300 goto fail; 301 302 return 0; 303 fail: 304 return -1; 305 } 306 307 static void _test_xfrm_info(void) 308 { 309 LIBBPF_OPTS(bpf_tc_hook, tc_hook, .attach_point = BPF_TC_INGRESS); 310 int get_xfrm_info_prog_fd, set_xfrm_info_prog_fd; 311 struct nstoken *nstoken = NULL; 312 struct xfrm_info *skel; 313 int ifindex; 314 315 /* load and attach bpf progs to ipsec dev tc hook point */ 316 skel = xfrm_info__open_and_load(); 317 if (!ASSERT_OK_PTR(skel, "xfrm_info__open_and_load")) 318 goto done; 319 nstoken = open_netns(NS0); 320 if (!ASSERT_OK_PTR(nstoken, "setns " NS0)) 321 goto done; 322 ifindex = if_nametoindex("ipsec0"); 323 if (!ASSERT_NEQ(ifindex, 0, "ipsec0 ifindex")) 324 goto done; 325 tc_hook.ifindex = ifindex; 326 set_xfrm_info_prog_fd = bpf_program__fd(skel->progs.set_xfrm_info); 327 get_xfrm_info_prog_fd = bpf_program__fd(skel->progs.get_xfrm_info); 328 if (!ASSERT_GE(set_xfrm_info_prog_fd, 0, "bpf_program__fd")) 329 goto done; 330 if (!ASSERT_GE(get_xfrm_info_prog_fd, 0, "bpf_program__fd")) 331 goto done; 332 if (attach_tc_prog(&tc_hook, get_xfrm_info_prog_fd, 333 set_xfrm_info_prog_fd)) 334 goto done; 335 336 /* perform test */ 337 if (!ASSERT_EQ(test_xfrm_ping(skel, IF_ID_0_TO_1), 0, "ping " NS1)) 338 goto done; 339 if (!ASSERT_EQ(test_xfrm_ping(skel, IF_ID_0_TO_2), 0, "ping " NS2)) 340 goto done; 341 342 done: 343 if (nstoken) 344 close_netns(nstoken); 345 xfrm_info__destroy(skel); 346 } 347 348 void test_xfrm_info(void) 349 { 350 cleanup(); 351 352 if (!ASSERT_OK(config_underlay(), "config_underlay")) 353 goto done; 354 if (!ASSERT_OK(config_overlay(), "config_overlay")) 355 goto done; 356 357 if (test__start_subtest("xfrm_info")) 358 _test_xfrm_info(); 359 360 done: 361 cleanup(); 362 } 363