summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/bpf/prog_tests/ksock_sendto.c
blob: 498f0e1be6ee1ab74e8cbe4cd8eb017fce2364ff (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2026 Isovalent */

#include "test_progs.h"
#include "network_helpers.h"
#include "ksock_sendto.skel.h"

struct sendto_test_case {
	const char *name;
	bool connected;
	bool use_sendto_addr;
};

static bool recv_payload(int fd, const void *expected, size_t len)
{
	char buf[64];
	ssize_t n;

	n = recv(fd, buf, sizeof(buf), MSG_TRUNC);
	if (!ASSERT_EQ(n, len, "recv length"))
		return false;
	return ASSERT_MEMEQ(buf, expected, len, "recv payload");
}

static void test_sendto_case(const struct sendto_test_case *test)
{
	LIBBPF_OPTS(bpf_test_run_opts, opts);
	struct ksock_sendto *skel;
	int receivers[2] = { -1, -1 };
	int err, i, port, sendto_fd;
	char buf[64];
	ssize_t n;

	skel = ksock_sendto__open_and_load();
	if (!ASSERT_OK_PTR(skel, "ksock_sendto open and load"))
		return;

	/* Use separate receivers for the default peer and explicit destination. */
	for (i = 0; i < ARRAY_SIZE(receivers); i++) {
		receivers[i] = start_server(AF_INET, SOCK_DGRAM, "127.0.0.1", 0, 1000);
		if (!ASSERT_OK_FD(receivers[i], "receiver socket"))
			goto out;
	}

	port = get_socket_local_port(receivers[0]);
	if (!ASSERT_GE(port, 0, "connected port"))
		goto out;
	skel->bss->connected_port = port;
	port = get_socket_local_port(receivers[1]);
	if (!ASSERT_GE(port, 0, "sendto port"))
		goto out;
	skel->bss->sendto_port = port;
	skel->bss->connected = test->connected;
	skel->bss->use_sendto_addr = test->use_sendto_addr;

	err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_sendto), &opts);
	if (!ASSERT_OK(err, "run test_sendto") || !ASSERT_OK(opts.retval, "test_sendto retval"))
		goto out;

	/* Check sendto() outcome */
	if (!ASSERT_EQ(skel->data->sendto_ret, sizeof(skel->data->sendto_data), "sendto bytes"))
		goto out;
	sendto_fd = receivers[test->use_sendto_addr ? 1 : 0];
	if (!recv_payload(sendto_fd, skel->data->sendto_data, sizeof(skel->data->sendto_data)))
		goto out;

	/* Check send() outcome */
	if (test->connected) {
		if (!ASSERT_EQ(skel->data->send_ret, sizeof(skel->data->send_data), "send bytes"))
			goto out;
		if (!recv_payload(receivers[0], skel->data->send_data,
				  sizeof(skel->data->send_data)))
			goto out;
	} else if (!ASSERT_EQ(skel->data->send_ret, -EDESTADDRREQ,
			      "send after sendto remains unconnected")) {
		goto out;
	}

	/* Catch unexpected deliveries to either destination. */
	for (i = 0; i < ARRAY_SIZE(receivers); i++) {
		n = recv(receivers[i], buf, sizeof(buf), MSG_DONTWAIT);
		if (!ASSERT_EQ(n, -1, "no extra datagram") ||
		    !ASSERT_EQ(errno, EAGAIN, "recv errno"))
			goto out;
	}

out:
	for (i = 0; i < ARRAY_SIZE(receivers); i++) {
		if (receivers[i] >= 0)
			close(receivers[i]);
	}
	ksock_sendto__destroy(skel);
}

void test_ksock_sendto(void)
{
	static const struct sendto_test_case tests[] = {
		{ .name = "unconnected", .use_sendto_addr = true },
		{ .name = "connected_null_addr", .connected = true },
		{ .name = "connected_explicit_addr", .connected = true, .use_sendto_addr = true },
	};
	int i;

	for (i = 0; i < ARRAY_SIZE(tests); i++) {
		if (test__start_subtest(tests[i].name))
			test_sendto_case(&tests[i]);
	}
}