2014-01-25 Mikael Pettersson * configure.ac: check for Linux TUN/TAP support * src/netdev.c: rearrange, fix typos, add support for Linux TUN/TAP devices diff -rupN ski-1.3.2/configure.ac ski-1.3.2.tuntap/configure.ac --- ski-1.3.2/configure.ac 2008-02-05 05:11:37.000000000 +0100 +++ ski-1.3.2.tuntap/configure.ac 2014-01-25 12:40:53.052086069 +0100 @@ -255,6 +255,26 @@ fi AM_CONDITIONAL(WITH_NETDEV, test "x$with_netdev" = xyes) +dnl Check that the host supports TUN/TAP devices +AC_CACHE_CHECK([whether TUN/TAP is supported], + ac_cv_have_tun_tap, [ + AC_TRY_COMPILE([ + #include + #include + #include + ], [ + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + ], + ac_cv_have_tun_tap=yes, ac_cv_have_tun_tap=no + ) +]) + +if test "x$ac_cv_have_tun_tap" = xyes; then + AC_DEFINE(HAVE_TUN_TAP, 1, [define if you have TUN/TAP support]) +fi + if test "$HOST_OS" = freebsd; then LDFLAGS="$LDFLAGS -lutil" fi diff -rupN ski-1.3.2/src/netdev.c ski-1.3.2.tuntap/src/netdev.c --- ski-1.3.2/src/netdev.c 2008-02-05 05:11:38.000000000 +0100 +++ ski-1.3.2.tuntap/src/netdev.c 2014-01-25 12:44:37.211465930 +0100 @@ -50,12 +50,212 @@ #ifdef SKINET_ENABLE +BOOL ski_nonet; /* when true network is disabled, ie no interface is found */ + +#define ETH_DETACHED 0x0 +#define ETH_ATTACHED 0x1 + +typedef struct _eth_dev_t + { + struct _eth_dev_t *eth_next; /* linked list of devices */ + int eth_fd; /* current file descriptor */ + int eth_flags; /* attached|detached */ +#if !HAVE_TUN_TAP + char eth_name[IFNAMSIZ]; /* real name */ + unsigned int eth_ipaddr; /* current ipaddress */ + struct sockaddr eth_sa; /* save extra copies on send */ +#endif + } +eth_dev_t; + +/* + * list of currently detected devices + */ +static eth_dev_t *eth_list; + +static inline eth_dev_t * +find_eth (int fd) +{ + eth_dev_t *p; + + for (p = eth_list; p; p = p->eth_next) + if (p->eth_fd == fd) + return p; + + return NULL; +} + +#if HAVE_TUN_TAP + +#include +#include + +int +netdev_open (char *name, unsigned char *macaddr) +{ + int fd; + struct ifreq ifr; + eth_dev_t *eth; + + if (ski_nonet == YES) + return -1; + + fd = open ("/dev/net/tun", O_RDWR); + if (fd == -1) + { + cmdwPrint ("netdev_open: open /dev/net/tun failed: %d\n", errno); + return -1; + } + + memset (&ifr, 0, sizeof ifr); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + if (name) + strncpy (ifr.ifr_name, name, IFNAMSIZ); + + if (ioctl (fd, TUNSETIFF, &ifr) < 0) + { + cmdwPrint ("netdev_open: ioctl TUNSETIFF failed: %d\n", errno); + close (fd); + return -1; + } + + if (ioctl (fd, SIOCGIFHWADDR, &ifr) == -1) + { + cmdwPrint ("netdev_open: ioctl SIOCGIFHWADDR failed: %d\n", errno); + close (fd); + return -1; + } + + memcpy (macaddr, (char *) ifr.ifr_hwaddr.sa_data, IFHWADDRLEN); + + eth = malloc (sizeof *eth); + if (!eth) + { + cmdwPrint ("netdev_open: malloc failed: %d\n", errno); + close (fd); + return -1; + } + + /* + * the device is not yet attached + */ + eth->eth_fd = fd; + eth->eth_next = eth_list; + eth->eth_flags = ETH_DETACHED; + eth_list = eth; /* we're at the head of the list now */ + + return fd; +} + +int +netdev_attach (int fd, unsigned int ipaddr) +{ + int r; + eth_dev_t *eth = find_eth (fd); + + if (eth == NULL) + progExit ("netdev_attach: can't find eth %d\n", fd); + + /* + * prepare for SIGIO: set ownership + asynchronous notification + */ + r = fcntl (fd, F_SETOWN, getpid ()); + if (r == -1) + progExit ("netdev_attach: f_setown %d\n", errno); + + r = fcntl (fd, F_SETFL, fcntl (fd, F_GETFL, 0) | O_ASYNC | O_NONBLOCK); + if (r == -1) + progExit ("netdev_attach: f_setfl %d\n", errno); + + eth->eth_flags = ETH_ATTACHED; + + return 0; +} + +int +netdev_detach (int fd) +{ + int r; + eth_dev_t *eth = find_eth (fd); + + if (eth == NULL) + progExit ("netdev_detach: can't find eth %d\n", fd); + + /* + * don't notify + * + * should also remove ownership but it does not really matter + */ + r = fcntl (fd, F_SETFL, fcntl (fd, F_GETFL, 0) & (~O_ASYNC | O_NONBLOCK)); + if (r == -1) + progExit ("netdev_detach: f_setfl %d\n", errno); + + eth->eth_flags = ETH_DETACHED; + + return 0; +} + +/* + * read the next frame from interface + */ +long +netdev_recv (int fd, char *fbuf, int len) +{ + int r; + + if ((unsigned long) fbuf & 0x3) + progExit ("receive frame not aligned"); + + r = read (fd, fbuf, len); + if (r == -1 && errno != EAGAIN) + progExit ("netdev_recv: error on read %d\n", errno); + + return (long) (r > 0 ? r : 0); +} + +/* + * write next frame to interface + */ +long +netdev_send (int fd, char *fbuf, int len) +{ + int r; + eth_dev_t *eth = find_eth (fd); + + if ((unsigned long) fbuf & 0x3) + progExit ("trasmit frame not aligned"); + + if (eth == NULL) + progExit ("netdev_send: can't find %d\n", fd); + + r = write (fd, fbuf, len); + if (r == -1) + progExit ("netdev_send: error on write %d\n", errno); + + return (long) len; +} + +static int +netdev_can_read (int fd) +{ + struct pollfd pfd; + int r; + + pfd.fd = fd; + pfd.events = POLLIN; + pfd.revents = 0; + r = poll (&pfd, 1, 0); + if (r == -1 || r == 0) + return r; + return pfd.revents & POLLIN; +} + +#else /* !HAVE_TUN_TAP */ + #define MAX_FRAME_SIZE 1536 /* should be more than enough for ethernet */ #define MAX_FD 256 -BOOL ski_nonet; /* when true network is disable, ie no interface is found */ - /* * thanks to tcpdump for the program dump * @@ -116,38 +316,6 @@ static struct sock_filter insns[] = BPF_STMT (BPF_RET + BPF_K, 0xffffffff) }; -#define ETH_DETACHED 0x0 -#define ETH_ATTACHED 0x1 - -typedef struct _eth_dev_t - { - struct _eth_dev_t *eth_next; /* linked list of devices */ - char eth_name[IFNAMSIZ]; /* real name */ - unsigned int eth_ipaddr; /* current ipaddress */ - int eth_fd; /* current fiel descriptor */ - int eth_flags; /* attached|detached */ - struct sockaddr eth_sa; /* save extra copies on send */ - } -eth_dev_t; - -/* - * list od currently detected devices - */ -static eth_dev_t *eth_list; - -static inline eth_dev_t * -find_eth (int fd) -{ - eth_dev_t *p; - - for (p = eth_list; p; p = p->eth_next) - { - if (p->eth_fd == fd) - return p; - } - return NULL; -} - int netdev_open (char *name, unsigned char *macaddr) { @@ -402,6 +570,20 @@ netdev_send (int fd, char *fbuf, int len return (long) len; } +static int +netdev_can_read (int fd) +{ + char c; + + /* + * We can't use FIONREAD here because it's only supported + * by TCP sockets. + */ + return recv (p->eth_fd, &c, 1, MSG_PEEK); +} + +#endif /* !HAVE_TUN_TAP */ + /* * check is SIGIO was because of network I/O * the other possible reason is keyboard input @@ -414,7 +596,6 @@ int isnetio (void) { int r; - char c; eth_dev_t *p; if (eth_list == NULL) @@ -427,11 +608,7 @@ isnetio (void) if (p->eth_flags != ETH_ATTACHED) continue; - /* - * We can't use FIONREAD here because it's only supported - * by TCP sockets. - */ - r = recv (p->eth_fd, &c, 1, MSG_PEEK); + r = netdev_can_read (p->eth_fd); if (r > 0) return 1;