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, &params->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, &params->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 " &> /dev/null");
302 	fib_lookup__destroy(skel);
303 }
304