From b31d99ae3fe8e635279862df35aedb4bca0a08c8 Mon Sep 17 00:00:00 2001 From: kabassanov Date: Thu, 5 Mar 2026 16:24:44 +0100 Subject: [PATCH 01/12] Initial version of macOS tap support through feth interfaces --- src/openvpn/forward.c | 114 +++++++++++++++++++++++++++--- src/openvpn/init.c | 10 +++ src/openvpn/openvpn.h | 11 +++ src/openvpn/tun.c | 158 ++++++++++++++++++++++++++++++++++++++++-- src/openvpn/tun.h | 8 +++ 5 files changed, 285 insertions(+), 16 deletions(-) diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 27cfd362955..6b602fef79d 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -45,6 +45,8 @@ #include "memdbg.h" +#include + counter_type link_read_bytes_global; /* GLOBAL */ counter_type link_write_bytes_global; /* GLOBAL */ @@ -1306,15 +1308,76 @@ read_incoming_tun(struct context *c) c->c2.buf = c->c2.buffers->read_tun_buf; -#ifdef _WIN32 - /* we cannot end up here when using dco */ - ASSERT(!dco_enabled(&c->options)); +#ifndef _WIN32 + ASSERT(buf_init(&c->c2.buf, c->c2.frame.buf.headroom)); + ASSERT(buf_safe(&c->c2.buf, c->c2.frame.buf.payload_size)); +#endif - sockethandle_t sh = { .is_handle = true, .h = c->c1.tuntap->hand, .prepend_sa = false }; - sockethandle_finalize(sh, &c->c1.tuntap->reads, &c->c2.buf, NULL); -#else /* ifdef _WIN32 */ - ASSERT(buf_init(&c->c2.buf, c->c2.frame.buf.headroom)); - ASSERT(buf_safe(&c->c2.buf, c->c2.frame.buf.payload_size)); +#if defined (TARGET_DARWIN) + if (c->c1.tuntap->actual_peer_name) + { + int next_bpf_packet_offset = 0; + + ASSERT(buf_init(&c->c2.buffers->read_tun_bpf_buf, 0)); + ASSERT(buf_safe(&c->c2.buffers->read_tun_bpf_buf, TUN_BPF_BUF_SIZE)); + + /* no data remaining in aux tun read buf, so read from bpf */ + if (!(c->c2.buffers->read_tun_aux_buf.len)) + { + if (c->c1.tuntap->backend_driver == DRIVER_AFUNIX) + { + c->c2.buffers->read_tun_bpf_buf.len = + (int)read_tun_afunix(c->c1.tuntap, BPTR(&c->c2.buffers->read_tun_bpf_buf), TUN_BPF_BUF_SIZE); + } + else + { + c->c2.buffers->read_tun_bpf_buf.len = (int)read_tun(c->c1.tuntap, BPTR(&c->c2.buffers->read_tun_bpf_buf), TUN_BPF_BUF_SIZE); + } + } + else + { + /* data remaining in aux tun read buf, copy it to bpf buf instead of real read */ + memcpy(c->c2.buffers->read_tun_bpf_buf.data, c->c2.buffers->read_tun_aux_buf.data, c->c2.buffers->read_tun_aux_buf.len); + c->c2.buffers->read_tun_bpf_buf.len=c->c2.buffers->read_tun_aux_buf.len; + + /* As we will refill the buffer only if there are still another packets, zero len for the moment */ + c->c2.buffers->read_tun_aux_buf.len = 0; + } + + /* This is the current bpf packet */ + struct bpf_hdr *hdr = (struct bpf_hdr *)BPTR(&c->c2.buffers->read_tun_bpf_buf); + + /* Need to split bpf packets */ + if ((unsigned int)c->c2.buffers->read_tun_bpf_buf.len > hdr->bh_hdrlen + hdr->bh_caplen) + { + ASSERT(buf_init(&c->c2.buffers->read_tun_aux_buf, 0)); + ASSERT(buf_safe(&c->c2.buffers->read_tun_aux_buf, TUN_BPF_BUF_SIZE)); + + next_bpf_packet_offset = BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen); + + memcpy(BPTR(&c->c2.buffers->read_tun_aux_buf),(char*)hdr+next_bpf_packet_offset, c->c2.buffers->read_tun_bpf_buf.len - next_bpf_packet_offset); + c->c2.buffers->read_tun_aux_buf.len = c->c2.buffers->read_tun_bpf_buf.len - next_bpf_packet_offset; + } + + /* fill standard read_tun_buf with data from current bpf packet */ + memcpy(BPTR(&c->c2.buf), (char*)hdr + hdr->bh_hdrlen, hdr->bh_caplen); + c->c2.buf.len=hdr->bh_caplen; + } + else + { + if (c->c1.tuntap->backend_driver == DRIVER_AFUNIX) + { + c->c2.buf.len = + (int)read_tun_afunix(c->c1.tuntap, BPTR(&c->c2.buf), c->c2.frame.buf.payload_size); + } + else + { + c->c2.buf.len = (int)read_tun(c->c1.tuntap, BPTR(&c->c2.buf), c->c2.frame.buf.payload_size); + } + } +#else /* TARGET_DARWIN */ + +#ifndef _WIN32 if (c->c1.tuntap->backend_driver == DRIVER_AFUNIX) { c->c2.buf.len = @@ -1324,7 +1387,15 @@ read_incoming_tun(struct context *c) { c->c2.buf.len = (int)read_tun(c->c1.tuntap, BPTR(&c->c2.buf), c->c2.frame.buf.payload_size); } -#endif /* ifdef _WIN32 */ + +#else /* ifndef _WIN32 */ + /* we cannot end up here when using dco */ + ASSERT(!dco_enabled(&c->options)); + + sockethandle_t sh = { .is_handle = true, .h = c->c1.tuntap->hand, .prepend_sa = false }; + sockethandle_finalize(sh, &c->c1.tuntap->reads, &c->c2.buf, NULL); + +#endif /* ifndef _WIN32 */ #ifdef PACKET_TRUNCATION_CHECK ipv4_packet_size_verify(BPTR(&c->c2.buf), BLEN(&c->c2.buf), TUNNEL_TYPE(c->c1.tuntap), @@ -2156,6 +2227,14 @@ get_io_flags_udp(struct context *c, struct multi_io *multi_io, const unsigned in multi_io->udp_flags = (out_socket << SOCKET_SHIFT); } +#ifdef TARGET_DARWIN +static inline bool +tun_read_residual(const struct context *c) +{ + return c->c2.buffers->read_tun_aux_buf.len > 0; +} +#endif /* TARGET_DARWIN */ + /* * This is the core I/O wait function, used for all I/O waits except * for the top-level server sockets. @@ -2213,7 +2292,24 @@ io_wait(struct context *c, const unsigned int flags) if (!c->sig->signal_received) { + +#ifdef TARGET_DARWIN + if (flags & IOW_CHECK_RESIDUAL) + { + if (sockets_read_residual(c)) + { + c->c2.event_set_status = SOCKET_READ; + } + else if ( (!(flags & IOW_TO_LINK)) && tun_read_residual(c) ) + { + c->c2.event_set_status = TUN_READ; + } + } + + if (!(flags & IOW_CHECK_RESIDUAL) || !((c->c2.event_set_status == SOCKET_READ ) || (c->c2.event_set_status == TUN_READ))) +#else /* TARGET_DARWIN */ if (!(flags & IOW_CHECK_RESIDUAL) || !sockets_read_residual(c)) +#endif /* TARGET_DARWIN */ { int status; diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 34ed4eb57cc..70563108454 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -3694,6 +3694,11 @@ init_context_buffers(const struct frame *frame) b->read_link_buf = alloc_buf(buf_size); b->read_tun_buf = alloc_buf(buf_size); +#if defined(TARGET_DARWIN) + b->read_tun_aux_buf = alloc_buf(TUN_BPF_BUF_SIZE); + b->read_tun_bpf_buf = alloc_buf(TUN_BPF_BUF_SIZE); +#endif + b->aux_buf = alloc_buf(buf_size); b->encrypt_buf = alloc_buf(buf_size); @@ -3716,6 +3721,11 @@ free_context_buffers(struct context_buffers *b) free_buf(&b->read_tun_buf); free_buf(&b->aux_buf); +#if defined(TARGET_DARWIN) + free_buf(&b->read_tun_aux_buf); + free_buf(&b->read_tun_bpf_buf); +#endif + #ifdef USE_COMP free_buf(&b->compress_buf); free_buf(&b->decompress_buf); diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h index fa00822d79d..369707d4fa3 100644 --- a/src/openvpn/openvpn.h +++ b/src/openvpn/openvpn.h @@ -112,8 +112,19 @@ struct context_buffers */ struct buffer read_link_buf; struct buffer read_tun_buf; + +#ifdef TARGET_DARWIN + struct buffer read_tun_aux_buf; + struct buffer read_tun_bpf_buf; }; +#define TUN_BPF_BUF_SIZE 32768 + +#else +}; + +#endif + /* * always-persistent context variables */ diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index 7f966023192..da35239f68b 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -54,6 +54,8 @@ #endif #include +#include +#include const char * print_tun_backend_driver(enum tun_driver_type driver) @@ -1887,17 +1889,139 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node, st msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev); tt->persistent_if = true; } +#if defined(TARGET_DARWIN) + if (strncmp(dev, "feth", 4) == 0) + { + + char feth_peer_name[256]; + + strncpy(feth_peer_name, dev + 4, strlen(dev) - 4); + if (strlen(feth_peer_name)) + { + long peer_index = strtol(feth_peer_name, NULL, 0); + if (peer_index || dev[4] == '0') + { + sprintf(feth_peer_name, "feth%ld", peer_index + 1000); + msg(M_INFO, "Peer interface for %s is expected to be %s", dev, feth_peer_name); + } + else + { + msg(M_ERR, "Cannot calculate feth peer interface number for TAP dev %s", tunname); + } + } + else + { + msg(M_ERR, "No valid feth name %s", tunname); + } + + if ((tt->wfd = socket(AF_NDRV, SOCK_RAW, 0)) < 0) + { + msg(M_ERR, "Cannot open writing socket for TAP dev %s", tunname); + } + + struct sockaddr_ndrv ndrv_sockaddr; + + memset(&ndrv_sockaddr,0, sizeof(ndrv_sockaddr)); + ndrv_sockaddr.snd_family = AF_NDRV; + + strcpy((char*)ndrv_sockaddr.snd_name, (char*)feth_peer_name); + msg(M_INFO, "ndrv_sockaddr.snd_name = %s", ndrv_sockaddr.snd_name); + + if (bind(tt->wfd, (struct sockaddr*)&ndrv_sockaddr, sizeof(ndrv_sockaddr)) < 0) + { + msg(M_ERR, "Cannot bind writing socket %d for TAP %s", tt->wfd, tunname); + } + + set_nonblock(tt->wfd); + set_cloexec(tt->wfd); /* don't pass fd to scripts */ + + msg(M_INFO, "Writing socket for TAP device peer %s opened", feth_peer_name); + + // Creating a read fd with bpf + char buf[11] = {0}; + int bpf = 0; + + for (int i = 0; i < 99; i++) + { + sprintf(buf, "/dev/bpf%i", i); + bpf = open(buf, O_RDONLY); + + if (bpf != -1) + break; + } + if (bpf == -1) + { + msg(M_ERR, "Cannot find free bpf for dev %s TAP", tunname); + } + else + { + tt->fd = bpf; + } + + int value = 1; + + if (ioctl(bpf, BIOCIMMEDIATE, &value) == -1) + { + msg(M_ERR, "Cannot disable buffering for %s TAP bpf %s", tunname, buf); + } - if ((tt->fd = open(tunname, O_RDWR)) < 0) + int buf_len = TUN_BPF_BUF_SIZE; + + if (ioctl(bpf, BIOCSBLEN, &buf_len) == -1) + { + msg(M_ERR, "Cannot set buffer size to %d for %s TAP bpf %s", buf_len, tunname, buf); + } + + struct ifreq bound_if; + + strcpy(bound_if.ifr_name, feth_peer_name); + + if (ioctl(bpf, BIOCSETIF, &bound_if) > 0) + { + msg(M_ERR, "Cannot set interface %s in TAP %s bpf %s properties", feth_peer_name, tunname, buf); + } + + value = 1; + + if (ioctl(bpf, BIOCSHDRCMPLT, &value) == -1) + { + msg(M_ERR, "Cannot disable lladdr completion for %s TAP bpf %s", tunname, buf); + } + + value = 1; + + if (ioctl(bpf, BIOCPROMISC, &value) == -1) + { + msg(M_ERR, "Cannot enable promiscuous mode for %s TAP bpf %s", tunname, buf); + } + + value = 0; + + if( ioctl( bpf, BIOCSSEESENT , &value ) == -1 ) + { + msg(M_ERR, "Cannot block bpf reception of our outgoing packets on %s", buf); + } + + set_nonblock(tt->fd); + set_cloexec(tt->fd); /* don't pass fd to scripts */ + msg(M_INFO, "Reading bpf for TAP device peer %s opened", feth_peer_name); + + tt->actual_peer_name = string_alloc(feth_peer_name, NULL); + } + else +#endif /* TARGET_DARWIN */ { - msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname); + if ((tt->fd = open(tunname, O_RDWR)) < 0) + { + msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname); + } + set_nonblock(tt->fd); + set_cloexec(tt->fd); /* don't pass fd to scripts */ + msg(M_INFO, "TUN/TAP device %s opened", tunname); + } } - set_nonblock(tt->fd); - set_cloexec(tt->fd); /* don't pass fd to scripts */ - msg(M_INFO, "TUN/TAP device %s opened", tunname); - /* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */ tt->actual_name = string_alloc(dynamic_opened ? dynamic_name : dev, NULL); } @@ -1980,6 +2104,14 @@ close_tun_generic(struct tuntap *tt) close(tt->fd); } +#if defined (TARGET_DARWIN) + if (tt->actual_peer_name) + { + close(tt->wfd); + free(tt->actual_peer_name); + } +#endif /* TARGET_DARWIN */ + free(tt->actual_name); clear_tuntap(tt); } @@ -3120,7 +3252,19 @@ write_tun(struct tuntap *tt, uint8_t *buf, int len) } else { - return write(tt->fd, buf, len); + /* feth TAP interface */ + if (tt->actual_peer_name) + { + struct sockaddr_ndrv ndrv_sockaddr; + memset(&ndrv_sockaddr,0, sizeof(ndrv_sockaddr)); + ndrv_sockaddr.snd_family = AF_NDRV; + strcpy((char*)ndrv_sockaddr.snd_name, (char*) tt->actual_peer_name); + return sendto(tt->wfd, buf, len, 0, (const struct sockaddr*)&ndrv_sockaddr, sizeof(ndrv_sockaddr)); + } + else + { + return write(tt->fd, buf, len); + } } } diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h index 4d6dfbb0032..67f82e29d5d 100644 --- a/src/openvpn/tun.h +++ b/src/openvpn/tun.h @@ -206,6 +206,10 @@ struct tuntap char *actual_name; /* actual name of TUN/TAP dev, usually including unit number */ +#ifdef TARGET_DARWIN + char *actual_peer_name; /* actual name of macOS TAP feth dev peer interface, with original unit number + 1000) */ +#endif + /* ifconfig parameters */ in_addr_t local; in_addr_t remote_netmask; @@ -239,6 +243,10 @@ struct tuntap int fd; /* file descriptor for TUN/TAP dev */ #endif /* ifdef _WIN32 */ +#ifdef TARGET_DARWIN + int wfd; /* file descriptor for feth tap emulated dev write */ +#endif + #ifdef TARGET_SOLARIS int ip_fd; #endif From 5bc5a687fee2653b023939733c661068a6db9e55 Mon Sep 17 00:00:00 2001 From: kabassanov Date: Thu, 5 Mar 2026 17:08:12 +0100 Subject: [PATCH 02/12] Store ndrv_sockaddr structure into tuntap in order to use it directly in the write function --- src/openvpn/tun.c | 21 ++++++++------------- src/openvpn/tun.h | 3 +++ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index da35239f68b..6d76adcc003 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -1889,7 +1889,8 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node, st msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev); tt->persistent_if = true; } -#if defined(TARGET_DARWIN) + + #if defined(TARGET_DARWIN) if (strncmp(dev, "feth", 4) == 0) { @@ -1919,15 +1920,13 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node, st msg(M_ERR, "Cannot open writing socket for TAP dev %s", tunname); } - struct sockaddr_ndrv ndrv_sockaddr; - - memset(&ndrv_sockaddr,0, sizeof(ndrv_sockaddr)); - ndrv_sockaddr.snd_family = AF_NDRV; + memset(&tt->ndrv_sockaddr,0, sizeof(tt->ndrv_sockaddr)); + tt->ndrv_sockaddr.snd_family = AF_NDRV; - strcpy((char*)ndrv_sockaddr.snd_name, (char*)feth_peer_name); - msg(M_INFO, "ndrv_sockaddr.snd_name = %s", ndrv_sockaddr.snd_name); + strcpy((char*)tt->ndrv_sockaddr.snd_name, (char*)feth_peer_name); + msg(M_INFO, "ndrv_sockaddr.snd_name = %s", tt->ndrv_sockaddr.snd_name); - if (bind(tt->wfd, (struct sockaddr*)&ndrv_sockaddr, sizeof(ndrv_sockaddr)) < 0) + if (bind(tt->wfd, (struct sockaddr*)&tt->ndrv_sockaddr, sizeof(tt->ndrv_sockaddr)) < 0) { msg(M_ERR, "Cannot bind writing socket %d for TAP %s", tt->wfd, tunname); } @@ -3255,11 +3254,7 @@ write_tun(struct tuntap *tt, uint8_t *buf, int len) /* feth TAP interface */ if (tt->actual_peer_name) { - struct sockaddr_ndrv ndrv_sockaddr; - memset(&ndrv_sockaddr,0, sizeof(ndrv_sockaddr)); - ndrv_sockaddr.snd_family = AF_NDRV; - strcpy((char*)ndrv_sockaddr.snd_name, (char*) tt->actual_peer_name); - return sendto(tt->wfd, buf, len, 0, (const struct sockaddr*)&ndrv_sockaddr, sizeof(ndrv_sockaddr)); + return sendto(tt->wfd, buf, len, 0, (const struct sockaddr*)&tt->ndrv_sockaddr, sizeof(tt->ndrv_sockaddr)); } else { diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h index 67f82e29d5d..668728ead0f 100644 --- a/src/openvpn/tun.h +++ b/src/openvpn/tun.h @@ -40,6 +40,8 @@ #include "networking.h" #include "dco.h" +#include + enum tun_driver_type { WINDOWS_DRIVER_UNSPECIFIED, @@ -208,6 +210,7 @@ struct tuntap #ifdef TARGET_DARWIN char *actual_peer_name; /* actual name of macOS TAP feth dev peer interface, with original unit number + 1000) */ + struct sockaddr_ndrv ndrv_sockaddr; /* write socket for feth TAP interface */ #endif /* ifconfig parameters */ From 2a522fe491d92bdef7508c67c5cf93119e1346fc Mon Sep 17 00:00:00 2001 From: kabassanov Date: Thu, 5 Mar 2026 17:21:33 +0100 Subject: [PATCH 03/12] Removing caps in comments --- src/openvpn/forward.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 6b602fef79d..d149bb66246 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -1340,14 +1340,14 @@ read_incoming_tun(struct context *c) memcpy(c->c2.buffers->read_tun_bpf_buf.data, c->c2.buffers->read_tun_aux_buf.data, c->c2.buffers->read_tun_aux_buf.len); c->c2.buffers->read_tun_bpf_buf.len=c->c2.buffers->read_tun_aux_buf.len; - /* As we will refill the buffer only if there are still another packets, zero len for the moment */ + /* as we will refill the buffer only if there are still another packets, zero len for the moment */ c->c2.buffers->read_tun_aux_buf.len = 0; } - /* This is the current bpf packet */ + /* this is the current bpf packet */ struct bpf_hdr *hdr = (struct bpf_hdr *)BPTR(&c->c2.buffers->read_tun_bpf_buf); - /* Need to split bpf packets */ + /* need to split bpf packets */ if ((unsigned int)c->c2.buffers->read_tun_bpf_buf.len > hdr->bh_hdrlen + hdr->bh_caplen) { ASSERT(buf_init(&c->c2.buffers->read_tun_aux_buf, 0)); From 74527226c905bff71317ee119a0172d4701cb8fd Mon Sep 17 00:00:00 2001 From: kabassanov Date: Thu, 5 Mar 2026 17:48:34 +0100 Subject: [PATCH 04/12] Include of bpf.h and ndrv.h only for macOS --- src/openvpn/forward.c | 4 +++- src/openvpn/tun.c | 7 +++++-- src/openvpn/tun.h | 4 +++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index d149bb66246..21a827b56ea 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -45,7 +45,9 @@ #include "memdbg.h" -#include +#ifdef TARGET_DARWIN + #include +#endif counter_type link_read_bytes_global; /* GLOBAL */ counter_type link_write_bytes_global; /* GLOBAL */ diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index 6d76adcc003..b4f0bc654f0 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -54,8 +54,11 @@ #endif #include -#include -#include + +#ifdef TARGET_DARWIN + #include + #include +#endif const char * print_tun_backend_driver(enum tun_driver_type driver) diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h index 668728ead0f..93fe37e7595 100644 --- a/src/openvpn/tun.h +++ b/src/openvpn/tun.h @@ -40,7 +40,9 @@ #include "networking.h" #include "dco.h" -#include +#ifdef TARGET_DARWIN + #include +#endif enum tun_driver_type { From ba0bf600a7c3ffcc4f2c3c5daa22f2efd371ed03 Mon Sep 17 00:00:00 2001 From: kabassanov Date: Thu, 5 Mar 2026 18:00:27 +0100 Subject: [PATCH 05/12] Subtree for bpf.h and ndrv.h --- src/openvpn/forward.c | 2 +- src/openvpn/tun.c | 4 ++-- src/openvpn/tun.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 21a827b56ea..b2bedea317e 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -46,7 +46,7 @@ #include "memdbg.h" #ifdef TARGET_DARWIN - #include + #include #endif counter_type link_read_bytes_global; /* GLOBAL */ diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index b4f0bc654f0..2110a6eb62e 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -56,8 +56,8 @@ #include #ifdef TARGET_DARWIN - #include - #include + #include + #include #endif const char * diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h index 93fe37e7595..9c128f539fa 100644 --- a/src/openvpn/tun.h +++ b/src/openvpn/tun.h @@ -41,7 +41,7 @@ #include "dco.h" #ifdef TARGET_DARWIN - #include + #include #endif enum tun_driver_type From 83fefd630d47075bb088bcb3220578467fcf45f1 Mon Sep 17 00:00:00 2001 From: kabassanov Date: Thu, 5 Mar 2026 20:09:45 +0100 Subject: [PATCH 06/12] Missing endif --- src/openvpn/forward.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index b2bedea317e..81ea2fe01dd 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -1399,6 +1399,8 @@ read_incoming_tun(struct context *c) #endif /* ifndef _WIN32 */ +#endif /* TARGET_DARWIN */ + #ifdef PACKET_TRUNCATION_CHECK ipv4_packet_size_verify(BPTR(&c->c2.buf), BLEN(&c->c2.buf), TUNNEL_TYPE(c->c1.tuntap), "READ_TUN", &c->c2.n_trunc_tun_read); From aa4e9e3fedeba30ce92cf933f255e182c6732780 Mon Sep 17 00:00:00 2001 From: kabassanov Date: Thu, 5 Mar 2026 20:42:08 +0100 Subject: [PATCH 07/12] Replace sprintf by snprintf --- src/openvpn/tun.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index 2110a6eb62e..a4eaa4c6f0d 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -1905,7 +1905,7 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node, st long peer_index = strtol(feth_peer_name, NULL, 0); if (peer_index || dev[4] == '0') { - sprintf(feth_peer_name, "feth%ld", peer_index + 1000); + snprintf(feth_peer_name, 9, "feth%ld", peer_index + 1000); msg(M_INFO, "Peer interface for %s is expected to be %s", dev, feth_peer_name); } else @@ -1945,7 +1945,7 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node, st for (int i = 0; i < 99; i++) { - sprintf(buf, "/dev/bpf%i", i); + snprintf(buf, 11, "/dev/bpf%i", i); bpf = open(buf, O_RDONLY); if (bpf != -1) From 7152ac9e1a6874eb0c34c640c509e74d7a26ae92 Mon Sep 17 00:00:00 2001 From: kabassanov Date: Fri, 6 Mar 2026 14:20:38 +0100 Subject: [PATCH 08/12] Correct indent --- src/openvpn/forward.c | 25 ++++++++++++------------- src/openvpn/init.c | 2 +- src/openvpn/openvpn.h | 2 +- src/openvpn/tun.c | 30 +++++++++++++++--------------- src/openvpn/tun.h | 7 ++++--- 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 81ea2fe01dd..878011d8e63 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -46,7 +46,7 @@ #include "memdbg.h" #ifdef TARGET_DARWIN - #include +#include #endif counter_type link_read_bytes_global; /* GLOBAL */ @@ -1311,11 +1311,11 @@ read_incoming_tun(struct context *c) c->c2.buf = c->c2.buffers->read_tun_buf; #ifndef _WIN32 - ASSERT(buf_init(&c->c2.buf, c->c2.frame.buf.headroom)); - ASSERT(buf_safe(&c->c2.buf, c->c2.frame.buf.payload_size)); + ASSERT(buf_init(&c->c2.buf, c->c2.frame.buf.headroom)); + ASSERT(buf_safe(&c->c2.buf, c->c2.frame.buf.payload_size)); #endif -#if defined (TARGET_DARWIN) +#if defined(TARGET_DARWIN) if (c->c1.tuntap->actual_peer_name) { int next_bpf_packet_offset = 0; @@ -1339,8 +1339,8 @@ read_incoming_tun(struct context *c) else { /* data remaining in aux tun read buf, copy it to bpf buf instead of real read */ - memcpy(c->c2.buffers->read_tun_bpf_buf.data, c->c2.buffers->read_tun_aux_buf.data, c->c2.buffers->read_tun_aux_buf.len); - c->c2.buffers->read_tun_bpf_buf.len=c->c2.buffers->read_tun_aux_buf.len; + memcpy(c->c2.buffers->read_tun_bpf_buf.data, c->c2.buffers->read_tun_aux_buf.data, c->c2.buffers->read_tun_aux_buf.len); + c->c2.buffers->read_tun_bpf_buf.len = c->c2.buffers->read_tun_aux_buf.len; /* as we will refill the buffer only if there are still another packets, zero len for the moment */ c->c2.buffers->read_tun_aux_buf.len = 0; @@ -1357,12 +1357,12 @@ read_incoming_tun(struct context *c) next_bpf_packet_offset = BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen); - memcpy(BPTR(&c->c2.buffers->read_tun_aux_buf),(char*)hdr+next_bpf_packet_offset, c->c2.buffers->read_tun_bpf_buf.len - next_bpf_packet_offset); + memcpy(BPTR(&c->c2.buffers->read_tun_aux_buf), (char *)hdr + next_bpf_packet_offset, c->c2.buffers->read_tun_bpf_buf.len - next_bpf_packet_offset); c->c2.buffers->read_tun_aux_buf.len = c->c2.buffers->read_tun_bpf_buf.len - next_bpf_packet_offset; } /* fill standard read_tun_buf with data from current bpf packet */ - memcpy(BPTR(&c->c2.buf), (char*)hdr + hdr->bh_hdrlen, hdr->bh_caplen); + memcpy(BPTR(&c->c2.buf), (char *)hdr + hdr->bh_hdrlen, hdr->bh_caplen); c->c2.buf.len=hdr->bh_caplen; } else @@ -1390,7 +1390,7 @@ read_incoming_tun(struct context *c) c->c2.buf.len = (int)read_tun(c->c1.tuntap, BPTR(&c->c2.buf), c->c2.frame.buf.payload_size); } -#else /* ifndef _WIN32 */ +#else /* ifndef _WIN32 */ /* we cannot end up here when using dco */ ASSERT(!dco_enabled(&c->options)); @@ -2296,7 +2296,6 @@ io_wait(struct context *c, const unsigned int flags) if (!c->sig->signal_received) { - #ifdef TARGET_DARWIN if (flags & IOW_CHECK_RESIDUAL) { @@ -2304,14 +2303,14 @@ io_wait(struct context *c, const unsigned int flags) { c->c2.event_set_status = SOCKET_READ; } - else if ( (!(flags & IOW_TO_LINK)) && tun_read_residual(c) ) + else if ((!(flags & IOW_TO_LINK)) && tun_read_residual(c)) { c->c2.event_set_status = TUN_READ; } } - if (!(flags & IOW_CHECK_RESIDUAL) || !((c->c2.event_set_status == SOCKET_READ ) || (c->c2.event_set_status == TUN_READ))) -#else /* TARGET_DARWIN */ + if (!(flags & IOW_CHECK_RESIDUAL) || !((c->c2.event_set_status == SOCKET_READ) || (c->c2.event_set_status == TUN_READ))) +#else /* TARGET_DARWIN */ if (!(flags & IOW_CHECK_RESIDUAL) || !sockets_read_residual(c)) #endif /* TARGET_DARWIN */ { diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 70563108454..cd9def09070 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -3724,7 +3724,7 @@ free_context_buffers(struct context_buffers *b) #if defined(TARGET_DARWIN) free_buf(&b->read_tun_aux_buf); free_buf(&b->read_tun_bpf_buf); -#endif +#endif #ifdef USE_COMP free_buf(&b->compress_buf); diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h index 369707d4fa3..e8d348879b9 100644 --- a/src/openvpn/openvpn.h +++ b/src/openvpn/openvpn.h @@ -124,7 +124,7 @@ struct context_buffers }; #endif - + /* * always-persistent context variables */ diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index a4eaa4c6f0d..51faa304462 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -56,8 +56,8 @@ #include #ifdef TARGET_DARWIN - #include - #include +#include +#include #endif const char * @@ -1893,10 +1893,9 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node, st tt->persistent_if = true; } - #if defined(TARGET_DARWIN) +#if defined(TARGET_DARWIN) if (strncmp(dev, "feth", 4) == 0) { - char feth_peer_name[256]; strncpy(feth_peer_name, dev + 4, strlen(dev) - 4); @@ -1915,7 +1914,7 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node, st } else { - msg(M_ERR, "No valid feth name %s", tunname); + msg(M_ERR, "No valid feth name %s", tunname); } if ((tt->wfd = socket(AF_NDRV, SOCK_RAW, 0)) < 0) @@ -1923,13 +1922,13 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node, st msg(M_ERR, "Cannot open writing socket for TAP dev %s", tunname); } - memset(&tt->ndrv_sockaddr,0, sizeof(tt->ndrv_sockaddr)); + memset(&tt->ndrv_sockaddr, 0, sizeof(tt->ndrv_sockaddr)); tt->ndrv_sockaddr.snd_family = AF_NDRV; - strcpy((char*)tt->ndrv_sockaddr.snd_name, (char*)feth_peer_name); + strcpy((char *)tt->ndrv_sockaddr.snd_name, (char *)feth_peer_name); msg(M_INFO, "ndrv_sockaddr.snd_name = %s", tt->ndrv_sockaddr.snd_name); - if (bind(tt->wfd, (struct sockaddr*)&tt->ndrv_sockaddr, sizeof(tt->ndrv_sockaddr)) < 0) + if (bind(tt->wfd, (struct sockaddr *)&tt->ndrv_sockaddr, sizeof(tt->ndrv_sockaddr)) < 0) { msg(M_ERR, "Cannot bind writing socket %d for TAP %s", tt->wfd, tunname); } @@ -1940,7 +1939,7 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node, st msg(M_INFO, "Writing socket for TAP device peer %s opened", feth_peer_name); // Creating a read fd with bpf - char buf[11] = {0}; + char buf[11] = { 0 }; int bpf = 0; for (int i = 0; i < 99; i++) @@ -1949,9 +1948,11 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node, st bpf = open(buf, O_RDONLY); if (bpf != -1) + { break; + } } - if (bpf == -1) + if (bpf == -1) { msg(M_ERR, "Cannot find free bpf for dev %s TAP", tunname); } @@ -1994,12 +1995,12 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node, st if (ioctl(bpf, BIOCPROMISC, &value) == -1) { - msg(M_ERR, "Cannot enable promiscuous mode for %s TAP bpf %s", tunname, buf); + msg(M_ERR, "Cannot enable promiscuous mode for %s TAP bpf %s", tunname, buf); } value = 0; - if( ioctl( bpf, BIOCSSEESENT , &value ) == -1 ) + if (ioctl( bpf, BIOCSSEESENT , &value ) == -1) { msg(M_ERR, "Cannot block bpf reception of our outgoing packets on %s", buf); } @@ -2020,7 +2021,6 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node, st set_nonblock(tt->fd); set_cloexec(tt->fd); /* don't pass fd to scripts */ msg(M_INFO, "TUN/TAP device %s opened", tunname); - } } @@ -2106,7 +2106,7 @@ close_tun_generic(struct tuntap *tt) close(tt->fd); } -#if defined (TARGET_DARWIN) +#if defined(TARGET_DARWIN) if (tt->actual_peer_name) { close(tt->wfd); @@ -3257,7 +3257,7 @@ write_tun(struct tuntap *tt, uint8_t *buf, int len) /* feth TAP interface */ if (tt->actual_peer_name) { - return sendto(tt->wfd, buf, len, 0, (const struct sockaddr*)&tt->ndrv_sockaddr, sizeof(tt->ndrv_sockaddr)); + return sendto(tt->wfd, buf, len, 0, (const struct sockaddr *)&tt->ndrv_sockaddr, sizeof(tt->ndrv_sockaddr)); } else { diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h index 9c128f539fa..9d3873d19d4 100644 --- a/src/openvpn/tun.h +++ b/src/openvpn/tun.h @@ -37,11 +37,12 @@ #include "event.h" #include "proto.h" #include "misc.h" -#include "networking.h" #include "dco.h" +#include "networking.h" + #ifdef TARGET_DARWIN - #include +#include #endif enum tun_driver_type @@ -211,7 +212,7 @@ struct tuntap char *actual_name; /* actual name of TUN/TAP dev, usually including unit number */ #ifdef TARGET_DARWIN - char *actual_peer_name; /* actual name of macOS TAP feth dev peer interface, with original unit number + 1000) */ + char *actual_peer_name; /* actual name of macOS TAP feth dev peer interface, with original unit number + 1000) */ struct sockaddr_ndrv ndrv_sockaddr; /* write socket for feth TAP interface */ #endif From c5827e2b3529f95f268f2998ba9d20224d2ea445 Mon Sep 17 00:00:00 2001 From: kabassanov Date: Fri, 6 Mar 2026 14:23:03 +0100 Subject: [PATCH 09/12] Correct indent 2 --- src/openvpn/forward.c | 2 +- src/openvpn/tun.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 878011d8e63..2fc053a13c4 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -1363,7 +1363,7 @@ read_incoming_tun(struct context *c) /* fill standard read_tun_buf with data from current bpf packet */ memcpy(BPTR(&c->c2.buf), (char *)hdr + hdr->bh_hdrlen, hdr->bh_caplen); - c->c2.buf.len=hdr->bh_caplen; + c->c2.buf.len = hdr->bh_caplen; } else { diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index 51faa304462..5b9517104b3 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -2000,7 +2000,7 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node, st value = 0; - if (ioctl( bpf, BIOCSSEESENT , &value ) == -1) + if (ioctl( bpf, BIOCSSEESENT , &value) == -1) { msg(M_ERR, "Cannot block bpf reception of our outgoing packets on %s", buf); } From 4edc5a3a77995816dce3bb4d621d11c5adeededa Mon Sep 17 00:00:00 2001 From: kabassanov Date: Fri, 6 Mar 2026 14:24:40 +0100 Subject: [PATCH 10/12] Correct indent 3 --- src/openvpn/tun.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index 5b9517104b3..ca9dcdfe0c2 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -2000,7 +2000,7 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node, st value = 0; - if (ioctl( bpf, BIOCSSEESENT , &value) == -1) + if (ioctl( bpf, BIOCSSEESENT, &value) == -1) { msg(M_ERR, "Cannot block bpf reception of our outgoing packets on %s", buf); } From f562dc0b0bec762791547b3842f6bbd8d6e0fe1b Mon Sep 17 00:00:00 2001 From: kabassanov Date: Fri, 6 Mar 2026 14:26:25 +0100 Subject: [PATCH 11/12] Correct indent 4 --- src/openvpn/tun.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index ca9dcdfe0c2..05bf2330abe 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -2000,7 +2000,7 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node, st value = 0; - if (ioctl( bpf, BIOCSSEESENT, &value) == -1) + if (ioctl(bpf, BIOCSSEESENT, &value) == -1) { msg(M_ERR, "Cannot block bpf reception of our outgoing packets on %s", buf); } From 61b235d2773a67cc7b59bb6b59394ab44df10bbb Mon Sep 17 00:00:00 2001 From: kabassanov Date: Fri, 6 Mar 2026 17:15:21 +0100 Subject: [PATCH 12/12] Wrong manual copy/paste --- src/openvpn/forward.c | 2 ++ src/openvpn/tun.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 2fc053a13c4..6d79ec43238 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -2364,10 +2364,12 @@ io_wait(struct context *c, const unsigned int flags) c->c2.event_set_status = ES_TIMEOUT; } } +#if !defined(TARGET_DARWIN) else { c->c2.event_set_status = SOCKET_READ; } +#endif } /* 'now' should always be a reasonably up-to-date timestamp */ diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index 05bf2330abe..581b037f3ce 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -1925,7 +1925,7 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node, st memset(&tt->ndrv_sockaddr, 0, sizeof(tt->ndrv_sockaddr)); tt->ndrv_sockaddr.snd_family = AF_NDRV; - strcpy((char *)tt->ndrv_sockaddr.snd_name, (char *)feth_peer_name); + strcpy((char *)&tt->ndrv_sockaddr.snd_name, (char *)feth_peer_name); msg(M_INFO, "ndrv_sockaddr.snd_name = %s", tt->ndrv_sockaddr.snd_name); if (bind(tt->wfd, (struct sockaddr *)&tt->ndrv_sockaddr, sizeof(tt->ndrv_sockaddr)) < 0)