From patchwork Tue May 4 15:48:30 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Doucha X-Patchwork-Id: 1473902 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.linux.it (client-ip=2001:1418:10:5::2; helo=picard.linux.it; envelope-from=ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it; receiver=) Received: from picard.linux.it (picard.linux.it [IPv6:2001:1418:10:5::2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4FZPQX4VCTz9sPf for ; Wed, 5 May 2021 01:48:52 +1000 (AEST) Received: from picard.linux.it (localhost [IPv6:::1]) by picard.linux.it (Postfix) with ESMTP id CEC4D3C58B0 for ; Tue, 4 May 2021 17:48:49 +0200 (CEST) X-Original-To: ltp@lists.linux.it Delivered-To: ltp@picard.linux.it Received: from in-3.smtp.seeweb.it (in-3.smtp.seeweb.it [IPv6:2001:4b78:1:20::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by picard.linux.it (Postfix) with ESMTPS id 3D5223C582E for ; Tue, 4 May 2021 17:48:37 +0200 (CEST) Received: from mx2.suse.de (mx2.suse.de [195.135.220.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by in-3.smtp.seeweb.it (Postfix) with ESMTPS id A9EC01A007E6 for ; Tue, 4 May 2021 17:48:36 +0200 (CEST) Received: from relay2.suse.de (unknown [195.135.221.27]) by mx2.suse.de (Postfix) with ESMTP id EAADDAF0E for ; Tue, 4 May 2021 15:48:35 +0000 (UTC) From: Martin Doucha To: ltp@lists.linux.it Date: Tue, 4 May 2021 17:48:30 +0200 Message-Id: <20210504154835.24881-1-mdoucha@suse.cz> X-Mailer: git-send-email 2.31.1 MIME-Version: 1.0 X-Virus-Scanned: clamav-milter 0.102.4 at in-3.smtp.seeweb.it X-Virus-Status: Clean X-Spam-Status: No, score=0.0 required=7.0 tests=SPF_HELO_NONE,SPF_PASS autolearn=disabled version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on in-3.smtp.seeweb.it Subject: [LTP] [PATCH v2 1/6] Add SAFE_REALLOC() helper function to LTP library X-BeenThere: ltp@lists.linux.it X-Mailman-Version: 2.1.29 Precedence: list List-Id: Linux Test Project List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it Sender: "ltp" Signed-off-by: Martin Doucha Reviewed-by: Cyril Hrubis --- 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 +#include #include #include #include @@ -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) { From patchwork Tue May 4 15:48:31 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Doucha X-Patchwork-Id: 1473903 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.linux.it (client-ip=2001:1418:10:5::2; helo=picard.linux.it; envelope-from=ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it; receiver=) Received: from picard.linux.it (picard.linux.it [IPv6:2001:1418:10:5::2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4FZPQj3p3Tz9sRf for ; Wed, 5 May 2021 01:49:01 +1000 (AEST) Received: from picard.linux.it (localhost [IPv6:::1]) by picard.linux.it (Postfix) with ESMTP id EA4B83C5873 for ; Tue, 4 May 2021 17:48:58 +0200 (CEST) X-Original-To: ltp@lists.linux.it Delivered-To: ltp@picard.linux.it Received: from in-2.smtp.seeweb.it (in-2.smtp.seeweb.it [217.194.8.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by picard.linux.it (Postfix) with ESMTPS id 3B3B13C582C for ; Tue, 4 May 2021 17:48:37 +0200 (CEST) Received: from mx2.suse.de (mx2.suse.de [195.135.220.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by in-2.smtp.seeweb.it (Postfix) with ESMTPS id 9E76E60091E for ; Tue, 4 May 2021 17:48:36 +0200 (CEST) Received: from relay2.suse.de (unknown [195.135.221.27]) by mx2.suse.de (Postfix) with ESMTP id 0BBD7AFC2 for ; Tue, 4 May 2021 15:48:36 +0000 (UTC) From: Martin Doucha To: ltp@lists.linux.it Date: Tue, 4 May 2021 17:48:31 +0200 Message-Id: <20210504154835.24881-2-mdoucha@suse.cz> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210504154835.24881-1-mdoucha@suse.cz> References: <20210504154835.24881-1-mdoucha@suse.cz> MIME-Version: 1.0 X-Virus-Scanned: clamav-milter 0.102.4 at in-2.smtp.seeweb.it X-Virus-Status: Clean X-Spam-Status: No, score=0.0 required=7.0 tests=SPF_HELO_NONE,SPF_PASS autolearn=disabled version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on in-2.smtp.seeweb.it Subject: [LTP] [PATCH v2 2/6] Add SAFE_RECV() helper function to LTP library X-BeenThere: ltp@lists.linux.it X-Mailman-Version: 2.1.29 Precedence: list List-Id: Linux Test Project List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it Sender: "ltp" Signed-off-by: Martin Doucha Reviewed-by: Cyril Hrubis --- 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) { From patchwork Tue May 4 15:48:32 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Doucha X-Patchwork-Id: 1473901 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.linux.it (client-ip=2001:1418:10:5::2; helo=picard.linux.it; envelope-from=ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it; receiver=) Received: from picard.linux.it (picard.linux.it [IPv6:2001:1418:10:5::2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4FZPQP4V7Hz9sPf for ; Wed, 5 May 2021 01:48:43 +1000 (AEST) Received: from picard.linux.it (localhost [IPv6:::1]) by picard.linux.it (Postfix) with ESMTP id 69EBF3C5833 for ; Tue, 4 May 2021 17:48:39 +0200 (CEST) X-Original-To: ltp@lists.linux.it Delivered-To: ltp@picard.linux.it Received: from in-3.smtp.seeweb.it (in-3.smtp.seeweb.it [217.194.8.3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by picard.linux.it (Postfix) with ESMTPS id 245883C582A for ; Tue, 4 May 2021 17:48:37 +0200 (CEST) Received: from mx2.suse.de (mx2.suse.de [195.135.220.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by in-3.smtp.seeweb.it (Postfix) with ESMTPS id 965481A006FD for ; Tue, 4 May 2021 17:48:36 +0200 (CEST) Received: from relay2.suse.de (unknown [195.135.221.27]) by mx2.suse.de (Postfix) with ESMTP id 1BFBCAFC8 for ; Tue, 4 May 2021 15:48:36 +0000 (UTC) From: Martin Doucha To: ltp@lists.linux.it Date: Tue, 4 May 2021 17:48:32 +0200 Message-Id: <20210504154835.24881-3-mdoucha@suse.cz> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210504154835.24881-1-mdoucha@suse.cz> References: <20210504154835.24881-1-mdoucha@suse.cz> MIME-Version: 1.0 X-Virus-Scanned: clamav-milter 0.102.4 at in-3.smtp.seeweb.it X-Virus-Status: Clean X-Spam-Status: No, score=0.0 required=7.0 tests=SPF_HELO_NONE,SPF_PASS autolearn=disabled version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on in-3.smtp.seeweb.it Subject: [LTP] [PATCH v2 3/6] Add SAFE_IOCTL() variant for library code X-BeenThere: ltp@lists.linux.it X-Mailman-Version: 2.1.29 Precedence: list List-Id: Linux Test Project List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it Sender: "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 --- 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 ? \ From patchwork Tue May 4 15:48:33 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Doucha X-Patchwork-Id: 1473904 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.linux.it (client-ip=213.254.12.146; helo=picard.linux.it; envelope-from=ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it; receiver=) Received: from picard.linux.it (picard.linux.it [213.254.12.146]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4FZPQs5DtGz9sPf for ; Wed, 5 May 2021 01:49:09 +1000 (AEST) Received: from picard.linux.it (localhost [IPv6:::1]) by picard.linux.it (Postfix) with ESMTP id 9D5483C584F for ; Tue, 4 May 2021 17:49:07 +0200 (CEST) X-Original-To: ltp@lists.linux.it Delivered-To: ltp@picard.linux.it Received: from in-4.smtp.seeweb.it (in-4.smtp.seeweb.it [217.194.8.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by picard.linux.it (Postfix) with ESMTPS id 7A1943C582A for ; Tue, 4 May 2021 17:48:37 +0200 (CEST) Received: from mx2.suse.de (mx2.suse.de [195.135.220.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by in-4.smtp.seeweb.it (Postfix) with ESMTPS id 9E57F1000D37 for ; Tue, 4 May 2021 17:48:36 +0200 (CEST) Received: from relay2.suse.de (unknown [195.135.221.27]) by mx2.suse.de (Postfix) with ESMTP id 35AC7AFDC for ; Tue, 4 May 2021 15:48:36 +0000 (UTC) From: Martin Doucha To: ltp@lists.linux.it Date: Tue, 4 May 2021 17:48:33 +0200 Message-Id: <20210504154835.24881-4-mdoucha@suse.cz> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210504154835.24881-1-mdoucha@suse.cz> References: <20210504154835.24881-1-mdoucha@suse.cz> MIME-Version: 1.0 X-Virus-Scanned: clamav-milter 0.102.4 at in-4.smtp.seeweb.it X-Virus-Status: Clean X-Spam-Status: No, score=0.0 required=7.0 tests=SPF_HELO_NONE,SPF_PASS autolearn=disabled version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on in-4.smtp.seeweb.it Subject: [LTP] [PATCH v2 4/6] Add rtnetlink helper library X-BeenThere: ltp@lists.linux.it X-Mailman-Version: 2.1.29 Precedence: list List-Id: Linux Test Project List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it Sender: "ltp" This library provides simple interface for creating arbitrary rtnetlink messages with complex attributes, sending requests and receiving results. Signed-off-by: Martin Doucha --- 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 +#include +#include +#include +#include +#include +#include +#include +#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; +} From patchwork Tue May 4 15:48:34 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Doucha X-Patchwork-Id: 1473905 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.linux.it (client-ip=2001:1418:10:5::2; helo=picard.linux.it; envelope-from=ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it; receiver=) Received: from picard.linux.it (picard.linux.it [IPv6:2001:1418:10:5::2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4FZPR44rRGz9sPf for ; Wed, 5 May 2021 01:49:20 +1000 (AEST) Received: from picard.linux.it (localhost [IPv6:::1]) by picard.linux.it (Postfix) with ESMTP id 0EE8F3C5868 for ; Tue, 4 May 2021 17:49:18 +0200 (CEST) X-Original-To: ltp@lists.linux.it Delivered-To: ltp@picard.linux.it Received: from in-2.smtp.seeweb.it (in-2.smtp.seeweb.it [IPv6:2001:4b78:1:20::2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by picard.linux.it (Postfix) with ESMTPS id A16F33C582A for ; Tue, 4 May 2021 17:48:37 +0200 (CEST) Received: from mx2.suse.de (mx2.suse.de [195.135.220.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by in-2.smtp.seeweb.it (Postfix) with ESMTPS id AADD7600A4C for ; Tue, 4 May 2021 17:48:36 +0200 (CEST) Received: from relay2.suse.de (unknown [195.135.221.27]) by mx2.suse.de (Postfix) with ESMTP id 450ABAFF8 for ; Tue, 4 May 2021 15:48:36 +0000 (UTC) From: Martin Doucha To: ltp@lists.linux.it Date: Tue, 4 May 2021 17:48:34 +0200 Message-Id: <20210504154835.24881-5-mdoucha@suse.cz> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210504154835.24881-1-mdoucha@suse.cz> References: <20210504154835.24881-1-mdoucha@suse.cz> MIME-Version: 1.0 X-Virus-Scanned: clamav-milter 0.102.4 at in-2.smtp.seeweb.it X-Virus-Status: Clean X-Spam-Status: No, score=0.0 required=7.0 tests=SPF_HELO_NONE,SPF_PASS autolearn=disabled version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on in-2.smtp.seeweb.it Subject: [LTP] [PATCH v2 5/6] Add helper functions for managing network interfaces X-BeenThere: ltp@lists.linux.it X-Mailman-Version: 2.1.29 Precedence: list List-Id: Linux Test Project List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it Sender: "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 --- 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 +#include +#include +#include +#include +#include +#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); +} From patchwork Tue May 4 15:48:35 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Doucha X-Patchwork-Id: 1473906 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.linux.it (client-ip=2001:1418:10:5::2; helo=picard.linux.it; envelope-from=ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it; receiver=) Received: from picard.linux.it (picard.linux.it [IPv6:2001:1418:10:5::2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4FZPRG6JCjz9sPf for ; Wed, 5 May 2021 01:49:30 +1000 (AEST) Received: from picard.linux.it (localhost [IPv6:::1]) by picard.linux.it (Postfix) with ESMTP id 8272C3C584A for ; Tue, 4 May 2021 17:49:27 +0200 (CEST) X-Original-To: ltp@lists.linux.it Delivered-To: ltp@picard.linux.it Received: from in-3.smtp.seeweb.it (in-3.smtp.seeweb.it [217.194.8.3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by picard.linux.it (Postfix) with ESMTPS id D99633C582A for ; Tue, 4 May 2021 17:48:37 +0200 (CEST) Received: from mx2.suse.de (mx2.suse.de [195.135.220.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by in-3.smtp.seeweb.it (Postfix) with ESMTPS id 3D5451A00936 for ; Tue, 4 May 2021 17:48:37 +0200 (CEST) Received: from relay2.suse.de (unknown [195.135.221.27]) by mx2.suse.de (Postfix) with ESMTP id 579BBB13B for ; Tue, 4 May 2021 15:48:36 +0000 (UTC) From: Martin Doucha To: ltp@lists.linux.it Date: Tue, 4 May 2021 17:48:35 +0200 Message-Id: <20210504154835.24881-6-mdoucha@suse.cz> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210504154835.24881-1-mdoucha@suse.cz> References: <20210504154835.24881-1-mdoucha@suse.cz> MIME-Version: 1.0 X-Virus-Scanned: clamav-milter 0.102.4 at in-3.smtp.seeweb.it X-Virus-Status: Clean X-Spam-Status: No, score=0.0 required=7.0 tests=SPF_HELO_NONE,SPF_PASS autolearn=disabled version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on in-3.smtp.seeweb.it Subject: [LTP] [PATCH v2 6/6] Add test for CVE 2020-25705 X-BeenThere: ltp@lists.linux.it X-Mailman-Version: 2.1.29 Precedence: list List-Id: Linux Test Project List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it Sender: "ltp" Fixes #742 Signed-off-by: Martin Doucha --- 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 + * LTP port: Martin Doucha + * + * 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 + * Date: Thu Oct 15 11:42:00 2020 -0700 + * + * icmp: randomize the global rate limiter + */ + +#include +#include +#include +#include +#include + +#include +#include +#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"}, + {} + } +};