Commit 64a4658d authored by Daniel T. Lee's avatar Daniel T. Lee Committed by Alexei Starovoitov
Browse files

selftests/bpf: migrate cgroup sock create test for prohibiting sockets



This patch continues the migration and removal process for cgroup
sock_create tests to selftests.

The test being migrated verifies the ability of cgroup BPF to block the
creation of specific types of sockets using a verdict. Specifically, the
test denies socket creation when the socket is of type AF_INET{6},
SOCK_DGRAM, and IPPROTO_ICMP{V6}. If the requested socket type matches
these attributes, the cgroup BPF verdict blocks the socket creation.

As with the previous commit, this test currently lacks coverage in
selftests, so this patch migrates the functionality into the sock_create
tests under selftests. This migration ensures that the socket creation
blocking behavior with cgroup bpf program is properly tested within the
selftest framework.

Signed-off-by: default avatarDaniel T. Lee <danieltimlee@gmail.com>
Link: https://lore.kernel.org/r/20241011044847.51584-3-danieltimlee@gmail.com


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent ec6c4be0
Loading
Loading
Loading
Loading
+0 −3
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ tprogs-y += map_perf_test
tprogs-y += test_overhead
tprogs-y += test_cgrp2_array_pin
tprogs-y += test_cgrp2_attach
tprogs-y += test_cgrp2_sock2
tprogs-y += xdp_router_ipv4
tprogs-y += test_current_task_under_cgroup
tprogs-y += trace_event
@@ -75,7 +74,6 @@ map_perf_test-objs := map_perf_test_user.o
test_overhead-objs := test_overhead_user.o
test_cgrp2_array_pin-objs := test_cgrp2_array_pin.o
test_cgrp2_attach-objs := test_cgrp2_attach.o
test_cgrp2_sock2-objs := test_cgrp2_sock2.o
test_current_task_under_cgroup-objs := $(CGROUP_HELPERS) \
				       test_current_task_under_cgroup_user.o
trace_event-objs := trace_event_user.o $(TRACE_HELPERS)
@@ -106,7 +104,6 @@ always-y += tracex4.bpf.o
always-y += tracex5.bpf.o
always-y += tracex6.bpf.o
always-y += tracex7.bpf.o
always-y += sock_flags.bpf.o
always-y += test_probe_write_user.bpf.o
always-y += trace_output.bpf.o
always-y += tcbpf1_kern.o

samples/bpf/sock_flags.bpf.c

deleted100644 → 0
+0 −47
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
#include "vmlinux.h"
#include "net_shared.h"
#include <bpf/bpf_helpers.h>

SEC("cgroup/sock")
int bpf_prog1(struct bpf_sock *sk)
{
	char fmt[] = "socket: family %d type %d protocol %d\n";
	char fmt2[] = "socket: uid %u gid %u\n";
	__u64 gid_uid = bpf_get_current_uid_gid();
	__u32 uid = gid_uid & 0xffffffff;
	__u32 gid = gid_uid >> 32;

	bpf_trace_printk(fmt, sizeof(fmt), sk->family, sk->type, sk->protocol);
	bpf_trace_printk(fmt2, sizeof(fmt2), uid, gid);

	/* block AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6 sockets
	 * ie., make ping6 fail
	 */
	if (sk->family == AF_INET6 &&
	    sk->type == SOCK_DGRAM   &&
	    sk->protocol == IPPROTO_ICMPV6)
		return 0;

	return 1;
}

SEC("cgroup/sock")
int bpf_prog2(struct bpf_sock *sk)
{
	char fmt[] = "socket: family %d type %d protocol %d\n";

	bpf_trace_printk(fmt, sizeof(fmt), sk->family, sk->type, sk->protocol);

	/* block AF_INET, SOCK_DGRAM, IPPROTO_ICMP sockets
	 * ie., make ping fail
	 */
	if (sk->family == AF_INET &&
	    sk->type == SOCK_DGRAM  &&
	    sk->protocol == IPPROTO_ICMP)
		return 0;

	return 1;
}

char _license[] SEC("license") = "GPL";

samples/bpf/test_cgrp2_sock2.c

deleted100644 → 0
+0 −95
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/* eBPF example program:
 *
 * - Loads eBPF program
 *
 *   The eBPF program loads a filter from file and attaches the
 *   program to a cgroup using BPF_PROG_ATTACH
 */

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <net/if.h>
#include <linux/bpf.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>

#include "bpf_insn.h"

static int usage(const char *argv0)
{
	printf("Usage: %s cg-path filter-path [filter-id]\n", argv0);
	return EXIT_FAILURE;
}

int main(int argc, char **argv)
{
	int cg_fd, err, ret = EXIT_FAILURE, filter_id = 0, prog_cnt = 0;
	const char *link_pin_path = "/sys/fs/bpf/test_cgrp2_sock2";
	struct bpf_link *link = NULL;
	struct bpf_program *progs[2];
	struct bpf_program *prog;
	struct bpf_object *obj;

	if (argc < 3)
		return usage(argv[0]);

	if (argc > 3)
		filter_id = atoi(argv[3]);

	cg_fd = open(argv[1], O_DIRECTORY | O_RDONLY);
	if (cg_fd < 0) {
		printf("Failed to open cgroup path: '%s'\n", strerror(errno));
		return ret;
	}

	obj = bpf_object__open_file(argv[2], NULL);
	if (libbpf_get_error(obj)) {
		printf("ERROR: opening BPF object file failed\n");
		return ret;
	}

	bpf_object__for_each_program(prog, obj) {
		progs[prog_cnt] = prog;
		prog_cnt++;
	}

	if (filter_id >= prog_cnt) {
		printf("Invalid program id; program not found in file\n");
		goto cleanup;
	}

	/* load BPF program */
	if (bpf_object__load(obj)) {
		printf("ERROR: loading BPF object file failed\n");
		goto cleanup;
	}

	link = bpf_program__attach_cgroup(progs[filter_id], cg_fd);
	if (libbpf_get_error(link)) {
		printf("ERROR: bpf_program__attach failed\n");
		link = NULL;
		goto cleanup;
	}

	err = bpf_link__pin(link, link_pin_path);
	if (err < 0) {
		printf("ERROR: bpf_link__pin failed: %d\n", err);
		goto cleanup;
	}

	ret = EXIT_SUCCESS;

cleanup:
	bpf_link__destroy(link);
	bpf_object__close(obj);
	return ret;
}

samples/bpf/test_cgrp2_sock2.sh

deleted100755 → 0
+0 −103
Original line number Diff line number Diff line
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0

BPFFS=/sys/fs/bpf
MY_DIR=$(dirname $0)
TEST=$MY_DIR/test_cgrp2_sock2
LINK_PIN=$BPFFS/test_cgrp2_sock2
BPF_PROG=$MY_DIR/sock_flags.bpf.o

function config_device {
	ip netns add at_ns0
	ip link add veth0 type veth peer name veth0b
	ip link set veth0 netns at_ns0
	ip netns exec at_ns0 sysctl -q net.ipv6.conf.veth0.disable_ipv6=0
	ip netns exec at_ns0 ip addr add 172.16.1.100/24 dev veth0
	ip netns exec at_ns0 ip addr add 2401:db00::1/64 dev veth0 nodad
	ip netns exec at_ns0 ip link set dev veth0 up
	sysctl -q net.ipv6.conf.veth0b.disable_ipv6=0
	ip addr add 172.16.1.101/24 dev veth0b
	ip addr add 2401:db00::2/64 dev veth0b nodad
	ip link set veth0b up
}

function config_cgroup {
	rm -rf /tmp/cgroupv2
	mkdir -p /tmp/cgroupv2
	mount -t cgroup2 none /tmp/cgroupv2
	mkdir -p /tmp/cgroupv2/foo
	echo $$ >> /tmp/cgroupv2/foo/cgroup.procs
}

function config_bpffs {
	if mount | grep $BPFFS > /dev/null; then
		echo "bpffs already mounted"
	else
		echo "bpffs not mounted. Mounting..."
		mount -t bpf none $BPFFS
	fi
}

function attach_bpf {
	$TEST /tmp/cgroupv2/foo $BPF_PROG $1
	[ $? -ne 0 ] && exit 1
}

function cleanup {
	rm -rf $LINK_PIN
	ip link del veth0b
	ip netns delete at_ns0
	umount /tmp/cgroupv2
	rm -rf /tmp/cgroupv2
}

cleanup 2>/dev/null

set -e
config_device
config_cgroup
config_bpffs
set +e

#
# Test 1 - fail ping6
#
attach_bpf 0
ping -c1 -w1 172.16.1.100
if [ $? -ne 0 ]; then
	echo "ping failed when it should succeed"
	cleanup
	exit 1
fi

ping6 -c1 -w1 2401:db00::1
if [ $? -eq 0 ]; then
	echo "ping6 succeeded when it should not"
	cleanup
	exit 1
fi

rm -rf $LINK_PIN
sleep 1                 # Wait for link detach

#
# Test 2 - fail ping
#
attach_bpf 1
ping6 -c1 -w1 2401:db00::1
if [ $? -ne 0 ]; then
	echo "ping6 failed when it should succeed"
	cleanup
	exit 1
fi

ping -c1 -w1 172.16.1.100
if [ $? -eq 0 ]; then
	echo "ping succeeded when it should not"
	cleanup
	exit 1
fi

cleanup
echo
echo "*** PASS ***"
+80 −3
Original line number Diff line number Diff line
@@ -6,6 +6,11 @@
static char bpf_log_buf[4096];
static bool verbose;

enum sock_create_test_error {
	OK = 0,
	DENY_CREATE,
};

static struct sock_create_test {
	const char			*descr;
	const struct bpf_insn		insns[64];
@@ -14,9 +19,11 @@ static struct sock_create_test {

	int				domain;
	int				type;
	int				protocol;

	int				optname;
	int				optval;
	enum sock_create_test_error	error;
} tests[] = {
	{
		.descr = "AF_INET set priority",
@@ -164,6 +171,72 @@ static struct sock_create_test {
		.optname = SO_BINDTOIFINDEX,
		.optval = 1,
	},
	{
		.descr = "block AF_INET, SOCK_DGRAM, IPPROTO_ICMP socket",
		.insns = {
			BPF_MOV64_IMM(BPF_REG_0, 1),	/* r0 = verdict */

			/* sock->family == AF_INET */
			BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1,
				    offsetof(struct bpf_sock, family)),
			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, AF_INET, 5),

			/* sock->type == SOCK_DGRAM */
			BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1,
				    offsetof(struct bpf_sock, type)),
			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, SOCK_DGRAM, 3),

			/* sock->protocol == IPPROTO_ICMP */
			BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1,
				    offsetof(struct bpf_sock, protocol)),
			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, IPPROTO_ICMP, 1),

			/* return 0 (block) */
			BPF_MOV64_IMM(BPF_REG_0, 0),
			BPF_EXIT_INSN(),
		},
		.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,

		.domain = AF_INET,
		.type = SOCK_DGRAM,
		.protocol = IPPROTO_ICMP,

		.error = DENY_CREATE,
	},
	{
		.descr = "block AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6 socket",
		.insns = {
			BPF_MOV64_IMM(BPF_REG_0, 1),	/* r0 = verdict */

			/* sock->family == AF_INET6 */
			BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1,
				    offsetof(struct bpf_sock, family)),
			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, AF_INET6, 5),

			/* sock->type == SOCK_DGRAM */
			BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1,
				    offsetof(struct bpf_sock, type)),
			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, SOCK_DGRAM, 3),

			/* sock->protocol == IPPROTO_ICMPV6 */
			BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1,
				    offsetof(struct bpf_sock, protocol)),
			BPF_JMP_IMM(BPF_JNE, BPF_REG_2, IPPROTO_ICMPV6, 1),

			/* return 0 (block) */
			BPF_MOV64_IMM(BPF_REG_0, 0),
			BPF_EXIT_INSN(),
		},
		.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,

		.domain = AF_INET,
		.type = SOCK_DGRAM,
		.protocol = IPPROTO_ICMPV6,

		.error = DENY_CREATE,
	},
};

static int load_prog(const struct bpf_insn *insns,
@@ -208,9 +281,13 @@ static int run_test(int cgroup_fd, struct sock_create_test *test)
		goto close_prog_fd;
	}

	sock_fd = socket(test->domain, test->type, 0);
	sock_fd = socket(test->domain, test->type, test->protocol);
	if (sock_fd < 0) {
		if (test->error == DENY_CREATE)
			ret = 0;
		else
			log_err("Failed to create socket");

		goto detach_prog;
	}

@@ -226,7 +303,7 @@ static int run_test(int cgroup_fd, struct sock_create_test *test)
		goto cleanup;
	}

	ret = 0;
	ret = test->error != OK;

cleanup:
	close(sock_fd);