From f3a4c67e9091c98596c19784040b88ad446a87cf Mon Sep 17 00:00:00 2001 From: Thomas Osterried Date: Sun, 30 Oct 2005 10:31:40 +0000 Subject: - tun/tap support for ax25ipd - fclose() fix in config.c description of tun/tap: added a new fast and efficient link to the linux kernel ax25 stack, via the ethertap interface. ax25 goes directly to the bpqether interface in the kernel via ethertap/tuntap interfaces, which is a much better way than traditional kissattach to a ttyp/ptyp pair. --- ax25ipd/HISTORY.ax25ipd | 7 + ax25ipd/Makefile.am | 3 +- ax25ipd/Makefile.in | 5 +- ax25ipd/ax25ipd.conf | 22 +++- ax25ipd/ax25ipd.conf.5 | 2 +- ax25ipd/ax25ipd.h | 9 ++ ax25ipd/bpqether.c | 342 ++++++++++++++++++++++++++++++++++++++++++++++++ ax25ipd/config.c | 1 + ax25ipd/io.c | 24 +++- ax25ipd/process.c | 12 +- 10 files changed, 417 insertions(+), 10 deletions(-) create mode 100644 ax25ipd/bpqether.c diff --git a/ax25ipd/HISTORY.ax25ipd b/ax25ipd/HISTORY.ax25ipd index f4a28cd..20a6339 100644 --- a/ax25ipd/HISTORY.ax25ipd +++ b/ax25ipd/HISTORY.ax25ipd @@ -46,4 +46,11 @@ * July97 Added support for broadcast addresses and routes. * Converted route array to a linked list and removed the * maximum route limitations. (vk2ktj) + * + * 2005-10-26 - dl9sau: + * added a new fast and efficient link to the linux kernel ax25 + * stack, via the ethertap interface. + * ax25 goes directly to the bpqether interface in the kernel via + * ethertap/tuntap interfaces, which is a much better way than + * traditional kissattach to a ttyp/ptyp pair. */ diff --git a/ax25ipd/Makefile.am b/ax25ipd/Makefile.am index 66b23dd..39ef22f 100644 --- a/ax25ipd/Makefile.am +++ b/ax25ipd/Makefile.am @@ -19,7 +19,8 @@ ax25ipd_SOURCES = \ ax25ipd.c \ ax25ipd.h \ process.c \ - routing.c + routing.c \ + bpqether.c # Needed so that install is optional etcfiles = ax25ipd.conf diff --git a/ax25ipd/Makefile.in b/ax25ipd/Makefile.in index 3d1fd23..8f67132 100644 --- a/ax25ipd/Makefile.in +++ b/ax25ipd/Makefile.in @@ -108,7 +108,8 @@ ax25ipd_SOURCES = \ ax25ipd.c \ ax25ipd.h \ process.c \ - routing.c + routing.c \ + bpqether.c # Needed so that install is optional @@ -129,7 +130,7 @@ PROGRAMS = $(sbin_PROGRAMS) am_ax25ipd_OBJECTS = config.$(OBJEXT) crc.$(OBJEXT) io.$(OBJEXT) \ kiss.$(OBJEXT) ax25ipd.$(OBJEXT) process.$(OBJEXT) \ - routing.$(OBJEXT) + routing.$(OBJEXT) bpqether.$(OBJEXT) ax25ipd_OBJECTS = $(am_ax25ipd_OBJECTS) ax25ipd_DEPENDENCIES = ax25ipd_LDFLAGS = diff --git a/ax25ipd/ax25ipd.conf b/ax25ipd/ax25ipd.conf index 391494f..af28d32 100644 --- a/ax25ipd/ax25ipd.conf +++ b/ax25ipd/ax25ipd.conf @@ -27,10 +27,30 @@ mode tnc #beacon after 540 #btext ax25ip -- tncmode rob/vk5xxx -- Experimental AXIP gateway # -# Serial port, or pipe connected to a kissattach in my case # +# Serial port, ethertap interface, or pipe connected to a kissattach in my case +# +# alternatively, if you have the kernel module bpqether: +# if you use tun/tap or ethertap instead of kissattach you may say, without +# leading slashes (!! - that's how ax25ipd consideres using the tty kiss +# driver or tun/tap or ethertap): +# with tun/tap: +# device foobar +# with ethertap (obsolete): +# device tap0 +# make sure you set a mycall above, or say axparms foobar -setcall te1st +# note: the device will be up when you assign an ip address +# _after_ starting ax25rtd (which initializes the device), start ax25d +# with bpqether or pty, you do not need to care abt the speed +# tun/tap: as descriped in /usr/src/linux/Documentatioa/networking/tuntap.txt, +# make a device # like this: +# crw-r--r-- 1 root root 10, 200 Nov 26 13:32 tun +# with the command mknod /dev/net/tun c 10 200 +# +#device ampr device /dev/ttyp0 # +# # Set the device speed # speed 9600 diff --git a/ax25ipd/ax25ipd.conf.5 b/ax25ipd/ax25ipd.conf.5 index 73ea569..d2f1561 100644 --- a/ax25ipd/ax25ipd.conf.5 +++ b/ax25ipd/ax25ipd.conf.5 @@ -66,7 +66,7 @@ mode tnc .br # .br -# Serial port, or pipe connected to a kissattach in my case +# Serial port, ethertap interface, or pipe connected to a kissattach in my case .br # .br diff --git a/ax25ipd/ax25ipd.h b/ax25ipd/ax25ipd.h index de845b8..818cd50 100644 --- a/ax25ipd/ax25ipd.h +++ b/ax25ipd/ax25ipd.h @@ -180,6 +180,15 @@ void usr1_handler(int); void int_handler(int); void term_handler(int); +/* io.c */ +extern int ttyfd_bpq; + +/* bpqether.c */ +int send_bpq(unsigned char *buf, int len); +int receive_bpq(unsigned char *buf, int l); +int open_ethertap(char *ifname); +int set_bpq_dev_call_and_up(char *ethertap_name); + /* * end */ diff --git a/ax25ipd/bpqether.c b/ax25ipd/bpqether.c new file mode 100644 index 0000000..58333f1 --- /dev/null +++ b/ax25ipd/bpqether.c @@ -0,0 +1,342 @@ +/* @(#) $Id: bpqether.c,v 1.1 2005/10/30 10:31:40 dl9sau Exp $ */ + +extern int ttyfd; +/* + * this is a port of wampes ethertap whith my extension for bpqether + * + * (c) 20020630 Thomas Osterried dl9sau + * License: GPL + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef linux + +#include +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +#endif +#ifndef LINUX_VERSION_CODE +#define LINUX_VERSION_CODE KERNEL_VERSION(2,4,0)-1 +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) +#define TRY_TUNTAP 1 +#include +#include +#else +#include +#endif + +#endif /* linux */ + +#include "ax25ipd.h" + +#define ETAP_MTU_MIN 256 +#define ETAP_MTU 1024 +#define ETAP_MTU_MAX 1500 +#ifndef MAX_FRAME +#define MAX_FRAME 2048 +#endif + +#define ETHERTAP_HEADER_LEN_TUN 18 +#define ETHERTAP_HEADER_LEN_ETHERTAP 16 +#define ETHERTAP_HEADER_LEN_MAX ETHERTAP_HEADER_LEN_TUN + +struct ethertap_packet { + char ethernet_header[18]; + char data[MAX_FRAME]; +}; + +unsigned char hwaddr_remote[6]; + +static int ethertap_header_len; + +/*---------------------------------------------------------------------------*/ + +int send_bpq(unsigned char *buf, int l) +{ + struct ethertap_packet ethertap_packet; + void *addr = ðertap_packet; + int offset = ETHERTAP_HEADER_LEN_MAX - ethertap_header_len; + + static const unsigned char ethernet_header[18] = { + 0x00, 0x00, 0x00, 0x02, /* ??? ??? ETH_P_AX25 (16bit) */ + 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, /* Destination address (kernel et +hertap module) */ + 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, /* Source address (WAMPES etherta +p module) */ + 0x08, 0xff /* Protocol (bpqether) */ + }; + + if (l <= 0) + return -1; + + if (l > (sizeof(ethertap_packet.data) - 2)) + l = sizeof(ethertap_packet.data); + memcpy(ethertap_packet.data + 2, buf, l); + memcpy(ethertap_packet.ethernet_header, (const char *) ethernet_header, sizeof(ethernet_header)); + memcpy(ethertap_packet.ethernet_header + 4, hwaddr_remote, 6); + memcpy(ethertap_packet.ethernet_header + 4 + 6 + 2, hwaddr_remote +2, 6 -2); + ethertap_packet.data[0] = (l + 5) % 256; + ethertap_packet.data[1] = (l + 5) / 256; + l += 2; + + //send_tty(addr + offset, l + sizeof(ethertap_packet.ethernet_header) - offset); + write(ttyfd, addr + offset, l + sizeof(ethertap_packet.ethernet_header) - offset); + return l; +} + +/*---------------------------------------------------------------------------*/ + +int receive_bpq(unsigned char *buf, int l) +{ + if ((l -= ethertap_header_len) <= 0 || + buf[ethertap_header_len-2] & 0xff != 0x08 || + buf[ethertap_header_len-1] & 0xff != 0xff) { + // not a bpqether packet. + // drop silently - ethernet.ax25 is not the only protocol spokenon ethernet ;) + return -1; + } + l -= 2; + if (l <= 0 && buf[ethertap_header_len] + buf[ethertap_header_len] * 256 - 5 != l) { + // length error in bpqether packet + return 0; + } + + from_kiss(buf + ethertap_header_len + 2, l); + return l; +} + +/*---------------------------------------------------------------------------*/ +/* TUN/TAP support for linux. ethertap is obsolete */ + +#ifdef TRY_TUNTAP +static 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; + if (*dev) { + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ-1] = 0; + } + + if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) { + close(fd); + return err; + } + strcpy(dev, ifr.ifr_name); + + /* persist mode */ + //if (ioctl(fd, TUNSETPERSIST, 1) < 0) + //perror("TUNSETPERSIST"); + + /* don't checksum */ + //if (ioctl(fd, TUNSETNOCSUM, 1) < 0) + //perror("TUNSETNOCSUM"); + + return fd; +} +#endif + +/*---------------------------------------------------------------------------*/ + +int open_ethertap(char *ifname) +{ + + int fd; + char devname[PATH_MAX]; + struct ifreq ifr; + struct stat statbuf; + int tuntap = 0; + int mtu = 0; + int skfd; + + strcpy(devname, "/dev/"); + strncpy(devname + 5, ifname, sizeof(devname) - 5 -1); + devname[sizeof(devname) -1] = 0; + + // tuntap == 0: original ethertap. works on a real character device + if (!stat(devname, &statbuf)) { + fd = open(devname, O_RDWR); + if (fd < 0) { + LOGL2("%s: %s\n", devname, strerror(errno)); + return -1; + } +#ifdef TRY_TUNTAP + } else { + strcpy(devname, ifname); + if ((fd = tun_alloc(devname)) < 0) { + LOGL2("%s: %s\n", devname, strerror(errno)); + return -1; + } + ifname = devname; + tuntap = 1; +#endif + } + ethertap_header_len = (tuntap ? ETHERTAP_HEADER_LEN_TUN : ETHERTAP_HEADER_LEN_ETHERTAP); + + if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket()"); + close(fd); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ-1] = 0; + if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) + perror("SIOCGIFFLAGS"); + + ifr.ifr_flags |= (IFF_UP | IFF_NOARP); + if (ioctl(skfd, SIOCSIFFLAGS, &ifr) < 0) + perror("SIOCSIFFLAGS"); + + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ-1] = 0; + if (ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) { + perror("SIOCGIFHWADDR"); + hwaddr_remote[0] = 0xfe; + hwaddr_remote[1] = 0xfd; + hwaddr_remote[2] = 0x0; + hwaddr_remote[3] = 0x0; + hwaddr_remote[4] = 0x0; + hwaddr_remote[5] = 0x0; + } else + memcpy(hwaddr_remote, ifr.ifr_hwaddr.sa_data, 6); + + if (ttyspeed > ETAP_MTU_MIN && ttyspeed != 9600) { + mtu = ttyspeed; + } else { + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ-1] = 0; + strcpy(ifr.ifr_name, ifname); + if (ioctl(skfd, SIOCGIFMTU, &ifr) < 0) + perror("SIOCGIFMTU"); + else + mtu = ifr.ifr_mtu; + } + if (mtu < ETAP_MTU_MIN || mtu > ETAP_MTU_MAX) + mtu = ETAP_MTU; + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ-1] = 0; + ifr.ifr_mtu = mtu; + if (ioctl(skfd, SIOCSIFMTU, &ifr) < 0) + perror("SIOSGIFMTU"); + + close(skfd); + + return fd; + +} + +/*---------------------------------------------------------------------------*/ + +int set_bpq_dev_call_and_up(char *ifname) +{ + FILE * fp; + char buf[1024]; + char bpq_name[IFNAMSIZ]; + char dev_name[IFNAMSIZ]; + struct ifreq ifr; + struct sockaddr *sa; + int drop; + int skfd; + int len; + int err = -1; + + // 1. find correspondent bpqether device in /proc (i.e. bpq0 for tap0) + // 2. set the ax25 call of bpq0 (in this example) + // 3. ifup bpq0 + + if (!(fp = fopen("/proc/net/bpqether", "r"))) { + perror("/proc/net/bpqether"); + return -1; + } + + // look up bpqether devce in /proc: + // dev ether destination accept from + // bpq0 tap0 FF:FF:FF:FF:FF:FF * + + drop = 1; + *bpq_name = *dev_name = 0; + while (fgets(buf, sizeof(buf), fp)) { + if (drop) { + // header line + drop = 0; + continue; + } + sscanf(buf, "%s %s ", bpq_name, dev_name); + if (!strcmp(dev_name, ifname)) + break; + *bpq_name = *dev_name = 0; + } + fclose(fp); + if (!*bpq_name) { + LOGL2("Did not found bpqether device for %s in /proc/net/bpqether: %s", ifr.ifr_name, strerror(errno)); + return -1; + } + + LOGL1("found bpq device %s for %s\n", bpq_name, dev_name); + + if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket()"); + return -1; + } + memset(&ifr, 0, sizeof(ifr)); + memcpy(ifr.ifr_name, bpq_name, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ-1] = 0; + + ifr.ifr_hwaddr.sa_family = PF_AX25; + memcpy(ifr.ifr_hwaddr.sa_data, mycallsign, 7); + + if (ioctl(skfd, SIOCSIFHWADDR, &ifr) < 0) { + perror("SIOCSIFHWADDR"); + close(skfd); + return -1; + } + strncpy(ifr.ifr_name, bpq_name, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ-1] = 0; + ifr.ifr_mtu = 256; + if (ioctl(skfd, SIOCSIFMTU, &ifr) < 0) + perror("SIOSGIFMTU"); + + strncpy(ifr.ifr_name, bpq_name, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ-1] = 0; + if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) { + perror("SIOCGIFFLAGS"); + ifr.ifr_flags = 0; + } + + ifr.ifr_flags |= (IFF_UP); + if (ioctl(skfd, SIOCSIFFLAGS, &ifr) < 0) { + perror("SIOCSIFFLAGS"); + err = -1; + } + + close(skfd); + + return err; +} diff --git a/ax25ipd/config.c b/ax25ipd/config.c index d78a333..e67ffcf 100644 --- a/ax25ipd/config.c +++ b/ax25ipd/config.c @@ -148,6 +148,7 @@ char *f; exit(1); } } + fclose(cf); } /* Process each line from the config file. The return value is encoded. */ diff --git a/ax25ipd/io.c b/ax25ipd/io.c index 9c59ac1..914255a 100644 --- a/ax25ipd/io.c +++ b/ax25ipd/io.c @@ -69,6 +69,8 @@ int fromlen; time_t last_bc_time; +int ttyfd_bpq = 0; + /* * I/O modes for the io_error routine */ @@ -187,7 +189,7 @@ void io_open() } } - ttyfd = open(ttydevice, O_RDWR, 0); + ttyfd = ((ttyfd_bpq = (strchr(ttydevice, '/') ? 0 : 1)) ? open_ethertap(ttydevice) : open(ttydevice, O_RDWR, 0)); if (ttyfd < 0) { perror("opening tty device"); exit(1); @@ -196,6 +198,10 @@ void io_open() perror("setting non-blocking I/O on tty device"); exit(1); } + if (ttyfd_bpq) { + set_bpq_dev_call_and_up(ttydevice); + goto behind_normal_tty; + } #ifdef USE_TERMIOS if (ioctl(ttyfd, TCGETS, &nterm) < 0) { #endif @@ -287,6 +293,8 @@ void io_open() if (digi) send_params(); +behind_normal_tty: + last_bc_time = 0; /* force immediate id */ } @@ -354,14 +362,24 @@ void io_start() { } while (io_error(n, buf, n, READ_MSG, TTY_MODE)); LOGL4("ttydata l=%d\n", n); - if (n > 0) - assemble_kiss(buf, n); + if (n > 0) { + if (!ttyfd_bpq) { + assemble_kiss(buf, n); + } else { + // no crc on bpqether. but a MAC header + if (receive_bpq(buf, n) < 0) { + goto out_ttyfd; + } + } + } + /* * If we are in "beacon after" mode, reset the "last_bc_time" each time * we hear something on the channel. */ if (!bc_every) last_bc_time = time(NULL); +out_ttyfd: } if (udp_mode) { diff --git a/ax25ipd/process.c b/ax25ipd/process.c index 0b1308c..34c0fc7 100644 --- a/ax25ipd/process.c +++ b/ax25ipd/process.c @@ -198,7 +198,11 @@ int l; } #endif } /* end of tnc mode */ - send_kiss(port, buf, l); + if (!ttyfd_bpq) + send_kiss(port, buf, l); + else { + send_bpq(buf, l); + } } /* @@ -240,7 +244,11 @@ void do_beacon() if (loglevel > 2) dump_ax25frame("do_beacon: ", bcbuf, bclen); stats.kiss_beacon_outs++; - send_kiss(0, bcbuf, bclen); + if (!ttyfd_bpq) + send_kiss(0, bcbuf, bclen); + else { + send_bpq(bcbuf, bclen); + } } /* -- cgit v1.2.3