diff options
Diffstat (limited to 'tap.c')
-rw-r--r-- | tap.c | 222 |
1 files changed, 222 insertions, 0 deletions
@@ -0,0 +1,222 @@ +/* Based on Documentation/networking/tuntap.txt */ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include "sis.h" +#ifdef __linux__ +#include <sys/socket.h> +#include <sys/file.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <linux/if_tun.h> +#include <linux/if_bridge.h> +#include <net/if_arp.h> /* for ARPHRD_ETHER */ +#include <arpa/inet.h> +#include <poll.h> + +#define POLLTIME 5000 + +static struct ifreq ifr; +static int fd, err, sockfd; +static char dev[64]; +static int tun_fd, nread, br_socket_fd; +static int br_add_interface (const char *bridge, const char *dev); +static void sis_tap_poll (); + +int +sis_tap_init (long unsigned emac) +{ + int i; + long unsigned xmac, xmac2; + if (fd) + return 1; + if ((fd = open ("/dev/net/tun", O_RDWR)) < 0) + { + printf ("Cannot open TUN/TAP dev\n"); + return 0; + } + + memset (&ifr, 0, sizeof (ifr)); + + /* Flags: IFF_TUN - TUN device (no Ethernet headers) + * IFF_TAP - TAP device + * + * IFF_NO_PI - Do not provide packet information + */ + + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + + if ((err = ioctl (fd, TUNSETIFF, (void *) &ifr)) < 0) + { + printf ("ERR: Could not ioctl tun: %s\n", strerror (errno)); + close (fd); + return 0; + } + + strcpy (dev, ifr.ifr_name); + + sockfd = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL)); + + ifr.ifr_flags = IFF_UP; + if ((err = ioctl (sockfd, SIOCSIFFLAGS, (void *) &ifr)) < 0) + { + printf ("net: could not enable tun %s\n", strerror (errno)); + close (fd); + close (sockfd); + return 0; + } + + ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; + + xmac = 0; + for (i = 0; i < 6; i++) + { + xmac <<= 8; + xmac |= (emac >> (i * 8)) & 0x0ff; + } + + xmac2 = xmac; + + memcpy (ifr.ifr_hwaddr.sa_data, &xmac, 6); + if (ioctl (sockfd, SIOCSIFHWADDR, &ifr) < 0) + { + printf ("net: hwaddr error\n"); + close (fd); + close (sockfd); + return 0; + } + + if (ioctl (sockfd, SIOCGIFHWADDR, &ifr) < 0) + { + printf ("net: hwaddr error\n"); + close (fd); + close (sockfd); + return 0; + } + memcpy (&xmac, ifr.ifr_hwaddr.sa_data, 6); + + if (xmac != xmac2) + { + printf ("net: could not set hwaddr\n"); + close (fd); + close (sockfd); + return 0; + } + + printf ("net: using %s, ether %lx", dev, emac); + if (bridge[0]) + { + if (br_add_interface (bridge, dev)) + { + printf ("ERR: Could not attach %s to bridge %s: %s\n", + dev, bridge, strerror (errno)); + /* + close (fd); + close (sockfd); + return 0; + */ + } + else + printf (", bridge %s\n", bridge); + } + printf ("\n"); + tun_fd = fd; + event (sis_tap_poll, 0, POLLTIME); + return 1; +} + +int +sis_tap_write (unsigned char *buffer, int len) +{ + int i, nwrite; + nwrite = write (tun_fd, buffer, len); + if (nread < 0) + { + perror ("Writing from interface"); + close (tun_fd); + exit (1); + } + if (sis_verbose) + { + printf ("Write %d bytes to device %s\n", len, dev); + for (i = 0; i < len; i++) + printf ("%02x", buffer[i]); + printf ("\n"); + } +} + +static void +sis_tap_poll () +{ + struct pollfd fds[1]; + int ret; + unsigned char buffer[1536]; + + fds[0].fd = tun_fd; + fds[0].events = POLLRDNORM; + ret = poll (fds, 1, 0); + + if (ret > 0) + { + if (fds[0].revents & POLLRDNORM) + { + nread = read (tun_fd, buffer, 1536); + if (nread < 0) + { + perror ("Reading from interface"); + close (tun_fd); + exit (1); + } + greth_rxready (buffer, nread); + } + } + event (sis_tap_poll, 0, POLLTIME); +} + +/* Attach tap device to bridge */ +/* Taken from bridge-utils-1.6 */ +static int +br_add_interface (const char *bridge, const char *dev) +{ + struct ifreq ifr; + int err; + int ifindex = if_nametoindex (dev); + + if ((br_socket_fd = socket (AF_LOCAL, SOCK_STREAM, 0)) < 0) + return ENODEV; + if (ifindex == 0) + return ENODEV; + + strncpy (ifr.ifr_name, bridge, IFNAMSIZ); +#ifdef SIOCBRADDIF + ifr.ifr_ifindex = ifindex; + err = ioctl (br_socket_fd, SIOCBRADDIF, &ifr); +#endif + { + unsigned long args[4] = { BRCTL_ADD_IF, ifindex, 0, 0 }; + + ifr.ifr_data = (char *) args; + err = ioctl (br_socket_fd, SIOCDEVPRIVATE, &ifr); + } + + return err < 0 ? errno : 0; +} + +#else + +int +sis_tap_init (long unsigned emac) +{ + printf ("Error: networking not supported on this host\n"); +} + +int +sis_tap_write (unsigned char *buffer, int len) +{ + printf ("Error: networking not supported on this host\n"); +} +#endif |