From 8e08bc085cc3376c0be63936b192412f22e12360 Mon Sep 17 00:00:00 2001 From: joxe Date: Fri, 8 May 2009 12:37:34 +0000 Subject: [PATCH] added tapslip6 for ipv6 connectivity over USB/SLIP/ethernet - use together with uip6-bridge --- tools/tapslip6.c | 629 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 629 insertions(+) create mode 100644 tools/tapslip6.c diff --git a/tools/tapslip6.c b/tools/tapslip6.c new file mode 100644 index 000000000..b7711f481 --- /dev/null +++ b/tools/tapslip6.c @@ -0,0 +1,629 @@ +/* + * Copyright (c) 2001, Adam Dunkels. + * Copyright (c) 2009, Joakim Eriksson, Niclas Finne. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * $Id: tapslip6.c,v 1.1 2009/05/08 12:37:34 joxe Exp $ + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +in_addr_t giaddr; +in_addr_t netaddr; +in_addr_t circuit_addr; + +int ssystem(const char *fmt, ...) + __attribute__((__format__ (__printf__, 1, 2))); +void write_to_serial(int outfd, void *inbuf, int len); + +//#define PROGRESS(s) fprintf(stderr, s) +#define PROGRESS(s) do { } while (0) + +char tundev[32] = { "tap0" }; + +int +ssystem(const char *fmt, ...) __attribute__((__format__ (__printf__, 1, 2))); + +int +ssystem(const char *fmt, ...) +{ + char cmd[128]; + va_list ap; + va_start(ap, fmt); + vsnprintf(cmd, sizeof(cmd), fmt, ap); + va_end(ap); + printf("%s\n", cmd); + fflush(stdout); + return system(cmd); +} + +#define SLIP_END 0300 +#define SLIP_ESC 0333 +#define SLIP_ESC_END 0334 +#define SLIP_ESC_ESC 0335 + +static void +print_packet(u_int8_t *p, int len) { + int i; + for(i = 0; i < len; i++) { + printf("%02x", p[i]); + if ((i & 3) == 3) + printf(" "); + if ((i & 15) == 15) + printf("\n"); + } + printf("\n"); +} + +/* + * Read from serial, when we have a packet write it to tun. No output + * buffering, input buffered by stdio. + */ +void +serial_to_tun(FILE *inslip, int outfd) +{ + static union { + unsigned char inbuf[2000]; + } uip; + static int inbufptr = 0; + + int ret; + unsigned char c; + +#ifdef linux + ret = fread(&c, 1, 1, inslip); + if(ret == -1 || ret == 0) err(1, "serial_to_tun: read"); + goto after_fread; +#endif + + read_more: + if(inbufptr >= sizeof(uip.inbuf)) { + inbufptr = 0; + } + ret = fread(&c, 1, 1, inslip); +#ifdef linux + after_fread: +#endif + if(ret == -1) { + err(1, "serial_to_tun: read"); + } + if(ret == 0) { + clearerr(inslip); + return; + fprintf(stderr, "serial_to_tun: EOF\n"); + exit(1); + } + /* fprintf(stderr, ".");*/ + switch(c) { + case SLIP_END: + if(inbufptr > 0) { + + if(uip.inbuf[0] == '!') { + if (uip.inbuf[1] == 'M') { + /* Read gateway MAC address and autoconfigure tap0 interface */ + char macs[24]; + int i, pos; + for(i = 0, pos = 0; i < 16; i++) { + macs[pos++] = uip.inbuf[2 + i]; + if ((i & 1) == 1 && i < 14) { + macs[pos++] = ':'; + } + } + macs[pos] = '\0'; + printf("*** Gateway's MAC address: %s\n", macs); + + ssystem("ifconfig %s down", tundev); + ssystem("ifconfig %s hw ether %s", tundev, &macs[6]); + ssystem("ifconfig %s up", tundev); + } + } else { + printf("Writing to tun len: %d\n", inbufptr); + print_packet(uip.inbuf, inbufptr); + if(write(outfd, uip.inbuf, inbufptr) != inbufptr) { + err(1, "serial_to_tun: write"); + } + } + inbufptr = 0; + } + break; + + case SLIP_ESC: + if(fread(&c, 1, 1, inslip) != 1) { + clearerr(inslip); + /* Put ESC back and give up! */ + ungetc(SLIP_ESC, inslip); + return; + } + + switch(c) { + case SLIP_ESC_END: + c = SLIP_END; + break; + case SLIP_ESC_ESC: + c = SLIP_ESC; + break; + } + /* FALLTHROUGH */ + default: + uip.inbuf[inbufptr++] = c; + break; + } + + goto read_more; +} + +unsigned char slip_buf[2000]; +int slip_end, slip_begin; + + +void +slip_send(int fd, unsigned char c) +{ + if (slip_end >= sizeof(slip_buf)) + err(1, "slip_send overflow"); + slip_buf[slip_end] = c; + slip_end++; +} + +int +slip_empty() +{ + return slip_end == 0; +} + +void +slip_flushbuf(int fd) +{ + int n; + + if (slip_empty()) + return; + + n = write(fd, slip_buf + slip_begin, (slip_end - slip_begin)); + + if(n == -1 && errno != EAGAIN) { + err(1, "slip_flushbuf write failed"); + } else if(n == -1) { + PROGRESS("Q"); /* Outqueueis full! */ + } else { + slip_begin += n; + if(slip_begin == slip_end) { + slip_begin = slip_end = 0; + } + } +} + +void +write_to_serial(int outfd, void *inbuf, int len) +{ + u_int8_t *p = inbuf; + int i, ecode; + + printf("Got packet of length %d - write SLIP\n", len); + print_packet(p, len); + + /* It would be ``nice'' to send a SLIP_END here but it's not + * really necessary. + */ + /* slip_send(outfd, SLIP_END); */ + printf("writing packet to serial!!! %d\n", len); + for(i = 0; i < len; i++) { + switch(p[i]) { + case SLIP_END: + slip_send(outfd, SLIP_ESC); + slip_send(outfd, SLIP_ESC_END); + break; + case SLIP_ESC: + slip_send(outfd, SLIP_ESC); + slip_send(outfd, SLIP_ESC_ESC); + break; + default: + slip_send(outfd, p[i]); + break; + } + + } + slip_send(outfd, SLIP_END); + PROGRESS("t"); +} + + +/* + * Read from tun, write to slip. + */ +void +tun_to_serial(int infd, int outfd) +{ + struct { + unsigned char inbuf[2000]; + } uip; + int size; + + if((size = read(infd, uip.inbuf, 2000)) == -1) err(1, "tun_to_serial: read"); + + write_to_serial(outfd, uip.inbuf, size); +} + +#ifndef BAUDRATE +#define BAUDRATE B115200 +#endif +speed_t b_rate = BAUDRATE; + +void +stty_telos(int fd) +{ + struct termios tty; + speed_t speed = b_rate; + int i; + + if(tcflush(fd, TCIOFLUSH) == -1) err(1, "tcflush"); + + if(tcgetattr(fd, &tty) == -1) err(1, "tcgetattr"); + + cfmakeraw(&tty); + + /* Nonblocking read. */ + tty.c_cc[VTIME] = 0; + tty.c_cc[VMIN] = 0; + tty.c_cflag &= ~CRTSCTS; + tty.c_cflag &= ~HUPCL; + tty.c_cflag &= ~CLOCAL; + + cfsetispeed(&tty, speed); + cfsetospeed(&tty, speed); + + if(tcsetattr(fd, TCSAFLUSH, &tty) == -1) err(1, "tcsetattr"); + +#if 1 + /* Nonblocking read and write. */ + /* if(fcntl(fd, F_SETFL, O_NONBLOCK) == -1) err(1, "fcntl"); */ + + tty.c_cflag |= CLOCAL; + if(tcsetattr(fd, TCSAFLUSH, &tty) == -1) err(1, "tcsetattr"); + + i = TIOCM_DTR; + if(ioctl(fd, TIOCMBIS, &i) == -1) err(1, "ioctl"); +#endif + + usleep(10*1000); /* Wait for hardware 10ms. */ + + /* Flush input and output buffers. */ + if(tcflush(fd, TCIOFLUSH) == -1) err(1, "tcflush"); +} + +int +devopen(const char *dev, int flags) +{ + char t[32]; + strcpy(t, "/dev/"); + strcat(t, dev); + return open(t, flags); +} + +#ifdef linux +#include +#include + +int +tun_alloc(char *dev) +{ + struct ifreq ifr; + int fd, err; + + if( (fd = open("/dev/net/tun", O_RDWR)) < 0 ) + return -1; + + 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(*dev != 0) + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + + if((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){ + close(fd); + return err; + } + strcpy(dev, ifr.ifr_name); + return fd; +} +#else +int +tun_alloc(char *dev) +{ + return devopen(dev, O_RDWR); +} +#endif + +const char *ipaddr; +const char *netmask; + +void +cleanup(void) +{ + ssystem("ifconfig %s down", tundev); +#ifndef linux + ssystem("sysctl -w net.ipv6.conf.all.forwarding=1"); +#endif + /* ssystem("arp -d %s", ipaddr); */ + ssystem("netstat -nr" + " | awk '{ if ($2 == \"%s\") print \"route delete -net \"$1; }'" + " | sh", + tundev); +} + +void +sigcleanup(int signo) +{ + fprintf(stderr, "signal %d\n", signo); + exit(0); /* exit(0) will call cleanup() */ +} + +static int got_sigalarm; +static int request_mac; + +void +sigalarm(int signo) +{ + got_sigalarm = 1; + return; +} + +void +sigalarm_reset() +{ +#ifdef linux +#define TIMEOUT (997*1000) +#else +#define TIMEOUT (2451*1000) +#endif + ualarm(TIMEOUT, TIMEOUT); + got_sigalarm = 0; +} + +void +ifconf(const char *tundev, const char *ipaddr, const char *netmask) +{ + struct in_addr netname; + netname.s_addr = inet_addr(ipaddr) & inet_addr(netmask); + +#ifdef linux + ssystem("ifconfig %s inet `hostname` up", tundev); + if(strcmp(ipaddr, "0.0.0.0") != 0) { + ssystem("route add -net %s netmask %s dev %s", + inet_ntoa(netname), netmask, tundev); + } +#else + ssystem("ifconfig %s inet `hostname` %s up", tundev, ipaddr); + if(strcmp(ipaddr, "0.0.0.0") != 0) { + ssystem("route add -net %s -netmask %s -interface %s", + inet_ntoa(netname), netmask, tundev); + } + ssystem("sysctl -w net.inet.ip.forwarding=1"); +#endif /* !linux */ + + ssystem("ifconfig %s\n", tundev); +} + +int +main(int argc, char **argv) +{ + int c; + int tunfd, slipfd, maxfd; + int ret; + fd_set rset, wset; + FILE *inslip; + const char *siodev = NULL; + int baudrate = -2; + request_mac = 1; + setvbuf(stdout, NULL, _IOLBF, 0); /* Line buffered output. */ + + while((c = getopt(argc, argv, "B:D:hs:t:")) != -1) { + switch (c) { + case 'B': + baudrate = atoi(optarg); + break; + + case 's': + if(strncmp("/dev/", optarg, 5) == 0) { + siodev = optarg + 5; + } else { + siodev = optarg; + } + break; + + case 't': + if(strncmp("/dev/", optarg, 5) == 0) { + strcpy(tundev, optarg + 5); + } else { + strcpy(tundev, optarg); + } + break; + + case '?': + case 'h': + default: + err(1, "usage: tapslip6 [-B baudrate] [-s siodev] [-t tundev] ipaddress netmask"); + break; + } + } + argc -= (optind - 1); + argv += (optind - 1); + + if(argc != 3 && argc != 4) { + err(1, "usage: tapslip6 [-s siodev] [-t tundev] ipaddress netmask "); + } + ipaddr = argv[1]; + netmask = argv[2]; + circuit_addr = inet_addr(ipaddr); + netaddr = inet_addr(ipaddr) & inet_addr(netmask); + + switch(baudrate) { + case -2: + break; /* Use default. */ + case 9600: + b_rate = B9600; + break; + case 19200: + b_rate = B19200; + break; + case 38400: + b_rate = B38400; + break; + case 57600: + b_rate = B57600; + break; + case 115200: + b_rate = B115200; + break; + default: + err(1, "unknown baudrate %d", baudrate); + break; + } + + + if(siodev != NULL) { + slipfd = devopen(siodev, O_RDWR | O_NONBLOCK); + if(slipfd == -1) { + err(1, "can't open siodev ``/dev/%s''", siodev); + } + } else { + static const char *siodevs[] = { + "ttyUSB0", "cuaU0", "ucom0" /* linux, fbsd6, fbsd5 */ + }; + int i; + for(i = 0; i < 3; i++) { + siodev = siodevs[i]; + slipfd = devopen(siodev, O_RDWR | O_NONBLOCK); + if (slipfd != -1) + break; + } + if(slipfd == -1) { + err(1, "can't open siodev"); + } + } + fprintf(stderr, "slip started on ``/dev/%s''\n", siodev); + stty_telos(slipfd); + slip_send(slipfd, SLIP_END); + inslip = fdopen(slipfd, "r"); + if(inslip == NULL) err(1, "main: fdopen"); + + tunfd = tun_alloc(tundev); + printf("opening: %s", tundev); + if(tunfd == -1) err(1, "main: open"); + fprintf(stderr, "opened device ``/dev/%s''\n", tundev); + + atexit(cleanup); + signal(SIGHUP, sigcleanup); + signal(SIGTERM, sigcleanup); + signal(SIGINT, sigcleanup); + signal(SIGALRM, sigalarm); + ifconf(tundev, ipaddr, netmask); + + while(1) { + maxfd = 0; + FD_ZERO(&rset); + FD_ZERO(&wset); + + /* request mac address from gateway node for autoconfiguration of + ethernet interface tap0 */ + if (request_mac) { + slip_send(slipfd, '?'); + slip_send(slipfd, 'M'); + slip_send(slipfd, SLIP_END); + request_mac = 0; + } + + if(got_sigalarm) { + /* Send "?IPA". */ + slip_send(slipfd, '?'); + slip_send(slipfd, 'I'); + slip_send(slipfd, 'P'); + slip_send(slipfd, 'A'); + slip_send(slipfd, SLIP_END); + got_sigalarm = 0; + } + + if(!slip_empty()) { /* Anything to flush? */ + FD_SET(slipfd, &wset); + } + + FD_SET(slipfd, &rset); /* Read from slip ASAP! */ + if(slipfd > maxfd) maxfd = slipfd; + + /* We only have one packet at a time queued for slip output. */ + if(slip_empty()) { + FD_SET(tunfd, &rset); + if(tunfd > maxfd) maxfd = tunfd; + } + + ret = select(maxfd + 1, &rset, &wset, NULL, NULL); + if(ret == -1 && errno != EINTR) { + err(1, "select"); + } else if(ret > 0) { + if(FD_ISSET(slipfd, &rset)) { + serial_to_tun(inslip, tunfd); + } + + if(FD_ISSET(slipfd, &wset)) { + slip_flushbuf(slipfd); + sigalarm_reset(); + } + + if(slip_empty() && FD_ISSET(tunfd, &rset)) { + tun_to_serial(tunfd, slipfd); + slip_flushbuf(slipfd); + sigalarm_reset(); + } + } + } +}