mirror of
https://github.com/digarok/gsplus.git
synced 2025-04-01 17:30:10 +00:00
to work around vmnet root/entitlement restrictions, add a stand-alone helper utility than can be setuid root.
vmnet_helper creates a vmnet connection then communicates back to GS+ via pipes to stdin/stdout and a simple binary messaging protocol.
This commit is contained in:
parent
55de506812
commit
4a5d6effd5
@ -134,6 +134,18 @@ SET_SOURCE_FILES_PROPERTIES(
|
||||
MACOSX_PACKAGE_LOCATION Resources
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
add_custom_command(TARGET GSplus POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/rawnet/vmnet_helper"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/GSplus.app/Contents/MacOS/vmnet_helper"
|
||||
# COMMAND sudo chown root "${CMAKE_CURRENT_BINARY_DIR}/GSplus.app/Contents/MacOS/vmnet_helper"
|
||||
# COMMAND sudo chmod +s "${CMAKE_CURRENT_BINARY_DIR}/GSplus.app/Contents/MacOS/vmnet_helper"
|
||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/rawnet/vmnet_helper)
|
||||
endif()
|
||||
# SET_SOURCE_FILES_PROPERTIES(vmnet_helper PROPERTIES MACOSX_PACKAGE_LOCATION MacOS)
|
||||
|
||||
|
||||
if (WITH_RAWNET)
|
||||
target_link_libraries(GSplus rawnet)
|
||||
endif()
|
||||
|
@ -2,7 +2,7 @@
|
||||
if(WIN32)
|
||||
set(rawnetarch rawnetarch_win32.c)
|
||||
elseif(APPLE)
|
||||
set(rawnetarch rawnetarch_darwin.c)
|
||||
set(rawnetarch rawnetarch_vmnet_helper.c)
|
||||
#set(rawnetarch rawnetarch_unix.c)
|
||||
elseif(UNIX)
|
||||
set(rawnetarch rawnetarch_unix.c)
|
||||
@ -13,11 +13,16 @@ add_library(rawnet cs8900.c rawnet.c rawnetsupp.c rawnetarch.c ${rawnetarch})
|
||||
target_compile_definitions(rawnet PUBLIC HAVE_RAWNET)
|
||||
target_compile_definitions(rawnet PRIVATE CS8900_DEBUG RAWNET_DEBUG_FRAMES)
|
||||
|
||||
target_compile_options(rawnet PRIVATE -g)
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(rawnet ws2_32) # winsock2
|
||||
elseif(APPLE)
|
||||
#target_link_libraries(rawnet PRIVATE pcap)
|
||||
target_link_libraries(rawnet PRIVATE "-framework vmnet")
|
||||
#target_link_libraries(rawnet PRIVATE "-framework vmnet")
|
||||
add_executable(vmnet_helper vmnet_helper.c)
|
||||
target_link_libraries(vmnet_helper PRIVATE "-framework vmnet")
|
||||
|
||||
elseif(UNIX)
|
||||
target_link_libraries(rawnet PRIVATE pcap)
|
||||
endif()
|
@ -77,10 +77,9 @@ char *rawnet_get_standard_interface(void)
|
||||
return rawnet_arch_get_standard_interface();
|
||||
}
|
||||
|
||||
extern int rawnet_status(void)
|
||||
int rawnet_status(void)
|
||||
{
|
||||
return rawnet_arch_status();
|
||||
}
|
||||
|
||||
|
||||
#endif /* #ifdef HAVE_RAWNET */
|
||||
|
@ -121,7 +121,7 @@ int rawnet_arch_receive(uint8_t *pbuffer, int *plen, int *phashed,
|
||||
assert((*plen & 1) == 0);
|
||||
|
||||
ok = rawnet_arch_read(pbuffer, *plen);
|
||||
if (ok < 0) return 0;
|
||||
if (ok <= 0) return 0;
|
||||
|
||||
if (ok & 1) ++ok;
|
||||
*plen = ok;
|
||||
|
@ -50,6 +50,8 @@ int rawnet_arch_activate(const char *interface_name) {
|
||||
* *MIGHT* re-use the previous MAC address.
|
||||
*/
|
||||
|
||||
if (interface) return 1;
|
||||
|
||||
memset(interface_mac, 0, sizeof(interface_mac));
|
||||
memset(interface_fake_mac, 0, sizeof(interface_fake_mac));
|
||||
interface_status = 0;
|
||||
|
673
src/rawnet/rawnetarch_vmnet_helper.c
Normal file
673
src/rawnet/rawnetarch_vmnet_helper.c
Normal file
@ -0,0 +1,673 @@
|
||||
/*
|
||||
* OS X 10.10+
|
||||
* vmnet support (clang -framework vmnet -framework Foundation)
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <spawn.h>
|
||||
#include <err.h>
|
||||
#include <signal.h>
|
||||
#include <mach-o/dyld.h>
|
||||
|
||||
#include "rawnetarch.h"
|
||||
#include "rawnetsupp.h"
|
||||
|
||||
|
||||
enum {
|
||||
MSG_QUIT,
|
||||
MSG_STATUS,
|
||||
MSG_READ,
|
||||
MSG_WRITE
|
||||
};
|
||||
#define MAKE_MSG(msg, extra) (msg | ((extra) << 8))
|
||||
|
||||
static const uint8_t broadcast_mac[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
static uint8_t interface_mac[6];
|
||||
static uint8_t interface_fake_mac[6];
|
||||
static uint32_t interface_mtu;
|
||||
static uint32_t interface_packet_size;
|
||||
static uint8_t *interface_buffer = NULL;
|
||||
|
||||
static pid_t interface_pid = 0;
|
||||
static int interface_pipe[2];
|
||||
|
||||
|
||||
static pid_t safe_waitpid(pid_t pid, int *stat_loc, int options) {
|
||||
for(;;) {
|
||||
pid_t rv = waitpid(pid, stat_loc,options);
|
||||
if (rv < 0 && errno == EINTR) continue;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t safe_read(void *data, size_t nbytes) {
|
||||
for (;;) {
|
||||
ssize_t rv = read(interface_pipe[0], data, nbytes);
|
||||
if (rv < 0 && errno == EINTR) continue;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t safe_readv(const struct iovec *iov, int iovcnt) {
|
||||
|
||||
for(;;) {
|
||||
ssize_t rv = readv(interface_pipe[0], iov, iovcnt);
|
||||
if (rv < 0 && errno == EINTR) continue;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ssize_t safe_write(const void *data, size_t nbytes) {
|
||||
for (;;) {
|
||||
ssize_t rv = write(interface_pipe[1], data, nbytes);
|
||||
if (rv < 0 && errno == EINTR) continue;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t safe_writev(const struct iovec *iov, int iovcnt) {
|
||||
|
||||
for(;;) {
|
||||
ssize_t rv = writev(interface_pipe[1], iov, iovcnt);
|
||||
if (rv < 0 && errno == EINTR) continue;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
/* block the sigpipe signal */
|
||||
static int block_pipe(struct sigaction *oact) {
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler = SIG_IGN;
|
||||
act.sa_flags = SA_RESTART;
|
||||
|
||||
return sigaction(SIGPIPE, &act, oact);
|
||||
}
|
||||
static int restore_pipe(const struct sigaction *oact) {
|
||||
return sigaction(SIGPIPE, oact, NULL);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int block_pipe(sigset_t *oldset) {
|
||||
sigset_t set;
|
||||
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGPIPE);
|
||||
sigaddset(&set, SIGCHLD);
|
||||
|
||||
return sigprocmask(SIG_BLOCK, &set, oldset);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int check_child_status(void) {
|
||||
pid_t pid;
|
||||
int stat;
|
||||
pid = safe_waitpid(interface_pid, &stat, WNOHANG);
|
||||
|
||||
if (pid < 0 && errno == ECHILD) {
|
||||
fprintf(stderr, "child process does not exist.\n");
|
||||
close(interface_pipe[0]);
|
||||
close(interface_pipe[1]);
|
||||
interface_pid = 0;
|
||||
return 0;
|
||||
}
|
||||
if (pid == interface_pid) {
|
||||
if (WIFEXITED(stat)) fprintf(stderr, "child process exited.\n");
|
||||
if (WIFSIGNALED(stat)) fprintf(stderr, "child process signalled.\n");
|
||||
|
||||
close(interface_pipe[0]);
|
||||
close(interface_pipe[1]);
|
||||
interface_pid = 0;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char *get_relative_path(const char *leaf) {
|
||||
|
||||
uint32_t size = 0;
|
||||
char *buffer = 0;
|
||||
int ok;
|
||||
char *cp;
|
||||
int l;
|
||||
|
||||
l = strlen(leaf);
|
||||
ok = _NSGetExecutablePath(NULL, &size);
|
||||
size += l + 1;
|
||||
buffer = malloc(size);
|
||||
if (buffer) {
|
||||
ok = _NSGetExecutablePath(buffer, &size);
|
||||
if (ok < 0) {
|
||||
free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
cp = strrchr(buffer, '/');
|
||||
if (cp)
|
||||
strcpy(cp + 1 , leaf);
|
||||
else {
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
int rawnet_arch_init(void) {
|
||||
//interface = NULL;
|
||||
return 1;
|
||||
}
|
||||
void rawnet_arch_pre_reset(void) {
|
||||
/* NOP */
|
||||
}
|
||||
|
||||
void rawnet_arch_post_reset(void) {
|
||||
/* NOP */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int rawnet_arch_activate(const char *interface_name) {
|
||||
|
||||
|
||||
int ok;
|
||||
posix_spawn_file_actions_t actions;
|
||||
posix_spawnattr_t attr;
|
||||
char *argv[] = { "vmnet_helper", NULL };
|
||||
char *path = NULL;
|
||||
|
||||
int pipe_stdin[2];
|
||||
int pipe_stdout[2];
|
||||
|
||||
struct sigaction oldaction;
|
||||
|
||||
|
||||
if (interface_pid > 0) return 1;
|
||||
|
||||
/* fd[0] = read, fd[1] = write */
|
||||
ok = pipe(pipe_stdin);
|
||||
if (ok < 0) { warn("pipe"); return 0; }
|
||||
|
||||
ok = pipe(pipe_stdout);
|
||||
if (ok < 0) { warn("pipe"); return 0; }
|
||||
|
||||
block_pipe(&oldaction);
|
||||
|
||||
posix_spawn_file_actions_init(&actions);
|
||||
posix_spawnattr_init(&attr);
|
||||
|
||||
|
||||
posix_spawn_file_actions_adddup2(&actions, pipe_stdin[0], STDIN_FILENO);
|
||||
posix_spawn_file_actions_adddup2(&actions, pipe_stdout[1], STDOUT_FILENO);
|
||||
|
||||
posix_spawn_file_actions_addclose(&actions, pipe_stdin[0]);
|
||||
posix_spawn_file_actions_addclose(&actions, pipe_stdin[1]);
|
||||
|
||||
posix_spawn_file_actions_addclose(&actions, pipe_stdout[0]);
|
||||
posix_spawn_file_actions_addclose(&actions, pipe_stdout[1]);
|
||||
|
||||
|
||||
path = get_relative_path("vmnet_helper");
|
||||
ok = posix_spawn(&interface_pid, path, &actions, &attr, argv, NULL);
|
||||
free(path);
|
||||
posix_spawn_file_actions_destroy(&actions);
|
||||
posix_spawnattr_destroy(&attr);
|
||||
|
||||
close(pipe_stdin[0]);
|
||||
close(pipe_stdout[1]);
|
||||
/* posix spawn returns 0 on success, error code on failure. */
|
||||
|
||||
if (ok != 0) {
|
||||
fprintf(stderr, "posix_spawn vmnet_helper failed: %d\n", ok);
|
||||
close(pipe_stdin[1]);
|
||||
close(pipe_stdout[0]);
|
||||
interface_pid = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
interface_pipe[0] = pipe_stdout[0];
|
||||
interface_pipe[1] = pipe_stdin[1];
|
||||
|
||||
/* now get the mac/mtu/etc */
|
||||
|
||||
uint32_t msg = MAKE_MSG(MSG_STATUS, 0);
|
||||
ok = safe_write(&msg, 4);
|
||||
if (ok != 4) goto fail;
|
||||
|
||||
ok = safe_read(&msg, 4);
|
||||
if (ok != 4) goto fail;
|
||||
|
||||
if (msg != MAKE_MSG(MSG_STATUS, 6 + 4 + 4)) goto fail;
|
||||
|
||||
struct iovec iov[3];
|
||||
iov[0].iov_len = 6;
|
||||
iov[0].iov_base = interface_mac;
|
||||
iov[1].iov_len = 4;
|
||||
iov[1].iov_base = &interface_mtu;
|
||||
iov[2].iov_len = 4;
|
||||
iov[2].iov_base = &interface_packet_size;
|
||||
|
||||
ok = safe_readv(iov, 3);
|
||||
if (ok != 6 + 4 + 4) goto fail;
|
||||
|
||||
/* sanity check */
|
||||
/* expect MTU = 1500, packet_size = 1518 */
|
||||
if (interface_packet_size < 256) {
|
||||
interface_packet_size = 1518;
|
||||
}
|
||||
interface_buffer = malloc(interface_packet_size);
|
||||
if (!interface_buffer) goto fail;
|
||||
|
||||
restore_pipe(&oldaction);
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
close(interface_pipe[0]);
|
||||
close(interface_pipe[1]);
|
||||
safe_waitpid(interface_pid, NULL, 0);
|
||||
interface_pid = 0;
|
||||
|
||||
restore_pipe(&oldaction);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rawnet_arch_deactivate(void) {
|
||||
|
||||
if (interface_pid) {
|
||||
close(interface_pipe[0]);
|
||||
close(interface_pipe[1]);
|
||||
for(;;) {
|
||||
int ok = waitpid(interface_pid, NULL, 0);
|
||||
if (ok < 0 && errno == EINTR) continue;
|
||||
break;
|
||||
}
|
||||
interface_pid = 0;
|
||||
}
|
||||
free(interface_buffer);
|
||||
interface_buffer = NULL;
|
||||
}
|
||||
|
||||
void rawnet_arch_set_mac(const uint8_t mac[6]) {
|
||||
|
||||
#ifdef RAWNET_DEBUG_ARCH
|
||||
log_message( rawnet_arch_log, "New MAC address set: %02X:%02X:%02X:%02X:%02X:%02X.", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] );
|
||||
#endif
|
||||
memcpy(interface_fake_mac, mac, 6);
|
||||
}
|
||||
void rawnet_arch_set_hashfilter(const uint32_t hash_mask[2]) {
|
||||
/* NOP */
|
||||
}
|
||||
|
||||
void rawnet_arch_recv_ctl(int bBroadcast, int bIA, int bMulticast, int bCorrect, int bPromiscuous, int bIAHash) {
|
||||
/* NOP */
|
||||
}
|
||||
|
||||
void rawnet_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver) {
|
||||
/* NOP */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
enum {
|
||||
eth_dest = 0, // destination address
|
||||
eth_src = 6, // source address
|
||||
eth_type = 12, // packet type
|
||||
eth_data = 14, // packet data
|
||||
};
|
||||
|
||||
enum {
|
||||
ip_ver_ihl = 0,
|
||||
ip_tos = 1,
|
||||
ip_len = 2,
|
||||
ip_id = 4,
|
||||
ip_frag = 6,
|
||||
ip_ttl = 8,
|
||||
ip_proto = 9,
|
||||
ip_header_cksum = 10,
|
||||
ip_src = 12,
|
||||
ip_dest = 16,
|
||||
ip_data = 20,
|
||||
};
|
||||
|
||||
enum {
|
||||
udp_source = 0, // source port
|
||||
udp_dest = 2, // destination port
|
||||
udp_len = 4, // length
|
||||
udp_cksum = 6, // checksum
|
||||
udp_data = 8, // total length udp header
|
||||
};
|
||||
|
||||
enum {
|
||||
bootp_op = 0, // operation
|
||||
bootp_hw = 1, // hardware type
|
||||
bootp_hlen = 2, // hardware len
|
||||
bootp_hp = 3, // hops
|
||||
bootp_transid = 4, // transaction id
|
||||
bootp_secs = 8, // seconds since start
|
||||
bootp_flags = 10, // flags
|
||||
bootp_ipaddr = 12, // ip address knwon by client
|
||||
bootp_ipclient = 16, // client ip from server
|
||||
bootp_ipserver = 20, // server ip
|
||||
bootp_ipgateway = 24, // gateway ip
|
||||
bootp_client_hrd = 28, // client mac address
|
||||
bootp_spare = 34,
|
||||
bootp_host = 44,
|
||||
bootp_fname = 108,
|
||||
bootp_data = 236, // total length bootp packet
|
||||
};
|
||||
|
||||
enum {
|
||||
arp_hw = 14, // hw type (eth = 0001)
|
||||
arp_proto = 16, // protocol (ip = 0800)
|
||||
arp_hwlen = 18, // hw addr len (eth = 06)
|
||||
arp_protolen = 19, // proto addr len (ip = 04)
|
||||
arp_op = 20, // request = 0001, reply = 0002
|
||||
arp_shw = 22, // sender hw addr
|
||||
arp_sp = 28, // sender proto addr
|
||||
arp_thw = 32, // target hw addr
|
||||
arp_tp = 38, // target protoaddr
|
||||
arp_data = 42, // total length of packet
|
||||
};
|
||||
|
||||
enum {
|
||||
dhcp_discover = 1,
|
||||
dhcp_offer = 2,
|
||||
dhcp_request = 3,
|
||||
dhcp_decline = 4,
|
||||
dhcp_pack = 5,
|
||||
dhcp_nack = 6,
|
||||
dhcp_release = 7,
|
||||
dhcp_inform = 8,
|
||||
};
|
||||
|
||||
static uint8_t oo[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
static uint8_t ff[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
static int is_arp(const uint8_t *packet, unsigned size) {
|
||||
return size == arp_data
|
||||
&& packet[12] == 0x08 && packet[13] == 0x06 /* ARP */
|
||||
&& packet[14] == 0x00 && packet[15] == 0x01 /* ethernet */
|
||||
&& packet[16] == 0x08 && packet[17] == 0x00 /* ipv4 */
|
||||
&& packet[18] == 0x06 /* hardware size */
|
||||
&& packet[19] == 0x04 /* protocol size */
|
||||
;
|
||||
}
|
||||
|
||||
static int is_broadcast(const uint8_t *packet, unsigned size) {
|
||||
return !memcmp(packet + 0, ff, 6);
|
||||
}
|
||||
|
||||
static int is_unicast(const uint8_t *packet, unsigned size) {
|
||||
return (*packet & 0x01) == 0;
|
||||
}
|
||||
|
||||
static int is_multicast(const uint8_t *packet, unsigned size) {
|
||||
return (*packet & 0x01) == 0x01 && !is_broadcast(packet, size);
|
||||
}
|
||||
|
||||
static int is_dhcp_out(const uint8_t *packet, unsigned size) {
|
||||
static uint8_t cookie[] = { 0x63, 0x82, 0x53, 0x63 };
|
||||
return size >= 282
|
||||
//&& !memcmp(&packet[0], ff, 6) /* broadcast */
|
||||
&& packet[12] == 0x08 && packet[13] == 0x00
|
||||
&& packet[14] == 0x45 /* version 4 */
|
||||
&& packet[23] == 0x11 /* UDP */
|
||||
//&& !memcmp(&packet[26], oo, 4) /* source ip */
|
||||
//&& !memcmp(&packet[30], ff, 4) /* dest ip */
|
||||
&& packet[34] == 0x00 && packet[35] == 0x44 /* source port */
|
||||
&& packet[36] == 0x00 && packet[37] == 0x43 /* dest port */
|
||||
//&& packet[44] == 0x01 /* dhcp boot req */
|
||||
&& packet[43] == 0x01 /* ethernet */
|
||||
&& packet[44] == 0x06 /* 6 byte mac */
|
||||
&& !memcmp(&packet[278], cookie, 4)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
static int is_dhcp_in(const uint8_t *packet, unsigned size) {
|
||||
static uint8_t cookie[] = { 0x63, 0x82, 0x53, 0x63 };
|
||||
return size >= 282
|
||||
//&& !memcmp(&packet[0], ff, 6) /* broadcast */
|
||||
&& packet[12] == 0x08 && packet[13] == 0x00
|
||||
&& packet[14] == 0x45 /* version 4 */
|
||||
&& packet[23] == 0x11 /* UDP */
|
||||
//&& !memcmp(&packet[26], oo, 4) /* source ip */
|
||||
//&& !memcmp(&packet[30], ff, 4) /* dest ip */
|
||||
&& packet[34] == 0x00 && packet[35] == 0x43 /* source port */
|
||||
&& packet[36] == 0x00 && packet[37] == 0x44 /* dest port */
|
||||
//&& packet[44] == 0x01 /* dhcp boot req */
|
||||
&& packet[43] == 0x01 /* ethernet */
|
||||
&& packet[44] == 0x06 /* 6 byte mac */
|
||||
&& !memcmp(&packet[278], cookie, 4)
|
||||
;
|
||||
}
|
||||
|
||||
static unsigned ip_checksum(const uint8_t *packet) {
|
||||
unsigned x = 0;
|
||||
unsigned i;
|
||||
for (i = 0; i < ip_data; i += 2) {
|
||||
if (i == ip_header_cksum) continue;
|
||||
x += packet[eth_data + i + 0 ] << 8;
|
||||
x += packet[eth_data + i + 1];
|
||||
}
|
||||
|
||||
/* add the carry */
|
||||
x += x >> 16;
|
||||
x &= 0xffff;
|
||||
return ~x & 0xffff;
|
||||
}
|
||||
|
||||
static void fix_incoming_packet(uint8_t *packet, unsigned size, const uint8_t real_mac[6], const uint8_t fake_mac[6]) {
|
||||
|
||||
if (memcmp(packet + 0, real_mac, 6) == 0)
|
||||
memcpy(packet + 0, fake_mac, 6);
|
||||
|
||||
/* dhcp request - fix the hardware address */
|
||||
if (is_unicast(packet, size) && is_dhcp_in(packet, size)) {
|
||||
if (!memcmp(packet + 70, real_mac, 6))
|
||||
memcpy(packet + 70, fake_mac, 6);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void fix_outgoing_packet(uint8_t *packet, unsigned size, const uint8_t real_mac[6], const uint8_t fake_mac[6]) {
|
||||
|
||||
|
||||
|
||||
if (memcmp(packet + 6, fake_mac, 6) == 0)
|
||||
memcpy(packet + 6, real_mac, 6);
|
||||
|
||||
if (is_arp(packet, size)) {
|
||||
/* sender mac address */
|
||||
if (!memcmp(packet + 22, fake_mac, 6))
|
||||
memcpy(packet + 22, real_mac, 6);
|
||||
return;
|
||||
}
|
||||
|
||||
/* dhcp request - fix the hardware address */
|
||||
if (is_broadcast(packet, size) && is_dhcp_out(packet, size)) {
|
||||
|
||||
if (!memcmp(packet + 70, fake_mac, 6))
|
||||
memcpy(packet + 70, real_mac, 6);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
int rawnet_arch_read(void *buffer, int nbyte) {
|
||||
|
||||
|
||||
uint32_t msg;
|
||||
int ok;
|
||||
int xfer;
|
||||
struct sigaction oldaction;
|
||||
|
||||
if (interface_pid <= 0) return -1;
|
||||
|
||||
block_pipe(&oldaction);
|
||||
|
||||
msg = MAKE_MSG(MSG_READ, 0);
|
||||
|
||||
ok = safe_write(&msg, 4);
|
||||
if (ok != 4) goto fail;
|
||||
|
||||
ok = safe_read(&msg, 4);
|
||||
if (ok != 4) goto fail;
|
||||
|
||||
if ((msg & 0xff) != MSG_READ) goto fail;
|
||||
|
||||
xfer = msg >> 8;
|
||||
if (xfer > interface_packet_size) {
|
||||
|
||||
fprintf(stderr, "packet size too big: %d\n", xfer);
|
||||
|
||||
/* drain the message ... */
|
||||
while (xfer) {
|
||||
int count = interface_packet_size;
|
||||
if (count > xfer) count = xfer;
|
||||
ok = safe_read(interface_buffer, count);
|
||||
if (ok < 0) goto fail;
|
||||
xfer -= ok;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (xfer == 0) return -1;
|
||||
|
||||
ok = safe_read(interface_buffer, xfer);
|
||||
if (ok != xfer) goto fail;
|
||||
|
||||
|
||||
fix_incoming_packet(interface_buffer, xfer, interface_mac, interface_fake_mac);
|
||||
|
||||
if (!is_multicast(interface_buffer, xfer)) { /* multicast crap */
|
||||
fprintf(stderr, "\nrawnet_arch_receive: %u\n", (unsigned)xfer);
|
||||
rawnet_hexdump(interface_buffer, xfer);
|
||||
}
|
||||
|
||||
if (xfer > nbyte) xfer = nbyte;
|
||||
memcpy(buffer, interface_buffer, xfer);
|
||||
|
||||
restore_pipe(&oldaction);
|
||||
return xfer;
|
||||
|
||||
fail:
|
||||
/* check if process still ok? */
|
||||
check_child_status();
|
||||
restore_pipe(&oldaction);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int rawnet_arch_write(const void *buffer, int nbyte) {
|
||||
|
||||
int ok;
|
||||
uint32_t msg;
|
||||
struct iovec iov[2];
|
||||
struct sigaction oldaction;
|
||||
|
||||
if (interface_pid <= 0) return -1;
|
||||
|
||||
|
||||
|
||||
if (nbyte <= 0) return 0;
|
||||
|
||||
if (nbyte > interface_packet_size) {
|
||||
log_message(rawnet_arch_log, "packet is too big: %d", nbyte);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* copy the buffer and fix the source mac address. */
|
||||
memcpy(interface_buffer, buffer, nbyte);
|
||||
fix_outgoing_packet(interface_buffer, nbyte, interface_mac, interface_fake_mac);
|
||||
|
||||
|
||||
fprintf(stderr, "\nrawnet_arch_transmit: %u\n", (unsigned)nbyte);
|
||||
rawnet_hexdump(interface_buffer, nbyte);
|
||||
|
||||
|
||||
block_pipe(&oldaction);
|
||||
|
||||
msg = MAKE_MSG(MSG_WRITE, nbyte);
|
||||
|
||||
iov[0].iov_base = &msg;
|
||||
iov[0].iov_len = 4;
|
||||
iov[1].iov_base = interface_buffer;
|
||||
iov[1].iov_len = nbyte;
|
||||
|
||||
|
||||
ok = safe_writev(iov, 2);
|
||||
if (ok != 4 + nbyte) goto fail;
|
||||
|
||||
ok = safe_read(&msg, 4);
|
||||
if (ok != 4) goto fail;
|
||||
|
||||
if (msg != MAKE_MSG(MSG_WRITE, nbyte)) goto fail;
|
||||
|
||||
restore_pipe(&oldaction);
|
||||
return nbyte;
|
||||
|
||||
fail:
|
||||
check_child_status();
|
||||
restore_pipe(&oldaction);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static unsigned adapter_index = 0;
|
||||
int rawnet_arch_enumadapter_open(void) {
|
||||
adapter_index = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rawnet_arch_enumadapter(char **ppname, char **ppdescription) {
|
||||
|
||||
if (adapter_index == 0) {
|
||||
++adapter_index;
|
||||
if (ppname) *ppname = lib_stralloc("vmnet");
|
||||
if (ppdescription) *ppdescription = lib_stralloc("vmnet");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rawnet_arch_enumadapter_close(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *rawnet_arch_get_standard_interface(void) {
|
||||
return lib_stralloc("vmnet");
|
||||
}
|
||||
|
||||
int rawnet_arch_get_mtu(void) {
|
||||
return interface_pid > 0 ? interface_mtu : -1;
|
||||
}
|
||||
|
||||
int rawnet_arch_get_mac(uint8_t mac[6]) {
|
||||
if (interface_pid > 0) {
|
||||
memcpy(mac, interface_mac, 6);
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int rawnet_arch_status(void) {
|
||||
return interface_pid > 0 ? 1 : 0;
|
||||
}
|
||||
|
331
src/rawnet/vmnet_helper.c
Normal file
331
src/rawnet/vmnet_helper.c
Normal file
@ -0,0 +1,331 @@
|
||||
/* vmnet helper */
|
||||
/* because it needs root permissions ... sigh */
|
||||
|
||||
/*
|
||||
* basicly... run as root, read messages from stdin, write to stdout.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
#include <vmnet/vmnet.h>
|
||||
#include <errno.h>
|
||||
#include <err.h>
|
||||
|
||||
|
||||
static interface_ref interface;
|
||||
static uint8_t interface_mac[6];
|
||||
static long interface_mtu;
|
||||
static long interface_packet_size;
|
||||
static vmnet_return_t interface_status;
|
||||
|
||||
static size_t buffer_size = 0;
|
||||
static uint8_t *buffer = NULL;
|
||||
|
||||
enum {
|
||||
MSG_QUIT,
|
||||
MSG_STATUS,
|
||||
MSG_READ,
|
||||
MSG_WRITE
|
||||
};
|
||||
#define MAKE_MSG(msg, extra) (msg | ((extra) << 8))
|
||||
|
||||
ssize_t safe_read(void *buffer, size_t nbyte) {
|
||||
|
||||
ssize_t rv;
|
||||
for(;;) {
|
||||
rv = read(STDIN_FILENO, buffer, nbyte);
|
||||
if (rv < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
err(1, "read");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
ssize_t safe_readv(const struct iovec *iov, int iovcnt) {
|
||||
|
||||
ssize_t rv;
|
||||
for(;;) {
|
||||
rv = readv(STDIN_FILENO, iov, iovcnt);
|
||||
if (rv < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
err(1, "readv");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
ssize_t safe_write(const void *buffer, size_t nbyte) {
|
||||
|
||||
ssize_t rv;
|
||||
for(;;) {
|
||||
rv = write(STDOUT_FILENO, buffer, nbyte);
|
||||
if (rv < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
err(1, "write");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
ssize_t safe_writev(const struct iovec *iov, int iovcnt) {
|
||||
|
||||
ssize_t rv;
|
||||
for(;;) {
|
||||
rv = writev(STDOUT_FILENO, iov, iovcnt);
|
||||
if (rv < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
err(1, "writev");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
void msg_status(uint32_t size) {
|
||||
struct iovec iov[4];
|
||||
|
||||
uint32_t msg = MAKE_MSG(MSG_STATUS, 6 + 4 + 4);
|
||||
|
||||
iov[0].iov_len = 4;
|
||||
iov[0].iov_base = &msg;
|
||||
|
||||
iov[1].iov_len = 6;
|
||||
iov[1].iov_base = interface_mac;
|
||||
|
||||
iov[2].iov_len = 4;
|
||||
iov[2].iov_base = &interface_mtu;
|
||||
|
||||
iov[3].iov_len = 4;
|
||||
iov[3].iov_base = &interface_packet_size;
|
||||
|
||||
|
||||
safe_writev(iov, 4);
|
||||
}
|
||||
|
||||
int classify_mac(uint8_t *mac) {
|
||||
if ((mac[0] & 0x01) == 0) return 1; /* unicast */
|
||||
if (memcmp(mac, "\xff\xff\xff\xff\xff\xff", 6) == 0) return 0xff; /* broadcast */
|
||||
return 2; /* multicast */
|
||||
}
|
||||
|
||||
void msg_read(uint32_t flags) {
|
||||
/* flag to block broadcast, multicast, etc? */
|
||||
|
||||
int count = 1;
|
||||
int xfer;
|
||||
vmnet_return_t st;
|
||||
struct vmpktdesc v;
|
||||
struct iovec iov[2];
|
||||
|
||||
uint32_t msg;
|
||||
|
||||
|
||||
for(;;) {
|
||||
int type;
|
||||
|
||||
iov[0].iov_base = buffer;
|
||||
iov[0].iov_len = interface_packet_size;
|
||||
|
||||
v.vm_pkt_size = interface_packet_size;
|
||||
v.vm_pkt_iov = iov;
|
||||
v.vm_pkt_iovcnt = 1;
|
||||
v.vm_flags = 0;
|
||||
|
||||
count = 1;
|
||||
st = vmnet_read(interface, &v, &count);
|
||||
if (st != VMNET_SUCCESS) errx(1, "vmnet_read");
|
||||
|
||||
if (count < 1) break;
|
||||
/* todo -- skip multicast messages based on flag? */
|
||||
type = classify_mac(buffer);
|
||||
if (type == 2) continue; /* multicast */
|
||||
break;
|
||||
}
|
||||
|
||||
xfer = count == 1 ? v.vm_pkt_size : 0;
|
||||
msg = MAKE_MSG(MSG_READ, xfer);
|
||||
iov[0].iov_len = 4;
|
||||
iov[0].iov_base = &msg;
|
||||
iov[1].iov_len = xfer;
|
||||
iov[1].iov_base = buffer;
|
||||
|
||||
safe_writev(iov, count == 1 ? 2 : 1);
|
||||
}
|
||||
|
||||
|
||||
void msg_write(uint32_t size) {
|
||||
|
||||
ssize_t ok;
|
||||
|
||||
int count = 1;
|
||||
vmnet_return_t st;
|
||||
struct vmpktdesc v;
|
||||
struct iovec iov;
|
||||
uint32_t msg;
|
||||
|
||||
if (size > interface_packet_size) errx(1, "packet too big");
|
||||
for(;;) {
|
||||
ok = safe_read(buffer, size);
|
||||
if (ok < 0) err(1,"read");
|
||||
if (ok != size) errx(1,"message truncated");
|
||||
break;
|
||||
}
|
||||
|
||||
iov.iov_base = buffer;
|
||||
iov.iov_len = size;
|
||||
|
||||
v.vm_pkt_size = size;
|
||||
v.vm_pkt_iov = &iov;
|
||||
v.vm_pkt_iovcnt = 1;
|
||||
v.vm_flags = 0;
|
||||
|
||||
st = vmnet_write(interface, &v, &count);
|
||||
|
||||
if (st != VMNET_SUCCESS) errx(1, "vmnet_write");
|
||||
|
||||
|
||||
msg = MAKE_MSG(MSG_WRITE, size);
|
||||
iov.iov_len = 4;
|
||||
iov.iov_base = &msg;
|
||||
|
||||
safe_writev(&iov, 1);
|
||||
}
|
||||
|
||||
void startup(void) {
|
||||
|
||||
xpc_object_t dict;
|
||||
dispatch_queue_t q;
|
||||
dispatch_semaphore_t sem;
|
||||
|
||||
|
||||
memset(interface_mac, 0, sizeof(interface_mac));
|
||||
interface_status = 0;
|
||||
interface_mtu = 0;
|
||||
interface_packet_size = 0;
|
||||
|
||||
dict = xpc_dictionary_create(NULL, NULL, 0);
|
||||
xpc_dictionary_set_uint64(dict, vmnet_operation_mode_key, VMNET_SHARED_MODE);
|
||||
sem = dispatch_semaphore_create(0);
|
||||
q = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);
|
||||
|
||||
interface = vmnet_start_interface(dict, q, ^(vmnet_return_t status, xpc_object_t params){
|
||||
interface_status = status;
|
||||
if (status == VMNET_SUCCESS) {
|
||||
const char *cp;
|
||||
cp = xpc_dictionary_get_string(params, vmnet_mac_address_key);
|
||||
fprintf(stderr, "vmnet mac: %s\n", cp);
|
||||
sscanf(cp, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
||||
&interface_mac[0],
|
||||
&interface_mac[1],
|
||||
&interface_mac[2],
|
||||
&interface_mac[3],
|
||||
&interface_mac[4],
|
||||
&interface_mac[5]
|
||||
);
|
||||
|
||||
interface_mtu = xpc_dictionary_get_uint64(params, vmnet_mtu_key);
|
||||
interface_packet_size = xpc_dictionary_get_uint64(params, vmnet_max_packet_size_key);
|
||||
|
||||
fprintf(stderr, "vmnet mtu: %u\n", (unsigned)interface_mtu);
|
||||
fprintf(stderr, "vmnet packet size: %u\n", (unsigned)interface_packet_size);
|
||||
|
||||
}
|
||||
dispatch_semaphore_signal(sem);
|
||||
});
|
||||
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
|
||||
|
||||
if (interface_status == VMNET_SUCCESS) {
|
||||
buffer_size = (interface_packet_size * 2 + 1023) & ~1023;
|
||||
buffer = (uint8_t *)malloc(buffer_size);
|
||||
} else {
|
||||
if (interface) {
|
||||
vmnet_stop_interface(interface, q, ^(vmnet_return_t status){
|
||||
dispatch_semaphore_signal(sem);
|
||||
});
|
||||
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
|
||||
interface = NULL;
|
||||
}
|
||||
errx(1,"vmnet_start_interface failed");
|
||||
}
|
||||
|
||||
|
||||
dispatch_release(sem);
|
||||
xpc_release(dict);
|
||||
|
||||
}
|
||||
|
||||
void shutdown(void) {
|
||||
|
||||
dispatch_queue_t q;
|
||||
dispatch_semaphore_t sem;
|
||||
|
||||
|
||||
if (interface) {
|
||||
sem = dispatch_semaphore_create(0);
|
||||
q = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);
|
||||
|
||||
vmnet_stop_interface(interface, q, ^(vmnet_return_t status){
|
||||
dispatch_semaphore_signal(sem);
|
||||
});
|
||||
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
|
||||
dispatch_release(sem);
|
||||
|
||||
interface = NULL;
|
||||
interface_status = 0;
|
||||
}
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
buffer_size = 0;
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
|
||||
uint32_t msg;
|
||||
uint32_t extra;
|
||||
ssize_t ok;
|
||||
|
||||
|
||||
startup();
|
||||
|
||||
for(;;) {
|
||||
ok = safe_read(&msg, 4);
|
||||
if (ok == 0) break;
|
||||
if (ok != 4) errx(1,"read msg");
|
||||
|
||||
extra = msg >> 8;
|
||||
msg = msg & 0xff;
|
||||
|
||||
switch(msg) {
|
||||
case MSG_STATUS:
|
||||
msg_status(extra);
|
||||
break;
|
||||
case MSG_QUIT:
|
||||
shutdown();
|
||||
exit(0);
|
||||
case MSG_READ:
|
||||
msg_read(extra);
|
||||
break;
|
||||
case MSG_WRITE:
|
||||
msg_write(extra);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
shutdown();
|
||||
exit(0);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user