The file cgroup_tcp_skb.c contains redundant implementations of the similar functions (create_server_sock_v6(), connect_client_server_v6() and get_sock_port_v6()) found in network_helpers.c. Let's eliminate these duplicated functions. Changes from v1: - Remove get_sock_port_v6() as well. v1: https://lore.kernel.org/all/20230807193840.567962-1-thinker.li@gmail.com/ Signed-off-by: Kui-Feng Lee <thinker.li@gmail.com> Link: https://lore.kernel.org/r/20230808162858.326871-1-thinker.li@gmail.com Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
344 lines
9.3 KiB
C
344 lines
9.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (c) 2023 Facebook */
|
|
#include <test_progs.h>
|
|
#include <linux/in6.h>
|
|
#include <sys/socket.h>
|
|
#include <sched.h>
|
|
#include <unistd.h>
|
|
#include "cgroup_helpers.h"
|
|
#include "testing_helpers.h"
|
|
#include "cgroup_tcp_skb.skel.h"
|
|
#include "cgroup_tcp_skb.h"
|
|
#include "network_helpers.h"
|
|
|
|
#define CGROUP_TCP_SKB_PATH "/test_cgroup_tcp_skb"
|
|
|
|
static int install_filters(int cgroup_fd,
|
|
struct bpf_link **egress_link,
|
|
struct bpf_link **ingress_link,
|
|
struct bpf_program *egress_prog,
|
|
struct bpf_program *ingress_prog,
|
|
struct cgroup_tcp_skb *skel)
|
|
{
|
|
/* Prepare filters */
|
|
skel->bss->g_sock_state = 0;
|
|
skel->bss->g_unexpected = 0;
|
|
*egress_link =
|
|
bpf_program__attach_cgroup(egress_prog,
|
|
cgroup_fd);
|
|
if (!ASSERT_OK_PTR(egress_link, "egress_link"))
|
|
return -1;
|
|
*ingress_link =
|
|
bpf_program__attach_cgroup(ingress_prog,
|
|
cgroup_fd);
|
|
if (!ASSERT_OK_PTR(ingress_link, "ingress_link"))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void uninstall_filters(struct bpf_link **egress_link,
|
|
struct bpf_link **ingress_link)
|
|
{
|
|
bpf_link__destroy(*egress_link);
|
|
*egress_link = NULL;
|
|
bpf_link__destroy(*ingress_link);
|
|
*ingress_link = NULL;
|
|
}
|
|
|
|
static int create_client_sock_v6(void)
|
|
{
|
|
int fd;
|
|
|
|
fd = socket(AF_INET6, SOCK_STREAM, 0);
|
|
if (fd < 0) {
|
|
perror("socket");
|
|
return -1;
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
/* Connect to the server in a cgroup from the outside of the cgroup. */
|
|
static int talk_to_cgroup(int *client_fd, int *listen_fd, int *service_fd,
|
|
struct cgroup_tcp_skb *skel)
|
|
{
|
|
int err, cp;
|
|
char buf[5];
|
|
int port;
|
|
|
|
/* Create client & server socket */
|
|
err = join_root_cgroup();
|
|
if (!ASSERT_OK(err, "join_root_cgroup"))
|
|
return -1;
|
|
*client_fd = create_client_sock_v6();
|
|
if (!ASSERT_GE(*client_fd, 0, "client_fd"))
|
|
return -1;
|
|
err = join_cgroup(CGROUP_TCP_SKB_PATH);
|
|
if (!ASSERT_OK(err, "join_cgroup"))
|
|
return -1;
|
|
*listen_fd = start_server(AF_INET6, SOCK_STREAM, NULL, 0, 0);
|
|
if (!ASSERT_GE(*listen_fd, 0, "listen_fd"))
|
|
return -1;
|
|
port = get_socket_local_port(*listen_fd);
|
|
if (!ASSERT_GE(port, 0, "get_socket_local_port"))
|
|
return -1;
|
|
skel->bss->g_sock_port = ntohs(port);
|
|
|
|
/* Connect client to server */
|
|
err = connect_fd_to_fd(*client_fd, *listen_fd, 0);
|
|
if (!ASSERT_OK(err, "connect_fd_to_fd"))
|
|
return -1;
|
|
*service_fd = accept(*listen_fd, NULL, NULL);
|
|
if (!ASSERT_GE(*service_fd, 0, "service_fd"))
|
|
return -1;
|
|
err = join_root_cgroup();
|
|
if (!ASSERT_OK(err, "join_root_cgroup"))
|
|
return -1;
|
|
cp = write(*client_fd, "hello", 5);
|
|
if (!ASSERT_EQ(cp, 5, "write"))
|
|
return -1;
|
|
cp = read(*service_fd, buf, 5);
|
|
if (!ASSERT_EQ(cp, 5, "read"))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Connect to the server out of a cgroup from inside the cgroup. */
|
|
static int talk_to_outside(int *client_fd, int *listen_fd, int *service_fd,
|
|
struct cgroup_tcp_skb *skel)
|
|
|
|
{
|
|
int err, cp;
|
|
char buf[5];
|
|
int port;
|
|
|
|
/* Create client & server socket */
|
|
err = join_root_cgroup();
|
|
if (!ASSERT_OK(err, "join_root_cgroup"))
|
|
return -1;
|
|
*listen_fd = start_server(AF_INET6, SOCK_STREAM, NULL, 0, 0);
|
|
if (!ASSERT_GE(*listen_fd, 0, "listen_fd"))
|
|
return -1;
|
|
err = join_cgroup(CGROUP_TCP_SKB_PATH);
|
|
if (!ASSERT_OK(err, "join_cgroup"))
|
|
return -1;
|
|
*client_fd = create_client_sock_v6();
|
|
if (!ASSERT_GE(*client_fd, 0, "client_fd"))
|
|
return -1;
|
|
err = join_root_cgroup();
|
|
if (!ASSERT_OK(err, "join_root_cgroup"))
|
|
return -1;
|
|
port = get_socket_local_port(*listen_fd);
|
|
if (!ASSERT_GE(port, 0, "get_socket_local_port"))
|
|
return -1;
|
|
skel->bss->g_sock_port = ntohs(port);
|
|
|
|
/* Connect client to server */
|
|
err = connect_fd_to_fd(*client_fd, *listen_fd, 0);
|
|
if (!ASSERT_OK(err, "connect_fd_to_fd"))
|
|
return -1;
|
|
*service_fd = accept(*listen_fd, NULL, NULL);
|
|
if (!ASSERT_GE(*service_fd, 0, "service_fd"))
|
|
return -1;
|
|
cp = write(*client_fd, "hello", 5);
|
|
if (!ASSERT_EQ(cp, 5, "write"))
|
|
return -1;
|
|
cp = read(*service_fd, buf, 5);
|
|
if (!ASSERT_EQ(cp, 5, "read"))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int close_connection(int *closing_fd, int *peer_fd, int *listen_fd,
|
|
struct cgroup_tcp_skb *skel)
|
|
{
|
|
__u32 saved_packet_count = 0;
|
|
int err;
|
|
int i;
|
|
|
|
/* Wait for ACKs to be sent */
|
|
saved_packet_count = skel->bss->g_packet_count;
|
|
usleep(100000); /* 0.1s */
|
|
for (i = 0;
|
|
skel->bss->g_packet_count != saved_packet_count && i < 10;
|
|
i++) {
|
|
saved_packet_count = skel->bss->g_packet_count;
|
|
usleep(100000); /* 0.1s */
|
|
}
|
|
if (!ASSERT_EQ(skel->bss->g_packet_count, saved_packet_count,
|
|
"packet_count"))
|
|
return -1;
|
|
|
|
skel->bss->g_packet_count = 0;
|
|
saved_packet_count = 0;
|
|
|
|
/* Half shutdown to make sure the closing socket having a chance to
|
|
* receive a FIN from the peer.
|
|
*/
|
|
err = shutdown(*closing_fd, SHUT_WR);
|
|
if (!ASSERT_OK(err, "shutdown closing_fd"))
|
|
return -1;
|
|
|
|
/* Wait for FIN and the ACK of the FIN to be observed */
|
|
for (i = 0;
|
|
skel->bss->g_packet_count < saved_packet_count + 2 && i < 10;
|
|
i++)
|
|
usleep(100000); /* 0.1s */
|
|
if (!ASSERT_GE(skel->bss->g_packet_count, saved_packet_count + 2,
|
|
"packet_count"))
|
|
return -1;
|
|
|
|
saved_packet_count = skel->bss->g_packet_count;
|
|
|
|
/* Fully shutdown the connection */
|
|
err = close(*peer_fd);
|
|
if (!ASSERT_OK(err, "close peer_fd"))
|
|
return -1;
|
|
*peer_fd = -1;
|
|
|
|
/* Wait for FIN and the ACK of the FIN to be observed */
|
|
for (i = 0;
|
|
skel->bss->g_packet_count < saved_packet_count + 2 && i < 10;
|
|
i++)
|
|
usleep(100000); /* 0.1s */
|
|
if (!ASSERT_GE(skel->bss->g_packet_count, saved_packet_count + 2,
|
|
"packet_count"))
|
|
return -1;
|
|
|
|
err = close(*closing_fd);
|
|
if (!ASSERT_OK(err, "close closing_fd"))
|
|
return -1;
|
|
*closing_fd = -1;
|
|
|
|
close(*listen_fd);
|
|
*listen_fd = -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This test case includes four scenarios:
|
|
* 1. Connect to the server from outside the cgroup and close the connection
|
|
* from outside the cgroup.
|
|
* 2. Connect to the server from outside the cgroup and close the connection
|
|
* from inside the cgroup.
|
|
* 3. Connect to the server from inside the cgroup and close the connection
|
|
* from outside the cgroup.
|
|
* 4. Connect to the server from inside the cgroup and close the connection
|
|
* from inside the cgroup.
|
|
*
|
|
* The test case is to verify that cgroup_skb/{egress,ingress} filters
|
|
* receive expected packets including SYN, SYN/ACK, ACK, FIN, and FIN/ACK.
|
|
*/
|
|
void test_cgroup_tcp_skb(void)
|
|
{
|
|
struct bpf_link *ingress_link = NULL;
|
|
struct bpf_link *egress_link = NULL;
|
|
int client_fd = -1, listen_fd = -1;
|
|
struct cgroup_tcp_skb *skel;
|
|
int service_fd = -1;
|
|
int cgroup_fd = -1;
|
|
int err;
|
|
|
|
skel = cgroup_tcp_skb__open_and_load();
|
|
if (!ASSERT_OK(!skel, "skel_open_load"))
|
|
return;
|
|
|
|
err = setup_cgroup_environment();
|
|
if (!ASSERT_OK(err, "setup_cgroup_environment"))
|
|
goto cleanup;
|
|
|
|
cgroup_fd = create_and_get_cgroup(CGROUP_TCP_SKB_PATH);
|
|
if (!ASSERT_GE(cgroup_fd, 0, "cgroup_fd"))
|
|
goto cleanup;
|
|
|
|
/* Scenario 1 */
|
|
err = install_filters(cgroup_fd, &egress_link, &ingress_link,
|
|
skel->progs.server_egress,
|
|
skel->progs.server_ingress,
|
|
skel);
|
|
if (!ASSERT_OK(err, "install_filters"))
|
|
goto cleanup;
|
|
|
|
err = talk_to_cgroup(&client_fd, &listen_fd, &service_fd, skel);
|
|
if (!ASSERT_OK(err, "talk_to_cgroup"))
|
|
goto cleanup;
|
|
|
|
err = close_connection(&client_fd, &service_fd, &listen_fd, skel);
|
|
if (!ASSERT_OK(err, "close_connection"))
|
|
goto cleanup;
|
|
|
|
ASSERT_EQ(skel->bss->g_unexpected, 0, "g_unexpected");
|
|
ASSERT_EQ(skel->bss->g_sock_state, CLOSED, "g_sock_state");
|
|
|
|
uninstall_filters(&egress_link, &ingress_link);
|
|
|
|
/* Scenario 2 */
|
|
err = install_filters(cgroup_fd, &egress_link, &ingress_link,
|
|
skel->progs.server_egress_srv,
|
|
skel->progs.server_ingress_srv,
|
|
skel);
|
|
|
|
err = talk_to_cgroup(&client_fd, &listen_fd, &service_fd, skel);
|
|
if (!ASSERT_OK(err, "talk_to_cgroup"))
|
|
goto cleanup;
|
|
|
|
err = close_connection(&service_fd, &client_fd, &listen_fd, skel);
|
|
if (!ASSERT_OK(err, "close_connection"))
|
|
goto cleanup;
|
|
|
|
ASSERT_EQ(skel->bss->g_unexpected, 0, "g_unexpected");
|
|
ASSERT_EQ(skel->bss->g_sock_state, TIME_WAIT, "g_sock_state");
|
|
|
|
uninstall_filters(&egress_link, &ingress_link);
|
|
|
|
/* Scenario 3 */
|
|
err = install_filters(cgroup_fd, &egress_link, &ingress_link,
|
|
skel->progs.client_egress_srv,
|
|
skel->progs.client_ingress_srv,
|
|
skel);
|
|
|
|
err = talk_to_outside(&client_fd, &listen_fd, &service_fd, skel);
|
|
if (!ASSERT_OK(err, "talk_to_outside"))
|
|
goto cleanup;
|
|
|
|
err = close_connection(&service_fd, &client_fd, &listen_fd, skel);
|
|
if (!ASSERT_OK(err, "close_connection"))
|
|
goto cleanup;
|
|
|
|
ASSERT_EQ(skel->bss->g_unexpected, 0, "g_unexpected");
|
|
ASSERT_EQ(skel->bss->g_sock_state, CLOSED, "g_sock_state");
|
|
|
|
uninstall_filters(&egress_link, &ingress_link);
|
|
|
|
/* Scenario 4 */
|
|
err = install_filters(cgroup_fd, &egress_link, &ingress_link,
|
|
skel->progs.client_egress,
|
|
skel->progs.client_ingress,
|
|
skel);
|
|
|
|
err = talk_to_outside(&client_fd, &listen_fd, &service_fd, skel);
|
|
if (!ASSERT_OK(err, "talk_to_outside"))
|
|
goto cleanup;
|
|
|
|
err = close_connection(&client_fd, &service_fd, &listen_fd, skel);
|
|
if (!ASSERT_OK(err, "close_connection"))
|
|
goto cleanup;
|
|
|
|
ASSERT_EQ(skel->bss->g_unexpected, 0, "g_unexpected");
|
|
ASSERT_EQ(skel->bss->g_sock_state, TIME_WAIT, "g_sock_state");
|
|
|
|
uninstall_filters(&egress_link, &ingress_link);
|
|
|
|
cleanup:
|
|
close(client_fd);
|
|
close(listen_fd);
|
|
close(service_fd);
|
|
close(cgroup_fd);
|
|
bpf_link__destroy(egress_link);
|
|
bpf_link__destroy(ingress_link);
|
|
cleanup_cgroup_environment();
|
|
cgroup_tcp_skb__destroy(skel);
|
|
}
|