network/tun: rewritting tun driver and tun module for modern code base
Change-Id: Idf7ab6d2aa2b4f8cb4893053b531d2eae7418427 Reviewed-on: https://review.haiku-os.org/c/haiku/+/6608 Reviewed-by: Jérôme Duval <[email protected]> Reviewed-by: Alex von Gluck IV <[email protected]>
This commit is contained in:
committed by
Alex von Gluck IV
parent
35c4600e21
commit
47f43bccee
@@ -14,15 +14,6 @@
|
|||||||
#define NET_TUN_MODULE_NAME "network/devices/tun/v1"
|
#define NET_TUN_MODULE_NAME "network/devices/tun/v1"
|
||||||
|
|
||||||
|
|
||||||
struct tun_device : net_device {
|
|
||||||
//queue
|
|
||||||
/*
|
|
||||||
int fd;
|
|
||||||
uint32 frame_size;*/
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct tun_module_info {
|
struct tun_module_info {
|
||||||
struct net_device_module_info;
|
struct net_device_module_info;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
SubDir HAIKU_TOP src add-ons kernel drivers network ;
|
SubDir HAIKU_TOP src add-ons kernel drivers network ;
|
||||||
|
|
||||||
HaikuSubInclude ether ;
|
HaikuSubInclude ether ;
|
||||||
|
HaikuSubInclude tun ;
|
||||||
HaikuSubInclude wlan ;
|
HaikuSubInclude wlan ;
|
||||||
HaikuSubInclude wwan ;
|
HaikuSubInclude wwan ;
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
SubDir HAIKU_TOP src add-ons kernel drivers network tun ;
|
SubDir HAIKU_TOP src add-ons kernel drivers network tun ;
|
||||||
|
|
||||||
|
SubDirHdrs [ FDirName $(HAIKU_TOP) src add-ons kernel network protocols tcp ] ;
|
||||||
|
UsePrivateHeaders drivers kernel net ;
|
||||||
|
UsePrivateKernelHeaders ;
|
||||||
UsePrivateSystemHeaders ;
|
UsePrivateSystemHeaders ;
|
||||||
# For net_tun.h
|
|
||||||
UsePrivateHeaders net ;
|
|
||||||
|
|
||||||
KernelAddon tun_config :
|
KernelAddon tun_driver :
|
||||||
driver.c
|
BufferQueue.cpp
|
||||||
|
driver.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
|
SEARCH on [ FGristFiles
|
||||||
|
BufferQueue.cpp
|
||||||
|
] = [ FDirName $(HAIKU_TOP) src add-ons kernel network protocols tcp ] ;
|
||||||
|
|||||||
@@ -1,379 +0,0 @@
|
|||||||
/*
|
|
||||||
* /dev/config/tun network tunnel driver for BeOS
|
|
||||||
* (c) 2003, mmu_man, [email protected]
|
|
||||||
* licenced under MIT licence.
|
|
||||||
*/
|
|
||||||
#include <Drivers.h>
|
|
||||||
#include <KernelExport.h>
|
|
||||||
#include <OS.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <fsproto.h>
|
|
||||||
#include "bone_tun.h"
|
|
||||||
|
|
||||||
|
|
||||||
const char * device_names[] = {TUN_DRIVER_NAME, NULL};
|
|
||||||
extern device_hooks tun_hooks;
|
|
||||||
|
|
||||||
int32 api_version = B_CUR_DRIVER_API_VERSION;
|
|
||||||
|
|
||||||
vint32 if_mod_ref_count = 0;
|
|
||||||
bone_tun_interface_info_t *gIfaceModule = NULL;
|
|
||||||
bone_util_info_t *gUtil = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
init_hardware(void)
|
|
||||||
{
|
|
||||||
dprintf("tun:init_hardware()\n");
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
init_driver(void)
|
|
||||||
{
|
|
||||||
dprintf("tun:init_driver()\n");
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
uninit_driver(void)
|
|
||||||
{
|
|
||||||
dprintf("tun:uninit_driver()\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const char**
|
|
||||||
publish_devices()
|
|
||||||
{
|
|
||||||
return device_names;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
device_hooks*
|
|
||||||
find_device(const char *name)
|
|
||||||
{
|
|
||||||
(void)name;
|
|
||||||
return &tun_hooks;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
tun_open(const char *name, uint32 flags, cookie_t **cookie)
|
|
||||||
{
|
|
||||||
status_t err = B_OK;
|
|
||||||
(void)name; (void)flags;
|
|
||||||
/* XXX: add O_NONBLOCK + FIONBIO */
|
|
||||||
#if DEBUG > 1
|
|
||||||
dprintf("tun:open(%s, 0x%08lx,)\n", name, flags);
|
|
||||||
#endif
|
|
||||||
err = get_module(BONE_UTIL_MOD_NAME, (struct module_info **)&gUtil);
|
|
||||||
if (err < B_OK)
|
|
||||||
return err;
|
|
||||||
err = get_module(TUN_INTERFACE_MODULE, (struct module_info **)&gIfaceModule);
|
|
||||||
if (err < B_OK) {
|
|
||||||
put_module(BONE_UTIL_MOD_NAME);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* XXX: FIXME, still not ok (rescan) */
|
|
||||||
if (atomic_add(&if_mod_ref_count, 1) < 1) /* force one more open to keep loaded */
|
|
||||||
get_module(TUN_INTERFACE_MODULE, (struct module_info **)&gIfaceModule);
|
|
||||||
|
|
||||||
*cookie = (void*)malloc(sizeof(cookie_t));
|
|
||||||
if (*cookie == NULL) {
|
|
||||||
dprintf("tun_open : error allocating cookie\n");
|
|
||||||
goto err0;
|
|
||||||
}
|
|
||||||
memset(*cookie, 0, sizeof(cookie_t));
|
|
||||||
(*cookie)->blocking_io = true;
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
err1:
|
|
||||||
dprintf("tun_open : cleanup : will free cookie\n");
|
|
||||||
free(*cookie);
|
|
||||||
*cookie = NULL;
|
|
||||||
put_module(TUN_INTERFACE_MODULE);
|
|
||||||
put_module(BONE_UTIL_MOD_NAME);
|
|
||||||
err0:
|
|
||||||
return B_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
tun_close(void *cookie)
|
|
||||||
{
|
|
||||||
(void)cookie;
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
tun_free(cookie_t *cookie)
|
|
||||||
{
|
|
||||||
status_t err = B_OK;
|
|
||||||
#if DEBUG > 1
|
|
||||||
dprintf("tun_close()\n");
|
|
||||||
#endif
|
|
||||||
if (cookie->iface)
|
|
||||||
err = gIfaceModule->tun_detach_driver(cookie->iface, true);
|
|
||||||
free(cookie);
|
|
||||||
atomic_add(&if_mod_ref_count, -1);
|
|
||||||
put_module(TUN_INTERFACE_MODULE);
|
|
||||||
put_module(BONE_UTIL_MOD_NAME);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
tun_ioctl(cookie_t *cookie, uint32 op, void *data, size_t len)
|
|
||||||
{
|
|
||||||
ifreq_t *ifr;
|
|
||||||
bone_tun_if_interface_t *iface;
|
|
||||||
(void)cookie; (void)op; (void)data; (void)len;
|
|
||||||
iface = cookie->iface;
|
|
||||||
#if DEBUG > 1
|
|
||||||
dprintf("tun_ioctl(%d(0x%08lx), , %d)\n", op, op, len);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
switch (op) {
|
|
||||||
case B_SET_NONBLOCKING_IO:
|
|
||||||
cookie->blocking_io = false;
|
|
||||||
return B_OK;
|
|
||||||
case B_SET_BLOCKING_IO:
|
|
||||||
cookie->blocking_io = true;
|
|
||||||
return B_OK;
|
|
||||||
case TUNSETNOCSUM:
|
|
||||||
return B_OK;//EOPNOTSUPP;
|
|
||||||
case TUNSETDEBUG:
|
|
||||||
return B_OK;//EOPNOTSUPP;
|
|
||||||
case TUNSETIFF:
|
|
||||||
if (data == NULL)
|
|
||||||
return EINVAL;
|
|
||||||
ifr = (ifreq_t *)data;
|
|
||||||
|
|
||||||
iface = gIfaceModule->tun_reuse_or_create(ifr, cookie);
|
|
||||||
if (iface != NULL) {
|
|
||||||
dprintf("tun: new tunnel created: %s, flags: 0x%08lx\n", ifr->ifr_name, iface->flags);
|
|
||||||
return B_OK;
|
|
||||||
} else
|
|
||||||
dprintf("tun: can't allocate a new tunnel!\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SIOCGIFHWADDR:
|
|
||||||
if (data == NULL)
|
|
||||||
return EINVAL;
|
|
||||||
ifr = (ifreq_t *)data;
|
|
||||||
if (iface == NULL)
|
|
||||||
return EINVAL;
|
|
||||||
if (strncmp(ifr->ifr_name, iface->ifn->if_name, IFNAMSIZ) != 0)
|
|
||||||
return EINVAL;
|
|
||||||
memcpy(ifr->ifr_hwaddr, iface->fakemac.octet, 6);
|
|
||||||
return B_OK;
|
|
||||||
case SIOCSIFHWADDR:
|
|
||||||
if (data == NULL)
|
|
||||||
return EINVAL;
|
|
||||||
ifr = (ifreq_t *)data;
|
|
||||||
if (iface == NULL)
|
|
||||||
return EINVAL;
|
|
||||||
if (strncmp(ifr->ifr_name, iface->ifn->if_name, IFNAMSIZ) != 0)
|
|
||||||
return EINVAL;
|
|
||||||
memcpy(iface->fakemac.octet, ifr->ifr_hwaddr, 6);
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
}
|
|
||||||
return B_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
tun_read(cookie_t *cookie, off_t position, void *data, size_t *numbytes)
|
|
||||||
{
|
|
||||||
bone_data_t *bdata;
|
|
||||||
uint32 got;
|
|
||||||
ssize_t pktsize;
|
|
||||||
if (cookie->iface == NULL)
|
|
||||||
return EBUSY;
|
|
||||||
|
|
||||||
//if ((pktsize = gUtil->dequeue_fifo_data(cookie->iface->wfifo, &bdata, B_INFINITE_TIMEOUT, false)) < 0)
|
|
||||||
pktsize = gUtil->dequeue_fifo_data(cookie->iface->wfifo, &bdata, cookie->blocking_io?B_INFINITE_TIMEOUT:0LL, false);
|
|
||||||
|
|
||||||
if (pktsize < 0) {
|
|
||||||
*numbytes = 0;
|
|
||||||
return pktsize;
|
|
||||||
}
|
|
||||||
#if DEBUG > 2
|
|
||||||
dprintf("tun: dequeue = %ld, datalen = %ld, \n", pktsize, bdata?bdata->datalen:0);
|
|
||||||
#endif
|
|
||||||
pktsize = (ssize_t)bdata->datalen;
|
|
||||||
|
|
||||||
got = gUtil->copy_from_data(bdata, 0L, data, MIN((bdata->datalen), (*numbytes)));
|
|
||||||
//got = MIN((*numbytes), bdata->datalen);
|
|
||||||
if ((pktsize > *numbytes) && !(cookie->iface->flags & TUN_NO_PI))
|
|
||||||
((struct tun_pi *)data)->flags |= TUN_PKT_STRIP;
|
|
||||||
gUtil->delete_data(bdata);
|
|
||||||
#if DEBUG > 2
|
|
||||||
dprintf("tun: read() got %ld bytes, buf is %ld\n", got, *numbytes);
|
|
||||||
dump_data(bdata);
|
|
||||||
iovec_dump_data(bdata);
|
|
||||||
#endif
|
|
||||||
*numbytes = got;
|
|
||||||
return B_OK;
|
|
||||||
ERROR_EOF:
|
|
||||||
*numbytes = 0;
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
tun_write(cookie_t *cookie, off_t position, const void *data, size_t *numbytes)
|
|
||||||
{
|
|
||||||
bone_data_t *bdata = NULL;
|
|
||||||
void *buf;
|
|
||||||
status_t err = B_NO_MEMORY;
|
|
||||||
(void)position;
|
|
||||||
|
|
||||||
/* XXX: max for *numbytes ? */
|
|
||||||
|
|
||||||
if (cookie->iface == NULL)
|
|
||||||
return EBUSY;
|
|
||||||
bdata = gUtil->new_data();
|
|
||||||
if (bdata == NULL)
|
|
||||||
return B_NO_MEMORY;
|
|
||||||
buf = gUtil->falloc(*numbytes);
|
|
||||||
if (buf == NULL)
|
|
||||||
goto ERROR_1;
|
|
||||||
memcpy(buf, data, *numbytes);
|
|
||||||
|
|
||||||
if (gUtil->prepend_data(bdata, buf, *numbytes, gUtil->free) < 0) {
|
|
||||||
gUtil->free(buf);
|
|
||||||
goto ERROR_1;
|
|
||||||
}
|
|
||||||
if ((err = gUtil->enqueue_fifo_data(cookie->iface->rfifo, bdata)) < B_OK)
|
|
||||||
goto ERROR_1;
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
ERROR_1:
|
|
||||||
if (bdata)
|
|
||||||
gUtil->delete_data(bdata);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
tun_select(cookie_t *cookie, uint8 event, uint32 ref, selectsync *sync)
|
|
||||||
{
|
|
||||||
status_t err = B_OK;
|
|
||||||
#if DEBUG > 1
|
|
||||||
dprintf("tun: select(, %d, %ld, )\n", event, ref);
|
|
||||||
#endif
|
|
||||||
if (cookie->iface == NULL)
|
|
||||||
return B_NO_INIT;
|
|
||||||
if (event > B_SELECT_EXCEPTION || !event)
|
|
||||||
return EINVAL;
|
|
||||||
err = gUtil->lock_benaphore(&cookie->iface->lock);
|
|
||||||
if (err < B_OK)
|
|
||||||
return err;
|
|
||||||
/* iface LOCKED */
|
|
||||||
if (cookie->iface->sel[event].sync)
|
|
||||||
err = EALREADY;
|
|
||||||
else {
|
|
||||||
bone_fifo_t *fifo = NULL;
|
|
||||||
switch (event) {
|
|
||||||
case B_SELECT_READ:
|
|
||||||
fifo = cookie->iface->wfifo;
|
|
||||||
break;
|
|
||||||
case B_SELECT_WRITE:
|
|
||||||
fifo = cookie->iface->rfifo;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (fifo) {
|
|
||||||
/* XXX: is it safe ??? shouldn't we dequeue(peek=true) ? */
|
|
||||||
err = gUtil->lock_benaphore(&fifo->lock);
|
|
||||||
if (err >= B_OK) {
|
|
||||||
bool avail;
|
|
||||||
switch (event) {
|
|
||||||
case B_SELECT_READ:
|
|
||||||
avail = (fifo->current_bytes > 1);
|
|
||||||
break;
|
|
||||||
case B_SELECT_WRITE:
|
|
||||||
avail = ((fifo->max_bytes - fifo->current_bytes) > cookie->iface->ifn->if_mtu);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* check if we don't already have the event */
|
|
||||||
if (avail) {
|
|
||||||
notify_select_event(sync, ref);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cookie->iface->sel[event].sync = sync;
|
|
||||||
cookie->iface->sel[event].ref = ref;
|
|
||||||
}
|
|
||||||
gUtil->unlock_benaphore(&fifo->lock);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cookie->iface->sel[event].sync = sync;
|
|
||||||
cookie->iface->sel[event].ref = ref;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gUtil->unlock_benaphore(&cookie->iface->lock);
|
|
||||||
/* iface UNLOCKED */
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
tun_deselect(cookie_t *cookie, uint8 event, selectsync *sync)
|
|
||||||
{
|
|
||||||
status_t err = B_OK;
|
|
||||||
#if DEBUG > 1
|
|
||||||
dprintf("tun: deselect(, %d, )\n", event);
|
|
||||||
#endif
|
|
||||||
if (cookie->iface == NULL)
|
|
||||||
return B_NO_INIT;
|
|
||||||
if (event > B_SELECT_EXCEPTION || !event)
|
|
||||||
return EINVAL;
|
|
||||||
err = gUtil->lock_benaphore(&cookie->iface->lock);
|
|
||||||
if (err < B_OK)
|
|
||||||
return err;
|
|
||||||
cookie->iface->sel[event].sync = NULL;
|
|
||||||
cookie->iface->sel[event].ref = 0;
|
|
||||||
gUtil->unlock_benaphore(&cookie->iface->lock);
|
|
||||||
/* iface LOCKED */
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
tun_readv(cookie_t *cookie, off_t position, const iovec *vec, size_t count, size_t *numBytes)
|
|
||||||
{
|
|
||||||
dprintf("tun: readv(, %lld, , %ld)\n", position, count);
|
|
||||||
return EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
tun_writev(cookie_t *cookie, off_t position, const iovec *vec, size_t count, size_t *numBytes)
|
|
||||||
{
|
|
||||||
dprintf("tun: writev(, %lld, , %ld)\n", position, count);
|
|
||||||
return EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
device_hooks tun_hooks = {
|
|
||||||
(device_open_hook)tun_open,
|
|
||||||
tun_close,
|
|
||||||
(device_free_hook)tun_free,
|
|
||||||
(device_control_hook)tun_ioctl,
|
|
||||||
(device_read_hook)tun_read,
|
|
||||||
(device_write_hook)tun_write,
|
|
||||||
(device_select_hook)tun_select,
|
|
||||||
(device_deselect_hook)tun_deselect,
|
|
||||||
(device_readv_hook)tun_readv,
|
|
||||||
(device_writev_hook)tun_writev
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,446 @@
|
|||||||
|
/*
|
||||||
|
* TUN/TAP Network Tunnel Driver for Haiku
|
||||||
|
* Copyright 2003 mmu_man, [email protected]
|
||||||
|
* Copyright 2023 Sean Brady, [email protected]
|
||||||
|
*
|
||||||
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#include <Drivers.h>
|
||||||
|
#include <KernelExport.h>
|
||||||
|
#include <OS.h>
|
||||||
|
#include <Select.h>
|
||||||
|
|
||||||
|
#include "BufferQueue.h"
|
||||||
|
#include <net_buffer.h>
|
||||||
|
|
||||||
|
#include <condition_variable.h>
|
||||||
|
#include <fs/select_sync_pool.h>
|
||||||
|
#include <net/if_tun.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <util/AutoLock.h>
|
||||||
|
|
||||||
|
|
||||||
|
// #define TUN_DRIVER_NAME "tun/0"
|
||||||
|
#define TAP_DRIVER_NAME "tap/0"
|
||||||
|
#define NET_TUN_MODULE_NAME "network/devices/tun/v1"
|
||||||
|
#define BUFFER_QUEUE_MAX 30000
|
||||||
|
|
||||||
|
const char* device_names[] = {TAP_DRIVER_NAME, NULL};
|
||||||
|
|
||||||
|
typedef struct tun_struct {
|
||||||
|
uint32_t name[3];
|
||||||
|
unsigned long flags;
|
||||||
|
BufferQueue* sendQueue;
|
||||||
|
BufferQueue* recvQueue;
|
||||||
|
ConditionVariable* readWait;
|
||||||
|
mutex readLock;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
mutex selectLock;
|
||||||
|
} app;
|
||||||
|
};
|
||||||
|
} tun_struct;
|
||||||
|
|
||||||
|
int32 api_version = B_CUR_DRIVER_API_VERSION;
|
||||||
|
static BufferQueue gAppQueue(BUFFER_QUEUE_MAX);
|
||||||
|
static BufferQueue gIntQueue(BUFFER_QUEUE_MAX);
|
||||||
|
static ConditionVariable gIntWait;
|
||||||
|
static select_sync_pool* gSelectPool = NULL;
|
||||||
|
struct net_buffer_module_info* gBufferModule;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a filled net_buffer based on the given data and size.
|
||||||
|
*
|
||||||
|
* @param data the pointer to the data that will be copied to the net_buffer
|
||||||
|
* @param bytes the size of the data in bytes
|
||||||
|
*
|
||||||
|
* @return a net_buffer pointer containing the copied data, or NULL if creation fails
|
||||||
|
*/
|
||||||
|
static net_buffer*
|
||||||
|
create_filled_buffer(uint8* data, size_t bytes)
|
||||||
|
{
|
||||||
|
net_buffer* buffer = gBufferModule->create(256);
|
||||||
|
if (buffer == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
status_t status = gBufferModule->append(buffer, data, bytes);
|
||||||
|
if (status != B_OK) {
|
||||||
|
gBufferModule->free(buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves a packet from the given buffer queue.
|
||||||
|
*
|
||||||
|
* @param queueToUse The buffer queue to retrieve the packet from.
|
||||||
|
* @param data A pointer to the memory location where the packet data will be copied.
|
||||||
|
* @param numbytes A pointer to the variable that will store the size of the retrieved packet.
|
||||||
|
*
|
||||||
|
* @return The status of the retrieval operation or B_OK if successful.
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
retrieve_packet(BufferQueue* queueToUse, void* data, size_t* numbytes)
|
||||||
|
{
|
||||||
|
net_buffer* buffer;
|
||||||
|
status_t status = B_OK;
|
||||||
|
if (queueToUse->Used() > 0) {
|
||||||
|
status_t status = queueToUse->Get(*numbytes, true, &buffer);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
*numbytes = buffer->size;
|
||||||
|
status = gBufferModule->read(buffer, 0, data, *numbytes);
|
||||||
|
gBufferModule->free(buffer);
|
||||||
|
} else
|
||||||
|
*numbytes = 0;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper function to notify events based on the readability and writability
|
||||||
|
* of a file descriptor.
|
||||||
|
*
|
||||||
|
* @param readable A boolean indicating if the file descriptor is readable.
|
||||||
|
* @param writable A boolean indicating if the file descriptor is writable.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @throws None
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
notify_select_helper(bool readable, bool writable)
|
||||||
|
{
|
||||||
|
if (readable) {
|
||||||
|
notify_select_event_pool(gSelectPool, B_SELECT_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (writable) {
|
||||||
|
notify_select_event_pool(gSelectPool, B_SELECT_WRITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Notifies the select event pool if the tun device is readable or writable.
|
||||||
|
*
|
||||||
|
* @param tun The tun_struct object representing the tun device.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
tun_notify(tun_struct* tun)
|
||||||
|
{
|
||||||
|
/* This function is just for the APP side but both sides need to call it */
|
||||||
|
bool select_pool_check = gSelectPool != NULL;
|
||||||
|
if (select_pool_check) {
|
||||||
|
bool readable = tun->recvQueue->Used() > 0;
|
||||||
|
bool writable = tun->sendQueue->Used() < BUFFER_QUEUE_MAX;
|
||||||
|
if ((tun->flags & O_NONBLOCK) != 0) // APP Side
|
||||||
|
notify_select_helper(readable, writable);
|
||||||
|
else /* IFACE side switches since its queues are flipped from APP*/
|
||||||
|
notify_select_helper(writable, readable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief First initialization step for the driver.
|
||||||
|
*
|
||||||
|
* @return the status of the initialization process.
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
init_hardware(void)
|
||||||
|
{
|
||||||
|
/* No Hardware */
|
||||||
|
dprintf("tun:init_hardware()\n");
|
||||||
|
return B_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes the driver and the net buffer module for packet operations.
|
||||||
|
*
|
||||||
|
* @return the status of the initialization.
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
init_driver(void)
|
||||||
|
{
|
||||||
|
/* Init driver */
|
||||||
|
dprintf("tun:init_driver()\n");
|
||||||
|
status_t status = get_module(NET_BUFFER_MODULE_NAME, (module_info**)&gBufferModule);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Uninitializes the driver.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
uninit_driver(void)
|
||||||
|
{
|
||||||
|
dprintf("tun:uninit_driver()\n");
|
||||||
|
put_module(NET_BUFFER_MODULE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the tun_struct for this session of open.
|
||||||
|
*
|
||||||
|
* @param name the name of the interface
|
||||||
|
* @param flags the flags for the interface
|
||||||
|
* @param cookie a pointer to store the cookie
|
||||||
|
*
|
||||||
|
* @return the status of the function
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
tun_open(const char* name, uint32 flags, void** cookie)
|
||||||
|
{
|
||||||
|
/* Setup driver for interface here */
|
||||||
|
tun_struct* tun = new tun_struct();
|
||||||
|
memcpy(tun->name, "app", sizeof(tun->name));
|
||||||
|
tun->sendQueue = &gIntQueue;
|
||||||
|
tun->recvQueue = &gAppQueue;
|
||||||
|
tun->flags = 0;
|
||||||
|
tun->readWait = &gIntWait;
|
||||||
|
mutex_init(&tun->readLock, "read_avail");
|
||||||
|
mutex_init(&tun->app.selectLock, "select_lock");
|
||||||
|
*cookie = static_cast<void*>(tun);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Close the open instance.
|
||||||
|
*
|
||||||
|
* @param cookie a pointer to the tun_struct object to be used
|
||||||
|
*
|
||||||
|
* @return The status of the function
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
tun_close(void* cookie)
|
||||||
|
{
|
||||||
|
/* Close interface here */
|
||||||
|
tun_struct* tun = static_cast<tun_struct*>(cookie);
|
||||||
|
if ((tun->flags & O_NONBLOCK) == 0) {
|
||||||
|
tun->readWait->NotifyAll(B_ERROR);
|
||||||
|
snooze(10); // Due to a lock timing issue, we sleep for 10ms
|
||||||
|
}
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Frees the memory allocated for a tun_struct object.
|
||||||
|
*
|
||||||
|
* @param cookie a pointer to the tun_struct object to be freed
|
||||||
|
*
|
||||||
|
* @return a status_t indicating the result of the operation
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
tun_free(void* cookie)
|
||||||
|
{
|
||||||
|
tun_struct* tun = static_cast<tun_struct*>(cookie);
|
||||||
|
mutex_destroy(&tun->readLock);
|
||||||
|
if ((tun->flags & O_NONBLOCK) != 0)
|
||||||
|
mutex_destroy(&tun->app.selectLock);
|
||||||
|
delete tun;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Updates the tun interface based on the given IOCTL operation and data.
|
||||||
|
*
|
||||||
|
* @param cookie a pointer to the tun_struct object
|
||||||
|
* @param op the IOCTL operation to perform
|
||||||
|
* @param data a pointer to the data to be used in the IOCTL operation
|
||||||
|
* @param len the size of the data in bytes
|
||||||
|
*
|
||||||
|
* @return the status of the IOCTL operation
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
tun_ioctl(void* cookie, uint32 op, void* data, size_t len)
|
||||||
|
{
|
||||||
|
/* IOCTL for driver */
|
||||||
|
tun_struct* tun = static_cast<tun_struct*>(cookie);
|
||||||
|
switch (op) {
|
||||||
|
case TUNSETIFF: // Reconfigures tun_struct to interface settings
|
||||||
|
memcpy(tun->name, "int", sizeof(tun->name));
|
||||||
|
tun->sendQueue = &gAppQueue;
|
||||||
|
tun->recvQueue = &gIntQueue;
|
||||||
|
mutex_destroy(&tun->app.selectLock);
|
||||||
|
memset(&tun->app, 0, sizeof(tun->app));
|
||||||
|
return B_OK;
|
||||||
|
case B_SET_NONBLOCKING_IO:
|
||||||
|
tun->flags |= O_NONBLOCK;
|
||||||
|
return B_OK;
|
||||||
|
default:
|
||||||
|
return B_DEV_INVALID_IOCTL;
|
||||||
|
};
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads data from the TUN/TAP device.
|
||||||
|
*
|
||||||
|
* @param cookie a pointer to the tun_struct
|
||||||
|
* @param position the position in the data stream to read from
|
||||||
|
* @param data a pointer to the buffer where the read data will be copied
|
||||||
|
* @param numbytes a pointer to the size to read, updated with the number of bytes read
|
||||||
|
*
|
||||||
|
* @return the status of the read operation
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
tun_read(void* cookie, off_t position, void* data, size_t* numbytes)
|
||||||
|
{
|
||||||
|
tun_struct* tun = static_cast<tun_struct*>(cookie);
|
||||||
|
status_t status;
|
||||||
|
MutexLocker _(tun->readLock); // released on exit
|
||||||
|
/* IFACE side is blocking I/O */
|
||||||
|
if ((tun->flags & O_NONBLOCK) == 0) {
|
||||||
|
while (tun->recvQueue->Used() == 0) {
|
||||||
|
status = tun->readWait->Wait(&tun->readLock, B_CAN_INTERRUPT);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
status = retrieve_packet(tun->recvQueue, data, numbytes);
|
||||||
|
tun_notify(tun);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Writes data to the specified sending queue.
|
||||||
|
*
|
||||||
|
* @param cookie a pointer to the tun_struct
|
||||||
|
* @param position the position in the file to write to (NOT USED)
|
||||||
|
* @param data a pointer to the data to write
|
||||||
|
* @param numbytes a pointer to the number of bytes to write
|
||||||
|
*
|
||||||
|
* @return a status code indicating the result of the write operation
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
tun_write(void* cookie, off_t position, const void* data, size_t* numbytes)
|
||||||
|
{
|
||||||
|
tun_struct* tun = static_cast<tun_struct*>(cookie);
|
||||||
|
size_t used = tun->sendQueue->Used();
|
||||||
|
// Buffer is full or will be so we have to drop the packet
|
||||||
|
if ((used + *numbytes) >= BUFFER_QUEUE_MAX)
|
||||||
|
return B_WOULD_BLOCK;
|
||||||
|
net_buffer* packet = create_filled_buffer((uint8*)data, *numbytes);
|
||||||
|
if (packet == NULL)
|
||||||
|
return B_ERROR;
|
||||||
|
tun->sendQueue->Add(packet);
|
||||||
|
if ((tun->flags & O_NONBLOCK) != 0)
|
||||||
|
tun->readWait->NotifyOne();
|
||||||
|
tun_notify(tun);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handles the selection of read and write events on a TUN/TAP device.
|
||||||
|
*
|
||||||
|
* @param cookie a pointer to the tun_struct object
|
||||||
|
* @param event the event type (B_SELECT_READ or B_SELECT_WRITE are accepted)
|
||||||
|
* @param ref the reference number
|
||||||
|
* @param sync a pointer to the selectsync object
|
||||||
|
*
|
||||||
|
* @return the status of the select operation
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
tun_select(void* cookie, uint8 event, uint32 ref, selectsync* sync)
|
||||||
|
{
|
||||||
|
tun_struct* tun = static_cast<tun_struct*>(cookie);
|
||||||
|
MutexLocker _(tun->app.selectLock);
|
||||||
|
bool isReadable = tun->recvQueue->Used() > 0;
|
||||||
|
bool isWritable = tun->sendQueue->Used() < BUFFER_QUEUE_MAX;
|
||||||
|
|
||||||
|
if (event != B_SELECT_READ && event != B_SELECT_WRITE)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
status_t status = add_select_sync_pool_entry(&gSelectPool, sync, event);
|
||||||
|
if (status != B_OK)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
if (event == B_SELECT_READ && isReadable)
|
||||||
|
notify_select_event(sync, event);
|
||||||
|
|
||||||
|
if (event == B_SELECT_WRITE && isWritable)
|
||||||
|
notify_select_event(sync, event);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Deselects a event in the select pool.
|
||||||
|
*
|
||||||
|
* @param cookie a pointer to the tun_struct object representing the tun device
|
||||||
|
* @param event the event type to be deselected (B_SELECT_READ or B_SELECT_WRITE)
|
||||||
|
* @param sync a pointer to the selectsync object to be removed from the select pool
|
||||||
|
*
|
||||||
|
* @return the status code indicating the result of the deselect operation
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
tun_deselect(void* cookie, uint8 event, selectsync* sync)
|
||||||
|
{
|
||||||
|
tun_struct* tun = static_cast<tun_struct*>(cookie);
|
||||||
|
MutexLocker _(tun->app.selectLock);
|
||||||
|
if (event != B_SELECT_READ && event != B_SELECT_WRITE)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
return remove_select_sync_pool_entry(&gSelectPool, sync, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the driver names to devfs.
|
||||||
|
*
|
||||||
|
* @return A pointer to a constant character pointer representing the device names.
|
||||||
|
*/
|
||||||
|
const char**
|
||||||
|
publish_devices()
|
||||||
|
{
|
||||||
|
return device_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
device_hooks tun_hooks = {
|
||||||
|
tun_open,
|
||||||
|
tun_close,
|
||||||
|
tun_free,
|
||||||
|
tun_ioctl,
|
||||||
|
tun_read,
|
||||||
|
tun_write,
|
||||||
|
tun_select,
|
||||||
|
tun_deselect,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @brief Finds hooks for driver functions.
|
||||||
|
*
|
||||||
|
* @param name the name of the device.
|
||||||
|
*
|
||||||
|
* @return a pointer to the device hooks (all devices return the same hooks)
|
||||||
|
*/
|
||||||
|
device_hooks*
|
||||||
|
find_device(const char* name)
|
||||||
|
{
|
||||||
|
return &tun_hooks;
|
||||||
|
}
|
||||||
@@ -1,126 +1,394 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2006-2007, Haiku, Inc. All Rights Reserved.
|
* Copyright 2023 Haiku, Inc. All rights reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
* Axel Dörfler, [email protected]
|
* Axel Dörfler, [email protected]
|
||||||
|
* Sean Brady, [email protected]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <net_tun.h>
|
#include <ethernet.h>
|
||||||
|
|
||||||
#include <net_buffer.h>
|
#include <net_buffer.h>
|
||||||
|
#include <net_datalink.h>
|
||||||
#include <net_device.h>
|
#include <net_device.h>
|
||||||
#include <net_stack.h>
|
#include <net_stack.h>
|
||||||
|
#include <net_tun.h>
|
||||||
|
|
||||||
|
#include <ByteOrder.h>
|
||||||
|
#include <Drivers.h>
|
||||||
|
#include <lock.h>
|
||||||
|
#include <util/AutoLock.h>
|
||||||
|
#include <util/DoublyLinkedList.h>
|
||||||
#include <KernelExport.h>
|
#include <KernelExport.h>
|
||||||
|
#include <NetBufferUtilities.h>
|
||||||
|
|
||||||
|
#include <debug.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <net/if_types.h>
|
|
||||||
#include <net/if_media.h>
|
#include <net/if_media.h>
|
||||||
|
#include <net/if_tun.h>
|
||||||
|
#include <net/if_types.h>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <util/Random.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct tun_device : net_device, DoublyLinkedListLinkImpl<tun_device>
|
||||||
|
{
|
||||||
|
~tun_device()
|
||||||
|
{
|
||||||
|
free(read_buffer);
|
||||||
|
free(write_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
uint32 frame_size;
|
||||||
|
|
||||||
|
void *read_buffer, *write_buffer;
|
||||||
|
mutex read_buffer_lock, write_buffer_lock;
|
||||||
|
};
|
||||||
|
|
||||||
struct net_buffer_module_info* gBufferModule;
|
struct net_buffer_module_info* gBufferModule;
|
||||||
static struct net_stack_module_info* sStackModule;
|
static struct net_stack_module_info* sStackModule;
|
||||||
|
|
||||||
//static mutex sListLock;
|
|
||||||
//static DoublyLinkedList<ethernet_device> sCheckList;
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prepends an ethernet frame to the given net buffer packet.
|
||||||
|
*
|
||||||
|
* @param buffer pointer to the net buffer to prepend the ethernet frame to
|
||||||
|
*
|
||||||
|
* @return the status of the operation
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
prepend_ethernet_frame(net_buffer* buffer)
|
||||||
|
{
|
||||||
|
NetBufferPrepend<ether_header> bufferHeader(buffer);
|
||||||
|
if (bufferHeader.Status() != B_OK)
|
||||||
|
return bufferHeader.Status();
|
||||||
|
ether_header &header = bufferHeader.Data();
|
||||||
|
header.type = B_HOST_TO_BENDIAN_INT16(ETHER_TYPE_IP);
|
||||||
|
memset(header.source, 0, ETHER_ADDRESS_LENGTH);
|
||||||
|
memset(header.destination, 0, ETHER_ADDRESS_LENGTH);
|
||||||
|
bufferHeader.Sync();
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Removes the ethernet header from the given network buffer.
|
||||||
|
*
|
||||||
|
* @param buffer Pointer to the network buffer.
|
||||||
|
*
|
||||||
|
* @return The status of the operation.
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
ethernet_header_deframe(net_buffer* buffer)
|
||||||
|
{
|
||||||
|
NetBufferHeaderRemover<ether_header> bufferHeader(buffer);
|
||||||
|
if (bufferHeader.Status() != B_OK)
|
||||||
|
return bufferHeader.Status();
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes a TUN/TAP interface with the given name.
|
||||||
|
*
|
||||||
|
* @param name The name of the TUN/TAP interface.
|
||||||
|
* @param _device A pointer to a net_device pointer for the newly created interface.
|
||||||
|
*
|
||||||
|
* @return The status of the initialization. Returns B_OK if successful, or an error code otherwise.
|
||||||
|
*/
|
||||||
status_t
|
status_t
|
||||||
tun_init(const char* name, net_device** _device)
|
tun_init(const char* name, net_device** _device)
|
||||||
{
|
{
|
||||||
tun_device* device;
|
|
||||||
|
|
||||||
if (strncmp(name, "tun", 3)
|
if (strncmp(name, "tun", 3)
|
||||||
&& strncmp(name, "tap", 3)
|
&& strncmp(name, "tap", 3)
|
||||||
&& strncmp(name, "dns", 3)) /* iodine uses that */
|
&& strncmp(name, "dns", 3)) /* iodine uses that */
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
device = new (std::nothrow) tun_device;
|
tun_device* device = new (std::nothrow) tun_device;
|
||||||
if (device == NULL) {
|
if (device == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
}
|
|
||||||
|
|
||||||
memset(device, 0, sizeof(tun_device));
|
memset(device, 0, sizeof(tun_device));
|
||||||
|
|
||||||
strcpy(device->name, name);
|
strcpy(device->name, name);
|
||||||
|
|
||||||
if (strncmp(name, "tun", 3) == 0) {
|
if (strncmp(name, "tun", 3) == 0) {
|
||||||
device->flags = IFF_LOOPBACK | IFF_LINK;
|
device->flags = IFF_POINTOPOINT | IFF_LINK;
|
||||||
device->type = IFT_TUN;
|
device->type = IFT_ETHER;
|
||||||
} else if (strncmp(name, "tap", 3) == 0) {
|
} else if (strncmp(name, "tap", 3) == 0) {
|
||||||
device->flags = IFF_BROADCAST | IFF_ALLMULTI | IFF_LINK;
|
device->flags = IFF_BROADCAST | IFF_ALLMULTI | IFF_LINK;
|
||||||
device->type = IFT_ETHER;
|
device->type = IFT_ETHER;
|
||||||
} else {
|
/* Set the first two bits to prevent it from becoming a multicast address */
|
||||||
|
device->address.data[0] = 0x00;
|
||||||
|
device->address.data[1] = 0xFF;
|
||||||
|
for (int i = 2; i < ETHER_ADDRESS_LENGTH; i++) {
|
||||||
|
int val = random_value();
|
||||||
|
device->address.data[i] = val * 0x11;
|
||||||
|
}
|
||||||
|
device->address.length = ETHER_ADDRESS_LENGTH;
|
||||||
|
} else
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
}
|
|
||||||
|
|
||||||
device->mtu = 1500; /* Almost all VPN MTU's are no more than 1500 bytes */
|
device->mtu = 1500;
|
||||||
device->media = IFM_ACTIVE;
|
device->media = IFM_ACTIVE | IFM_ETHER;
|
||||||
|
device->frame_size = ETHER_MAX_FRAME_SIZE;
|
||||||
|
device->header_length = ETHER_HEADER_LENGTH;
|
||||||
|
device->fd = -1;
|
||||||
|
mutex_init(&device->read_buffer_lock, "tun read_buffer");
|
||||||
|
mutex_init(&device->write_buffer_lock, "tun write_buffer");
|
||||||
|
|
||||||
*_device = device;
|
*_device = device;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Uninitializes a TUN/TAP interface.
|
||||||
|
*
|
||||||
|
* @param _device the net_device to uninitialize
|
||||||
|
*
|
||||||
|
* @return the status of the uninitialization process
|
||||||
|
*/
|
||||||
status_t
|
status_t
|
||||||
tun_uninit(net_device* _device)
|
tun_uninit(net_device* _device)
|
||||||
{
|
{
|
||||||
tun_device* device = (tun_device*)_device;
|
tun_device* device = (tun_device*)_device;
|
||||||
|
close(device->fd);
|
||||||
put_module(NET_STACK_MODULE_NAME);
|
device->fd = -1;
|
||||||
put_module(NET_BUFFER_MODULE_NAME);
|
mutex_destroy(&device->read_buffer_lock);
|
||||||
|
mutex_destroy(&device->write_buffer_lock);
|
||||||
delete device;
|
delete device;
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets up the TUN/TAP network interface to be used.
|
||||||
|
*
|
||||||
|
* @param _device the network device to set up
|
||||||
|
*
|
||||||
|
* @return the status of the setup process
|
||||||
|
*/
|
||||||
status_t
|
status_t
|
||||||
tun_up(net_device* device)
|
tun_up(net_device* _device)
|
||||||
{
|
{
|
||||||
|
tun_device* device = (tun_device*)_device;
|
||||||
|
device->fd = open("/dev/tap/0", O_RDWR);
|
||||||
|
if (device->fd < 0)
|
||||||
|
return errno;
|
||||||
|
ioctl(device->fd, TUNSETIFF);
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
/**
|
||||||
tun_down(net_device* device)
|
* @brief Closes the TUN/TAP network interface to not be used.
|
||||||
|
*
|
||||||
|
* @param _device A pointer to the net_device structure representing the tun_device.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
void tun_down(net_device* _device)
|
||||||
{
|
{
|
||||||
|
tun_device* device = (tun_device*)_device;
|
||||||
|
close(device->fd);
|
||||||
|
device->fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
tun_control(net_device* device, int32 op, void* argument,
|
tun_control(net_device* device, int32 op, void* argument,
|
||||||
size_t length)
|
size_t length)
|
||||||
{
|
{
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Writes data to the TUN/TAP network driver.
|
||||||
|
*
|
||||||
|
* @param _device the network device to get data from network stack
|
||||||
|
* @param buffer the buffer containing the data to write
|
||||||
|
*
|
||||||
|
* @return the status of the operation
|
||||||
|
*
|
||||||
|
* @throws ErrorType if an error occurs during the operation
|
||||||
|
*/
|
||||||
status_t
|
status_t
|
||||||
tun_send_data(net_device* device, net_buffer* buffer)
|
tun_send_data(net_device* _device, net_buffer* buffer)
|
||||||
{
|
{
|
||||||
return sStackModule->device_enqueue_buffer(device, buffer);
|
tun_device* device = (tun_device*)_device;
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
if (strncmp(device->name, "tun", 3) == 0) {
|
||||||
|
status = ethernet_header_deframe(buffer);
|
||||||
|
if (status != B_OK)
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer->size > device->mtu)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
net_buffer* allocated = NULL;
|
||||||
|
net_buffer* original = buffer;
|
||||||
|
|
||||||
|
MutexLocker bufferLocker;
|
||||||
|
struct iovec iovec;
|
||||||
|
if (gBufferModule->count_iovecs(buffer) > 1) {
|
||||||
|
if (device->write_buffer != NULL) {
|
||||||
|
bufferLocker.SetTo(device->write_buffer_lock, false);
|
||||||
|
status_t status = gBufferModule->read(buffer, 0,
|
||||||
|
device->write_buffer, buffer->size);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
iovec.iov_base = device->write_buffer;
|
||||||
|
iovec.iov_len = buffer->size;
|
||||||
|
} else {
|
||||||
|
// Fall back to creating a new buffer.
|
||||||
|
allocated = gBufferModule->duplicate(original);
|
||||||
|
if (allocated == NULL)
|
||||||
|
return ENOBUFS;
|
||||||
|
|
||||||
|
buffer = allocated;
|
||||||
|
|
||||||
|
if (gBufferModule->count_iovecs(allocated) > 1) {
|
||||||
|
dprintf("tun_send_data: no write buffer, cannot perform scatter I/O\n");
|
||||||
|
gBufferModule->free(allocated);
|
||||||
|
device->stats.send.errors++;
|
||||||
|
return B_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
gBufferModule->get_iovecs(buffer, &iovec, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gBufferModule->get_iovecs(buffer, &iovec, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t bytesWritten = write(device->fd, iovec.iov_base, iovec.iov_len);
|
||||||
|
if (bytesWritten < 0) {
|
||||||
|
device->stats.send.errors++;
|
||||||
|
if (allocated)
|
||||||
|
gBufferModule->free(allocated);
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->stats.send.packets++;
|
||||||
|
device->stats.send.bytes += bytesWritten;
|
||||||
|
|
||||||
|
gBufferModule->free(original);
|
||||||
|
if (allocated)
|
||||||
|
gBufferModule->free(allocated);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads data from the TUN/TAP network driver.
|
||||||
|
*
|
||||||
|
* @param _device the network device to put the data into the network stack
|
||||||
|
* @param buffer the packet to be filled and sent to the network stack
|
||||||
|
*
|
||||||
|
* @return the status of the operation
|
||||||
|
*
|
||||||
|
* @throws ErrorType if an error occurs during the operation
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
tun_receive_data(net_device* _device, net_buffer** _buffer)
|
||||||
|
{
|
||||||
|
/* Recieve Data */
|
||||||
|
tun_device* device = (tun_device*)_device;
|
||||||
|
if (device->fd == -1)
|
||||||
|
return B_FILE_ERROR;
|
||||||
|
|
||||||
|
// TODO: better header space
|
||||||
|
net_buffer* buffer = gBufferModule->create(256);
|
||||||
|
if (buffer == NULL)
|
||||||
|
return ENOBUFS;
|
||||||
|
|
||||||
|
MutexLocker bufferLocker;
|
||||||
|
struct iovec iovec;
|
||||||
|
size_t bytesRead;
|
||||||
|
status_t status;
|
||||||
|
if (device->read_buffer != NULL) {
|
||||||
|
bufferLocker.SetTo(device->read_buffer_lock, false);
|
||||||
|
|
||||||
|
iovec.iov_base = device->read_buffer;
|
||||||
|
iovec.iov_len = device->frame_size;
|
||||||
|
} else {
|
||||||
|
void* data;
|
||||||
|
status = gBufferModule->append_size(buffer, device->mtu, &data);
|
||||||
|
if (status == B_OK && data == NULL) {
|
||||||
|
dprintf("tun_receive_data: no read buffer, cannot perform scattered I/O!\n");
|
||||||
|
status = B_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
if (status < B_OK) {
|
||||||
|
gBufferModule->free(buffer);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
iovec.iov_base = data;
|
||||||
|
iovec.iov_len = device->frame_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesRead = read(device->fd, iovec.iov_base, iovec.iov_len);
|
||||||
|
if (bytesRead < 0 || iovec.iov_base == NULL) {
|
||||||
|
device->stats.receive.errors++;
|
||||||
|
status = errno;
|
||||||
|
gBufferModule->free(buffer);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(device->name, "tun", 3) == 0) {
|
||||||
|
status = prepend_ethernet_frame(buffer);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iovec.iov_base == device->read_buffer)
|
||||||
|
status = gBufferModule->append(buffer, iovec.iov_base, buffer->size);
|
||||||
|
else
|
||||||
|
status = gBufferModule->trim(buffer, buffer->size);
|
||||||
|
|
||||||
|
if (status < B_OK) {
|
||||||
|
device->stats.receive.dropped++;
|
||||||
|
gBufferModule->free(buffer);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->stats.receive.bytes += bytesRead;
|
||||||
|
device->stats.receive.packets++;
|
||||||
|
|
||||||
|
*_buffer = buffer;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the maximum transmission unit (MTU) for a network interface.
|
||||||
|
*
|
||||||
|
* @param device a pointer to the network interface
|
||||||
|
* @param mtu the desired MTU value
|
||||||
|
*
|
||||||
|
* @return the status of the operation (B_OK if successful, B_BAD_VALUE if the
|
||||||
|
* provided MTU is out of range)
|
||||||
|
*/
|
||||||
status_t
|
status_t
|
||||||
tun_set_mtu(net_device* device, size_t mtu)
|
tun_set_mtu(net_device* device, size_t mtu)
|
||||||
{
|
{
|
||||||
if (mtu > 65536 || mtu < 16)
|
if (mtu > 65536 || mtu < 16)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
device->mtu = mtu;
|
device->mtu = mtu;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
tun_set_promiscuous(net_device* device, bool promiscuous)
|
tun_set_promiscuous(net_device* device, bool promiscuous)
|
||||||
{
|
{
|
||||||
@@ -138,6 +406,7 @@ tun_set_media(net_device* device, uint32 media)
|
|||||||
status_t
|
status_t
|
||||||
tun_add_multicast(net_device* device, const sockaddr* address)
|
tun_add_multicast(net_device* device, const sockaddr* address)
|
||||||
{
|
{
|
||||||
|
/* Nothing to do for multicast filters as we always accept all frames. */
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,18 +418,24 @@ tun_remove_multicast(net_device* device, const sockaddr* address)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Performs various operations on the TUN/TAP network interface.
|
||||||
|
*
|
||||||
|
* @param op The operation to be performed.
|
||||||
|
* @param ... Additional arguments based on the operation.
|
||||||
|
*
|
||||||
|
* @return The status of the operation.
|
||||||
|
*/
|
||||||
static status_t
|
static status_t
|
||||||
tun_std_ops(int32 op, ...)
|
tun_std_ops(int32 op, ...)
|
||||||
{
|
{
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case B_MODULE_INIT:
|
case B_MODULE_INIT:
|
||||||
{
|
{
|
||||||
status_t status = get_module(NET_STACK_MODULE_NAME,
|
status_t status = get_module(NET_STACK_MODULE_NAME, (module_info**)&sStackModule);
|
||||||
(module_info**)&sStackModule);
|
|
||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
return status;
|
return status;
|
||||||
status = get_module(NET_BUFFER_MODULE_NAME,
|
status = get_module(NET_BUFFER_MODULE_NAME, (module_info**)&gBufferModule);
|
||||||
(module_info**)&gBufferModule);
|
|
||||||
if (status < B_OK) {
|
if (status < B_OK) {
|
||||||
put_module(NET_STACK_MODULE_NAME);
|
put_module(NET_STACK_MODULE_NAME);
|
||||||
return status;
|
return status;
|
||||||
@@ -189,16 +464,15 @@ net_device_module_info sTunModule = {
|
|||||||
tun_down,
|
tun_down,
|
||||||
tun_control,
|
tun_control,
|
||||||
tun_send_data,
|
tun_send_data,
|
||||||
NULL, // receive_data
|
tun_receive_data,
|
||||||
tun_set_mtu,
|
tun_set_mtu,
|
||||||
tun_set_promiscuous,
|
tun_set_promiscuous,
|
||||||
tun_set_media,
|
tun_set_media,
|
||||||
tun_add_multicast,
|
tun_add_multicast,
|
||||||
tun_remove_multicast,
|
tun_remove_multicast,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
module_info* modules[] = {
|
module_info* modules[] = {
|
||||||
(module_info*)&sTunModule,
|
(module_info*)&sTunModule,
|
||||||
NULL
|
NULL};
|
||||||
};
|
|
||||||
|
|||||||
@@ -831,6 +831,8 @@ init_stack()
|
|||||||
scan_modules("network/datalink_protocols");
|
scan_modules("network/datalink_protocols");
|
||||||
|
|
||||||
// TODO: for now!
|
// TODO: for now!
|
||||||
|
register_domain_datalink_protocols(AF_INET, IFT_TUN,
|
||||||
|
"network/datalink_protocols/ethernet_frame/v1", NULL);
|
||||||
register_domain_datalink_protocols(AF_INET, IFT_LOOP,
|
register_domain_datalink_protocols(AF_INET, IFT_LOOP,
|
||||||
"network/datalink_protocols/loopback_frame/v1", NULL);
|
"network/datalink_protocols/loopback_frame/v1", NULL);
|
||||||
#if 0 // PPP is not (currently) included in the build
|
#if 0 // PPP is not (currently) included in the build
|
||||||
|
|||||||
Reference in New Issue
Block a user