/*
* tunemu - Tun device emulation for Darwin
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
- *
+ *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
+ *
*/
#include "tunemu.h"
#define PPPPROTO_CTL 1
-#define PPP_IP 0x21
-#define PPP_IPV6 0x57
+#define PPP_IP 0x21
+#define PPP_IPV6 0x57
#define SC_LOOP_TRAFFIC 0x00000200
-#define PPPIOCNEWUNIT _IOWR('t', 62, int)
-#define PPPIOCSFLAGS _IOW('t', 89, int)
-#define PPPIOCSNPMODE _IOW('t', 75, struct npioctl)
-#define PPPIOCATTCHAN _IOW('t', 56, int)
-#define PPPIOCGCHAN _IOR('t', 55, int)
-#define PPPIOCCONNECT _IOW('t', 58, int)
-#define PPPIOCGUNIT _IOR('t', 86, int)
-
-struct sockaddr_ppp
-{
- u_int8_t ppp_len;
- u_int8_t ppp_family;
- u_int16_t ppp_proto;
- u_int32_t ppp_cookie;
+#define PPPIOCNEWUNIT _IOWR('t', 62, int)
+#define PPPIOCSFLAGS _IOW('t', 89, int)
+#define PPPIOCSNPMODE _IOW('t', 75, struct npioctl)
+#define PPPIOCATTCHAN _IOW('t', 56, int)
+#define PPPIOCGCHAN _IOR('t', 55, int)
+#define PPPIOCCONNECT _IOW('t', 58, int)
+#define PPPIOCGUNIT _IOR('t', 86, int)
+
+struct sockaddr_ppp {
+ uint8_t ppp_len;
+ uint8_t ppp_family;
+ uint16_t ppp_proto;
+ uint32_t ppp_cookie;
};
-enum NPmode
-{
+enum NPmode {
NPMODE_PASS,
- NPMODE_DROP,
- NPMODE_ERROR,
- NPMODE_QUEUE
+ NPMODE_DROP,
+ NPMODE_ERROR,
+ NPMODE_QUEUE
};
-struct npioctl
-{
+struct npioctl {
int protocol;
enum NPmode mode;
};
static int pcap_use_count = 0;
static pcap_t *pcap = NULL;
-static int data_buffer_length = 0;
-static char *data_buffer = NULL;
+static size_t data_buffer_length = 0;
+static uint8_t *data_buffer = NULL;
-static void tun_error(char *format, ...)
-{
+static void tun_error(char *format, ...) {
va_list vl;
va_start(vl, format);
- vsnprintf(tunemu_error, ERROR_BUFFER_SIZE, format, vl);
+ vsnprintf(tunemu_error, sizeof(tunemu_error), format, vl);
va_end(vl);
}
-static void tun_noerror()
-{
+static void tun_noerror() {
*tunemu_error = 0;
}
-static void closeall()
-{
- int fd = getdtablesize();
- while (fd--)
+static void closeall() {
+ int fd = getdtablesize();
+
+ while(fd--) {
close(fd);
+ }
- open("/dev/null", O_RDWR, 0);
- dup(0);
- dup(0);
+ open("/dev/null", O_RDWR, 0);
+ dup(0);
+ dup(0);
}
-static int ppp_load_kext()
-{
+static int ppp_load_kext() {
int pid = fork();
- if (pid < 0)
- {
+
+ if(pid < 0) {
tun_error("fork for ppp kext: %s", strerror(errno));
return -1;
}
- if (pid == 0)
- {
+ if(pid == 0) {
closeall();
execle("/sbin/kextload", "kextload", PPP_KEXT_PATH, NULL, NULL);
exit(1);
}
int status;
- while (waitpid(pid, &status, 0) < 0)
- {
- if (errno == EINTR)
+
+ while(waitpid(pid, &status, 0) < 0) {
+ if(errno == EINTR) {
continue;
+ }
tun_error("waitpid for ppp kext: %s", strerror(errno));
return -1;
}
- if (WEXITSTATUS(status) != 0)
- {
+ if(WEXITSTATUS(status) != 0) {
tun_error("could not load ppp kext \"%s\"", PPP_KEXT_PATH);
return -1;
}
return 0;
}
-static int ppp_new_instance()
-{
+static int ppp_new_instance() {
// create ppp socket
- int ppp_sockfd = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL);
- if (ppp_sockfd < 0)
- {
- if (ppp_load_kext() < 0)
+ int ppp_sockfd = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL);
+
+ if(ppp_sockfd < 0) {
+ if(ppp_load_kext() < 0) {
return -1;
+ }
ppp_sockfd = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL);
- if (ppp_sockfd < 0)
- {
+
+ if(ppp_sockfd < 0) {
tun_error("creating ppp socket: %s", strerror(errno));
return -1;
}
}
// connect to ppp procotol
- struct sockaddr_ppp pppaddr;
- pppaddr.ppp_len = sizeof(struct sockaddr_ppp);
- pppaddr.ppp_family = AF_PPP;
- pppaddr.ppp_proto = PPPPROTO_CTL;
- pppaddr.ppp_cookie = 0;
- if (connect(ppp_sockfd, (struct sockaddr *)&pppaddr, sizeof(struct sockaddr_ppp)) < 0)
- {
+ struct sockaddr_ppp pppaddr;
+ pppaddr.ppp_len = sizeof(struct sockaddr_ppp);
+ pppaddr.ppp_family = AF_PPP;
+ pppaddr.ppp_proto = PPPPROTO_CTL;
+ pppaddr.ppp_cookie = 0;
+
+ if(connect(ppp_sockfd, (struct sockaddr *)&pppaddr, sizeof(struct sockaddr_ppp)) < 0) {
tun_error("connecting ppp socket: %s", strerror(errno));
close(ppp_sockfd);
return -1;
- }
+ }
tun_noerror();
return ppp_sockfd;
}
-static int ppp_new_unit(int *unit_number)
-{
+static int ppp_new_unit(int *unit_number) {
int fd = ppp_new_instance();
- if (fd < 0)
+
+ if(fd < 0) {
return -1;
+ }
// create ppp unit
- if (ioctl(fd, PPPIOCNEWUNIT, unit_number) < 0)
- {
+ if(ioctl(fd, PPPIOCNEWUNIT, unit_number) < 0) {
tun_error("creating ppp unit: %s", strerror(errno));
close(fd);
return -1;
- }
+ }
tun_noerror();
return fd;
}
-static int ppp_setup_unit(int unit_fd)
-{
+static int ppp_setup_unit(int unit_fd) {
// send traffic to program
int flags = SC_LOOP_TRAFFIC;
- if (ioctl(unit_fd, PPPIOCSFLAGS, &flags) < 0)
- {
+
+ if(ioctl(unit_fd, PPPIOCSFLAGS, &flags) < 0) {
tun_error("setting ppp loopback mode: %s", strerror(errno));
return -1;
- }
+ }
// allow packets
struct npioctl npi;
npi.protocol = PPP_IP;
npi.mode = NPMODE_PASS;
- if (ioctl(unit_fd, PPPIOCSNPMODE, &npi) < 0)
- {
+
+ if(ioctl(unit_fd, PPPIOCSNPMODE, &npi) < 0) {
tun_error("starting ppp unit: %s", strerror(errno));
return -1;
}
return 0;
}
-static int open_pcap()
-{
- if (pcap != NULL)
- {
+static int open_pcap() {
+ if(pcap != NULL) {
pcap_use_count++;
return 0;
}
pcap = pcap_open_live("lo0", BUFSIZ, 0, 1, errbuf);
pcap_use_count = 1;
- if (pcap == NULL)
- {
+ if(pcap == NULL) {
tun_error("opening pcap: %s", errbuf);
return -1;
}
return 0;
}
-static void close_pcap()
-{
- if (pcap == NULL)
+static void close_pcap() {
+ if(pcap == NULL) {
return;
+ }
pcap_use_count--;
- if (pcap_use_count == 0)
- {
+
+ if(pcap_use_count == 0) {
pcap_close(pcap);
pcap = NULL;
}
}
-static void allocate_data_buffer(int size)
-{
- if (data_buffer_length < size)
- {
+static void allocate_data_buffer(size_t size) {
+ if(data_buffer_length < size) {
free(data_buffer);
data_buffer_length = size;
data_buffer = malloc(data_buffer_length);
}
}
-static void make_device_name(tunemu_device device, int unit_number)
-{
+static void make_device_name(tunemu_device device, int unit_number) {
snprintf(device, sizeof(tunemu_device), "ppp%d", unit_number);
}
-static int check_device_name(tunemu_device device)
-{
- if (strlen(device) < 4)
+static int check_device_name(tunemu_device device) {
+ if(strlen(device) < 4) {
return -1;
+ }
int unit_number = atoi(device + 3);
- if (unit_number < 0 || unit_number > 999)
+
+ if(unit_number < 0 || unit_number > 999) {
return -1;
+ }
tunemu_device compare;
make_device_name(compare, unit_number);
- if (strcmp(device, compare) != 0)
+ if(strcmp(device, compare) != 0) {
return -1;
+ }
return 0;
}
-int tunemu_open(tunemu_device device)
-{
+int tunemu_open(tunemu_device device) {
int ppp_unit_number = -1;
- if (device[0] != 0)
- {
- if (check_device_name(device) < 0)
- {
+
+ if(device[0] != 0) {
+ if(check_device_name(device) < 0) {
tun_error("invalid device name \"%s\"", device);
return -1;
}
}
int ppp_unit_fd = ppp_new_unit(&ppp_unit_number);
- if (ppp_unit_fd < 0)
+
+ if(ppp_unit_fd < 0) {
return -1;
+ }
- if (ppp_setup_unit(ppp_unit_fd) < 0)
- {
+ if(ppp_setup_unit(ppp_unit_fd) < 0) {
close(ppp_unit_fd);
return -1;
}
- if (open_pcap() < 0)
- {
+ if(open_pcap() < 0) {
close(ppp_unit_fd);
return -1;
}
return ppp_unit_fd;
}
-int tunemu_close(int ppp_sockfd)
-{
+int tunemu_close(int ppp_sockfd) {
int ret = close(ppp_sockfd);
- if (ret == 0)
+ if(ret == 0) {
close_pcap();
+ }
return ret;
}
-int tunemu_read(int ppp_sockfd, char *buffer, int length)
-{
- allocate_data_buffer(length + 2);
+ssize_t tunemu_read(int ppp_sockfd, uint8_t *buffer, size_t buflen) {
+ allocate_data_buffer(buflen + 2);
- length = read(ppp_sockfd, data_buffer, length + 2);
- if (length < 0)
- {
+ ssize_t length = read(ppp_sockfd, data_buffer, buflen + 2);
+
+ if(length < 0) {
tun_error("reading packet: %s", strerror(errno));
return length;
}
+
tun_noerror();
length -= 2;
- if (length < 0)
+
+ if(length < 0) {
return 0;
+ }
memcpy(buffer, data_buffer + 2, length);
return length;
}
-int tunemu_write(int ppp_sockfd, char *buffer, int length)
-{
- allocate_data_buffer(length + 4);
+ssize_t tunemu_write(uint8_t *buffer, size_t buflen) {
+ allocate_data_buffer(buflen + 4);
data_buffer[0] = 0x02;
data_buffer[1] = 0x00;
data_buffer[2] = 0x00;
data_buffer[3] = 0x00;
- memcpy(data_buffer + 4, buffer, length);
+ memcpy(data_buffer + 4, buffer, buflen);
- if (pcap == NULL)
- {
+ if(pcap == NULL) {
tun_error("pcap not open");
return -1;
}
- length = pcap_inject(pcap, data_buffer, length + 4);
- if (length < 0)
- {
+ ssize_t length = pcap_inject(pcap, data_buffer, buflen + 4);
+
+ if(length < 0) {
tun_error("injecting packet: %s", pcap_geterr(pcap));
return length;
}
+
tun_noerror();
length -= 4;
- if (length < 0)
+
+ if(length < 0) {
return 0;
+ }
return length;
}