diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 27cfd362955..6d79ec43238 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -45,6 +45,10 @@ #include "memdbg.h" +#ifdef TARGET_DARWIN +#include +#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 */ diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 34ed4eb57cc..cd9def09070 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..e8d348879b9 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..581b037f3ce 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -55,6 +55,11 @@ #include +#ifdef TARGET_DARWIN +#include +#include +#endif + const char * print_tun_backend_driver(enum tun_driver_type driver) { @@ -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); } @@ -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); } @@ -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); + } } } diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h index 4d6dfbb0032..9d3873d19d4 100644 --- a/src/openvpn/tun.h +++ b/src/openvpn/tun.h @@ -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 +#endif enum tun_driver_type { @@ -206,6 +211,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; @@ -239,6 +249,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