378 lines · c
1// SPDX-License-Identifier: GPL-2.02/* 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 3330#define MARK 4231#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 45struct 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 57static 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 147static 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;208fail:209 return -1;210}211 212static 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 250static 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 256static 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 285static 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 290static 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 295void 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, so304 * 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 372fail:373 if (nstoken)374 close_netns(nstoken);375 SYS_NOFAIL("ip netns del " NS_TEST);376 fib_lookup__destroy(skel);377}378