From patchwork Wed Jul 29 07:13:59 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marco Baffo X-Patchwork-Id: 5158 Return-Path: Delivered-To: patchwork@openvpn.net Received: by 2002:a05:7000:fd0b:b0:87d:ab56:3700 with SMTP id cw11csp1585093mac; Wed, 29 Jul 2026 00:14:39 -0700 (PDT) X-Forwarded-Encrypted: i=2; AHgh+RovKeIFEP+xtzAiPm+eAeTrsq8fY6feIzlF3rp9liWM0D2xB34JhP/YygWxTretRusn/8L/QEUBrlA=@openvpn.net X-Received: by 2002:a05:6830:6018:b0:7e6:fdea:7ab1 with SMTP id 46e09a7af769-7efff0c1b3amr3575666a34.8.1785309278974; Wed, 29 Jul 2026 00:14:38 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1785309278; cv=none; d=google.com; s=arc-20260327; b=mcbB7+xhSmvforEKIpXov+v0c0QxMQT/n7hHYNEAZjEm6OKOa0lLVo15sFx2VT86l/ HZHIyTVGbcc3lRdWom+s5gmZ6+1xX/786OGp3Op6yM1gRyUL18uZsjHo1DoAwgrmRPDl iMxxnyjFfVuraod6p2xgr3Ms5cp9LWHrLiEu0bwBWz+zmpoE+ZvuE8hrFma3SLdr3fXd 8mUqZ1UlEks6LIzsVEplJ4cFyR+d7hE65h7wC7WTamjF7HkyDCNFGUIQ7aWpz4eZ4A8K xjxofA8M5ZibcTftHugYsmIQMw2A89aaFrs9pyRrCvvZqI/zDpqDumMOWFQgRgii4Bkd xEMw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=errors-to:content-transfer-encoding:cc:list-subscribe:list-help :list-post:list-archive:list-unsubscribe:list-id:precedence:subject :mime-version:references:in-reply-to:message-id:date:to:from :dkim-signature:dkim-signature:dkim-signature:dkim-signature; bh=1CtzvWsvJoBRmxfyMNS1mIFZdU1JZPYs4kNK4kPA/7s=; fh=BsMg/B0Yb/hS/rzP5Npz4luh0IleZm8REk1XWiWRt2A=; b=qWRMiywVMhdR/6dE1JrqIoEy/46klrVuwdpSrXcMD/OvpNRR6uk2q3PGv5fi21GDWn Y81jebLphfcyx5tU8KktKnPNK13YramTMT/N31KK8BOlTYM1FlBZIinxB4AAprfLjDYW 2N8gWZ7Px4qPRzT7VsjAt6IyV+lfelhXTqBgoBZWWD8i34GmJPHGEKOvvxgQECwkwwAv x2N+FHLWrQSH9Y38GTh3GCpNf6LKeCBVKUiDl2PkM31Nw9UrCnyBvNF9h/VJmGGc1XuW KxV1RMBwonbGPTsrM6WRbNaIU85TKPpo7lJ7CJTduXTh65D2K4OcdrAIpo98VTArXfYY 2nPg==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@lists.sourceforge.net header.s=beta header.b=nHrNlgHE; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=nJHDp4tl; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=JaXN7O9H; dkim=neutral (body hash did not verify) header.i=@mandelbit.com header.s=MBO0001 header.b=fxZNTVSj; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net Received: from lists.sourceforge.net (lists.sourceforge.net. [216.105.38.7]) by mx.google.com with ESMTPS id 46e09a7af769-7f00d5dad7esi1683456a34.3.2026.07.29.00.14.38 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 29 Jul 2026 00:14:38 -0700 (PDT) Received-SPF: pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) client-ip=216.105.38.7; Authentication-Results: mx.google.com; dkim=pass header.i=@lists.sourceforge.net header.s=beta header.b=nHrNlgHE; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=nJHDp4tl; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=JaXN7O9H; dkim=neutral (body hash did not verify) header.i=@mandelbit.com header.s=MBO0001 header.b=fxZNTVSj; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.sourceforge.net; s=beta; h=Content-Transfer-Encoding:Content-Type:Cc: List-Subscribe:List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id: Subject:MIME-Version:References:In-Reply-To:Message-ID:Date:To:From:Sender: Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender :Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=1CtzvWsvJoBRmxfyMNS1mIFZdU1JZPYs4kNK4kPA/7s=; b=nHrNlgHEaD1wk/PsQtU5WzmrK7 zLXuKmOlcbtKqL9bsfxBxfva/ACMZEMCzjcSQzdsMiiM1l51NgutynkVUpoYQ1g0BAWOCPhVXmig9 Eegb6su5rEPViiRgtJmMfGiKlM67vPm+DYfSTh33Os49/ZVsou8famQ2KcKkyetUnk98=; Received: from [127.0.0.1] (helo=sfs-ml-1.v29.lw.sourceforge.com) by sfs-ml-1.v29.lw.sourceforge.com with esmtp (Exim 4.95) (envelope-from ) id 1woyUv-000639-Sq; Wed, 29 Jul 2026 07:14:35 +0000 Received: from [172.30.29.66] (helo=mx.sourceforge.net) by sfs-ml-1.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95) (envelope-from ) id 1woyUu-000631-FA for openvpn-devel@lists.sourceforge.net; Wed, 29 Jul 2026 07:14:34 +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=xZyk3IyDroL7Nn1ENLzzZgQrZaHVcIwichYBr5rnyRE=; b=nJHDp4tlbJXasoR4viuASOn6zo Qd7xVdoZY+5Rh5LtgCivG5QritJPAqcc8+vYXRoBieZkExHTW4DYFP6nvB/F9c6d1UvRE5wuHlaov DiCJauwngfN4KSl2UoVtbsqMUef+WRemLLnysW+wlBt4d/e0pEPgLj44ycjNvVTokvZY=; 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=xZyk3IyDroL7Nn1ENLzzZgQrZaHVcIwichYBr5rnyRE=; b=JaXN7O9HjbR6cAtTyhob0iknTc slE6AWD/djhrTGwIBN1zLFyMnbDACgDTITZwGJfY3CBHY2hbr3eG+Bwqfxl0+no3+1kgbR0R3CI02 qmdn0c/fVXNPNDaUaoItV9iFyJ21uZ2dqg3BL4KJtoEXBjPWCBGdJVQb797nrjXW2bcQ=; Received: from mout-b-106.mailbox.org ([195.10.208.46]) by sfi-mx-1.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.95) id 1woyUs-0004QF-9P for openvpn-devel@lists.sourceforge.net; Wed, 29 Jul 2026 07:14:33 +0000 Received: from smtp102.mailbox.org (unknown [10.196.197.102]) (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 mout-b-106.mailbox.org (Postfix) with ESMTPS id 4h93VQ6BZMzNljF; Wed, 29 Jul 2026 09:14:22 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mandelbit.com; s=MBO0001; t=1785309262; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=xZyk3IyDroL7Nn1ENLzzZgQrZaHVcIwichYBr5rnyRE=; b=fxZNTVSj4WkaSHsBOe3ak21BnT1h6esL28AfjG7N79fNQdKqcmkPCV2qSkFZlucUyEp3Rr jg7VR7R20AYYLHinV6jW5p7EF6WyWmGhIQLVWauJSEidou5BWIHH4dKWd8Lb5k9n13dbMz k7bc5zlHCyXwji05bHFMMudB+KYPRJ29Xsvu2WHRjlZIdQ4bQRCFsNstLykdtX7/gKwgiL +YUU7/Q+ueGpyZgABUTWS2S0XLmOnTXEYpRT7yi9RDSMKQPgJBcEZfrkaI2UosM046P6y5 ENILB/RinbfBelZEu/6c20LgVWP9aD+R6arZ+f0EbnmsKHFjFBjNrxkgFU/Iyg== From: Marco Baffo To: openvpn-devel@lists.sourceforge.net Date: Wed, 29 Jul 2026 09:13:59 +0200 Message-ID: <20260729071400.2403200-3-marco@mandelbit.com> In-Reply-To: <20260729071400.2403200-1-marco@mandelbit.com> References: <20260729071400.2403200-1-marco@mandelbit.com> MIME-Version: 1.0 X-Spam-Score: -0.2 (/) X-Spam-Report: Spam detection software, running on the system "sfi-spamd-1.hosts.colo.sdot.me", 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: Extend ovpn-cli with -n to resolve an interface in a named network namespace and include OVPN_A_TARGET_NETNSID in peer and key requests. Use -n for existing peer and key operations that do not create transport sockets, exercising foreign-netns requests throughout the test suite. Content analysis details: (-0.2 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature X-Headers-End: 1woyUs-0004QF-9P Subject: [Openvpn-devel] [PATCH ovpn net-next v2 3/4] selftests: ovpn: test operations in a foreign netns 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 X-GMAIL-THRID: 1872032462304970123 X-GMAIL-MSGID: 1872032462304970123 Extend ovpn-cli with -n to resolve an interface in a named network namespace and include OVPN_A_TARGET_NETNSID in peer and key requests. Use -n for existing peer and key operations that do not create transport sockets, exercising foreign-netns requests throughout the test suite. Signed-off-by: Marco Baffo --- Changes in v2: - Used "ovpn-cli -n" instead of "ip netns exec ovpn-cli" where possible. - Removed additional tests since we modified the existing ones to exercise the new code path. tools/testing/selftests/net/ovpn/common.sh | 8 +- tools/testing/selftests/net/ovpn/ovpn-cli.c | 266 +++++++++++++++++- .../selftests/net/ovpn/test-close-socket.sh | 4 +- tools/testing/selftests/net/ovpn/test-mark.sh | 6 +- tools/testing/selftests/net/ovpn/test.sh | 50 ++-- 5 files changed, 289 insertions(+), 45 deletions(-) diff --git a/tools/testing/selftests/net/ovpn/common.sh b/tools/testing/selftests/net/ovpn/common.sh index 2d844eb3aa6e..4f2510fb6d7f 100644 --- a/tools/testing/selftests/net/ovpn/common.sh +++ b/tools/testing/selftests/net/ovpn/common.sh @@ -221,7 +221,7 @@ ovpn_add_peer() { ${OVPN_UDP_PEERS_FILE} for p in $(seq 1 ${OVPN_NUM_PEERS}); do - ip netns exec "${server_ns}" ${OVPN_CLI} \ + "${OVPN_CLI}" -n "${server_ns}" \ new_key tun0 ${p} 1 0 ${OVPN_ALG} 0 \ data64.key done @@ -244,7 +244,7 @@ ovpn_add_peer() { ip netns exec "${peer_ns}" ${OVPN_CLI} new_peer \ tun${1} ${PEER_ID} ${TX_ID} ${LPORT} ${RADDR} \ ${RPORT} - ip netns exec "${peer_ns}" ${OVPN_CLI} new_key tun${1} \ + "${OVPN_CLI}" -n "${peer_ns}" new_key tun${1} \ ${PEER_ID} 1 0 ${OVPN_ALG} 1 data64.key fi else @@ -252,8 +252,8 @@ ovpn_add_peer() { (ip netns exec "${server_ns}" ${OVPN_CLI} listen tun0 \ 1 ${M_ID} ${OVPN_TCP_PEERS_FILE} && { for p in $(seq 1 ${OVPN_NUM_PEERS}); do - ip netns exec "${server_ns}" \ - ${OVPN_CLI} new_key tun0 ${p} \ + "${OVPN_CLI}" -n "${server_ns}" \ + new_key tun0 ${p} \ 1 0 ${OVPN_ALG} 0 data64.key done }) & diff --git a/tools/testing/selftests/net/ovpn/ovpn-cli.c b/tools/testing/selftests/net/ovpn/ovpn-cli.c index d40953375c86..6ea39b3ecc29 100644 --- a/tools/testing/selftests/net/ovpn/ovpn-cli.c +++ b/tools/testing/selftests/net/ovpn/ovpn-cli.c @@ -6,6 +6,7 @@ * Author: Antonio Quartulli */ +#include #include #include #include @@ -21,6 +22,7 @@ #include #include #include +#include #include #include @@ -121,6 +123,8 @@ struct ovpn_ctx { unsigned int ifindex; char ifname[IFNAMSIZ]; + const char *target_netns_name; + int target_netnsid; enum ovpn_mode mode; bool mode_set; @@ -224,6 +228,10 @@ static struct nl_ctx *nl_ctx_alloc_flags(struct ovpn_ctx *ovpn, int cmd, if (ovpn->ifindex > 0) NLA_PUT_U32(ctx->nl_msg, OVPN_A_IFINDEX, ovpn->ifindex); + if (ovpn->target_netnsid >= 0) + NLA_PUT_S32(ctx->nl_msg, OVPN_A_TARGET_NETNSID, + ovpn->target_netnsid); + return ctx; nla_put_failure: err_msg: @@ -1365,12 +1373,196 @@ static int ovpn_rt_send(struct nlmsghdr *payload, pid_t peer, return ret; } +static int ovpn_parse_netnsid(struct nlmsghdr *msg, void *arg) +{ + struct rtgenmsg *gen; + struct rtattr *attr; + int len; + + if (msg->nlmsg_type != RTM_NEWNSID || + msg->nlmsg_len < NLMSG_LENGTH(sizeof(*gen))) + return -EINVAL; + + gen = NLMSG_DATA(msg); + len = NLMSG_PAYLOAD(msg, sizeof(*gen)); + attr = (struct rtattr *)((char *)gen + NLMSG_ALIGN(sizeof(*gen))); + + for (; RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) { + if (attr->rta_type != NETNSA_NSID) + continue; + + if (RTA_PAYLOAD(attr) < sizeof(int32_t)) + return -EINVAL; + + memcpy(arg, RTA_DATA(attr), sizeof(int32_t)); + return 0; + } + + return -ENOENT; +} + +struct ovpn_netns_req { + struct nlmsghdr n; + struct rtgenmsg g; + char buf[64]; +}; + +static int ovpn_get_netnsid(int netns_fd, int *netnsid) +{ + struct ovpn_netns_req req = { 0 }; + uint32_t fd = netns_fd; + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.g)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_GETNSID; + req.g.rtgen_family = AF_UNSPEC; + + if (ovpn_addattr(&req.n, sizeof(req), NETNSA_FD, &fd, + sizeof(fd)) < 0) + return -EMSGSIZE; + + *netnsid = NETNSA_NSID_NOT_ASSIGNED; + return ovpn_rt_send(&req.n, 0, 0, ovpn_parse_netnsid, netnsid); +} + +static int ovpn_assign_netnsid(int netns_fd) +{ + /* NETNSA_NSID_NOT_ASSIGNED asks the kernel to allocate an NSID. */ + int32_t netnsid = NETNSA_NSID_NOT_ASSIGNED; + struct ovpn_netns_req req = { 0 }; + uint32_t fd = netns_fd; + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.g)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_NEWNSID; + req.g.rtgen_family = AF_UNSPEC; + + if (ovpn_addattr(&req.n, sizeof(req), NETNSA_NSID, &netnsid, + sizeof(netnsid)) < 0 || + ovpn_addattr(&req.n, sizeof(req), NETNSA_FD, &fd, + sizeof(fd)) < 0) + return -EMSGSIZE; + + return ovpn_rt_send(&req.n, 0, 0, NULL, NULL); +} + +/* Get the target NSID relative to the current netns, assigning one if necessary. */ +static int ovpn_get_or_assign_netnsid(int netns_fd) +{ + int netnsid, ret; + + ret = ovpn_get_netnsid(netns_fd, &netnsid); + if (ret < 0) + return ret; + + if (netnsid >= 0) + return netnsid; + + ret = ovpn_assign_netnsid(netns_fd); + if (ret < 0 && ret != -EEXIST) + return ret; + + ret = ovpn_get_netnsid(netns_fd, &netnsid); + if (ret < 0) + return ret; + + if (netnsid < 0) + return -ENOENT; + + return netnsid; +} + +static int ovpn_parse_ifindex(struct nlmsghdr *msg, void *arg) +{ + struct ifinfomsg *ifinfo; + + if (msg->nlmsg_type != RTM_NEWLINK || + msg->nlmsg_len < NLMSG_LENGTH(sizeof(*ifinfo))) + return -EINVAL; + + ifinfo = NLMSG_DATA(msg); + if (ifinfo->ifi_index <= 0) + return -EINVAL; + + *(unsigned int *)arg = ifinfo->ifi_index; + + return 0; +} + struct ovpn_link_req { struct nlmsghdr n; struct ifinfomsg i; char buf[256]; }; +static int ovpn_get_ifindex(int netnsid, const char *ifname, + unsigned int *ifindex) +{ + struct ovpn_link_req req = { 0 }; + int32_t id = netnsid; + + 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_UNSPEC; + + if (ovpn_addattr(&req.n, sizeof(req), IFLA_TARGET_NETNSID, &id, + sizeof(id)) < 0 || + ovpn_addattr(&req.n, sizeof(req), IFLA_IFNAME, ifname, + strlen(ifname) + 1) < 0) + return -EMSGSIZE; + + return ovpn_rt_send(&req.n, 0, 0, ovpn_parse_ifindex, ifindex); +} + +#define NETNS_RUN_DIR "/var/run/netns" + +static int ovpn_resolve_netns(struct ovpn_ctx *ovpn) +{ + int target_fd, ret; + char path[PATH_MAX]; + int len; + + if (!ovpn->target_netns_name[0] || + strchr(ovpn->target_netns_name, '/')) { + fprintf(stderr, "invalid network namespace name: %s\n", + ovpn->target_netns_name); + return -EINVAL; + } + + len = snprintf(path, sizeof(path), "%s/%s", NETNS_RUN_DIR, + ovpn->target_netns_name); + if (len < 0 || len >= (int)sizeof(path)) + return -ENAMETOOLONG; + + target_fd = open(path, O_RDONLY); + if (target_fd < 0) { + ret = errno; + fprintf(stderr, "cannot open network namespace %s: %s\n", + ovpn->target_netns_name, strerror(ret)); + return -ret; + } + + ovpn->target_netnsid = ovpn_get_or_assign_netnsid(target_fd); + if (ovpn->target_netnsid < 0) { + ret = ovpn->target_netnsid; + fprintf(stderr, "cannot resolve network namespace ID for %s\n", + ovpn->target_netns_name); + goto out; + } + + ret = ovpn_get_ifindex(ovpn->target_netnsid, ovpn->ifname, + &ovpn->ifindex); + if (ret < 0) { + fprintf(stderr, "cannot find interface %s in namespace %s\n", + ovpn->ifname, ovpn->target_netns_name); + } +out: + close(target_fd); + + return ret; +} + static int ovpn_new_iface(struct ovpn_ctx *ovpn) { struct rtattr *linkinfo, *data; @@ -1662,8 +1854,10 @@ static int ovpn_listen_mcast(void) static void usage(const char *cmd) { fprintf(stderr, - "Usage %s [arguments..]\n", + "Usage %s [-n ] [arguments..]\n", cmd); + fprintf(stderr, + "\t-n netns: run peer/key operations on an interface in the named network namespace\n"); fprintf(stderr, "where can be one of the following\n\n"); fprintf(stderr, "* new_iface [mode]: create new ovpn interface\n"); @@ -1966,6 +2160,26 @@ static enum ovpn_cmd ovpn_parse_cmd(const char *cmd) return CMD_INVALID; } +static bool ovpn_cmd_supports_target_netns(enum ovpn_cmd cmd) +{ + switch (cmd) { + case CMD_LISTEN: + case CMD_CONNECT: + case CMD_NEW_PEER: + case CMD_NEW_MULTI_PEER: + case CMD_SET_PEER: + case CMD_DEL_PEER: + case CMD_GET_PEER: + case CMD_NEW_KEY: + case CMD_DEL_KEY: + case CMD_GET_KEY: + case CMD_SWAP_KEYS: + return true; + default: + return false; + } +} + /* Send process to background and waits for signal. * * This helper is called at the end of commands @@ -2021,6 +2235,7 @@ static int ovpn_run_cmd(struct ovpn_ctx *ovpn) } peer_ctx.ifindex = ovpn->ifindex; + peer_ctx.target_netnsid = ovpn->target_netnsid; peer_ctx.sa_family = ovpn->sa_family; peer_ctx.asymm_id = ovpn->asymm_id; @@ -2107,6 +2322,7 @@ static int ovpn_run_cmd(struct ovpn_ctx *ovpn) struct ovpn_ctx peer_ctx = { 0 }; peer_ctx.ifindex = ovpn->ifindex; + peer_ctx.target_netnsid = ovpn->target_netnsid; peer_ctx.socket = ovpn->socket; peer_ctx.sa_family = AF_UNSPEC; peer_ctx.asymm_id = ovpn->asymm_id; @@ -2179,13 +2395,19 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[]) strscpy(ovpn->ifname, argv[2], IFNAMSIZ - 1); ovpn->ifname[IFNAMSIZ - 1] = '\0'; - /* all commands, except NEW_IFNAME, needs an ifindex */ + /* all commands, except NEW_IFACE, need an ifindex */ if (ovpn->cmd != CMD_NEW_IFACE) { - ovpn->ifindex = if_nametoindex(ovpn->ifname); - if (!ovpn->ifindex) { - fprintf(stderr, "cannot find interface: %s\n", - strerror(errno)); - return -1; + if (ovpn->target_netns_name) { + ret = ovpn_resolve_netns(ovpn); + if (ret < 0) + return ret; + } else { + ovpn->ifindex = if_nametoindex(ovpn->ifname); + if (!ovpn->ifindex) { + fprintf(stderr, "cannot find interface: %s\n", + strerror(errno)); + return -1; + } } } @@ -2432,29 +2654,51 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[]) int main(int argc, char *argv[]) { struct ovpn_ctx ovpn; + const char *program = argv[0]; int ret; + memset(&ovpn, 0, sizeof(ovpn)); + if (argc > 1 && !strcmp(argv[1], "-n")) { + if (argc < 4) { + fprintf(stderr, + "Error: -n requires a namespace and a command.\n\n"); + usage(program); + return -EINVAL; + } + + ovpn.target_netns_name = argv[2]; + argc -= 2; + argv += 2; + } + if (argc < 2) { - usage(argv[0]); + usage(program); return -1; } - memset(&ovpn, 0, sizeof(ovpn)); + ovpn.target_netnsid = NETNSA_NSID_NOT_ASSIGNED; ovpn.sa_family = AF_UNSPEC; ovpn.cipher = OVPN_CIPHER_ALG_NONE; ovpn.cmd = ovpn_parse_cmd(argv[1]); if (ovpn.cmd == CMD_INVALID) { fprintf(stderr, "Error: unknown command.\n\n"); - usage(argv[0]); + usage(program); return -1; } + if (ovpn.target_netns_name && + !ovpn_cmd_supports_target_netns(ovpn.cmd)) { + fprintf(stderr, "Error: -n is not supported for command %s.\n", + argv[1]); + return -EOPNOTSUPP; + } + ret = ovpn_parse_cmd_args(&ovpn, argc, argv); if (ret < 0) { fprintf(stderr, "Error: invalid arguments.\n\n"); if (ret == -EINVAL) - usage(argv[0]); + usage(program); return ret; } diff --git a/tools/testing/selftests/net/ovpn/test-close-socket.sh b/tools/testing/selftests/net/ovpn/test-close-socket.sh index ec9a51bbf3c9..0e4e33b2973f 100755 --- a/tools/testing/selftests/net/ovpn/test-close-socket.sh +++ b/tools/testing/selftests/net/ovpn/test-close-socket.sh @@ -40,10 +40,10 @@ ovpn_prepare_network() { for p in $(seq 1 ${OVPN_NUM_PEERS}); do peer_ns="ovpn_peer${p}" ovpn_cmd_ok "set peer0 timeout for peer ${p}" \ - ip netns exec ovpn_peer0 ${OVPN_CLI} set_peer tun0 \ + "${OVPN_CLI}" -n ovpn_peer0 set_peer tun0 \ ${p} 60 120 ovpn_cmd_ok "set peer${p} timeout for peer ${p}" \ - ip netns exec "${peer_ns}" ${OVPN_CLI} set_peer \ + "${OVPN_CLI}" -n "${peer_ns}" set_peer \ tun${p} $((p + OVPN_ID_OFFSET)) 60 120 done } diff --git a/tools/testing/selftests/net/ovpn/test-mark.sh b/tools/testing/selftests/net/ovpn/test-mark.sh index 7c1d56e9c525..8a9ca52f86be 100755 --- a/tools/testing/selftests/net/ovpn/test-mark.sh +++ b/tools/testing/selftests/net/ovpn/test-mark.sh @@ -42,7 +42,7 @@ ovpn_mark_prepare_network() { ASYMM "${OVPN_UDP_PEERS_FILE}" "${MARK}" for p in $(seq 1 3); do ovpn_cmd_ok "install server key for peer ${p}" \ - ip netns exec ovpn_peer0 "${OVPN_CLI}" new_key tun0 \ + "${OVPN_CLI}" -n ovpn_peer0 new_key tun0 \ "${p}" 1 0 "${OVPN_ALG}" 0 data64.key done @@ -53,10 +53,10 @@ ovpn_mark_prepare_network() { for p in $(seq 1 3); do peer_ns="ovpn_peer${p}" ovpn_cmd_ok "set peer0 timeout for peer ${p}" \ - ip netns exec ovpn_peer0 "${OVPN_CLI}" set_peer tun0 \ + "${OVPN_CLI}" -n ovpn_peer0 set_peer tun0 \ "${p}" 60 120 ovpn_cmd_ok "set peer${p} timeout for peer ${p}" \ - ip netns exec "${peer_ns}" "${OVPN_CLI}" set_peer \ + "${OVPN_CLI}" -n "${peer_ns}" set_peer \ tun"${p}" $((p + OVPN_ID_OFFSET)) 60 120 done } diff --git a/tools/testing/selftests/net/ovpn/test.sh b/tools/testing/selftests/net/ovpn/test.sh index 9b5610837032..468dd7985c8f 100755 --- a/tools/testing/selftests/net/ovpn/test.sh +++ b/tools/testing/selftests/net/ovpn/test.sh @@ -48,10 +48,10 @@ ovpn_prepare_network() { for p in $(seq 1 ${OVPN_NUM_PEERS}); do peer_ns="ovpn_peer${p}" ovpn_cmd_ok "set peer0 timeout for peer ${p}" \ - ip netns exec ovpn_peer0 ${OVPN_CLI} set_peer tun0 \ + "${OVPN_CLI}" -n ovpn_peer0 set_peer tun0 \ ${p} 60 120 ovpn_cmd_ok "set peer${p} timeout for peer ${p}" \ - ip netns exec "${peer_ns}" ${OVPN_CLI} set_peer \ + "${OVPN_CLI}" -n "${peer_ns}" set_peer \ tun${p} $((p + OVPN_ID_OFFSET)) 60 120 done } @@ -151,14 +151,14 @@ ovpn_run_key_rollover() { for p in $(seq 1 ${OVPN_NUM_PEERS}); do peer_ns="ovpn_peer${p}" ovpn_cmd_ok "add secondary key on peer0 for peer ${p}" \ - ip netns exec ovpn_peer0 ${OVPN_CLI} new_key tun0 \ + "${OVPN_CLI}" -n ovpn_peer0 new_key tun0 \ ${p} 2 1 ${OVPN_ALG} 0 data64.key ovpn_cmd_ok "add secondary key on peer${p} for peer ${p}" \ - ip netns exec "${peer_ns}" ${OVPN_CLI} new_key tun${p} \ + "${OVPN_CLI}" -n "${peer_ns}" new_key tun${p} \ $((p + OVPN_ID_OFFSET)) 2 1 ${OVPN_ALG} 1 \ data64.key ovpn_cmd_ok "swap keys on peer${p}" \ - ip netns exec "${peer_ns}" ${OVPN_CLI} swap_keys \ + "${OVPN_CLI}" -n "${peer_ns}" swap_keys \ tun${p} $((p + OVPN_ID_OFFSET)) done } @@ -166,22 +166,22 @@ ovpn_run_key_rollover() { ovpn_run_queries() { ovpn_log "Querying all peers:" - ovpn_cmd_ok "query all peers from peer0" \ - ip netns exec ovpn_peer0 ${OVPN_CLI} get_peer tun0 - ovpn_cmd_ok "query all peers from peer1" \ - ip netns exec ovpn_peer1 ${OVPN_CLI} get_peer tun1 + ovpn_cmd_ok "query all peers on peer0" \ + "${OVPN_CLI}" -n ovpn_peer0 get_peer tun0 + ovpn_cmd_ok "query all peers on peer1" \ + "${OVPN_CLI}" -n ovpn_peer1 get_peer tun1 ovpn_log "Querying peer 1:" - ovpn_cmd_ok "query peer 1 from peer0" \ - ip netns exec ovpn_peer0 ${OVPN_CLI} get_peer tun0 1 + ovpn_cmd_ok "query peer 1 on peer0" \ + "${OVPN_CLI}" -n ovpn_peer0 get_peer tun0 1 } ovpn_query_peer_missing() { ovpn_log "Querying non-existent peer 20:" ovpn_cmd_fail "query missing peer 20 on peer0" \ - ip netns exec ovpn_peer0 ${OVPN_CLI} get_peer tun0 20 + "${OVPN_CLI}" -n ovpn_peer0 get_peer tun0 20 } ovpn_run_peer_cleanup() { @@ -191,9 +191,9 @@ ovpn_run_peer_cleanup() { ovpn_log "Deleting peer 1:" ovpn_cmd_ok "delete peer1 on peer0" \ - ip netns exec ovpn_peer0 ${OVPN_CLI} del_peer tun0 1 + "${OVPN_CLI}" -n ovpn_peer0 del_peer tun0 1 ovpn_cmd_ok "delete peer1 on peer1" \ - ip netns exec ovpn_peer1 ${OVPN_CLI} del_peer tun1 \ + "${OVPN_CLI}" -n ovpn_peer1 del_peer tun1 \ $((1 + OVPN_ID_OFFSET)) ovpn_log "Querying keys:" @@ -201,10 +201,10 @@ ovpn_run_peer_cleanup() { for p in $(seq 2 ${OVPN_NUM_PEERS}); do peer_ns="ovpn_peer${p}" ovpn_cmd_ok "query peer${p} key 1" \ - ip netns exec "${peer_ns}" ${OVPN_CLI} get_key tun${p} \ + "${OVPN_CLI}" -n "${peer_ns}" get_key tun${p} \ $((p + OVPN_ID_OFFSET)) 1 ovpn_cmd_ok "query peer${p} key 2" \ - ip netns exec "${peer_ns}" ${OVPN_CLI} get_key tun${p} \ + "${OVPN_CLI}" -n "${peer_ns}" get_key tun${p} \ $((p + OVPN_ID_OFFSET)) 2 done } @@ -217,16 +217,16 @@ ovpn_run_traffic_delete_peer() { ovpn_run_bg ping_pid ip netns exec ovpn_peer2 ping -qf -w 4 5.5.5.1 sleep 2 ovpn_cmd_ok "delete peer0 peer 2" \ - ip netns exec ovpn_peer0 ${OVPN_CLI} del_peer tun0 2 + "${OVPN_CLI}" -n ovpn_peer0 del_peer tun0 2 if [ "${OVPN_PROTO}" == "TCP" ]; then # In TCP mode this command is expected to fail for both peers. ovpn_cmd_mayfail "delete peer2 peer 2 (TCP non-fatal)" \ - ip netns exec ovpn_peer2 ${OVPN_CLI} del_peer tun2 \ + "${OVPN_CLI}" -n ovpn_peer2 del_peer tun2 \ $((2 + OVPN_ID_OFFSET)) else - ovpn_cmd_ok "delete peer2 peer 2" ip netns exec ovpn_peer2 \ - ${OVPN_CLI} del_peer tun2 $((2 + OVPN_ID_OFFSET)) + ovpn_cmd_ok "delete peer2 peer 2" "${OVPN_CLI}" -n ovpn_peer2 \ + del_peer tun2 $((2 + OVPN_ID_OFFSET)) fi wait "${ping_pid}" || true @@ -241,10 +241,10 @@ ovpn_run_key_cleanup() { for p in $(seq 3 ${OVPN_NUM_PEERS}); do peer_ns="ovpn_peer${p}" ovpn_cmd_ok "delete key 1 for peer${p}" \ - ip netns exec "${peer_ns}" ${OVPN_CLI} del_key tun${p} \ + "${OVPN_CLI}" -n "${peer_ns}" del_key tun${p} \ $((p + OVPN_ID_OFFSET)) 1 ovpn_cmd_ok "delete key 2 for peer${p}" \ - ip netns exec "${peer_ns}" ${OVPN_CLI} del_key tun${p} \ + "${OVPN_CLI}" -n "${peer_ns}" del_key tun${p} \ $((p + OVPN_ID_OFFSET)) 2 done } @@ -258,11 +258,11 @@ ovpn_run_timeouts() { for p in $(seq 3 ${OVPN_NUM_PEERS}); do # Non-fatal: this may fail in some protocol modes. ovpn_cmd_mayfail "set peer0 timeout for peer ${p} (non-fatal)" \ - ip netns exec ovpn_peer0 ${OVPN_CLI} set_peer tun0 \ + "${OVPN_CLI}" -n ovpn_peer0 set_peer tun0 \ ${p} 3 3 peer_ns="ovpn_peer${p}" ovpn_cmd_ok "disable timeout on peer${p} while peer0 adjusts \ - state" ip netns exec "${peer_ns}" ${OVPN_CLI} set_peer \ + state" "${OVPN_CLI}" -n "${peer_ns}" set_peer \ tun${p} $((p + OVPN_ID_OFFSET)) 0 0 done # wait for peers to timeout @@ -273,7 +273,7 @@ ovpn_run_timeouts() { for p in $(seq 3 ${OVPN_NUM_PEERS}); do peer_ns="ovpn_peer${p}" ovpn_cmd_ok "set peer${p} P2P timeout" \ - ip netns exec "${peer_ns}" ${OVPN_CLI} set_peer \ + "${OVPN_CLI}" -n "${peer_ns}" set_peer \ tun${p} $((p + OVPN_ID_OFFSET)) 3 3 done sleep 5