From patchwork Wed Jul 13 02:43:32 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antonio Quartulli X-Patchwork-Id: 2589 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director15.mail.ord1d.rsapps.net ([172.27.255.8]) by backend30.mail.ord1d.rsapps.net with LMTP id IE3lBxu+zmL2IAAAIUCqbw (envelope-from ) for ; Wed, 13 Jul 2022 08:44:11 -0400 Received: from proxy5.mail.iad3a.rsapps.net ([172.27.255.8]) by director15.mail.ord1d.rsapps.net with LMTP id EOvZBxu+zmI/LgAAIcMcQg (envelope-from ) for ; Wed, 13 Jul 2022 08:44:11 -0400 Received: from smtp5.gate.iad3a ([172.27.255.8]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy5.mail.iad3a.rsapps.net with LMTPS id 4DBNARu+zmIzXgAAhn5joQ (envelope-from ) for ; Wed, 13 Jul 2022 08:44:11 -0400 X-Spam-Threshold: 95 X-Spam-Score: 0 X-Spam-Flag: NO X-Virus-Scanned: OK X-Orig-To: openvpnslackdevel@openvpn.net X-Originating-Ip: [216.105.38.7] Authentication-Results: smtp5.gate.iad3a.rsapps.net; iprev=pass policy.iprev="216.105.38.7"; spf=pass smtp.mailfrom="openvpn-devel-bounces@lists.sourceforge.net" smtp.helo="lists.sourceforge.net"; dkim=fail (signature verification failed) header.d=sourceforge.net; dkim=fail (signature verification failed) header.d=sf.net; dmarc=none (p=nil; dis=none) header.from=unstable.cc X-Suspicious-Flag: YES X-Classification-ID: 7da4caf0-02a9-11ed-ba0c-525400de824c-1-1 Received: from [216.105.38.7] ([216.105.38.7:59896] helo=lists.sourceforge.net) by smtp5.gate.iad3a.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 73/D4-10920-A1EBEC26; Wed, 13 Jul 2022 08:44:10 -0400 Received: from [127.0.0.1] (helo=sfs-ml-2.v29.lw.sourceforge.com) by sfs-ml-2.v29.lw.sourceforge.com with esmtp (Exim 4.94.2) (envelope-from ) id 1oBbhu-0001Uo-0U; Wed, 13 Jul 2022 12:43:10 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-2.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1oBbhs-0001Ug-B4 for openvpn-devel@lists.sourceforge.net; Wed, 13 Jul 2022 12:43:09 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Transfer-Encoding:MIME-Version:References: In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=a3OuAW+ljEnEBsX6ZkI5mmLY08IX7Gp6aLMUMkWVjr0=; b=ff3+i46kBnReoYcynAI+dEEMr/ osvv4kC50Tl/ji8Eio80c7dRGGobjbVzcfKfsixSZcIUL1DDwXUBfWxbcEPiJX7G4qwJo0+tQ2838 ErKyVi6aEjTQunqny+BaIpqw+Gf6AbEotch/9U1GYtelqqltq9QEz1bLPdKg5w1zHUmc=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-Id: Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=a3OuAW+ljEnEBsX6ZkI5mmLY08IX7Gp6aLMUMkWVjr0=; b=jE1sV/jog8d2zNHD99Mtcqx/ks fc073cpIsRn6zVZvdwqhwhlvpQ4bbfe64FItY9DK+FApTKpkW9pPwuD4XThIOvRaFmtLtW7Y+rmF5 yisciyz91oVqROFnD7DeqMXK8BX0UhsnA7+K+y688IoNKSYMG+C1XeH6dQOsVQndYC6Y=; Received: from s2.neomailbox.net ([5.148.176.60]) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLS1.2:DHE-RSA-AES256-GCM-SHA384:256) (Exim 4.94.2) id 1oBbhs-0007xw-4M for openvpn-devel@lists.sourceforge.net; Wed, 13 Jul 2022 12:43:08 +0000 From: Antonio Quartulli To: openvpn-devel@lists.sourceforge.net Date: Wed, 13 Jul 2022 14:43:32 +0200 Message-Id: <20220713124332.16147-1-a@unstable.cc> In-Reply-To: <20220711081019.29511-1-a@unstable.cc> References: MIME-Version: 1.0 X-Spam-Report: Spam detection software, running on the system "util-spamd-2.v13.lw.sourceforge.com", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: This new API can be used to retrieve the type of a specific interface. It's mostly platform dependant, but right now expected values are "ovpn-dco", "tun" or "tap". Other values are possible too, but they are not of interest to us. Content analysis details: (-0.0 points, 6.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 T_SCC_BODY_TEXT_LINE No description available. X-Headers-End: 1oBbhs-0007xw-4M Subject: [Openvpn-devel] [PATCH v3 pre-05/25] networking: add net_iface_type API X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Antonio Quartulli Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox This new API can be used to retrieve the type of a specific interface. It's mostly platform dependant, but right now expected values are "ovpn-dco", "tun" or "tap". Other values are possible too, but they are not of interest to us. This commit also extends the networking unit-test by using the newly introduced API in conjunction with iface_new and iface_del. The t_net.sh script has been slightly adapted to allow running these tests in standalone (as they don't require any iproute2 counterpart). Signed-off-by: Antonio Quartulli Acked-by: Gert Doering --- Changes from v2: * extended unit-test to check for "no device exists case" * fixed typ0 in commit message * properly return -errno instead of errno Changes from v1: * added unit-test for net_iface_type() * adjusted t_net.sh script so that iface_new/type/del could be executed automatically src/openvpn/networking.h | 14 +++ src/openvpn/networking_iproute2.c | 9 ++ src/openvpn/networking_sitnl.c | 109 +++++++++++++++++++++ tests/t_net.sh | 15 ++- tests/unit_tests/openvpn/test_networking.c | 28 ++++-- 5 files changed, 166 insertions(+), 9 deletions(-) diff --git a/src/openvpn/networking.h b/src/openvpn/networking.h index 79963756..cf6d39ac 100644 --- a/src/openvpn/networking.h +++ b/src/openvpn/networking.h @@ -23,6 +23,8 @@ #include "syshead.h" +#define IFACE_TYPE_LEN_MAX 64 + struct context; #ifdef ENABLE_SITNL @@ -100,6 +102,18 @@ void net_ctx_free(openvpn_net_ctx_t *ctx); int net_iface_new(openvpn_net_ctx_t *ctx, const openvpn_net_iface_t *iface, const char *type, void *arg); +/** + * Retrieve the interface type + * + * @param ctx the implementation specific context + * @param iface interface to query + * @param type buffer where the type will be stored + * + * @return 0 on success, a negative error code otherwise + */ +int net_iface_type(openvpn_net_ctx_t *ctx, const char *iface, + char type[IFACE_TYPE_LEN_MAX]); + /** * Remove an interface * diff --git a/src/openvpn/networking_iproute2.c b/src/openvpn/networking_iproute2.c index 4b220576..3062c1da 100644 --- a/src/openvpn/networking_iproute2.c +++ b/src/openvpn/networking_iproute2.c @@ -78,6 +78,15 @@ net_iface_new(openvpn_net_ctx_t *ctx, const char *iface, const char *type, return 0; } +int +net_iface_type(openvpn_net_ctx_t *ctx, const char *iface, + char type[IFACE_TYPE_LEN_MAX]) +{ + /* not supported by iproute2 */ + msg(M_WARN, "%s: operation not supported by iproute2 backend", __func__); + return -1; +} + int net_iface_del(openvpn_net_ctx_t *ctx, const char *iface) { diff --git a/src/openvpn/networking_sitnl.c b/src/openvpn/networking_sitnl.c index 0944ad0a..b2f3ac72 100644 --- a/src/openvpn/networking_sitnl.c +++ b/src/openvpn/networking_sitnl.c @@ -1366,6 +1366,115 @@ err: return ret; } +static int +sitnl_parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta, + int len, unsigned short flags) +{ + unsigned short type; + + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + + while (RTA_OK(rta, len)) + { + type = rta->rta_type & ~flags; + + if ((type <= max) && (!tb[type])) + { + tb[type] = rta; + } + + rta = RTA_NEXT(rta, len); + } + + if (len) + { + msg(D_ROUTE, "%s: %d bytes not parsed! (rta_len=%d)", __func__, len, + rta->rta_len); + } + + return 0; +} + +static int +sitnl_parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + return sitnl_parse_rtattr_flags(tb, max, rta, len, 0); +} + +#define sitnl_parse_rtattr_nested(tb, max, rta) \ + (sitnl_parse_rtattr_flags(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta), \ + NLA_F_NESTED)) + +static int +sitnl_type_save(struct nlmsghdr *n, void *arg) +{ + char *type = arg; + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct rtattr *tb[IFLA_MAX + 1]; + int ret; + + ret = sitnl_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n)); + if (ret < 0) + { + return ret; + } + + if (tb[IFLA_LINKINFO]) + { + struct rtattr *tb_link[IFLA_INFO_MAX + 1]; + + ret = sitnl_parse_rtattr_nested(tb_link, IFLA_INFO_MAX, + tb[IFLA_LINKINFO]); + if (ret < 0) + { + return ret; + } + + if (!tb_link[IFLA_INFO_KIND]) + { + return -ENOENT; + } + + strncpynt(type, RTA_DATA(tb_link[IFLA_INFO_KIND]), IFACE_TYPE_LEN_MAX); + } + + return 0; +} + +int +net_iface_type(openvpn_net_ctx_t *ctx, const char *iface, + char type[IFACE_TYPE_LEN_MAX]) +{ + struct sitnl_link_req req = { }; + int ifindex = if_nametoindex(iface); + + if (!ifindex) + { + return -errno; + } + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_GETLINK; + + req.i.ifi_family = AF_PACKET; + req.i.ifi_index = ifindex; + + memset(type, 0, IFACE_TYPE_LEN_MAX); + + int ret = sitnl_send(&req.n, 0, 0, sitnl_type_save, type); + if (ret < 0) + { + msg(D_ROUTE, "%s: cannot retrieve iface %s: %s (%d)", __func__, iface, + strerror(-ret), ret); + return ret; + } + + msg(D_ROUTE, "%s: type of %s: %s", __func__, iface, type); + + return 0; +} + int net_iface_del(openvpn_net_ctx_t *ctx, const char *iface) { diff --git a/tests/t_net.sh b/tests/t_net.sh index af78152c..bc91f825 100755 --- a/tests/t_net.sh +++ b/tests/t_net.sh @@ -2,7 +2,8 @@ IFACE="ovpn-dummy0" UNIT_TEST="./unit_tests/openvpn/networking_testdriver" -MAX_TEST=${1:-7} +LAST_AUTO_TEST=7 +LAST_TEST=8 srcdir="${srcdir:-.}" top_builddir="${top_builddir:-..}" @@ -128,7 +129,7 @@ else fi fi -for i in $(seq 0 $MAX_TEST); do +for i in $(seq 0 $LAST_AUTO_TEST); do # reload dummy module to cleanup state reload_dummy typeset -a STATE_TEST @@ -168,4 +169,14 @@ done # remove interface for good $RUN_SUDO ip link del $IFACE +for i in $(seq $(($LAST_AUTO_TEST + 1)) ${LAST_TEST}); do + $RUN_SUDO $UNIT_TEST $i + if [ $? -ne 0 ]; then + echo "unit-test $i errored out" + exit 1 + fi + + echo "Test $i: OK" +done + exit 0 diff --git a/tests/unit_tests/openvpn/test_networking.c b/tests/unit_tests/openvpn/test_networking.c index 10ed2cb5..04347076 100644 --- a/tests/unit_tests/openvpn/test_networking.c +++ b/tests/unit_tests/openvpn/test_networking.c @@ -2,6 +2,7 @@ #include "syshead.h" #include "networking.h" +#include static char *iface = "ovpn-dummy0"; @@ -16,14 +17,25 @@ net__iface_up(bool up) static int net__iface_new(const char *name, const char *type) { - printf("CMD: ip link add %s type %s\n", name, type); return net_iface_new(NULL, name, type, NULL); } +static int +net__iface_type(const char *name, const char *type) +{ + char ret_type[IFACE_TYPE_LEN_MAX]; + int ret = net_iface_type(NULL, name, ret_type); + if (ret == 0) + { + assert(strcmp(type, ret_type) == 0); + } + + return ret; +} + static int net__iface_del(const char *name) { - printf("CMD: ip link del %s\n", name); return net_iface_del(NULL, name); } @@ -205,7 +217,7 @@ net__route_v6_add_gw(const char *dst_str, int prefixlen, const char *gw_str, static void usage(char *name) { - printf("Usage: %s <0-9>\n", name); + printf("Usage: %s <0-8>\n", name); } int @@ -257,11 +269,13 @@ main(int argc, char *argv[]) case 7: return net__route_v6_add_gw("2001:cafe:babe::", 48, "2001::2", 600); + /* following tests are standalone and do not print any CMD= */ case 8: - return net__iface_new("dummy0815", "dummy"); - - case 9: - return net__iface_del("dummy0815"); + assert(net__iface_new("dummy0815", "dummy") == 0); + assert(net__iface_type("dummy0815", "dummy") == 0); + assert(net__iface_del("dummy0815") == 0); + assert(net__iface_type("dummy0815", NULL) == -ENODEV); + return 0; default: printf("invalid test: %d\n", test);