[Openvpn-devel,1/1] Allow to set ifmode for existing DCO interfaces in FreeBSD

Message ID AE20A784-506C-488B-9302-2D3AE775B168@opnsense.org
State Accepted
Headers show
Series [Openvpn-devel,1/1] Allow to set ifmode for existing DCO interfaces in FreeBSD | expand

Commit Message

Franco Fichtner May 28, 2024, 5:42 p.m. UTC
While prexisting devices work well TUN/TAP the DCO interfaces require
setting the ifmode which cannot be done by FreeBSD base tooling.  In
peer-to-peer mode this is not a problem because that is the default mode.
Subnet mode, however, will fail to be set and the resulting connection does
not start:

  Failed to create interface ovpns2 (SIOCSIFNAME): File exists (errno=17)
  DCO device ovpns2 already exists, won't be destroyed at shutdown
  /sbin/ifconfig ovpns2 10.1.8.1/24 mtu 1500 up
  ifconfig: in_exec_nl(): Empty IFA_LOCAL/IFA_ADDRESS
  ifconfig: ioctl (SIOCAIFADDR): Invalid argument
  FreeBSD ifconfig failed: external program exited with error status: 1
  Exiting due to fatal error

Slightly restructure the code to catch the specific error
condition and execute dco_set_ifmode() in this case as well.

Signed-off-by: Franco Fichtner <franco@opnsense.org>
---
 src/openvpn/dco_freebsd.c | 28 +++++++++++++++++-----------
 1 file changed, 17 insertions(+), 11 deletions(-)

 open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev)
 {
-    return create_interface(tt, dev);
+    int ret = create_interface(tt, dev);
+
+    if (ret >= 0 || ret == -EEXIST)
+    {
+        /* see "Interface Flags" in ifnet(9) */
+        int i = IFF_POINTOPOINT | IFF_MULTICAST;
+        if (tt->topology == TOP_SUBNET)
+        {
+            i = IFF_BROADCAST | IFF_MULTICAST;
+        }
+        dco_set_ifmode(&tt->dco, i);
+    }
+
+    return ret;
 }
   void

Comments

Gert Doering June 2, 2024, 3:50 p.m. UTC | #1
Acked-by: Gert Doering <gert@greenie.muc.de>

Thanks for the patch, and the explanation on IRC.  This is FreeBSD/DCO
specific, and makes the case

  ifconfig ovpn3 create
  openvpn --dev ovpn3 --dev-type tun --topology subnet ...

work correctly (without it, p2p ifconfig claims to work but ipv6 route
addition fails as well).

I have stared at the code (looks good) and tested on my FreeBSD 14/DCO
test VM with and without the patch, adding relevant test cases to the
t_client.rc setup.  With the patch, nothing breaks ;-) and the newly
considered cases work.

There is a slight catch - if the interface (ovpn3) has already been
brought "up", the dco_set_ifmode() call fails with 

  2024-06-02 14:54:06 dco_set_ifmode: failed to set ifmode=00008002: Device busy (errno=16)

.. so it will not work properly in all cases.

(Also, t_client.sh is not really suited for this sort of test today,
as the "compare pre/post ifconfig state" check fails on interface flags
- need to think more how to make this work)


The patch got badly whitespace mangled in transit (empty lines got
turned into extra indent in the following line, and thus).  As I said
on IRC, I'm happy to fix that for occasional submitters, but generally
recommend "git send-email" or pushing to gerrit to avoid outlook induced
garbage.

Your patch has been applied to the master and release/2.6 branch
(highly localized, and arguably a bugfix).

commit 82036c17c45d45c3fe8725f64b33720cb9c94dad (master)
commit 2f2ff186564c3999efaf48d734df95471ac22d84 (release/2.6)
Author: Franco Fichtner
Date:   Tue May 28 17:42:52 2024 +0000

     Allow to set ifmode for existing DCO interfaces in FreeBSD

     Signed-off-by: Franco Fichtner <franco@opnsense.org>
     Acked-by: Gert Doering <gert@greenie.muc.de>
     Message-Id: <AE20A784-506C-488B-9302-2D3AE775B168@opnsense.org>
     URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg28688.html
     Signed-off-by: Gert Doering <gert@greenie.muc.de>


--
kind regards,

Gert Doering
Franco Fichtner June 3, 2024, 7:17 a.m. UTC | #2
Hi Gert,

> On 2. Jun 2024, at 17:50, Gert Doering <gert@greenie.muc.de> wrote:
> 
> work correctly (without it, p2p ifconfig claims to work but ipv6 route
> addition fails as well).

I did not notice but all the better.

> There is a slight catch - if the interface (ovpn3) has already been
> brought "up", the dco_set_ifmode() call fails with 
> 
>  2024-06-02 14:54:06 dco_set_ifmode: failed to set ifmode=00008002: Device busy (errno=16)
> 
> .. so it will not work properly in all cases.

I mentioned this in the original work patch on our end:

https://github.com/opnsense/ports/commit/2dc9273a03577

However, I believe something on the OpenVPN end changed in 2.6
for DCO related changes prior as we ran into these integration
bugs:

https://github.com/opnsense/core/commit/c22f74a786

Which indicate the daemon stops working properly in these cases
anyway. This appeared to be a general problem, not directly related to
DCO as we weren't using it / lacked support on FreeBSD 13 at the
time. Did not dive too much into the OpenVPN code but it did verify
the suspicion and this needs to be addressed from the user side now
which is more than reasonable.

> The patch got badly whitespace mangled in transit (empty lines got
> turned into extra indent in the following line, and thus).  As I said
> on IRC, I'm happy to fix that for occasional submitters, but generally
> recommend "git send-email" or pushing to gerrit to avoid outlook induced
> garbage.

I'll improve my process for the next one. Thanks!


Cheers,
Franco

Patch

diff --git a/src/openvpn/dco_freebsd.c b/src/openvpn/dco_freebsd.c
index 577c65f8..7c8b29c9 100644
--- a/src/openvpn/dco_freebsd.c
+++ b/src/openvpn/dco_freebsd.c
@@ -219,6 +219,9 @@  create_interface(struct tuntap *tt, const char *dev)
     {
         ifr.ifr_data = (char *)dev;
     }
+
+    snprintf(tt->dco.ifname, IFNAMSIZ, "%s", ifr.ifr_data);
+
     ret = ioctl(tt->dco.fd, SIOCSIFNAME, &ifr);
     if (ret)
     {
@@ -229,16 +232,6 @@  create_interface(struct tuntap *tt, const char *dev)
         return ret;
     }
  -    snprintf(tt->dco.ifname, IFNAMSIZ, "%s", ifr.ifr_data);
-
-    /* see "Interface Flags" in ifnet(9) */
-    int i = IFF_POINTOPOINT | IFF_MULTICAST;
-    if (tt->topology == TOP_SUBNET)
-    {
-        i = IFF_BROADCAST | IFF_MULTICAST;
-    }
-    dco_set_ifmode(&tt->dco, i);
-
     return 0;
 }
  @@ -265,7 +258,20 @@ remove_interface(struct tuntap *tt)
 int