|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | +/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ |
| 3 | + |
| 4 | +#include <sys/types.h> |
| 5 | +#include <net/if.h> |
| 6 | + |
| 7 | +#include "test_progs.h" |
| 8 | +#include "network_helpers.h" |
| 9 | +#include "fib_lookup.skel.h" |
| 10 | + |
| 11 | +#define SYS(fmt, ...) \ |
| 12 | + ({ \ |
| 13 | + char cmd[1024]; \ |
| 14 | + snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \ |
| 15 | + if (!ASSERT_OK(system(cmd), cmd)) \ |
| 16 | + goto fail; \ |
| 17 | + }) |
| 18 | + |
| 19 | +#define NS_TEST "fib_lookup_ns" |
| 20 | +#define IPV6_IFACE_ADDR "face::face" |
| 21 | +#define IPV6_NUD_FAILED_ADDR "face::1" |
| 22 | +#define IPV6_NUD_STALE_ADDR "face::2" |
| 23 | +#define IPV4_IFACE_ADDR "10.0.0.254" |
| 24 | +#define IPV4_NUD_FAILED_ADDR "10.0.0.1" |
| 25 | +#define IPV4_NUD_STALE_ADDR "10.0.0.2" |
| 26 | +#define DMAC "11:11:11:11:11:11" |
| 27 | +#define DMAC_INIT { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, } |
| 28 | + |
| 29 | +struct fib_lookup_test { |
| 30 | + const char *desc; |
| 31 | + const char *daddr; |
| 32 | + int expected_ret; |
| 33 | + int lookup_flags; |
| 34 | + __u8 dmac[6]; |
| 35 | +}; |
| 36 | + |
| 37 | +static const struct fib_lookup_test tests[] = { |
| 38 | + { .desc = "IPv6 failed neigh", |
| 39 | + .daddr = IPV6_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_NO_NEIGH, }, |
| 40 | + { .desc = "IPv6 stale neigh", |
| 41 | + .daddr = IPV6_NUD_STALE_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, |
| 42 | + .dmac = DMAC_INIT, }, |
| 43 | + { .desc = "IPv6 skip neigh", |
| 44 | + .daddr = IPV6_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, |
| 45 | + .lookup_flags = BPF_FIB_LOOKUP_SKIP_NEIGH, }, |
| 46 | + { .desc = "IPv4 failed neigh", |
| 47 | + .daddr = IPV4_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_NO_NEIGH, }, |
| 48 | + { .desc = "IPv4 stale neigh", |
| 49 | + .daddr = IPV4_NUD_STALE_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, |
| 50 | + .dmac = DMAC_INIT, }, |
| 51 | + { .desc = "IPv4 skip neigh", |
| 52 | + .daddr = IPV4_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS, |
| 53 | + .lookup_flags = BPF_FIB_LOOKUP_SKIP_NEIGH, }, |
| 54 | +}; |
| 55 | + |
| 56 | +static int ifindex; |
| 57 | + |
| 58 | +static int setup_netns(void) |
| 59 | +{ |
| 60 | + int err; |
| 61 | + |
| 62 | + SYS("ip link add veth1 type veth peer name veth2"); |
| 63 | + SYS("ip link set dev veth1 up"); |
| 64 | + |
| 65 | + SYS("ip addr add %s/64 dev veth1 nodad", IPV6_IFACE_ADDR); |
| 66 | + SYS("ip neigh add %s dev veth1 nud failed", IPV6_NUD_FAILED_ADDR); |
| 67 | + SYS("ip neigh add %s dev veth1 lladdr %s nud stale", IPV6_NUD_STALE_ADDR, DMAC); |
| 68 | + |
| 69 | + SYS("ip addr add %s/24 dev veth1 nodad", IPV4_IFACE_ADDR); |
| 70 | + SYS("ip neigh add %s dev veth1 nud failed", IPV4_NUD_FAILED_ADDR); |
| 71 | + SYS("ip neigh add %s dev veth1 lladdr %s nud stale", IPV4_NUD_STALE_ADDR, DMAC); |
| 72 | + |
| 73 | + err = write_sysctl("/proc/sys/net/ipv4/conf/veth1/forwarding", "1"); |
| 74 | + if (!ASSERT_OK(err, "write_sysctl(net.ipv4.conf.veth1.forwarding)")) |
| 75 | + goto fail; |
| 76 | + |
| 77 | + err = write_sysctl("/proc/sys/net/ipv6/conf/veth1/forwarding", "1"); |
| 78 | + if (!ASSERT_OK(err, "write_sysctl(net.ipv6.conf.veth1.forwarding)")) |
| 79 | + goto fail; |
| 80 | + |
| 81 | + return 0; |
| 82 | +fail: |
| 83 | + return -1; |
| 84 | +} |
| 85 | + |
| 86 | +static int set_lookup_params(struct bpf_fib_lookup *params, const char *daddr) |
| 87 | +{ |
| 88 | + int ret; |
| 89 | + |
| 90 | + memset(params, 0, sizeof(*params)); |
| 91 | + |
| 92 | + params->l4_protocol = IPPROTO_TCP; |
| 93 | + params->ifindex = ifindex; |
| 94 | + |
| 95 | + if (inet_pton(AF_INET6, daddr, params->ipv6_dst) == 1) { |
| 96 | + params->family = AF_INET6; |
| 97 | + ret = inet_pton(AF_INET6, IPV6_IFACE_ADDR, params->ipv6_src); |
| 98 | + if (!ASSERT_EQ(ret, 1, "inet_pton(IPV6_IFACE_ADDR)")) |
| 99 | + return -1; |
| 100 | + return 0; |
| 101 | + } |
| 102 | + |
| 103 | + ret = inet_pton(AF_INET, daddr, ¶ms->ipv4_dst); |
| 104 | + if (!ASSERT_EQ(ret, 1, "convert IP[46] address")) |
| 105 | + return -1; |
| 106 | + params->family = AF_INET; |
| 107 | + ret = inet_pton(AF_INET, IPV4_IFACE_ADDR, ¶ms->ipv4_src); |
| 108 | + if (!ASSERT_EQ(ret, 1, "inet_pton(IPV4_IFACE_ADDR)")) |
| 109 | + return -1; |
| 110 | + |
| 111 | + return 0; |
| 112 | +} |
| 113 | + |
| 114 | +static void mac_str(char *b, const __u8 *mac) |
| 115 | +{ |
| 116 | + sprintf(b, "%02X:%02X:%02X:%02X:%02X:%02X", |
| 117 | + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); |
| 118 | +} |
| 119 | + |
| 120 | +void test_fib_lookup(void) |
| 121 | +{ |
| 122 | + struct bpf_fib_lookup *fib_params; |
| 123 | + struct nstoken *nstoken = NULL; |
| 124 | + struct __sk_buff skb = { }; |
| 125 | + struct fib_lookup *skel; |
| 126 | + int prog_fd, err, ret, i; |
| 127 | + |
| 128 | + /* The test does not use the skb->data, so |
| 129 | + * use pkt_v6 for both v6 and v4 test. |
| 130 | + */ |
| 131 | + LIBBPF_OPTS(bpf_test_run_opts, run_opts, |
| 132 | + .data_in = &pkt_v6, |
| 133 | + .data_size_in = sizeof(pkt_v6), |
| 134 | + .ctx_in = &skb, |
| 135 | + .ctx_size_in = sizeof(skb), |
| 136 | + ); |
| 137 | + |
| 138 | + skel = fib_lookup__open_and_load(); |
| 139 | + if (!ASSERT_OK_PTR(skel, "skel open_and_load")) |
| 140 | + return; |
| 141 | + prog_fd = bpf_program__fd(skel->progs.fib_lookup); |
| 142 | + |
| 143 | + SYS("ip netns add %s", NS_TEST); |
| 144 | + |
| 145 | + nstoken = open_netns(NS_TEST); |
| 146 | + if (!ASSERT_OK_PTR(nstoken, "open_netns")) |
| 147 | + goto fail; |
| 148 | + |
| 149 | + if (setup_netns()) |
| 150 | + goto fail; |
| 151 | + |
| 152 | + ifindex = if_nametoindex("veth1"); |
| 153 | + skb.ifindex = ifindex; |
| 154 | + fib_params = &skel->bss->fib_params; |
| 155 | + |
| 156 | + for (i = 0; i < ARRAY_SIZE(tests); i++) { |
| 157 | + printf("Testing %s\n", tests[i].desc); |
| 158 | + |
| 159 | + if (set_lookup_params(fib_params, tests[i].daddr)) |
| 160 | + continue; |
| 161 | + skel->bss->fib_lookup_ret = -1; |
| 162 | + skel->bss->lookup_flags = BPF_FIB_LOOKUP_OUTPUT | |
| 163 | + tests[i].lookup_flags; |
| 164 | + |
| 165 | + err = bpf_prog_test_run_opts(prog_fd, &run_opts); |
| 166 | + if (!ASSERT_OK(err, "bpf_prog_test_run_opts")) |
| 167 | + continue; |
| 168 | + |
| 169 | + ASSERT_EQ(tests[i].expected_ret, skel->bss->fib_lookup_ret, |
| 170 | + "fib_lookup_ret"); |
| 171 | + |
| 172 | + ret = memcmp(tests[i].dmac, fib_params->dmac, sizeof(tests[i].dmac)); |
| 173 | + if (!ASSERT_EQ(ret, 0, "dmac not match")) { |
| 174 | + char expected[18], actual[18]; |
| 175 | + |
| 176 | + mac_str(expected, tests[i].dmac); |
| 177 | + mac_str(actual, fib_params->dmac); |
| 178 | + printf("dmac expected %s actual %s\n", expected, actual); |
| 179 | + } |
| 180 | + } |
| 181 | + |
| 182 | +fail: |
| 183 | + if (nstoken) |
| 184 | + close_netns(nstoken); |
| 185 | + system("ip netns del " NS_TEST " &> /dev/null"); |
| 186 | + fib_lookup__destroy(skel); |
| 187 | +} |
0 commit comments