|
| 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 | +} |
0 commit comments