* [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.