selftests: bpf: Extend sk_assign tests for UDP
Add support for testing UDP sk_assign to the existing tests. Signed-off-by: Joe Stringer <joe@wand.net.nz> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Lorenz Bauer <lmb@cloudflare.com> Acked-by: Martin KaFai Lau <kafai@fb.com> Link: https://lore.kernel.org/bpf/20200329225342.16317-6-joe@wand.net.nz
This commit is contained in:
parent
2d7824ffd2
commit
8a02a17036
@ -69,7 +69,7 @@ start_server(const struct sockaddr *addr, socklen_t len, int type)
|
|||||||
goto close_out;
|
goto close_out;
|
||||||
if (CHECK_FAIL(bind(fd, addr, len) == -1))
|
if (CHECK_FAIL(bind(fd, addr, len) == -1))
|
||||||
goto close_out;
|
goto close_out;
|
||||||
if (CHECK_FAIL(listen(fd, 128) == -1))
|
if (type == SOCK_STREAM && CHECK_FAIL(listen(fd, 128) == -1))
|
||||||
goto close_out;
|
goto close_out;
|
||||||
|
|
||||||
goto out;
|
goto out;
|
||||||
@ -125,6 +125,20 @@ get_port(int fd)
|
|||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
rcv_msg(int srv_client, int type)
|
||||||
|
{
|
||||||
|
struct sockaddr_storage ss;
|
||||||
|
char buf[BUFSIZ];
|
||||||
|
socklen_t slen;
|
||||||
|
|
||||||
|
if (type == SOCK_STREAM)
|
||||||
|
return read(srv_client, &buf, sizeof(buf));
|
||||||
|
else
|
||||||
|
return recvfrom(srv_client, &buf, sizeof(buf), 0,
|
||||||
|
(struct sockaddr *)&ss, &slen);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
run_test(int server_fd, const struct sockaddr *addr, socklen_t len, int type)
|
run_test(int server_fd, const struct sockaddr *addr, socklen_t len, int type)
|
||||||
{
|
{
|
||||||
@ -139,16 +153,20 @@ run_test(int server_fd, const struct sockaddr *addr, socklen_t len, int type)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
srv_client = accept(server_fd, NULL, NULL);
|
if (type == SOCK_STREAM) {
|
||||||
if (CHECK_FAIL(srv_client == -1)) {
|
srv_client = accept(server_fd, NULL, NULL);
|
||||||
perror("Can't accept connection");
|
if (CHECK_FAIL(srv_client == -1)) {
|
||||||
goto out;
|
perror("Can't accept connection");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
srv_client = server_fd;
|
||||||
}
|
}
|
||||||
if (CHECK_FAIL(write(client, buf, sizeof(buf)) != sizeof(buf))) {
|
if (CHECK_FAIL(write(client, buf, sizeof(buf)) != sizeof(buf))) {
|
||||||
perror("Can't write on client");
|
perror("Can't write on client");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (CHECK_FAIL(read(srv_client, &buf, sizeof(buf)) != sizeof(buf))) {
|
if (CHECK_FAIL(rcv_msg(srv_client, type) != sizeof(buf))) {
|
||||||
perror("Can't read on server");
|
perror("Can't read on server");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -156,9 +174,20 @@ run_test(int server_fd, const struct sockaddr *addr, socklen_t len, int type)
|
|||||||
port = get_port(srv_client);
|
port = get_port(srv_client);
|
||||||
if (CHECK_FAIL(!port))
|
if (CHECK_FAIL(!port))
|
||||||
goto out;
|
goto out;
|
||||||
if (CHECK(port != htons(CONNECT_PORT), "Expected", "port %u but got %u",
|
/* SOCK_STREAM is connected via accept(), so the server's local address
|
||||||
|
* will be the CONNECT_PORT rather than the BIND port that corresponds
|
||||||
|
* to the listen socket. SOCK_DGRAM on the other hand is connectionless
|
||||||
|
* so we can't really do the same check there; the server doesn't ever
|
||||||
|
* create a socket with CONNECT_PORT.
|
||||||
|
*/
|
||||||
|
if (type == SOCK_STREAM &&
|
||||||
|
CHECK(port != htons(CONNECT_PORT), "Expected", "port %u but got %u",
|
||||||
CONNECT_PORT, ntohs(port)))
|
CONNECT_PORT, ntohs(port)))
|
||||||
goto out;
|
goto out;
|
||||||
|
else if (type == SOCK_DGRAM &&
|
||||||
|
CHECK(port != htons(BIND_PORT), "Expected",
|
||||||
|
"port %u but got %u", BIND_PORT, ntohs(port)))
|
||||||
|
goto out;
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
out:
|
out:
|
||||||
@ -230,6 +259,10 @@ void test_sk_assign(void)
|
|||||||
TEST("ipv4 tcp addr redir", AF_INET, SOCK_STREAM, true),
|
TEST("ipv4 tcp addr redir", AF_INET, SOCK_STREAM, true),
|
||||||
TEST("ipv6 tcp port redir", AF_INET6, SOCK_STREAM, false),
|
TEST("ipv6 tcp port redir", AF_INET6, SOCK_STREAM, false),
|
||||||
TEST("ipv6 tcp addr redir", AF_INET6, SOCK_STREAM, true),
|
TEST("ipv6 tcp addr redir", AF_INET6, SOCK_STREAM, true),
|
||||||
|
TEST("ipv4 udp port redir", AF_INET, SOCK_DGRAM, false),
|
||||||
|
TEST("ipv4 udp addr redir", AF_INET, SOCK_DGRAM, true),
|
||||||
|
TEST("ipv6 udp port redir", AF_INET6, SOCK_DGRAM, false),
|
||||||
|
TEST("ipv6 udp addr redir", AF_INET6, SOCK_DGRAM, true),
|
||||||
};
|
};
|
||||||
int server = -1;
|
int server = -1;
|
||||||
int self_net;
|
int self_net;
|
||||||
|
@ -21,7 +21,7 @@ char _license[] SEC("license") = "GPL";
|
|||||||
|
|
||||||
/* Fill 'tuple' with L3 info, and attempt to find L4. On fail, return NULL. */
|
/* Fill 'tuple' with L3 info, and attempt to find L4. On fail, return NULL. */
|
||||||
static inline struct bpf_sock_tuple *
|
static inline struct bpf_sock_tuple *
|
||||||
get_tuple(struct __sk_buff *skb, bool *ipv4)
|
get_tuple(struct __sk_buff *skb, bool *ipv4, bool *tcp)
|
||||||
{
|
{
|
||||||
void *data_end = (void *)(long)skb->data_end;
|
void *data_end = (void *)(long)skb->data_end;
|
||||||
void *data = (void *)(long)skb->data;
|
void *data = (void *)(long)skb->data;
|
||||||
@ -60,12 +60,64 @@ get_tuple(struct __sk_buff *skb, bool *ipv4)
|
|||||||
return (struct bpf_sock_tuple *)data;
|
return (struct bpf_sock_tuple *)data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result + 1 > data_end || proto != IPPROTO_TCP)
|
if (proto != IPPROTO_TCP && proto != IPPROTO_UDP)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
*tcp = (proto == IPPROTO_TCP);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
handle_udp(struct __sk_buff *skb, struct bpf_sock_tuple *tuple, bool ipv4)
|
||||||
|
{
|
||||||
|
struct bpf_sock_tuple ln = {0};
|
||||||
|
struct bpf_sock *sk;
|
||||||
|
size_t tuple_len;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
tuple_len = ipv4 ? sizeof(tuple->ipv4) : sizeof(tuple->ipv6);
|
||||||
|
if ((void *)tuple + tuple_len > (void *)(long)skb->data_end)
|
||||||
|
return TC_ACT_SHOT;
|
||||||
|
|
||||||
|
sk = bpf_sk_lookup_udp(skb, tuple, tuple_len, BPF_F_CURRENT_NETNS, 0);
|
||||||
|
if (sk)
|
||||||
|
goto assign;
|
||||||
|
|
||||||
|
if (ipv4) {
|
||||||
|
if (tuple->ipv4.dport != bpf_htons(4321))
|
||||||
|
return TC_ACT_OK;
|
||||||
|
|
||||||
|
ln.ipv4.daddr = bpf_htonl(0x7f000001);
|
||||||
|
ln.ipv4.dport = bpf_htons(1234);
|
||||||
|
|
||||||
|
sk = bpf_sk_lookup_udp(skb, &ln, sizeof(ln.ipv4),
|
||||||
|
BPF_F_CURRENT_NETNS, 0);
|
||||||
|
} else {
|
||||||
|
if (tuple->ipv6.dport != bpf_htons(4321))
|
||||||
|
return TC_ACT_OK;
|
||||||
|
|
||||||
|
/* Upper parts of daddr are already zero. */
|
||||||
|
ln.ipv6.daddr[3] = bpf_htonl(0x1);
|
||||||
|
ln.ipv6.dport = bpf_htons(1234);
|
||||||
|
|
||||||
|
sk = bpf_sk_lookup_udp(skb, &ln, sizeof(ln.ipv6),
|
||||||
|
BPF_F_CURRENT_NETNS, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* workaround: We can't do a single socket lookup here, because then
|
||||||
|
* the compiler will likely spill tuple_len to the stack. This makes it
|
||||||
|
* lose all bounds information in the verifier, which then rejects the
|
||||||
|
* call as unsafe.
|
||||||
|
*/
|
||||||
|
if (!sk)
|
||||||
|
return TC_ACT_SHOT;
|
||||||
|
|
||||||
|
assign:
|
||||||
|
ret = bpf_sk_assign(skb, sk, 0);
|
||||||
|
bpf_sk_release(sk);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
handle_tcp(struct __sk_buff *skb, struct bpf_sock_tuple *tuple, bool ipv4)
|
handle_tcp(struct __sk_buff *skb, struct bpf_sock_tuple *tuple, bool ipv4)
|
||||||
{
|
{
|
||||||
@ -130,14 +182,23 @@ int bpf_sk_assign_test(struct __sk_buff *skb)
|
|||||||
{
|
{
|
||||||
struct bpf_sock_tuple *tuple, ln = {0};
|
struct bpf_sock_tuple *tuple, ln = {0};
|
||||||
bool ipv4 = false;
|
bool ipv4 = false;
|
||||||
|
bool tcp = false;
|
||||||
int tuple_len;
|
int tuple_len;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
tuple = get_tuple(skb, &ipv4);
|
tuple = get_tuple(skb, &ipv4, &tcp);
|
||||||
if (!tuple)
|
if (!tuple)
|
||||||
return TC_ACT_SHOT;
|
return TC_ACT_SHOT;
|
||||||
|
|
||||||
ret = handle_tcp(skb, tuple, ipv4);
|
/* Note that the verifier socket return type for bpf_skc_lookup_tcp()
|
||||||
|
* differs from bpf_sk_lookup_udp(), so even though the C-level type is
|
||||||
|
* the same here, if we try to share the implementations they will
|
||||||
|
* fail to verify because we're crossing pointer types.
|
||||||
|
*/
|
||||||
|
if (tcp)
|
||||||
|
ret = handle_tcp(skb, tuple, ipv4);
|
||||||
|
else
|
||||||
|
ret = handle_udp(skb, tuple, ipv4);
|
||||||
|
|
||||||
return ret == 0 ? TC_ACT_OK : TC_ACT_SHOT;
|
return ret == 0 ? TC_ACT_OK : TC_ACT_SHOT;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user