2015-05-19 09:50:29 +02:00
|
|
|
/*
|
2015-09-27 18:58:30 +02:00
|
|
|
* Copyright (C) 2015 Ludwig Knüpfer <ludwig.knuepfer@fu-berlin.de>,
|
2015-05-19 09:50:29 +02:00
|
|
|
* Martine Lenders <mlenders@inf.fu-berlin.de>
|
|
|
|
* Kaspar Schleiser <kaspar@schleiser.de>
|
|
|
|
* Ell-i open source co-operative
|
|
|
|
*
|
|
|
|
* This file is subject to the terms and conditions of the GNU Lesser General
|
|
|
|
* Public License v2.1. See the file LICENSE in the top level directory for
|
|
|
|
* more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2015-08-21 16:08:43 +02:00
|
|
|
* @ingroup netdev2
|
2015-05-19 09:50:29 +02:00
|
|
|
* @{
|
|
|
|
* @brief Low-level ethernet driver for tap interfaces
|
|
|
|
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
|
|
|
* @}
|
|
|
|
*/
|
2015-08-21 16:08:43 +02:00
|
|
|
#include <assert.h>
|
2015-05-19 09:50:29 +02:00
|
|
|
#include <err.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#ifdef __MACH__
|
|
|
|
#define _POSIX_C_SOURCE
|
|
|
|
#include <net/if.h>
|
|
|
|
#undef _POSIX_C_SOURCE
|
2015-07-01 16:00:31 +02:00
|
|
|
#include <sys/types.h>
|
2015-05-19 09:50:29 +02:00
|
|
|
#include <ifaddrs.h>
|
|
|
|
#include <net/if_dl.h>
|
|
|
|
#elif defined(__FreeBSD__)
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <ifaddrs.h>
|
|
|
|
#include <net/if_dl.h>
|
|
|
|
#else
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <linux/if_tun.h>
|
|
|
|
#include <linux/if_ether.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "native_internal.h"
|
|
|
|
|
2015-12-09 09:29:47 +01:00
|
|
|
#include "async_read.h"
|
|
|
|
|
2015-08-21 16:08:43 +02:00
|
|
|
#include "net/eui64.h"
|
|
|
|
#include "net/netdev2.h"
|
2016-03-25 15:10:07 +01:00
|
|
|
#include "net/netdev2/eth.h"
|
2015-08-21 16:08:43 +02:00
|
|
|
#include "net/ethernet.h"
|
|
|
|
#include "net/ethernet/hdr.h"
|
|
|
|
#include "netdev2_tap.h"
|
|
|
|
#include "net/netopt.h"
|
|
|
|
#include "net/eui64.h"
|
2015-05-19 09:50:29 +02:00
|
|
|
|
2015-08-21 16:08:43 +02:00
|
|
|
#define ENABLE_DEBUG (0)
|
2015-05-19 09:50:29 +02:00
|
|
|
#include "debug.h"
|
|
|
|
|
|
|
|
/* support one tap interface for now */
|
2015-08-21 16:08:43 +02:00
|
|
|
netdev2_tap_t netdev2_tap;
|
2015-05-19 09:50:29 +02:00
|
|
|
|
2015-08-21 16:08:43 +02:00
|
|
|
/* netdev2 interface */
|
|
|
|
static int _init(netdev2_t *netdev);
|
2016-08-02 16:56:15 +02:00
|
|
|
static int _send(netdev2_t *netdev, const struct iovec *vector, unsigned n);
|
|
|
|
static int _recv(netdev2_t *netdev, void *buf, size_t n, void *info);
|
2015-05-19 09:50:29 +02:00
|
|
|
|
2015-08-21 16:08:43 +02:00
|
|
|
static inline void _get_mac_addr(netdev2_t *netdev, uint8_t *dst)
|
|
|
|
{
|
|
|
|
netdev2_tap_t *dev = (netdev2_tap_t*)netdev;
|
2015-08-07 07:39:02 +02:00
|
|
|
memcpy(dst, dev->addr, ETHERNET_ADDR_LEN);
|
2015-05-19 09:50:29 +02:00
|
|
|
}
|
|
|
|
|
2015-08-21 16:08:43 +02:00
|
|
|
static inline void _set_mac_addr(netdev2_t *netdev, uint8_t *src)
|
|
|
|
{
|
|
|
|
netdev2_tap_t *dev = (netdev2_tap_t*)netdev;
|
|
|
|
memcpy(dev->addr, src, ETHERNET_ADDR_LEN);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int _get_promiscous(netdev2_t *netdev)
|
|
|
|
{
|
|
|
|
netdev2_tap_t *dev = (netdev2_tap_t*)netdev;
|
2015-05-19 09:50:29 +02:00
|
|
|
return dev->promiscous;
|
|
|
|
}
|
|
|
|
|
2015-08-21 16:08:43 +02:00
|
|
|
static inline int _set_promiscous(netdev2_t *netdev, int value)
|
|
|
|
{
|
|
|
|
netdev2_tap_t *dev = (netdev2_tap_t*)netdev;
|
2015-05-19 09:50:29 +02:00
|
|
|
dev->promiscous = value;
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2015-08-21 16:08:43 +02:00
|
|
|
static inline void _isr(netdev2_t *netdev)
|
|
|
|
{
|
|
|
|
if (netdev->event_callback) {
|
2016-02-21 23:56:19 +01:00
|
|
|
netdev->event_callback(netdev, NETDEV2_EVENT_RX_COMPLETE);
|
2015-08-21 16:08:43 +02:00
|
|
|
}
|
|
|
|
#if DEVELHELP
|
|
|
|
else {
|
|
|
|
puts("netdev2_tap: _isr(): no event_callback set.");
|
|
|
|
}
|
|
|
|
#endif
|
2015-05-19 09:50:29 +02:00
|
|
|
}
|
|
|
|
|
2016-04-04 15:03:31 +02:00
|
|
|
static int _get(netdev2_t *dev, netopt_t opt, void *value, size_t max_len)
|
2015-08-21 16:08:43 +02:00
|
|
|
{
|
|
|
|
if (dev != (netdev2_t *)&netdev2_tap) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
switch (opt) {
|
|
|
|
case NETOPT_ADDRESS:
|
|
|
|
if (max_len < ETHERNET_ADDR_LEN) {
|
|
|
|
res = -EINVAL;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
_get_mac_addr(dev, (uint8_t*)value);
|
|
|
|
res = ETHERNET_ADDR_LEN;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NETOPT_PROMISCUOUSMODE:
|
|
|
|
*((bool*)value) = (bool)_get_promiscous(dev);
|
|
|
|
res = sizeof(bool);
|
|
|
|
break;
|
|
|
|
default:
|
2015-11-30 14:01:48 +01:00
|
|
|
res = netdev2_eth_get(dev, opt, value, max_len);
|
2015-08-21 16:08:43 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2016-04-04 15:03:31 +02:00
|
|
|
static int _set(netdev2_t *dev, netopt_t opt, void *value, size_t value_len)
|
2015-08-21 16:08:43 +02:00
|
|
|
{
|
|
|
|
(void)value_len;
|
|
|
|
|
|
|
|
if (dev != (netdev2_t *)&netdev2_tap) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
switch (opt) {
|
|
|
|
case NETOPT_ADDRESS:
|
|
|
|
assert(value_len==ETHERNET_ADDR_LEN);
|
|
|
|
_set_mac_addr(dev, (uint8_t*)value);
|
|
|
|
break;
|
|
|
|
case NETOPT_PROMISCUOUSMODE:
|
|
|
|
_set_promiscous(dev, ((bool *)value)[0]);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static netdev2_driver_t netdev2_driver_tap = {
|
2015-05-19 09:50:29 +02:00
|
|
|
.send = _send,
|
|
|
|
.recv = _recv,
|
2015-08-21 16:08:43 +02:00
|
|
|
.init = _init,
|
2015-05-19 09:50:29 +02:00
|
|
|
.isr = _isr,
|
2015-08-21 16:08:43 +02:00
|
|
|
.get = _get,
|
|
|
|
.set = _set,
|
2015-05-19 09:50:29 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/* driver implementation */
|
|
|
|
static inline bool _is_addr_broadcast(uint8_t *addr)
|
|
|
|
{
|
|
|
|
return ((addr[0] == 0xff) && (addr[1] == 0xff) && (addr[2] == 0xff) &&
|
|
|
|
(addr[3] == 0xff) && (addr[4] == 0xff) && (addr[5] == 0xff));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool _is_addr_multicast(uint8_t *addr)
|
|
|
|
{
|
|
|
|
/* source: http://ieee802.org/secmail/pdfocSP2xXA6d.pdf */
|
|
|
|
return (addr[0] & 0x01);
|
|
|
|
}
|
|
|
|
|
2015-12-24 06:24:57 +01:00
|
|
|
static void _continue_reading(netdev2_tap_t *dev)
|
|
|
|
{
|
|
|
|
/* work around lost signals */
|
|
|
|
fd_set rfds;
|
|
|
|
struct timeval t;
|
|
|
|
memset(&t, 0, sizeof(t));
|
|
|
|
FD_ZERO(&rfds);
|
|
|
|
FD_SET(dev->tap_fd, &rfds);
|
|
|
|
|
|
|
|
_native_in_syscall++; /* no switching here */
|
|
|
|
|
|
|
|
if (real_select(dev->tap_fd + 1, &rfds, NULL, NULL, &t) == 1) {
|
|
|
|
int sig = SIGIO;
|
|
|
|
extern int _sig_pipefd[2];
|
|
|
|
extern ssize_t (*real_write)(int fd, const void * buf, size_t count);
|
|
|
|
real_write(_sig_pipefd[1], &sig, sizeof(int));
|
|
|
|
_native_sigpend++;
|
|
|
|
DEBUG("netdev2_tap: sigpend++\n");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
DEBUG("netdev2_tap: native_async_read_continue\n");
|
|
|
|
native_async_read_continue(dev->tap_fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
_native_in_syscall--;
|
|
|
|
}
|
|
|
|
|
2016-08-02 16:56:15 +02:00
|
|
|
static int _recv(netdev2_t *netdev2, void *buf, size_t len, void *info)
|
2015-08-21 16:08:43 +02:00
|
|
|
{
|
|
|
|
netdev2_tap_t *dev = (netdev2_tap_t*)netdev2;
|
2016-01-29 15:48:10 +01:00
|
|
|
(void)info;
|
2015-08-21 16:08:43 +02:00
|
|
|
|
|
|
|
if (!buf) {
|
2015-12-24 06:24:57 +01:00
|
|
|
if (len > 0) {
|
|
|
|
/* no memory available in pktbuf, discarding the frame */
|
|
|
|
DEBUG("netdev2_tap: discarding the frame\n");
|
|
|
|
|
|
|
|
/* repeating `real_read` for small size on tap device results in
|
|
|
|
* freeze for some reason. Using a large buffer for now. */
|
|
|
|
/*
|
|
|
|
uint8_t buf[4];
|
|
|
|
while (real_read(dev->tap_fd, buf, sizeof(buf)) > 0) {
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
static uint8_t buf[ETHERNET_FRAME_LEN];
|
|
|
|
|
|
|
|
real_read(dev->tap_fd, buf, sizeof(buf));
|
|
|
|
|
|
|
|
_continue_reading(dev);
|
|
|
|
}
|
|
|
|
|
2015-08-21 16:08:43 +02:00
|
|
|
/* no way of figuring out packet size without racey buffering,
|
|
|
|
* so we return the maximum possible size */
|
2015-09-11 21:14:21 +02:00
|
|
|
return ETHERNET_FRAME_LEN;
|
2015-08-21 16:08:43 +02:00
|
|
|
}
|
2015-05-19 09:50:29 +02:00
|
|
|
|
|
|
|
int nread = real_read(dev->tap_fd, buf, len);
|
2015-08-21 16:08:43 +02:00
|
|
|
DEBUG("netdev2_tap: read %d bytes\n", nread);
|
2015-05-19 09:50:29 +02:00
|
|
|
|
|
|
|
if (nread > 0) {
|
2015-08-07 07:39:02 +02:00
|
|
|
ethernet_hdr_t *hdr = (ethernet_hdr_t *)buf;
|
2015-05-19 09:50:29 +02:00
|
|
|
if (!(dev->promiscous) && !_is_addr_multicast(hdr->dst) &&
|
|
|
|
!_is_addr_broadcast(hdr->dst) &&
|
2015-08-07 07:39:02 +02:00
|
|
|
(memcmp(hdr->dst, dev->addr, ETHERNET_ADDR_LEN) != 0)) {
|
2015-08-21 16:08:43 +02:00
|
|
|
DEBUG("netdev2_tap: received for %02x:%02x:%02x:%02x:%02x:%02x\n"
|
2015-05-19 09:50:29 +02:00
|
|
|
"That's not me => Dropped\n",
|
|
|
|
hdr->dst[0], hdr->dst[1], hdr->dst[2],
|
|
|
|
hdr->dst[3], hdr->dst[4], hdr->dst[5]);
|
2015-12-09 09:29:47 +01:00
|
|
|
|
|
|
|
native_async_read_continue(dev->tap_fd);
|
|
|
|
|
2015-05-19 09:50:29 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-12-24 06:24:57 +01:00
|
|
|
_continue_reading(dev);
|
2015-05-19 09:50:29 +02:00
|
|
|
|
2016-02-11 23:06:07 +01:00
|
|
|
#ifdef MODULE_NETSTATS_L2
|
|
|
|
netdev2->stats.rx_count++;
|
|
|
|
netdev2->stats.rx_bytes += nread;
|
|
|
|
#endif
|
2015-05-19 09:50:29 +02:00
|
|
|
return nread;
|
|
|
|
}
|
|
|
|
else if (nread == -1) {
|
|
|
|
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
|
|
|
|
}
|
|
|
|
else {
|
2015-08-21 16:08:43 +02:00
|
|
|
err(EXIT_FAILURE, "netdev2_tap: read");
|
2015-05-19 09:50:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (nread == 0) {
|
|
|
|
DEBUG("_native_handle_tap_input: ignoring null-event");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
errx(EXIT_FAILURE, "internal error _rx_event");
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-08-02 16:56:15 +02:00
|
|
|
static int _send(netdev2_t *netdev, const struct iovec *vector, unsigned n)
|
2015-08-21 16:08:43 +02:00
|
|
|
{
|
|
|
|
netdev2_tap_t *dev = (netdev2_tap_t*)netdev;
|
2016-02-11 23:06:07 +01:00
|
|
|
int res = _native_writev(dev->tap_fd, vector, n);
|
|
|
|
#ifdef MODULE_NETSTATS_L2
|
|
|
|
size_t bytes = 0;
|
2016-08-02 16:56:15 +02:00
|
|
|
for (unsigned i = 0; i < n; i++) {
|
2016-02-11 23:06:07 +01:00
|
|
|
bytes += vector->iov_len;
|
|
|
|
vector++;
|
|
|
|
}
|
|
|
|
netdev->stats.tx_bytes += bytes;
|
|
|
|
#endif
|
2016-04-03 18:04:03 +02:00
|
|
|
if (netdev->event_callback) {
|
2016-02-21 23:56:19 +01:00
|
|
|
netdev->event_callback(netdev, NETDEV2_EVENT_TX_COMPLETE);
|
2016-04-03 18:04:03 +02:00
|
|
|
}
|
2016-02-11 23:06:07 +01:00
|
|
|
return res;
|
2015-05-19 09:50:29 +02:00
|
|
|
}
|
|
|
|
|
2016-03-27 21:36:00 +02:00
|
|
|
void netdev2_tap_setup(netdev2_tap_t *dev, const netdev2_tap_params_t *params) {
|
2015-08-21 16:08:43 +02:00
|
|
|
dev->netdev.driver = &netdev2_driver_tap;
|
2016-03-27 21:36:00 +02:00
|
|
|
strncpy(dev->tap_name, *(params->tap_name), IFNAMSIZ);
|
2015-05-19 09:50:29 +02:00
|
|
|
}
|
|
|
|
|
2016-07-07 17:24:55 +02:00
|
|
|
static void _tap_isr(int fd, void *arg) {
|
2015-12-09 09:29:47 +01:00
|
|
|
(void) fd;
|
2016-07-07 17:24:55 +02:00
|
|
|
(void) arg;
|
2015-12-09 09:29:47 +01:00
|
|
|
|
2015-08-21 16:08:43 +02:00
|
|
|
netdev2_t *netdev = (netdev2_t *)&netdev2_tap;
|
|
|
|
|
|
|
|
if (netdev->event_callback) {
|
2016-02-21 23:56:19 +01:00
|
|
|
netdev->event_callback(netdev, NETDEV2_EVENT_ISR);
|
2015-08-21 16:08:43 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
puts("netdev2_tap: _isr: no event callback.");
|
|
|
|
}
|
2015-05-19 09:50:29 +02:00
|
|
|
}
|
|
|
|
|
2015-08-21 16:08:43 +02:00
|
|
|
static int _init(netdev2_t *netdev)
|
2015-05-19 09:50:29 +02:00
|
|
|
{
|
2015-08-21 16:08:43 +02:00
|
|
|
DEBUG("%s:%s:%u\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
|
|
|
|
|
|
|
|
netdev2_tap_t *dev = (netdev2_tap_t*)netdev;
|
2015-05-19 09:50:29 +02:00
|
|
|
|
|
|
|
/* check device parametrs */
|
|
|
|
if (dev == NULL) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *name = dev->tap_name;
|
|
|
|
#ifdef __MACH__ /* OSX */
|
|
|
|
char clonedev[255] = "/dev/"; /* XXX bad size */
|
|
|
|
strncpy(clonedev + 5, name, 250);
|
|
|
|
#elif defined(__FreeBSD__)
|
|
|
|
char clonedev[255] = "/dev/"; /* XXX bad size */
|
|
|
|
strncpy(clonedev + 5, name, 250);
|
|
|
|
#else /* Linux */
|
|
|
|
struct ifreq ifr;
|
|
|
|
const char *clonedev = "/dev/net/tun";
|
|
|
|
#endif
|
|
|
|
/* initialize device descriptor */
|
|
|
|
dev->promiscous = 0;
|
|
|
|
/* implicitly create the tap interface */
|
2015-12-24 06:24:57 +01:00
|
|
|
if ((dev->tap_fd = real_open(clonedev, O_RDWR | O_NONBLOCK)) == -1) {
|
2015-05-19 09:50:29 +02:00
|
|
|
err(EXIT_FAILURE, "open(%s)", clonedev);
|
|
|
|
}
|
|
|
|
#if (defined(__MACH__) || defined(__FreeBSD__)) /* OSX/FreeBSD */
|
|
|
|
struct ifaddrs *iflist;
|
|
|
|
if (real_getifaddrs(&iflist) == 0) {
|
|
|
|
for (struct ifaddrs *cur = iflist; cur; cur = cur->ifa_next) {
|
|
|
|
if ((cur->ifa_addr->sa_family == AF_LINK) && (strcmp(cur->ifa_name, name) == 0) && cur->ifa_addr) {
|
|
|
|
struct sockaddr_dl *sdl = (struct sockaddr_dl *)cur->ifa_addr;
|
|
|
|
memcpy(dev->addr, LLADDR(sdl), sdl->sdl_alen);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
real_freeifaddrs(iflist);
|
|
|
|
}
|
|
|
|
#else /* Linux */
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
|
|
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
|
|
|
strncpy(ifr.ifr_name, name, IFNAMSIZ);
|
|
|
|
if (real_ioctl(dev->tap_fd, TUNSETIFF, (void *)&ifr) == -1) {
|
|
|
|
_native_in_syscall++;
|
|
|
|
warn("ioctl TUNSETIFF");
|
|
|
|
warnx("probably the tap interface (%s) does not exist or is already in use", name);
|
|
|
|
real_exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get MAC address */
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
|
|
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", name);
|
|
|
|
if (real_ioctl(dev->tap_fd, SIOCGIFHWADDR, &ifr) == -1) {
|
|
|
|
_native_in_syscall++;
|
|
|
|
warn("ioctl SIOCGIFHWADDR");
|
|
|
|
if (real_close(dev->tap_fd) == -1) {
|
|
|
|
warn("close");
|
|
|
|
}
|
|
|
|
real_exit(EXIT_FAILURE);
|
|
|
|
}
|
2015-08-07 07:39:02 +02:00
|
|
|
memcpy(dev->addr, ifr.ifr_hwaddr.sa_data, ETHERNET_ADDR_LEN);
|
2015-05-19 09:50:29 +02:00
|
|
|
|
|
|
|
/* change mac addr so it differs from what the host is using */
|
|
|
|
dev->addr[5]++;
|
|
|
|
#endif
|
2015-08-17 15:41:29 +02:00
|
|
|
DEBUG("gnrc_tapnet_init(): dev->addr = %02x:%02x:%02x:%02x:%02x:%02x\n",
|
2015-05-19 09:50:29 +02:00
|
|
|
dev->addr[0], dev->addr[1], dev->addr[2],
|
|
|
|
dev->addr[3], dev->addr[4], dev->addr[5]);
|
2015-12-09 09:29:47 +01:00
|
|
|
|
2015-05-19 09:50:29 +02:00
|
|
|
/* configure signal handler for fds */
|
2015-12-09 09:29:47 +01:00
|
|
|
native_async_read_setup();
|
2016-07-07 17:24:55 +02:00
|
|
|
native_async_read_add_handler(dev->tap_fd, NULL, _tap_isr);
|
2015-12-09 09:29:47 +01:00
|
|
|
|
2016-02-11 23:06:07 +01:00
|
|
|
#ifdef MODULE_NETSTATS_L2
|
|
|
|
memset(&netdev->stats, 0, sizeof(netstats_t));
|
|
|
|
#endif
|
2015-08-17 15:41:29 +02:00
|
|
|
DEBUG("gnrc_tapnet: initialized.\n");
|
2015-05-19 09:50:29 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-21 16:08:43 +02:00
|
|
|
void netdev2_tap_cleanup(netdev2_tap_t *dev)
|
2015-07-19 12:05:38 +02:00
|
|
|
{
|
|
|
|
/* Do we have a device */
|
|
|
|
if (!dev) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* cleanup signal handling */
|
2015-12-09 09:29:47 +01:00
|
|
|
native_async_read_cleanup();
|
2015-07-19 12:05:38 +02:00
|
|
|
|
|
|
|
/* close the tap device */
|
|
|
|
real_close(dev->tap_fd);
|
|
|
|
}
|