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 DMAC "11:11:11:11:11:11" 30 #define DMAC_INIT { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, } 31 #define DMAC2 "01:01:01:01:01:01" 32 #define DMAC_INIT2 { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, } 33 34 struct fib_lookup_test { 35 const char *desc; 36 const char *daddr; 37 int expected_ret; 38 const char *expected_src; 39 int lookup_flags; 40 __u32 tbid; 41 __u8 dmac[6]; 42 }; 43 44 static const struct fib_lookup_test tests[] = { 45 { .desc = "IPv6 failed neigh", 46 .daddr = IPV6_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_NO_NEIGH, }, 47 { .desc = "IPv6 stale neigh", 48 .daddr = IPV6_NUD_STALE_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 49 .dmac = DMAC_INIT, }, 50 { .desc = "IPv6 skip neigh", 51 .daddr = IPV6_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 52 .lookup_flags = BPF_FIB_LOOKUP_SKIP_NEIGH, }, 53 { .desc = "IPv4 failed neigh", 54 .daddr = IPV4_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_NO_NEIGH, }, 55 { .desc = "IPv4 stale neigh", 56 .daddr = IPV4_NUD_STALE_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 57 .dmac = DMAC_INIT, }, 58 { .desc = "IPv4 skip neigh", 59 .daddr = IPV4_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 60 .lookup_flags = BPF_FIB_LOOKUP_SKIP_NEIGH, }, 61 { .desc = "IPv4 TBID lookup failure", 62 .daddr = IPV4_TBID_DST, .expected_ret = BPF_FIB_LKUP_RET_NOT_FWDED, 63 .lookup_flags = BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_TBID, 64 .tbid = RT_TABLE_MAIN, }, 65 { .desc = "IPv4 TBID lookup success", 66 .daddr = IPV4_TBID_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 67 .lookup_flags = BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_TBID, .tbid = 100, 68 .dmac = DMAC_INIT2, }, 69 { .desc = "IPv6 TBID lookup failure", 70 .daddr = IPV6_TBID_DST, .expected_ret = BPF_FIB_LKUP_RET_NOT_FWDED, 71 .lookup_flags = BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_TBID, 72 .tbid = RT_TABLE_MAIN, }, 73 { .desc = "IPv6 TBID lookup success", 74 .daddr = IPV6_TBID_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 75 .lookup_flags = BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_TBID, .tbid = 100, 76 .dmac = DMAC_INIT2, }, 77 { .desc = "IPv4 set src addr from netdev", 78 .daddr = IPV4_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 79 .expected_src = IPV4_IFACE_ADDR, 80 .lookup_flags = BPF_FIB_LOOKUP_SRC | BPF_FIB_LOOKUP_SKIP_NEIGH, }, 81 { .desc = "IPv6 set src addr from netdev", 82 .daddr = IPV6_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 83 .expected_src = IPV6_IFACE_ADDR, 84 .lookup_flags = BPF_FIB_LOOKUP_SRC | BPF_FIB_LOOKUP_SKIP_NEIGH, }, 85 { .desc = "IPv4 set prefsrc addr from route", 86 .daddr = IPV4_ADDR_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 87 .expected_src = IPV4_IFACE_ADDR_SEC, 88 .lookup_flags = BPF_FIB_LOOKUP_SRC | BPF_FIB_LOOKUP_SKIP_NEIGH, }, 89 { .desc = "IPv6 set prefsrc addr route", 90 .daddr = IPV6_ADDR_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, 91 .expected_src = IPV6_IFACE_ADDR_SEC, 92 .lookup_flags = BPF_FIB_LOOKUP_SRC | BPF_FIB_LOOKUP_SKIP_NEIGH, }, 93 }; 94 95 static int ifindex; 96 97 static int setup_netns(void) 98 { 99 int err; 100 101 SYS(fail, "ip link add veth1 type veth peer name veth2"); 102 SYS(fail, "ip link set dev veth1 up"); 103 SYS(fail, "ip link set dev veth2 up"); 104 105 err = write_sysctl("/proc/sys/net/ipv4/neigh/veth1/gc_stale_time", "900"); 106 if (!ASSERT_OK(err, "write_sysctl(net.ipv4.neigh.veth1.gc_stale_time)")) 107 goto fail; 108 109 err = write_sysctl("/proc/sys/net/ipv6/neigh/veth1/gc_stale_time", "900"); 110 if (!ASSERT_OK(err, "write_sysctl(net.ipv6.neigh.veth1.gc_stale_time)")) 111 goto fail; 112 113 SYS(fail, "ip addr add %s/64 dev veth1 nodad", IPV6_IFACE_ADDR); 114 SYS(fail, "ip neigh add %s dev veth1 nud failed", IPV6_NUD_FAILED_ADDR); 115 SYS(fail, "ip neigh add %s dev veth1 lladdr %s nud stale", IPV6_NUD_STALE_ADDR, DMAC); 116 117 SYS(fail, "ip addr add %s/24 dev veth1", IPV4_IFACE_ADDR); 118 SYS(fail, "ip neigh add %s dev veth1 nud failed", IPV4_NUD_FAILED_ADDR); 119 SYS(fail, "ip neigh add %s dev veth1 lladdr %s nud stale", IPV4_NUD_STALE_ADDR, DMAC); 120 121 /* Setup for prefsrc IP addr selection */ 122 SYS(fail, "ip addr add %s/24 dev veth1", IPV4_IFACE_ADDR_SEC); 123 SYS(fail, "ip route add %s/32 dev veth1 src %s", IPV4_ADDR_DST, IPV4_IFACE_ADDR_SEC); 124 125 SYS(fail, "ip addr add %s/64 dev veth1 nodad", IPV6_IFACE_ADDR_SEC); 126 SYS(fail, "ip route add %s/128 dev veth1 src %s", IPV6_ADDR_DST, IPV6_IFACE_ADDR_SEC); 127 128 /* Setup for tbid lookup tests */ 129 SYS(fail, "ip addr add %s/24 dev veth2", IPV4_TBID_ADDR); 130 SYS(fail, "ip route del %s/24 dev veth2", IPV4_TBID_NET); 131 SYS(fail, "ip route add table 100 %s/24 dev veth2", IPV4_TBID_NET); 132 SYS(fail, "ip neigh add %s dev veth2 lladdr %s nud stale", IPV4_TBID_DST, DMAC2); 133 134 SYS(fail, "ip addr add %s/64 dev veth2", IPV6_TBID_ADDR); 135 SYS(fail, "ip -6 route del %s/64 dev veth2", IPV6_TBID_NET); 136 SYS(fail, "ip -6 route add table 100 %s/64 dev veth2", IPV6_TBID_NET); 137 SYS(fail, "ip neigh add %s dev veth2 lladdr %s nud stale", IPV6_TBID_DST, DMAC2); 138 139 err = write_sysctl("/proc/sys/net/ipv4/conf/veth1/forwarding", "1"); 140 if (!ASSERT_OK(err, "write_sysctl(net.ipv4.conf.veth1.forwarding)")) 141 goto fail; 142 143 err = write_sysctl("/proc/sys/net/ipv6/conf/veth1/forwarding", "1"); 144 if (!ASSERT_OK(err, "write_sysctl(net.ipv6.conf.veth1.forwarding)")) 145 goto fail; 146 147 return 0; 148 fail: 149 return -1; 150 } 151 152 static int set_lookup_params(struct bpf_fib_lookup *params, const struct fib_lookup_test *test) 153 { 154 int ret; 155 156 memset(params, 0, sizeof(*params)); 157 158 params->l4_protocol = IPPROTO_TCP; 159 params->ifindex = ifindex; 160 params->tbid = test->tbid; 161 162 if (inet_pton(AF_INET6, test->daddr, params->ipv6_dst) == 1) { 163 params->family = AF_INET6; 164 if (!(test->lookup_flags & BPF_FIB_LOOKUP_SRC)) { 165 ret = inet_pton(AF_INET6, IPV6_IFACE_ADDR, params->ipv6_src); 166 if (!ASSERT_EQ(ret, 1, "inet_pton(IPV6_IFACE_ADDR)")) 167 return -1; 168 } 169 170 return 0; 171 } 172 173 ret = inet_pton(AF_INET, test->daddr, ¶ms->ipv4_dst); 174 if (!ASSERT_EQ(ret, 1, "convert IP[46] address")) 175 return -1; 176 params->family = AF_INET; 177 178 if (!(test->lookup_flags & BPF_FIB_LOOKUP_SRC)) { 179 ret = inet_pton(AF_INET, IPV4_IFACE_ADDR, ¶ms->ipv4_src); 180 if (!ASSERT_EQ(ret, 1, "inet_pton(IPV4_IFACE_ADDR)")) 181 return -1; 182 } 183 184 return 0; 185 } 186 187 static void mac_str(char *b, const __u8 *mac) 188 { 189 sprintf(b, "%02X:%02X:%02X:%02X:%02X:%02X", 190 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 191 } 192 193 static void assert_src_ip(struct bpf_fib_lookup *fib_params, const char *expected_src) 194 { 195 int ret; 196 __u32 src6[4]; 197 __be32 src4; 198 199 switch (fib_params->family) { 200 case AF_INET6: 201 ret = inet_pton(AF_INET6, expected_src, src6); 202 ASSERT_EQ(ret, 1, "inet_pton(expected_src)"); 203 204 ret = memcmp(src6, fib_params->ipv6_src, sizeof(fib_params->ipv6_src)); 205 if (!ASSERT_EQ(ret, 0, "fib_lookup ipv6 src")) { 206 char str_src6[64]; 207 208 inet_ntop(AF_INET6, fib_params->ipv6_src, str_src6, 209 sizeof(str_src6)); 210 printf("ipv6 expected %s actual %s ", expected_src, 211 str_src6); 212 } 213 214 break; 215 case AF_INET: 216 ret = inet_pton(AF_INET, expected_src, &src4); 217 ASSERT_EQ(ret, 1, "inet_pton(expected_src)"); 218 219 ASSERT_EQ(fib_params->ipv4_src, src4, "fib_lookup ipv4 src"); 220 221 break; 222 default: 223 PRINT_FAIL("invalid addr family: %d", fib_params->family); 224 } 225 } 226 227 void test_fib_lookup(void) 228 { 229 struct bpf_fib_lookup *fib_params; 230 struct nstoken *nstoken = NULL; 231 struct __sk_buff skb = { }; 232 struct fib_lookup *skel; 233 int prog_fd, err, ret, i; 234 235 /* The test does not use the skb->data, so 236 * use pkt_v6 for both v6 and v4 test. 237 */ 238 LIBBPF_OPTS(bpf_test_run_opts, run_opts, 239 .data_in = &pkt_v6, 240 .data_size_in = sizeof(pkt_v6), 241 .ctx_in = &skb, 242 .ctx_size_in = sizeof(skb), 243 ); 244 245 skel = fib_lookup__open_and_load(); 246 if (!ASSERT_OK_PTR(skel, "skel open_and_load")) 247 return; 248 prog_fd = bpf_program__fd(skel->progs.fib_lookup); 249 250 SYS(fail, "ip netns add %s", NS_TEST); 251 252 nstoken = open_netns(NS_TEST); 253 if (!ASSERT_OK_PTR(nstoken, "open_netns")) 254 goto fail; 255 256 if (setup_netns()) 257 goto fail; 258 259 ifindex = if_nametoindex("veth1"); 260 skb.ifindex = ifindex; 261 fib_params = &skel->bss->fib_params; 262 263 for (i = 0; i < ARRAY_SIZE(tests); i++) { 264 printf("Testing %s ", tests[i].desc); 265 266 if (set_lookup_params(fib_params, &tests[i])) 267 continue; 268 skel->bss->fib_lookup_ret = -1; 269 skel->bss->lookup_flags = tests[i].lookup_flags; 270 271 err = bpf_prog_test_run_opts(prog_fd, &run_opts); 272 if (!ASSERT_OK(err, "bpf_prog_test_run_opts")) 273 continue; 274 275 ASSERT_EQ(skel->bss->fib_lookup_ret, tests[i].expected_ret, 276 "fib_lookup_ret"); 277 278 if (tests[i].expected_src) 279 assert_src_ip(fib_params, tests[i].expected_src); 280 281 ret = memcmp(tests[i].dmac, fib_params->dmac, sizeof(tests[i].dmac)); 282 if (!ASSERT_EQ(ret, 0, "dmac not match")) { 283 char expected[18], actual[18]; 284 285 mac_str(expected, tests[i].dmac); 286 mac_str(actual, fib_params->dmac); 287 printf("dmac expected %s actual %s ", expected, actual); 288 } 289 290 // ensure tbid is zero'd out after fib lookup. 291 if (tests[i].lookup_flags & BPF_FIB_LOOKUP_DIRECT) { 292 if (!ASSERT_EQ(skel->bss->fib_params.tbid, 0, 293 "expected fib_params.tbid to be zero")) 294 goto fail; 295 } 296 } 297 298 fail: 299 if (nstoken) 300 close_netns(nstoken); 301 SYS_NOFAIL("ip netns del " NS_TEST); 302 fib_lookup__destroy(skel); 303 } 304