root/tools/testing/selftests/bpf/prog_tests/connect_force_port.c
// SPDX-License-Identifier: GPL-2.0

#include <test_progs.h>
#include "cgroup_helpers.h"
#include "network_helpers.h"

static int verify_ports(int family, int fd,
                        __u16 expected_local, __u16 expected_peer)
{
        struct sockaddr_storage addr;
        socklen_t len = sizeof(addr);
        __u16 port;

        if (getsockname(fd, (struct sockaddr *)&addr, &len)) {
                log_err("Failed to get server addr");
                return -1;
        }

        if (family == AF_INET)
                port = ((struct sockaddr_in *)&addr)->sin_port;
        else
                port = ((struct sockaddr_in6 *)&addr)->sin6_port;

        if (ntohs(port) != expected_local) {
                log_err("Unexpected local port %d, expected %d", ntohs(port),
                        expected_local);
                return -1;
        }

        if (getpeername(fd, (struct sockaddr *)&addr, &len)) {
                log_err("Failed to get peer addr");
                return -1;
        }

        if (family == AF_INET)
                port = ((struct sockaddr_in *)&addr)->sin_port;
        else
                port = ((struct sockaddr_in6 *)&addr)->sin6_port;

        if (ntohs(port) != expected_peer) {
                log_err("Unexpected peer port %d, expected %d", ntohs(port),
                        expected_peer);
                return -1;
        }

        return 0;
}

static int run_test(int cgroup_fd, int server_fd, int family, int type)
{
        bool v4 = family == AF_INET;
        __u16 expected_local_port = v4 ? 22222 : 22223;
        __u16 expected_peer_port = 60000;
        struct bpf_program *prog;
        struct bpf_object *obj;
        const char *obj_file = v4 ? "connect_force_port4.bpf.o" : "connect_force_port6.bpf.o";
        int fd, err;
        __u32 duration = 0;

        obj = bpf_object__open_file(obj_file, NULL);
        if (!ASSERT_OK_PTR(obj, "bpf_obj_open"))
                return -1;

        err = bpf_object__load(obj);
        if (!ASSERT_OK(err, "bpf_obj_load")) {
                err = -EIO;
                goto close_bpf_object;
        }

        prog = bpf_object__find_program_by_name(obj, v4 ?
                                                "connect4" :
                                                "connect6");
        if (CHECK(!prog, "find_prog", "connect prog not found\n")) {
                err = -EIO;
                goto close_bpf_object;
        }

        err = bpf_prog_attach(bpf_program__fd(prog), cgroup_fd, v4 ?
                              BPF_CGROUP_INET4_CONNECT :
                              BPF_CGROUP_INET6_CONNECT, 0);
        if (err) {
                log_err("Failed to attach BPF program");
                goto close_bpf_object;
        }

        prog = bpf_object__find_program_by_name(obj, v4 ?
                                                "getpeername4" :
                                                "getpeername6");
        if (CHECK(!prog, "find_prog", "getpeername prog not found\n")) {
                err = -EIO;
                goto close_bpf_object;
        }

        err = bpf_prog_attach(bpf_program__fd(prog), cgroup_fd, v4 ?
                              BPF_CGROUP_INET4_GETPEERNAME :
                              BPF_CGROUP_INET6_GETPEERNAME, 0);
        if (err) {
                log_err("Failed to attach BPF program");
                goto close_bpf_object;
        }

        prog = bpf_object__find_program_by_name(obj, v4 ?
                                                "getsockname4" :
                                                "getsockname6");
        if (CHECK(!prog, "find_prog", "getsockname prog not found\n")) {
                err = -EIO;
                goto close_bpf_object;
        }

        err = bpf_prog_attach(bpf_program__fd(prog), cgroup_fd, v4 ?
                              BPF_CGROUP_INET4_GETSOCKNAME :
                              BPF_CGROUP_INET6_GETSOCKNAME, 0);
        if (err) {
                log_err("Failed to attach BPF program");
                goto close_bpf_object;
        }

        fd = connect_to_fd(server_fd, 0);
        if (fd < 0) {
                err = -1;
                goto close_bpf_object;
        }

        err = verify_ports(family, fd, expected_local_port,
                           expected_peer_port);
        close(fd);

close_bpf_object:
        bpf_object__close(obj);
        return err;
}

void test_connect_force_port(void)
{
        int server_fd, cgroup_fd;

        cgroup_fd = test__join_cgroup("/connect_force_port");
        if (CHECK_FAIL(cgroup_fd < 0))
                return;

        server_fd = start_server(AF_INET, SOCK_STREAM, NULL, 60123, 0);
        if (CHECK_FAIL(server_fd < 0))
                goto close_cgroup_fd;
        CHECK_FAIL(run_test(cgroup_fd, server_fd, AF_INET, SOCK_STREAM));
        close(server_fd);

        server_fd = start_server(AF_INET6, SOCK_STREAM, NULL, 60124, 0);
        if (CHECK_FAIL(server_fd < 0))
                goto close_cgroup_fd;
        CHECK_FAIL(run_test(cgroup_fd, server_fd, AF_INET6, SOCK_STREAM));
        close(server_fd);

        server_fd = start_server(AF_INET, SOCK_DGRAM, NULL, 60123, 0);
        if (CHECK_FAIL(server_fd < 0))
                goto close_cgroup_fd;
        CHECK_FAIL(run_test(cgroup_fd, server_fd, AF_INET, SOCK_DGRAM));
        close(server_fd);

        server_fd = start_server(AF_INET6, SOCK_DGRAM, NULL, 60124, 0);
        if (CHECK_FAIL(server_fd < 0))
                goto close_cgroup_fd;
        CHECK_FAIL(run_test(cgroup_fd, server_fd, AF_INET6, SOCK_DGRAM));
        close(server_fd);

close_cgroup_fd:
        close(cgroup_fd);
}