All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dmitrii Banshchikov <me@ubique.spb.ru>
To: bpf@vger.kernel.org
Cc: Dmitrii Banshchikov <me@ubique.spb.ru>,
	ast@kernel.org, davem@davemloft.net, daniel@iogearbox.net,
	andrii@kernel.org, kafai@fb.com, songliubraving@fb.com,
	yhs@fb.com, john.fastabend@gmail.com, kpsingh@kernel.org,
	netdev@vger.kernel.org, rdna@fb.com
Subject: [PATCH bpf-next 03/11] bpfilter: Add IO functions
Date: Tue, 18 May 2021 02:53:00 +0400	[thread overview]
Message-ID: <20210517225308.720677-4-me@ubique.spb.ru> (raw)
In-Reply-To: <20210517225308.720677-1-me@ubique.spb.ru>

Introduce IO functions for:
1) reading and writing data from a descriptor: read_exact(), write_exact(),
2) reading and writing memory of other processes: pvm_read(), pvm_write().

read_exact() and write_exact() are wrappers over read(2)/write(2) with
correct handling of partial read/write. These functions are intended to
be used for communication over pipe with the kernel part of bpfilter.

pvm_read() and pvm_write() are wrappers over
process_vm_readv(2)/process_vm_writev(2) with an interface that uses a
single buffer instead of vectored form. These functions are intended to
be used for readining/writing memory buffers supplied to iptables ABI
setsockopt(2) from other processes.

Signed-off-by: Dmitrii Banshchikov <me@ubique.spb.ru>
---
 net/bpfilter/Makefile                         |   2 +-
 net/bpfilter/io.c                             |  77 ++++++++++++++
 net/bpfilter/io.h                             |  18 ++++
 .../testing/selftests/bpf/bpfilter/.gitignore |   2 +
 tools/testing/selftests/bpf/bpfilter/Makefile |  17 +++
 .../testing/selftests/bpf/bpfilter/test_io.c  | 100 ++++++++++++++++++
 6 files changed, 215 insertions(+), 1 deletion(-)
 create mode 100644 net/bpfilter/io.c
 create mode 100644 net/bpfilter/io.h
 create mode 100644 tools/testing/selftests/bpf/bpfilter/.gitignore
 create mode 100644 tools/testing/selftests/bpf/bpfilter/Makefile
 create mode 100644 tools/testing/selftests/bpf/bpfilter/test_io.c

diff --git a/net/bpfilter/Makefile b/net/bpfilter/Makefile
index 874d5ef6237d..69a6c139fc7a 100644
--- a/net/bpfilter/Makefile
+++ b/net/bpfilter/Makefile
@@ -4,7 +4,7 @@
 #
 
 userprogs := bpfilter_umh
-bpfilter_umh-objs := main.o bflog.o
+bpfilter_umh-objs := main.o bflog.o io.o
 userccflags += -I $(srctree)/tools/include/ -I $(srctree)/tools/include/uapi
 
 ifeq ($(CONFIG_BPFILTER_UMH), y)
diff --git a/net/bpfilter/io.c b/net/bpfilter/io.c
new file mode 100644
index 000000000000..e645ae9d7a50
--- /dev/null
+++ b/net/bpfilter/io.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Telegram FZ-LLC
+ */
+
+#define _GNU_SOURCE
+
+#include "io.h"
+
+#include <errno.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#define do_exact(fd, op, buffer, count)                                                            \
+	({                                                                                         \
+		size_t total = 0;                                                                  \
+		int err = 0;                                                                       \
+												   \
+		do {                                                                               \
+			const ssize_t part = op(fd, (buffer) + total, (count) - total);            \
+			if (part > 0) {                                                            \
+				total += part;                                                     \
+			} else if (part == 0 && (count) > 0) {                                     \
+				err = -EIO;                                                        \
+				break;                                                             \
+			} else if (part == -1) {                                                   \
+				if (errno == EINTR)                                                \
+					continue;                                                  \
+				err = -errno;                                                      \
+				break;                                                             \
+			}                                                                          \
+		} while (total < (count));                                                         \
+												   \
+		err;                                                                               \
+	})
+
+int read_exact(int fd, void *buffer, size_t count)
+{
+	return do_exact(fd, read, buffer, count);
+}
+
+int write_exact(int fd, const void *buffer, size_t count)
+{
+	return do_exact(fd, write, buffer, count);
+}
+
+int pvm_read(pid_t pid, void *to, const void *from, size_t count)
+{
+	const struct iovec r_iov = { .iov_base = (void *)from, .iov_len = count };
+	const struct iovec l_iov = { .iov_base = to, .iov_len = count };
+	size_t total_bytes;
+
+	total_bytes = process_vm_readv(pid, &l_iov, 1, &r_iov, 1, 0);
+	if (total_bytes == -1)
+		return -errno;
+
+	if (total_bytes != count)
+		return -EFAULT;
+
+	return 0;
+}
+
+int pvm_write(pid_t pid, void *to, const void *from, size_t count)
+{
+	const struct iovec l_iov = { .iov_base = (void *)from, .iov_len = count };
+	const struct iovec r_iov = { .iov_base = to, .iov_len = count };
+	size_t total_bytes;
+
+	total_bytes = process_vm_writev(pid, &l_iov, 1, &r_iov, 1, 0);
+	if (total_bytes == -1)
+		return -errno;
+
+	if (total_bytes != count)
+		return -EFAULT;
+
+	return 0;
+}
diff --git a/net/bpfilter/io.h b/net/bpfilter/io.h
new file mode 100644
index 000000000000..ab56c8bb8e61
--- /dev/null
+++ b/net/bpfilter/io.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Telegram FZ-LLC
+ */
+
+#ifndef NET_BPFILTER_IO_H
+#define NET_BPFILTER_IO_H
+
+#include <stddef.h>
+#include <sys/types.h>
+
+int read_exact(int fd, void *buffer, size_t count);
+int write_exact(int fd, const void *buffer, size_t count);
+
+int pvm_read(pid_t pid, void *to, const void *from, size_t count);
+int pvm_write(pid_t pid, void *to, const void *from, size_t count);
+
+#endif // NET_BPFILTER_IO_H
diff --git a/tools/testing/selftests/bpf/bpfilter/.gitignore b/tools/testing/selftests/bpf/bpfilter/.gitignore
new file mode 100644
index 000000000000..f5785e366013
--- /dev/null
+++ b/tools/testing/selftests/bpf/bpfilter/.gitignore
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+test_io
diff --git a/tools/testing/selftests/bpf/bpfilter/Makefile b/tools/testing/selftests/bpf/bpfilter/Makefile
new file mode 100644
index 000000000000..c02d72d89199
--- /dev/null
+++ b/tools/testing/selftests/bpf/bpfilter/Makefile
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0
+
+top_srcdir = ../../../../..
+TOOLSDIR := $(abspath ../../../../)
+TOOLSINCDIR := $(TOOLSDIR)/include
+APIDIR := $(TOOLSINCDIR)/uapi
+BPFILTERSRCDIR := $(top_srcdir)/net/bpfilter
+
+CFLAGS += -Wall -g -pthread -I$(TOOLSINCDIR) -I$(APIDIR) -I$(BPFILTERSRCDIR)
+
+TEST_GEN_PROGS += test_io
+
+KSFT_KHDR_INSTALL := 1
+
+include ../../lib.mk
+
+$(OUTPUT)/test_io: test_io.c $(BPFILTERSRCDIR)/io.c
diff --git a/tools/testing/selftests/bpf/bpfilter/test_io.c b/tools/testing/selftests/bpf/bpfilter/test_io.c
new file mode 100644
index 000000000000..e4294930c581
--- /dev/null
+++ b/tools/testing/selftests/bpf/bpfilter/test_io.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "io.h"
+
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../../kselftest_harness.h"
+
+FIXTURE(test_pvm)
+{
+	int wstatus;
+	int fd[2];
+	pid_t pid;
+	pid_t ppid;
+	char expected[5];
+	char actual[5];
+};
+
+FIXTURE_SETUP(test_pvm)
+{
+	snprintf(self->expected, sizeof(self->expected), "ipfw");
+	memset(self->actual, 0, sizeof(self->actual));
+	self->ppid = getpid();
+	ASSERT_EQ(pipe(self->fd), 0);
+	self->pid = fork();
+	ASSERT_NE(self->pid, -1) TH_LOG("Cannot fork(): %m\n");
+	close(self->fd[!!self->pid]);
+};
+
+FIXTURE_TEARDOWN(test_pvm)
+{
+	int wstatus;
+
+	if (!self->pid)
+		exit(0);
+
+	kill(self->pid, SIGKILL);
+	waitpid(self->pid, &wstatus, -2);
+	close(self->fd[1]);
+}
+
+TEST_F(test_pvm, read)
+{
+	if (!self->pid) {
+		const uint8_t baton = 'x';
+
+		memcpy(self->actual, self->expected, sizeof(self->actual));
+		ASSERT_EQ(write(self->fd[1], &baton, sizeof(baton)), sizeof(baton));
+
+		pause();
+		exit(0);
+	} else {
+		int err;
+		uint8_t baton;
+
+		EXPECT_EQ(read(self->fd[0], &baton, sizeof(baton)), sizeof(baton));
+		EXPECT_EQ(baton, 'x');
+
+		err = pvm_read(self->pid, &self->actual, &self->actual, sizeof(self->actual));
+		EXPECT_EQ(err, 0)
+		TH_LOG("Cannot pvm_read(): %s\n", strerror(-err));
+
+		EXPECT_EQ(memcmp(&self->expected, &self->actual, sizeof(self->actual)), 0);
+	}
+}
+
+TEST_F(test_pvm, write)
+{
+	if (getuid())
+		SKIP(return, "pvm_write requires CAP_SYS_PTRACE");
+
+	if (!self->pid) {
+		const uint8_t baton = 'x';
+		int err;
+
+		err = pvm_write(self->ppid, &self->actual, &self->expected, sizeof(self->expected));
+		EXPECT_EQ(err, 0) TH_LOG("Cannot pvm_write: %s\n", strerror(-err));
+
+		ASSERT_EQ(write(self->fd[1], &baton, sizeof(baton)), sizeof(baton));
+
+		pause();
+		exit(0);
+
+	} else {
+		uint8_t baton;
+
+		EXPECT_EQ(read(self->fd[0], &baton, sizeof(baton)), sizeof(baton));
+		EXPECT_EQ(baton, 'x');
+
+		EXPECT_EQ(memcmp(&self->expected, &self->actual, sizeof(self->actual)), 0);
+	}
+}
+
+TEST_HARNESS_MAIN
-- 
2.25.1


  parent reply	other threads:[~2021-05-17 22:53 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-05-17 22:52 [PATCH bpf-next 00/11] bpfilter Dmitrii Banshchikov
2021-05-17 22:52 ` [PATCH bpf-next 01/11] bpfilter: Add types for usermode helper Dmitrii Banshchikov
2021-05-17 22:52 ` [PATCH bpf-next 02/11] bpfilter: Add logging facility Dmitrii Banshchikov
2021-05-19 17:32   ` Song Liu
2021-05-20  7:08     ` Dmitrii Banshchikov
2021-05-20 16:35       ` Song Liu
2021-05-21  6:46         ` Dmitrii Banshchikov
2021-05-17 22:53 ` Dmitrii Banshchikov [this message]
2021-05-19 18:47   ` [PATCH bpf-next 03/11] bpfilter: Add IO functions Song Liu
2021-05-17 22:53 ` [PATCH bpf-next 04/11] tools: Add bpfilter usermode helper header Dmitrii Banshchikov
2021-05-17 22:53 ` [PATCH bpf-next 05/11] bpfilter: Add map container Dmitrii Banshchikov
2021-05-17 22:53 ` [PATCH bpf-next 06/11] bpfilter: Add struct match Dmitrii Banshchikov
2021-05-20  4:26   ` Song Liu
2021-05-20  7:31     ` Dmitrii Banshchikov
2021-05-20 17:44       ` Song Liu
2021-05-17 22:53 ` [PATCH bpf-next 07/11] bpfilter: Add struct target Dmitrii Banshchikov
2021-05-20  4:36   ` Song Liu
2021-05-20  7:44     ` Dmitrii Banshchikov
2021-05-17 22:53 ` [PATCH bpf-next 08/11] bpfilter: Add struct rule Dmitrii Banshchikov
2021-05-17 22:53 ` [PATCH bpf-next 09/11] bpfilter: Add struct table Dmitrii Banshchikov
2021-05-20 18:07   ` Song Liu
2021-05-17 22:53 ` [PATCH bpf-next 10/11] bpfilter: Add handling of setsockopt() calls Dmitrii Banshchikov
2021-05-17 22:53 ` [PATCH bpf-next 11/11] bpfilter: Handle setsockopts Dmitrii Banshchikov
2021-05-20  4:54 ` [PATCH bpf-next 00/11] bpfilter Song Liu
2021-05-20  7:53   ` Dmitrii Banshchikov
2021-05-20 16:55     ` Alexei Starovoitov
2021-05-20 17:56       ` Song Liu
2021-05-21  6:00         ` Dmitrii Banshchikov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210517225308.720677-4-me@ubique.spb.ru \
    --to=me@ubique.spb.ru \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=davem@davemloft.net \
    --cc=john.fastabend@gmail.com \
    --cc=kafai@fb.com \
    --cc=kpsingh@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=rdna@fb.com \
    --cc=songliubraving@fb.com \
    --cc=yhs@fb.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.