1096eccdeSJussi Maki // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2096eccdeSJussi Maki
3096eccdeSJussi Maki /*
4096eccdeSJussi Maki * This test sets up 3 netns (src <-> fwd <-> dst). There is no direct veth link
5096eccdeSJussi Maki * between src and dst. The netns fwd has veth links to each src and dst. The
6096eccdeSJussi Maki * client is in src and server in dst. The test installs a TC BPF program to each
7096eccdeSJussi Maki * host facing veth in fwd which calls into i) bpf_redirect_neigh() to perform the
8096eccdeSJussi Maki * neigh addr population and redirect or ii) bpf_redirect_peer() for namespace
9096eccdeSJussi Maki * switch from ingress side; it also installs a checker prog on the egress side
10096eccdeSJussi Maki * to drop unexpected traffic.
11096eccdeSJussi Maki */
12096eccdeSJussi Maki
136fd5fb63SJussi Maki #include <arpa/inet.h>
144cda0c82SJussi Maki #include <linux/if_tun.h>
15096eccdeSJussi Maki #include <linux/limits.h>
16096eccdeSJussi Maki #include <linux/sysctl.h>
17c803475fSMartin KaFai Lau #include <linux/time_types.h>
18c803475fSMartin KaFai Lau #include <linux/net_tstamp.h>
19052c82dcSMartin KaFai Lau #include <net/if.h>
20096eccdeSJussi Maki #include <stdbool.h>
21096eccdeSJussi Maki #include <stdio.h>
224cda0c82SJussi Maki #include <sys/stat.h>
234cda0c82SJussi Maki #include <unistd.h>
24096eccdeSJussi Maki
25096eccdeSJussi Maki #include "test_progs.h"
26096eccdeSJussi Maki #include "network_helpers.h"
27096eccdeSJussi Maki #include "test_tc_neigh_fib.skel.h"
28096eccdeSJussi Maki #include "test_tc_neigh.skel.h"
29096eccdeSJussi Maki #include "test_tc_peer.skel.h"
30c803475fSMartin KaFai Lau #include "test_tc_dtime.skel.h"
31c803475fSMartin KaFai Lau
32c803475fSMartin KaFai Lau #ifndef TCP_TX_DELAY
33c803475fSMartin KaFai Lau #define TCP_TX_DELAY 37
34c803475fSMartin KaFai Lau #endif
35096eccdeSJussi Maki
36096eccdeSJussi Maki #define NS_SRC "ns_src"
37096eccdeSJussi Maki #define NS_FWD "ns_fwd"
38096eccdeSJussi Maki #define NS_DST "ns_dst"
39096eccdeSJussi Maki
40096eccdeSJussi Maki #define IP4_SRC "172.16.1.100"
41096eccdeSJussi Maki #define IP4_DST "172.16.2.100"
426fd5fb63SJussi Maki #define IP4_TUN_SRC "172.17.1.100"
436fd5fb63SJussi Maki #define IP4_TUN_FWD "172.17.1.200"
44096eccdeSJussi Maki #define IP4_PORT 9004
45096eccdeSJussi Maki
466fd5fb63SJussi Maki #define IP6_SRC "0::1:dead:beef:cafe"
476fd5fb63SJussi Maki #define IP6_DST "0::2:dead:beef:cafe"
486fd5fb63SJussi Maki #define IP6_TUN_SRC "1::1:dead:beef:cafe"
496fd5fb63SJussi Maki #define IP6_TUN_FWD "1::2:dead:beef:cafe"
50096eccdeSJussi Maki #define IP6_PORT 9006
51096eccdeSJussi Maki
52096eccdeSJussi Maki #define IP4_SLL "169.254.0.1"
53096eccdeSJussi Maki #define IP4_DLL "169.254.0.2"
54096eccdeSJussi Maki #define IP4_NET "169.254.0.0"
55096eccdeSJussi Maki
566fd5fb63SJussi Maki #define MAC_DST_FWD "00:11:22:33:44:55"
576fd5fb63SJussi Maki #define MAC_DST "00:22:33:44:55:66"
586fd5fb63SJussi Maki
59096eccdeSJussi Maki #define IFADDR_STR_LEN 18
606fd5fb63SJussi Maki #define PING_ARGS "-i 0.2 -c 3 -w 10 -q"
61096eccdeSJussi Maki
62096eccdeSJussi Maki #define TIMEOUT_MILLIS 10000
63c803475fSMartin KaFai Lau #define NSEC_PER_SEC 1000000000ULL
64096eccdeSJussi Maki
65096eccdeSJussi Maki #define log_err(MSG, ...) \
66096eccdeSJussi Maki fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", \
67096eccdeSJussi Maki __FILE__, __LINE__, strerror(errno), ##__VA_ARGS__)
68096eccdeSJussi Maki
69096eccdeSJussi Maki static const char * const namespaces[] = {NS_SRC, NS_FWD, NS_DST, NULL};
70096eccdeSJussi Maki
write_file(const char * path,const char * newval)716fd5fb63SJussi Maki static int write_file(const char *path, const char *newval)
72096eccdeSJussi Maki {
73096eccdeSJussi Maki FILE *f;
74096eccdeSJussi Maki
75096eccdeSJussi Maki f = fopen(path, "r+");
76096eccdeSJussi Maki if (!f)
77096eccdeSJussi Maki return -1;
78096eccdeSJussi Maki if (fwrite(newval, strlen(newval), 1, f) != 1) {
79096eccdeSJussi Maki log_err("writing to %s failed", path);
80096eccdeSJussi Maki fclose(f);
81096eccdeSJussi Maki return -1;
82096eccdeSJussi Maki }
83096eccdeSJussi Maki fclose(f);
846fd5fb63SJussi Maki return 0;
85096eccdeSJussi Maki }
86096eccdeSJussi Maki
netns_setup_namespaces(const char * verb)87096eccdeSJussi Maki static int netns_setup_namespaces(const char *verb)
88096eccdeSJussi Maki {
89096eccdeSJussi Maki const char * const *ns = namespaces;
90096eccdeSJussi Maki char cmd[128];
91096eccdeSJussi Maki
92096eccdeSJussi Maki while (*ns) {
93096eccdeSJussi Maki snprintf(cmd, sizeof(cmd), "ip netns %s %s", verb, *ns);
94096eccdeSJussi Maki if (!ASSERT_OK(system(cmd), cmd))
95096eccdeSJussi Maki return -1;
96096eccdeSJussi Maki ns++;
97096eccdeSJussi Maki }
98096eccdeSJussi Maki return 0;
99096eccdeSJussi Maki }
100096eccdeSJussi Maki
netns_setup_namespaces_nofail(const char * verb)101e1ef62a4SYucong Sun static void netns_setup_namespaces_nofail(const char *verb)
102e1ef62a4SYucong Sun {
103e1ef62a4SYucong Sun const char * const *ns = namespaces;
104e1ef62a4SYucong Sun char cmd[128];
105e1ef62a4SYucong Sun
106e1ef62a4SYucong Sun while (*ns) {
107e1ef62a4SYucong Sun snprintf(cmd, sizeof(cmd), "ip netns %s %s > /dev/null 2>&1", verb, *ns);
108e1ef62a4SYucong Sun system(cmd);
109e1ef62a4SYucong Sun ns++;
110e1ef62a4SYucong Sun }
111e1ef62a4SYucong Sun }
112e1ef62a4SYucong Sun
11372f1ba02SDaniel Borkmann enum dev_mode {
11472f1ba02SDaniel Borkmann MODE_VETH,
11572f1ba02SDaniel Borkmann };
11672f1ba02SDaniel Borkmann
117096eccdeSJussi Maki struct netns_setup_result {
11872f1ba02SDaniel Borkmann enum dev_mode dev_mode;
11972f1ba02SDaniel Borkmann int ifindex_src;
12072f1ba02SDaniel Borkmann int ifindex_src_fwd;
12172f1ba02SDaniel Borkmann int ifindex_dst;
12272f1ba02SDaniel Borkmann int ifindex_dst_fwd;
123096eccdeSJussi Maki };
124096eccdeSJussi Maki
get_ifaddr(const char * name,char * ifaddr)125096eccdeSJussi Maki static int get_ifaddr(const char *name, char *ifaddr)
126096eccdeSJussi Maki {
127096eccdeSJussi Maki char path[PATH_MAX];
128096eccdeSJussi Maki FILE *f;
129096eccdeSJussi Maki int ret;
130096eccdeSJussi Maki
131096eccdeSJussi Maki snprintf(path, PATH_MAX, "/sys/class/net/%s/address", name);
132096eccdeSJussi Maki f = fopen(path, "r");
133096eccdeSJussi Maki if (!ASSERT_OK_PTR(f, path))
134096eccdeSJussi Maki return -1;
135096eccdeSJussi Maki
136096eccdeSJussi Maki ret = fread(ifaddr, 1, IFADDR_STR_LEN, f);
137096eccdeSJussi Maki if (!ASSERT_EQ(ret, IFADDR_STR_LEN, "fread ifaddr")) {
138096eccdeSJussi Maki fclose(f);
139096eccdeSJussi Maki return -1;
140096eccdeSJussi Maki }
141096eccdeSJussi Maki fclose(f);
142096eccdeSJussi Maki return 0;
143096eccdeSJussi Maki }
144096eccdeSJussi Maki
netns_setup_links_and_routes(struct netns_setup_result * result)145096eccdeSJussi Maki static int netns_setup_links_and_routes(struct netns_setup_result *result)
146096eccdeSJussi Maki {
1476fd5fb63SJussi Maki struct nstoken *nstoken = NULL;
14872f1ba02SDaniel Borkmann char src_fwd_addr[IFADDR_STR_LEN+1] = {};
149f02bcb41SMartin KaFai Lau char src_addr[IFADDR_STR_LEN + 1] = {};
150096eccdeSJussi Maki
15172f1ba02SDaniel Borkmann if (result->dev_mode == MODE_VETH) {
15272f1ba02SDaniel Borkmann SYS(fail, "ip link add src type veth peer name src_fwd");
15372f1ba02SDaniel Borkmann SYS(fail, "ip link add dst type veth peer name dst_fwd");
1546fd5fb63SJussi Maki
15572f1ba02SDaniel Borkmann SYS(fail, "ip link set dst_fwd address " MAC_DST_FWD);
15672f1ba02SDaniel Borkmann SYS(fail, "ip link set dst address " MAC_DST);
15772f1ba02SDaniel Borkmann }
1586fd5fb63SJussi Maki
15972f1ba02SDaniel Borkmann if (get_ifaddr("src_fwd", src_fwd_addr))
160096eccdeSJussi Maki goto fail;
161096eccdeSJussi Maki
162f02bcb41SMartin KaFai Lau if (get_ifaddr("src", src_addr))
163f02bcb41SMartin KaFai Lau goto fail;
164f02bcb41SMartin KaFai Lau
16572f1ba02SDaniel Borkmann result->ifindex_src = if_nametoindex("src");
16672f1ba02SDaniel Borkmann if (!ASSERT_GT(result->ifindex_src, 0, "ifindex_src"))
167096eccdeSJussi Maki goto fail;
168052c82dcSMartin KaFai Lau
16972f1ba02SDaniel Borkmann result->ifindex_src_fwd = if_nametoindex("src_fwd");
17072f1ba02SDaniel Borkmann if (!ASSERT_GT(result->ifindex_src_fwd, 0, "ifindex_src_fwd"))
171052c82dcSMartin KaFai Lau goto fail;
172052c82dcSMartin KaFai Lau
17372f1ba02SDaniel Borkmann result->ifindex_dst = if_nametoindex("dst");
17472f1ba02SDaniel Borkmann if (!ASSERT_GT(result->ifindex_dst, 0, "ifindex_dst"))
175052c82dcSMartin KaFai Lau goto fail;
176052c82dcSMartin KaFai Lau
17772f1ba02SDaniel Borkmann result->ifindex_dst_fwd = if_nametoindex("dst_fwd");
17872f1ba02SDaniel Borkmann if (!ASSERT_GT(result->ifindex_dst_fwd, 0, "ifindex_dst_fwd"))
179096eccdeSJussi Maki goto fail;
180096eccdeSJussi Maki
18172f1ba02SDaniel Borkmann SYS(fail, "ip link set src netns " NS_SRC);
18272f1ba02SDaniel Borkmann SYS(fail, "ip link set src_fwd netns " NS_FWD);
18372f1ba02SDaniel Borkmann SYS(fail, "ip link set dst_fwd netns " NS_FWD);
18472f1ba02SDaniel Borkmann SYS(fail, "ip link set dst netns " NS_DST);
185096eccdeSJussi Maki
186096eccdeSJussi Maki /** setup in 'src' namespace */
1876fd5fb63SJussi Maki nstoken = open_netns(NS_SRC);
1886fd5fb63SJussi Maki if (!ASSERT_OK_PTR(nstoken, "setns src"))
189096eccdeSJussi Maki goto fail;
190096eccdeSJussi Maki
19172f1ba02SDaniel Borkmann SYS(fail, "ip addr add " IP4_SRC "/32 dev src");
19272f1ba02SDaniel Borkmann SYS(fail, "ip addr add " IP6_SRC "/128 dev src nodad");
19372f1ba02SDaniel Borkmann SYS(fail, "ip link set dev src up");
194096eccdeSJussi Maki
19572f1ba02SDaniel Borkmann SYS(fail, "ip route add " IP4_DST "/32 dev src scope global");
19672f1ba02SDaniel Borkmann SYS(fail, "ip route add " IP4_NET "/16 dev src scope global");
19772f1ba02SDaniel Borkmann SYS(fail, "ip route add " IP6_DST "/128 dev src scope global");
198096eccdeSJussi Maki
19972f1ba02SDaniel Borkmann if (result->dev_mode == MODE_VETH) {
20072f1ba02SDaniel Borkmann SYS(fail, "ip neigh add " IP4_DST " dev src lladdr %s",
20172f1ba02SDaniel Borkmann src_fwd_addr);
20272f1ba02SDaniel Borkmann SYS(fail, "ip neigh add " IP6_DST " dev src lladdr %s",
20372f1ba02SDaniel Borkmann src_fwd_addr);
20472f1ba02SDaniel Borkmann }
205096eccdeSJussi Maki
2066fd5fb63SJussi Maki close_netns(nstoken);
2076fd5fb63SJussi Maki
208096eccdeSJussi Maki /** setup in 'fwd' namespace */
2096fd5fb63SJussi Maki nstoken = open_netns(NS_FWD);
2106fd5fb63SJussi Maki if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
211096eccdeSJussi Maki goto fail;
212096eccdeSJussi Maki
213096eccdeSJussi Maki /* The fwd netns automatically gets a v6 LL address / routes, but also
214096eccdeSJussi Maki * needs v4 one in order to start ARP probing. IP4_NET route is added
215096eccdeSJussi Maki * to the endpoints so that the ARP processing will reply.
216096eccdeSJussi Maki */
21772f1ba02SDaniel Borkmann SYS(fail, "ip addr add " IP4_SLL "/32 dev src_fwd");
21872f1ba02SDaniel Borkmann SYS(fail, "ip addr add " IP4_DLL "/32 dev dst_fwd");
21972f1ba02SDaniel Borkmann SYS(fail, "ip link set dev src_fwd up");
22072f1ba02SDaniel Borkmann SYS(fail, "ip link set dev dst_fwd up");
221096eccdeSJussi Maki
22272f1ba02SDaniel Borkmann SYS(fail, "ip route add " IP4_SRC "/32 dev src_fwd scope global");
22372f1ba02SDaniel Borkmann SYS(fail, "ip route add " IP6_SRC "/128 dev src_fwd scope global");
22472f1ba02SDaniel Borkmann SYS(fail, "ip route add " IP4_DST "/32 dev dst_fwd scope global");
22572f1ba02SDaniel Borkmann SYS(fail, "ip route add " IP6_DST "/128 dev dst_fwd scope global");
226096eccdeSJussi Maki
227f02bcb41SMartin KaFai Lau if (result->dev_mode == MODE_VETH) {
228f02bcb41SMartin KaFai Lau SYS(fail, "ip neigh add " IP4_SRC " dev src_fwd lladdr %s", src_addr);
229f02bcb41SMartin KaFai Lau SYS(fail, "ip neigh add " IP6_SRC " dev src_fwd lladdr %s", src_addr);
230f02bcb41SMartin KaFai Lau SYS(fail, "ip neigh add " IP4_DST " dev dst_fwd lladdr %s", MAC_DST);
231f02bcb41SMartin KaFai Lau SYS(fail, "ip neigh add " IP6_DST " dev dst_fwd lladdr %s", MAC_DST);
232f02bcb41SMartin KaFai Lau }
233f02bcb41SMartin KaFai Lau
2346fd5fb63SJussi Maki close_netns(nstoken);
2356fd5fb63SJussi Maki
236096eccdeSJussi Maki /** setup in 'dst' namespace */
2376fd5fb63SJussi Maki nstoken = open_netns(NS_DST);
2386fd5fb63SJussi Maki if (!ASSERT_OK_PTR(nstoken, "setns dst"))
239096eccdeSJussi Maki goto fail;
240096eccdeSJussi Maki
24172f1ba02SDaniel Borkmann SYS(fail, "ip addr add " IP4_DST "/32 dev dst");
24272f1ba02SDaniel Borkmann SYS(fail, "ip addr add " IP6_DST "/128 dev dst nodad");
24372f1ba02SDaniel Borkmann SYS(fail, "ip link set dev dst up");
244*a9800dc6SMartin KaFai Lau SYS(fail, "ip link set dev lo up");
245096eccdeSJussi Maki
24672f1ba02SDaniel Borkmann SYS(fail, "ip route add " IP4_SRC "/32 dev dst scope global");
24772f1ba02SDaniel Borkmann SYS(fail, "ip route add " IP4_NET "/16 dev dst scope global");
24872f1ba02SDaniel Borkmann SYS(fail, "ip route add " IP6_SRC "/128 dev dst scope global");
249096eccdeSJussi Maki
25072f1ba02SDaniel Borkmann if (result->dev_mode == MODE_VETH) {
25172f1ba02SDaniel Borkmann SYS(fail, "ip neigh add " IP4_SRC " dev dst lladdr " MAC_DST_FWD);
25272f1ba02SDaniel Borkmann SYS(fail, "ip neigh add " IP6_SRC " dev dst lladdr " MAC_DST_FWD);
25372f1ba02SDaniel Borkmann }
254096eccdeSJussi Maki
2556fd5fb63SJussi Maki close_netns(nstoken);
2566fd5fb63SJussi Maki
257096eccdeSJussi Maki return 0;
258096eccdeSJussi Maki fail:
2596fd5fb63SJussi Maki if (nstoken)
2606fd5fb63SJussi Maki close_netns(nstoken);
261096eccdeSJussi Maki return -1;
262096eccdeSJussi Maki }
263096eccdeSJussi Maki
qdisc_clsact_create(struct bpf_tc_hook * qdisc_hook,int ifindex)26457d0863fSMartin KaFai Lau static int qdisc_clsact_create(struct bpf_tc_hook *qdisc_hook, int ifindex)
26557d0863fSMartin KaFai Lau {
26657d0863fSMartin KaFai Lau char err_str[128], ifname[16];
26757d0863fSMartin KaFai Lau int err;
26857d0863fSMartin KaFai Lau
26957d0863fSMartin KaFai Lau qdisc_hook->ifindex = ifindex;
27057d0863fSMartin KaFai Lau qdisc_hook->attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
27157d0863fSMartin KaFai Lau err = bpf_tc_hook_create(qdisc_hook);
27257d0863fSMartin KaFai Lau snprintf(err_str, sizeof(err_str),
27357d0863fSMartin KaFai Lau "qdisc add dev %s clsact",
27457d0863fSMartin KaFai Lau if_indextoname(qdisc_hook->ifindex, ifname) ? : "<unknown_iface>");
27557d0863fSMartin KaFai Lau err_str[sizeof(err_str) - 1] = 0;
27657d0863fSMartin KaFai Lau ASSERT_OK(err, err_str);
27757d0863fSMartin KaFai Lau
27857d0863fSMartin KaFai Lau return err;
27957d0863fSMartin KaFai Lau }
28057d0863fSMartin KaFai Lau
xgress_filter_add(struct bpf_tc_hook * qdisc_hook,enum bpf_tc_attach_point xgress,const struct bpf_program * prog,int priority)28157d0863fSMartin KaFai Lau static int xgress_filter_add(struct bpf_tc_hook *qdisc_hook,
28257d0863fSMartin KaFai Lau enum bpf_tc_attach_point xgress,
28357d0863fSMartin KaFai Lau const struct bpf_program *prog, int priority)
28457d0863fSMartin KaFai Lau {
28557d0863fSMartin KaFai Lau LIBBPF_OPTS(bpf_tc_opts, tc_attach);
28657d0863fSMartin KaFai Lau char err_str[128], ifname[16];
28757d0863fSMartin KaFai Lau int err;
28857d0863fSMartin KaFai Lau
28957d0863fSMartin KaFai Lau qdisc_hook->attach_point = xgress;
29057d0863fSMartin KaFai Lau tc_attach.prog_fd = bpf_program__fd(prog);
29157d0863fSMartin KaFai Lau tc_attach.priority = priority;
29257d0863fSMartin KaFai Lau err = bpf_tc_attach(qdisc_hook, &tc_attach);
29357d0863fSMartin KaFai Lau snprintf(err_str, sizeof(err_str),
29457d0863fSMartin KaFai Lau "filter add dev %s %s prio %d bpf da %s",
29557d0863fSMartin KaFai Lau if_indextoname(qdisc_hook->ifindex, ifname) ? : "<unknown_iface>",
29657d0863fSMartin KaFai Lau xgress == BPF_TC_INGRESS ? "ingress" : "egress",
29757d0863fSMartin KaFai Lau priority, bpf_program__name(prog));
29857d0863fSMartin KaFai Lau err_str[sizeof(err_str) - 1] = 0;
29957d0863fSMartin KaFai Lau ASSERT_OK(err, err_str);
30057d0863fSMartin KaFai Lau
30157d0863fSMartin KaFai Lau return err;
30257d0863fSMartin KaFai Lau }
30357d0863fSMartin KaFai Lau
30457d0863fSMartin KaFai Lau #define QDISC_CLSACT_CREATE(qdisc_hook, ifindex) ({ \
30557d0863fSMartin KaFai Lau if ((err = qdisc_clsact_create(qdisc_hook, ifindex))) \
30657d0863fSMartin KaFai Lau goto fail; \
30757d0863fSMartin KaFai Lau })
30857d0863fSMartin KaFai Lau
30957d0863fSMartin KaFai Lau #define XGRESS_FILTER_ADD(qdisc_hook, xgress, prog, priority) ({ \
31057d0863fSMartin KaFai Lau if ((err = xgress_filter_add(qdisc_hook, xgress, prog, priority))) \
31157d0863fSMartin KaFai Lau goto fail; \
31257d0863fSMartin KaFai Lau })
31357d0863fSMartin KaFai Lau
netns_load_bpf(const struct bpf_program * src_prog,const struct bpf_program * dst_prog,const struct bpf_program * chk_prog,const struct netns_setup_result * setup_result)3145dc42a7fSMartin KaFai Lau static int netns_load_bpf(const struct bpf_program *src_prog,
3155dc42a7fSMartin KaFai Lau const struct bpf_program *dst_prog,
3165dc42a7fSMartin KaFai Lau const struct bpf_program *chk_prog,
3175dc42a7fSMartin KaFai Lau const struct netns_setup_result *setup_result)
318096eccdeSJussi Maki {
31972f1ba02SDaniel Borkmann LIBBPF_OPTS(bpf_tc_hook, qdisc_src_fwd);
32072f1ba02SDaniel Borkmann LIBBPF_OPTS(bpf_tc_hook, qdisc_dst_fwd);
3215dc42a7fSMartin KaFai Lau int err;
322096eccdeSJussi Maki
32372f1ba02SDaniel Borkmann /* tc qdisc add dev src_fwd clsact */
32472f1ba02SDaniel Borkmann QDISC_CLSACT_CREATE(&qdisc_src_fwd, setup_result->ifindex_src_fwd);
32572f1ba02SDaniel Borkmann /* tc filter add dev src_fwd ingress bpf da src_prog */
32672f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_src_fwd, BPF_TC_INGRESS, src_prog, 0);
32772f1ba02SDaniel Borkmann /* tc filter add dev src_fwd egress bpf da chk_prog */
32872f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_src_fwd, BPF_TC_EGRESS, chk_prog, 0);
3295dc42a7fSMartin KaFai Lau
33072f1ba02SDaniel Borkmann /* tc qdisc add dev dst_fwd clsact */
33172f1ba02SDaniel Borkmann QDISC_CLSACT_CREATE(&qdisc_dst_fwd, setup_result->ifindex_dst_fwd);
33272f1ba02SDaniel Borkmann /* tc filter add dev dst_fwd ingress bpf da dst_prog */
33372f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_dst_fwd, BPF_TC_INGRESS, dst_prog, 0);
33472f1ba02SDaniel Borkmann /* tc filter add dev dst_fwd egress bpf da chk_prog */
33572f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_dst_fwd, BPF_TC_EGRESS, chk_prog, 0);
336096eccdeSJussi Maki
337096eccdeSJussi Maki return 0;
338096eccdeSJussi Maki fail:
339096eccdeSJussi Maki return -1;
340096eccdeSJussi Maki }
341096eccdeSJussi Maki
test_tcp(int family,const char * addr,__u16 port)342096eccdeSJussi Maki static void test_tcp(int family, const char *addr, __u16 port)
343096eccdeSJussi Maki {
344096eccdeSJussi Maki int listen_fd = -1, accept_fd = -1, client_fd = -1;
345096eccdeSJussi Maki char buf[] = "testing testing";
346096eccdeSJussi Maki int n;
3476fd5fb63SJussi Maki struct nstoken *nstoken;
348096eccdeSJussi Maki
3496fd5fb63SJussi Maki nstoken = open_netns(NS_DST);
3506fd5fb63SJussi Maki if (!ASSERT_OK_PTR(nstoken, "setns dst"))
351096eccdeSJussi Maki return;
352096eccdeSJussi Maki
353096eccdeSJussi Maki listen_fd = start_server(family, SOCK_STREAM, addr, port, 0);
354096eccdeSJussi Maki if (!ASSERT_GE(listen_fd, 0, "listen"))
355096eccdeSJussi Maki goto done;
356096eccdeSJussi Maki
3576fd5fb63SJussi Maki close_netns(nstoken);
3586fd5fb63SJussi Maki nstoken = open_netns(NS_SRC);
3596fd5fb63SJussi Maki if (!ASSERT_OK_PTR(nstoken, "setns src"))
360096eccdeSJussi Maki goto done;
361096eccdeSJussi Maki
362096eccdeSJussi Maki client_fd = connect_to_fd(listen_fd, TIMEOUT_MILLIS);
363096eccdeSJussi Maki if (!ASSERT_GE(client_fd, 0, "connect_to_fd"))
364096eccdeSJussi Maki goto done;
365096eccdeSJussi Maki
366096eccdeSJussi Maki accept_fd = accept(listen_fd, NULL, NULL);
367096eccdeSJussi Maki if (!ASSERT_GE(accept_fd, 0, "accept"))
368096eccdeSJussi Maki goto done;
369096eccdeSJussi Maki
370096eccdeSJussi Maki if (!ASSERT_OK(settimeo(accept_fd, TIMEOUT_MILLIS), "settimeo"))
371096eccdeSJussi Maki goto done;
372096eccdeSJussi Maki
373096eccdeSJussi Maki n = write(client_fd, buf, sizeof(buf));
374096eccdeSJussi Maki if (!ASSERT_EQ(n, sizeof(buf), "send to server"))
375096eccdeSJussi Maki goto done;
376096eccdeSJussi Maki
377096eccdeSJussi Maki n = read(accept_fd, buf, sizeof(buf));
378096eccdeSJussi Maki ASSERT_EQ(n, sizeof(buf), "recv from server");
379096eccdeSJussi Maki
380096eccdeSJussi Maki done:
3816fd5fb63SJussi Maki if (nstoken)
3826fd5fb63SJussi Maki close_netns(nstoken);
383096eccdeSJussi Maki if (listen_fd >= 0)
384096eccdeSJussi Maki close(listen_fd);
385096eccdeSJussi Maki if (accept_fd >= 0)
386096eccdeSJussi Maki close(accept_fd);
387096eccdeSJussi Maki if (client_fd >= 0)
388096eccdeSJussi Maki close(client_fd);
389096eccdeSJussi Maki }
390096eccdeSJussi Maki
test_ping(int family,const char * addr)391096eccdeSJussi Maki static int test_ping(int family, const char *addr)
392096eccdeSJussi Maki {
393b61987d3SHangbin Liu SYS(fail, "ip netns exec " NS_SRC " %s " PING_ARGS " %s > /dev/null", ping_command(family), addr);
394096eccdeSJussi Maki return 0;
395096eccdeSJussi Maki fail:
396096eccdeSJussi Maki return -1;
397096eccdeSJussi Maki }
398096eccdeSJussi Maki
test_connectivity(void)399096eccdeSJussi Maki static void test_connectivity(void)
400096eccdeSJussi Maki {
401096eccdeSJussi Maki test_tcp(AF_INET, IP4_DST, IP4_PORT);
402096eccdeSJussi Maki test_ping(AF_INET, IP4_DST);
403096eccdeSJussi Maki test_tcp(AF_INET6, IP6_DST, IP6_PORT);
404096eccdeSJussi Maki test_ping(AF_INET6, IP6_DST);
405096eccdeSJussi Maki }
406096eccdeSJussi Maki
set_forwarding(bool enable)4076fd5fb63SJussi Maki static int set_forwarding(bool enable)
4086fd5fb63SJussi Maki {
4096fd5fb63SJussi Maki int err;
4106fd5fb63SJussi Maki
4116fd5fb63SJussi Maki err = write_file("/proc/sys/net/ipv4/ip_forward", enable ? "1" : "0");
4126fd5fb63SJussi Maki if (!ASSERT_OK(err, "set ipv4.ip_forward=0"))
4136fd5fb63SJussi Maki return err;
4146fd5fb63SJussi Maki
4156fd5fb63SJussi Maki err = write_file("/proc/sys/net/ipv6/conf/all/forwarding", enable ? "1" : "0");
4166fd5fb63SJussi Maki if (!ASSERT_OK(err, "set ipv6.forwarding=0"))
4176fd5fb63SJussi Maki return err;
4186fd5fb63SJussi Maki
4196fd5fb63SJussi Maki return 0;
4206fd5fb63SJussi Maki }
4216fd5fb63SJussi Maki
__rcv_tstamp(int fd,const char * expected,size_t s,__u64 * tstamp)422*a9800dc6SMartin KaFai Lau static int __rcv_tstamp(int fd, const char *expected, size_t s, __u64 *tstamp)
423c803475fSMartin KaFai Lau {
424c803475fSMartin KaFai Lau struct __kernel_timespec pkt_ts = {};
425c803475fSMartin KaFai Lau char ctl[CMSG_SPACE(sizeof(pkt_ts))];
426c803475fSMartin KaFai Lau struct timespec now_ts;
427c803475fSMartin KaFai Lau struct msghdr msg = {};
428c803475fSMartin KaFai Lau __u64 now_ns, pkt_ns;
429c803475fSMartin KaFai Lau struct cmsghdr *cmsg;
430c803475fSMartin KaFai Lau struct iovec iov;
431c803475fSMartin KaFai Lau char data[32];
432c803475fSMartin KaFai Lau int ret;
433c803475fSMartin KaFai Lau
434c803475fSMartin KaFai Lau iov.iov_base = data;
435c803475fSMartin KaFai Lau iov.iov_len = sizeof(data);
436c803475fSMartin KaFai Lau msg.msg_iov = &iov;
437c803475fSMartin KaFai Lau msg.msg_iovlen = 1;
438c803475fSMartin KaFai Lau msg.msg_control = &ctl;
439c803475fSMartin KaFai Lau msg.msg_controllen = sizeof(ctl);
440c803475fSMartin KaFai Lau
441c803475fSMartin KaFai Lau ret = recvmsg(fd, &msg, 0);
442c803475fSMartin KaFai Lau if (!ASSERT_EQ(ret, s, "recvmsg"))
443*a9800dc6SMartin KaFai Lau return -1;
444c803475fSMartin KaFai Lau ASSERT_STRNEQ(data, expected, s, "expected rcv data");
445c803475fSMartin KaFai Lau
446c803475fSMartin KaFai Lau cmsg = CMSG_FIRSTHDR(&msg);
447c803475fSMartin KaFai Lau if (cmsg && cmsg->cmsg_level == SOL_SOCKET &&
448c803475fSMartin KaFai Lau cmsg->cmsg_type == SO_TIMESTAMPNS_NEW)
449c803475fSMartin KaFai Lau memcpy(&pkt_ts, CMSG_DATA(cmsg), sizeof(pkt_ts));
450c803475fSMartin KaFai Lau
451c803475fSMartin KaFai Lau pkt_ns = pkt_ts.tv_sec * NSEC_PER_SEC + pkt_ts.tv_nsec;
452*a9800dc6SMartin KaFai Lau if (tstamp) {
453*a9800dc6SMartin KaFai Lau /* caller will check the tstamp itself */
454*a9800dc6SMartin KaFai Lau *tstamp = pkt_ns;
455*a9800dc6SMartin KaFai Lau return 0;
456*a9800dc6SMartin KaFai Lau }
457*a9800dc6SMartin KaFai Lau
458c803475fSMartin KaFai Lau ASSERT_NEQ(pkt_ns, 0, "pkt rcv tstamp");
459c803475fSMartin KaFai Lau
460c803475fSMartin KaFai Lau ret = clock_gettime(CLOCK_REALTIME, &now_ts);
461c803475fSMartin KaFai Lau ASSERT_OK(ret, "clock_gettime");
462c803475fSMartin KaFai Lau now_ns = now_ts.tv_sec * NSEC_PER_SEC + now_ts.tv_nsec;
463c803475fSMartin KaFai Lau
464c803475fSMartin KaFai Lau if (ASSERT_GE(now_ns, pkt_ns, "check rcv tstamp"))
465c803475fSMartin KaFai Lau ASSERT_LT(now_ns - pkt_ns, 5 * NSEC_PER_SEC,
466c803475fSMartin KaFai Lau "check rcv tstamp");
467*a9800dc6SMartin KaFai Lau return 0;
468*a9800dc6SMartin KaFai Lau }
469*a9800dc6SMartin KaFai Lau
rcv_tstamp(int fd,const char * expected,size_t s)470*a9800dc6SMartin KaFai Lau static void rcv_tstamp(int fd, const char *expected, size_t s)
471*a9800dc6SMartin KaFai Lau {
472*a9800dc6SMartin KaFai Lau __rcv_tstamp(fd, expected, s, NULL);
473*a9800dc6SMartin KaFai Lau }
474*a9800dc6SMartin KaFai Lau
wait_netstamp_needed_key(void)475*a9800dc6SMartin KaFai Lau static int wait_netstamp_needed_key(void)
476*a9800dc6SMartin KaFai Lau {
477*a9800dc6SMartin KaFai Lau int opt = 1, srv_fd = -1, cli_fd = -1, nretries = 0, err, n;
478*a9800dc6SMartin KaFai Lau char buf[] = "testing testing";
479*a9800dc6SMartin KaFai Lau struct nstoken *nstoken;
480*a9800dc6SMartin KaFai Lau __u64 tstamp = 0;
481*a9800dc6SMartin KaFai Lau
482*a9800dc6SMartin KaFai Lau nstoken = open_netns(NS_DST);
483*a9800dc6SMartin KaFai Lau if (!nstoken)
484*a9800dc6SMartin KaFai Lau return -1;
485*a9800dc6SMartin KaFai Lau
486*a9800dc6SMartin KaFai Lau srv_fd = start_server(AF_INET6, SOCK_DGRAM, "::1", 0, 0);
487*a9800dc6SMartin KaFai Lau if (!ASSERT_GE(srv_fd, 0, "start_server"))
488*a9800dc6SMartin KaFai Lau goto done;
489*a9800dc6SMartin KaFai Lau
490*a9800dc6SMartin KaFai Lau err = setsockopt(srv_fd, SOL_SOCKET, SO_TIMESTAMPNS_NEW,
491*a9800dc6SMartin KaFai Lau &opt, sizeof(opt));
492*a9800dc6SMartin KaFai Lau if (!ASSERT_OK(err, "setsockopt(SO_TIMESTAMPNS_NEW)"))
493*a9800dc6SMartin KaFai Lau goto done;
494*a9800dc6SMartin KaFai Lau
495*a9800dc6SMartin KaFai Lau cli_fd = connect_to_fd(srv_fd, TIMEOUT_MILLIS);
496*a9800dc6SMartin KaFai Lau if (!ASSERT_GE(cli_fd, 0, "connect_to_fd"))
497*a9800dc6SMartin KaFai Lau goto done;
498*a9800dc6SMartin KaFai Lau
499*a9800dc6SMartin KaFai Lau again:
500*a9800dc6SMartin KaFai Lau n = write(cli_fd, buf, sizeof(buf));
501*a9800dc6SMartin KaFai Lau if (!ASSERT_EQ(n, sizeof(buf), "send to server"))
502*a9800dc6SMartin KaFai Lau goto done;
503*a9800dc6SMartin KaFai Lau err = __rcv_tstamp(srv_fd, buf, sizeof(buf), &tstamp);
504*a9800dc6SMartin KaFai Lau if (!ASSERT_OK(err, "__rcv_tstamp"))
505*a9800dc6SMartin KaFai Lau goto done;
506*a9800dc6SMartin KaFai Lau if (!tstamp && nretries++ < 5) {
507*a9800dc6SMartin KaFai Lau sleep(1);
508*a9800dc6SMartin KaFai Lau printf("netstamp_needed_key retry#%d\n", nretries);
509*a9800dc6SMartin KaFai Lau goto again;
510*a9800dc6SMartin KaFai Lau }
511*a9800dc6SMartin KaFai Lau
512*a9800dc6SMartin KaFai Lau done:
513*a9800dc6SMartin KaFai Lau if (!tstamp && srv_fd != -1) {
514*a9800dc6SMartin KaFai Lau close(srv_fd);
515*a9800dc6SMartin KaFai Lau srv_fd = -1;
516*a9800dc6SMartin KaFai Lau }
517*a9800dc6SMartin KaFai Lau if (cli_fd != -1)
518*a9800dc6SMartin KaFai Lau close(cli_fd);
519*a9800dc6SMartin KaFai Lau close_netns(nstoken);
520*a9800dc6SMartin KaFai Lau return srv_fd;
521c803475fSMartin KaFai Lau }
522c803475fSMartin KaFai Lau
snd_tstamp(int fd,char * b,size_t s)523c803475fSMartin KaFai Lau static void snd_tstamp(int fd, char *b, size_t s)
524c803475fSMartin KaFai Lau {
525c803475fSMartin KaFai Lau struct sock_txtime opt = { .clockid = CLOCK_TAI };
526c803475fSMartin KaFai Lau char ctl[CMSG_SPACE(sizeof(__u64))];
527c803475fSMartin KaFai Lau struct timespec now_ts;
528c803475fSMartin KaFai Lau struct msghdr msg = {};
529c803475fSMartin KaFai Lau struct cmsghdr *cmsg;
530c803475fSMartin KaFai Lau struct iovec iov;
531c803475fSMartin KaFai Lau __u64 now_ns;
532c803475fSMartin KaFai Lau int ret;
533c803475fSMartin KaFai Lau
534c803475fSMartin KaFai Lau ret = clock_gettime(CLOCK_TAI, &now_ts);
535c803475fSMartin KaFai Lau ASSERT_OK(ret, "clock_get_time(CLOCK_TAI)");
536c803475fSMartin KaFai Lau now_ns = now_ts.tv_sec * NSEC_PER_SEC + now_ts.tv_nsec;
537c803475fSMartin KaFai Lau
538c803475fSMartin KaFai Lau iov.iov_base = b;
539c803475fSMartin KaFai Lau iov.iov_len = s;
540c803475fSMartin KaFai Lau msg.msg_iov = &iov;
541c803475fSMartin KaFai Lau msg.msg_iovlen = 1;
542c803475fSMartin KaFai Lau msg.msg_control = &ctl;
543c803475fSMartin KaFai Lau msg.msg_controllen = sizeof(ctl);
544c803475fSMartin KaFai Lau
545c803475fSMartin KaFai Lau cmsg = CMSG_FIRSTHDR(&msg);
546c803475fSMartin KaFai Lau cmsg->cmsg_level = SOL_SOCKET;
547c803475fSMartin KaFai Lau cmsg->cmsg_type = SCM_TXTIME;
548c803475fSMartin KaFai Lau cmsg->cmsg_len = CMSG_LEN(sizeof(now_ns));
549c803475fSMartin KaFai Lau *(__u64 *)CMSG_DATA(cmsg) = now_ns;
550c803475fSMartin KaFai Lau
551c803475fSMartin KaFai Lau ret = setsockopt(fd, SOL_SOCKET, SO_TXTIME, &opt, sizeof(opt));
552c803475fSMartin KaFai Lau ASSERT_OK(ret, "setsockopt(SO_TXTIME)");
553c803475fSMartin KaFai Lau
554c803475fSMartin KaFai Lau ret = sendmsg(fd, &msg, 0);
555c803475fSMartin KaFai Lau ASSERT_EQ(ret, s, "sendmsg");
556c803475fSMartin KaFai Lau }
557c803475fSMartin KaFai Lau
test_inet_dtime(int family,int type,const char * addr,__u16 port)558c803475fSMartin KaFai Lau static void test_inet_dtime(int family, int type, const char *addr, __u16 port)
559c803475fSMartin KaFai Lau {
560c803475fSMartin KaFai Lau int opt = 1, accept_fd = -1, client_fd = -1, listen_fd, err;
561c803475fSMartin KaFai Lau char buf[] = "testing testing";
562c803475fSMartin KaFai Lau struct nstoken *nstoken;
563c803475fSMartin KaFai Lau
564c803475fSMartin KaFai Lau nstoken = open_netns(NS_DST);
565c803475fSMartin KaFai Lau if (!ASSERT_OK_PTR(nstoken, "setns dst"))
566c803475fSMartin KaFai Lau return;
567c803475fSMartin KaFai Lau listen_fd = start_server(family, type, addr, port, 0);
568c803475fSMartin KaFai Lau close_netns(nstoken);
569c803475fSMartin KaFai Lau
570c803475fSMartin KaFai Lau if (!ASSERT_GE(listen_fd, 0, "listen"))
571c803475fSMartin KaFai Lau return;
572c803475fSMartin KaFai Lau
573c803475fSMartin KaFai Lau /* Ensure the kernel puts the (rcv) timestamp for all skb */
574c803475fSMartin KaFai Lau err = setsockopt(listen_fd, SOL_SOCKET, SO_TIMESTAMPNS_NEW,
575c803475fSMartin KaFai Lau &opt, sizeof(opt));
576c803475fSMartin KaFai Lau if (!ASSERT_OK(err, "setsockopt(SO_TIMESTAMPNS_NEW)"))
577c803475fSMartin KaFai Lau goto done;
578c803475fSMartin KaFai Lau
579c803475fSMartin KaFai Lau if (type == SOCK_STREAM) {
580c803475fSMartin KaFai Lau /* Ensure the kernel set EDT when sending out rst/ack
581c803475fSMartin KaFai Lau * from the kernel's ctl_sk.
582c803475fSMartin KaFai Lau */
583c803475fSMartin KaFai Lau err = setsockopt(listen_fd, SOL_TCP, TCP_TX_DELAY, &opt,
584c803475fSMartin KaFai Lau sizeof(opt));
585c803475fSMartin KaFai Lau if (!ASSERT_OK(err, "setsockopt(TCP_TX_DELAY)"))
586c803475fSMartin KaFai Lau goto done;
587c803475fSMartin KaFai Lau }
588c803475fSMartin KaFai Lau
589c803475fSMartin KaFai Lau nstoken = open_netns(NS_SRC);
590c803475fSMartin KaFai Lau if (!ASSERT_OK_PTR(nstoken, "setns src"))
591c803475fSMartin KaFai Lau goto done;
592c803475fSMartin KaFai Lau client_fd = connect_to_fd(listen_fd, TIMEOUT_MILLIS);
593c803475fSMartin KaFai Lau close_netns(nstoken);
594c803475fSMartin KaFai Lau
595c803475fSMartin KaFai Lau if (!ASSERT_GE(client_fd, 0, "connect_to_fd"))
596c803475fSMartin KaFai Lau goto done;
597c803475fSMartin KaFai Lau
598c803475fSMartin KaFai Lau if (type == SOCK_STREAM) {
599c803475fSMartin KaFai Lau int n;
600c803475fSMartin KaFai Lau
601c803475fSMartin KaFai Lau accept_fd = accept(listen_fd, NULL, NULL);
602c803475fSMartin KaFai Lau if (!ASSERT_GE(accept_fd, 0, "accept"))
603c803475fSMartin KaFai Lau goto done;
604c803475fSMartin KaFai Lau
605c803475fSMartin KaFai Lau n = write(client_fd, buf, sizeof(buf));
606c803475fSMartin KaFai Lau if (!ASSERT_EQ(n, sizeof(buf), "send to server"))
607c803475fSMartin KaFai Lau goto done;
608c803475fSMartin KaFai Lau rcv_tstamp(accept_fd, buf, sizeof(buf));
609c803475fSMartin KaFai Lau } else {
610c803475fSMartin KaFai Lau snd_tstamp(client_fd, buf, sizeof(buf));
611c803475fSMartin KaFai Lau rcv_tstamp(listen_fd, buf, sizeof(buf));
612c803475fSMartin KaFai Lau }
613c803475fSMartin KaFai Lau
614c803475fSMartin KaFai Lau done:
615c803475fSMartin KaFai Lau close(listen_fd);
616c803475fSMartin KaFai Lau if (accept_fd != -1)
617c803475fSMartin KaFai Lau close(accept_fd);
618c803475fSMartin KaFai Lau if (client_fd != -1)
619c803475fSMartin KaFai Lau close(client_fd);
620c803475fSMartin KaFai Lau }
621c803475fSMartin KaFai Lau
netns_load_dtime_bpf(struct test_tc_dtime * skel,const struct netns_setup_result * setup_result)62257d0863fSMartin KaFai Lau static int netns_load_dtime_bpf(struct test_tc_dtime *skel,
62357d0863fSMartin KaFai Lau const struct netns_setup_result *setup_result)
624c803475fSMartin KaFai Lau {
62572f1ba02SDaniel Borkmann LIBBPF_OPTS(bpf_tc_hook, qdisc_src_fwd);
62672f1ba02SDaniel Borkmann LIBBPF_OPTS(bpf_tc_hook, qdisc_dst_fwd);
62772f1ba02SDaniel Borkmann LIBBPF_OPTS(bpf_tc_hook, qdisc_src);
62872f1ba02SDaniel Borkmann LIBBPF_OPTS(bpf_tc_hook, qdisc_dst);
629c803475fSMartin KaFai Lau struct nstoken *nstoken;
63057d0863fSMartin KaFai Lau int err;
631c803475fSMartin KaFai Lau
632c803475fSMartin KaFai Lau /* setup ns_src tc progs */
633c803475fSMartin KaFai Lau nstoken = open_netns(NS_SRC);
634c803475fSMartin KaFai Lau if (!ASSERT_OK_PTR(nstoken, "setns " NS_SRC))
635c803475fSMartin KaFai Lau return -1;
63672f1ba02SDaniel Borkmann /* tc qdisc add dev src clsact */
63772f1ba02SDaniel Borkmann QDISC_CLSACT_CREATE(&qdisc_src, setup_result->ifindex_src);
63872f1ba02SDaniel Borkmann /* tc filter add dev src ingress bpf da ingress_host */
63972f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_src, BPF_TC_INGRESS, skel->progs.ingress_host, 0);
64072f1ba02SDaniel Borkmann /* tc filter add dev src egress bpf da egress_host */
64172f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_src, BPF_TC_EGRESS, skel->progs.egress_host, 0);
642c803475fSMartin KaFai Lau close_netns(nstoken);
643c803475fSMartin KaFai Lau
644c803475fSMartin KaFai Lau /* setup ns_dst tc progs */
645c803475fSMartin KaFai Lau nstoken = open_netns(NS_DST);
646c803475fSMartin KaFai Lau if (!ASSERT_OK_PTR(nstoken, "setns " NS_DST))
647c803475fSMartin KaFai Lau return -1;
64872f1ba02SDaniel Borkmann /* tc qdisc add dev dst clsact */
64972f1ba02SDaniel Borkmann QDISC_CLSACT_CREATE(&qdisc_dst, setup_result->ifindex_dst);
65072f1ba02SDaniel Borkmann /* tc filter add dev dst ingress bpf da ingress_host */
65172f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_dst, BPF_TC_INGRESS, skel->progs.ingress_host, 0);
65272f1ba02SDaniel Borkmann /* tc filter add dev dst egress bpf da egress_host */
65372f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_dst, BPF_TC_EGRESS, skel->progs.egress_host, 0);
654c803475fSMartin KaFai Lau close_netns(nstoken);
655c803475fSMartin KaFai Lau
656c803475fSMartin KaFai Lau /* setup ns_fwd tc progs */
657c803475fSMartin KaFai Lau nstoken = open_netns(NS_FWD);
658c803475fSMartin KaFai Lau if (!ASSERT_OK_PTR(nstoken, "setns " NS_FWD))
659c803475fSMartin KaFai Lau return -1;
66072f1ba02SDaniel Borkmann /* tc qdisc add dev dst_fwd clsact */
66172f1ba02SDaniel Borkmann QDISC_CLSACT_CREATE(&qdisc_dst_fwd, setup_result->ifindex_dst_fwd);
66272f1ba02SDaniel Borkmann /* tc filter add dev dst_fwd ingress prio 100 bpf da ingress_fwdns_prio100 */
66372f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_dst_fwd, BPF_TC_INGRESS,
66457d0863fSMartin KaFai Lau skel->progs.ingress_fwdns_prio100, 100);
66572f1ba02SDaniel Borkmann /* tc filter add dev dst_fwd ingress prio 101 bpf da ingress_fwdns_prio101 */
66672f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_dst_fwd, BPF_TC_INGRESS,
66757d0863fSMartin KaFai Lau skel->progs.ingress_fwdns_prio101, 101);
66872f1ba02SDaniel Borkmann /* tc filter add dev dst_fwd egress prio 100 bpf da egress_fwdns_prio100 */
66972f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_dst_fwd, BPF_TC_EGRESS,
67057d0863fSMartin KaFai Lau skel->progs.egress_fwdns_prio100, 100);
67172f1ba02SDaniel Borkmann /* tc filter add dev dst_fwd egress prio 101 bpf da egress_fwdns_prio101 */
67272f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_dst_fwd, BPF_TC_EGRESS,
67357d0863fSMartin KaFai Lau skel->progs.egress_fwdns_prio101, 101);
67457d0863fSMartin KaFai Lau
67572f1ba02SDaniel Borkmann /* tc qdisc add dev src_fwd clsact */
67672f1ba02SDaniel Borkmann QDISC_CLSACT_CREATE(&qdisc_src_fwd, setup_result->ifindex_src_fwd);
67772f1ba02SDaniel Borkmann /* tc filter add dev src_fwd ingress prio 100 bpf da ingress_fwdns_prio100 */
67872f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_src_fwd, BPF_TC_INGRESS,
67957d0863fSMartin KaFai Lau skel->progs.ingress_fwdns_prio100, 100);
68072f1ba02SDaniel Borkmann /* tc filter add dev src_fwd ingress prio 101 bpf da ingress_fwdns_prio101 */
68172f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_src_fwd, BPF_TC_INGRESS,
68257d0863fSMartin KaFai Lau skel->progs.ingress_fwdns_prio101, 101);
68372f1ba02SDaniel Borkmann /* tc filter add dev src_fwd egress prio 100 bpf da egress_fwdns_prio100 */
68472f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_src_fwd, BPF_TC_EGRESS,
68557d0863fSMartin KaFai Lau skel->progs.egress_fwdns_prio100, 100);
68672f1ba02SDaniel Borkmann /* tc filter add dev src_fwd egress prio 101 bpf da egress_fwdns_prio101 */
68772f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_src_fwd, BPF_TC_EGRESS,
68857d0863fSMartin KaFai Lau skel->progs.egress_fwdns_prio101, 101);
689c803475fSMartin KaFai Lau close_netns(nstoken);
690c803475fSMartin KaFai Lau return 0;
691c803475fSMartin KaFai Lau
692c803475fSMartin KaFai Lau fail:
693c803475fSMartin KaFai Lau close_netns(nstoken);
69457d0863fSMartin KaFai Lau return err;
695c803475fSMartin KaFai Lau }
696c803475fSMartin KaFai Lau
697c803475fSMartin KaFai Lau enum {
698c803475fSMartin KaFai Lau INGRESS_FWDNS_P100,
699c803475fSMartin KaFai Lau INGRESS_FWDNS_P101,
700c803475fSMartin KaFai Lau EGRESS_FWDNS_P100,
701c803475fSMartin KaFai Lau EGRESS_FWDNS_P101,
702c803475fSMartin KaFai Lau INGRESS_ENDHOST,
703c803475fSMartin KaFai Lau EGRESS_ENDHOST,
704c803475fSMartin KaFai Lau SET_DTIME,
705c803475fSMartin KaFai Lau __MAX_CNT,
706c803475fSMartin KaFai Lau };
707c803475fSMartin KaFai Lau
708c803475fSMartin KaFai Lau const char *cnt_names[] = {
709c803475fSMartin KaFai Lau "ingress_fwdns_p100",
710c803475fSMartin KaFai Lau "ingress_fwdns_p101",
711c803475fSMartin KaFai Lau "egress_fwdns_p100",
712c803475fSMartin KaFai Lau "egress_fwdns_p101",
713c803475fSMartin KaFai Lau "ingress_endhost",
714c803475fSMartin KaFai Lau "egress_endhost",
715c803475fSMartin KaFai Lau "set_dtime",
716c803475fSMartin KaFai Lau };
717c803475fSMartin KaFai Lau
718c803475fSMartin KaFai Lau enum {
719c803475fSMartin KaFai Lau TCP_IP6_CLEAR_DTIME,
720c803475fSMartin KaFai Lau TCP_IP4,
721c803475fSMartin KaFai Lau TCP_IP6,
722c803475fSMartin KaFai Lau UDP_IP4,
723c803475fSMartin KaFai Lau UDP_IP6,
724c803475fSMartin KaFai Lau TCP_IP4_RT_FWD,
725c803475fSMartin KaFai Lau TCP_IP6_RT_FWD,
726c803475fSMartin KaFai Lau UDP_IP4_RT_FWD,
727c803475fSMartin KaFai Lau UDP_IP6_RT_FWD,
728c803475fSMartin KaFai Lau UKN_TEST,
729c803475fSMartin KaFai Lau __NR_TESTS,
730c803475fSMartin KaFai Lau };
731c803475fSMartin KaFai Lau
732c803475fSMartin KaFai Lau const char *test_names[] = {
733c803475fSMartin KaFai Lau "tcp ip6 clear dtime",
734c803475fSMartin KaFai Lau "tcp ip4",
735c803475fSMartin KaFai Lau "tcp ip6",
736c803475fSMartin KaFai Lau "udp ip4",
737c803475fSMartin KaFai Lau "udp ip6",
738c803475fSMartin KaFai Lau "tcp ip4 rt fwd",
739c803475fSMartin KaFai Lau "tcp ip6 rt fwd",
740c803475fSMartin KaFai Lau "udp ip4 rt fwd",
741c803475fSMartin KaFai Lau "udp ip6 rt fwd",
742c803475fSMartin KaFai Lau };
743c803475fSMartin KaFai Lau
dtime_cnt_str(int test,int cnt)744c803475fSMartin KaFai Lau static const char *dtime_cnt_str(int test, int cnt)
745c803475fSMartin KaFai Lau {
746c803475fSMartin KaFai Lau static char name[64];
747c803475fSMartin KaFai Lau
748c803475fSMartin KaFai Lau snprintf(name, sizeof(name), "%s %s", test_names[test], cnt_names[cnt]);
749c803475fSMartin KaFai Lau
750c803475fSMartin KaFai Lau return name;
751c803475fSMartin KaFai Lau }
752c803475fSMartin KaFai Lau
dtime_err_str(int test,int cnt)753c803475fSMartin KaFai Lau static const char *dtime_err_str(int test, int cnt)
754c803475fSMartin KaFai Lau {
755c803475fSMartin KaFai Lau static char name[64];
756c803475fSMartin KaFai Lau
757c803475fSMartin KaFai Lau snprintf(name, sizeof(name), "%s %s errs", test_names[test],
758c803475fSMartin KaFai Lau cnt_names[cnt]);
759c803475fSMartin KaFai Lau
760c803475fSMartin KaFai Lau return name;
761c803475fSMartin KaFai Lau }
762c803475fSMartin KaFai Lau
test_tcp_clear_dtime(struct test_tc_dtime * skel)763c803475fSMartin KaFai Lau static void test_tcp_clear_dtime(struct test_tc_dtime *skel)
764c803475fSMartin KaFai Lau {
765c803475fSMartin KaFai Lau int i, t = TCP_IP6_CLEAR_DTIME;
766c803475fSMartin KaFai Lau __u32 *dtimes = skel->bss->dtimes[t];
767c803475fSMartin KaFai Lau __u32 *errs = skel->bss->errs[t];
768c803475fSMartin KaFai Lau
769c803475fSMartin KaFai Lau skel->bss->test = t;
770e6ff92f4SMartin KaFai Lau test_inet_dtime(AF_INET6, SOCK_STREAM, IP6_DST, 50000 + t);
771c803475fSMartin KaFai Lau
772c803475fSMartin KaFai Lau ASSERT_EQ(dtimes[INGRESS_FWDNS_P100], 0,
773c803475fSMartin KaFai Lau dtime_cnt_str(t, INGRESS_FWDNS_P100));
774c803475fSMartin KaFai Lau ASSERT_EQ(dtimes[INGRESS_FWDNS_P101], 0,
775c803475fSMartin KaFai Lau dtime_cnt_str(t, INGRESS_FWDNS_P101));
776c803475fSMartin KaFai Lau ASSERT_GT(dtimes[EGRESS_FWDNS_P100], 0,
777c803475fSMartin KaFai Lau dtime_cnt_str(t, EGRESS_FWDNS_P100));
778c803475fSMartin KaFai Lau ASSERT_EQ(dtimes[EGRESS_FWDNS_P101], 0,
779c803475fSMartin KaFai Lau dtime_cnt_str(t, EGRESS_FWDNS_P101));
780c803475fSMartin KaFai Lau ASSERT_GT(dtimes[EGRESS_ENDHOST], 0,
781c803475fSMartin KaFai Lau dtime_cnt_str(t, EGRESS_ENDHOST));
782c803475fSMartin KaFai Lau ASSERT_GT(dtimes[INGRESS_ENDHOST], 0,
783c803475fSMartin KaFai Lau dtime_cnt_str(t, INGRESS_ENDHOST));
784c803475fSMartin KaFai Lau
785c803475fSMartin KaFai Lau for (i = INGRESS_FWDNS_P100; i < __MAX_CNT; i++)
786c803475fSMartin KaFai Lau ASSERT_EQ(errs[i], 0, dtime_err_str(t, i));
787c803475fSMartin KaFai Lau }
788c803475fSMartin KaFai Lau
test_tcp_dtime(struct test_tc_dtime * skel,int family,bool bpf_fwd)789c803475fSMartin KaFai Lau static void test_tcp_dtime(struct test_tc_dtime *skel, int family, bool bpf_fwd)
790c803475fSMartin KaFai Lau {
791c803475fSMartin KaFai Lau __u32 *dtimes, *errs;
792c803475fSMartin KaFai Lau const char *addr;
793c803475fSMartin KaFai Lau int i, t;
794c803475fSMartin KaFai Lau
795c803475fSMartin KaFai Lau if (family == AF_INET) {
796c803475fSMartin KaFai Lau t = bpf_fwd ? TCP_IP4 : TCP_IP4_RT_FWD;
797c803475fSMartin KaFai Lau addr = IP4_DST;
798c803475fSMartin KaFai Lau } else {
799c803475fSMartin KaFai Lau t = bpf_fwd ? TCP_IP6 : TCP_IP6_RT_FWD;
800c803475fSMartin KaFai Lau addr = IP6_DST;
801c803475fSMartin KaFai Lau }
802c803475fSMartin KaFai Lau
803c803475fSMartin KaFai Lau dtimes = skel->bss->dtimes[t];
804c803475fSMartin KaFai Lau errs = skel->bss->errs[t];
805c803475fSMartin KaFai Lau
806c803475fSMartin KaFai Lau skel->bss->test = t;
807e6ff92f4SMartin KaFai Lau test_inet_dtime(family, SOCK_STREAM, addr, 50000 + t);
808c803475fSMartin KaFai Lau
809c803475fSMartin KaFai Lau /* fwdns_prio100 prog does not read delivery_time_type, so
810c803475fSMartin KaFai Lau * kernel puts the (rcv) timetamp in __sk_buff->tstamp
811c803475fSMartin KaFai Lau */
812c803475fSMartin KaFai Lau ASSERT_EQ(dtimes[INGRESS_FWDNS_P100], 0,
813c803475fSMartin KaFai Lau dtime_cnt_str(t, INGRESS_FWDNS_P100));
814c803475fSMartin KaFai Lau for (i = INGRESS_FWDNS_P101; i < SET_DTIME; i++)
815c803475fSMartin KaFai Lau ASSERT_GT(dtimes[i], 0, dtime_cnt_str(t, i));
816c803475fSMartin KaFai Lau
817c803475fSMartin KaFai Lau for (i = INGRESS_FWDNS_P100; i < __MAX_CNT; i++)
818c803475fSMartin KaFai Lau ASSERT_EQ(errs[i], 0, dtime_err_str(t, i));
819c803475fSMartin KaFai Lau }
820c803475fSMartin KaFai Lau
test_udp_dtime(struct test_tc_dtime * skel,int family,bool bpf_fwd)821c803475fSMartin KaFai Lau static void test_udp_dtime(struct test_tc_dtime *skel, int family, bool bpf_fwd)
822c803475fSMartin KaFai Lau {
823c803475fSMartin KaFai Lau __u32 *dtimes, *errs;
824c803475fSMartin KaFai Lau const char *addr;
825c803475fSMartin KaFai Lau int i, t;
826c803475fSMartin KaFai Lau
827c803475fSMartin KaFai Lau if (family == AF_INET) {
828c803475fSMartin KaFai Lau t = bpf_fwd ? UDP_IP4 : UDP_IP4_RT_FWD;
829c803475fSMartin KaFai Lau addr = IP4_DST;
830c803475fSMartin KaFai Lau } else {
831c803475fSMartin KaFai Lau t = bpf_fwd ? UDP_IP6 : UDP_IP6_RT_FWD;
832c803475fSMartin KaFai Lau addr = IP6_DST;
833c803475fSMartin KaFai Lau }
834c803475fSMartin KaFai Lau
835c803475fSMartin KaFai Lau dtimes = skel->bss->dtimes[t];
836c803475fSMartin KaFai Lau errs = skel->bss->errs[t];
837c803475fSMartin KaFai Lau
838c803475fSMartin KaFai Lau skel->bss->test = t;
839e6ff92f4SMartin KaFai Lau test_inet_dtime(family, SOCK_DGRAM, addr, 50000 + t);
840c803475fSMartin KaFai Lau
841c803475fSMartin KaFai Lau ASSERT_EQ(dtimes[INGRESS_FWDNS_P100], 0,
842c803475fSMartin KaFai Lau dtime_cnt_str(t, INGRESS_FWDNS_P100));
843c803475fSMartin KaFai Lau /* non mono delivery time is not forwarded */
844c803475fSMartin KaFai Lau ASSERT_EQ(dtimes[INGRESS_FWDNS_P101], 0,
845e6ff92f4SMartin KaFai Lau dtime_cnt_str(t, INGRESS_FWDNS_P101));
846c803475fSMartin KaFai Lau for (i = EGRESS_FWDNS_P100; i < SET_DTIME; i++)
847c803475fSMartin KaFai Lau ASSERT_GT(dtimes[i], 0, dtime_cnt_str(t, i));
848c803475fSMartin KaFai Lau
849c803475fSMartin KaFai Lau for (i = INGRESS_FWDNS_P100; i < __MAX_CNT; i++)
850c803475fSMartin KaFai Lau ASSERT_EQ(errs[i], 0, dtime_err_str(t, i));
851c803475fSMartin KaFai Lau }
852c803475fSMartin KaFai Lau
test_tc_redirect_dtime(struct netns_setup_result * setup_result)853c803475fSMartin KaFai Lau static void test_tc_redirect_dtime(struct netns_setup_result *setup_result)
854c803475fSMartin KaFai Lau {
855c803475fSMartin KaFai Lau struct test_tc_dtime *skel;
856c803475fSMartin KaFai Lau struct nstoken *nstoken;
857*a9800dc6SMartin KaFai Lau int hold_tstamp_fd, err;
858*a9800dc6SMartin KaFai Lau
859*a9800dc6SMartin KaFai Lau /* Hold a sk with the SOCK_TIMESTAMP set to ensure there
860*a9800dc6SMartin KaFai Lau * is no delay in the kernel net_enable_timestamp().
861*a9800dc6SMartin KaFai Lau * This ensures the following tests must have
862*a9800dc6SMartin KaFai Lau * non zero rcv tstamp in the recvmsg().
863*a9800dc6SMartin KaFai Lau */
864*a9800dc6SMartin KaFai Lau hold_tstamp_fd = wait_netstamp_needed_key();
865*a9800dc6SMartin KaFai Lau if (!ASSERT_GE(hold_tstamp_fd, 0, "wait_netstamp_needed_key"))
866*a9800dc6SMartin KaFai Lau return;
867c803475fSMartin KaFai Lau
868c803475fSMartin KaFai Lau skel = test_tc_dtime__open();
869c803475fSMartin KaFai Lau if (!ASSERT_OK_PTR(skel, "test_tc_dtime__open"))
870*a9800dc6SMartin KaFai Lau goto done;
871c803475fSMartin KaFai Lau
87272f1ba02SDaniel Borkmann skel->rodata->IFINDEX_SRC = setup_result->ifindex_src_fwd;
87372f1ba02SDaniel Borkmann skel->rodata->IFINDEX_DST = setup_result->ifindex_dst_fwd;
874c803475fSMartin KaFai Lau
875c803475fSMartin KaFai Lau err = test_tc_dtime__load(skel);
876c803475fSMartin KaFai Lau if (!ASSERT_OK(err, "test_tc_dtime__load"))
877c803475fSMartin KaFai Lau goto done;
878c803475fSMartin KaFai Lau
87957d0863fSMartin KaFai Lau if (netns_load_dtime_bpf(skel, setup_result))
880c803475fSMartin KaFai Lau goto done;
881c803475fSMartin KaFai Lau
882c803475fSMartin KaFai Lau nstoken = open_netns(NS_FWD);
883c803475fSMartin KaFai Lau if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
884c803475fSMartin KaFai Lau goto done;
885c803475fSMartin KaFai Lau err = set_forwarding(false);
886c803475fSMartin KaFai Lau close_netns(nstoken);
887c803475fSMartin KaFai Lau if (!ASSERT_OK(err, "disable forwarding"))
888c803475fSMartin KaFai Lau goto done;
889c803475fSMartin KaFai Lau
890c803475fSMartin KaFai Lau test_tcp_clear_dtime(skel);
891c803475fSMartin KaFai Lau
892c803475fSMartin KaFai Lau test_tcp_dtime(skel, AF_INET, true);
893c803475fSMartin KaFai Lau test_tcp_dtime(skel, AF_INET6, true);
894c803475fSMartin KaFai Lau test_udp_dtime(skel, AF_INET, true);
895c803475fSMartin KaFai Lau test_udp_dtime(skel, AF_INET6, true);
896c803475fSMartin KaFai Lau
897c803475fSMartin KaFai Lau /* Test the kernel ip[6]_forward path instead
898c803475fSMartin KaFai Lau * of bpf_redirect_neigh().
899c803475fSMartin KaFai Lau */
900c803475fSMartin KaFai Lau nstoken = open_netns(NS_FWD);
901c803475fSMartin KaFai Lau if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
902c803475fSMartin KaFai Lau goto done;
903c803475fSMartin KaFai Lau err = set_forwarding(true);
904c803475fSMartin KaFai Lau close_netns(nstoken);
905c803475fSMartin KaFai Lau if (!ASSERT_OK(err, "enable forwarding"))
906c803475fSMartin KaFai Lau goto done;
907c803475fSMartin KaFai Lau
908c803475fSMartin KaFai Lau test_tcp_dtime(skel, AF_INET, false);
909c803475fSMartin KaFai Lau test_tcp_dtime(skel, AF_INET6, false);
910c803475fSMartin KaFai Lau test_udp_dtime(skel, AF_INET, false);
911c803475fSMartin KaFai Lau test_udp_dtime(skel, AF_INET6, false);
912c803475fSMartin KaFai Lau
913c803475fSMartin KaFai Lau done:
914c803475fSMartin KaFai Lau test_tc_dtime__destroy(skel);
915*a9800dc6SMartin KaFai Lau close(hold_tstamp_fd);
916c803475fSMartin KaFai Lau }
917c803475fSMartin KaFai Lau
test_tc_redirect_neigh_fib(struct netns_setup_result * setup_result)918096eccdeSJussi Maki static void test_tc_redirect_neigh_fib(struct netns_setup_result *setup_result)
919096eccdeSJussi Maki {
9206fd5fb63SJussi Maki struct nstoken *nstoken = NULL;
9216fd5fb63SJussi Maki struct test_tc_neigh_fib *skel = NULL;
922096eccdeSJussi Maki
9236fd5fb63SJussi Maki nstoken = open_netns(NS_FWD);
9246fd5fb63SJussi Maki if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
9256fd5fb63SJussi Maki return;
9266fd5fb63SJussi Maki
927096eccdeSJussi Maki skel = test_tc_neigh_fib__open();
928096eccdeSJussi Maki if (!ASSERT_OK_PTR(skel, "test_tc_neigh_fib__open"))
9296fd5fb63SJussi Maki goto done;
930096eccdeSJussi Maki
9316fd5fb63SJussi Maki if (!ASSERT_OK(test_tc_neigh_fib__load(skel), "test_tc_neigh_fib__load"))
9326fd5fb63SJussi Maki goto done;
933096eccdeSJussi Maki
9345dc42a7fSMartin KaFai Lau if (netns_load_bpf(skel->progs.tc_src, skel->progs.tc_dst,
9355dc42a7fSMartin KaFai Lau skel->progs.tc_chk, setup_result))
936096eccdeSJussi Maki goto done;
937096eccdeSJussi Maki
938096eccdeSJussi Maki /* bpf_fib_lookup() checks if forwarding is enabled */
9396fd5fb63SJussi Maki if (!ASSERT_OK(set_forwarding(true), "enable forwarding"))
940096eccdeSJussi Maki goto done;
941096eccdeSJussi Maki
942096eccdeSJussi Maki test_connectivity();
9436fd5fb63SJussi Maki
944096eccdeSJussi Maki done:
9456fd5fb63SJussi Maki if (skel)
946096eccdeSJussi Maki test_tc_neigh_fib__destroy(skel);
9476fd5fb63SJussi Maki close_netns(nstoken);
948096eccdeSJussi Maki }
949096eccdeSJussi Maki
test_tc_redirect_neigh(struct netns_setup_result * setup_result)950096eccdeSJussi Maki static void test_tc_redirect_neigh(struct netns_setup_result *setup_result)
951096eccdeSJussi Maki {
9526fd5fb63SJussi Maki struct nstoken *nstoken = NULL;
9536fd5fb63SJussi Maki struct test_tc_neigh *skel = NULL;
954096eccdeSJussi Maki int err;
955096eccdeSJussi Maki
9566fd5fb63SJussi Maki nstoken = open_netns(NS_FWD);
9576fd5fb63SJussi Maki if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
9586fd5fb63SJussi Maki return;
9596fd5fb63SJussi Maki
960096eccdeSJussi Maki skel = test_tc_neigh__open();
961096eccdeSJussi Maki if (!ASSERT_OK_PTR(skel, "test_tc_neigh__open"))
9626fd5fb63SJussi Maki goto done;
963096eccdeSJussi Maki
96472f1ba02SDaniel Borkmann skel->rodata->IFINDEX_SRC = setup_result->ifindex_src_fwd;
96572f1ba02SDaniel Borkmann skel->rodata->IFINDEX_DST = setup_result->ifindex_dst_fwd;
966096eccdeSJussi Maki
967096eccdeSJussi Maki err = test_tc_neigh__load(skel);
9686fd5fb63SJussi Maki if (!ASSERT_OK(err, "test_tc_neigh__load"))
9696fd5fb63SJussi Maki goto done;
970096eccdeSJussi Maki
9715dc42a7fSMartin KaFai Lau if (netns_load_bpf(skel->progs.tc_src, skel->progs.tc_dst,
9725dc42a7fSMartin KaFai Lau skel->progs.tc_chk, setup_result))
973096eccdeSJussi Maki goto done;
974096eccdeSJussi Maki
9756fd5fb63SJussi Maki if (!ASSERT_OK(set_forwarding(false), "disable forwarding"))
9766fd5fb63SJussi Maki goto done;
9776fd5fb63SJussi Maki
978096eccdeSJussi Maki test_connectivity();
979096eccdeSJussi Maki
980096eccdeSJussi Maki done:
9816fd5fb63SJussi Maki if (skel)
982096eccdeSJussi Maki test_tc_neigh__destroy(skel);
9836fd5fb63SJussi Maki close_netns(nstoken);
984096eccdeSJussi Maki }
985096eccdeSJussi Maki
test_tc_redirect_peer(struct netns_setup_result * setup_result)986096eccdeSJussi Maki static void test_tc_redirect_peer(struct netns_setup_result *setup_result)
987096eccdeSJussi Maki {
9886fd5fb63SJussi Maki struct nstoken *nstoken;
989096eccdeSJussi Maki struct test_tc_peer *skel;
990096eccdeSJussi Maki int err;
991096eccdeSJussi Maki
9926fd5fb63SJussi Maki nstoken = open_netns(NS_FWD);
9936fd5fb63SJussi Maki if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
9946fd5fb63SJussi Maki return;
9956fd5fb63SJussi Maki
996096eccdeSJussi Maki skel = test_tc_peer__open();
997096eccdeSJussi Maki if (!ASSERT_OK_PTR(skel, "test_tc_peer__open"))
9986fd5fb63SJussi Maki goto done;
999096eccdeSJussi Maki
100072f1ba02SDaniel Borkmann skel->rodata->IFINDEX_SRC = setup_result->ifindex_src_fwd;
100172f1ba02SDaniel Borkmann skel->rodata->IFINDEX_DST = setup_result->ifindex_dst_fwd;
1002096eccdeSJussi Maki
1003096eccdeSJussi Maki err = test_tc_peer__load(skel);
10046fd5fb63SJussi Maki if (!ASSERT_OK(err, "test_tc_peer__load"))
10056fd5fb63SJussi Maki goto done;
1006096eccdeSJussi Maki
10075dc42a7fSMartin KaFai Lau if (netns_load_bpf(skel->progs.tc_src, skel->progs.tc_dst,
10085dc42a7fSMartin KaFai Lau skel->progs.tc_chk, setup_result))
1009096eccdeSJussi Maki goto done;
1010096eccdeSJussi Maki
10116fd5fb63SJussi Maki if (!ASSERT_OK(set_forwarding(false), "disable forwarding"))
10126fd5fb63SJussi Maki goto done;
10136fd5fb63SJussi Maki
1014096eccdeSJussi Maki test_connectivity();
1015096eccdeSJussi Maki
1016096eccdeSJussi Maki done:
10176fd5fb63SJussi Maki if (skel)
1018096eccdeSJussi Maki test_tc_peer__destroy(skel);
10196fd5fb63SJussi Maki close_netns(nstoken);
10206fd5fb63SJussi Maki }
10216fd5fb63SJussi Maki
tun_open(char * name)10226fd5fb63SJussi Maki static int tun_open(char *name)
10236fd5fb63SJussi Maki {
10246fd5fb63SJussi Maki struct ifreq ifr;
10256fd5fb63SJussi Maki int fd, err;
10266fd5fb63SJussi Maki
10276fd5fb63SJussi Maki fd = open("/dev/net/tun", O_RDWR);
10286fd5fb63SJussi Maki if (!ASSERT_GE(fd, 0, "open /dev/net/tun"))
10296fd5fb63SJussi Maki return -1;
10306fd5fb63SJussi Maki
10316fd5fb63SJussi Maki memset(&ifr, 0, sizeof(ifr));
10326fd5fb63SJussi Maki
10336fd5fb63SJussi Maki ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
10346fd5fb63SJussi Maki if (*name)
10356fd5fb63SJussi Maki strncpy(ifr.ifr_name, name, IFNAMSIZ);
10366fd5fb63SJussi Maki
10376fd5fb63SJussi Maki err = ioctl(fd, TUNSETIFF, &ifr);
10386fd5fb63SJussi Maki if (!ASSERT_OK(err, "ioctl TUNSETIFF"))
10396fd5fb63SJussi Maki goto fail;
10406fd5fb63SJussi Maki
1041b61987d3SHangbin Liu SYS(fail, "ip link set dev %s up", name);
10426fd5fb63SJussi Maki
10436fd5fb63SJussi Maki return fd;
10446fd5fb63SJussi Maki fail:
10456fd5fb63SJussi Maki close(fd);
10466fd5fb63SJussi Maki return -1;
10476fd5fb63SJussi Maki }
10486fd5fb63SJussi Maki
10496fd5fb63SJussi Maki enum {
10506fd5fb63SJussi Maki SRC_TO_TARGET = 0,
10516fd5fb63SJussi Maki TARGET_TO_SRC = 1,
10526fd5fb63SJussi Maki };
10536fd5fb63SJussi Maki
tun_relay_loop(int src_fd,int target_fd)10546fd5fb63SJussi Maki static int tun_relay_loop(int src_fd, int target_fd)
10556fd5fb63SJussi Maki {
10566fd5fb63SJussi Maki fd_set rfds, wfds;
10576fd5fb63SJussi Maki
10586fd5fb63SJussi Maki FD_ZERO(&rfds);
10596fd5fb63SJussi Maki FD_ZERO(&wfds);
10606fd5fb63SJussi Maki
10616fd5fb63SJussi Maki for (;;) {
10626fd5fb63SJussi Maki char buf[1500];
10636fd5fb63SJussi Maki int direction, nread, nwrite;
10646fd5fb63SJussi Maki
10656fd5fb63SJussi Maki FD_SET(src_fd, &rfds);
10666fd5fb63SJussi Maki FD_SET(target_fd, &rfds);
10676fd5fb63SJussi Maki
10686fd5fb63SJussi Maki if (select(1 + MAX(src_fd, target_fd), &rfds, NULL, NULL, NULL) < 0) {
10696fd5fb63SJussi Maki log_err("select failed");
10706fd5fb63SJussi Maki return 1;
10716fd5fb63SJussi Maki }
10726fd5fb63SJussi Maki
10736fd5fb63SJussi Maki direction = FD_ISSET(src_fd, &rfds) ? SRC_TO_TARGET : TARGET_TO_SRC;
10746fd5fb63SJussi Maki
10756fd5fb63SJussi Maki nread = read(direction == SRC_TO_TARGET ? src_fd : target_fd, buf, sizeof(buf));
10766fd5fb63SJussi Maki if (nread < 0) {
10776fd5fb63SJussi Maki log_err("read failed");
10786fd5fb63SJussi Maki return 1;
10796fd5fb63SJussi Maki }
10806fd5fb63SJussi Maki
10816fd5fb63SJussi Maki nwrite = write(direction == SRC_TO_TARGET ? target_fd : src_fd, buf, nread);
10826fd5fb63SJussi Maki if (nwrite != nread) {
10836fd5fb63SJussi Maki log_err("write failed");
10846fd5fb63SJussi Maki return 1;
10856fd5fb63SJussi Maki }
10866fd5fb63SJussi Maki }
10876fd5fb63SJussi Maki }
10886fd5fb63SJussi Maki
test_tc_redirect_peer_l3(struct netns_setup_result * setup_result)10896fd5fb63SJussi Maki static void test_tc_redirect_peer_l3(struct netns_setup_result *setup_result)
10906fd5fb63SJussi Maki {
1091f1b73577SMartin KaFai Lau LIBBPF_OPTS(bpf_tc_hook, qdisc_tun_fwd);
109272f1ba02SDaniel Borkmann LIBBPF_OPTS(bpf_tc_hook, qdisc_dst_fwd);
10936fd5fb63SJussi Maki struct test_tc_peer *skel = NULL;
10946fd5fb63SJussi Maki struct nstoken *nstoken = NULL;
10956fd5fb63SJussi Maki int err;
10966fd5fb63SJussi Maki int tunnel_pid = -1;
1097ca21a3e5SYonghong Song int src_fd, target_fd = -1;
10986fd5fb63SJussi Maki int ifindex;
10996fd5fb63SJussi Maki
11006fd5fb63SJussi Maki /* Start a L3 TUN/TAP tunnel between the src and dst namespaces.
11016fd5fb63SJussi Maki * This test is using TUN/TAP instead of e.g. IPIP or GRE tunnel as those
11026fd5fb63SJussi Maki * expose the L2 headers encapsulating the IP packet to BPF and hence
11036fd5fb63SJussi Maki * don't have skb in suitable state for this test. Alternative to TUN/TAP
11046fd5fb63SJussi Maki * would be e.g. Wireguard which would appear as a pure L3 device to BPF,
11056fd5fb63SJussi Maki * but that requires much more complicated setup.
11066fd5fb63SJussi Maki */
11076fd5fb63SJussi Maki nstoken = open_netns(NS_SRC);
11086fd5fb63SJussi Maki if (!ASSERT_OK_PTR(nstoken, "setns " NS_SRC))
11096fd5fb63SJussi Maki return;
11106fd5fb63SJussi Maki
11116fd5fb63SJussi Maki src_fd = tun_open("tun_src");
11126fd5fb63SJussi Maki if (!ASSERT_GE(src_fd, 0, "tun_open tun_src"))
11136fd5fb63SJussi Maki goto fail;
11146fd5fb63SJussi Maki
11156fd5fb63SJussi Maki close_netns(nstoken);
11166fd5fb63SJussi Maki
11176fd5fb63SJussi Maki nstoken = open_netns(NS_FWD);
11186fd5fb63SJussi Maki if (!ASSERT_OK_PTR(nstoken, "setns " NS_FWD))
11196fd5fb63SJussi Maki goto fail;
11206fd5fb63SJussi Maki
11216fd5fb63SJussi Maki target_fd = tun_open("tun_fwd");
11226fd5fb63SJussi Maki if (!ASSERT_GE(target_fd, 0, "tun_open tun_fwd"))
11236fd5fb63SJussi Maki goto fail;
11246fd5fb63SJussi Maki
11256fd5fb63SJussi Maki tunnel_pid = fork();
11266fd5fb63SJussi Maki if (!ASSERT_GE(tunnel_pid, 0, "fork tun_relay_loop"))
11276fd5fb63SJussi Maki goto fail;
11286fd5fb63SJussi Maki
11296fd5fb63SJussi Maki if (tunnel_pid == 0)
11306fd5fb63SJussi Maki exit(tun_relay_loop(src_fd, target_fd));
11316fd5fb63SJussi Maki
11326fd5fb63SJussi Maki skel = test_tc_peer__open();
11336fd5fb63SJussi Maki if (!ASSERT_OK_PTR(skel, "test_tc_peer__open"))
11346fd5fb63SJussi Maki goto fail;
11356fd5fb63SJussi Maki
1136052c82dcSMartin KaFai Lau ifindex = if_nametoindex("tun_fwd");
1137052c82dcSMartin KaFai Lau if (!ASSERT_GT(ifindex, 0, "if_indextoname tun_fwd"))
11386fd5fb63SJussi Maki goto fail;
11396fd5fb63SJussi Maki
11406fd5fb63SJussi Maki skel->rodata->IFINDEX_SRC = ifindex;
114172f1ba02SDaniel Borkmann skel->rodata->IFINDEX_DST = setup_result->ifindex_dst_fwd;
11426fd5fb63SJussi Maki
11436fd5fb63SJussi Maki err = test_tc_peer__load(skel);
11446fd5fb63SJussi Maki if (!ASSERT_OK(err, "test_tc_peer__load"))
11456fd5fb63SJussi Maki goto fail;
11466fd5fb63SJussi Maki
11476fd5fb63SJussi Maki /* Load "tc_src_l3" to the tun_fwd interface to redirect packets
11486fd5fb63SJussi Maki * towards dst, and "tc_dst" to redirect packets
114972f1ba02SDaniel Borkmann * and "tc_chk" on dst_fwd to drop non-redirected packets.
11506fd5fb63SJussi Maki */
1151f1b73577SMartin KaFai Lau /* tc qdisc add dev tun_fwd clsact */
1152f1b73577SMartin KaFai Lau QDISC_CLSACT_CREATE(&qdisc_tun_fwd, ifindex);
1153f1b73577SMartin KaFai Lau /* tc filter add dev tun_fwd ingress bpf da tc_src_l3 */
1154f1b73577SMartin KaFai Lau XGRESS_FILTER_ADD(&qdisc_tun_fwd, BPF_TC_INGRESS, skel->progs.tc_src_l3, 0);
11556fd5fb63SJussi Maki
115672f1ba02SDaniel Borkmann /* tc qdisc add dev dst_fwd clsact */
115772f1ba02SDaniel Borkmann QDISC_CLSACT_CREATE(&qdisc_dst_fwd, setup_result->ifindex_dst_fwd);
115872f1ba02SDaniel Borkmann /* tc filter add dev dst_fwd ingress bpf da tc_dst_l3 */
115972f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_dst_fwd, BPF_TC_INGRESS, skel->progs.tc_dst_l3, 0);
116072f1ba02SDaniel Borkmann /* tc filter add dev dst_fwd egress bpf da tc_chk */
116172f1ba02SDaniel Borkmann XGRESS_FILTER_ADD(&qdisc_dst_fwd, BPF_TC_EGRESS, skel->progs.tc_chk, 0);
11626fd5fb63SJussi Maki
11636fd5fb63SJussi Maki /* Setup route and neigh tables */
1164b61987d3SHangbin Liu SYS(fail, "ip -netns " NS_SRC " addr add dev tun_src " IP4_TUN_SRC "/24");
1165b61987d3SHangbin Liu SYS(fail, "ip -netns " NS_FWD " addr add dev tun_fwd " IP4_TUN_FWD "/24");
11666fd5fb63SJussi Maki
1167b61987d3SHangbin Liu SYS(fail, "ip -netns " NS_SRC " addr add dev tun_src " IP6_TUN_SRC "/64 nodad");
1168b61987d3SHangbin Liu SYS(fail, "ip -netns " NS_FWD " addr add dev tun_fwd " IP6_TUN_FWD "/64 nodad");
11696fd5fb63SJussi Maki
117072f1ba02SDaniel Borkmann SYS(fail, "ip -netns " NS_SRC " route del " IP4_DST "/32 dev src scope global");
1171b61987d3SHangbin Liu SYS(fail, "ip -netns " NS_SRC " route add " IP4_DST "/32 via " IP4_TUN_FWD
11726fd5fb63SJussi Maki " dev tun_src scope global");
117372f1ba02SDaniel Borkmann SYS(fail, "ip -netns " NS_DST " route add " IP4_TUN_SRC "/32 dev dst scope global");
117472f1ba02SDaniel Borkmann SYS(fail, "ip -netns " NS_SRC " route del " IP6_DST "/128 dev src scope global");
1175b61987d3SHangbin Liu SYS(fail, "ip -netns " NS_SRC " route add " IP6_DST "/128 via " IP6_TUN_FWD
11766fd5fb63SJussi Maki " dev tun_src scope global");
117772f1ba02SDaniel Borkmann SYS(fail, "ip -netns " NS_DST " route add " IP6_TUN_SRC "/128 dev dst scope global");
11786fd5fb63SJussi Maki
117972f1ba02SDaniel Borkmann SYS(fail, "ip -netns " NS_DST " neigh add " IP4_TUN_SRC " dev dst lladdr " MAC_DST_FWD);
118072f1ba02SDaniel Borkmann SYS(fail, "ip -netns " NS_DST " neigh add " IP6_TUN_SRC " dev dst lladdr " MAC_DST_FWD);
11816fd5fb63SJussi Maki
11826fd5fb63SJussi Maki if (!ASSERT_OK(set_forwarding(false), "disable forwarding"))
11836fd5fb63SJussi Maki goto fail;
11846fd5fb63SJussi Maki
11856fd5fb63SJussi Maki test_connectivity();
11866fd5fb63SJussi Maki
11876fd5fb63SJussi Maki fail:
11886fd5fb63SJussi Maki if (tunnel_pid > 0) {
11896fd5fb63SJussi Maki kill(tunnel_pid, SIGTERM);
11906fd5fb63SJussi Maki waitpid(tunnel_pid, NULL, 0);
11916fd5fb63SJussi Maki }
11926fd5fb63SJussi Maki if (src_fd >= 0)
11936fd5fb63SJussi Maki close(src_fd);
11946fd5fb63SJussi Maki if (target_fd >= 0)
11956fd5fb63SJussi Maki close(target_fd);
11966fd5fb63SJussi Maki if (skel)
11976fd5fb63SJussi Maki test_tc_peer__destroy(skel);
11986fd5fb63SJussi Maki if (nstoken)
11996fd5fb63SJussi Maki close_netns(nstoken);
12006fd5fb63SJussi Maki }
12016fd5fb63SJussi Maki
120272f1ba02SDaniel Borkmann #define RUN_TEST(name, mode) \
12036fd5fb63SJussi Maki ({ \
120472f1ba02SDaniel Borkmann struct netns_setup_result setup_result = { .dev_mode = mode, }; \
12056fd5fb63SJussi Maki if (test__start_subtest(#name)) \
12066fd5fb63SJussi Maki if (ASSERT_OK(netns_setup_namespaces("add"), "setup namespaces")) { \
12076fd5fb63SJussi Maki if (ASSERT_OK(netns_setup_links_and_routes(&setup_result), \
12086fd5fb63SJussi Maki "setup links and routes")) \
12096fd5fb63SJussi Maki test_ ## name(&setup_result); \
12106fd5fb63SJussi Maki netns_setup_namespaces("delete"); \
12116fd5fb63SJussi Maki } \
12126fd5fb63SJussi Maki })
12136fd5fb63SJussi Maki
test_tc_redirect_run_tests(void * arg)12146fd5fb63SJussi Maki static void *test_tc_redirect_run_tests(void *arg)
12156fd5fb63SJussi Maki {
1216e1ef62a4SYucong Sun netns_setup_namespaces_nofail("delete");
1217e1ef62a4SYucong Sun
121872f1ba02SDaniel Borkmann RUN_TEST(tc_redirect_peer, MODE_VETH);
121972f1ba02SDaniel Borkmann RUN_TEST(tc_redirect_peer_l3, MODE_VETH);
122072f1ba02SDaniel Borkmann RUN_TEST(tc_redirect_neigh, MODE_VETH);
122172f1ba02SDaniel Borkmann RUN_TEST(tc_redirect_neigh_fib, MODE_VETH);
122272f1ba02SDaniel Borkmann RUN_TEST(tc_redirect_dtime, MODE_VETH);
12236fd5fb63SJussi Maki return NULL;
1224096eccdeSJussi Maki }
1225096eccdeSJussi Maki
test_tc_redirect(void)12269b6a7773SMartin KaFai Lau void test_tc_redirect(void)
1227096eccdeSJussi Maki {
12286fd5fb63SJussi Maki pthread_t test_thread;
12296fd5fb63SJussi Maki int err;
1230096eccdeSJussi Maki
12316fd5fb63SJussi Maki /* Run the tests in their own thread to isolate the namespace changes
12326fd5fb63SJussi Maki * so they do not affect the environment of other tests.
12336fd5fb63SJussi Maki * (specifically needed because of unshare(CLONE_NEWNS) in open_netns())
12346fd5fb63SJussi Maki */
12356fd5fb63SJussi Maki err = pthread_create(&test_thread, NULL, &test_tc_redirect_run_tests, NULL);
12366fd5fb63SJussi Maki if (ASSERT_OK(err, "pthread_create"))
12376fd5fb63SJussi Maki ASSERT_OK(pthread_join(test_thread, NULL), "pthread_join");
1238096eccdeSJussi Maki }
1239