@@ -787,7 +787,20 @@ dnl
AC_DEFINE(ENABLE_DCO, 1, [Enable data channel offload for Linux])
AC_MSG_NOTICE([Enabled ovpn-dco support for Linux])
;;
-
+ *-*-freebsd*)
+ DCO_CFLAGS="-I${DCO_SOURCEDIR}"
+ saved_CFLAGS="${CFLAGS}"
+ CFLAGS="${CFLAGS} ${DCO_CFLAGS}"
+ AC_CHECK_HEADERS(
+ [if_ovpn.h],
+ ,
+ [AC_MSG_ERROR([if_ovpn.h is missing (use DCO_SOURCEDIR to set path to it, CFLAGS=${CFLAGS})])]
+ )
+ CFLAGS=${saved_CFLAGS}
+ LIBS="${LIBS} -lnv"
+ AC_DEFINE(ENABLE_DCO, 1, [Enable data channel offload for FreeBSD])
+ AC_MSG_NOTICE([Enabled ovpn-dco support for FreeBSD])
+ ;;
*-mingw*)
AC_MSG_NOTICE([NOTE: --enable-dco ignored on Windows because it's always enabled])
;;
@@ -54,6 +54,7 @@ openvpn_SOURCES = \
crypto_openssl.c crypto_openssl.h \
crypto_mbedtls.c crypto_mbedtls.h \
dco.c dco.h dco_internal.h \
+ dco_freebsd.c dco_freebsd.h \
dco_linux.c dco_linux.h \
dco_win.c dco_win.h \
dhcp.c dhcp.h \
new file mode 100644
@@ -0,0 +1,559 @@
+/*
+ * Interface to FreeBSD dco networking code
+ *
+ * Copyright (C) 2022 Rubicon Communications, LLC (Netgate). All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#if defined(ENABLE_DCO) && defined(TARGET_FREEBSD)
+
+#include "syshead.h"
+
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/nv.h>
+#include <netinet/in.h>
+
+#include "dco_freebsd.h"
+#include "dco.h"
+#include "tun.h"
+#include "crypto.h"
+#include "ssl_common.h"
+
+static nvlist_t *
+sockaddr_to_nvlist(const struct sockaddr *sa)
+{
+ nvlist_t *nvl = nvlist_create(0);
+
+ nvlist_add_number(nvl, "af", sa->sa_family);
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ {
+ const struct sockaddr_in *in = (const struct sockaddr_in *)sa;
+ nvlist_add_binary(nvl, "address", &in->sin_addr, sizeof(in->sin_addr));
+ nvlist_add_number(nvl, "port", in->sin_port);
+ break;
+ }
+ case AF_INET6:
+ {
+ const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *)sa;
+ nvlist_add_binary(nvl, "address", &in6->sin6_addr, sizeof(in6->sin6_addr));
+ nvlist_add_number(nvl, "port", in6->sin6_port);
+ break;
+ }
+ default:
+ abort();
+ }
+
+ return (nvl);
+}
+
+int
+dco_new_peer(dco_context_t *dco, unsigned int peerid, int sd,
+ struct sockaddr *localaddr, struct sockaddr *remoteaddr,
+ struct in_addr *remote_in4, struct in6_addr *remote_in6)
+{
+ struct ifdrv drv;
+ nvlist_t *nvl;
+ int ret;
+
+ nvl = nvlist_create(0);
+
+ msg(D_DCO_DEBUG, "%s: peer-id %d, fd %d", __func__, peerid, sd);
+
+ if (localaddr)
+ nvlist_add_nvlist(nvl, "local", sockaddr_to_nvlist(localaddr));
+
+ if (remoteaddr)
+ nvlist_add_nvlist(nvl, "remote", sockaddr_to_nvlist(remoteaddr));
+
+ nvlist_add_number(nvl, "fd", sd);
+
+ bzero(&drv, sizeof(drv));
+ snprintf(drv.ifd_name, IFNAMSIZ, "%s", dco->ifname);
+ drv.ifd_cmd = OVPN_SET_PEER;
+ drv.ifd_data = nvlist_pack(nvl, &drv.ifd_len);
+
+ ret = ioctl(dco->fd, SIOCSDRVSPEC, &drv);
+ free(drv.ifd_data);
+ nvlist_destroy(nvl);
+ if (ret)
+ {
+ msg(D_DCO, "Failed to create new peer %d", errno);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+open_fd(dco_context_t *dco)
+{
+ int ret;
+
+ ret = pipe2(dco->pipefd, O_CLOEXEC);
+ if (ret != 0)
+ return -1;
+
+ dco->fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ dco->dco_packet_in = alloc_buf(PAGE_SIZE);
+
+ return dco->fd;
+}
+
+static void
+close_fd(dco_context_t *dco)
+{
+ close(dco->pipefd[0]);
+ close(dco->pipefd[1]);
+ close(dco->fd);
+}
+
+bool
+ovpn_dco_init(dco_context_t *dco)
+{
+ if (open_fd(dco) < 0)
+ {
+ msg(D_DCO, "Failed to open socket");
+ return false;
+ }
+ return true;
+}
+
+static int
+create_interface(struct tuntap *tt, const char *dev)
+{
+ int ret;
+ struct ifreq ifr;
+
+ bzero(&ifr, sizeof(ifr));
+
+ /* Attempt to load the module. Ignore errors, because it might already be
+ * loaded, or built into the kernel. */
+ (void)kldload("if_ovpn");
+
+ /* Create ovpnx first, then rename it. */
+ snprintf(ifr.ifr_name, IFNAMSIZ, "ovpn");
+ ret = ioctl(tt->dco.fd, SIOCIFCREATE2, &ifr);
+ if (ret)
+ {
+ msg(D_DCO, "Failed to create interface %s: %d", ifr.ifr_name, errno);
+ return ret;
+ }
+
+ /* Rename */
+ if (! strcmp(dev, "tun"))
+ ifr.ifr_data = "ovpn";
+ else
+ ifr.ifr_data = dev;
+ ret = ioctl(tt->dco.fd, SIOCSIFNAME, &ifr);
+ if (ret)
+ {
+ /* Delete the created interface again. */
+ (void)ioctl(tt->dco.fd, SIOCIFDESTROY, &ifr);
+ msg(D_DCO, "Failed to create interface %s: %d", ifr.ifr_data, errno);
+ return ret;
+ }
+
+ snprintf(tt->dco.ifname, IFNAMSIZ, "%s", ifr.ifr_data);
+ tt->actual_name = string_alloc(tt->dco.ifname, NULL);
+
+ return 0;
+}
+
+ static int
+remove_interface(struct tuntap *tt)
+{
+ int ret;
+ struct ifreq ifr;
+
+ bzero(&ifr, sizeof(ifr));
+ snprintf(ifr.ifr_name, IFNAMSIZ, "%s", tt->dco.ifname);
+
+ ret = ioctl(tt->dco.fd, SIOCIFDESTROY, &ifr);
+ if (ret)
+ {
+ msg(D_DCO, "Failed to remove interface %s: %d", ifr.ifr_name, errno);
+ return ret;
+ }
+
+ tt->dco.ifname[0] = 0;
+
+ return 0;
+}
+
+int
+open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev)
+{
+ int ret;
+
+ ret = create_interface(tt, dev);
+
+ if (ret < 0)
+ msg(D_DCO, "Failed to create interface");
+
+ return ret;
+}
+
+void
+close_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx)
+{
+ remove_interface(tt);
+ close_fd(&tt->dco);
+}
+
+int
+dco_swap_keys(dco_context_t *dco, unsigned int peerid)
+{
+ struct ifdrv drv;
+ int ret;
+
+ msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peerid);
+
+ bzero(&drv, sizeof(drv));
+ snprintf(drv.ifd_name, IFNAMSIZ, "%s", dco->ifname);
+ drv.ifd_cmd = OVPN_SWAP_KEYS;
+
+ ret = ioctl(dco->fd, SIOCSDRVSPEC, &drv);
+ if (ret)
+ {
+ msg(D_DCO, "Failed to swap keys %d", errno);
+ return ret;
+ }
+
+ return 0;
+}
+
+int
+dco_del_peer(dco_context_t *dco, unsigned int peerid)
+{
+ struct ifdrv drv;
+ int ret;
+
+ bzero(&drv, sizeof(drv));
+ snprintf(drv.ifd_name, IFNAMSIZ, "%s", dco->ifname);
+ drv.ifd_cmd = OVPN_DEL_PEER;
+
+ ret = ioctl(dco->fd, SIOCSDRVSPEC, &drv);
+ if (ret)
+ {
+ msg(D_DCO, "Failed to delete peer %d", errno);
+ return ret;
+ }
+
+ return 0;
+}
+
+int
+dco_del_key(dco_context_t *dco, unsigned int peerid,
+ dco_key_slot_t slot)
+{
+ struct ifdrv drv;
+ nvlist_t *nvl;
+ int ret;
+
+ msg(D_DCO, "%s: peer-id %d, slot %d", __func__, peerid, slot);
+
+ nvl = nvlist_create(0);
+ nvlist_add_number(nvl, "slot", slot);
+ nvlist_add_number(nvl, "peerid", peerid);
+
+ bzero(&drv, sizeof(drv));
+ snprintf(drv.ifd_name, IFNAMSIZ, "%s", dco->ifname);
+ drv.ifd_cmd = OVPN_DEL_KEY;
+ drv.ifd_data = nvlist_pack(nvl, &drv.ifd_len);
+
+ ret = ioctl(dco->fd, SIOCSDRVSPEC, &drv);
+ free(drv.ifd_data);
+ nvlist_destroy(nvl);
+ if (ret)
+ {
+ msg(D_DCO, "Failed to delete key %d", errno);
+ return ret;
+ }
+
+ return 0;
+}
+
+static nvlist_t *
+key_to_nvlist(const uint8_t *key, const uint8_t *implicit_iv, const char *ciphername)
+{
+ nvlist_t *nvl;
+ size_t key_len;
+
+ nvl = nvlist_create(0);
+
+ nvlist_add_string(nvl, "cipher", ciphername);
+
+ if (strcmp(ciphername, "none") != 0)
+ {
+ key_len = cipher_kt_key_size(ciphername);
+
+ nvlist_add_binary(nvl, "key", key, key_len);
+ nvlist_add_binary(nvl, "iv", implicit_iv, 8);
+ }
+
+ return (nvl);
+}
+
+static int
+start_tun(dco_context_t *dco)
+{
+ struct ifdrv drv;
+ int ret;
+
+ bzero(&drv, sizeof(drv));
+ snprintf(drv.ifd_name, IFNAMSIZ, "%s", dco->ifname);
+ drv.ifd_cmd = OVPN_START_VPN;
+
+ ret = ioctl(dco->fd, SIOCSDRVSPEC, &drv);
+ if (ret)
+ {
+ msg(D_DCO, "Failed to start vpn %d", errno);
+ return ret;
+ }
+
+ return 0;
+}
+
+int
+dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid,
+ dco_key_slot_t slot,
+ const uint8_t *encrypt_key, const uint8_t *encrypt_iv,
+ const uint8_t *decrypt_key, const uint8_t *decrypt_iv,
+ const char *ciphername)
+{
+ struct ifdrv drv;
+ nvlist_t *nvl;
+ int ret;
+
+ msg(D_DCO_DEBUG, "%s: slot %d, key-id %d, peer-id %d, cipher %s",
+ __func__, slot, keyid, peerid, ciphername);
+
+ nvl = nvlist_create(0);
+
+ nvlist_add_number(nvl, "slot", slot);
+ nvlist_add_number(nvl, "keyid", keyid);
+ nvlist_add_number(nvl, "peerid", peerid);
+
+ nvlist_add_nvlist(nvl, "encrypt",
+ key_to_nvlist(encrypt_key, encrypt_iv, ciphername));
+ nvlist_add_nvlist(nvl, "decrypt",
+ key_to_nvlist(decrypt_key, decrypt_iv, ciphername));
+
+ bzero(&drv, sizeof(drv));
+ snprintf(drv.ifd_name, IFNAMSIZ, "%s", dco->ifname);
+ drv.ifd_cmd = OVPN_NEW_KEY;
+ drv.ifd_data = nvlist_pack(nvl, &drv.ifd_len);
+
+ ret = ioctl(dco->fd, SIOCSDRVSPEC, &drv);
+ free(drv.ifd_data);
+ nvlist_destroy(nvl);
+ if (ret) {
+ msg(D_DCO, "Failed to set key %d", errno);
+ return ret;
+ }
+
+ return start_tun(dco);
+}
+
+int
+ovpn_set_peer(dco_context_t *dco, unsigned int peerid,
+ unsigned int keepalive_interval, unsigned int keepalive_timeout)
+{
+ struct ifdrv drv;
+ nvlist_t *nvl;
+ int ret;
+
+ nvl = nvlist_create(0);
+ nvlist_add_number(nvl, "interval", keepalive_interval);
+ nvlist_add_number(nvl, "timeout", keepalive_timeout);
+
+ bzero(&drv, sizeof(drv));
+ snprintf(drv.ifd_name, IFNAMSIZ, "%s", dco->ifname);
+ drv.ifd_cmd = OVPN_SET_TIMEOUT;
+ drv.ifd_data = nvlist_pack(nvl, &drv.ifd_len);
+
+ ret = ioctl(dco->fd, SIOCSDRVSPEC, &drv);
+ free(drv.ifd_data);
+ nvlist_destroy(nvl);
+ if (ret)
+ {
+ msg(D_DCO, "Failed to set keepalive %d", errno);
+ return ret;
+ }
+
+ return 0;
+}
+
+int
+dco_do_read(dco_context_t *dco)
+{
+ struct ifdrv drv;
+ uint8_t buf[4096];
+ nvlist_t *nvl;
+ const uint8_t *pkt;
+ size_t pktlen;
+ int ret;
+
+ bzero(&drv, sizeof(drv));
+ snprintf(drv.ifd_name, IFNAMSIZ, "%s", dco->ifname);
+ drv.ifd_cmd = OVPN_GET_PKT;
+ drv.ifd_data = buf;
+ drv.ifd_len = sizeof(buf);
+
+ ret = ioctl(dco->fd, SIOCGDRVSPEC, &drv);
+ if (ret)
+ {
+ msg(D_DCO, "Failed to read control packet %d", errno);
+ return errno;
+ }
+
+ nvl = nvlist_unpack(buf, drv.ifd_len, 0);
+ if (nvl == NULL)
+ {
+ msg(D_DCO, "Failed to unpack nvlist");
+ return EINVAL;
+ }
+
+ dco->dco_message_peer_id = nvlist_get_number(nvl, "peerid");
+
+ pkt = nvlist_get_binary(nvl, "packet", &pktlen);
+ memcpy(BPTR(&dco->dco_packet_in), pkt, pktlen);
+ dco->dco_packet_in.len = pktlen;
+
+ nvlist_destroy(nvl);
+
+ return 0;
+}
+
+int
+dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf)
+{
+ struct ifdrv drv;
+ nvlist_t *nvl;
+ int ret;
+
+ nvl = nvlist_create(0);
+
+ nvlist_add_binary(nvl, "packet", BSTR(buf), BLEN(buf));
+ nvlist_add_number(nvl, "peerid", peer_id);
+
+ bzero(&drv, sizeof(drv));
+ snprintf(drv.ifd_name, IFNAMSIZ, "%s", dco->ifname);
+ drv.ifd_cmd = OVPN_SEND_PKT;
+ drv.ifd_data = nvlist_pack(nvl, &drv.ifd_len);
+
+ ret = ioctl(dco->fd, SIOCSDRVSPEC, &drv);
+ free(drv.ifd_data);
+ nvlist_destroy(nvl);
+ if (ret)
+ {
+ msg(D_DCO, "Failed to send control packet %d", errno);
+ return ret;
+ }
+
+ return BLEN(buf);
+}
+
+bool
+dco_available(int msglevel)
+{
+ struct if_clonereq ifcr;
+ char *buf = NULL;
+ int fd;
+ int ret;
+ bool available = false;
+
+ fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return false;
+
+ bzero(&ifcr, sizeof(ifcr));
+
+ ret = ioctl(fd, SIOCIFGCLONERS, &ifcr);
+ if (ret != 0)
+ goto out;
+
+ buf = malloc(ifcr.ifcr_total * IFNAMSIZ);
+
+ ifcr.ifcr_count = ifcr.ifcr_total;
+ ifcr.ifcr_buffer = buf;
+ ret = ioctl(fd, SIOCIFGCLONERS, &ifcr);
+ if (ret != 0)
+ goto out;
+
+ for (int i = 0; i < ifcr.ifcr_total; i++)
+ {
+ if (strcmp(buf + (i * IFNAMSIZ), "ovpn") == 0)
+ {
+ available = true;
+ goto out;
+ }
+ }
+
+out:
+ free(buf);
+ close(fd);
+
+ return available;
+}
+
+void
+dco_event_set(dco_context_t *dco, struct event_set *es, void *arg)
+{
+ struct ifdrv drv;
+ nvlist_t *nvl;
+ uint8_t buf[128];
+ int ret;
+
+ bzero(&drv, sizeof(drv));
+ snprintf(drv.ifd_name, IFNAMSIZ, "%s", dco->ifname);
+ drv.ifd_cmd = OVPN_POLL_PKT;
+ drv.ifd_len = sizeof(buf);
+ drv.ifd_data = buf;
+
+ ret = ioctl(dco->fd, SIOCGDRVSPEC, &drv);
+ if (ret)
+ {
+ msg(D_DCO, "Failed to poll for packets %d", errno);
+ return;
+ }
+
+ nvl = nvlist_unpack(buf, drv.ifd_len, 0);
+ if (nvl == NULL)
+ {
+ msg(D_DCO, "Failed to unpack nvlist");
+ return;
+ }
+
+ if (nvlist_get_number(nvl, "pending") > 0)
+ {
+ (void)write(dco->pipefd[0], " ", 1);
+ event_ctl(es, dco->pipefd[1], EVENT_READ, arg);
+ }
+
+ nvlist_destroy(nvl);
+}
+
+#endif /* defined(ENABLE_DCO) && defined(TARGET_FREEBSD) */
new file mode 100644
@@ -0,0 +1,47 @@
+/*
+ * Interface to FreeBSD dco networking code
+ *
+ * Copyright (C) 2022 Rubicon Communications, LLC (Netgate). All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef DCO_FREEBSD_H
+#define DCO_FREEBSD_H
+
+#if defined(ENABLE_DCO) && defined(TARGET_FREEBSD)
+
+#include <if_ovpn.h>
+#include <buffer.h>
+#include "event.h"
+
+#define DCO_SUPPORTED_CIPHERS "none:AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305"
+
+typedef enum ovpn_key_slot dco_key_slot_t;
+typedef enum ovpn_key_cipher dco_cipher_t;
+
+typedef struct dco_context {
+ int fd;
+ int pipefd[2];
+
+ char ifname[IFNAMSIZ];
+
+ struct buffer dco_packet_in;
+
+ int dco_message_peer_id;
+ int dco_del_peer_reason;
+} dco_context_t;
+
+#endif /* defined(ENABLE_DCO) && defined(TARGET_FREEBSD) */
+#endif /* ifndef DCO_FREEBSD_H */
@@ -27,6 +27,7 @@
#if defined(ENABLE_DCO)
+#include "dco_freebsd.h"
#include "dco_linux.h"
#include "dco_win.h"
@@ -1639,7 +1639,7 @@ process_outgoing_link(struct context *c)
socks_preprocess_outgoing_link(c, &to_addr, &size_delta);
/* Send packet */
-#ifdef TARGET_LINUX
+#if defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
if (c->c2.link_socket->info.dco_installed)
{
size = dco_do_write(&c->c1.tuntap->dco,
@@ -1914,7 +1914,7 @@ io_wait_dowork(struct context *c, const unsigned int flags)
#ifdef ENABLE_ASYNC_PUSH
static int file_shift = FILE_SHIFT;
#endif
-#ifdef TARGET_LINUX
+#if defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
static int dco_shift = DCO_SHIFT; /* Event from DCO linux kernel module */
#endif
@@ -2024,7 +2024,7 @@ io_wait_dowork(struct context *c, const unsigned int flags)
*/
socket_set(c->c2.link_socket, c->c2.event_set, socket, (void *)&socket_shift, NULL);
tun_set(c->c1.tuntap, c->c2.event_set, tuntap, (void *)&tun_shift, NULL);
-#if defined(TARGET_LINUX)
+#if defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
if (socket & EVENT_READ && c->c2.did_open_tun)
{
dco_event_set(&c->c1.tuntap->dco, c->c2.event_set, (void *)&dco_shift);
@@ -281,7 +281,7 @@ multi_tcp_wait(const struct context *c,
}
#endif
tun_set(c->c1.tuntap, mtcp->es, EVENT_READ, MTCP_TUN, persistent);
-#if defined(TARGET_LINUX)
+#if defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
dco_event_set(&c->c1.tuntap->dco, mtcp->es, MTCP_DCO);
#endif
@@ -400,7 +400,7 @@ multi_tcp_wait_lite(struct multi_context *m, struct multi_instance *mi, const in
tv_clear(&c->c2.timeval); /* ZERO-TIMEOUT */
-#if defined(TARGET_LINUX)
+#if defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
if (mi && mi->context.c2.link_socket->info.dco_installed)
{
/* If we got a socket that has been handed over to the kernel
@@ -763,7 +763,7 @@ multi_tcp_process_io(struct multi_context *m)
multi_tcp_action(m, mi, TA_INITIAL, false);
}
}
-#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+#if defined(ENABLE_DCO) && (defined(TARGET_LINUX) || defined(TARGET_FREEBSD))
/* incoming data on DCO? */
else if (e->arg == MTCP_DCO)
{
@@ -3138,7 +3138,8 @@ multi_signal_instance(struct multi_context *m, struct multi_instance *mi, const
}
#endif
-#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+#if defined(ENABLE_DCO)
+#if defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
static void
process_incoming_dco_packet(struct multi_context *m, struct multi_instance *mi, dco_context_t *dco)
{
@@ -3166,7 +3167,9 @@ process_incoming_dco_packet(struct multi_context *m, struct multi_instance *mi,
done:
buf_init(&dco->dco_packet_in, 0);
}
+#endif
+#if defined(TARGET_LINUX)
static void
process_incoming_del_peer(struct multi_context *m, struct multi_instance *mi, dco_context_t *dco)
{
@@ -3225,6 +3228,33 @@ multi_process_incoming_dco(struct multi_context *m)
dco->dco_message_peer_id = -1;
return ret > 0;
}
+
+#elif defined(TARGET_FREEBSD)
+
+bool
+multi_process_incoming_dco(struct multi_context *m)
+{
+ dco_context_t *dco = &m->top.c1.tuntap->dco;
+
+ struct multi_instance *mi = NULL;
+
+ int ret = dco_do_read(&m->top.c1.tuntap->dco);
+ int peer_id = dco->dco_message_peer_id;
+
+ if ((peer_id >= 0) && (peer_id < m->max_clients) && (m->instances[peer_id]))
+ {
+ mi = m->instances[peer_id];
+ process_incoming_dco_packet(m, mi, dco);
+ }
+ else
+ {
+ msg(D_DCO, "Received packet for peer-id unknown to OpenVPN: %d" , peer_id);
+ }
+
+ dco->dco_message_peer_id = -1;
+ return ret > 0;
+}
+#endif
#endif
/*
@@ -3343,7 +3343,7 @@ options_postprocess_mutate(struct options *o)
}
/* check if any option should force disabling DCO */
-#if defined(TARGET_LINUX)
+#if defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
o->tuntap_options.disable_dco = dco_check_option_conflict(D_DCO, o);
#endif
@@ -5668,7 +5668,7 @@ add_option(struct options *options,
#endif
else if (streq(p[0], "disable-dco") || streq(p[0], "dco-disable"))
{
-#if defined(TARGET_LINUX)
+#if defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
options->tuntap_options.disable_dco = true;
#endif
}
@@ -1796,7 +1796,7 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
"/dev/%s%d", dev, i);
openvpn_snprintf(dynamic_name, sizeof(dynamic_name),
"%s%d", dev, i);
-#ifdef TARGET_LINUX
+#if defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
if (!tt->options.disable_dco)
{
if (open_tun_dco(tt, ctx, dynamic_name) == 0)
@@ -1829,7 +1829,7 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
}
}
-#ifdef TARGET_LINUX
+#if defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
if (!tt->options.disable_dco)
{
if (!dynamic_opened)
@@ -2009,7 +2009,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
open_null(tt);
}
-#if defined(TARGET_LINUX)
+#if defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
else if (!tt->options.disable_dco)
{
open_tun_generic(dev, dev_type, NULL, true, tt, ctx);
@@ -2265,7 +2265,7 @@ close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx)
net_ctx_reset(ctx);
}
-#ifdef TARGET_LINUX
+#if defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
if (!tt->options.disable_dco)
{
close_tun_dco(tt, ctx);
@@ -145,6 +145,12 @@ struct tuntap_options {
bool disable_dco;
};
+#elif defined(TARGET_FREEBSD)
+
+struct tuntap_options {
+ bool disable_dco;
+};
+
#else /* if defined(_WIN32) || defined(TARGET_ANDROID) */
struct tuntap_options {