/* Based on Documentation/networking/tuntap.txt */ #include #include #include #include #include #include "sis.h" #ifdef __linux__ #include #include #include #include #include #include #include #include /* for ARPHRD_ETHER */ #include #include #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