1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
3
4 #include <linux/rtnetlink.h>
5 #include <linux/if_ether.h>
6 #include <sys/types.h>
7 #include <net/if.h>
8
9 #include "test_progs.h"
10 #include "network_helpers.h"
11 #include "fib_lookup.skel.h"
12
13 #define NS_TEST "fib_lookup_ns"
14 #define IPV6_IFACE_ADDR "face::face"
15 #define IPV6_IFACE_ADDR_SEC "cafe::cafe"
16 #define IPV6_ADDR_DST "face::3"
17 #define IPV6_NUD_FAILED_ADDR "face::1"
18 #define IPV6_NUD_STALE_ADDR "face::2"
19 #define IPV4_IFACE_ADDR "10.0.0.254"
20 #define IPV4_IFACE_ADDR_SEC "10.1.0.254"
21 #define IPV4_ADDR_DST "10.2.0.254"
22 #define IPV4_NUD_FAILED_ADDR "10.0.0.1"
23 #define IPV4_NUD_STALE_ADDR "10.0.0.2"
24 #define IPV4_TBID_ADDR "172.0.0.254"
25 #define IPV4_TBID_NET "172.0.0.0"
26 #define IPV4_TBID_DST "172.0.0.2"
27 #define IPV4_TBID_NONEIGH_DST "172.0.0.5"
28 #define IPV6_TBID_ADDR "fd00::FFFF"
29 #define IPV6_TBID_NET "fd00::"
30 #define IPV6_TBID_DST "fd00::2"
31 #define MARK_NO_POLICY 33
32 #define MARK 42
33 #define MARK_TABLE "200"
34 #define IPV4_REMOTE_DST "1.2.3.4"
35 #define IPV4_LOCAL "10.4.0.3"
36 #define IPV4_GW1 "10.4.0.1"
37 #define IPV4_GW2 "10.4.0.2"
38 #define IPV6_REMOTE_DST "be:ef::b0:10"
39 #define IPV6_LOCAL "fd01::3"
40 #define IPV6_GW1 "fd01::1"
41 #define IPV6_GW2 "fd01::2"
42 #define VLAN_ID 100
43 #define VLAN_IFACE "veth1.100"
44 #define VLAN_ID_DOWN 102
45 #define VLAN_IFACE_DOWN "veth1.102"
46 #define QINQ_OUTER_IFACE "veth1.200"
47 #define QINQ_INNER_IFACE "veth1.200.300"
48 #define VLAN_TABLE "300"
49 #define IPV4_VLAN_IFACE_ADDR "10.5.0.254"
50 #define IPV4_VLAN_EGRESS_DST "10.5.0.2"
51 #define IPV4_QINQ_DST "10.7.0.2"
52 #define IPV4_VLAN_DST "10.6.0.2"
53 #define IPV4_VLAN_GW "10.5.0.1"
54 #define IPV6_VLAN_IFACE_ADDR "fd02::254"
55 #define IPV6_VLAN_EGRESS_DST "fd02::2"
56 #define IPV6_VLAN_DST "fd03::2"
57 #define IPV6_VLAN_GW "fd02::1"
58 #define VLAN_VID_UNUSED 999
59 #define VRF_IFACE "vrf-blue"
60 #define VRF_TABLE "1000"
61 #define VRF_VLAN_ID 101
62 #define VRF_VLAN_IFACE "veth1.101"
63 #define IPV4_VRF_IFACE_ADDR "10.8.0.254"
64 #define IPV4_VRF_GW "10.8.0.1"
65 #define IPV4_VRF_DST "10.9.0.2"
66 #define TBID_VLAN_ID 50
67 #define TBID_VLAN_IFACE "veth2.50"
68 #define IPV4_TBID_VLAN_DST "172.2.0.2"
69 #define IPV4_BOND_VLAN_DST "10.11.0.2"
70 #define IPV4_VLAN_MTU_DST "10.5.9.2"
71 #define QINQ_AD_VLAN_ID 200
72 #define QINQ_INNER_VLAN_ID 300
73 #define BOND_IFACE "bond99"
74 #define BOND_PORT "veth3"
75 #define BOND_PORT_PEER "veth4"
76 #define BOND_VLAN_ID 500
77 #define DMAC "11:11:11:11:11:11"
78 #define DMAC_INIT { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, }
79 #define DMAC2 "01:01:01:01:01:01"
80 #define DMAC_INIT2 { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, }
81
82 struct fib_lookup_test {
83 const char *desc;
84 const char *daddr;
85 int expected_ret;
86 const char *expected_src;
87 const char *expected_dst;
88 int lookup_flags;
89 __u32 tbid;
90 __u8 dmac[6];
91 __u32 mark;
92 /*
93 * input tag with BPF_FIB_LOOKUP_VLAN_INPUT; expected output tag
94 * with BPF_FIB_LOOKUP_VLAN (checked when check_vlan is set)
95 */
96 __u16 vlan_proto;
97 __u16 vlan_id;
98 bool check_vlan;
99 const char *expected_dev; /* expected params->ifindex after lookup */
100 const char *iif; /* override the default veth1 input device */
101 __u16 tot_len; /* triggers the in-lookup mtu check when set */
102 __u16 expected_mtu; /* expected mtu_result (union with tot_len) */
103 };
104
105 static const struct fib_lookup_test tests[] = {
106 { .desc = "IPv6 failed neigh",
107 .daddr = IPV6_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_NO_NEIGH, },
108 { .desc = "IPv6 stale neigh",
109 .daddr = IPV6_NUD_STALE_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
110 .dmac = DMAC_INIT, },
111 { .desc = "IPv6 skip neigh",
112 .daddr = IPV6_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
113 .lookup_flags = BPF_FIB_LOOKUP_SKIP_NEIGH, },
114 { .desc = "IPv4 failed neigh",
115 .daddr = IPV4_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_NO_NEIGH, },
116 { .desc = "IPv4 stale neigh",
117 .daddr = IPV4_NUD_STALE_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
118 .dmac = DMAC_INIT, },
119 { .desc = "IPv4 skip neigh",
120 .daddr = IPV4_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
121 .lookup_flags = BPF_FIB_LOOKUP_SKIP_NEIGH, },
122 { .desc = "IPv4 TBID lookup failure",
123 .daddr = IPV4_TBID_DST, .expected_ret = BPF_FIB_LKUP_RET_NOT_FWDED,
124 .lookup_flags = BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_TBID,
125 .tbid = RT_TABLE_MAIN, },
126 { .desc = "IPv4 TBID lookup success",
127 .daddr = IPV4_TBID_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
128 .lookup_flags = BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_TBID, .tbid = 100,
129 .dmac = DMAC_INIT2, },
130 /*
131 * An error that returns after the egress device is resolved must
132 * report the egress ifindex, not the input. This routes from input
133 * veth1 via veth2 (table 100) to a dst with no neighbour, so
134 * input != egress, pinning NO_NEIGH to the egress device.
135 */
136 { .desc = "IPv4 NO_NEIGH reports the egress ifindex, not the input",
137 .daddr = IPV4_TBID_NONEIGH_DST,
138 .expected_ret = BPF_FIB_LKUP_RET_NO_NEIGH,
139 .lookup_flags = BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_TBID, .tbid = 100,
140 .expected_dev = "veth2", },
141 { .desc = "IPv6 TBID lookup failure",
142 .daddr = IPV6_TBID_DST, .expected_ret = BPF_FIB_LKUP_RET_NOT_FWDED,
143 .lookup_flags = BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_TBID,
144 .tbid = RT_TABLE_MAIN, },
145 { .desc = "IPv6 TBID lookup success",
146 .daddr = IPV6_TBID_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
147 .lookup_flags = BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_TBID, .tbid = 100,
148 .dmac = DMAC_INIT2, },
149 { .desc = "IPv4 set src addr from netdev",
150 .daddr = IPV4_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
151 .expected_src = IPV4_IFACE_ADDR,
152 .lookup_flags = BPF_FIB_LOOKUP_SRC | BPF_FIB_LOOKUP_SKIP_NEIGH, },
153 { .desc = "IPv6 set src addr from netdev",
154 .daddr = IPV6_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
155 .expected_src = IPV6_IFACE_ADDR,
156 .lookup_flags = BPF_FIB_LOOKUP_SRC | BPF_FIB_LOOKUP_SKIP_NEIGH, },
157 { .desc = "IPv4 set prefsrc addr from route",
158 .daddr = IPV4_ADDR_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
159 .expected_src = IPV4_IFACE_ADDR_SEC,
160 .lookup_flags = BPF_FIB_LOOKUP_SRC | BPF_FIB_LOOKUP_SKIP_NEIGH, },
161 { .desc = "IPv6 set prefsrc addr route",
162 .daddr = IPV6_ADDR_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
163 .expected_src = IPV6_IFACE_ADDR_SEC,
164 .lookup_flags = BPF_FIB_LOOKUP_SRC | BPF_FIB_LOOKUP_SKIP_NEIGH, },
165 /* policy routing */
166 { .desc = "IPv4 policy routing, default",
167 .daddr = IPV4_REMOTE_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
168 .expected_dst = IPV4_GW1,
169 .lookup_flags = BPF_FIB_LOOKUP_MARK | BPF_FIB_LOOKUP_SKIP_NEIGH, },
170 { .desc = "IPv4 policy routing, mark doesn't point to a policy",
171 .daddr = IPV4_REMOTE_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
172 .expected_dst = IPV4_GW1,
173 .lookup_flags = BPF_FIB_LOOKUP_MARK | BPF_FIB_LOOKUP_SKIP_NEIGH,
174 .mark = MARK_NO_POLICY, },
175 { .desc = "IPv4 policy routing, mark points to a policy",
176 .daddr = IPV4_REMOTE_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
177 .expected_dst = IPV4_GW2,
178 .lookup_flags = BPF_FIB_LOOKUP_MARK | BPF_FIB_LOOKUP_SKIP_NEIGH,
179 .mark = MARK, },
180 { .desc = "IPv4 policy routing, mark points to a policy, but no flag",
181 .daddr = IPV4_REMOTE_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
182 .expected_dst = IPV4_GW1,
183 .lookup_flags = BPF_FIB_LOOKUP_SKIP_NEIGH,
184 .mark = MARK, },
185 { .desc = "IPv6 policy routing, default",
186 .daddr = IPV6_REMOTE_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
187 .expected_dst = IPV6_GW1,
188 .lookup_flags = BPF_FIB_LOOKUP_MARK | BPF_FIB_LOOKUP_SKIP_NEIGH, },
189 { .desc = "IPv6 policy routing, mark doesn't point to a policy",
190 .daddr = IPV6_REMOTE_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
191 .expected_dst = IPV6_GW1,
192 .lookup_flags = BPF_FIB_LOOKUP_MARK | BPF_FIB_LOOKUP_SKIP_NEIGH,
193 .mark = MARK_NO_POLICY, },
194 { .desc = "IPv6 policy routing, mark points to a policy",
195 .daddr = IPV6_REMOTE_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
196 .expected_dst = IPV6_GW2,
197 .lookup_flags = BPF_FIB_LOOKUP_MARK | BPF_FIB_LOOKUP_SKIP_NEIGH,
198 .mark = MARK, },
199 { .desc = "IPv6 policy routing, mark points to a policy, but no flag",
200 .daddr = IPV6_REMOTE_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
201 .expected_dst = IPV6_GW1,
202 .lookup_flags = BPF_FIB_LOOKUP_SKIP_NEIGH,
203 .mark = MARK, },
204 /* vlan egress resolution */
205 /*
206 * Invariant the VLAN-egress arms jointly enforce: a
207 * BPF_FIB_LOOKUP_VLAN SUCCESS always carries a physical,
208 * xmit-capable ifindex; no SUCCESS ever returns a VLAN-device
209 * ifindex. Reducible arms pin ifindex == the physical parent; the
210 * QinQ and foreign-netns arms pin VLAN_FAILURE with params->ifindex
211 * left at the input, so a regression to best-effort (SUCCESS + the
212 * VLAN ifindex) fails one.
213 */
214 { .desc = "IPv4 VLAN egress, no flag",
215 .daddr = IPV4_VLAN_EGRESS_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
216 .lookup_flags = BPF_FIB_LOOKUP_SKIP_NEIGH,
217 .expected_dev = VLAN_IFACE, .check_vlan = true, },
218 { .desc = "IPv4 VLAN egress, single VLAN",
219 .daddr = IPV4_VLAN_EGRESS_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
220 .lookup_flags = BPF_FIB_LOOKUP_VLAN | BPF_FIB_LOOKUP_SKIP_NEIGH,
221 .expected_dev = "veth1", .check_vlan = true,
222 .vlan_proto = ETH_P_8021Q, .vlan_id = VLAN_ID, },
223 /*
224 * skb path without tot_len: mtu_result is the VLAN device's mtu
225 * (1400), not the parent's (1500)
226 */
227 { .desc = "IPv4 VLAN egress, skb-path mtu is the VLAN device's without the flag",
228 .daddr = IPV4_VLAN_EGRESS_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
229 .lookup_flags = BPF_FIB_LOOKUP_SKIP_NEIGH,
230 .expected_dev = VLAN_IFACE, .check_vlan = true, .expected_mtu = 1400, },
231 { .desc = "IPv4 VLAN egress, flag set but egress is not a VLAN",
232 .daddr = IPV4_NUD_FAILED_ADDR, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
233 .lookup_flags = BPF_FIB_LOOKUP_VLAN | BPF_FIB_LOOKUP_SKIP_NEIGH,
234 .expected_dev = "veth1", .check_vlan = true, },
235 { .desc = "IPv4 VLAN egress, QinQ not reducible (VLAN_FAILURE)",
236 .daddr = IPV4_QINQ_DST,
237 .expected_ret = BPF_FIB_LKUP_RET_VLAN_FAILURE,
238 .lookup_flags = BPF_FIB_LOOKUP_VLAN | BPF_FIB_LOOKUP_SKIP_NEIGH,
239 .expected_dev = "veth1", .check_vlan = true, },
240 { .desc = "IPv4 QinQ egress without the flag (escape hatch)",
241 .daddr = IPV4_QINQ_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
242 .lookup_flags = BPF_FIB_LOOKUP_SKIP_NEIGH,
243 .expected_dev = QINQ_INNER_IFACE, },
244 { .desc = "IPv6 VLAN egress, single VLAN",
245 .daddr = IPV6_VLAN_EGRESS_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
246 .lookup_flags = BPF_FIB_LOOKUP_VLAN | BPF_FIB_LOOKUP_SKIP_NEIGH,
247 .expected_dev = "veth1", .check_vlan = true,
248 .vlan_proto = ETH_P_8021Q, .vlan_id = VLAN_ID, },
249 { .desc = "IPv4 VLAN egress, neighbour on the VLAN device",
250 .daddr = IPV4_VLAN_EGRESS_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
251 .lookup_flags = BPF_FIB_LOOKUP_VLAN,
252 .expected_dev = "veth1", .check_vlan = true,
253 .vlan_proto = ETH_P_8021Q, .vlan_id = VLAN_ID, .dmac = DMAC_INIT, },
254 { .desc = "IPv4 VLAN egress in OUTPUT mode",
255 .daddr = IPV4_VLAN_EGRESS_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
256 .iif = VLAN_IFACE,
257 .lookup_flags = BPF_FIB_LOOKUP_OUTPUT | BPF_FIB_LOOKUP_VLAN |
258 BPF_FIB_LOOKUP_SKIP_NEIGH,
259 .expected_dev = "veth1", .check_vlan = true,
260 .vlan_proto = ETH_P_8021Q, .vlan_id = VLAN_ID, },
261 { .desc = "IPv4 VLAN egress over a bond",
262 .daddr = IPV4_BOND_VLAN_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
263 .lookup_flags = BPF_FIB_LOOKUP_VLAN | BPF_FIB_LOOKUP_SKIP_NEIGH,
264 .expected_dev = BOND_IFACE, .check_vlan = true,
265 .vlan_proto = ETH_P_8021Q, .vlan_id = BOND_VLAN_ID, },
266 { .desc = "IPv4 VLAN egress via TBID table",
267 .daddr = IPV4_TBID_VLAN_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
268 .lookup_flags = BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_TBID |
269 BPF_FIB_LOOKUP_VLAN | BPF_FIB_LOOKUP_SKIP_NEIGH,
270 .tbid = 100,
271 .expected_dev = "veth2", .check_vlan = true,
272 .vlan_proto = ETH_P_8021Q, .vlan_id = TBID_VLAN_ID, },
273 { .desc = "IPv4 VLAN egress, success writes mtu_result with the swap",
274 .daddr = IPV4_VLAN_MTU_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
275 .tot_len = 500, .expected_mtu = 1000,
276 .lookup_flags = BPF_FIB_LOOKUP_VLAN | BPF_FIB_LOOKUP_SKIP_NEIGH,
277 .expected_dev = "veth1", .check_vlan = true,
278 .vlan_proto = ETH_P_8021Q, .vlan_id = VLAN_ID, },
279 { .desc = "IPv4 VLAN egress, FRAG_NEEDED reports mtu, swap unwritten",
280 .daddr = IPV4_VLAN_MTU_DST, .expected_ret = BPF_FIB_LKUP_RET_FRAG_NEEDED,
281 .tot_len = 1400, .expected_mtu = 1000,
282 .lookup_flags = BPF_FIB_LOOKUP_VLAN | BPF_FIB_LOOKUP_SKIP_NEIGH,
283 .expected_dev = "veth1", .check_vlan = true, },
284 /* vlan tag as lookup input */
285 { .desc = "IPv4 VLAN input, no flag",
286 .daddr = IPV4_VLAN_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
287 .expected_dst = IPV4_GW1,
288 .lookup_flags = BPF_FIB_LOOKUP_SKIP_NEIGH, },
289 { .desc = "IPv4 VLAN input, tag selects subinterface route",
290 .daddr = IPV4_VLAN_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
291 .expected_dst = IPV4_VLAN_GW, .expected_dev = VLAN_IFACE,
292 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_SKIP_NEIGH,
293 .vlan_proto = ETH_P_8021Q, .vlan_id = VLAN_ID, },
294 { .desc = "IPv6 VLAN input, tag selects subinterface route",
295 .daddr = IPV6_VLAN_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
296 .expected_dst = IPV6_VLAN_GW, .expected_dev = VLAN_IFACE,
297 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_SKIP_NEIGH,
298 .vlan_proto = ETH_P_8021Q, .vlan_id = VLAN_ID, },
299 { .desc = "IPv4 VLAN input and egress combined",
300 .daddr = IPV4_VLAN_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
301 .expected_dst = IPV4_VLAN_GW, .expected_dev = "veth1",
302 .check_vlan = true,
303 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_VLAN |
304 BPF_FIB_LOOKUP_SKIP_NEIGH,
305 .vlan_proto = ETH_P_8021Q, .vlan_id = VLAN_ID, },
306 { .desc = "IPv4 VLAN input, neighbour resolved on the route",
307 .daddr = IPV4_VLAN_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
308 .expected_dst = IPV4_VLAN_GW, .expected_dev = VLAN_IFACE,
309 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT,
310 .vlan_proto = ETH_P_8021Q, .vlan_id = VLAN_ID, .dmac = DMAC_INIT2, },
311 { .desc = "IPv4 VLAN input, source address from the subinterface",
312 .daddr = IPV4_VLAN_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
313 .expected_src = IPV4_VLAN_IFACE_ADDR,
314 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_SRC |
315 BPF_FIB_LOOKUP_SKIP_NEIGH,
316 .vlan_proto = ETH_P_8021Q, .vlan_id = VLAN_ID, },
317 /*
318 * VRF: the resolved subinterface is enslaved, so the l3mdev rule
319 * (full lookup) and l3mdev_fib_table_rcu() (DIRECT) must select
320 * the VRF table from the resolved ingress
321 */
322 { .desc = "IPv4 VLAN input, VRF subinterface, no flag",
323 .daddr = IPV4_VRF_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
324 .expected_dst = IPV4_GW1,
325 .lookup_flags = BPF_FIB_LOOKUP_SKIP_NEIGH, },
326 { .desc = "IPv4 VLAN input, tag selects VRF table",
327 .daddr = IPV4_VRF_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
328 .expected_dst = IPV4_VRF_GW, .expected_dev = VRF_VLAN_IFACE,
329 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_SKIP_NEIGH,
330 .vlan_proto = ETH_P_8021Q, .vlan_id = VRF_VLAN_ID, },
331 { .desc = "IPv4 VLAN input, DIRECT uses VRF table from resolved ingress",
332 .daddr = IPV4_VRF_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
333 .expected_dst = IPV4_VRF_GW, .expected_dev = VRF_VLAN_IFACE,
334 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_DIRECT |
335 BPF_FIB_LOOKUP_SKIP_NEIGH,
336 .vlan_proto = ETH_P_8021Q, .vlan_id = VRF_VLAN_ID, },
337 /*
338 * failure arms also assert params is left untouched: ifindex still
339 * names the physical device and the input tag bytes survive
340 */
341 { .desc = "IPv4 VLAN input, invalid proto",
342 .daddr = IPV4_VLAN_DST, .expected_ret = -EINVAL,
343 .expected_dev = "veth1", .check_vlan = true,
344 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_SKIP_NEIGH,
345 .vlan_proto = 0x1234, .vlan_id = VLAN_ID, },
346 { .desc = "IPv4 VLAN input, unmatched VID",
347 .daddr = IPV4_VLAN_DST, .expected_ret = BPF_FIB_LKUP_RET_NOT_FWDED,
348 .expected_dev = "veth1", .check_vlan = true,
349 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_SKIP_NEIGH,
350 .vlan_proto = ETH_P_8021Q, .vlan_id = VLAN_VID_UNUSED, },
351 { .desc = "IPv4 VLAN input, subinterface down",
352 .daddr = IPV4_VLAN_DST, .expected_ret = BPF_FIB_LKUP_RET_NOT_FWDED,
353 .expected_dev = "veth1", .check_vlan = true,
354 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_SKIP_NEIGH,
355 .vlan_proto = ETH_P_8021Q, .vlan_id = VLAN_ID_DOWN, },
356 /*
357 * the resolver runs before the forwarding check, so on devices
358 * with forwarding off FWD_DISABLED (not NOT_FWDED) proves the tag
359 * resolved to that device and the lookup used it as ingress
360 */
361 { .desc = "IPv4 VLAN input, 802.1ad tag",
362 .daddr = IPV4_VLAN_DST, .expected_ret = BPF_FIB_LKUP_RET_FWD_DISABLED,
363 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_SKIP_NEIGH,
364 .vlan_proto = ETH_P_8021AD, .vlan_id = QINQ_AD_VLAN_ID, },
365 { .desc = "IPv4 VLAN input, PCP and DEI bits ignored in TCI",
366 .daddr = IPV4_VLAN_DST, .expected_ret = BPF_FIB_LKUP_RET_SUCCESS,
367 .expected_dst = IPV4_VLAN_GW,
368 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_SKIP_NEIGH,
369 .vlan_proto = ETH_P_8021Q, .vlan_id = 0xe000 | VLAN_ID, },
370 { .desc = "IPv4 VLAN input, inner QinQ device from VLAN ifindex",
371 .daddr = IPV4_VLAN_DST, .expected_ret = BPF_FIB_LKUP_RET_FWD_DISABLED,
372 .iif = QINQ_OUTER_IFACE,
373 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_SKIP_NEIGH,
374 .vlan_proto = ETH_P_8021Q, .vlan_id = QINQ_INNER_VLAN_ID, },
375 /*
376 * bonding: the VLANs live on the master, as on receive, where the
377 * frame is steered to the master before VLAN processing; a port
378 * ifindex does not match (ports carry vid state but no VLAN devs)
379 */
380 { .desc = "IPv4 VLAN input, tag on bond master resolves",
381 .daddr = IPV4_VLAN_DST, .expected_ret = BPF_FIB_LKUP_RET_FWD_DISABLED,
382 .iif = BOND_IFACE,
383 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_SKIP_NEIGH,
384 .vlan_proto = ETH_P_8021Q, .vlan_id = BOND_VLAN_ID, },
385 { .desc = "IPv4 VLAN input, tag on bond port does not match",
386 .daddr = IPV4_VLAN_DST, .expected_ret = BPF_FIB_LKUP_RET_NOT_FWDED,
387 .iif = BOND_PORT, .expected_dev = BOND_PORT, .check_vlan = true,
388 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_SKIP_NEIGH,
389 .vlan_proto = ETH_P_8021Q, .vlan_id = BOND_VLAN_ID, },
390 { .desc = "IPv6 VLAN input, invalid proto",
391 .daddr = IPV6_VLAN_DST, .expected_ret = -EINVAL,
392 .expected_dev = "veth1", .check_vlan = true,
393 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_SKIP_NEIGH,
394 .vlan_proto = 0x1234, .vlan_id = VLAN_ID, },
395 { .desc = "IPv4 VLAN input, VID 0 priority tag fails closed",
396 .daddr = IPV4_VLAN_DST, .expected_ret = BPF_FIB_LKUP_RET_NOT_FWDED,
397 .expected_dev = "veth1", .check_vlan = true,
398 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_SKIP_NEIGH,
399 .vlan_proto = ETH_P_8021Q, .vlan_id = 0, },
400 { .desc = "IPv6 VLAN input, unmatched VID",
401 .daddr = IPV6_VLAN_DST, .expected_ret = BPF_FIB_LKUP_RET_NOT_FWDED,
402 .expected_dev = "veth1", .check_vlan = true,
403 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_SKIP_NEIGH,
404 .vlan_proto = ETH_P_8021Q, .vlan_id = VLAN_VID_UNUSED, },
405 { .desc = "unknown flag bit rejected",
406 .daddr = IPV4_VLAN_DST, .expected_ret = -EINVAL,
407 .lookup_flags = (1 << 14) | BPF_FIB_LOOKUP_SKIP_NEIGH, },
408 { .desc = "IPv4 VLAN input rejected with TBID",
409 .daddr = IPV4_VLAN_DST, .expected_ret = -EINVAL,
410 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_TBID,
411 .vlan_proto = ETH_P_8021Q, .vlan_id = VLAN_ID, },
412 { .desc = "IPv4 VLAN input rejected with OUTPUT",
413 .daddr = IPV4_VLAN_DST, .expected_ret = -EINVAL,
414 .lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT | BPF_FIB_LOOKUP_OUTPUT,
415 .vlan_proto = ETH_P_8021Q, .vlan_id = VLAN_ID, },
416 };
417
setup_netns(void)418 static int setup_netns(void)
419 {
420 int err;
421
422 /*
423 * a new netns copies the IPv4 conf from init_net, so on a host with
424 * forwarding enabled the arms that expect FWD_DISABLED would see the
425 * lookup succeed instead; pin it off here and enable it per device
426 */
427 err = write_sysctl("/proc/sys/net/ipv4/conf/all/forwarding", "0");
428 if (!ASSERT_OK(err, "write_sysctl(net.ipv4.conf.all.forwarding)"))
429 goto fail;
430
431 err = write_sysctl("/proc/sys/net/ipv4/conf/default/forwarding", "0");
432 if (!ASSERT_OK(err, "write_sysctl(net.ipv4.conf.default.forwarding)"))
433 goto fail;
434
435 SYS(fail, "ip link add veth1 type veth peer name veth2");
436 SYS(fail, "ip link set dev veth1 up");
437 SYS(fail, "ip link set dev veth2 up");
438
439 err = write_sysctl("/proc/sys/net/ipv4/neigh/veth1/gc_stale_time", "900");
440 if (!ASSERT_OK(err, "write_sysctl(net.ipv4.neigh.veth1.gc_stale_time)"))
441 goto fail;
442
443 err = write_sysctl("/proc/sys/net/ipv6/neigh/veth1/gc_stale_time", "900");
444 if (!ASSERT_OK(err, "write_sysctl(net.ipv6.neigh.veth1.gc_stale_time)"))
445 goto fail;
446
447 SYS(fail, "ip addr add %s/64 dev veth1 nodad", IPV6_IFACE_ADDR);
448 SYS(fail, "ip neigh add %s dev veth1 nud failed", IPV6_NUD_FAILED_ADDR);
449 SYS(fail, "ip neigh add %s dev veth1 lladdr %s nud stale", IPV6_NUD_STALE_ADDR, DMAC);
450
451 SYS(fail, "ip addr add %s/24 dev veth1", IPV4_IFACE_ADDR);
452 SYS(fail, "ip neigh add %s dev veth1 nud failed", IPV4_NUD_FAILED_ADDR);
453 SYS(fail, "ip neigh add %s dev veth1 lladdr %s nud stale", IPV4_NUD_STALE_ADDR, DMAC);
454
455 /* Setup for prefsrc IP addr selection */
456 SYS(fail, "ip addr add %s/24 dev veth1", IPV4_IFACE_ADDR_SEC);
457 SYS(fail, "ip route add %s/32 dev veth1 src %s", IPV4_ADDR_DST, IPV4_IFACE_ADDR_SEC);
458
459 SYS(fail, "ip addr add %s/64 dev veth1 nodad", IPV6_IFACE_ADDR_SEC);
460 SYS(fail, "ip route add %s/128 dev veth1 src %s", IPV6_ADDR_DST, IPV6_IFACE_ADDR_SEC);
461
462 /* Setup for tbid lookup tests */
463 SYS(fail, "ip addr add %s/24 dev veth2", IPV4_TBID_ADDR);
464 SYS(fail, "ip route del %s/24 dev veth2", IPV4_TBID_NET);
465 SYS(fail, "ip route add table 100 %s/24 dev veth2", IPV4_TBID_NET);
466 SYS(fail, "ip neigh add %s dev veth2 lladdr %s nud stale", IPV4_TBID_DST, DMAC2);
467
468 SYS(fail, "ip addr add %s/64 dev veth2", IPV6_TBID_ADDR);
469 SYS(fail, "ip -6 route del %s/64 dev veth2", IPV6_TBID_NET);
470 SYS(fail, "ip -6 route add table 100 %s/64 dev veth2", IPV6_TBID_NET);
471 SYS(fail, "ip neigh add %s dev veth2 lladdr %s nud stale", IPV6_TBID_DST, DMAC2);
472
473 err = write_sysctl("/proc/sys/net/ipv4/conf/veth1/forwarding", "1");
474 if (!ASSERT_OK(err, "write_sysctl(net.ipv4.conf.veth1.forwarding)"))
475 goto fail;
476
477 err = write_sysctl("/proc/sys/net/ipv6/conf/veth1/forwarding", "1");
478 if (!ASSERT_OK(err, "write_sysctl(net.ipv6.conf.veth1.forwarding)"))
479 goto fail;
480
481 /* Setup for policy routing tests */
482 SYS(fail, "ip addr add %s/24 dev veth1", IPV4_LOCAL);
483 SYS(fail, "ip addr add %s/64 dev veth1 nodad", IPV6_LOCAL);
484 SYS(fail, "ip route add %s/32 via %s", IPV4_REMOTE_DST, IPV4_GW1);
485 SYS(fail, "ip route add %s/32 via %s table %s", IPV4_REMOTE_DST, IPV4_GW2, MARK_TABLE);
486 SYS(fail, "ip -6 route add %s/128 via %s", IPV6_REMOTE_DST, IPV6_GW1);
487 SYS(fail, "ip -6 route add %s/128 via %s table %s", IPV6_REMOTE_DST, IPV6_GW2, MARK_TABLE);
488 SYS(fail, "ip rule add prio 2 fwmark %d lookup %s", MARK, MARK_TABLE);
489 SYS(fail, "ip -6 rule add prio 2 fwmark %d lookup %s", MARK, MARK_TABLE);
490
491 /*
492 * Setup for vlan tests: a subinterface for egress resolution and
493 * tag-as-input, a QinQ stack, and an iif rule so the input tests
494 * observe which device the lookup used as ingress.
495 */
496 SYS(fail, "ip link add link veth1 name %s type vlan id %d",
497 VLAN_IFACE, VLAN_ID);
498 SYS(fail, "ip link set dev %s up", VLAN_IFACE);
499 /*
500 * lower than the veth1 parent (1500): the skb-path mtu check uses the
501 * FIB result (VLAN) device, so mtu_result is this value, which the
502 * no-flag arm below pins
503 */
504 SYS(fail, "ip link set dev %s mtu 1400", VLAN_IFACE);
505 SYS(fail, "ip addr add %s/24 dev %s", IPV4_VLAN_IFACE_ADDR, VLAN_IFACE);
506 SYS(fail, "ip addr add %s/64 dev %s nodad", IPV6_VLAN_IFACE_ADDR, VLAN_IFACE);
507
508 /*
509 * stays down: the input flag must treat its tag the way real
510 * ingress treats a frame arriving on a down VLAN device (drop)
511 */
512 SYS(fail, "ip link add link veth1 name %s type vlan id %d",
513 VLAN_IFACE_DOWN, VLAN_ID_DOWN);
514
515 err = write_sysctl("/proc/sys/net/ipv4/conf/" VLAN_IFACE "/forwarding", "1");
516 if (!ASSERT_OK(err, "write_sysctl(net.ipv4.conf." VLAN_IFACE ".forwarding)"))
517 goto fail;
518
519 err = write_sysctl("/proc/sys/net/ipv6/conf/" VLAN_IFACE "/forwarding", "1");
520 if (!ASSERT_OK(err, "write_sysctl(net.ipv6.conf." VLAN_IFACE ".forwarding)"))
521 goto fail;
522
523 SYS(fail, "ip link add link veth1 name %s type vlan proto 802.1ad id 200",
524 QINQ_OUTER_IFACE);
525 SYS(fail, "ip link add link %s name %s type vlan id 300",
526 QINQ_OUTER_IFACE, QINQ_INNER_IFACE);
527 SYS(fail, "ip link set dev %s up", QINQ_OUTER_IFACE);
528 SYS(fail, "ip link set dev %s up", QINQ_INNER_IFACE);
529 SYS(fail, "ip route add %s/32 dev %s", IPV4_QINQ_DST, QINQ_INNER_IFACE);
530
531 SYS(fail, "ip route add %s/32 via %s", IPV4_VLAN_DST, IPV4_GW1);
532 SYS(fail, "ip route add table %s %s/32 via %s",
533 VLAN_TABLE, IPV4_VLAN_DST, IPV4_VLAN_GW);
534 SYS(fail, "ip rule add prio 3 iif %s lookup %s", VLAN_IFACE, VLAN_TABLE);
535 SYS(fail, "ip -6 route add %s/128 via %s", IPV6_VLAN_DST, IPV6_GW1);
536 SYS(fail, "ip -6 route add table %s %s/128 via %s",
537 VLAN_TABLE, IPV6_VLAN_DST, IPV6_VLAN_GW);
538 SYS(fail, "ip -6 rule add prio 3 iif %s lookup %s", VLAN_IFACE, VLAN_TABLE);
539
540 /* a bond with one port and a VLAN on the bond */
541 SYS(fail, "ip link add %s type bond", BOND_IFACE);
542 SYS(fail, "ip link add %s type veth peer name %s", BOND_PORT, BOND_PORT_PEER);
543 SYS(fail, "ip link set %s master %s", BOND_PORT, BOND_IFACE);
544 SYS(fail, "ip link set dev %s up", BOND_IFACE);
545 SYS(fail, "ip link set dev %s up", BOND_PORT);
546 SYS(fail, "ip link add link %s name %s.%d type vlan id %d",
547 BOND_IFACE, BOND_IFACE, BOND_VLAN_ID, BOND_VLAN_ID);
548 SYS(fail, "ip link set dev %s.%d up", BOND_IFACE, BOND_VLAN_ID);
549 SYS(fail, "ip route add %s/32 dev %s.%d",
550 IPV4_BOND_VLAN_DST, BOND_IFACE, BOND_VLAN_ID);
551
552 /*
553 * a VRF with its own dedicated subinterface (the iif rules above
554 * must not see it), for the table-selection-by-ingress cases
555 */
556 SYS(fail, "ip link add %s type vrf table %s", VRF_IFACE, VRF_TABLE);
557 SYS(fail, "ip link set dev %s up", VRF_IFACE);
558 SYS(fail, "ip link add link veth1 name %s type vlan id %d",
559 VRF_VLAN_IFACE, VRF_VLAN_ID);
560 SYS(fail, "ip link set %s master %s", VRF_VLAN_IFACE, VRF_IFACE);
561 SYS(fail, "ip link set dev %s up", VRF_VLAN_IFACE);
562 SYS(fail, "ip addr add %s/24 dev %s", IPV4_VRF_IFACE_ADDR, VRF_VLAN_IFACE);
563 err = write_sysctl("/proc/sys/net/ipv4/conf/" VRF_VLAN_IFACE "/forwarding", "1");
564 if (!ASSERT_OK(err, "write_sysctl(net.ipv4.conf." VRF_VLAN_IFACE ".forwarding)"))
565 goto fail;
566 SYS(fail, "ip route add %s/32 via %s", IPV4_VRF_DST, IPV4_GW1);
567 SYS(fail, "ip route add table %s %s/32 via %s",
568 VRF_TABLE, IPV4_VRF_DST, IPV4_VRF_GW);
569
570 /* neighbours on the VLAN subinterface for the non-SKIP_NEIGH cases */
571 err = write_sysctl("/proc/sys/net/ipv4/neigh/" VLAN_IFACE "/gc_stale_time", "900");
572 if (!ASSERT_OK(err, "write_sysctl(net.ipv4.neigh." VLAN_IFACE ".gc_stale_time)"))
573 goto fail;
574 SYS(fail, "ip neigh add %s dev %s lladdr %s nud stale",
575 IPV4_VLAN_EGRESS_DST, VLAN_IFACE, DMAC);
576 SYS(fail, "ip neigh add %s dev %s lladdr %s nud stale",
577 IPV4_VLAN_GW, VLAN_IFACE, DMAC2);
578
579 /* a VLAN on veth2 with a route in the tbid test table */
580 SYS(fail, "ip link add link veth2 name %s type vlan id %d",
581 TBID_VLAN_IFACE, TBID_VLAN_ID);
582 SYS(fail, "ip link set dev %s up", TBID_VLAN_IFACE);
583 SYS(fail, "ip route add table 100 %s/32 dev %s",
584 IPV4_TBID_VLAN_DST, TBID_VLAN_IFACE);
585
586 /* a locked-mtu route via the subinterface for the FRAG_NEEDED case */
587 SYS(fail, "ip route add %s/32 dev %s mtu lock 1000",
588 IPV4_VLAN_MTU_DST, VLAN_IFACE);
589
590 return 0;
591 fail:
592 return -1;
593 }
594
set_lookup_params(struct bpf_fib_lookup * params,const struct fib_lookup_test * test,int ifindex)595 static int set_lookup_params(struct bpf_fib_lookup *params,
596 const struct fib_lookup_test *test,
597 int ifindex)
598 {
599 int ret;
600
601 memset(params, 0, sizeof(*params));
602
603 params->l4_protocol = IPPROTO_TCP;
604 params->ifindex = test->iif ? if_nametoindex(test->iif) : ifindex;
605 params->tbid = test->tbid;
606 params->mark = test->mark;
607 params->tot_len = test->tot_len;
608
609 /* h_vlan_proto/h_vlan_TCI union with tbid */
610 if (test->lookup_flags & BPF_FIB_LOOKUP_VLAN_INPUT) {
611 params->h_vlan_proto = htons(test->vlan_proto);
612 params->h_vlan_TCI = htons(test->vlan_id);
613 }
614
615 if (inet_pton(AF_INET6, test->daddr, params->ipv6_dst) == 1) {
616 params->family = AF_INET6;
617 if (!(test->lookup_flags & BPF_FIB_LOOKUP_SRC)) {
618 ret = inet_pton(AF_INET6, IPV6_IFACE_ADDR, params->ipv6_src);
619 if (!ASSERT_EQ(ret, 1, "inet_pton(IPV6_IFACE_ADDR)"))
620 return -1;
621 }
622
623 return 0;
624 }
625
626 ret = inet_pton(AF_INET, test->daddr, ¶ms->ipv4_dst);
627 if (!ASSERT_EQ(ret, 1, "convert IP[46] address"))
628 return -1;
629 params->family = AF_INET;
630
631 if (!(test->lookup_flags & BPF_FIB_LOOKUP_SRC)) {
632 ret = inet_pton(AF_INET, IPV4_IFACE_ADDR, ¶ms->ipv4_src);
633 if (!ASSERT_EQ(ret, 1, "inet_pton(IPV4_IFACE_ADDR)"))
634 return -1;
635 }
636
637 return 0;
638 }
639
mac_str(char * b,const __u8 * mac)640 static void mac_str(char *b, const __u8 *mac)
641 {
642 sprintf(b, "%02X:%02X:%02X:%02X:%02X:%02X",
643 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
644 }
645
assert_ip_address(int family,void * addr,const char * expected_str)646 static void assert_ip_address(int family, void *addr, const char *expected_str)
647 {
648 char str[INET6_ADDRSTRLEN];
649 u8 expected_addr[16];
650 int addr_len = 0;
651 int ret;
652
653 switch (family) {
654 case AF_INET6:
655 ret = inet_pton(AF_INET6, expected_str, expected_addr);
656 ASSERT_EQ(ret, 1, "inet_pton(AF_INET6, expected_str)");
657 addr_len = 16;
658 break;
659 case AF_INET:
660 ret = inet_pton(AF_INET, expected_str, expected_addr);
661 ASSERT_EQ(ret, 1, "inet_pton(AF_INET, expected_str)");
662 addr_len = 4;
663 break;
664 default:
665 PRINT_FAIL("invalid address family: %d", family);
666 break;
667 }
668
669 if (memcmp(addr, expected_addr, addr_len)) {
670 inet_ntop(family, addr, str, sizeof(str));
671 PRINT_FAIL("expected %s actual %s ", expected_str, str);
672 }
673 }
674
assert_src_ip(struct bpf_fib_lookup * params,const char * expected)675 static void assert_src_ip(struct bpf_fib_lookup *params, const char *expected)
676 {
677 assert_ip_address(params->family, params->ipv6_src, expected);
678 }
679
assert_dst_ip(struct bpf_fib_lookup * params,const char * expected)680 static void assert_dst_ip(struct bpf_fib_lookup *params, const char *expected)
681 {
682 assert_ip_address(params->family, params->ipv6_dst, expected);
683 }
684
test_fib_lookup(void)685 void test_fib_lookup(void)
686 {
687 struct bpf_fib_lookup *fib_params;
688 struct nstoken *nstoken = NULL;
689 struct __sk_buff skb = { };
690 struct fib_lookup *skel;
691 int prog_fd, xdp_fd, err, ret, i;
692
693 /* The test does not use the skb->data, so
694 * use pkt_v6 for both v6 and v4 test.
695 */
696 LIBBPF_OPTS(bpf_test_run_opts, run_opts,
697 .data_in = &pkt_v6,
698 .data_size_in = sizeof(pkt_v6),
699 .ctx_in = &skb,
700 .ctx_size_in = sizeof(skb),
701 );
702 LIBBPF_OPTS(bpf_test_run_opts, xdp_opts,
703 .data_in = &pkt_v6,
704 .data_size_in = sizeof(pkt_v6),
705 );
706
707 skel = fib_lookup__open_and_load();
708 if (!ASSERT_OK_PTR(skel, "skel open_and_load"))
709 return;
710 prog_fd = bpf_program__fd(skel->progs.fib_lookup);
711 xdp_fd = bpf_program__fd(skel->progs.fib_lookup_xdp);
712
713 SYS(fail, "ip netns add %s", NS_TEST);
714
715 nstoken = open_netns(NS_TEST);
716 if (!ASSERT_OK_PTR(nstoken, "open_netns"))
717 goto fail;
718
719 if (setup_netns())
720 goto fail;
721
722 skb.ifindex = if_nametoindex("veth1");
723 if (!ASSERT_NEQ(skb.ifindex, 0, "if_nametoindex(veth1)"))
724 goto fail;
725
726 fib_params = &skel->bss->fib_params;
727
728 for (i = 0; i < ARRAY_SIZE(tests); i++) {
729 printf("Testing %s ", tests[i].desc);
730
731 if (set_lookup_params(fib_params, &tests[i], skb.ifindex))
732 continue;
733
734 skel->bss->fib_lookup_ret = -1;
735 skel->bss->lookup_flags = tests[i].lookup_flags;
736
737 err = bpf_prog_test_run_opts(prog_fd, &run_opts);
738 if (!ASSERT_OK(err, "bpf_prog_test_run_opts"))
739 continue;
740
741 /*
742 * BPF_FIB_LOOKUP_VLAN is XDP-only; the tc helper rejects it.
743 * These cases are exercised on the XDP path below.
744 */
745 if (tests[i].lookup_flags & BPF_FIB_LOOKUP_VLAN) {
746 ASSERT_EQ(skel->bss->fib_lookup_ret, -EINVAL,
747 "tc rejects BPF_FIB_LOOKUP_VLAN");
748 continue;
749 }
750
751 ASSERT_EQ(skel->bss->fib_lookup_ret, tests[i].expected_ret,
752 "fib_lookup_ret");
753
754 if (tests[i].expected_src)
755 assert_src_ip(fib_params, tests[i].expected_src);
756
757 if (tests[i].expected_dst)
758 assert_dst_ip(fib_params, tests[i].expected_dst);
759
760 if (tests[i].expected_dev)
761 ASSERT_EQ(fib_params->ifindex,
762 if_nametoindex(tests[i].expected_dev), "ifindex");
763
764 if (tests[i].expected_mtu)
765 ASSERT_EQ(fib_params->mtu_result, tests[i].expected_mtu,
766 "mtu_result");
767
768 if (tests[i].check_vlan) {
769 ASSERT_EQ(fib_params->h_vlan_proto,
770 htons(tests[i].vlan_proto), "h_vlan_proto");
771 ASSERT_EQ(fib_params->h_vlan_TCI,
772 htons(tests[i].vlan_id), "h_vlan_TCI");
773 }
774
775 ret = memcmp(tests[i].dmac, fib_params->dmac, sizeof(tests[i].dmac));
776 if (!ASSERT_EQ(ret, 0, "dmac not match")) {
777 char expected[18], actual[18];
778
779 mac_str(expected, tests[i].dmac);
780 mac_str(actual, fib_params->dmac);
781 printf("dmac expected %s actual %s ", expected, actual);
782 }
783
784 /*
785 * ensure tbid is zero'd out after fib lookup. With
786 * BPF_FIB_LOOKUP_VLAN the union holds the packed vlan
787 * fields instead, so skip the check for those.
788 */
789 if ((tests[i].lookup_flags & BPF_FIB_LOOKUP_DIRECT) &&
790 !(tests[i].lookup_flags & BPF_FIB_LOOKUP_VLAN)) {
791 if (!ASSERT_EQ(skel->bss->fib_params.tbid, 0,
792 "expected fib_params.tbid to be zero"))
793 goto fail;
794 }
795 }
796
797 /*
798 * Re-run the cases through bpf_xdp_fib_lookup(). test_run uses the
799 * current netns' loopback for ctx->rxq->dev, so dev_net() is NS_TEST
800 * and the lookup runs against its FIB. The path-independent results
801 * (return code, swapped ifindex, vlan tag, gateway) must match the skb
802 * path; the no-tot_len mtu_result is skb-specific and not rechecked.
803 */
804 for (i = 0; i < ARRAY_SIZE(tests); i++) {
805 if (set_lookup_params(fib_params, &tests[i], skb.ifindex))
806 continue;
807
808 skel->bss->fib_lookup_ret = -1;
809 skel->bss->lookup_flags = tests[i].lookup_flags;
810
811 err = bpf_prog_test_run_opts(xdp_fd, &xdp_opts);
812 if (!ASSERT_OK(err, "xdp test_run"))
813 continue;
814
815 if (!ASSERT_EQ(skel->bss->fib_lookup_ret, tests[i].expected_ret,
816 "xdp fib_lookup_ret"))
817 printf("(xdp) %s\n", tests[i].desc);
818
819 if (tests[i].expected_dev)
820 ASSERT_EQ(fib_params->ifindex,
821 if_nametoindex(tests[i].expected_dev),
822 "xdp ifindex");
823
824 if (tests[i].expected_dst)
825 assert_dst_ip(fib_params, tests[i].expected_dst);
826
827 if (tests[i].check_vlan) {
828 ASSERT_EQ(fib_params->h_vlan_proto,
829 htons(tests[i].vlan_proto), "xdp h_vlan_proto");
830 ASSERT_EQ(fib_params->h_vlan_TCI,
831 htons(tests[i].vlan_id), "xdp h_vlan_TCI");
832 }
833
834 ret = memcmp(tests[i].dmac, fib_params->dmac, sizeof(tests[i].dmac));
835 ASSERT_EQ(ret, 0, "xdp dmac");
836
837 /*
838 * mtu_result from a tot_len lookup is the route mtu and is
839 * path-independent; the no-tot_len arm reads dev->mtu and is
840 * skb-only, so gate on tot_len
841 */
842 if (tests[i].expected_mtu && tests[i].tot_len)
843 ASSERT_EQ(fib_params->mtu_result, tests[i].expected_mtu,
844 "xdp mtu_result");
845 }
846
847 fail:
848 if (nstoken)
849 close_netns(nstoken);
850 SYS_NOFAIL("ip netns del " NS_TEST);
851 fib_lookup__destroy(skel);
852 }
853
854 #define NS_VLAN_A "fib_lookup_vlan_ns_a"
855 #define NS_VLAN_B "fib_lookup_vlan_ns_b"
856 #define IPV4_VLAN_NETNS_ADDR "10.66.0.1"
857 #define IPV4_VLAN_NETNS_DST "10.66.0.2"
858
859 /*
860 * A VLAN device can be moved to another netns while staying registered
861 * on its parent. Neither direction may then cross the boundary: the
862 * egress flag must not publish the foreign parent's ifindex, and the
863 * input flag must fail closed rather than use a foreign ingress.
864 */
test_fib_lookup_vlan_netns(void)865 void test_fib_lookup_vlan_netns(void)
866 {
867 struct bpf_fib_lookup *fib_params;
868 struct nstoken *nstoken = NULL;
869 struct __sk_buff skb = { };
870 struct fib_lookup *skel = NULL;
871 int prog_fd, xdp_fd, err, parent_idx, vlan_idx;
872
873 LIBBPF_OPTS(bpf_test_run_opts, run_opts,
874 .data_in = &pkt_v6,
875 .data_size_in = sizeof(pkt_v6),
876 .ctx_in = &skb,
877 .ctx_size_in = sizeof(skb),
878 );
879 LIBBPF_OPTS(bpf_test_run_opts, xdp_opts,
880 .data_in = &pkt_v6,
881 .data_size_in = sizeof(pkt_v6),
882 );
883
884 skel = fib_lookup__open_and_load();
885 if (!ASSERT_OK_PTR(skel, "skel open_and_load"))
886 return;
887 prog_fd = bpf_program__fd(skel->progs.fib_lookup);
888 xdp_fd = bpf_program__fd(skel->progs.fib_lookup_xdp);
889 fib_params = &skel->bss->fib_params;
890
891 SYS(fail, "ip netns add %s", NS_VLAN_A);
892 SYS(fail, "ip netns add %s", NS_VLAN_B);
893
894 nstoken = open_netns(NS_VLAN_A);
895 if (!ASSERT_OK_PTR(nstoken, "open_netns(a)"))
896 goto fail;
897
898 SYS(fail, "ip link add veth7 type veth peer name veth8");
899 SYS(fail, "ip link set dev veth7 up");
900 SYS(fail, "ip link add link veth7 name veth7.66 type vlan id 66");
901 SYS(fail, "ip link set veth7.66 netns %s", NS_VLAN_B);
902 /*
903 * up it in B before the input lookup: the move closed it, and a
904 * down device fails the resolver on IFF_UP before reaching the
905 * netns check this subtest exists to pin
906 */
907 SYS(fail, "ip -n %s link set dev veth7.66 up", NS_VLAN_B);
908
909 parent_idx = if_nametoindex("veth7");
910 if (!ASSERT_NEQ(parent_idx, 0, "if_nametoindex(veth7)"))
911 goto fail;
912
913 /*
914 * give this netns a route to the destination: the lookup below runs
915 * against this FIB, so without the route a kernel that resolved the
916 * moved device anyway would still return NOT_FWDED and the arm would
917 * pass for the wrong reason
918 */
919 SYS(fail, "ip route add %s/32 dev veth7", IPV4_VLAN_NETNS_DST);
920
921 /*
922 * input: the moved device is still in veth7's VLAN group, but it
923 * lives in another netns, so the lookup must fail closed
924 */
925 skb.ifindex = parent_idx;
926 memset(fib_params, 0, sizeof(*fib_params));
927 fib_params->family = AF_INET;
928 fib_params->l4_protocol = IPPROTO_TCP;
929 fib_params->ifindex = parent_idx;
930 fib_params->h_vlan_proto = htons(ETH_P_8021Q);
931 fib_params->h_vlan_TCI = htons(66);
932 if (!ASSERT_EQ(inet_pton(AF_INET, IPV4_VLAN_NETNS_DST, &fib_params->ipv4_dst),
933 1, "inet_pton(dst)"))
934 goto fail;
935
936 skel->bss->fib_lookup_ret = -1;
937 skel->bss->lookup_flags = BPF_FIB_LOOKUP_VLAN_INPUT |
938 BPF_FIB_LOOKUP_SKIP_NEIGH;
939 err = bpf_prog_test_run_opts(prog_fd, &run_opts);
940 if (!ASSERT_OK(err, "test_run(input)"))
941 goto fail;
942 ASSERT_EQ(skel->bss->fib_lookup_ret, BPF_FIB_LKUP_RET_NOT_FWDED,
943 "input across netns fails closed");
944 ASSERT_EQ(fib_params->ifindex, parent_idx, "ifindex untouched");
945 ASSERT_EQ(fib_params->h_vlan_TCI, htons(66), "tag untouched");
946
947 close_netns(nstoken);
948 nstoken = open_netns(NS_VLAN_B);
949 if (!ASSERT_OK_PTR(nstoken, "open_netns(b)"))
950 goto fail;
951
952 /*
953 * egress: the fib result is the VLAN device here, but its parent
954 * is in the other netns, so the swap must not happen
955 */
956 SYS(fail, "ip addr add %s/24 dev veth7.66", IPV4_VLAN_NETNS_ADDR);
957 err = write_sysctl("/proc/sys/net/ipv4/conf/veth7.66/forwarding", "1");
958 if (!ASSERT_OK(err, "write_sysctl(forwarding)"))
959 goto fail;
960
961 vlan_idx = if_nametoindex("veth7.66");
962 if (!ASSERT_NEQ(vlan_idx, 0, "if_nametoindex(veth7.66)"))
963 goto fail;
964
965 memset(fib_params, 0, sizeof(*fib_params));
966 fib_params->family = AF_INET;
967 fib_params->l4_protocol = IPPROTO_TCP;
968 fib_params->ifindex = vlan_idx;
969 if (!ASSERT_EQ(inet_pton(AF_INET, IPV4_VLAN_NETNS_DST, &fib_params->ipv4_dst),
970 1, "inet_pton(dst)") ||
971 !ASSERT_EQ(inet_pton(AF_INET, IPV4_VLAN_NETNS_ADDR, &fib_params->ipv4_src),
972 1, "inet_pton(src)"))
973 goto fail;
974
975 skel->bss->fib_lookup_ret = -1;
976 skel->bss->lookup_flags = BPF_FIB_LOOKUP_VLAN |
977 BPF_FIB_LOOKUP_SKIP_NEIGH;
978 err = bpf_prog_test_run_opts(xdp_fd, &xdp_opts);
979 if (!ASSERT_OK(err, "test_run(egress)"))
980 goto fail;
981 ASSERT_EQ(skel->bss->fib_lookup_ret, BPF_FIB_LKUP_RET_VLAN_FAILURE,
982 "egress returns VLAN_FAILURE");
983 ASSERT_EQ(fib_params->ifindex, vlan_idx,
984 "foreign parent not published");
985 ASSERT_EQ(fib_params->h_vlan_TCI, 0, "vlan fields zero");
986
987 fail:
988 if (nstoken)
989 close_netns(nstoken);
990 SYS_NOFAIL("ip netns del " NS_VLAN_A);
991 SYS_NOFAIL("ip netns del " NS_VLAN_B);
992 fib_lookup__destroy(skel);
993 }
994
995 #define REDIRECT_NPKTS 1000
996 #define NS_REDIRECT "fib_lookup_redirect_ns"
997
998 /*
999 * The egress flag exists so an XDP program can redirect to the physical
1000 * parent. A redirect that lands on a VLAN device is dropped at
1001 * xdp_do_flush(), because a VLAN device has no ndo_xdp_xmit. Drive real
1002 * frames with BPF_F_TEST_XDP_LIVE_FRAMES, which runs the native
1003 * xdp_do_redirect() + xdp_do_flush() path: a reducible VLAN egress
1004 * resolves to veth1 and is delivered to its peer veth2, while a QinQ
1005 * egress returns VLAN_FAILURE and is passed to the stack instead of
1006 * redirected to a device that would silently drop it.
1007 */
test_fib_lookup_vlan_redirect(void)1008 void test_fib_lookup_vlan_redirect(void)
1009 {
1010 int redirect_fd, err, veth1_idx, veth2_idx = -1;
1011 struct bpf_fib_lookup *fib_params;
1012 struct nstoken *nstoken = NULL;
1013 struct fib_lookup *skel = NULL;
1014 bool xdp_attached = false;
1015
1016 LIBBPF_OPTS(bpf_test_run_opts, lf_opts,
1017 .data_in = &pkt_v4,
1018 .data_size_in = sizeof(pkt_v4),
1019 .flags = BPF_F_TEST_XDP_LIVE_FRAMES,
1020 .repeat = REDIRECT_NPKTS,
1021 );
1022
1023 skel = fib_lookup__open_and_load();
1024 if (!ASSERT_OK_PTR(skel, "skel open_and_load"))
1025 return;
1026 redirect_fd = bpf_program__fd(skel->progs.fib_lookup_redirect);
1027 fib_params = &skel->bss->fib_params;
1028
1029 SYS(fail, "ip netns add %s", NS_REDIRECT);
1030 nstoken = open_netns(NS_REDIRECT);
1031 if (!ASSERT_OK_PTR(nstoken, "open_netns"))
1032 goto fail;
1033 if (setup_netns())
1034 goto fail;
1035
1036 veth1_idx = if_nametoindex("veth1");
1037 veth2_idx = if_nametoindex("veth2");
1038 if (!ASSERT_NEQ(veth1_idx, 0, "if_nametoindex(veth1)") ||
1039 !ASSERT_NEQ(veth2_idx, 0, "if_nametoindex(veth2)"))
1040 goto fail;
1041
1042 /*
1043 * A redirect to veth1 is delivered to its peer veth2. veth_xdp_xmit()
1044 * only accepts the frame if veth2's NAPI is up, which on veth means
1045 * veth2 carries an XDP program; xdp_count tallies what arrives.
1046 */
1047 err = bpf_xdp_attach(veth2_idx, bpf_program__fd(skel->progs.xdp_count),
1048 XDP_FLAGS_DRV_MODE, NULL);
1049 if (!ASSERT_OK(err, "attach xdp_count on veth2"))
1050 goto fail;
1051 xdp_attached = true;
1052
1053 /* reducible VLAN egress: resolves to the physical parent veth1 */
1054 memset(fib_params, 0, sizeof(*fib_params));
1055 fib_params->family = AF_INET;
1056 fib_params->l4_protocol = IPPROTO_TCP;
1057 fib_params->ifindex = veth1_idx;
1058 if (!ASSERT_EQ(inet_pton(AF_INET, IPV4_IFACE_ADDR, &fib_params->ipv4_src),
1059 1, "inet_pton(src)") ||
1060 !ASSERT_EQ(inet_pton(AF_INET, IPV4_VLAN_EGRESS_DST, &fib_params->ipv4_dst),
1061 1, "inet_pton(reducible dst)"))
1062 goto fail;
1063 skel->bss->lookup_flags = BPF_FIB_LOOKUP_VLAN | BPF_FIB_LOOKUP_SKIP_NEIGH;
1064 skel->bss->redirected = 0;
1065 skel->bss->passed = 0;
1066 skel->bss->delivered = 0;
1067
1068 err = bpf_prog_test_run_opts(redirect_fd, &lf_opts);
1069 if (!ASSERT_OK(err, "test_run(reducible egress)"))
1070 goto fail;
1071 ASSERT_EQ(skel->bss->redirected, REDIRECT_NPKTS, "reducible egress redirected");
1072 ASSERT_EQ(skel->bss->passed, 0, "reducible egress not passed");
1073 ASSERT_GT(skel->bss->delivered, 0, "reducible egress delivered to veth2");
1074
1075 /*
1076 * QinQ egress: not reducible, so the lookup returns VLAN_FAILURE and
1077 * the program passes the frame instead of redirecting to the inner
1078 * VLAN device. redirected == 0 is the assertion that matters: the
1079 * program did not redirect to a device that would drop the frame at
1080 * xdp_do_flush(). veth2's delivered count is not checked here, since
1081 * a passed frame can still reach veth2 through the stack's forwarding
1082 * path, which is unrelated to the redirect under test.
1083 */
1084 memset(fib_params, 0, sizeof(*fib_params));
1085 fib_params->family = AF_INET;
1086 fib_params->l4_protocol = IPPROTO_TCP;
1087 fib_params->ifindex = veth1_idx;
1088 if (!ASSERT_EQ(inet_pton(AF_INET, IPV4_IFACE_ADDR, &fib_params->ipv4_src),
1089 1, "inet_pton(src)") ||
1090 !ASSERT_EQ(inet_pton(AF_INET, IPV4_QINQ_DST, &fib_params->ipv4_dst),
1091 1, "inet_pton(qinq dst)"))
1092 goto fail;
1093 skel->bss->lookup_flags = BPF_FIB_LOOKUP_VLAN | BPF_FIB_LOOKUP_SKIP_NEIGH;
1094 skel->bss->redirected = 0;
1095 skel->bss->passed = 0;
1096
1097 err = bpf_prog_test_run_opts(redirect_fd, &lf_opts);
1098 if (!ASSERT_OK(err, "test_run(qinq egress)"))
1099 goto fail;
1100 ASSERT_EQ(skel->bss->passed, REDIRECT_NPKTS, "qinq egress passed");
1101 ASSERT_EQ(skel->bss->redirected, 0, "qinq egress not redirected");
1102
1103 fail:
1104 if (xdp_attached)
1105 bpf_xdp_detach(veth2_idx, XDP_FLAGS_DRV_MODE, NULL);
1106 if (nstoken)
1107 close_netns(nstoken);
1108 SYS_NOFAIL("ip netns del " NS_REDIRECT);
1109 fib_lookup__destroy(skel);
1110 }
1111