1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ 3 4 #include <linux/rtnetlink.h> 5 #include <sys/types.h> 6 #include <net/if.h> 7 8 #include "test_progs.h" 9 #include "network_helpers.h" 10 #include "fib_lookup.skel.h" 11 12 #define NS_TEST "fib_lookup_ns" 13 #define IPV6_IFACE_ADDR "face::face" 14 #define IPV6_IFACE_ADDR_SEC "cafe::cafe" 15 #define IPV6_ADDR_DST "face::3" 16 #define IPV6_NUD_FAILED_ADDR "face::1" 17 #define IPV6_NUD_STALE_ADDR "face::2" 18 #define IPV4_IFACE_ADDR "10.0.0.254" 19 #define IPV4_IFACE_ADDR_SEC "10.1.0.254" 20 #define IPV4_ADDR_DST "10.2.0.254" 21 #define IPV4_NUD_FAILED_ADDR "10.0.0.1" 22 #define IPV4_NUD_STALE_ADDR "10.0.0.2" 23 #define IPV4_TBID_ADDR "172.0.0.254" 24 #define IPV4_TBID_NET "172.0.0.0" 25 #define IPV4_TBID_DST "172.0.0.2" 26 #define IPV6_TBID_ADDR "fd00::FFFF" 27 #define IPV6_TBID_NET "fd00::" 28 #define IPV6_TBID_DST "fd00::2" 29 #define MARK_NO_POLICY 33 30 #define MARK 42 31 #define MARK_TABLE "200" 32 #define IPV4_REMOTE_DST "1.2.3.4" 33 #define IPV4_LOCAL "10.4.0.3" 34 #define IPV4_GW1 "10.4.0.1" 35 #define IPV4_GW2 "10.4.0.2" 36 #define IPV6_REMOTE_DST "be:ef::b0:10" 37 #define IPV6_LOCAL "fd01::3" 38 #define IPV6_GW1 "fd01::1" 39 #define IPV6_GW2 "fd01::2" 40 #define DMAC "11:11:11:11:11:11" 41 #define DMAC_INIT { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, } 42 #define DMAC2 "01:01:01:01:01:01" 43 #define DMAC_INIT2 { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, } 44 45 struct fib_lookup_test { 46 const char *desc; 47 const char *daddr; 48 int expected_ret; 49 const char *expected_src; 50 const char *expected_dst; 51 int lookup_flags; 52 __u32 tbid; 53 __u8 dmac[6]; 54 __u32 mark; 55 }; 56 57 static const struct fib_lookup_test tests[] = { 58 { .desc = "IPv6 failed neigh", 59 .daddr = IPV6_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_NO_NEIGH, }, 60 { .desc = "IPv6 stale neigh", 61 .daddr = IPV6_NUD_STALE_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 62 .dmac = DMAC_INIT, }, 63 { .desc = "IPv6 skip neigh", 64 .daddr = IPV6_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 65 .lookup_flags = BPF_FIB_LOOKUP_SKIP_NEIGH, }, 66 { .desc = "IPv4 failed neigh", 67 .daddr = IPV4_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_NO_NEIGH, }, 68 { .desc = "IPv4 stale neigh", 69 .daddr = IPV4_NUD_STALE_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 70 .dmac = DMAC_INIT, }, 71 { .desc = "IPv4 skip neigh", 72 .daddr = IPV4_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 73 .lookup_flags = BPF_FIB_LOOKUP_SKIP_NEIGH, }, 74 { .desc = "IPv4 TBID lookup failure", 75 .daddr = IPV4_TBID_DST, .expected_ret = BPF_FIB_LKUP_RET_NOT_FWDED, 76 .lookup_flags = BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_TBID, 77 .tbid = RT_TABLE_MAIN, }, 78 { .desc = "IPv4 TBID lookup success", 79 .daddr = IPV4_TBID_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 80 .lookup_flags = BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_TBID, .tbid = 100, 81 .dmac = DMAC_INIT2, }, 82 { .desc = "IPv6 TBID lookup failure", 83 .daddr = IPV6_TBID_DST, .expected_ret = BPF_FIB_LKUP_RET_NOT_FWDED, 84 .lookup_flags = BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_TBID, 85 .tbid = RT_TABLE_MAIN, }, 86 { .desc = "IPv6 TBID lookup success", 87 .daddr = IPV6_TBID_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 88 .lookup_flags = BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_TBID, .tbid = 100, 89 .dmac = DMAC_INIT2, }, 90 { .desc = "IPv4 set src addr from netdev", 91 .daddr = IPV4_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 92 .expected_src = IPV4_IFACE_ADDR, 93 .lookup_flags = BPF_FIB_LOOKUP_SRC | BPF_FIB_LOOKUP_SKIP_NEIGH, }, 94 { .desc = "IPv6 set src addr from netdev", 95 .daddr = IPV6_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 96 .expected_src = IPV6_IFACE_ADDR, 97 .lookup_flags = BPF_FIB_LOOKUP_SRC | BPF_FIB_LOOKUP_SKIP_NEIGH, }, 98 { .desc = "IPv4 set prefsrc addr from route", 99 .daddr = IPV4_ADDR_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 100 .expected_src = IPV4_IFACE_ADDR_SEC, 101 .lookup_flags = BPF_FIB_LOOKUP_SRC | BPF_FIB_LOOKUP_SKIP_NEIGH, }, 102 { .desc = "IPv6 set prefsrc addr route", 103 .daddr = IPV6_ADDR_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 104 .expected_src = IPV6_IFACE_ADDR_SEC, 105 .lookup_flags = BPF_FIB_LOOKUP_SRC | BPF_FIB_LOOKUP_SKIP_NEIGH, }, 106 /* policy routing */ 107 { .desc = "IPv4 policy routing, default", 108 .daddr = IPV4_REMOTE_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 109 .expected_dst = IPV4_GW1, 110 .lookup_flags = BPF_FIB_LOOKUP_MARK | BPF_FIB_LOOKUP_SKIP_NEIGH, }, 111 { .desc = "IPv4 policy routing, mark doesn't point to a policy", 112 .daddr = IPV4_REMOTE_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 113 .expected_dst = IPV4_GW1, 114 .lookup_flags = BPF_FIB_LOOKUP_MARK | BPF_FIB_LOOKUP_SKIP_NEIGH, 115 .mark = MARK_NO_POLICY, }, 116 { .desc = "IPv4 policy routing, mark points to a policy", 117 .daddr = IPV4_REMOTE_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 118 .expected_dst = IPV4_GW2, 119 .lookup_flags = BPF_FIB_LOOKUP_MARK | BPF_FIB_LOOKUP_SKIP_NEIGH, 120 .mark = MARK, }, 121 { .desc = "IPv4 policy routing, mark points to a policy, but no flag", 122 .daddr = IPV4_REMOTE_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 123 .expected_dst = IPV4_GW1, 124 .lookup_flags = BPF_FIB_LOOKUP_SKIP_NEIGH, 125 .mark = MARK, }, 126 { .desc = "IPv6 policy routing, default", 127 .daddr = IPV6_REMOTE_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 128 .expected_dst = IPV6_GW1, 129 .lookup_flags = BPF_FIB_LOOKUP_MARK | BPF_FIB_LOOKUP_SKIP_NEIGH, }, 130 { .desc = "IPv6 policy routing, mark doesn't point to a policy", 131 .daddr = IPV6_REMOTE_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 132 .expected_dst = IPV6_GW1, 133 .lookup_flags = BPF_FIB_LOOKUP_MARK | BPF_FIB_LOOKUP_SKIP_NEIGH, 134 .mark = MARK_NO_POLICY, }, 135 { .desc = "IPv6 policy routing, mark points to a policy", 136 .daddr = IPV6_REMOTE_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 137 .expected_dst = IPV6_GW2, 138 .lookup_flags = BPF_FIB_LOOKUP_MARK | BPF_FIB_LOOKUP_SKIP_NEIGH, 139 .mark = MARK, }, 140 { .desc = "IPv6 policy routing, mark points to a policy, but no flag", 141 .daddr = IPV6_REMOTE_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 142 .expected_dst = IPV6_GW1, 143 .lookup_flags = BPF_FIB_LOOKUP_SKIP_NEIGH, 144 .mark = MARK, }, 145 }; 146 147 static int setup_netns(void) 148 { 149 int err; 150 151 SYS(fail, "ip link add veth1 type veth peer name veth2"); 152 SYS(fail, "ip link set dev veth1 up"); 153 SYS(fail, "ip link set dev veth2 up"); 154 155 err = write_sysctl("/proc/sys/net/ipv4/neigh/veth1/gc_stale_time", "900"); 156 if (!ASSERT_OK(err, "write_sysctl(net.ipv4.neigh.veth1.gc_stale_time)")) 157 goto fail; 158 159 err = write_sysctl("/proc/sys/net/ipv6/neigh/veth1/gc_stale_time", "900"); 160 if (!ASSERT_OK(err, "write_sysctl(net.ipv6.neigh.veth1.gc_stale_time)")) 161 goto fail; 162 163 SYS(fail, "ip addr add %s/64 dev veth1 nodad", IPV6_IFACE_ADDR); 164 SYS(fail, "ip neigh add %s dev veth1 nud failed", IPV6_NUD_FAILED_ADDR); 165 SYS(fail, "ip neigh add %s dev veth1 lladdr %s nud stale", IPV6_NUD_STALE_ADDR, DMAC); 166 167 SYS(fail, "ip addr add %s/24 dev veth1", IPV4_IFACE_ADDR); 168 SYS(fail, "ip neigh add %s dev veth1 nud failed", IPV4_NUD_FAILED_ADDR); 169 SYS(fail, "ip neigh add %s dev veth1 lladdr %s nud stale", IPV4_NUD_STALE_ADDR, DMAC); 170 171 /* Setup for prefsrc IP addr selection */ 172 SYS(fail, "ip addr add %s/24 dev veth1", IPV4_IFACE_ADDR_SEC); 173 SYS(fail, "ip route add %s/32 dev veth1 src %s", IPV4_ADDR_DST, IPV4_IFACE_ADDR_SEC); 174 175 SYS(fail, "ip addr add %s/64 dev veth1 nodad", IPV6_IFACE_ADDR_SEC); 176 SYS(fail, "ip route add %s/128 dev veth1 src %s", IPV6_ADDR_DST, IPV6_IFACE_ADDR_SEC); 177 178 /* Setup for tbid lookup tests */ 179 SYS(fail, "ip addr add %s/24 dev veth2", IPV4_TBID_ADDR); 180 SYS(fail, "ip route del %s/24 dev veth2", IPV4_TBID_NET); 181 SYS(fail, "ip route add table 100 %s/24 dev veth2", IPV4_TBID_NET); 182 SYS(fail, "ip neigh add %s dev veth2 lladdr %s nud stale", IPV4_TBID_DST, DMAC2); 183 184 SYS(fail, "ip addr add %s/64 dev veth2", IPV6_TBID_ADDR); 185 SYS(fail, "ip -6 route del %s/64 dev veth2", IPV6_TBID_NET); 186 SYS(fail, "ip -6 route add table 100 %s/64 dev veth2", IPV6_TBID_NET); 187 SYS(fail, "ip neigh add %s dev veth2 lladdr %s nud stale", IPV6_TBID_DST, DMAC2); 188 189 err = write_sysctl("/proc/sys/net/ipv4/conf/veth1/forwarding", "1"); 190 if (!ASSERT_OK(err, "write_sysctl(net.ipv4.conf.veth1.forwarding)")) 191 goto fail; 192 193 err = write_sysctl("/proc/sys/net/ipv6/conf/veth1/forwarding", "1"); 194 if (!ASSERT_OK(err, "write_sysctl(net.ipv6.conf.veth1.forwarding)")) 195 goto fail; 196 197 /* Setup for policy routing tests */ 198 SYS(fail, "ip addr add %s/24 dev veth1", IPV4_LOCAL); 199 SYS(fail, "ip addr add %s/64 dev veth1 nodad", IPV6_LOCAL); 200 SYS(fail, "ip route add %s/32 via %s", IPV4_REMOTE_DST, IPV4_GW1); 201 SYS(fail, "ip route add %s/32 via %s table %s", IPV4_REMOTE_DST, IPV4_GW2, MARK_TABLE); 202 SYS(fail, "ip -6 route add %s/128 via %s", IPV6_REMOTE_DST, IPV6_GW1); 203 SYS(fail, "ip -6 route add %s/128 via %s table %s", IPV6_REMOTE_DST, IPV6_GW2, MARK_TABLE); 204 SYS(fail, "ip rule add prio 2 fwmark %d lookup %s", MARK, MARK_TABLE); 205 SYS(fail, "ip -6 rule add prio 2 fwmark %d lookup %s", MARK, MARK_TABLE); 206 207 return 0; 208 fail: 209 return -1; 210 } 211 212 static int set_lookup_params(struct bpf_fib_lookup *params, 213 const struct fib_lookup_test *test, 214 int ifindex) 215 { 216 int ret; 217 218 memset(params, 0, sizeof(*params)); 219 220 params->l4_protocol = IPPROTO_TCP; 221 params->ifindex = ifindex; 222 params->tbid = test->tbid; 223 params->mark = test->mark; 224 225 if (inet_pton(AF_INET6, test->daddr, params->ipv6_dst) == 1) { 226 params->family = AF_INET6; 227 if (!(test->lookup_flags & BPF_FIB_LOOKUP_SRC)) { 228 ret = inet_pton(AF_INET6, IPV6_IFACE_ADDR, params->ipv6_src); 229 if (!ASSERT_EQ(ret, 1, "inet_pton(IPV6_IFACE_ADDR)")) 230 return -1; 231 } 232 233 return 0; 234 } 235 236 ret = inet_pton(AF_INET, test->daddr, ¶ms->ipv4_dst); 237 if (!ASSERT_EQ(ret, 1, "convert IP[46] address")) 238 return -1; 239 params->family = AF_INET; 240 241 if (!(test->lookup_flags & BPF_FIB_LOOKUP_SRC)) { 242 ret = inet_pton(AF_INET, IPV4_IFACE_ADDR, ¶ms->ipv4_src); 243 if (!ASSERT_EQ(ret, 1, "inet_pton(IPV4_IFACE_ADDR)")) 244 return -1; 245 } 246 247 return 0; 248 } 249 250 static void mac_str(char *b, const __u8 *mac) 251 { 252 sprintf(b, "%02X:%02X:%02X:%02X:%02X:%02X", 253 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 254 } 255 256 static void assert_ip_address(int family, void *addr, const char *expected_str) 257 { 258 char str[INET6_ADDRSTRLEN]; 259 u8 expected_addr[16]; 260 int addr_len = 0; 261 int ret; 262 263 switch (family) { 264 case AF_INET6: 265 ret = inet_pton(AF_INET6, expected_str, expected_addr); 266 ASSERT_EQ(ret, 1, "inet_pton(AF_INET6, expected_str)"); 267 addr_len = 16; 268 break; 269 case AF_INET: 270 ret = inet_pton(AF_INET, expected_str, expected_addr); 271 ASSERT_EQ(ret, 1, "inet_pton(AF_INET, expected_str)"); 272 addr_len = 4; 273 break; 274 default: 275 PRINT_FAIL("invalid address family: %d", family); 276 break; 277 } 278 279 if (memcmp(addr, expected_addr, addr_len)) { 280 inet_ntop(family, addr, str, sizeof(str)); 281 PRINT_FAIL("expected %s actual %s ", expected_str, str); 282 } 283 } 284 285 static void assert_src_ip(struct bpf_fib_lookup *params, const char *expected) 286 { 287 assert_ip_address(params->family, params->ipv6_src, expected); 288 } 289 290 static void assert_dst_ip(struct bpf_fib_lookup *params, const char *expected) 291 { 292 assert_ip_address(params->family, params->ipv6_dst, expected); 293 } 294 295 void test_fib_lookup(void) 296 { 297 struct bpf_fib_lookup *fib_params; 298 struct nstoken *nstoken = NULL; 299 struct __sk_buff skb = { }; 300 struct fib_lookup *skel; 301 int prog_fd, err, ret, i; 302 303 /* The test does not use the skb->data, so 304 * use pkt_v6 for both v6 and v4 test. 305 */ 306 LIBBPF_OPTS(bpf_test_run_opts, run_opts, 307 .data_in = &pkt_v6, 308 .data_size_in = sizeof(pkt_v6), 309 .ctx_in = &skb, 310 .ctx_size_in = sizeof(skb), 311 ); 312 313 skel = fib_lookup__open_and_load(); 314 if (!ASSERT_OK_PTR(skel, "skel open_and_load")) 315 return; 316 prog_fd = bpf_program__fd(skel->progs.fib_lookup); 317 318 SYS(fail, "ip netns add %s", NS_TEST); 319 320 nstoken = open_netns(NS_TEST); 321 if (!ASSERT_OK_PTR(nstoken, "open_netns")) 322 goto fail; 323 324 if (setup_netns()) 325 goto fail; 326 327 skb.ifindex = if_nametoindex("veth1"); 328 if (!ASSERT_NEQ(skb.ifindex, 0, "if_nametoindex(veth1)")) 329 goto fail; 330 331 fib_params = &skel->bss->fib_params; 332 333 for (i = 0; i < ARRAY_SIZE(tests); i++) { 334 printf("Testing %s ", tests[i].desc); 335 336 if (set_lookup_params(fib_params, &tests[i], skb.ifindex)) 337 continue; 338 339 skel->bss->fib_lookup_ret = -1; 340 skel->bss->lookup_flags = tests[i].lookup_flags; 341 342 err = bpf_prog_test_run_opts(prog_fd, &run_opts); 343 if (!ASSERT_OK(err, "bpf_prog_test_run_opts")) 344 continue; 345 346 ASSERT_EQ(skel->bss->fib_lookup_ret, tests[i].expected_ret, 347 "fib_lookup_ret"); 348 349 if (tests[i].expected_src) 350 assert_src_ip(fib_params, tests[i].expected_src); 351 352 if (tests[i].expected_dst) 353 assert_dst_ip(fib_params, tests[i].expected_dst); 354 355 ret = memcmp(tests[i].dmac, fib_params->dmac, sizeof(tests[i].dmac)); 356 if (!ASSERT_EQ(ret, 0, "dmac not match")) { 357 char expected[18], actual[18]; 358 359 mac_str(expected, tests[i].dmac); 360 mac_str(actual, fib_params->dmac); 361 printf("dmac expected %s actual %s ", expected, actual); 362 } 363 364 // ensure tbid is zero'd out after fib lookup. 365 if (tests[i].lookup_flags & BPF_FIB_LOOKUP_DIRECT) { 366 if (!ASSERT_EQ(skel->bss->fib_params.tbid, 0, 367 "expected fib_params.tbid to be zero")) 368 goto fail; 369 } 370 } 371 372 fail: 373 if (nstoken) 374 close_netns(nstoken); 375 SYS_NOFAIL("ip netns del " NS_TEST); 376 fib_lookup__destroy(skel); 377 } 378
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.