This commit is contained in:
kabassanov 2026-05-14 12:34:40 +02:00 committed by GitHub
commit 0af8e0acaa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 294 additions and 16 deletions

View file

@ -45,6 +45,10 @@
#include "memdbg.h"
#ifdef TARGET_DARWIN
#include <net/bpf.h>
#endif
counter_type link_read_bytes_global; /* GLOBAL */
counter_type link_write_bytes_global; /* GLOBAL */
@ -1306,15 +1310,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));
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 */
#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
#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 +1389,17 @@ 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 */
#endif /* TARGET_DARWIN */
#ifdef PACKET_TRUNCATION_CHECK
ipv4_packet_size_verify(BPTR(&c->c2.buf), BLEN(&c->c2.buf), TUNNEL_TYPE(c->c1.tuntap),
@ -2156,6 +2231,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 +2296,23 @@ 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;
@ -2265,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 */

View file

@ -3695,6 +3695,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);
@ -3717,6 +3722,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);

View file

@ -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
*/

View file

@ -55,6 +55,11 @@
#include <string.h>
#ifdef TARGET_DARWIN
#include <net/ndrv.h>
#include <net/bpf.h>
#endif
const char *
print_tun_backend_driver(enum tun_driver_type driver)
{
@ -1879,16 +1884,137 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node, st
tt->persistent_if = true;
}
if ((tt->fd = open(tunname, O_RDWR)) < 0)
#if defined(TARGET_DARWIN)
if (strncmp(dev, "feth", 4) == 0)
{
msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
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')
{
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
{
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);
}
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);
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)
{
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++)
{
snprintf(buf, 11, "/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);
}
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 */
{
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);
}
@ -1971,6 +2097,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);
}
@ -3111,7 +3245,15 @@ 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)
{
return sendto(tt->wfd, buf, len, 0, (const struct sockaddr *)&tt->ndrv_sockaddr, sizeof(tt->ndrv_sockaddr));
}
else
{
return write(tt->fd, buf, len);
}
}
}

View file

@ -37,8 +37,13 @@
#include "event.h"
#include "proto.h"
#include "misc.h"
#include "networking.h"
#include "dco.h"
#include "networking.h"
#ifdef TARGET_DARWIN
#include <net/ndrv.h>
#endif
enum tun_driver_type
{
@ -204,6 +209,11 @@ 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) */
struct sockaddr_ndrv ndrv_sockaddr; /* write socket for feth TAP interface */
#endif
/* ifconfig parameters */
in_addr_t local;
in_addr_t remote_netmask;
@ -237,6 +247,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