summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/bpf/prog_tests/ksock.c
blob: dd6b167623d947e35568245e19bb2d43c846337b (plain)
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2026 Isovalent */

#include <arpa/inet.h>

#include "test_progs.h"
#include "network_helpers.h"
#include "ksock_lsm.skel.h"
#include "ksock_lsm_verifier.skel.h"

#define NS_TEST "ksock_lsm_ns"
#define RECV_PORT 7777
#define RECV_TIMEOUT_SEC 5

struct ksock_test_env {
	struct nstoken *nstoken;
	int rfd;
};

static bool ksock_test_env_setup(struct ksock_test_env *env)
{
	struct sockaddr_in addr = {
		.sin_family = AF_INET,
		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
		.sin_port = htons(RECV_PORT),
	};
	struct timeval tv = { .tv_sec = RECV_TIMEOUT_SEC };
	int err;

	memset(env, 0, sizeof(*env));
	env->rfd = -1;

	if (!ASSERT_OK(make_netns(NS_TEST), "make_netns"))
		goto fail;

	env->nstoken = open_netns(NS_TEST);
	if (!ASSERT_OK_PTR(env->nstoken, "open_netns"))
		goto fail;

	env->rfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (!ASSERT_OK_FD(env->rfd, "receiver socket"))
		goto fail;

	err = bind(env->rfd, (struct sockaddr *)&addr, sizeof(addr));
	if (!ASSERT_OK(err, "bind receiver"))
		goto fail;

	err = setsockopt(env->rfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
	if (!ASSERT_OK(err, "set rcvtimeo"))
		goto fail;

	return true;

fail:
	return false;
}

void test_ksock_lsm(void)
{
	LIBBPF_OPTS(bpf_test_run_opts, opts);
	struct ksock_test_env env;
	struct sockaddr_in trigger_addr = {
		.sin_family = AF_INET,
		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
	};
	struct ksock_lsm *skel;
	char recv_data[sizeof(skel->data->send_data)] = {};
	ssize_t n;
	int tfd = -1;
	int err;

	skel = ksock_lsm__open_and_load();
	if (!ASSERT_OK_PTR(skel, "skel open_and_load"))
		return;

	if (!ksock_test_env_setup(&env))
		goto fail;

	/* Step 1: Run the setup SYSCALL prog to create the ksock */
	skel->bss->ipv4_remote = htonl(INADDR_LOOPBACK);
	skel->bss->remote_port = RECV_PORT;
	err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.ksock_setup),
				     &opts);
	if (!ASSERT_OK(err, "ksock_setup run"))
		goto fail;
	if (!ASSERT_OK(opts.retval, "ksock_setup retval"))
		goto fail;

	/* Step 2: Attach LSM prog and trigger socket_bind from userspace */
	skel->links.ksock_socket_bind =
		bpf_program__attach_lsm(skel->progs.ksock_socket_bind);
	if (!ASSERT_OK_PTR(skel->links.ksock_socket_bind,
			   "attach socket_bind lsm"))
		goto fail;

	tfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (!ASSERT_OK_FD(tfd, "trigger socket"))
		goto fail;

	skel->bss->target_pid = getpid();
	err = bind(tfd, (struct sockaddr *)&trigger_addr, sizeof(trigger_addr));
	skel->bss->target_pid = 0;
	if (!ASSERT_OK(err, "trigger bind"))
		goto fail;

	/* Step 3: Verify the LSM hook sent the notification */
	if (!ASSERT_EQ(skel->data->send_ret, sizeof(skel->data->send_data),
		       "LSM send bytes"))
		goto fail;

	n = recvfrom(env.rfd, recv_data, sizeof(recv_data), 0, NULL, NULL);
	if (ASSERT_EQ(n, sizeof(recv_data), "recvfrom len"))
		ASSERT_MEMEQ(recv_data, skel->data->send_data, sizeof(recv_data),
			     "payload match");

fail:
	if (tfd >= 0)
		close(tfd);
	if (env.rfd >= 0)
		close(env.rfd);
	if (env.nstoken)
		close_netns(env.nstoken);
	remove_netns(NS_TEST);
	ksock_lsm__destroy(skel);
}

void test_ksock_lsm_verifier(void)
{
	RUN_TESTS(ksock_lsm_verifier);
}