Skip to content
117 changes: 109 additions & 8 deletions src/openvpn/forward.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down Expand Up @@ -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 =
Expand All @@ -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),
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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 */
Expand Down
10 changes: 10 additions & 0 deletions src/openvpn/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
11 changes: 11 additions & 0 deletions src/openvpn/openvpn.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
156 changes: 149 additions & 7 deletions src/openvpn/tun.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -1888,16 +1893,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);
}
Expand Down Expand Up @@ -1980,6 +2106,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);
}
Expand Down Expand Up @@ -3120,7 +3254,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);
}
}
}

Expand Down
Loading