[Openvpn-devel,v5] Don't "undo" ifconfig on exit if it wasn't done

Message ID 20220629170555.116087-1-maximilian.fillinger@foxcrypto.com
State Changes Requested
Headers show
Series [Openvpn-devel,v5] Don't "undo" ifconfig on exit if it wasn't done | expand

Commit Message

Maximilian Fillinger June 29, 2022, 7:05 a.m. UTC
When running with --ifconfig-noexec, OpenVPN does not execute ifconfig,
but on exit, it still tries to "undo" the configuration it would have
done. This patch fixes it by extracting an undo_ifconfig() function from
close_tun(). The undo function is called before close_tun(), but only if
--ifconfig-noexec isn't set. This is symmetric to how open_tun() and
do_ifconfig() are used.

v2: Fix tabs-vs-spaces.
v3: Fix another style mistake.
v4: Move undo_ifconfig{4,6}() out of #ifdef TARGET_LINUX.
v5: Keep ctx argument in close_tun().

Signed-off-by: Max Fillinger <maximilian.fillinger@foxcrypto.com>
---
 src/openvpn/init.c |   4 ++
 src/openvpn/tun.c  | 161 +++++++++++++++++++++++----------------------
 src/openvpn/tun.h  |   8 +++
 3 files changed, 96 insertions(+), 77 deletions(-)

Comments

Antonio Quartulli July 11, 2022, 5:38 a.m. UTC | #1
Hi,

On 29/06/2022 19:05, Max Fillinger wrote:
> When running with --ifconfig-noexec, OpenVPN does not execute ifconfig,
> but on exit, it still tries to "undo" the configuration it would have
> done. This patch fixes it by extracting an undo_ifconfig() function from
> close_tun(). The undo function is called before close_tun(), but only if
> --ifconfig-noexec isn't set. This is symmetric to how open_tun() and
> do_ifconfig() are used.
> 
> v2: Fix tabs-vs-spaces.
> v3: Fix another style mistake.
> v4: Move undo_ifconfig{4,6}() out of #ifdef TARGET_LINUX.
> v5: Keep ctx argument in close_tun().
> 
> Signed-off-by: Max Fillinger <maximilian.fillinger@foxcrypto.com>
> ---
>   src/openvpn/init.c |   4 ++
>   src/openvpn/tun.c  | 161 +++++++++++++++++++++++----------------------
>   src/openvpn/tun.h  |   8 +++
>   3 files changed, 96 insertions(+), 77 deletions(-)
> 
> diff --git a/src/openvpn/init.c b/src/openvpn/init.c
> index c9d05c31..f5ca2be8 100644
> --- a/src/openvpn/init.c
> +++ b/src/openvpn/init.c
> @@ -1873,6 +1873,10 @@ do_close_tun_simple(struct context *c)
>       msg(D_CLOSE, "Closing TUN/TAP interface");
>       if (c->c1.tuntap)
>       {
> +        if (!c->options.ifconfig_noexec)
> +        {
> +            undo_ifconfig(c->c1.tuntap, &c->net_ctx);
> +        }
>           close_tun(c->c1.tuntap, &c->net_ctx);
>           c->c1.tuntap = NULL;
>       }
> diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
> index 5e7b8c49..feee83ef 100644
> --- a/src/openvpn/tun.c
> +++ b/src/openvpn/tun.c
> @@ -1605,6 +1605,89 @@ do_ifconfig(struct tuntap *tt, const char *ifname, int tun_mtu,
>       net_ctx_free(ctx);
>   }
>   
> +static void
> +undo_ifconfig_ipv4(struct tuntap *tt, openvpn_net_ctx_t *ctx)
> +{
> +#if defined(TARGET_LINUX)
> +    int netbits = netmask_to_netbits2(tt->remote_netmask);
> +
> +    if (is_tun_p2p(tt))
> +    {
> +        if (net_addr_ptp_v4_del(ctx, tt->actual_name, &tt->local,
> +                                &tt->remote_netmask) < 0)
> +        {
> +            msg(M_WARN, "Linux can't del IP from iface %s",
> +                tt->actual_name);
> +        }
> +    }
> +    else
> +    {
> +        if (net_addr_v4_del(ctx, tt->actual_name, &tt->local, netbits) < 0)
> +        {
> +            msg(M_WARN, "Linux can't del IP from iface %s",
> +                tt->actual_name);
> +        }
> +    }
> +#elif !defined(_WIN32)  /* if !defined(TARGET_LINUX) && !defined(_WIN32) */
> +    struct argv argv = argv_new();
> +
> +    argv_printf(&argv, "%s %s 0.0.0.0", IFCONFIG_PATH, tt->actual_name);
> +
> +    argv_msg(M_INFO, &argv);
> +    openvpn_execve_check(&argv, NULL, 0, "Generic ip addr del failed");
> +
> +    argv_free(&argv);
> +#endif /* ifdef TARGET_LINUX */

The comment should contain the same guard as the first if, i.e. "if 
defined(TARGET_LINUX)"

> +       /* Empty for _WIN32. */

Indentation of comment is wrong

> +}
> +
> +static void
> +undo_ifconfig_ipv6(struct tuntap *tt, openvpn_net_ctx_t *ctx)
> +{
> +#if defined(TARGET_LINUX)
> +    if (net_addr_v6_del(ctx, tt->actual_name, &tt->local_ipv6,
> +                        tt->netbits_ipv6) < 0)
> +    {
> +        msg(M_WARN, "Linux can't del IPv6 from iface %s", tt->actual_name);
> +    }
> +#elif !defined(_WIN32)  /* if !defined(TARGET_LINUX) && !defined(_WIN32) */
> +    struct gc_arena gc = gc_new();
> +    const char *ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, gc);
> +    struct argv argv = argv_new();
> +
> +    argv_printf(&argv, "%s %s del %s/%d", IFCONFIG_PATH, tt->actual_name,
> +                ifconfig_ipv6_local, tt->netbits_ipv6);
> +
> +    argv_msg(M_INFO, &argv);
> +    openvpn_execve_check(&argv, NULL, 0, "Linux ip -6 addr del failed");

Error message is wrong, because it should say "Generic" as above.
> +
> +    argv_free(&argv);
> +    gc_free(&gc);
> +#endif /* ifdef TARGET_LINUX */

as above

> +       /* Empty for _WIN32. */

as above

> +}
> +
> +void
> +undo_ifconfig(struct tuntap *tt, openvpn_net_ctx_t *ctx)
> +{
> +    if (tt->type != DEV_TYPE_NULL)
> +    {
> +        if (tt->did_ifconfig_setup)
> +        {
> +            undo_ifconfig_ipv4(tt, ctx);
> +        }
> +
> +        if (tt->did_ifconfig_ipv6_setup)
> +        {
> +            undo_ifconfig_ipv6(tt, ctx);
> +        }
> +
> +        /* release resources potentially allocated during undo */
> +        net_ctx_reset(ctx);
> +    }
> +

spurious empty line

> +}
> +
>   static void
>   clear_tuntap(struct tuntap *tuntap)
>   {
> @@ -1846,7 +1929,7 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
>               msg(M_INFO, "DCO device %s opened", tunname);
>           }
>           else
> -#endif
> +#endif /* if defined(TARGET_LINUX) */
>           {
>               if (!dynamic_opened)
>               {
> @@ -2176,87 +2259,11 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node,
>   
>   #endif /* ENABLE_FEATURE_TUN_PERSIST */
>   
> -static void
> -undo_ifconfig_ipv4(struct tuntap *tt, openvpn_net_ctx_t *ctx)
> -{
> -#if defined(TARGET_LINUX)
> -    int netbits = netmask_to_netbits2(tt->remote_netmask);
> -
> -    if (is_tun_p2p(tt))
> -    {
> -        if (net_addr_ptp_v4_del(ctx, tt->actual_name, &tt->local,
> -                                &tt->remote_netmask) < 0)
> -        {
> -            msg(M_WARN, "Linux can't del IP from iface %s",
> -                tt->actual_name);
> -        }
> -    }
> -    else
> -    {
> -        if (net_addr_v4_del(ctx, tt->actual_name, &tt->local, netbits) < 0)
> -        {
> -            msg(M_WARN, "Linux can't del IP from iface %s",
> -                tt->actual_name);
> -        }
> -    }
> -#else  /* ifndef TARGET_LINUX */
> -    struct argv argv = argv_new();
> -
> -    argv_printf(&argv, "%s %s 0.0.0.0", IFCONFIG_PATH, tt->actual_name);
> -
> -    argv_msg(M_INFO, &argv);
> -    openvpn_execve_check(&argv, NULL, 0, "Generic ip addr del failed");
> -
> -    argv_free(&argv);
> -#endif /* ifdef TARGET_LINUX */
> -}
> -
> -static void
> -undo_ifconfig_ipv6(struct tuntap *tt, openvpn_net_ctx_t *ctx)
> -{
> -#if defined(TARGET_LINUX)
> -    if (net_addr_v6_del(ctx, tt->actual_name, &tt->local_ipv6,
> -                        tt->netbits_ipv6) < 0)
> -    {
> -        msg(M_WARN, "Linux can't del IPv6 from iface %s", tt->actual_name);
> -    }
> -#else  /* ifndef TARGET_LINUX */
> -    struct gc_arena gc = gc_new();
> -    const char *ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, gc);
> -    struct argv argv = argv_new();
> -
> -    argv_printf(&argv, "%s %s del %s/%d", IFCONFIG_PATH, tt->actual_name,
> -                ifconfig_ipv6_local, tt->netbits_ipv6);
> -
> -    argv_msg(M_INFO, &argv);
> -    openvpn_execve_check(&argv, NULL, 0, "Linux ip -6 addr del failed");
> -
> -    argv_free(&argv);
> -    gc_free(&gc);
> -#endif /* ifdef TARGET_LINUX */
> -}
> -
>   void
>   close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx)
>   {
>       ASSERT(tt);
>   
> -    if (tt->type != DEV_TYPE_NULL)
> -    {
> -        if (tt->did_ifconfig_setup)
> -        {
> -            undo_ifconfig_ipv4(tt, ctx);
> -        }
> -
> -        if (tt->did_ifconfig_ipv6_setup)
> -        {
> -            undo_ifconfig_ipv6(tt, ctx);
> -        }
> -
> -        /* release resources potentially allocated during undo */
> -        net_ctx_reset(ctx);
> -    }
> -
>   #ifdef TARGET_LINUX
>       if (!tt->options.disable_dco)
>       {
> diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
> index cf02bf43..6d4530f7 100644
> --- a/src/openvpn/tun.h
> +++ b/src/openvpn/tun.h
> @@ -300,6 +300,14 @@ void do_ifconfig_setenv(const struct tuntap *tt,
>   void do_ifconfig(struct tuntap *tt, const char *ifname, int tun_mtu,
>                    const struct env_set *es, openvpn_net_ctx_t *ctx);
>   
> +/**
> + * undo_ifconfig - undo configuration of the tunnel interface
> + *
> + * @param tt    the tuntap interface context
> + * @param ctx   the networking API opaque context
> + */
> +void undo_ifconfig(struct tuntap *tt, openvpn_net_ctx_t *ctx);
> +
>   bool is_dev_type(const char *dev, const char *dev_type, const char *match_type);
>   
>   int dev_type_enum(const char *dev, const char *dev_type);

Other than the nitpicks above, it looks good to me.

On top of that, most of the issues I highlighted already exist in the 
current code (it is just being moved). But this could be the chance to 
fix those on the fly.

Acked-by: Antonio Quartulli <a@unstable.cc>

Note: this change still requires more testing with non-linux/non-windows 
platforms.

Cheers,
Gert Doering Aug. 6, 2022, 4:20 a.m. UTC | #2
HI,

On Wed, Jun 29, 2022 at 07:05:55PM +0200, Max Fillinger wrote:
> When running with --ifconfig-noexec, OpenVPN does not execute ifconfig,
> but on exit, it still tries to "undo" the configuration it would have
> done. This patch fixes it by extracting an undo_ifconfig() function from
> close_tun(). The undo function is called before close_tun(), but only if
> --ifconfig-noexec isn't set. This is symmetric to how open_tun() and
> do_ifconfig() are used.
> 
> v2: Fix tabs-vs-spaces.
> v3: Fix another style mistake.
> v4: Move undo_ifconfig{4,6}() out of #ifdef TARGET_LINUX.
> v5: Keep ctx argument in close_tun().

I did what I promised too many weeks ago, and pushed this change
towards the buildbot army...  alas, they did what I was afraid they
would do: complained.

+-DPLUGIN_LIBDIR=\"/usr/local/lib/openvpn/plugins\"  -Wall -g -O2 -std=c99 -MT
+tun.o -MD -MP -MF .deps/tun.Tpo -c -o tun.o tun.c
tun.c:1656:73: error: passing 'struct gc_arena' to parameter of incompatible
+type 'struct gc_arena *'; take the address with &
    const char *ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, gc);
                                                                        ^~
                                                                        &
./socket.h:391:88: note: passing argument to parameter 'gc' here
const char *print_in6_addr(struct in6_addr addr6, unsigned int flags, struct
+gc_arena *gc);


so the "non-linux" bits of undo_ifconfig_ipv6() have been broken since
ever - but the patch now actually shows that to the compiler...

> +#elif !defined(_WIN32)  /* if !defined(TARGET_LINUX) && !defined(_WIN32) */
> +    struct gc_arena gc = gc_new();
> +    const char *ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, gc);
> +    struct argv argv = argv_new();

... and inded, this needs to be "&gc"...


Can you please send a v6 that changes this, and also rebases against
master?  The context has changed a bit, and one hunk (#endif /* comment */)
is no longer relevant, as that #endif never came into existance :-)

We should then be able to proceed more quickly...

thanks,

gert
Gert Doering Aug. 19, 2022, 7:04 a.m. UTC | #3
I had to apply a bit of pressure to get the patch to apply - it had
one hunk trying to fix an #endif which never existed :-) - and the context
of the last tun.c hunk needed adjustment (if (tun_dco_enabled(tt))) -
both fairly trivial, and no actual code change.

Pushed to the new and renovated buildbot army, to see if it would break
any platforms (very convenient, this ;-) ).

The change is as we have discussed, and I still think it's a good and
clean way forward.  The "undo_ifconfig_ipv4() / _ipv6()" functions
are as they have been before (except that they now get compiled for
all platforms except WIN32 - the "linux only" part was a 2.5.0 oversight),
and the calls moved 1:1 from (TARGET_LINUX) close_tun() to a new
undo_ifconfig() function.  tun.c is still a mess, but this helps a bit.

While staring at the diff, I kicked a spurious blank line from the
end of undo_ifconfig()... :-)

Your patch has been applied to the master branch.

commit 054f60fc5eaeacb90333e6b5e183459eb39a6d4f
Author: Max Fillinger
Date:   Wed Jun 29 19:05:55 2022 +0200

     Don't undo ifconfig on exit if it wasn't done

     Signed-off-by: Max Fillinger <maximilian.fillinger@foxcrypto.com>
     Acked-by: Antonio Quartulli <antonio@openvpn.net>
     Message-Id: <20220629170555.116087-1-maximilian.fillinger@foxcrypto.com>
     URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24610.html
     Signed-off-by: Gert Doering <gert@greenie.muc.de>


--
kind regards,

Gert Doering
Gert Doering Aug. 19, 2022, 8:26 a.m. UTC | #4
Hi,

On Fri, Aug 19, 2022 at 07:04:42PM +0200, Gert Doering wrote:
> I had to apply a bit of pressure to get the patch to apply - it had
> one hunk trying to fix an #endif which never existed :-) - and the context
> of the last tun.c hunk needed adjustment (if (tun_dco_enabled(tt))) -
> both fairly trivial, and no actual code change.

"This mail escaped".  Of course I have only applied v6, but I had the
mail for v5 still sitting around, and never sent it when I discovered
that &gc was missing the "&"...

> commit 054f60fc5eaeacb90333e6b5e183459eb39a6d4f

This commit does not exist (outside of my tree).

gert

Patch

diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index c9d05c31..f5ca2be8 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1873,6 +1873,10 @@  do_close_tun_simple(struct context *c)
     msg(D_CLOSE, "Closing TUN/TAP interface");
     if (c->c1.tuntap)
     {
+        if (!c->options.ifconfig_noexec)
+        {
+            undo_ifconfig(c->c1.tuntap, &c->net_ctx);
+        }
         close_tun(c->c1.tuntap, &c->net_ctx);
         c->c1.tuntap = NULL;
     }
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index 5e7b8c49..feee83ef 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -1605,6 +1605,89 @@  do_ifconfig(struct tuntap *tt, const char *ifname, int tun_mtu,
     net_ctx_free(ctx);
 }
 
+static void
+undo_ifconfig_ipv4(struct tuntap *tt, openvpn_net_ctx_t *ctx)
+{
+#if defined(TARGET_LINUX)
+    int netbits = netmask_to_netbits2(tt->remote_netmask);
+
+    if (is_tun_p2p(tt))
+    {
+        if (net_addr_ptp_v4_del(ctx, tt->actual_name, &tt->local,
+                                &tt->remote_netmask) < 0)
+        {
+            msg(M_WARN, "Linux can't del IP from iface %s",
+                tt->actual_name);
+        }
+    }
+    else
+    {
+        if (net_addr_v4_del(ctx, tt->actual_name, &tt->local, netbits) < 0)
+        {
+            msg(M_WARN, "Linux can't del IP from iface %s",
+                tt->actual_name);
+        }
+    }
+#elif !defined(_WIN32)  /* if !defined(TARGET_LINUX) && !defined(_WIN32) */
+    struct argv argv = argv_new();
+
+    argv_printf(&argv, "%s %s 0.0.0.0", IFCONFIG_PATH, tt->actual_name);
+
+    argv_msg(M_INFO, &argv);
+    openvpn_execve_check(&argv, NULL, 0, "Generic ip addr del failed");
+
+    argv_free(&argv);
+#endif /* ifdef TARGET_LINUX */
+       /* Empty for _WIN32. */
+}
+
+static void
+undo_ifconfig_ipv6(struct tuntap *tt, openvpn_net_ctx_t *ctx)
+{
+#if defined(TARGET_LINUX)
+    if (net_addr_v6_del(ctx, tt->actual_name, &tt->local_ipv6,
+                        tt->netbits_ipv6) < 0)
+    {
+        msg(M_WARN, "Linux can't del IPv6 from iface %s", tt->actual_name);
+    }
+#elif !defined(_WIN32)  /* if !defined(TARGET_LINUX) && !defined(_WIN32) */
+    struct gc_arena gc = gc_new();
+    const char *ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, gc);
+    struct argv argv = argv_new();
+
+    argv_printf(&argv, "%s %s del %s/%d", IFCONFIG_PATH, tt->actual_name,
+                ifconfig_ipv6_local, tt->netbits_ipv6);
+
+    argv_msg(M_INFO, &argv);
+    openvpn_execve_check(&argv, NULL, 0, "Linux ip -6 addr del failed");
+
+    argv_free(&argv);
+    gc_free(&gc);
+#endif /* ifdef TARGET_LINUX */
+       /* Empty for _WIN32. */
+}
+
+void
+undo_ifconfig(struct tuntap *tt, openvpn_net_ctx_t *ctx)
+{
+    if (tt->type != DEV_TYPE_NULL)
+    {
+        if (tt->did_ifconfig_setup)
+        {
+            undo_ifconfig_ipv4(tt, ctx);
+        }
+
+        if (tt->did_ifconfig_ipv6_setup)
+        {
+            undo_ifconfig_ipv6(tt, ctx);
+        }
+
+        /* release resources potentially allocated during undo */
+        net_ctx_reset(ctx);
+    }
+
+}
+
 static void
 clear_tuntap(struct tuntap *tuntap)
 {
@@ -1846,7 +1929,7 @@  open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
             msg(M_INFO, "DCO device %s opened", tunname);
         }
         else
-#endif
+#endif /* if defined(TARGET_LINUX) */
         {
             if (!dynamic_opened)
             {
@@ -2176,87 +2259,11 @@  tuncfg(const char *dev, const char *dev_type, const char *dev_node,
 
 #endif /* ENABLE_FEATURE_TUN_PERSIST */
 
-static void
-undo_ifconfig_ipv4(struct tuntap *tt, openvpn_net_ctx_t *ctx)
-{
-#if defined(TARGET_LINUX)
-    int netbits = netmask_to_netbits2(tt->remote_netmask);
-
-    if (is_tun_p2p(tt))
-    {
-        if (net_addr_ptp_v4_del(ctx, tt->actual_name, &tt->local,
-                                &tt->remote_netmask) < 0)
-        {
-            msg(M_WARN, "Linux can't del IP from iface %s",
-                tt->actual_name);
-        }
-    }
-    else
-    {
-        if (net_addr_v4_del(ctx, tt->actual_name, &tt->local, netbits) < 0)
-        {
-            msg(M_WARN, "Linux can't del IP from iface %s",
-                tt->actual_name);
-        }
-    }
-#else  /* ifndef TARGET_LINUX */
-    struct argv argv = argv_new();
-
-    argv_printf(&argv, "%s %s 0.0.0.0", IFCONFIG_PATH, tt->actual_name);
-
-    argv_msg(M_INFO, &argv);
-    openvpn_execve_check(&argv, NULL, 0, "Generic ip addr del failed");
-
-    argv_free(&argv);
-#endif /* ifdef TARGET_LINUX */
-}
-
-static void
-undo_ifconfig_ipv6(struct tuntap *tt, openvpn_net_ctx_t *ctx)
-{
-#if defined(TARGET_LINUX)
-    if (net_addr_v6_del(ctx, tt->actual_name, &tt->local_ipv6,
-                        tt->netbits_ipv6) < 0)
-    {
-        msg(M_WARN, "Linux can't del IPv6 from iface %s", tt->actual_name);
-    }
-#else  /* ifndef TARGET_LINUX */
-    struct gc_arena gc = gc_new();
-    const char *ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, gc);
-    struct argv argv = argv_new();
-
-    argv_printf(&argv, "%s %s del %s/%d", IFCONFIG_PATH, tt->actual_name,
-                ifconfig_ipv6_local, tt->netbits_ipv6);
-
-    argv_msg(M_INFO, &argv);
-    openvpn_execve_check(&argv, NULL, 0, "Linux ip -6 addr del failed");
-
-    argv_free(&argv);
-    gc_free(&gc);
-#endif /* ifdef TARGET_LINUX */
-}
-
 void
 close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx)
 {
     ASSERT(tt);
 
-    if (tt->type != DEV_TYPE_NULL)
-    {
-        if (tt->did_ifconfig_setup)
-        {
-            undo_ifconfig_ipv4(tt, ctx);
-        }
-
-        if (tt->did_ifconfig_ipv6_setup)
-        {
-            undo_ifconfig_ipv6(tt, ctx);
-        }
-
-        /* release resources potentially allocated during undo */
-        net_ctx_reset(ctx);
-    }
-
 #ifdef TARGET_LINUX
     if (!tt->options.disable_dco)
     {
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index cf02bf43..6d4530f7 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -300,6 +300,14 @@  void do_ifconfig_setenv(const struct tuntap *tt,
 void do_ifconfig(struct tuntap *tt, const char *ifname, int tun_mtu,
                  const struct env_set *es, openvpn_net_ctx_t *ctx);
 
+/**
+ * undo_ifconfig - undo configuration of the tunnel interface
+ *
+ * @param tt    the tuntap interface context
+ * @param ctx   the networking API opaque context
+ */
+void undo_ifconfig(struct tuntap *tt, openvpn_net_ctx_t *ctx);
+
 bool is_dev_type(const char *dev, const char *dev_type, const char *match_type);
 
 int dev_type_enum(const char *dev, const char *dev_type);