From patchwork Wed Jul 29 13:41:31 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ralf Lici X-Patchwork-Id: 5175 Return-Path: Delivered-To: patchwork@openvpn.net Received: by 2002:a05:7000:fd0b:b0:87d:ab56:3700 with SMTP id cw11csp1967281mac; Wed, 29 Jul 2026 06:42:19 -0700 (PDT) X-Forwarded-Encrypted: i=2; AHgh+RoB1NAbhmGoZmAifCmwC6AWnzDMxbzIyugCpDO4mn9XZmtoaNotk7gmR8/h/UxHAcnwA31fxLgCxv4=@openvpn.net X-Received: by 2002:a05:6808:1a0b:b0:490:afef:97b1 with SMTP id 5614622812f47-4ad5b9b9ce0mr3645258b6e.13.1785332539728; Wed, 29 Jul 2026 06:42:19 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1785332539; cv=none; d=google.com; s=arc-20260327; b=sUWlESaYDN2qqRvPvmqjvuRlG5yXIiT9Y7WUR/gCpbaleD0LHpaVCdu1yDnZxOrGj6 V5YcePqBNCFNdRS66KZtrtqEvOd9nIFQVLHTJhV3cN7BhMqrQMrNCzlkNDaVlr4Uaa2c iLH21MKCiw1aTDRXw/OzLKHCgjzAOPDCJPKG8tO1+lY3Xr28fBf7V3ZXNfpVf3cuEgLC 7l9Ts0oH8i84Z/b9Z2qVDsa0QtAiOepFQpmSGopeX3K67yPUKsqa8SjZXe4yD+s4B/R7 Yday65A6xuidwCkXkNmj2KzYCHLtwUVNL9ITdclzCCtH7DSwReiazUkPVyO1c3tzkis/ B3vQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=errors-to:content-transfer-encoding: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=dtH3URR6j0z2uTrXAAhJ1RZu0SvUkO0jnmtFcJD4yUs=; fh=4NbAC/LsuMLI0S0hprUlLSLCiHwg6SCAifhH718Jh0Q=; b=gJRXq/qhs00rHCCXPVZjg927ZN2jg8PEwV04DYPXDK+SfKdIfPf97+73+fmjCT2nes 3qoRzTT/iMUN9DaggUpSsqv7+A8U5O1+YyYF8fTtvq0etz9wDXysM924MoMm3CeDNwbV rA5GyYbQwM8t1ch+WNCFLp8pKbyuND8EfCDLMffaZ85lvOseDgGT5B+XnND/3Rj8mm+z Fm4MaDgMNxiF+ywJ3B9SYVcfFk5E9F1DLPqYKhlclwBKZe5v6eRQo/PHW5RsYFQV2IQ8 snihUuorcA02NCEDVHoWcZ+klIIDvsESEWN+sKbPnPVcrUbljVYNvD8aWJCEnd6gNobq llXQ==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@lists.sourceforge.net header.s=beta header.b=ALjJntSS; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=SGvjXgfu; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=MkGqKHdM; dkim=neutral (body hash did not verify) header.i=@mandelbit.com header.s=MBO0001 header.b="x27FP/c7"; 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 5614622812f47-4ad6eca93b2si1596873b6e.3.2026.07.29.06.42.19 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 29 Jul 2026 06:42:19 -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=ALjJntSS; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=SGvjXgfu; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=MkGqKHdM; dkim=neutral (body hash did not verify) header.i=@mandelbit.com header.s=MBO0001 header.b="x27FP/c7"; 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: 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:Cc:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=dtH3URR6j0z2uTrXAAhJ1RZu0SvUkO0jnmtFcJD4yUs=; b=ALjJntSS6rz/nATJZBZYUo1YXD CeKub4HGnHAb1Ap0wiMf/ec3my5F+CLhuPBUekuDlK4XRyDFevx+Jbntp9P1JztRwuZkgDI5KINfa wjdQYxys40pwvpOmKxMyNlojTq8NstPqi/oiZZYzK60pdxE8v/IFT1+jUGg25NAQHo7M=; 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 1wp4Y6-0001R2-Vu; Wed, 29 Jul 2026 13:42:16 +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 1wp4Xu-0001Qo-0p for openvpn-devel@lists.sourceforge.net; Wed, 29 Jul 2026 13:42:03 +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=Gq1go1a5RnvOt4FRF7UF1JUcrFzeideIha7URoR+KtM=; b=SGvjXgfupYc48rP/R/1G33V47L tFnL0348YrAikZEnOJPoSDn/8kV/eDzc3bYtkslWLcqQM5syEQF7j5Hc2z14gV0NzVwGosdTWK3ex eKTf0y1opUfJ7KkeQeQz4O7maOFnxDIf4S1eblr+B7rqpNU7eDo+wZb2YvvZVLuFLmOg=; 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=Gq1go1a5RnvOt4FRF7UF1JUcrFzeideIha7URoR+KtM=; b=MkGqKHdM8/cngSui+1sxlyCkSn Q7PO1INuIeUYEARduaczhi5SF2vQ1kdQIw43xxHCufj2H8zWHyJ+dr3teHZ9jMNcboRoWYl0BmzKV HSlaOXhzHFz7UBKoiJnK+bAlFXp+u0Mp4rvHLmwvIYHML4azCOS8K+NsvtwdnTOQc19c=; Received: from mout-b-210.mailbox.org ([195.10.208.40]) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.95) id 1wp4Xo-00086n-E8 for openvpn-devel@lists.sourceforge.net; Wed, 29 Jul 2026 13:42:03 +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-210.mailbox.org (Postfix) with ESMTPS id 4h9D5X32TMzFrCG; Wed, 29 Jul 2026 15:41:52 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mandelbit.com; s=MBO0001; t=1785332512; 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=Gq1go1a5RnvOt4FRF7UF1JUcrFzeideIha7URoR+KtM=; b=x27FP/c7/udQGaxPa51KQDHmK3C28vLyd8O35nhm1ojlAq0yRzdg/9+PqzO8Tj16xIa+sS I4WrEHh/pdjdvbQ2k2JBf4RnwNaa14/QYiUfeu0eMX6UdVy8RpB1FdV72jhShIQW3pZr98 +qHitL2/5DX2ibcui2d3fUIx8AeL1MZM4Lo0sGffYC4soqDpL8mUV80/fmBnCgwjzT3wWG XxHw/N/u8NdDhHMG7YrWIB/INWS9xW4lb+FlaRaUAcfFjOWTnxjaL+BCSRLRnALxwNObN2 dSagVuDCaylb+yAXFSfFnYsBaZoHKtXMylu/BPIw6CL3omAyHiUZeE6D7qNtpw== From: Ralf Lici To: openvpn-devel@lists.sourceforge.net Date: Wed, 29 Jul 2026 15:41:31 +0200 Message-ID: <256a0f1828ffa78a4c1c002b5561812eb1d4635e.1785330764.git.ralf@mandelbit.com> In-Reply-To: References: 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: Add a get_iface command to ovpn-cli that queries RTM_GETLINK and parses the ovpn rtnl linkinfo nest, including IFLA_OVPN_MODE. When called without an interface name, make get_iface issue a filtered RTM_GETLINK dump for ovpn links. Use the dump form in the main ovpn selftest to verify that each namespace reports exactly one ov [...] Content analysis details: (-0.2 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid X-Headers-End: 1wp4Xo-00086n-E8 Subject: [Openvpn-devel] [PATCH ovpn net v2 2/2] selftests: ovpn: add rtnl link mode check 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: , Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox X-GMAIL-THRID: 1872056852967929928 X-GMAIL-MSGID: 1872056852967929928 Add a get_iface command to ovpn-cli that queries RTM_GETLINK and parses the ovpn rtnl linkinfo nest, including IFLA_OVPN_MODE. When called without an interface name, make get_iface issue a filtered RTM_GETLINK dump for ovpn links. Use the dump form in the main ovpn selftest to verify that each namespace reports exactly one ovpn interface with the expected name and mode: MP for the server and P2P for the clients. This exercises the rtnl link-info dump path used by ovpn_fill_info. Signed-off-by: Ralf Lici --- New patch added in v2. tools/testing/selftests/net/ovpn/ovpn-cli.c | 157 +++++++++++++++++++- tools/testing/selftests/net/ovpn/test.sh | 48 +++++- 2 files changed, 200 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/net/ovpn/ovpn-cli.c b/tools/testing/selftests/net/ovpn/ovpn-cli.c index f4effa7580c0..f633dcea2833 100644 --- a/tools/testing/selftests/net/ovpn/ovpn-cli.c +++ b/tools/testing/selftests/net/ovpn/ovpn-cli.c @@ -78,6 +78,7 @@ struct nl_ctx { enum ovpn_cmd { CMD_INVALID, CMD_NEW_IFACE, + CMD_GET_IFACE, CMD_DEL_IFACE, CMD_LISTEN, CMD_CONNECT, @@ -1417,6 +1418,143 @@ static int ovpn_new_iface(struct ovpn_ctx *ovpn) return ret; } +static const char *ovpn_mode_str(enum ovpn_mode mode) +{ + switch (mode) { + case OVPN_MODE_P2P: + return "P2P"; + case OVPN_MODE_MP: + return "MP"; + } + + return "unknown"; +} + +static int ovpn_handle_iface(struct nlmsghdr *msg, void *arg) +{ + struct nlattr *linkinfo[IFLA_INFO_MAX + 1]; + struct nlattr *data[IFLA_OVPN_MAX + 1]; + struct nlattr *attrs[IFLA_MAX + 1]; + const struct ifinfomsg *ifinfo; + bool dump = *(bool *)arg; + enum ovpn_mode mode; + const char *kind; + int ret; + + if (msg->nlmsg_type != RTM_NEWLINK) { + fprintf(stderr, "unexpected rtnl message type: %u\n", + msg->nlmsg_type); + return -EINVAL; + } + + if (msg->nlmsg_len < NLMSG_LENGTH(sizeof(*ifinfo))) { + fprintf(stderr, "truncated rtnl link message\n"); + return -EINVAL; + } + + ifinfo = NLMSG_DATA(msg); + ret = nla_parse(attrs, IFLA_MAX, (struct nlattr *)IFLA_RTA(ifinfo), + IFLA_PAYLOAD(msg), NULL); + if (ret < 0) { + fprintf(stderr, "cannot parse rtnl link attributes: %d\n", ret); + return ret; + } + + if (!attrs[IFLA_LINKINFO]) { + fprintf(stderr, "missing linkinfo for ifindex %d\n", + ifinfo->ifi_index); + return -EINVAL; + } + + ret = nla_parse(linkinfo, IFLA_INFO_MAX, + nla_data(attrs[IFLA_LINKINFO]), + nla_len(attrs[IFLA_LINKINFO]), NULL); + if (ret < 0) { + fprintf(stderr, "cannot parse linkinfo attributes: %d\n", ret); + return ret; + } + + if (!linkinfo[IFLA_INFO_KIND]) { + fprintf(stderr, "missing link kind for ifindex %d\n", + ifinfo->ifi_index); + return -EINVAL; + } + + kind = nla_get_string(linkinfo[IFLA_INFO_KIND]); + if (strcmp(kind, OVPN_FAMILY_NAME)) { + fprintf(stderr, "unexpected link kind: %s\n", kind); + return -EINVAL; + } + + if (!linkinfo[IFLA_INFO_DATA]) { + fprintf(stderr, "missing ovpn link data for ifindex %d\n", + ifinfo->ifi_index); + return -EINVAL; + } + + ret = nla_parse(data, IFLA_OVPN_MAX, + nla_data(linkinfo[IFLA_INFO_DATA]), + nla_len(linkinfo[IFLA_INFO_DATA]), NULL); + if (ret < 0) { + fprintf(stderr, "cannot parse ovpn link data: %d\n", ret); + return ret; + } + + if (!data[IFLA_OVPN_MODE]) { + fprintf(stderr, "missing ovpn mode for ifindex %d\n", + ifinfo->ifi_index); + return -EINVAL; + } + + mode = nla_get_u8(data[IFLA_OVPN_MODE]); + fprintf(stdout, "ifindex %d\n", ifinfo->ifi_index); + if (attrs[IFLA_IFNAME]) + fprintf(stdout, "ifname %s\n", + nla_get_string(attrs[IFLA_IFNAME])); + fprintf(stdout, "kind %s\n", kind); + fprintf(stdout, "mode %s\n", ovpn_mode_str(mode)); + + return dump; +} + +static int ovpn_get_iface(struct ovpn_ctx *ovpn) +{ + uint32_t ext_filter_mask = RTEXT_FILTER_SKIP_STATS; + struct ovpn_link_req req = { 0 }; + bool dump = !ovpn->ifindex; + struct rtattr *linkinfo; + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i)); + req.n.nlmsg_flags = NLM_F_REQUEST; + if (dump) + req.n.nlmsg_flags |= NLM_F_DUMP; + req.n.nlmsg_type = RTM_GETLINK; + + /* don't include stats */ + if (ovpn_addattr(&req.n, sizeof(req), IFLA_EXT_MASK, + &ext_filter_mask, sizeof(ext_filter_mask)) < 0) + return -1; + + /* if no iface was provided as argument, dump only the ovpn ifaces */ + if (dump) { + linkinfo = ovpn_nest_start(&req.n, sizeof(req), IFLA_LINKINFO); + if (!linkinfo) + return -1; + + if (ovpn_addattr(&req.n, sizeof(req), IFLA_INFO_KIND, + OVPN_FAMILY_NAME, + strlen(OVPN_FAMILY_NAME) + 1) < 0) + return -1; + + ovpn_nest_end(&req.n, linkinfo); + } + + req.i.ifi_family = AF_PACKET; + req.i.ifi_index = ovpn->ifindex; + + return ovpn_rt_send(&req.n, 0, 0, ovpn_handle_iface, &dump); +} + static int ovpn_del_iface(struct ovpn_ctx *ovpn) { struct ovpn_link_req req = { 0 }; @@ -1672,6 +1810,11 @@ static void usage(const char *cmd) fprintf(stderr, "\t\t- P2P for peer-to-peer mode (i.e. client)\n"); fprintf(stderr, "\t\t- MP for multi-peer mode (i.e. server)\n"); + fprintf(stderr, + "* get_iface [iface]: dump ovpn interface attributes\n"); + fprintf(stderr, + "\tiface: optional ovpn interface name; omit it to dump all ovpn interfaces in the current netns\n"); + fprintf(stderr, "* del_iface : delete ovpn interface\n"); fprintf(stderr, "\tiface: ovpn interface name\n"); @@ -1926,6 +2069,9 @@ static enum ovpn_cmd ovpn_parse_cmd(const char *cmd) if (!strcmp(cmd, "new_iface")) return CMD_NEW_IFACE; + if (!strcmp(cmd, "get_iface")) + return CMD_GET_IFACE; + if (!strcmp(cmd, "del_iface")) return CMD_DEL_IFACE; @@ -1994,6 +2140,9 @@ static int ovpn_run_cmd(struct ovpn_ctx *ovpn) case CMD_NEW_IFACE: ret = ovpn_new_iface(ovpn); break; + case CMD_GET_IFACE: + ret = ovpn_get_iface(ovpn); + break; case CMD_DEL_IFACE: ret = ovpn_del_iface(ovpn); break; @@ -2170,8 +2319,9 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[]) { int ret; - /* no args required for LISTEN_MCAST */ - if (ovpn->cmd == CMD_LISTEN_MCAST) + /* no args required for LISTEN_MCAST or GET_IFACE in dump mode */ + if (ovpn->cmd == CMD_LISTEN_MCAST || + (ovpn->cmd == CMD_GET_IFACE && argc == 2)) return 0; /* all commands need an ifname */ @@ -2181,7 +2331,7 @@ 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 CMD_NEW_IFACE, needs an ifindex */ if (ovpn->cmd != CMD_NEW_IFACE) { ovpn->ifindex = if_nametoindex(ovpn->ifname); if (!ovpn->ifindex) { @@ -2207,6 +2357,7 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[]) } ovpn->mode_set = true; break; + case CMD_GET_IFACE: case CMD_DEL_IFACE: break; case CMD_LISTEN: diff --git a/tools/testing/selftests/net/ovpn/test.sh b/tools/testing/selftests/net/ovpn/test.sh index 9b5610837032..acd4aed0d254 100755 --- a/tools/testing/selftests/net/ovpn/test.sh +++ b/tools/testing/selftests/net/ovpn/test.sh @@ -56,6 +56,49 @@ ovpn_prepare_network() { done } +ovpn_check_iface() { + local expected + local peer_ns + local actual + local ifname + local count + local mode + local dump + local p + + for p in $(seq 0 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ifname="tun${p}" + mode="P2P" + [ "${p}" -eq 0 ] && mode="MP" + + if ! dump=$(ip netns exec "${peer_ns}" ${OVPN_CLI} get_iface); + then + printf 'failed to dump ovpn ifaces in %s\n' "${peer_ns}" + return 1 + fi + + # ensure exactly 1 iface is reported + count=$(printf '%s\n' "${dump}" | grep -c '^ifindex ' || true) + if [ "${count}" -ne 1 ]; then + printf 'unexpected iface count (%d) in %s\n' \ + "${count}" "${peer_ns}" + return 1 + fi + + # validate the stable interface attributes + printf -v expected 'ifname %s\nmode %s' "${ifname}" "${mode}" + actual=$(printf '%s\n' "${dump}" | + sed -n -e '/^ifname /p' -e '/^mode /p') + if [[ "${actual}" != "${expected}" ]]; then + printf 'unexpected ovpn interface in %s:\n' "${peer_ns}" + printf 'expected:\n%s\nactual:\n%s\n' \ + "${expected}" "${actual}" + return 1 + fi + done +} + ovpn_run_basic_traffic() { local p local header1 @@ -293,15 +336,16 @@ trap ovpn_stage_err ERR ktap_print_header if [ "${OVPN_FLOAT}" == "1" ]; then - ktap_set_plan 13 + ktap_set_plan 14 else - ktap_set_plan 12 + ktap_set_plan 13 fi ovpn_cleanup modprobe -q ovpn || true ovpn_run_stage "setup network topology" ovpn_prepare_network +ovpn_run_stage "validate iface modes" ovpn_check_iface ovpn_run_stage "run baseline data traffic" ovpn_run_basic_traffic ovpn_run_stage "run LAN traffic behind peer1" ovpn_run_lan_traffic [ "${OVPN_FLOAT}" == "1" ] && ovpn_run_stage "run floating peer checks" \