All of lore.kernel.org
 help / color / mirror / Atom feed
* [LTP] [PATCH v2 1/6] Add SAFE_REALLOC() helper function to LTP library
@ 2021-05-04 15:48 Martin Doucha
  2021-05-04 15:48 ` [LTP] [PATCH v2 2/6] Add SAFE_RECV() " Martin Doucha
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Martin Doucha @ 2021-05-04 15:48 UTC (permalink / raw)
  To: ltp

Signed-off-by: Martin Doucha <mdoucha@suse.cz>
Reviewed-by: Cyril Hrubis <chrubis@suse.cz>
---

Changes since v1: None

 include/tst_safe_macros.h |  5 +++++
 lib/tst_safe_macros.c     | 15 +++++++++++++++
 2 files changed, 20 insertions(+)

diff --git a/include/tst_safe_macros.h b/include/tst_safe_macros.h
index b9d9baa1a..d6f32ef4c 100644
--- a/include/tst_safe_macros.h
+++ b/include/tst_safe_macros.h
@@ -67,6 +67,11 @@ int safe_dup2(const char *file, const int lineno, int oldfd, int newfd);
 #define SAFE_MALLOC(size) \
 	safe_malloc(__FILE__, __LINE__, NULL, (size))
 
+void *safe_realloc(const char *file, const int lineno, void *ptr, size_t size);
+
+#define SAFE_REALLOC(ptr, size) \
+	safe_realloc(__FILE__, __LINE__, (ptr), (size))
+
 #define SAFE_MKDIR(pathname, mode) \
 	safe_mkdir(__FILE__, __LINE__, NULL, (pathname), (mode))
 
diff --git a/lib/tst_safe_macros.c b/lib/tst_safe_macros.c
index 182b690bb..fd5f1704b 100644
--- a/lib/tst_safe_macros.c
+++ b/lib/tst_safe_macros.c
@@ -5,6 +5,7 @@
 
 #define _GNU_SOURCE
 #include <unistd.h>
+#include <stdlib.h>
 #include <errno.h>
 #include <sched.h>
 #include <sys/ptrace.h>
@@ -433,6 +434,20 @@ int safe_dup2(const char *file, const int lineno, int oldfd, int newfd)
 	return rval;
 }
 
+void *safe_realloc(const char *file, const int lineno, void *ptr, size_t size)
+{
+	void *ret;
+
+	ret = realloc(ptr, size);
+
+	if (!ret) {
+		tst_brk_(file, lineno, TBROK | TERRNO,
+			"realloc(%p, %zu) failed", ptr, size);
+	}
+
+	return ret;
+}
+
 sighandler_t safe_signal(const char *file, const int lineno,
 	int signum, sighandler_t handler)
 {
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [LTP] [PATCH v2 2/6] Add SAFE_RECV() helper function to LTP library
  2021-05-04 15:48 [LTP] [PATCH v2 1/6] Add SAFE_REALLOC() helper function to LTP library Martin Doucha
@ 2021-05-04 15:48 ` Martin Doucha
  2021-05-04 15:48 ` [LTP] [PATCH v2 3/6] Add SAFE_IOCTL() variant for library code Martin Doucha
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Martin Doucha @ 2021-05-04 15:48 UTC (permalink / raw)
  To: ltp

Signed-off-by: Martin Doucha <mdoucha@suse.cz>
Reviewed-by: Cyril Hrubis <chrubis@suse.cz>
---

Changes since v1: None

 include/safe_net_fn.h  |  3 +++
 include/tst_safe_net.h |  3 +++
 lib/safe_net.c         | 25 +++++++++++++++++++++++++
 3 files changed, 31 insertions(+)

diff --git a/include/safe_net_fn.h b/include/safe_net_fn.h
index 2fda11fab..ff81b1337 100644
--- a/include/safe_net_fn.h
+++ b/include/safe_net_fn.h
@@ -47,6 +47,9 @@ ssize_t safe_sendto(const char *file, const int lineno, char len_strict,
 ssize_t safe_sendmsg(const char *file, const int lineno, size_t msg_len,
 		  int sockfd, const struct msghdr *msg, int flags);
 
+ssize_t safe_recv(const char *file, const int lineno, size_t len,
+	int sockfd, void *buf, size_t size, int flags);
+
 ssize_t safe_recvmsg(const char *file, const int lineno, size_t msg_len,
 		  int sockfd, struct msghdr *msg, int flags);
 
diff --git a/include/tst_safe_net.h b/include/tst_safe_net.h
index 78a488a18..e85b79a3e 100644
--- a/include/tst_safe_net.h
+++ b/include/tst_safe_net.h
@@ -42,6 +42,9 @@
 #define SAFE_SENDMSG(msg_len, fd, msg, flags) \
 	safe_sendmsg(__FILE__, __LINE__, msg_len, fd, msg, flags)
 
+#define SAFE_RECV(msg_len, fd, buf, size, flags)		\
+	safe_recv(__FILE__, __LINE__, (msg_len), (fd), (buf), (size), (flags))
+
 #define SAFE_RECVMSG(msg_len, fd, msg, flags)		\
 	safe_recvmsg(__FILE__, __LINE__, msg_len, fd, msg, flags)
 
diff --git a/lib/safe_net.c b/lib/safe_net.c
index f9ebea610..211fd9b67 100644
--- a/lib/safe_net.c
+++ b/lib/safe_net.c
@@ -273,6 +273,31 @@ ssize_t safe_sendmsg(const char *file, const int lineno, size_t len,
 	return rval;
 }
 
+ssize_t safe_recv(const char *file, const int lineno, size_t len,
+	int sockfd, void *buf, size_t size, int flags)
+{
+	ssize_t rval;
+
+	rval = recv(sockfd, buf, size, flags);
+
+	if (rval == -1) {
+		tst_brkm_(file, lineno, TBROK | TERRNO, NULL,
+			"recv(%d, %p, %zu, %d) failed", sockfd, buf, size,
+			flags);
+	} else if (rval < 0) {
+		tst_brkm_(file, lineno, TBROK | TERRNO, NULL,
+			"Invalid recv(%d, %p, %zu, %d) return value %zd",
+			sockfd, buf, size, flags, rval);
+	} else if (len && (size_t)rval != len) {
+		tst_brkm_(file, lineno, TBROK, NULL,
+			"recv(%d, %p, %zu, %d) ret(%zd) != len(%zu)",
+			sockfd, buf, size, flags, rval, len);
+	}
+
+	return rval;
+
+}
+
 ssize_t safe_recvmsg(const char *file, const int lineno, size_t len,
 		     int sockfd, struct msghdr *msg, int flags)
 {
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [LTP] [PATCH v2 3/6] Add SAFE_IOCTL() variant for library code
  2021-05-04 15:48 [LTP] [PATCH v2 1/6] Add SAFE_REALLOC() helper function to LTP library Martin Doucha
  2021-05-04 15:48 ` [LTP] [PATCH v2 2/6] Add SAFE_RECV() " Martin Doucha
@ 2021-05-04 15:48 ` Martin Doucha
  2021-05-04 15:48 ` [LTP] [PATCH v2 4/6] Add rtnetlink helper library Martin Doucha
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Martin Doucha @ 2021-05-04 15:48 UTC (permalink / raw)
  To: ltp

SAFE_IOCTL() cannot be implemented as a function due to the variadic argument
but the macro still needs a variant which accepts arbitrary file:line position
for error messages as arguments.

Signed-off-by: Martin Doucha <mdoucha@suse.cz>
---

Changes since v1: New patch

 include/tst_safe_macros.h | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/include/tst_safe_macros.h b/include/tst_safe_macros.h
index d6f32ef4c..dcef58bda 100644
--- a/include/tst_safe_macros.h
+++ b/include/tst_safe_macros.h
@@ -211,13 +211,16 @@ pid_t safe_getpgid(const char *file, const int lineno, pid_t pid);
 #define SAFE_READDIR(dirp) \
 	safe_readdir(__FILE__, __LINE__, NULL, (dirp))
 
-#define SAFE_IOCTL(fd, request, ...)                         \
+#define SAFE_IOCTL_(file, lineno, fd, request, ...)          \
 	({int tst_ret_ = ioctl(fd, request, ##__VA_ARGS__);  \
 	  tst_ret_ < 0 ?                                     \
-	   tst_brk(TBROK | TERRNO,                           \
+	   tst_brk_((file), (lineno), TBROK | TERRNO,        \
 	            "ioctl(%i,%s,...) failed", fd, #request), 0 \
 	 : tst_ret_;})
 
+#define SAFE_IOCTL(fd, request, ...) \
+	SAFE_IOCTL_(__FILE__, __LINE__, (fd), (request), ##__VA_ARGS__)
+
 #define SAFE_FCNTL(fd, cmd, ...)                            \
 	({int tst_ret_ = fcntl(fd, cmd, ##__VA_ARGS__);     \
 	  tst_ret_ == -1 ?                                  \
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [LTP] [PATCH v2 4/6] Add rtnetlink helper library
  2021-05-04 15:48 [LTP] [PATCH v2 1/6] Add SAFE_REALLOC() helper function to LTP library Martin Doucha
  2021-05-04 15:48 ` [LTP] [PATCH v2 2/6] Add SAFE_RECV() " Martin Doucha
  2021-05-04 15:48 ` [LTP] [PATCH v2 3/6] Add SAFE_IOCTL() variant for library code Martin Doucha
@ 2021-05-04 15:48 ` Martin Doucha
  2021-05-04 15:48 ` [LTP] [PATCH v2 5/6] Add helper functions for managing network interfaces Martin Doucha
  2021-05-04 15:48 ` [LTP] [PATCH v2 6/6] Add test for CVE 2020-25705 Martin Doucha
  4 siblings, 0 replies; 6+ messages in thread
From: Martin Doucha @ 2021-05-04 15:48 UTC (permalink / raw)
  To: ltp

This library provides simple interface for creating arbitrary rtnetlink
messages with complex attributes, sending requests and receiving results.

Signed-off-by: Martin Doucha <mdoucha@suse.cz>
---

Changes since v1:
- fixed error handling in tst_rtnl_create_context()
- renamed tst_rtnl_free_context() to tst_rtnl_destroy_context()
- switched from select() to poll() in tst_rtnl_wait()
- use tst_rtnl_add_message() for adding NLMSG_DONE
- receive all pending messages in tst_rtnl_recv(), not just one
- use inline struct initialization where possible

 include/tst_rtnetlink.h | 106 +++++++++++
 lib/tst_rtnetlink.c     | 407 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 513 insertions(+)
 create mode 100644 include/tst_rtnetlink.h
 create mode 100644 lib/tst_rtnetlink.c

diff --git a/include/tst_rtnetlink.h b/include/tst_rtnetlink.h
new file mode 100644
index 000000000..12ec258f2
--- /dev/null
+++ b/include/tst_rtnetlink.h
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright (c) 2021 Linux Test Project
+ */
+
+#ifndef TST_RTNETLINK_H
+#define TST_RTNETLINK_H
+
+struct tst_rtnl_context;
+
+struct tst_rtnl_attr_list {
+	unsigned short type;
+	const void *data;
+	ssize_t len;
+	const struct tst_rtnl_attr_list *sublist;
+};
+
+struct tst_rtnl_message {
+	struct nlmsghdr *header;
+	struct nlmsgerr *err;
+	void *payload;
+	size_t payload_size;
+};
+
+/* Open a netlink socket */
+struct tst_rtnl_context *tst_rtnl_create_context(const char *file,
+	const int lineno);
+#define RTNL_CREATE_CONTEXT() tst_rtnl_create_context(__FILE__, __LINE__)
+
+/* Free a tst_rtnl_message array returned by tst_rtnl_recv() */
+void tst_rtnl_free_message(struct tst_rtnl_message *msg);
+#define RTNL_FREE_MESSAGE tst_rtnl_free_message
+
+/* Close netlink socket */
+void tst_rtnl_destroy_context(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx);
+#define RTNL_DESTROY_CONTEXT(ctx) \
+	tst_rtnl_destroy_context(__FILE__, __LINE__, (ctx))
+
+/* Send all messages in given buffer */
+int tst_rtnl_send(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx);
+#define RTNL_SEND(ctx) tst_rtnl_send(__FILE__, __LINE__, (ctx))
+
+/* Send all messages in given buffer and validate kernel response */
+int tst_rtnl_send_validate(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx);
+#define RTNL_SEND_VALIDATE(ctx) \
+	tst_rtnl_send_validate(__FILE__, __LINE__, (ctx))
+
+/* Wait until data is available for reading from the netlink socket */
+int tst_rtnl_wait(struct tst_rtnl_context *ctx);
+#define RTNL_WAIT tst_rtnl_wait
+
+/*
+ * Read from netlink socket and return an array of partially parsed messages.
+ * header == NULL indicates end of array.
+ */
+struct tst_rtnl_message *tst_rtnl_recv(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx);
+#define RTNL_RECV(ctx) tst_rtnl_recv(__FILE__, __LINE__, (ctx))
+
+/* Add new message to buffer */
+int tst_rtnl_add_message(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx, const struct nlmsghdr *header,
+	const void *payload, size_t payload_size);
+#define RTNL_ADD_MESSAGE(ctx, header, payload, psize) \
+	tst_rtnl_add_message(__FILE__, __LINE__, (ctx), (header), (payload), \
+		(psize))
+
+/* Add arbitrary attribute to last message */
+int tst_rtnl_add_attr(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx, unsigned short type, const void *data,
+	unsigned short len);
+#define RTNL_ADD_ATTR(ctx, type, data, len) \
+	tst_rtnl_add_attr(__FILE__, __LINE__, (ctx), (type), (data), (len))
+
+/* Add string attribute to last message */
+int tst_rtnl_add_attr_string(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx, unsigned short type, const char *data);
+#define RTNL_ADD_ATTR_STRING(ctx, type, data) \
+	tst_rtnl_add_attr_string(__FILE__, __LINE__, (ctx), (type), (data))
+
+/*
+ * Add list of arbitrary attributes to last message. The list is terminated
+ * by attribute with negative length. Nested sublists are supported.
+ */
+int tst_rtnl_add_attr_list(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx, const struct tst_rtnl_attr_list *list);
+#define RTNL_ADD_ATTR_LIST(ctx, list) \
+	tst_rtnl_add_attr_list(__FILE__, __LINE__, (ctx), (list))
+
+/* Check that all sent messages with NLM_F_ACK flag have been acked without
+ * error. Usage:
+ *
+ * tst_rtnl_send(ctx);
+ * tst_rtnl_wait(ctx);
+ * response = tst_rtnl_recv(ctx);
+ * if (!tst_rtnl_check_acks(ctx, response)) { ... }
+ * tst_rtnl_free_message(response);
+ */
+int tst_rtnl_check_acks(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx, struct tst_rtnl_message *response);
+#define RTNL_CHECK_ACKS(ctx, response) \
+	tst_rtnl_context(__FILE__, __LINE__, (ctx), (response))
+
+#endif /* TST_RTNETLINK_H */
diff --git a/lib/tst_rtnetlink.c b/lib/tst_rtnetlink.c
new file mode 100644
index 000000000..cd5013064
--- /dev/null
+++ b/lib/tst_rtnetlink.c
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021 Linux Test Project
+ */
+
+#include <stdlib.h>
+#include <limits.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_rtnetlink.h"
+
+struct tst_rtnl_context {
+	int socket;
+	pid_t pid;
+	uint32_t seq;
+	size_t bufsize, datalen;
+	char *buffer;
+	struct nlmsghdr *curmsg;
+};
+
+static int tst_rtnl_grow_buffer(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx, size_t size)
+{
+	size_t needed, offset, curlen = NLMSG_ALIGN(ctx->datalen);
+	char *buf;
+
+	if (ctx->bufsize - curlen >= size)
+		return 1;
+
+	needed = size - (ctx->bufsize - curlen);
+	size = ctx->bufsize + (ctx->bufsize > needed ? ctx->bufsize : needed);
+	size = NLMSG_ALIGN(size);
+	buf = safe_realloc(file, lineno, ctx->buffer, size);
+
+	if (!buf)
+		return 0;
+
+	memset(buf + ctx->bufsize, 0, size - ctx->bufsize);
+	offset = ((char *)ctx->curmsg) - ctx->buffer;
+	ctx->buffer = buf;
+	ctx->curmsg = (struct nlmsghdr *)(buf + offset);
+	ctx->bufsize = size;
+	return 1;
+}
+
+void tst_rtnl_destroy_context(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx)
+{
+	safe_close(file, lineno, NULL, ctx->socket);
+	free(ctx->buffer);
+	free(ctx);
+}
+
+struct tst_rtnl_context *tst_rtnl_create_context(const char *file,
+	const int lineno)
+{
+	struct tst_rtnl_context *ctx;
+	struct sockaddr_nl addr = { .nl_family = AF_NETLINK };
+
+	ctx = safe_malloc(file, lineno, NULL, sizeof(struct tst_rtnl_context));
+
+	if (!ctx)
+		return NULL;
+
+	ctx->pid = 0;
+	ctx->seq = 0;
+	ctx->buffer = NULL;
+	ctx->bufsize = 1024;
+	ctx->datalen = 0;
+	ctx->curmsg = NULL;
+	ctx->socket = safe_socket(file, lineno, NULL, AF_NETLINK,
+		SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
+
+	if (ctx->socket < 0) {
+		free(ctx);
+		return NULL;
+	}
+
+	if (safe_bind(file, lineno, NULL, ctx->socket, (struct sockaddr *)&addr,
+		sizeof(addr))) {
+		tst_rtnl_destroy_context(file, lineno, ctx);
+		return NULL;
+	}
+
+	ctx->buffer = safe_malloc(file, lineno, NULL, ctx->bufsize);
+
+	if (!ctx->buffer) {
+		tst_rtnl_destroy_context(file, lineno, ctx);
+		return NULL;
+	}
+
+	memset(ctx->buffer, 0, ctx->bufsize);
+	return ctx;
+}
+
+void tst_rtnl_free_message(struct tst_rtnl_message *msg)
+{
+	if (!msg)
+		return;
+
+	// all ptr->header and ptr->info pointers point to the same buffer
+	// msg->header is the start of the buffer
+	free(msg->header);
+	free(msg);
+}
+
+int tst_rtnl_send(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx)
+{
+	int ret;
+	struct sockaddr_nl addr = { .nl_family = AF_NETLINK };
+	struct iovec iov;
+	struct msghdr msg = {
+		.msg_name = &addr,
+		.msg_namelen = sizeof(addr),
+		.msg_iov = &iov,
+		.msg_iovlen = 1
+	};
+
+	if (!ctx->curmsg) {
+		tst_brk_(file, lineno, TBROK, "%s(): No message to send",
+			__func__);
+		return 0;
+	}
+
+	if (ctx->curmsg->nlmsg_flags & NLM_F_MULTI) {
+		struct nlmsghdr eom = { .nlmsg_type = NLMSG_DONE };
+
+		if (!tst_rtnl_add_message(file, lineno, ctx, &eom, NULL, 0))
+			return 0;
+
+		/* NLMSG_DONE message must not have NLM_F_MULTI flag */
+		ctx->curmsg->nlmsg_flags = 0;
+	}
+
+	iov.iov_base = ctx->buffer;
+	iov.iov_len = ctx->datalen;
+	ret = safe_sendmsg(file, lineno, ctx->datalen, ctx->socket, &msg, 0);
+
+	if (ret > 0)
+		ctx->curmsg = NULL;
+
+	return ret;
+}
+
+int tst_rtnl_wait(struct tst_rtnl_context *ctx)
+{
+	struct pollfd fdinfo = {
+		.fd = ctx->socket,
+		.events = POLLIN
+	};
+
+	return poll(&fdinfo, 1, 1000);
+}
+
+struct tst_rtnl_message *tst_rtnl_recv(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx)
+{
+	char tmp, *tmpbuf, *buffer = NULL;
+	struct tst_rtnl_message *ret;
+	struct nlmsghdr *ptr;
+	size_t retsize, bufsize = 0;
+	ssize_t size;
+	int i, size_left, msgcount;
+
+	/* Each recv() call returns one message, read all pending messages */
+	while (1) {
+		errno = 0;
+		size = recv(ctx->socket, &tmp, 1,
+			MSG_DONTWAIT | MSG_PEEK | MSG_TRUNC);
+
+		if (size < 0) {
+			if (errno != EAGAIN) {
+				tst_brk_(file, lineno, TBROK | TERRNO,
+					"recv() failed");
+			}
+
+			break;
+		}
+
+		tmpbuf = safe_realloc(file, lineno, buffer, bufsize + size);
+
+		if (!tmpbuf)
+			break;
+
+		buffer = tmpbuf;
+		size = safe_recv(file, lineno, size, ctx->socket,
+			buffer + bufsize, size, 0);
+
+		if (size < 0)
+			break;
+
+		bufsize += size;
+	}
+
+	if (!bufsize) {
+		free(buffer);
+		return NULL;
+	}
+
+	ptr = (struct nlmsghdr *)buffer;
+	size_left = bufsize;
+	msgcount = 0;
+
+	for (; size_left > 0 && NLMSG_OK(ptr, size_left); msgcount++)
+		ptr = NLMSG_NEXT(ptr, size_left);
+
+	retsize = (msgcount + 1) * sizeof(struct tst_rtnl_message);
+	ret = safe_malloc(file, lineno, NULL, retsize);
+
+	if (!ret) {
+		free(buffer);
+		return NULL;
+	}
+
+	memset(ret, 0, retsize);
+	ptr = (struct nlmsghdr *)buffer;
+	size_left = bufsize;
+
+	for (i = 0; i < msgcount; i++, ptr = NLMSG_NEXT(ptr, size_left)) {
+		ret[i].header = ptr;
+		ret[i].payload = NLMSG_DATA(ptr);
+		ret[i].payload_size = NLMSG_PAYLOAD(ptr, 0);
+
+		if (ptr->nlmsg_type == NLMSG_ERROR)
+			ret[i].err = NLMSG_DATA(ptr);
+	}
+
+	return ret;
+}
+
+int tst_rtnl_add_message(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx, const struct nlmsghdr *header,
+	const void *payload, size_t payload_size)
+{
+	size_t size;
+	unsigned int extra_flags = 0;
+
+	if (!tst_rtnl_grow_buffer(file, lineno, ctx, NLMSG_SPACE(payload_size)))
+		return 0;
+
+	if (!ctx->curmsg) {
+		/*
+		 * datalen may hold the size of last sent message for ACK
+		 * checking, reset it back to 0 here
+		 */
+		ctx->datalen = 0;
+		ctx->curmsg = (struct nlmsghdr *)ctx->buffer;
+	} else {
+		size = NLMSG_ALIGN(ctx->curmsg->nlmsg_len);
+
+		extra_flags = NLM_F_MULTI;
+		ctx->curmsg->nlmsg_flags |= extra_flags;
+		ctx->curmsg = NLMSG_NEXT(ctx->curmsg, size);
+		ctx->datalen = NLMSG_ALIGN(ctx->datalen);
+	}
+
+	*ctx->curmsg = *header;
+	ctx->curmsg->nlmsg_len = NLMSG_LENGTH(payload_size);
+	ctx->curmsg->nlmsg_flags |= extra_flags;
+	ctx->curmsg->nlmsg_seq = ctx->seq++;
+	ctx->curmsg->nlmsg_pid = ctx->pid;
+
+	if (payload_size)
+		memcpy(NLMSG_DATA(ctx->curmsg), payload, payload_size);
+
+	ctx->datalen += ctx->curmsg->nlmsg_len;
+	return 1;
+}
+
+int tst_rtnl_add_attr(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx, unsigned short type,
+	const void *data, unsigned short len)
+{
+	size_t size;
+	struct rtattr *attr;
+
+	if (!ctx->curmsg) {
+		tst_brk_(file, lineno, TBROK,
+			"%s(): No message to add attributes to", __func__);
+		return 0;
+	}
+
+	if (!tst_rtnl_grow_buffer(file, lineno, ctx, RTA_SPACE(len)))
+		return 0;
+
+	size = NLMSG_ALIGN(ctx->curmsg->nlmsg_len);
+	attr = (struct rtattr *)(((char *)ctx->curmsg) + size);
+	attr->rta_type = type;
+	attr->rta_len = RTA_LENGTH(len);
+	memcpy(RTA_DATA(attr), data, len);
+	ctx->curmsg->nlmsg_len = size + attr->rta_len;
+	ctx->datalen = NLMSG_ALIGN(ctx->datalen) + attr->rta_len;
+	return 1;
+}
+
+int tst_rtnl_add_attr_string(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx, unsigned short type,
+	const char *data)
+{
+	return tst_rtnl_add_attr(file, lineno, ctx, type, data,
+		strlen(data) + 1);
+}
+
+int tst_rtnl_add_attr_list(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx,
+	const struct tst_rtnl_attr_list *list)
+{
+	int i, ret;
+	size_t offset;
+
+	for (i = 0; list[i].len >= 0; i++) {
+		if (list[i].len > USHRT_MAX) {
+			tst_brk_(file, lineno, TBROK,
+				"%s(): Attribute value too long", __func__);
+			return -1;
+		}
+
+		offset = NLMSG_ALIGN(ctx->datalen);
+		ret = tst_rtnl_add_attr(file, lineno, ctx, list[i].type,
+			list[i].data, list[i].len);
+
+		if (!ret)
+			return -1;
+
+		if (list[i].sublist) {
+			struct rtattr *attr;
+
+			ret = tst_rtnl_add_attr_list(file, lineno, ctx,
+				list[i].sublist);
+
+			if (ret < 0)
+				return ret;
+
+			attr = (struct rtattr *)(ctx->buffer + offset);
+
+			if (ctx->datalen - offset > USHRT_MAX) {
+				tst_brk_(file, lineno, TBROK,
+					"%s(): Sublist too long", __func__);
+				return -1;
+			}
+
+			attr->rta_len = ctx->datalen - offset;
+		}
+	}
+
+	return i;
+}
+
+int tst_rtnl_check_acks(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx, struct tst_rtnl_message *res)
+{
+	struct nlmsghdr *msg = (struct nlmsghdr *)ctx->buffer;
+	int size_left = ctx->datalen;
+
+	for (; size_left > 0 && NLMSG_OK(msg, size_left);
+		msg = NLMSG_NEXT(msg, size_left)) {
+
+		if (!(msg->nlmsg_flags & NLM_F_ACK))
+			continue;
+
+		while (res->header && res->header->nlmsg_seq != msg->nlmsg_seq)
+			res++;
+
+		if (!res->err || res->header->nlmsg_seq != msg->nlmsg_seq) {
+			tst_brk_(file, lineno, TBROK,
+				"No ACK found for Netlink message %u",
+				msg->nlmsg_seq);
+			return 0;
+		}
+
+		if (res->err->error) {
+			TST_ERR = -res->err->error;
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+int tst_rtnl_send_validate(const char *file, const int lineno,
+	struct tst_rtnl_context *ctx)
+{
+	struct tst_rtnl_message *response;
+	int ret;
+
+	TST_ERR = 0;
+
+	if (tst_rtnl_send(file, lineno, ctx) <= 0)
+		return 0;
+
+	tst_rtnl_wait(ctx);
+	response = tst_rtnl_recv(file, lineno, ctx);
+
+	if (!response)
+		return 0;
+
+	ret = tst_rtnl_check_acks(file, lineno, ctx, response);
+	tst_rtnl_free_message(response);
+	return ret;
+}
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [LTP] [PATCH v2 5/6] Add helper functions for managing network interfaces
  2021-05-04 15:48 [LTP] [PATCH v2 1/6] Add SAFE_REALLOC() helper function to LTP library Martin Doucha
                   ` (2 preceding siblings ...)
  2021-05-04 15:48 ` [LTP] [PATCH v2 4/6] Add rtnetlink helper library Martin Doucha
@ 2021-05-04 15:48 ` Martin Doucha
  2021-05-04 15:48 ` [LTP] [PATCH v2 6/6] Add test for CVE 2020-25705 Martin Doucha
  4 siblings, 0 replies; 6+ messages in thread
From: Martin Doucha @ 2021-05-04 15:48 UTC (permalink / raw)
  To: ltp

The library currently supports:
- creating a virtual ethernet device pair
- removing network interfaces
- enabling or disabling a network interface
- managing interface addresses
- managing routing table entries
- moving network interfaces between network namespaces

Signed-off-by: Martin Doucha <mdoucha@suse.cz>
---

Changes since v1:
- renamed tst_netdevice_index() to tst_netdev_index_by_name()
- renamed tst_netdevice_activate() to tst_netdev_set_state()
- shortened tst_netdevice_*() to tst_netdev_*()
- use inline struct initialization where possible
- use SAFE_IOCTL_()
- added modify_route_inet() internal helper function

Changes I've decided not to do:
- moving *_address_inet() and *_route_inet() functions to header file
- breaking long lines like if(foo & tst_rtnl_add_attr(...)) before the function
  call instead of in the argument list, the result would be too long

 include/tst_netdevice.h | 118 ++++++++++
 lib/tst_netdevice.c     | 463 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 581 insertions(+)
 create mode 100644 include/tst_netdevice.h
 create mode 100644 lib/tst_netdevice.c

diff --git a/include/tst_netdevice.h b/include/tst_netdevice.h
new file mode 100644
index 000000000..3a6698731
--- /dev/null
+++ b/include/tst_netdevice.h
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright (c) 2021 Linux Test Project
+ */
+
+#ifndef TST_NETDEVICE_H
+#define TST_NETDEVICE_H
+
+/* Find device index for given network interface name. */
+int tst_netdev_index_by_name(const char *file, const int lineno,
+	const char *ifname);
+#define NETDEV_INDEX_BY_NAME(ifname) \
+	tst_netdev_index_by_name(__FILE__, __LINE__, (ifname))
+
+/* Activate or deactivate network interface */
+int tst_netdev_set_state(const char *file, const int lineno,
+	const char *ifname, int up);
+#define NETDEV_SET_STATE(ifname, up) \
+	tst_netdev_set_state(__FILE__, __LINE__, (ifname), (up))
+
+/* Create a connected pair of virtual network devices */
+int tst_create_veth_pair(const char *file, const int lineno,
+	const char *ifname1, const char *ifname2);
+#define CREATE_VETH_PAIR(ifname1, ifname2) \
+	tst_create_veth_pair(__FILE__, __LINE__, (ifname1), (ifname2))
+
+int tst_remove_netdev(const char *file, const int lineno, const char *ifname);
+#define REMOVE_NETDEV(ifname) tst_remove_netdev(__FILE__, __LINE__, (ifname))
+
+int tst_netdev_add_address(const char *file, const int lineno,
+	const char *ifname, unsigned int family, const void *address,
+	unsigned int prefix, size_t addrlen, unsigned int flags);
+#define NETDEV_ADD_ADDRESS(ifname, family, address, prefix, addrlen, flags) \
+	tst_netdev_add_address(__FILE__, __LINE__, (ifname), (family), \
+		(address), (prefix), (addrlen), (flags))
+
+int tst_netdev_add_address_inet(const char *file, const int lineno,
+	const char *ifname, in_addr_t address, unsigned int prefix,
+	unsigned int flags);
+#define NETDEV_ADD_ADDRESS_INET(ifname, address, prefix, flags) \
+	tst_netdev_add_address_inet(__FILE__, __LINE__, (ifname), (address), \
+		(prefix), (flags))
+
+int tst_netdev_remove_address(const char *file, const int lineno,
+	const char *ifname, unsigned int family, const void *address,
+	size_t addrlen);
+#define NETDEV_REMOVE_ADDRESS(ifname, family, address, addrlen) \
+	tst_netdev_remove_address(__FILE__, __LINE__, (ifname), (family), \
+		(address), (addrlen))
+
+int tst_netdev_remove_address_inet(const char *file, const int lineno,
+	const char *ifname, in_addr_t address);
+#define NETDEV_REMOVE_ADDRESS_INET(ifname, address) \
+	tst_netdev_remove_address_inet(__FILE__, __LINE__, (ifname), (address))
+
+int tst_netdev_change_ns_fd(const char *file, const int lineno,
+	const char *ifname, int nsfd);
+#define NETDEV_CHANGE_NS_FD(ifname, nsfd) \
+	tst_netdev_change_ns_fd(__FILE__, __LINE__, (ifname), (nsfd))
+
+int tst_netdev_change_ns_pid(const char *file, const int lineno,
+	const char *ifname, pid_t nspid);
+#define NETDEV_CHANGE_NS_PID(ifname, nspid) \
+	tst_netdev_change_ns_pid(__FILE__, __LINE__, (ifname), (nspid))
+
+/*
+ * Add new static entry to main routing table. If you specify gateway address,
+ * the interface name is optional.
+ */
+int tst_netdev_add_route(const char *file, const int lineno,
+	const char *ifname, unsigned int family, const void *srcaddr,
+	unsigned int srcprefix, size_t srclen, const void *dstaddr,
+	unsigned int dstprefix, size_t dstlen, const void *gateway,
+	size_t gatewaylen);
+#define NETDEV_ADD_ROUTE(ifname, family, srcaddr, srcprefix, srclen, dstaddr, \
+	dstprefix, dstlen, gateway, gatewaylen) \
+	tst_netdev_add_route(__FILE__, __LINE__, (ifname), (family), \
+		(srcaddr), (srcprefix), (srclen), (dstaddr), (dstprefix), \
+		(dstlen), (gateway), (gatewaylen))
+
+/*
+ * Simplified function for adding IPv4 static route. If you set srcprefix
+ * or dstprefix to 0, the corresponding address will be ignored. Interface
+ * name is optional if gateway address is non-zero.
+ */
+int tst_netdev_add_route_inet(const char *file, const int lineno,
+	const char *ifname, in_addr_t srcaddr, unsigned int srcprefix,
+	in_addr_t dstaddr, unsigned int dstprefix, in_addr_t gateway);
+#define NETDEV_ADD_ROUTE_INET(ifname, srcaddr, srcprefix, dstaddr, dstprefix, \
+	gateway) \
+	tst_netdev_add_route_inet(__FILE__, __LINE__, (ifname), (srcaddr), \
+		(srcprefix), (dstaddr), (dstprefix), (gateway))
+
+/*
+ * Remove static entry from main routing table.
+ */
+int tst_netdev_remove_route(const char *file, const int lineno,
+	const char *ifname, unsigned int family, const void *srcaddr,
+	unsigned int srcprefix, size_t srclen, const void *dstaddr,
+	unsigned int dstprefix, size_t dstlen, const void *gateway,
+	size_t gatewaylen);
+#define NETDEV_REMOVE_ROUTE(ifname, family, srcaddr, srcprefix, srclen, \
+	dstaddr, dstprefix, dstlen, gateway, gatewaylen) \
+	tst_netdev_remove_route(__FILE__, __LINE__, (ifname), (family), \
+		(srcaddr), (srcprefix), (srclen), (dstaddr), (dstprefix), \
+		(dstlen), (gateway), (gatewaylen))
+
+/*
+ * Simplified function for removing IPv4 static route.
+ */
+int tst_netdev_remove_route_inet(const char *file, const int lineno,
+	const char *ifname, in_addr_t srcaddr, unsigned int srcprefix,
+	in_addr_t dstaddr, unsigned int dstprefix, in_addr_t gateway);
+#define NETDEV_REMOVE_ROUTE_INET(ifname, srcaddr, srcprefix, dstaddr, \
+	dstprefix, gateway) \
+	tst_netdev_remove_route_inet(__FILE__, __LINE__, (ifname), (srcaddr), \
+		(srcprefix), (dstaddr), (dstprefix), (gateway))
+
+#endif /* TST_NETDEVICE_H */
diff --git a/lib/tst_netdevice.c b/lib/tst_netdevice.c
new file mode 100644
index 000000000..93019a140
--- /dev/null
+++ b/lib/tst_netdevice.c
@@ -0,0 +1,463 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021 Linux Test Project
+ */
+
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/veth.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_rtnetlink.h"
+#include "tst_netdevice.h"
+
+static struct tst_rtnl_context *create_request(const char *file,
+	const int lineno, unsigned int type, unsigned int flags,
+	const void *payload, size_t psize)
+{
+	struct tst_rtnl_context *ctx;
+	struct nlmsghdr header = {
+		.nlmsg_type = type,
+		.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags,
+	};
+
+	ctx = tst_rtnl_create_context(file, lineno);
+
+	if (!ctx)
+		return NULL;
+
+	if (!tst_rtnl_add_message(file, lineno, ctx, &header, payload, psize)) {
+		tst_rtnl_destroy_context(file, lineno, ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+int tst_netdev_index_by_name(const char *file, const int lineno,
+	const char *ifname)
+{
+	struct ifreq ifr;
+	int sock, ret;
+
+	if (strlen(ifname) >= IFNAMSIZ) {
+		tst_brk_(file, lineno, TBROK,
+			"Network device name \"%s\" too long", ifname);
+		return -1;
+	}
+
+	sock = safe_socket(file, lineno, NULL, AF_INET, SOCK_DGRAM, 0);
+
+	if (sock < 0)
+		return -1;
+
+	strcpy(ifr.ifr_name, ifname);
+	ret = SAFE_IOCTL_(file, lineno, sock, SIOCGIFINDEX, &ifr);
+	safe_close(file, lineno, NULL, sock);
+	return ret ? -1 : ifr.ifr_ifindex;
+}
+
+int tst_netdev_set_state(const char *file, const int lineno,
+	const char *ifname, int up)
+{
+	struct ifreq ifr;
+	int sock, ret;
+
+	if (strlen(ifname) >= IFNAMSIZ) {
+		tst_brk_(file, lineno, TBROK,
+			"Network device name \"%s\" too long", ifname);
+		return -1;
+	}
+
+	sock = safe_socket(file, lineno, NULL, AF_INET, SOCK_DGRAM, 0);
+
+	if (sock < 0)
+		return -1;
+
+	strcpy(ifr.ifr_name, ifname);
+	ret = SAFE_IOCTL_(file, lineno, sock, SIOCGIFFLAGS, &ifr);
+
+	if (ret) {
+		safe_close(file, lineno, NULL, sock);
+		return ret;
+	}
+
+	if (up)
+		ifr.ifr_flags |= IFF_UP;
+	else
+		ifr.ifr_flags &= ~IFF_UP;
+
+	ret = SAFE_IOCTL_(file, lineno, sock, SIOCSIFFLAGS, &ifr);
+	safe_close(file, lineno, NULL, sock);
+	return ret;
+}
+
+int tst_create_veth_pair(const char *file, const int lineno,
+	const char *ifname1, const char *ifname2)
+{
+	int ret;
+	struct ifinfomsg info = { .ifi_family = AF_UNSPEC };
+	struct tst_rtnl_context *ctx;
+	struct tst_rtnl_attr_list peerinfo[] = {
+		{IFLA_IFNAME, ifname2, strlen(ifname2) + 1, NULL},
+		{0, NULL, -1, NULL}
+	};
+	struct tst_rtnl_attr_list peerdata[] = {
+		{VETH_INFO_PEER, &info, sizeof(info), peerinfo},
+		{0, NULL, -1, NULL}
+	};
+	struct tst_rtnl_attr_list attrs[] = {
+		{IFLA_IFNAME, ifname1, strlen(ifname1) + 1, NULL},
+		{IFLA_LINKINFO, NULL, 0, (const struct tst_rtnl_attr_list[]){
+			{IFLA_INFO_KIND, "veth", 4, NULL},
+			{IFLA_INFO_DATA, NULL, 0, peerdata},
+			{0, NULL, -1, NULL}
+		}},
+		{0, NULL, -1, NULL}
+	};
+
+	if (strlen(ifname1) >= IFNAMSIZ) {
+		tst_brk_(file, lineno, TBROK,
+			"Network device name \"%s\" too long", ifname1);
+		return 0;
+	}
+
+	if (strlen(ifname2) >= IFNAMSIZ) {
+		tst_brk_(file, lineno, TBROK,
+			"Network device name \"%s\" too long", ifname2);
+		return 0;
+	}
+
+	ctx = create_request(file, lineno, RTM_NEWLINK,
+		NLM_F_CREATE | NLM_F_EXCL, &info, sizeof(info));
+
+	if (!ctx)
+		return 0;
+
+	if (tst_rtnl_add_attr_list(file, lineno, ctx, attrs) != 2) {
+		tst_rtnl_destroy_context(file, lineno, ctx);
+		return 0;
+	}
+
+	ret = tst_rtnl_send_validate(file, lineno, ctx);
+	tst_rtnl_destroy_context(file, lineno, ctx);
+
+	if (!ret) {
+		tst_brk_(file, lineno, TBROK | TTERRNO,
+			"Failed to create veth interfaces %s+%s", ifname1,
+			ifname2);
+	}
+
+	return ret;
+}
+
+int tst_remove_netdev(const char *file, const int lineno, const char *ifname)
+{
+	struct ifinfomsg info = { .ifi_family = AF_UNSPEC };
+	struct tst_rtnl_context *ctx;
+	int ret;
+
+	if (strlen(ifname) >= IFNAMSIZ) {
+		tst_brk_(file, lineno, TBROK,
+			"Network device name \"%s\" too long", ifname);
+		return 0;
+	}
+
+	ctx = create_request(file, lineno, RTM_DELLINK, 0, &info, sizeof(info));
+
+	if (!ctx)
+		return 0;
+
+	if (!tst_rtnl_add_attr_string(file, lineno, ctx, IFLA_IFNAME, ifname)) {
+		tst_rtnl_destroy_context(file, lineno, ctx);
+		return 0;
+	}
+
+	ret = tst_rtnl_send_validate(file, lineno, ctx);
+	tst_rtnl_destroy_context(file, lineno, ctx);
+
+	if (!ret) {
+		tst_brk_(file, lineno, TBROK | TTERRNO,
+			"Failed to remove netdevice %s", ifname);
+	}
+
+	return ret;
+}
+
+static int modify_address(const char *file, const int lineno,
+	unsigned int action, unsigned int nl_flags, const char *ifname,
+	unsigned int family, const void *address, unsigned int prefix,
+	size_t addrlen, uint32_t addr_flags)
+{
+	struct tst_rtnl_context *ctx;
+	int index, ret;
+	struct ifaddrmsg info = {
+		.ifa_family = family,
+		.ifa_prefixlen = prefix
+	};
+
+	index = tst_netdev_index_by_name(file, lineno, ifname);
+
+	if (index < 0) {
+		tst_brk_(file, lineno, TBROK, "Interface %s not found", ifname);
+		return 0;
+	}
+
+	info.ifa_index = index;
+	ctx = create_request(file, lineno, action, nl_flags, &info,
+		sizeof(info));
+
+	if (!ctx)
+		return 0;
+
+	if (!tst_rtnl_add_attr(file, lineno, ctx, IFA_FLAGS, &addr_flags,
+		sizeof(uint32_t))) {
+		tst_rtnl_destroy_context(file, lineno, ctx);
+		return 0;
+	}
+
+	if (!tst_rtnl_add_attr(file, lineno, ctx, IFA_LOCAL, address,
+		addrlen)) {
+		tst_rtnl_destroy_context(file, lineno, ctx);
+		return 0;
+	}
+
+	ret = tst_rtnl_send_validate(file, lineno, ctx);
+	tst_rtnl_destroy_context(file, lineno, ctx);
+
+	if (!ret) {
+		tst_brk_(file, lineno, TBROK | TTERRNO,
+			"Failed to modify %s network address", ifname);
+	}
+
+	return ret;
+}
+
+int tst_netdev_add_address(const char *file, const int lineno,
+	const char *ifname, unsigned int family, const void *address,
+	unsigned int prefix, size_t addrlen, unsigned int flags)
+{
+	return modify_address(file, lineno, RTM_NEWADDR,
+		NLM_F_CREATE | NLM_F_EXCL, ifname, family, address, prefix,
+		addrlen, flags);
+}
+
+int tst_netdev_add_address_inet(const char *file, const int lineno,
+	const char *ifname, in_addr_t address, unsigned int prefix,
+	unsigned int flags)
+{
+	return tst_netdev_add_address(file, lineno, ifname, AF_INET,
+		&address, prefix, sizeof(address), flags);
+}
+
+int tst_netdev_remove_address(const char *file, const int lineno,
+	const char *ifname, unsigned int family, const void *address,
+	size_t addrlen)
+{
+	return modify_address(file, lineno, RTM_DELADDR, 0, ifname, family,
+		address, 0, addrlen, 0);
+}
+
+int tst_netdev_remove_address_inet(const char *file, const int lineno,
+	const char *ifname, in_addr_t address)
+{
+	return tst_netdev_remove_address(file, lineno, ifname, AF_INET,
+		&address, sizeof(address));
+}
+
+static int change_ns(const char *file, const int lineno, const char *ifname,
+	unsigned short attr, uint32_t value)
+{
+	struct ifinfomsg info = { .ifi_family = AF_UNSPEC };
+	struct tst_rtnl_context *ctx;
+	int ret;
+
+	if (strlen(ifname) >= IFNAMSIZ) {
+		tst_brk_(file, lineno, TBROK,
+			"Network device name \"%s\" too long", ifname);
+		return 0;
+	}
+
+	ctx = create_request(file, lineno, RTM_NEWLINK, 0, &info, sizeof(info));
+
+	if (!tst_rtnl_add_attr_string(file, lineno, ctx, IFLA_IFNAME, ifname)) {
+		tst_rtnl_destroy_context(file, lineno, ctx);
+		return 0;
+	}
+
+	if (!tst_rtnl_add_attr(file, lineno, ctx, attr, &value,
+		sizeof(uint32_t))) {
+		tst_rtnl_destroy_context(file, lineno, ctx);
+		return 0;
+	}
+
+	ret = tst_rtnl_send_validate(file, lineno, ctx);
+	tst_rtnl_destroy_context(file, lineno, ctx);
+
+	if (!ret) {
+		tst_brk_(file, lineno, TBROK | TTERRNO,
+			"Failed to move %s to another namespace", ifname);
+	}
+
+	return ret;
+}
+
+int tst_netdev_change_ns_fd(const char *file, const int lineno,
+	const char *ifname, int nsfd)
+{
+	return change_ns(file, lineno, ifname, IFLA_NET_NS_FD, nsfd);
+}
+
+int tst_netdev_change_ns_pid(const char *file, const int lineno,
+	const char *ifname, pid_t nspid)
+{
+	return change_ns(file, lineno, ifname, IFLA_NET_NS_PID, nspid);
+}
+
+static int modify_route(const char *file, const int lineno, unsigned int action,
+	unsigned int flags, const char *ifname, unsigned int family,
+	const void *srcaddr, unsigned int srcprefix, size_t srclen,
+	const void *dstaddr, unsigned int dstprefix, size_t dstlen,
+	const void *gateway, size_t gatewaylen)
+{
+	struct tst_rtnl_context *ctx;
+	int ret;
+	int32_t index;
+	struct rtmsg info = {
+		.rtm_family = family,
+		.rtm_dst_len = dstprefix,
+		.rtm_src_len = srcprefix,
+		.rtm_table = RT_TABLE_MAIN,
+		.rtm_protocol = RTPROT_STATIC,
+		.rtm_type = RTN_UNICAST
+	};
+
+	if (!ifname && !gateway) {
+		tst_brk_(file, lineno, TBROK,
+			"Interface name or gateway address required");
+		return 0;
+	}
+
+	if (ifname && strlen(ifname) >= IFNAMSIZ) {
+		tst_brk_(file, lineno, TBROK,
+			"Network device name \"%s\" too long", ifname);
+		return 0;
+	}
+
+	if (ifname) {
+		index = tst_netdev_index_by_name(file, lineno, ifname);
+
+		if (index < 0)
+			return 0;
+	}
+
+	if (action == RTM_DELROUTE)
+		info.rtm_scope = RT_SCOPE_NOWHERE;
+	else
+		info.rtm_scope = RT_SCOPE_UNIVERSE;
+
+	ctx = create_request(file, lineno, action, flags, &info, sizeof(info));
+
+	if (srcaddr && !tst_rtnl_add_attr(file, lineno, ctx, RTA_SRC, srcaddr,
+		srclen)) {
+		tst_rtnl_destroy_context(file, lineno, ctx);
+		return 0;
+	}
+
+	if (dstaddr && !tst_rtnl_add_attr(file, lineno, ctx, RTA_DST, dstaddr,
+		dstlen)) {
+		tst_rtnl_destroy_context(file, lineno, ctx);
+		return 0;
+	}
+
+	if (gateway && !tst_rtnl_add_attr(file, lineno, ctx, RTA_GATEWAY,
+		gateway, gatewaylen)) {
+		tst_rtnl_destroy_context(file, lineno, ctx);
+		return 0;
+	}
+
+	if (ifname && !tst_rtnl_add_attr(file, lineno, ctx, RTA_OIF, &index,
+		sizeof(index))) {
+		tst_rtnl_destroy_context(file, lineno, ctx);
+		return 0;
+	}
+
+	ret = tst_rtnl_send_validate(file, lineno, ctx);
+	tst_rtnl_destroy_context(file, lineno, ctx);
+
+	if (!ret) {
+		tst_brk_(file, lineno, TBROK | TTERRNO,
+			"Failed to modify network route");
+	}
+
+	return ret;
+}
+
+static int modify_route_inet(const char *file, const int lineno,
+	unsigned int action, unsigned int flags, const char *ifname,
+	in_addr_t srcaddr, unsigned int srcprefix, in_addr_t dstaddr,
+	unsigned int dstprefix, in_addr_t gateway)
+{
+	void *src = NULL, *dst = NULL, *gw = NULL;
+	size_t srclen = 0, dstlen = 0, gwlen = 0;
+
+	if (srcprefix) {
+		src = &srcaddr;
+		srclen = sizeof(srcaddr);
+	}
+
+	if (dstprefix) {
+		dst = &dstaddr;
+		dstlen = sizeof(dstaddr);
+	}
+
+	if (gateway) {
+		gw = &gateway;
+		gwlen = sizeof(gateway);
+	}
+
+	return modify_route(file, lineno, action, flags, ifname, AF_INET, src,
+		srcprefix, srclen, dst, dstprefix, dstlen, gw, gwlen);
+}
+
+int tst_netdev_add_route(const char *file, const int lineno,
+	const char *ifname, unsigned int family, const void *srcaddr,
+	unsigned int srcprefix, size_t srclen, const void *dstaddr,
+	unsigned int dstprefix, size_t dstlen, const void *gateway,
+	size_t gatewaylen)
+{
+	return modify_route(file, lineno, RTM_NEWROUTE,
+		NLM_F_CREATE | NLM_F_EXCL, ifname, family, srcaddr, srcprefix,
+		srclen, dstaddr, dstprefix, dstlen, gateway, gatewaylen);
+}
+
+int tst_netdev_add_route_inet(const char *file, const int lineno,
+	const char *ifname, in_addr_t srcaddr, unsigned int srcprefix,
+	in_addr_t dstaddr, unsigned int dstprefix, in_addr_t gateway)
+{
+	return modify_route_inet(file, lineno, RTM_NEWROUTE,
+		NLM_F_CREATE | NLM_F_EXCL, ifname, srcaddr, srcprefix, dstaddr,
+		dstprefix, gateway);
+}
+
+int tst_netdev_remove_route(const char *file, const int lineno,
+	const char *ifname, unsigned int family, const void *srcaddr,
+	unsigned int srcprefix, size_t srclen, const void *dstaddr,
+	unsigned int dstprefix, size_t dstlen, const void *gateway,
+	size_t gatewaylen)
+{
+	return modify_route(file, lineno, RTM_DELROUTE, 0, ifname, family,
+		srcaddr, srcprefix, srclen, dstaddr, dstprefix, dstlen,
+		gateway, gatewaylen);
+}
+
+int tst_netdev_remove_route_inet(const char *file, const int lineno,
+	const char *ifname, in_addr_t srcaddr, unsigned int srcprefix,
+	in_addr_t dstaddr, unsigned int dstprefix, in_addr_t gateway)
+{
+	return modify_route_inet(file, lineno, RTM_DELROUTE, 0, ifname,
+		srcaddr, srcprefix, dstaddr, dstprefix, gateway);
+}
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [LTP] [PATCH v2 6/6] Add test for CVE 2020-25705
  2021-05-04 15:48 [LTP] [PATCH v2 1/6] Add SAFE_REALLOC() helper function to LTP library Martin Doucha
                   ` (3 preceding siblings ...)
  2021-05-04 15:48 ` [LTP] [PATCH v2 5/6] Add helper functions for managing network interfaces Martin Doucha
@ 2021-05-04 15:48 ` Martin Doucha
  4 siblings, 0 replies; 6+ messages in thread
From: Martin Doucha @ 2021-05-04 15:48 UTC (permalink / raw)
  To: ltp

Fixes #742

Signed-off-by: Martin Doucha <mdoucha@suse.cz>
---

Changes since v1: New patch

 testcases/cve/cve-2020-25705.c | 262 +++++++++++++++++++++++++++++++++
 1 file changed, 262 insertions(+)
 create mode 100644 testcases/cve/cve-2020-25705.c

diff --git a/testcases/cve/cve-2020-25705.c b/testcases/cve/cve-2020-25705.c
new file mode 100644
index 000000000..7d6bbafa8
--- /dev/null
+++ b/testcases/cve/cve-2020-25705.c
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 SUSE LLC
+ * Author: Nicolai Stange <nstange@suse.de>
+ * LTP port: Martin Doucha <mdoucha@suse.cz>
+ *
+ * CVE-2020-25705
+ *
+ * Test of ICMP rate limiting behavior that may be abused for DNS cache
+ * poisoning attack. Send a few batches of 100 packets to a closed UDP port
+ * and count the ICMP errors. If the number of errors is always the same
+ * for each batch (not randomized), the system is vulnerable. Send packets
+ * from multiple IP addresses to bypass per-address ICMP throttling.
+ *
+ * Fixed in:
+ *
+ *  commit b38e7819cae946e2edf869e604af1e65a5d241c5
+ *  Author: Eric Dumazet <edumazet@google.com>
+ *  Date:   Thu Oct 15 11:42:00 2020 -0700
+ *
+ *  icmp: randomize the global rate limiter
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <linux/if_addr.h>
+#include <linux/errqueue.h>
+
+#include <sched.h>
+#include <limits.h>
+#include "tst_test.h"
+#include "tst_netdevice.h"
+
+#define DSTADDR 0xfa444e02 /* 250.68.78.2 */
+#define SRCADDR_BASE 0xfa444e41 /* 250.68.78.65 */
+#define SRCADDR_COUNT 50
+#define BATCH_COUNT 8
+#define BUFSIZE 1024
+
+static int parentns = -1, childns = -1;
+static int fds[SRCADDR_COUNT];
+
+static void setup(void)
+{
+	struct sockaddr_in ipaddr = { .sin_family = AF_INET };
+	uint32_t addr;
+	int i;
+	int real_uid = getuid();
+	int real_gid = getgid();
+
+	for (i = 0; i < SRCADDR_COUNT; i++)
+		fds[i] = -1;
+
+	SAFE_UNSHARE(CLONE_NEWUSER);
+	SAFE_UNSHARE(CLONE_NEWNET);
+	SAFE_FILE_PRINTF("/proc/self/setgroups", "deny");
+	SAFE_FILE_PRINTF("/proc/self/uid_map", "0 %d 1\n", real_uid);
+	SAFE_FILE_PRINTF("/proc/self/gid_map", "0 %d 1\n", real_gid);
+
+	/*
+	 * Create network namespace to hide the destination interface from
+	 * the test process.
+	 */
+	parentns = SAFE_OPEN("/proc/self/ns/net", O_RDONLY);
+	SAFE_UNSHARE(CLONE_NEWNET);
+
+	/* Do NOT close this FD, or both interfaces will be destroyed */
+	childns = SAFE_OPEN("/proc/self/ns/net", O_RDONLY);
+
+	/* Configure child namespace */
+	CREATE_VETH_PAIR("ltp_veth1", "ltp_veth2");
+	addr = DSTADDR;
+	NETDEV_ADD_ADDRESS_INET("ltp_veth2", htonl(addr), 26,
+		IFA_F_NOPREFIXROUTE);
+	NETDEV_SET_STATE("ltp_veth2", 1);
+	NETDEV_ADD_ROUTE_INET("ltp_veth2", 0, 0, htonl(0xfa444e40), 26,
+		0);
+
+	/* Configure parent namespace */
+	NETDEV_CHANGE_NS_FD("ltp_veth1", parentns);
+	SAFE_SETNS(parentns, CLONE_NEWNET);
+	addr = SRCADDR_BASE; /* 250.68.78.65 */
+
+	for (i = 0; i < SRCADDR_COUNT; i++, addr++) {
+		NETDEV_ADD_ADDRESS_INET("ltp_veth1", htonl(addr), 26,
+			IFA_F_NOPREFIXROUTE);
+	}
+
+	NETDEV_SET_STATE("ltp_veth1", 1);
+	NETDEV_ADD_ROUTE_INET("ltp_veth1", 0, 0, htonl(0xfa444e00), 26, 0);
+	SAFE_FILE_PRINTF("/proc/sys/net/ipv4/conf/ltp_veth1/forwarding", "1");
+
+	/* Open test sockets */
+	for (i = 0; i < SRCADDR_COUNT; i++) {
+		ipaddr.sin_addr.s_addr = htonl(SRCADDR_BASE + i);
+		fds[i] = SAFE_SOCKET(AF_INET, SOCK_DGRAM, 0);
+		SAFE_SETSOCKOPT_INT(fds[i], IPPROTO_IP, IP_RECVERR, 1);
+		SAFE_BIND(fds[i], (struct sockaddr *)&ipaddr, sizeof(ipaddr));
+	}
+}
+
+static int count_icmp_errors(int fd)
+{
+	int error_count = 0;
+	ssize_t len;
+	char msgbuf[BUFSIZE], errbuf[BUFSIZE];
+	struct sockaddr_in addr;
+	struct cmsghdr *cmsg;
+	struct sock_extended_err exterr;
+	struct iovec iov = {
+		.iov_base = msgbuf,
+		.iov_len = BUFSIZE
+	};
+
+	while (1) {
+		struct msghdr msg = {
+			.msg_name = (struct sockaddr *)&addr,
+			.msg_namelen = sizeof(addr),
+			.msg_iov = &iov,
+			.msg_iovlen = 1,
+			.msg_flags = 0,
+			.msg_control = errbuf,
+			.msg_controllen = BUFSIZE
+		};
+
+		memset(errbuf, 0, BUFSIZE);
+		errno = 0;
+		len = recvmsg(fd, &msg, MSG_ERRQUEUE);
+
+		if (len == -1) {
+			if (errno == EWOULDBLOCK || errno == EAGAIN)
+				break;
+
+			tst_brk(TBROK | TERRNO, "recvmsg() failed");
+		}
+
+		if (len < 0) {
+			tst_brk(TBROK | TERRNO,
+				"Invalid recvmsg() return value %zd", len);
+		}
+
+		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+			cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+			if (cmsg->cmsg_level != SOL_IP)
+				continue;
+
+			if (cmsg->cmsg_type != IP_RECVERR)
+				continue;
+
+			memcpy(&exterr, CMSG_DATA(cmsg), sizeof(exterr));
+
+			if (exterr.ee_origin != SO_EE_ORIGIN_ICMP)
+				tst_brk(TBROK, "Unexpected non-ICMP error");
+
+			if (exterr.ee_errno != ECONNREFUSED) {
+				TST_ERR = exterr.ee_errno;
+				tst_brk(TBROK | TTERRNO,
+					"Unexpected ICMP error");
+			}
+
+			error_count++;
+		}
+	}
+
+	return error_count;
+}
+
+static int packet_batch(const struct sockaddr *addr, socklen_t addrsize)
+{
+	int i, j, error_count = 0;
+	char data = 0;
+
+	for (i = 0; i < SRCADDR_COUNT; i++) {
+		for (j = 0; j < 2; j++) {
+			error_count += count_icmp_errors(fds[i]);
+			TEST(sendto(fds[i], &data, sizeof(data), 0, addr,
+				addrsize));
+
+			if (TST_RET == -1) {
+				if (TST_ERR == ECONNREFUSED) {
+					j--; /* flush ICMP errors and retry */
+					continue;
+				}
+
+				tst_brk(TBROK | TTERRNO, "sento() failed");
+			}
+
+			if (TST_RET < 0) {
+				tst_brk(TBROK | TTERRNO,
+					"Invalid sento() return value %ld",
+					TST_RET);
+			}
+		}
+	}
+
+	/* Wait and collect pending ICMP errors */
+	sleep(2);
+
+	for (i = 0; i < SRCADDR_COUNT; i++)
+		error_count += count_icmp_errors(fds[i]);
+
+	return error_count;
+}
+
+static void run(void)
+{
+	int i, errors_baseline, errors;
+	struct sockaddr_in addr = {
+		.sin_family = AF_INET,
+		.sin_port = TST_GET_UNUSED_PORT(AF_INET, SOCK_DGRAM),
+		.sin_addr = { htonl(DSTADDR) }
+	};
+
+	errors_baseline = packet_batch((struct sockaddr *)&addr, sizeof(addr));
+	errors = errors_baseline;
+	tst_res(TINFO, "Batch 0: Got %d ICMP errors", errors);
+
+	for (i = 1; i < BATCH_COUNT && errors == errors_baseline; i++) {
+		errors = packet_batch((struct sockaddr *)&addr, sizeof(addr));
+		tst_res(TINFO, "Batch %d: Got %d ICMP errors", i, errors);
+	}
+
+	if (errors == errors_baseline) {
+		tst_res(TFAIL,
+			"ICMP rate limit not randomized, system is vulnerable");
+		return;
+	}
+
+	tst_res(TPASS, "ICMP rate limit is randomized");
+}
+
+static void cleanup(void)
+{
+	int i;
+
+	for (i = 0; i < SRCADDR_COUNT; i++)
+		if (fds[i] >= 0)
+			SAFE_CLOSE(fds[i]);
+
+	if (childns >= 0)
+		SAFE_CLOSE(childns);
+
+	if (parentns >= 0)
+		SAFE_CLOSE(parentns);
+}
+
+static struct tst_test test = {
+	.test_all = run,
+	.setup = setup,
+	.cleanup = cleanup,
+	.needs_kconfigs = (const char *[]) {
+		"CONFIG_USER_NS=y",
+		"CONFIG_NET_NS=y",
+		NULL
+	},
+	.tags = (const struct tst_tag[]) {
+		{"linux-git", "b38e7819cae9"},
+		{"CVE", "2020-25705"},
+		{}
+	}
+};
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2021-05-04 15:48 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-04 15:48 [LTP] [PATCH v2 1/6] Add SAFE_REALLOC() helper function to LTP library Martin Doucha
2021-05-04 15:48 ` [LTP] [PATCH v2 2/6] Add SAFE_RECV() " Martin Doucha
2021-05-04 15:48 ` [LTP] [PATCH v2 3/6] Add SAFE_IOCTL() variant for library code Martin Doucha
2021-05-04 15:48 ` [LTP] [PATCH v2 4/6] Add rtnetlink helper library Martin Doucha
2021-05-04 15:48 ` [LTP] [PATCH v2 5/6] Add helper functions for managing network interfaces Martin Doucha
2021-05-04 15:48 ` [LTP] [PATCH v2 6/6] Add test for CVE 2020-25705 Martin Doucha

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.