Skip to content

Commit 8a02a17

Browse files
joestringerAlexei Starovoitov
authored andcommitted
selftests: bpf: Extend sk_assign tests for UDP
Add support for testing UDP sk_assign to the existing tests. Signed-off-by: Joe Stringer <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Acked-by: Lorenz Bauer <[email protected]> Acked-by: Martin KaFai Lau <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 2d7824f commit 8a02a17

File tree

2 files changed

+105
-11
lines changed

2 files changed

+105
-11
lines changed

tools/testing/selftests/bpf/prog_tests/sk_assign.c

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ start_server(const struct sockaddr *addr, socklen_t len, int type)
6969
goto close_out;
7070
if (CHECK_FAIL(bind(fd, addr, len) == -1))
7171
goto close_out;
72-
if (CHECK_FAIL(listen(fd, 128) == -1))
72+
if (type == SOCK_STREAM && CHECK_FAIL(listen(fd, 128) == -1))
7373
goto close_out;
7474

7575
goto out;
@@ -125,6 +125,20 @@ get_port(int fd)
125125
return port;
126126
}
127127

128+
static ssize_t
129+
rcv_msg(int srv_client, int type)
130+
{
131+
struct sockaddr_storage ss;
132+
char buf[BUFSIZ];
133+
socklen_t slen;
134+
135+
if (type == SOCK_STREAM)
136+
return read(srv_client, &buf, sizeof(buf));
137+
else
138+
return recvfrom(srv_client, &buf, sizeof(buf), 0,
139+
(struct sockaddr *)&ss, &slen);
140+
}
141+
128142
static int
129143
run_test(int server_fd, const struct sockaddr *addr, socklen_t len, int type)
130144
{
@@ -139,26 +153,41 @@ run_test(int server_fd, const struct sockaddr *addr, socklen_t len, int type)
139153
goto out;
140154
}
141155

142-
srv_client = accept(server_fd, NULL, NULL);
143-
if (CHECK_FAIL(srv_client == -1)) {
144-
perror("Can't accept connection");
145-
goto out;
156+
if (type == SOCK_STREAM) {
157+
srv_client = accept(server_fd, NULL, NULL);
158+
if (CHECK_FAIL(srv_client == -1)) {
159+
perror("Can't accept connection");
160+
goto out;
161+
}
162+
} else {
163+
srv_client = server_fd;
146164
}
147165
if (CHECK_FAIL(write(client, buf, sizeof(buf)) != sizeof(buf))) {
148166
perror("Can't write on client");
149167
goto out;
150168
}
151-
if (CHECK_FAIL(read(srv_client, &buf, sizeof(buf)) != sizeof(buf))) {
169+
if (CHECK_FAIL(rcv_msg(srv_client, type) != sizeof(buf))) {
152170
perror("Can't read on server");
153171
goto out;
154172
}
155173

156174
port = get_port(srv_client);
157175
if (CHECK_FAIL(!port))
158176
goto out;
159-
if (CHECK(port != htons(CONNECT_PORT), "Expected", "port %u but got %u",
177+
/* SOCK_STREAM is connected via accept(), so the server's local address
178+
* will be the CONNECT_PORT rather than the BIND port that corresponds
179+
* to the listen socket. SOCK_DGRAM on the other hand is connectionless
180+
* so we can't really do the same check there; the server doesn't ever
181+
* create a socket with CONNECT_PORT.
182+
*/
183+
if (type == SOCK_STREAM &&
184+
CHECK(port != htons(CONNECT_PORT), "Expected", "port %u but got %u",
160185
CONNECT_PORT, ntohs(port)))
161186
goto out;
187+
else if (type == SOCK_DGRAM &&
188+
CHECK(port != htons(BIND_PORT), "Expected",
189+
"port %u but got %u", BIND_PORT, ntohs(port)))
190+
goto out;
162191

163192
ret = 0;
164193
out:
@@ -230,6 +259,10 @@ void test_sk_assign(void)
230259
TEST("ipv4 tcp addr redir", AF_INET, SOCK_STREAM, true),
231260
TEST("ipv6 tcp port redir", AF_INET6, SOCK_STREAM, false),
232261
TEST("ipv6 tcp addr redir", AF_INET6, SOCK_STREAM, true),
262+
TEST("ipv4 udp port redir", AF_INET, SOCK_DGRAM, false),
263+
TEST("ipv4 udp addr redir", AF_INET, SOCK_DGRAM, true),
264+
TEST("ipv6 udp port redir", AF_INET6, SOCK_DGRAM, false),
265+
TEST("ipv6 udp addr redir", AF_INET6, SOCK_DGRAM, true),
233266
};
234267
int server = -1;
235268
int self_net;

tools/testing/selftests/bpf/progs/test_sk_assign.c

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ char _license[] SEC("license") = "GPL";
2121

2222
/* Fill 'tuple' with L3 info, and attempt to find L4. On fail, return NULL. */
2323
static inline struct bpf_sock_tuple *
24-
get_tuple(struct __sk_buff *skb, bool *ipv4)
24+
get_tuple(struct __sk_buff *skb, bool *ipv4, bool *tcp)
2525
{
2626
void *data_end = (void *)(long)skb->data_end;
2727
void *data = (void *)(long)skb->data;
@@ -60,12 +60,64 @@ get_tuple(struct __sk_buff *skb, bool *ipv4)
6060
return (struct bpf_sock_tuple *)data;
6161
}
6262

63-
if (result + 1 > data_end || proto != IPPROTO_TCP)
63+
if (proto != IPPROTO_TCP && proto != IPPROTO_UDP)
6464
return NULL;
6565

66+
*tcp = (proto == IPPROTO_TCP);
6667
return result;
6768
}
6869

70+
static inline int
71+
handle_udp(struct __sk_buff *skb, struct bpf_sock_tuple *tuple, bool ipv4)
72+
{
73+
struct bpf_sock_tuple ln = {0};
74+
struct bpf_sock *sk;
75+
size_t tuple_len;
76+
int ret;
77+
78+
tuple_len = ipv4 ? sizeof(tuple->ipv4) : sizeof(tuple->ipv6);
79+
if ((void *)tuple + tuple_len > (void *)(long)skb->data_end)
80+
return TC_ACT_SHOT;
81+
82+
sk = bpf_sk_lookup_udp(skb, tuple, tuple_len, BPF_F_CURRENT_NETNS, 0);
83+
if (sk)
84+
goto assign;
85+
86+
if (ipv4) {
87+
if (tuple->ipv4.dport != bpf_htons(4321))
88+
return TC_ACT_OK;
89+
90+
ln.ipv4.daddr = bpf_htonl(0x7f000001);
91+
ln.ipv4.dport = bpf_htons(1234);
92+
93+
sk = bpf_sk_lookup_udp(skb, &ln, sizeof(ln.ipv4),
94+
BPF_F_CURRENT_NETNS, 0);
95+
} else {
96+
if (tuple->ipv6.dport != bpf_htons(4321))
97+
return TC_ACT_OK;
98+
99+
/* Upper parts of daddr are already zero. */
100+
ln.ipv6.daddr[3] = bpf_htonl(0x1);
101+
ln.ipv6.dport = bpf_htons(1234);
102+
103+
sk = bpf_sk_lookup_udp(skb, &ln, sizeof(ln.ipv6),
104+
BPF_F_CURRENT_NETNS, 0);
105+
}
106+
107+
/* workaround: We can't do a single socket lookup here, because then
108+
* the compiler will likely spill tuple_len to the stack. This makes it
109+
* lose all bounds information in the verifier, which then rejects the
110+
* call as unsafe.
111+
*/
112+
if (!sk)
113+
return TC_ACT_SHOT;
114+
115+
assign:
116+
ret = bpf_sk_assign(skb, sk, 0);
117+
bpf_sk_release(sk);
118+
return ret;
119+
}
120+
69121
static inline int
70122
handle_tcp(struct __sk_buff *skb, struct bpf_sock_tuple *tuple, bool ipv4)
71123
{
@@ -130,14 +182,23 @@ int bpf_sk_assign_test(struct __sk_buff *skb)
130182
{
131183
struct bpf_sock_tuple *tuple, ln = {0};
132184
bool ipv4 = false;
185+
bool tcp = false;
133186
int tuple_len;
134187
int ret = 0;
135188

136-
tuple = get_tuple(skb, &ipv4);
189+
tuple = get_tuple(skb, &ipv4, &tcp);
137190
if (!tuple)
138191
return TC_ACT_SHOT;
139192

140-
ret = handle_tcp(skb, tuple, ipv4);
193+
/* Note that the verifier socket return type for bpf_skc_lookup_tcp()
194+
* differs from bpf_sk_lookup_udp(), so even though the C-level type is
195+
* the same here, if we try to share the implementations they will
196+
* fail to verify because we're crossing pointer types.
197+
*/
198+
if (tcp)
199+
ret = handle_tcp(skb, tuple, ipv4);
200+
else
201+
ret = handle_udp(skb, tuple, ipv4);
141202

142203
return ret == 0 ? TC_ACT_OK : TC_ACT_SHOT;
143204
}

0 commit comments

Comments
 (0)