Skip to content

Commit 43a7c3e

Browse files
yanzhai-cfborkmann
authored andcommitted
selftests/bpf: Add lwt_xmit tests for BPF_REDIRECT
There is no lwt_xmit test case for BPF_REDIRECT yet. Add test cases for both normal and abnormal situations. For abnormal test cases, devices are set down or have its carrier set down. Without proper fixes, BPF_REDIRECT to either ingress or egress of such device would panic the kernel. Signed-off-by: Yan Zhai <yan@cloudflare.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Link: https://lore.kernel.org/bpf/96bf435243641939d9c9da329fab29cb45f7df22.1692326837.git.yan@cloudflare.com
1 parent a171fbe commit 43a7c3e

4 files changed

Lines changed: 560 additions & 0 deletions

File tree

tools/testing/selftests/bpf/config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ CONFIG_CRYPTO_USER_API_HASH=y
1616
CONFIG_DEBUG_INFO=y
1717
CONFIG_DEBUG_INFO_BTF=y
1818
CONFIG_DEBUG_INFO_DWARF4=y
19+
CONFIG_DUMMY=y
1920
CONFIG_DYNAMIC_FTRACE=y
2021
CONFIG_FPROBE=y
2122
CONFIG_FTRACE_SYSCALLS=y
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
3+
#ifndef __LWT_HELPERS_H
4+
#define __LWT_HELPERS_H
5+
6+
#include <time.h>
7+
#include <net/if.h>
8+
#include <linux/if_tun.h>
9+
#include <linux/icmp.h>
10+
11+
#include "test_progs.h"
12+
13+
#define log_err(MSG, ...) \
14+
fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", \
15+
__FILE__, __LINE__, strerror(errno), ##__VA_ARGS__)
16+
17+
#define RUN_TEST(name) \
18+
({ \
19+
if (test__start_subtest(#name)) \
20+
if (ASSERT_OK(netns_create(), "netns_create")) { \
21+
struct nstoken *token = open_netns(NETNS); \
22+
if (ASSERT_OK_PTR(token, "setns")) { \
23+
test_ ## name(); \
24+
close_netns(token); \
25+
} \
26+
netns_delete(); \
27+
} \
28+
})
29+
30+
#define NETNS "ns_lwt"
31+
32+
static inline int netns_create(void)
33+
{
34+
return system("ip netns add " NETNS);
35+
}
36+
37+
static inline int netns_delete(void)
38+
{
39+
return system("ip netns del " NETNS ">/dev/null 2>&1");
40+
}
41+
42+
static int open_tuntap(const char *dev_name, bool need_mac)
43+
{
44+
int err = 0;
45+
struct ifreq ifr;
46+
int fd = open("/dev/net/tun", O_RDWR);
47+
48+
if (!ASSERT_GT(fd, 0, "open(/dev/net/tun)"))
49+
return -1;
50+
51+
ifr.ifr_flags = IFF_NO_PI | (need_mac ? IFF_TAP : IFF_TUN);
52+
memcpy(ifr.ifr_name, dev_name, IFNAMSIZ);
53+
54+
err = ioctl(fd, TUNSETIFF, &ifr);
55+
if (!ASSERT_OK(err, "ioctl(TUNSETIFF)")) {
56+
close(fd);
57+
return -1;
58+
}
59+
60+
err = fcntl(fd, F_SETFL, O_NONBLOCK);
61+
if (!ASSERT_OK(err, "fcntl(O_NONBLOCK)")) {
62+
close(fd);
63+
return -1;
64+
}
65+
66+
return fd;
67+
}
68+
69+
#define ICMP_PAYLOAD_SIZE 100
70+
71+
/* Match an ICMP packet with payload len ICMP_PAYLOAD_SIZE */
72+
static int __expect_icmp_ipv4(char *buf, ssize_t len)
73+
{
74+
struct iphdr *ip = (struct iphdr *)buf;
75+
struct icmphdr *icmp = (struct icmphdr *)(ip + 1);
76+
ssize_t min_header_len = sizeof(*ip) + sizeof(*icmp);
77+
78+
if (len < min_header_len)
79+
return -1;
80+
81+
if (ip->protocol != IPPROTO_ICMP)
82+
return -1;
83+
84+
if (icmp->type != ICMP_ECHO)
85+
return -1;
86+
87+
return len == ICMP_PAYLOAD_SIZE + min_header_len;
88+
}
89+
90+
typedef int (*filter_t) (char *, ssize_t);
91+
92+
/* wait_for_packet - wait for a packet that matches the filter
93+
*
94+
* @fd: tun fd/packet socket to read packet
95+
* @filter: filter function, returning 1 if matches
96+
* @timeout: timeout to wait for the packet
97+
*
98+
* Returns 1 if a matching packet is read, 0 if timeout expired, -1 on error.
99+
*/
100+
static int wait_for_packet(int fd, filter_t filter, struct timeval *timeout)
101+
{
102+
char buf[4096];
103+
int max_retry = 5; /* in case we read some spurious packets */
104+
fd_set fds;
105+
106+
FD_ZERO(&fds);
107+
while (max_retry--) {
108+
/* Linux modifies timeout arg... So make a copy */
109+
struct timeval copied_timeout = *timeout;
110+
ssize_t ret = -1;
111+
112+
FD_SET(fd, &fds);
113+
114+
ret = select(1 + fd, &fds, NULL, NULL, &copied_timeout);
115+
if (ret <= 0) {
116+
if (errno == EINTR)
117+
continue;
118+
else if (errno == EAGAIN || ret == 0)
119+
return 0;
120+
121+
log_err("select failed");
122+
return -1;
123+
}
124+
125+
ret = read(fd, buf, sizeof(buf));
126+
127+
if (ret <= 0) {
128+
log_err("read(dev): %ld", ret);
129+
return -1;
130+
}
131+
132+
if (filter && filter(buf, ret) > 0)
133+
return 1;
134+
}
135+
136+
return 0;
137+
}
138+
139+
#endif /* __LWT_HELPERS_H */

0 commit comments

Comments
 (0)