diff --git a/Constants.h b/Constants.h index fd366ee..149a711 100644 --- a/Constants.h +++ b/Constants.h @@ -6,10 +6,4 @@ #define MTU_MIN 74 #define MTU_MAX 1522 -#define MTU_DEFAULT 329 - -#define TXQUEUELEN 10 - -// ARP timings, in seconds -#define ARP_BASE_REACHABLE_TIME 300 -#define ARP_RETRANS_TIME 5 \ No newline at end of file +#define MTU_DEFAULT 329 \ No newline at end of file diff --git a/KISS.c b/KISS.c index 40fc495..ff72b1d 100644 --- a/KISS.c +++ b/KISS.c @@ -13,73 +13,69 @@ uint8_t frame_buffer[MAX_PAYLOAD]; uint8_t write_buffer[MAX_PAYLOAD*2+3]; extern bool verbose; -extern bool daemonize; extern int attached_if; -extern int device_type; extern void cleanup(void); void kiss_frame_received(int frame_len) { - if ( (device_type == IF_TUN && frame_len >= TUN_MIN_FRAME_SIZE) || (device_type == IF_TAP && frame_len >= ETHERNET_MIN_FRAME_SIZE) ) { - int written = write(attached_if, frame_buffer, frame_len); - if (written == -1) { - if (verbose && !daemonize) printf("Could not write received KISS frame (%d bytes) to network interface, is the interface up?\r\n", frame_len); - } else if (written != frame_len) { - if (!daemonize) printf("Error: Could only write %d of %d bytes to interface", written, frame_len); - cleanup(); - exit(1); - } - if (verbose && !daemonize) printf("Got %d bytes from TNC, wrote %d bytes to interface\r\n", frame_len, written); - } + if (verbose) printf("Got KISS frame\r\n"); + int written = write(attached_if, frame_buffer, frame_len); + if (written == -1) { + if (verbose) printf("Could not write received KISS frame to network interface, is the interface up?\r\n"); + } else if (written != frame_len) { + printf("Error: Could only write %d of %d bytes to interface", written, frame_len); + cleanup(); + exit(1); + } } void kiss_serial_read(uint8_t sbyte) { - if (IN_FRAME && sbyte == FEND && kiss_command == CMD_DATA) { - IN_FRAME = false; - kiss_frame_received(frame_len); - } else if (sbyte == FEND) { - IN_FRAME = true; - kiss_command = CMD_UNKNOWN; - frame_len = 0; - } else if (IN_FRAME && frame_len < MAX_PAYLOAD) { - // Have a look at the command byte first - if (frame_len == 0 && kiss_command == CMD_UNKNOWN) { - // Strip of port nibble - kiss_command = sbyte & 0x0F; - } else if (kiss_command == CMD_DATA) { - if (sbyte == FESC) { - ESCAPE = true; - } else { - if (ESCAPE) { - if (sbyte == TFEND) sbyte = FEND; - if (sbyte == TFESC) sbyte = FESC; - ESCAPE = false; - } + if (IN_FRAME && sbyte == FEND && kiss_command == CMD_DATA) { + IN_FRAME = false; + kiss_frame_received(frame_len); + } else if (sbyte == FEND) { + IN_FRAME = true; + kiss_command = CMD_UNKNOWN; + frame_len = 0; + } else if (IN_FRAME && frame_len < MAX_PAYLOAD) { + // Have a look at the command byte first + if (frame_len == 0 && kiss_command == CMD_UNKNOWN) { + // Strip of port nibble + kiss_command = sbyte & 0x0F; + } else if (kiss_command == CMD_DATA) { + if (sbyte == FESC) { + ESCAPE = true; + } else { + if (ESCAPE) { + if (sbyte == TFEND) sbyte = FEND; + if (sbyte == TFESC) sbyte = FESC; + ESCAPE = false; + } - if (frame_len < MAX_PAYLOAD) { - frame_buffer[frame_len++] = sbyte; - } - } - } - } + if (frame_len < MAX_PAYLOAD) { + frame_buffer[frame_len++] = sbyte; + } + } + } + } } int kiss_write_frame(int serial_port, uint8_t* buffer, int frame_len) { - int write_len = 0; - write_buffer[write_len++] = FEND; - write_buffer[write_len++] = CMD_DATA; - for (int i = 0; i < frame_len; i++) { - uint8_t byte = buffer[i]; - if (byte == FEND) { - write_buffer[write_len++] = FESC; - write_buffer[write_len++] = TFEND; - } else if (byte == FESC) { - write_buffer[write_len++] = FESC; - write_buffer[write_len++] = TFESC; - } else { - write_buffer[write_len++] = byte; - } - } - write_buffer[write_len++] = FEND; + int write_len = 0; + write_buffer[write_len++] = FEND; + write_buffer[write_len++] = CMD_DATA; + for (int i = 0; i < frame_len; i++) { + uint8_t byte = buffer[i]; + if (byte == FEND) { + write_buffer[write_len++] = FESC; + write_buffer[write_len++] = TFEND; + } else if (byte == FESC) { + write_buffer[write_len++] = FESC; + write_buffer[write_len++] = TFESC; + } else { + write_buffer[write_len++] = byte; + } + } + write_buffer[write_len++] = FEND; - return write(serial_port, write_buffer, write_len); + return write(serial_port, write_buffer, write_len); } \ No newline at end of file diff --git a/README.md b/README.md index 943d8ed..17b0b7d 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,7 @@ Attach KISS TNC devices as network interfaces in Linux. This program allows you ## Installation -Currently it is recommended to compile and install __tncattach__ from source with the below commands. - -If that is not possible for you, precompiled __amd64__ and __armhf__ (Raspberry Pi and similar) binaries have been provided in the releases section. You can [download the latest release here](https://github.com/markqvist/tncattach/releases). +Currently it is recommended to compile and install __tncattach__ from source with the below commands. If that is not possible for you, precompiled __amd64__ and __armhf__ (Raspberry Pi) binaries exist in the releases section. ```sh # If you don't already have a compiler installed @@ -34,17 +32,12 @@ Usage: tncattach [OPTION...] port baudrate Attach TNC devices as system network interfaces - -m, --mtu=MTU Specify interface MTU + -d, --daemon Run tncattach as a daemon -e, --ethernet Create a full ethernet device -i, --ipv4=IP_ADDRESS Configure an IPv4 address on interface + -m, --mtu=MTU Specify interface MTU -n, --noipv6 Filter IPv6 traffic from reaching TNC --noup Only create interface, don't bring it up - -T, --kisstcp Use KISS over TCP instead of serial port - -H, --tcphost=TCP_HOST Host to connect to when using KISS over TCP - -P, --tcpport=TCP_PORT TCP port when using KISS over TCP - -t, --interval=SECONDS Maximum interval between station identifications - -s, --id=CALLSIGN Station identification data - -d, --daemon Run tncattach as a daemon -v, --verbose Enable verbose output -?, --help Give this help list --usage Give a short usage message @@ -53,24 +46,10 @@ Attach TNC devices as system network interfaces The program supports attaching TNCs as point-to-point tunnel devices, or generic ethernet devices. The ethernet mode is suitable for point-to-multipoint setups, and can be enabled with the corresponding command line switch. If you only need point-to-point links, it is advisable to just use the standard point-to-point mode, since it doesn't incur the ethernet header overhead on each packet. -If you want to connect to a virtual KISS TNC over a TCP connection, you can use the -T option, along with the -H and -P options to specify the host and port. - Additionally, it is worth noting that __tncattach__ can filter out IPv6 packets from reaching the TNC. Most operating systems attempts to autoconfigure IPv6 when an interface is brought up, which results in a substantial amount of IPv6 traffic generated by router solicitations and similar, which is usually unwanted for packet radio links and similar. If you intend to use __tncattach__ on a system with mDNS services enabled (avahi-daemon, for example), you may want to consider modifying your mDNS setup to exclude TNC interfaces, or turning it off entirely, since it will generate a lot of traffic that might be unwanted. -## Station Identification - -You can configure tncattach to automatically transmit station identification beacons according to a given interval, by using the --id and --interval options. Identification will be transmitted as raw data frames with whatever content has been specified in the --id option. Useful for amateur radio use, or other areas where station identification is necessary. - -Identification beacons will be transmitted when: - - - There is outgoing data to send, and the specified interval has elapsed. - - The specified interval elapses, and data has been sent since the last ID beacon. - - The program exits, if any data frames have been transmitted since the last ID beacon. - -The above methodology should comply with station identification rules for amateur radio in most parts of the world, and complies with US Part 97 rules. - ## Examples Create an ethernet device with a USB-connected TNC, set the MTU, filter IPv6 traffic, and set an IPv4 address: @@ -80,13 +59,6 @@ Create an ethernet device with a USB-connected TNC, set the MTU, filter IPv6 tra sudo tncattach /dev/ttyUSB0 115200 --ethernet --mtu 576 --noipv6 --ipv4 10.92.0.10/24 ``` -Create an ethernet device with a TCP-connected TNC, set the MTU, filter IPv6 traffic, and set an IPv4 address: - -```sh -# Attach interface -sudo tncattach -T -H localhost -P 8001 --ethernet --mtu 576 --noipv6 --ipv4 10.92.0.10/24 -``` - You can interact with the interface like any other using the __ip__ or __ifconfig__ utilities: ```sh @@ -123,21 +95,3 @@ tnc0: flags=4305 mtu 400 TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 ``` - -## Worth Knowing on Raspbian - -On some versions of Raspbian (and probably other operating systems), the DHCP client daemon _dhcpcd_ interferes with TNC interfaces, by overriding their MTU and trying to auto-configure link-local addresses. You probably don't want this, and it can be disabled by editing the __/etc/dhcpcd.conf__ file, adding a statement telling _dhcpcd_ to ignore your TNC interface: - -``` -# Add the following statement somewhere at the beginning -# of /etc/dhcpcd.conf to prevent dhcpcd from changing MTU - -denyinterfaces tnc0 -``` - -## Support tncattach development -You can help support the continued development of open, free and private communications systems by donating via one of the following channels: - -- Ethereum: 0x81F7B979fEa6134bA9FD5c701b3501A2e61E897a -- Bitcoin: 3CPmacGm34qYvR6XWLVEJmi2aNe3PZqUuq -- Ko-Fi: https://ko-fi.com/markqvist diff --git a/Serial.c b/Serial.c index 09b0fa8..ef91c03 100644 --- a/Serial.c +++ b/Serial.c @@ -3,172 +3,172 @@ extern void cleanup(); int open_port(char* port) { - int fd; - fd = open(port, O_RDWR | O_NOCTTY | O_SYNC | O_NDELAY); + int fd; + fd = open(port, O_RDWR | O_NOCTTY | O_SYNC | O_NDELAY); - if (fd == -1) { - perror("The serial port could not be opened"); - cleanup(); - exit(1); - } else { - fcntl(fd, F_SETFL, 0); - } + if (fd == -1) { + perror("The serial port could not be opened"); + cleanup(); + exit(1); + } else { + fcntl(fd, F_SETFL, 0); + } - return fd; + return fd; } int close_port(int fd) { - return close(fd); + return close(fd); } void set_speed(void *tty_s, int speed) { - cfsetospeed(tty_s, speed); - cfsetispeed(tty_s, speed); + cfsetospeed(tty_s, speed); + cfsetispeed(tty_s, speed); } bool setup_port(int fd, int speed) { - struct termios tty; - if (tcgetattr(fd, &tty) != 0) { - perror("Error setting port speed, could not read port parameters"); - return false; - } + struct termios tty; + if (tcgetattr(fd, &tty) != 0) { + perror("Error setting port speed, could not read port parameters"); + return false; + } - switch (speed) { - case 0: - set_speed(&tty, B0); - break; - case 50: - set_speed(&tty, B50); - break; - case 75: - set_speed(&tty, B75); - break; - case 110: - set_speed(&tty, B110); - break; - case 134: - set_speed(&tty, B134); - break; - case 150: - set_speed(&tty, B150); - break; - case 200: - set_speed(&tty, B200); - break; - case 300: - set_speed(&tty, B300); - break; - case 600: - set_speed(&tty, B600); - break; - case 1200: - set_speed(&tty, B1200); - break; - case 2400: - set_speed(&tty, B2400); - break; - case 4800: - set_speed(&tty, B4800); - break; - case 9600: - set_speed(&tty, B9600); - break; - case 19200: - set_speed(&tty, B19200); - break; - case 38400: - set_speed(&tty, B38400); - break; - case 57600: - set_speed(&tty, B57600); - break; - case 115200: - set_speed(&tty, B115200); - break; - case 230400: - set_speed(&tty, B230400); - break; - default: - printf("Error: Invalid port speed %d specified\r\n", speed); - cleanup(); - exit(1); - return false; - } + switch (speed) { + case 0: + set_speed(&tty, B0); + break; + case 50: + set_speed(&tty, B50); + break; + case 75: + set_speed(&tty, B75); + break; + case 110: + set_speed(&tty, B110); + break; + case 134: + set_speed(&tty, B134); + break; + case 150: + set_speed(&tty, B150); + break; + case 200: + set_speed(&tty, B200); + break; + case 300: + set_speed(&tty, B300); + break; + case 600: + set_speed(&tty, B600); + break; + case 1200: + set_speed(&tty, B1200); + break; + case 2400: + set_speed(&tty, B2400); + break; + case 4800: + set_speed(&tty, B4800); + break; + case 9600: + set_speed(&tty, B9600); + break; + case 19200: + set_speed(&tty, B19200); + break; + case 38400: + set_speed(&tty, B38400); + break; + case 57600: + set_speed(&tty, B57600); + break; + case 115200: + set_speed(&tty, B115200); + break; + case 230400: + set_speed(&tty, B230400); + break; + default: + printf("Error: Invalid port speed %d specified\r\n", speed); + cleanup(); + exit(1); + return false; + } - // Set 8-bit characters, no parity, one stop bit - tty.c_cflag |= CS8; - tty.c_cflag &= ~PARENB; - tty.c_cflag &= ~CSTOPB; + // Set 8-bit characters, no parity, one stop bit + tty.c_cflag |= CS8; + tty.c_cflag &= ~PARENB; + tty.c_cflag &= ~CSTOPB; - // Disable hardware flow control - tty.c_cflag &= ~CRTSCTS; + // Disable hardware flow control + tty.c_cflag &= ~CRTSCTS; - // Enable reading and ignore modem - // control lines - tty.c_cflag |= CREAD | CLOCAL; + // Enable reading and ignore modem + // control lines + tty.c_cflag |= CREAD | CLOCAL; - // Disable canonical mode, echo - // and signal characters. - tty.c_lflag &= ~ICANON; - tty.c_lflag &= ~ECHO; - tty.c_lflag &= ~ECHOE; - tty.c_lflag &= ~ECHONL; - tty.c_lflag &= ~ISIG; + // Disable canonical mode, echo + // and signal characters. + tty.c_lflag &= ~ICANON; + tty.c_lflag &= ~ECHO; + tty.c_lflag &= ~ECHOE; + tty.c_lflag &= ~ECHONL; + tty.c_lflag &= ~ISIG; - // Disable processing of input, - // just pass the raw data. - tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); + // Disable processing of input, + // just pass the raw data. + tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); - // Disable XON/XOFF software flow control. - tty.c_iflag &= ~(IXON | IXOFF | IXANY); + // Disable XON/XOFF software flow control. + tty.c_iflag &= ~(IXON | IXOFF | IXANY); - // Disable processing output bytes - // and new line conversions - tty.c_oflag &= ~OPOST; - tty.c_oflag &= ~ONLCR; + // Disable processing output bytes + // and new line conversions + tty.c_oflag &= ~OPOST; + tty.c_oflag &= ~ONLCR; - // Block forever until at least one byte is read. - tty.c_cc[VMIN] = 1; - tty.c_cc[VTIME] = 0; + // Block forever until at least one byte is read. + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; - // TODO: Check these - // Prevent conversion of tabs to spaces (NOT PRESENT IN LINUX) - // tty.c_oflag &= ~OXTABS; - // Prevent removal of C-d chars (0x004) in output (NOT PRESENT IN LINUX) - // tty.c_oflag &= ~ONOEOT; + // TODO: Check these + // Prevent conversion of tabs to spaces (NOT PRESENT IN LINUX) + // tty.c_oflag &= ~OXTABS; + // Prevent removal of C-d chars (0x004) in output (NOT PRESENT IN LINUX) + // tty.c_oflag &= ~ONOEOT; - if (tcsetattr(fd, TCSANOW, &tty) != 0) { - perror("Could not configure serial port parameters"); - return false; - } else { - return true; - } + if (tcsetattr(fd, TCSANOW, &tty) != 0) { + perror("Could not configure serial port parameters"); + return false; + } else { + return true; + } } bool set_port_blocking(int fd, bool should_block) { - struct termios tty; - memset(&tty, 0, sizeof tty); + struct termios tty; + memset(&tty, 0, sizeof tty); - if (tcgetattr(fd, &tty) != 0) { - perror("Error configuring port blocking behaviour, could not read port parameters"); - return false; - } else { - // TODO: Implement this correctly - if (should_block) { - // Block forever until at least one byte is read. - tty.c_cc[VMIN] = 1; - tty.c_cc[VTIME] = 0; - } else { - // Never block, always return immediately with - // whatever is available. - tty.c_cc[VMIN] = 0; - tty.c_cc[VTIME] = 0; - } - if (tcsetattr(fd, TCSANOW, &tty) != 0) { - perror("Could not set port parameters while configuring blocking behaviour"); - return false; - } else { - return true; - } - } + if (tcgetattr(fd, &tty) != 0) { + perror("Error configuring port blocking behaviour, could not read port parameters"); + return false; + } else { + // TODO: Implement this correctly + if (should_block) { + // Block forever until at least one byte is read. + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; + } else { + // Never block, always return immediately with + // whatever is available. + tty.c_cc[VMIN] = 0; + tty.c_cc[VTIME] = 0; + } + if (tcsetattr(fd, TCSANOW, &tty) != 0) { + perror("Could not set port parameters while configuring blocking behaviour"); + return false; + } else { + return true; + } + } } \ No newline at end of file diff --git a/TAP.c b/TAP.c index 289fd76..1b6c2fd 100644 --- a/TAP.c +++ b/TAP.c @@ -5,300 +5,128 @@ char tap_name[IFNAMSIZ]; extern bool verbose; extern bool noipv6; extern bool set_ipv4; -extern bool set_ipv6; -extern bool set_linklocal; extern bool set_netmask; extern bool noup; extern int mtu; extern int device_type; extern char if_name[IFNAMSIZ]; extern char* ipv4_addr; -extern char* ipv6_addr; -extern long ipv6_prefixLen; extern char* netmask; extern void cleanup(); - -void trySixSet -( - int interfaceIndex, - struct in6_addr address, - int prefixLen -) -{ - char ip_str[INET6_ADDRSTRLEN+1]; - inet_ntop(AF_INET6, &address, ip_str, INET6_ADDRSTRLEN+1); - - printf - ( - "Adding IPv6 address of '%s/%d' to interface at if_index %d\n", - ip_str, - prefixLen, - interfaceIndex - ); - - int inet6 = socket(AF_INET6, SOCK_DGRAM, 0); - if(inet6 < 0) - { - printf("Error opening control socket for adding IPv6 address to interface\n"); - cleanup(); - exit(1); - } - - struct in6_ifreq paramReq; - memset(¶mReq, 0, sizeof(struct in6_ifreq)); - paramReq.ifr6_ifindex = interfaceIndex; - paramReq.ifr6_prefixlen = prefixLen; - paramReq.ifr6_addr = address; - - - // Try add the address - if(ioctl(inet6, SIOCSIFADDR, ¶mReq) < 0) - { - printf - ( - "There was an errror assigning address '%s/%d' to if_index %d\n", - ip_str, - prefixLen, - interfaceIndex - ); - cleanup(); - close(inet6); - exit(1); - } - - printf("Address '%s/%d' added\n", ip_str, prefixLen); - close(inet6); -} - int open_tap(void) { - struct ifreq ifr; - int fd = open("/dev/net/tun", O_RDWR); + struct ifreq ifr; + int fd = open("/dev/net/tun", O_RDWR); - if (fd < 0) { - perror("Could not open clone device"); - exit(1); - } else { - memset(&ifr, 0, sizeof(ifr)); - // TODO: Enable PI header again? + if (fd < 0) { + perror("Could not open clone device"); + exit(1); + } else { + memset(&ifr, 0, sizeof(ifr)); + // TODO: Enable PI header again? - if (device_type == IF_TAP) { - ifr.ifr_flags = IFF_TAP | IFF_NO_PI; - } else if (device_type == IF_TUN) { - ifr.ifr_flags = IFF_TUN; - } else { - printf("Error: Unsupported interface type\r\n"); - cleanup(); - exit(1); - } + if (device_type == IF_TAP) { + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + } else if (device_type == IF_TUN) { + ifr.ifr_flags = IFF_TUN; + } else { + printf("Error: Unsupported interface type\r\n"); + cleanup(); + exit(1); + } - strcpy(tap_name, "tnc%d"); - strncpy(ifr.ifr_name, tap_name, IFNAMSIZ); + strcpy(tap_name, "tnc%d"); + strncpy(ifr.ifr_name, tap_name, IFNAMSIZ); - if (ioctl(fd, TUNSETIFF, &ifr) < 0) { - perror("Could not configure network interface"); - exit(1); - } else { - strcpy(if_name, ifr.ifr_name); + if (ioctl(fd, TUNSETIFF, &ifr) < 0) { + perror("Could not configure network interface"); + exit(1); + } else { + strcpy(if_name, ifr.ifr_name); + + int inet = socket(AF_INET, SOCK_DGRAM, 0); + if (inet == -1) { + perror("Could not open AF_INET socket\r\n"); + cleanup(); + exit(1); + } else { + if (ioctl(inet, SIOCGIFFLAGS, &ifr) < 0) { + perror("Could not get interface flags from kernel"); + close(inet); + cleanup(); + exit(1); + } else { + ifr.ifr_mtu = mtu; + if (ioctl(inet, SIOCSIFMTU, &ifr) < 0) { + perror("Could not configure interface MTU"); + close(inet); + cleanup(); + exit(1); + } - - int inet = socket(AF_INET, SOCK_DGRAM, 0); - if (inet == -1) { - perror("Could not open control socket"); - cleanup(); - exit(1); - } else { - if (ioctl(inet, SIOCGIFMTU, &ifr) < 0) { - perror("Could not get interface flags from kernel"); - close(inet); - cleanup(); - exit(1); - } else { - ifr.ifr_mtu = mtu; - if (ioctl(inet, SIOCSIFMTU, &ifr) < 0) { - perror("Could not configure interface MTU"); - close(inet); - cleanup(); - exit(1); - } + if (!noup) { + ifr.ifr_flags |= IFF_UP | IFF_RUNNING; + if (ioctl(inet, SIOCSIFFLAGS, &ifr) < 0) { + perror("Could not bring up interface"); + close(inet); + cleanup(); + exit(1); + } else { + if (set_ipv4) { + struct ifreq a_ifr; + struct sockaddr_in addr, snm; - // Configure TX queue length - if (ioctl(inet, SIOCGIFTXQLEN, &ifr) < 0) { - perror("Could not get interface flags from kernel"); - close(inet); - cleanup(); - exit(1); - } else { - ifr.ifr_qlen = TXQUEUELEN; - if (ioctl(inet, SIOCSIFTXQLEN, &ifr) < 0) { - perror("Could not set interface TX queue length"); - close(inet); - cleanup(); - exit(1); - } - } + memset(&a_ifr, 0, sizeof(a_ifr)); + memset(&addr, 0, sizeof(addr)); + memset(&snm, 0, sizeof(addr)); + strncpy(a_ifr.ifr_name, ifr.ifr_name, IFNAMSIZ); + addr.sin_family = AF_INET; + snm.sin_family = AF_INET; - // Configure ARP characteristics - char path_buf[256]; - if (device_type == IF_TAP) { - snprintf(path_buf, sizeof(path_buf), "/proc/sys/net/ipv4/neigh/%s/base_reachable_time_ms", ifr.ifr_name); - int arp_fd = open(path_buf, O_WRONLY); - if (arp_fd < 0) { - perror("Could not open proc entry for ARP parameters"); - close(inet); - cleanup(); - exit(1); - } else { - if (dprintf(arp_fd, "%d", ARP_BASE_REACHABLE_TIME*1000) <= 0) { - perror("Could not configure interface ARP parameter base_reachable_time_ms"); - close(inet); - close(arp_fd); - cleanup(); - exit(1); - } else { - close(arp_fd); - } - } + int addr_conversion = inet_pton(AF_INET, ipv4_addr, &(addr.sin_addr)); + if (addr_conversion != 1) { + printf("Error: Invalid IPv4 address specified\r\n"); + close(inet); + cleanup(); + exit(1); + } else { + a_ifr.ifr_addr = *(struct sockaddr*)&addr; + if (ioctl(inet, SIOCSIFADDR, &a_ifr) < 0) { + perror("Could not set IP-address"); + close(inet); + cleanup(); + exit(1); + } else { + if (set_netmask) { + int snm_conversion = inet_pton(AF_INET, netmask, &(snm.sin_addr)); + if (snm_conversion != 1) { + printf("Error: Invalid subnet mask specified\r\n"); + close(inet); + cleanup(); + exit(1); + } else { + a_ifr.ifr_addr = *(struct sockaddr*)&snm; + if (ioctl(inet, SIOCSIFNETMASK, &a_ifr) < 0) { + perror("Could not set subnet mask"); + close(inet); + cleanup(); + exit(1); + } + } + } + } + } + } + } + } + } + } - snprintf(path_buf, sizeof(path_buf), "/proc/sys/net/ipv4/neigh/%s/retrans_time_ms", ifr.ifr_name); - arp_fd = open(path_buf, O_WRONLY); - if (arp_fd < 0) { - perror("Could not open proc entry for ARP parameters"); - close(inet); - cleanup(); - exit(1); - } else { - if (dprintf(arp_fd, "%d", ARP_RETRANS_TIME*1000) <= 0) { - perror("Could not configure interface ARP parameter retrans_time_ms"); - close(inet); - close(arp_fd); - cleanup(); - exit(1); - } else { - close(arp_fd); - } - } - } - - // Bring up if requested - if (!noup) { - if (ioctl(inet, SIOCGIFFLAGS, &ifr) < 0) { - perror("Could not get interface flags from kernel"); - close(inet); - cleanup(); - exit(1); - } else { - ifr.ifr_flags |= IFF_UP | IFF_RUNNING; - if (ioctl(inet, SIOCSIFFLAGS, &ifr) < 0) { - perror("Could not bring up interface"); - close(inet); - cleanup(); - exit(1); - } else { - if (set_ipv4) { - struct ifreq a_ifr; - struct sockaddr_in addr, snm; - - memset(&a_ifr, 0, sizeof(a_ifr)); - memset(&addr, 0, sizeof(addr)); - memset(&snm, 0, sizeof(addr)); - strncpy(a_ifr.ifr_name, ifr.ifr_name, IFNAMSIZ); - addr.sin_family = AF_INET; - snm.sin_family = AF_INET; - - int addr_conversion = inet_pton(AF_INET, ipv4_addr, &(addr.sin_addr)); - if (addr_conversion != 1) { - printf("Error: Invalid IPv4 address specified\r\n"); - close(inet); - cleanup(); - exit(1); - } else { - a_ifr.ifr_addr = *(struct sockaddr*)&addr; - if (ioctl(inet, SIOCSIFADDR, &a_ifr) < 0) { - perror("Could not set IP-address"); - close(inet); - cleanup(); - exit(1); - } else { - if (set_netmask) { - int snm_conversion = inet_pton(AF_INET, netmask, &(snm.sin_addr)); - if (snm_conversion != 1) { - printf("Error: Invalid subnet mask specified\r\n"); - close(inet); - cleanup(); - exit(1); - } else { - a_ifr.ifr_addr = *(struct sockaddr*)&snm; - if (ioctl(inet, SIOCSIFNETMASK, &a_ifr) < 0) { - perror("Could not set subnet mask"); - close(inet); - cleanup(); - exit(1); - } - } - } - } - } - } - - if(set_ipv6 || set_linklocal) - { - // Firstly, obtain the interface index by `ifr_name` - int inet6 = socket(AF_INET6, SOCK_DGRAM, 0); - if(inet6 < 0) - { - printf("Error opening control socket for adding IPv6 address to interface\n"); - cleanup(); - exit(1); - } - - if(ioctl(inet6, SIOCGIFINDEX, &ifr) < 0) - { - printf("Could not get interface index for interface '%s'\n", ifr.ifr_name); - close(inet6); - cleanup(); - exit(1); - } - - // if link-local was NOT requested and interface - // has been up'd -> then kernel would have added - // a link-local already, this removes it - if(!set_linklocal & !noup) - { - // TODO: Get all addresses that start with fe80 - } - // Else it could have been no-up; hence you will have to remove - // the link-local yourself - // Other else is link-local was requested, then we don't care (whether - // up'd or not as it will inevitably be added by the kernel) - - // Convert ASCII IPv6 address to ABI structure - struct in6_addr six_addr_itself; - memset(&six_addr_itself, 0, sizeof(struct in6_addr)); - if(inet_pton(AF_INET6, ipv6_addr, &six_addr_itself) < 0) - { - printf("Error parsing IPv6 address '%s'\n", ipv6_addr); - close(inet6); - cleanup(); - exit(1); - } - - // Add user's requested address - trySixSet(ifr.ifr_ifindex, six_addr_itself, ipv6_prefixLen); - - close(inet6); - } - } - } - } - } - } - - return fd; - } - } + return fd; + } + } } int close_tap(int tap_fd) { - return close(tap_fd); -} + return close(tap_fd); +} \ No newline at end of file diff --git a/TAP.h b/TAP.h index 07f003a..eeb6a14 100644 --- a/TAP.h +++ b/TAP.h @@ -9,7 +9,6 @@ #include #include #include -#include #include "Constants.h" int open_tap(void); diff --git a/TCP.c b/TCP.c deleted file mode 100644 index c8e3d52..0000000 --- a/TCP.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "TCP.h" - -int open_tcp(char* ip, int port) { - int sockfd = socket(AF_INET, SOCK_STREAM, 0); - - if (sockfd < 0) { - perror("Could not open AF_INET socket"); - exit(1); - } - - struct hostent *server; - struct sockaddr_in serv_addr; - - server = gethostbyname(ip); - - if (server == NULL) { - perror("Error resolving host"); - exit(1); - } - - memset(&serv_addr, 0, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - - memcpy(server->h_addr, &serv_addr.sin_addr.s_addr, server->h_length); - serv_addr.sin_port = htons(port); - - if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { - perror("Could not connect TCP socket"); - exit(1); - } - - return sockfd; -} - -int close_tcp(int fd) { - return close(fd); -} \ No newline at end of file diff --git a/TCP.h b/TCP.h deleted file mode 100644 index d386b59..0000000 --- a/TCP.h +++ /dev/null @@ -1,9 +0,0 @@ -#include -#include -#include -#include -#include -#include - -int open_tcp(char* ip, int port); -int close_tcp(int fd); \ No newline at end of file diff --git a/makefile b/makefile index 8d6ba66..f373c4f 100644 --- a/makefile +++ b/makefile @@ -1,35 +1,26 @@ .DEFAULT_GOAL := all .PHONY: all clean install uninstall tncattach -RM ?= rm -INSTALL ?= install -CC ?= gcc -CFLAGS ?= -Wall -std=gnu11 -static-libgcc -LDFLAGS ?= -PREFIX ?= /usr/local +compiler = gcc +flags = -std=gnu11 -lm all: tncattach rebuild: clean all clean: @echo "Cleaning tncattach build..." - $(RM) -f tncattach + @rm -f tncattach tncattach: @echo "Making tncattach..." - @echo "Compiling with: $(CC)" - $(CC) $(CFLAGS) $(LDFLAGS) tncattach.c Serial.c TCP.c KISS.c TAP.c -o tncattach + @echo "Compiling with: ${compiler}" + ${compiler} ${flags} tncattach.c Serial.c KISS.c TAP.c -o tncattach -Wall install: @echo "Installing tncattach..." - $(INSTALL) -d $(DESTDIR)/$(PREFIX)/bin - $(INSTALL) -Dm755 tncattach $(DESTDIR)/$(PREFIX)/bin/tncattach - @echo "Installing man page..." - gzip -9 tncattach.8 - $(INSTALL) -d $(DESTDIR)/$(PREFIX)/share/man/man8 - $(INSTALL) -Dm644 tncattach.8.gz $(DESTDIR)/$(PREFIX)/share/man/man8/tncattach.8.gz + chmod a+x tncattach + cp ./tncattach /usr/local/sbin/ uninstall: @echo "Uninstalling tncattach" - $(RM) $(DESTDIR)/$(PREFIX)/bin/tncattach - $(RM) $(DESTDIR)/$(PREFIX)/share/man/man8/tncattach.8.gz + rm /usr/local/sbin/tncattach \ No newline at end of file diff --git a/tncattach.8 b/tncattach.8 deleted file mode 100644 index f41390f..0000000 --- a/tncattach.8 +++ /dev/null @@ -1,165 +0,0 @@ -.TH tncattach 8 "September 12, 2020" - -.SH NAME -. -. -tncattach \- Attach TNC devices as system network interfaces - -.SH SYNOPSIS -. -. -\f[B]tncattach\f[R] [OPTION...] port baudrate - -.SH DESCRIPTION -Attach KISS TNC devices as network interfaces in Linux. This program allows you to attach TNCs or any KISS-compatible device as a network interface. This program does not need any kernel modules, and has no external dependencies outside the standard Linux and GNU C libraries. - -.SH OPTIONS -. -. -.TP -.BI \-m, \-\-mtu=MTU -. -Specify interface MTU -. -. -.TP -.BI \-e, \-\-ethernet -Create a full ethernet device -. -. -.TP -.BI \-i, \-\-ipv4=IP_ADDRESS -Configure an IPv4 address on interface -. -. -.TP -.BI \-n, \-\-noipv6 -Filter IPv6 traffic from reaching TNC -. -. -.TP -.BI \-\-noup -Only create interface, don't bring it up -. -. -.TP -.BI \-T, \-\-kisstcp -Use KISS over TCP instead of serial port -. -. -.TP -.BI \-H, \-\-tcphost=TCP_HOST -Host to connect to when using KISS over TCP -. -. -.TP -.BI \-P, \-\-tcpport=TCP_PORT -TCP port when using KISS over TCP -. -. -.TP -.BI \-t, \-\-interval=SECONDS -Maximum interval between station identifications -. -. -.TP -.B \-s, \-\-id=CALLSIGN -Station identification data -. -. -.TP -.BI \-d, \-\-daemon -Run tncattach as a daemon -. -. -.TP -.BI \-v, \-\-verbose -Enable verbose output -. -. -.TP -.BI \-?, \-\-help -Show help -. -. -.TP -.BI \-\-usage -Give a short usage message -. -. -.TP -.BI \-V, \-\-version -Print program version -. -. - -.SH USAGE -The program supports attaching TNCs as point-to-point tunnel devices, or generic ethernet devices. The ethernet mode is suitable for point-to-multipoint setups, and can be enabled with the corresponding command line switch. If you only need point-to-point links, it is advisable to just use the standard point-to-point mode, since it doesn't incur the ethernet header overhead on each packet. -.P -If you want to connect to a virtual KISS TNC over a TCP connection, you can use the -T option, along with the -H and -P options to specify the host and port. -.P -Additionally, it is worth noting that tncattach can filter out IPv6 packets from reaching the TNC. Most operating systems attempts to autoconfigure IPv6 when an interface is brought up, which results in a substantial amount of IPv6 traffic generated by router solicitations and similar, which is usually unwanted for packet radio links and similar. -.P -If you intend to use tncattach on a system with mDNS services enabled (avahi-daemon, for example), you may want to consider modifying your mDNS setup to exclude TNC interfaces, or turning it off entirely, since it will generate a lot of traffic that might be unwanted. - -.SH STATION IDENTIFICATION - -You can configure tncattach to automatically transmit station identification beacons according to a given interval, by using the --id and --interval options. Identification will be transmitted as raw data frames with whatever content has been specified in the --id option. Useful for amateur radio use, or other areas where station identification is necessary. -.P -Identification beacons will be transmitted when: -.P -.IP -There is outgoing data to send, and the specified interval has elapsed. -.IP -The specified interval elapses, and data has been sent since the last ID beacon. -.IP -The program exits, if any data frames have been transmitted since the last ID beacon. -.P -The above methodology should comply with station identification rules for amateur radio in most parts of the world, and complies with US Part 97 rules. - -.SH EXAMPLES -. -Create an ethernet device with a USB-connected TNC, set the MTU, filter IPv6 traffic, and set an IPv4 address: -.IP -sudo tncattach /dev/ttyUSB0 115200 --ethernet --mtu 576 --noipv6 --ipv4 10.92.0.10/24 -.P -Create an ethernet device with a TCP-connected TNC, set the MTU, filter IPv6 traffic, and set an IPv4 address: -.IP -sudo tncattach -T -H localhost -P 8001 --ethernet --mtu 576 --noipv6 --ipv4 10.92.0.10/24 -.P -You can interact with the interface like any other using the ip or ifconfig utilities. -.p -Check interface is running: -.P -# ifconfig -.br -tnc0: flags=4305 mtu 400 -.br - inet 10.93.0.1 netmask 255.255.255.255 destination 10.93.0.2 -.br - unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 500 (UNSPEC) -.br - RX packets 0 bytes 0 (0.0 B) -.br - RX errors 0 dropped 0 overruns 0 frame 0 -.br - TX packets 0 bytes 0 (0.0 B) -.br - TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 -.P -.SH WORTH KNOWING ON RASPBIAN -On some versions of Raspbian (and probably other operating systems), the DHCP client daemon dhcpcd interferes with TNC interfaces, by overriding their MTU and trying to auto-configure link-local addresses. You probably don't want this, and it can be disabled by editing the /etc/dhcpcd.conf file, adding a statement telling dhcpcd to ignore your TNC interface: -.P -# Add the following statement somewhere at the beginning -.br -# of /etc/dhcpcd.conf to prevent dhcpcd from changing MTU -.br -denyinterfaces tnc0 - -.SH SEE ALSO - -rnodeconfigutil(8) - -.SH AUTHOR - -Mark Qvist diff --git a/tncattach.c b/tncattach.c index 7d825b9..1c83782 100644 --- a/tncattach.c +++ b/tncattach.c @@ -5,11 +5,9 @@ #include #include #include -#include #include "Constants.h" #include "Serial.h" #include "KISS.h" -#include "TCP.h" #include "TAP.h" #define BAUDRATE_DEFAULT 0 @@ -34,569 +32,373 @@ bool noipv6 = false; bool noup = false; bool daemonize = false; bool set_ipv4 = false; -bool set_ipv6 = false; -bool set_linklocal = false; bool set_netmask = false; -bool kiss_over_tcp = false; char* ipv4_addr; char* netmask; - -char* ipv6_addr; -long ipv6_prefixLen; - -char* tcp_host; -int tcp_port; - int mtu; int device_type = IF_TUN; -char* id; -int id_interval = -1; -time_t last_id = 0; -bool tx_since_last_id = false; - void cleanup(void) { - if (kiss_over_tcp) { - close_tcp(attached_tnc); - } else { - close_port(attached_tnc); - } - close_tap(attached_if); -} - -bool is_ipv6(uint8_t* frame) { - if (device_type == IF_TAP) { - if (frame[12] == 0x86 && frame[13] == 0xdd) { - return true; - } else { - return false; - } - } else if (device_type == IF_TUN) { - if (frame[2] == 0x86 && frame[3] == 0xdd) { - return true; - } else { - return false; - } - } else { - printf("Error: Unsupported interface type\r\n"); - cleanup(); - exit(1); - } -} - -time_t time_now(void) { - time_t now = time(NULL); - if (now == -1) { - if (daemonize) { - syslog(LOG_ERR, "Could not get system time, exiting now"); - } else { - printf("Error: Could not get system time, exiting now\r\n"); - } - cleanup(); - exit(1); - } else { - return now; - } -} - -void transmit_id(void) { - time_t now = time(NULL); - int id_len = strlen(id); - if (verbose) { - if (!daemonize) { - printf("Transmitting %d bytes of identification data on %s: %s\r\n", id_len, if_name, id); - } - } - - uint8_t* id_frame = malloc(strlen(id)); - memcpy(id_frame, id, id_len); - kiss_write_frame(attached_tnc, id_frame, id_len); - last_id = now; - tx_since_last_id = false; - -} - -bool should_id(void) { - if (id_interval != -1) { - time_t now = time_now(); - return now > last_id + id_interval; - } else { - return false; - } + close_port(attached_tnc); + close_tap(attached_if); } void signal_handler(int signal) { - if (daemonize) syslog(LOG_NOTICE, "tncattach daemon exiting"); + if (daemonize) { + cleanup(); + syslog(LOG_NOTICE, "tncattach daemon exiting"); + exit(0); + } else { + cleanup(); + exit(0); + } +} - // Transmit final ID if necessary - if (id_interval != -1 && tx_since_last_id) transmit_id(); - - cleanup(); - exit(0); +bool is_ipv6(uint8_t* frame) { + if (device_type == IF_TAP) { + if (frame[12] == 0x86 && frame[13] == 0xdd) { + return true; + } else { + return false; + } + } else if (device_type == IF_TUN) { + if (frame[2] == 0x86 && frame[3] == 0xdd) { + return true; + } else { + return false; + } + } else { + printf("Error: Unsupported interface type\r\n"); + cleanup(); + exit(1); + } } void read_loop(void) { - bool should_continue = true; - int min_frame_size; - if (device_type == IF_TAP) { - min_frame_size = ETHERNET_MIN_FRAME_SIZE; - } else if (device_type == IF_TUN) { - min_frame_size = TUN_MIN_FRAME_SIZE; - } else { - if (daemonize) { - syslog(LOG_ERR, "Unsupported interface type"); - } else { - printf("Error: Unsupported interface type\r\n"); - } + bool should_continue = true; + int min_frame_size; + if (device_type == IF_TAP) { + min_frame_size = ETHERNET_MIN_FRAME_SIZE; + } else if (device_type == IF_TUN) { + min_frame_size = TUN_MIN_FRAME_SIZE; + } else { + if (daemonize) { + syslog(LOG_ERR, "Unsupported interface type"); + } else { + printf("Error: Unsupported interface type\r\n"); + } + + cleanup(); + exit(1); + } - cleanup(); - exit(1); - } + while (should_continue) { - int poll_timeout = 1000; - while (should_continue) { - int poll_result = poll(fds, 2, poll_timeout); - if (poll_result != -1) { - if (poll_result == 0) { - // No resources are ready for reading, - // run scheduled tasks instead. - if (id_interval != -1 && tx_since_last_id) { - time_t now = time_now(); - if (now > last_id + id_interval) transmit_id(); - } - } else { - for (int fdi = 0; fdi < N_FDS; fdi++) { - if (fds[fdi].revents != 0) { - // Check for hangup event - if (fds[fdi].revents & POLLHUP) { - if (fdi == IF_FD_INDEX) { - if (daemonize) { - syslog(LOG_ERR, "Received hangup from interface"); - } else { - printf("Received hangup from interface\r\n"); - } - cleanup(); - exit(1); - } - if (fdi == TNC_FD_INDEX) { - if (daemonize) { - syslog(LOG_ERR, "Received hangup from TNC"); - } else { - printf("Received hangup from TNC\r\n"); - } - cleanup(); - exit(1); - } - } + int poll_result = poll(fds, 2, -1); + if (poll_result != -1) { + for (int fdi = 0; fdi < N_FDS; fdi++) { + if (fds[fdi].revents != 0) { + // Check for hangup event + if (fds[fdi].revents & POLLHUP) { + if (fdi == IF_FD_INDEX) { + if (daemonize) { + syslog(LOG_ERR, "Received hangup from interface"); + } else { + printf("Received hangup from interface\r\n"); + } + cleanup(); + exit(1); + } + if (fdi == TNC_FD_INDEX) { + if (daemonize) { + syslog(LOG_ERR, "Received hangup from TNC"); + } else { + printf("Received hangup from TNC\r\n"); + } + cleanup(); + exit(1); + } + } - // Check for error event - if (fds[fdi].revents & POLLERR) { - if (fdi == IF_FD_INDEX) { - if (daemonize) { - syslog(LOG_ERR, "Received error event from interface"); - } else { - perror("Received error event from interface\r\n"); - } - cleanup(); - exit(1); - } - if (fdi == TNC_FD_INDEX) { - if (daemonize) { - syslog(LOG_ERR, "Received error event from TNC"); - } else { - perror("Received error event from TNC\r\n"); - } - cleanup(); - exit(1); - } - } + // Check for error event + if (fds[fdi].revents & POLLERR) { + if (fdi == IF_FD_INDEX) { + if (daemonize) { + syslog(LOG_ERR, "Received error event from interface"); + } else { + perror("Received error event from interface\r\n"); + } + cleanup(); + exit(1); + } + if (fdi == TNC_FD_INDEX) { + if (daemonize) { + syslog(LOG_ERR, "Received error event from TNC"); + } else { + perror("Received error event from TNC\r\n"); + } + cleanup(); + exit(1); + } + } - // If data is ready, read it - if (fds[fdi].revents & POLLIN) { - if (fdi == IF_FD_INDEX) { - int if_len = read(attached_if, if_buffer, sizeof(if_buffer)); - if (if_len > 0) { - if (if_len >= min_frame_size) { - if (!noipv6 || (noipv6 && !is_ipv6(if_buffer))) { + // If data is ready, read it + if (fds[fdi].revents & POLLIN) { + if (fdi == IF_FD_INDEX) { + int if_len = read(attached_if, if_buffer, sizeof(if_buffer)); + if (if_len > 0) { + if (if_len >= min_frame_size) { + if (!noipv6 || (noipv6 && !is_ipv6(if_buffer))) { + kiss_write_frame(attached_tnc, if_buffer, if_len); + } + } + } else { + if (daemonize) { + syslog(LOG_ERR, "Could not read from network interface, exiting now"); + } else { + printf("Error: Could not read from network interface, exiting now\r\n"); + } + cleanup(); + exit(1); + } + } - int tnc_written = kiss_write_frame(attached_tnc, if_buffer, if_len); - if (verbose && !daemonize) printf("Got %d bytes from interface, wrote %d bytes (KISS-framed and escaped) to TNC\r\n", if_len, tnc_written); - tx_since_last_id = true; - - if (should_id()) transmit_id(); - } - } - } else { - if (daemonize) { - syslog(LOG_ERR, "Could not read from network interface, exiting now"); - } else { - printf("Error: Could not read from network interface, exiting now\r\n"); - } - cleanup(); - exit(1); - } - } - - if (fdi == TNC_FD_INDEX) { - int tnc_len = read(attached_tnc, serial_buffer, sizeof(serial_buffer)); - if (tnc_len > 0) { - for (int i = 0; i < tnc_len; i++) { - kiss_serial_read(serial_buffer[i]); - } - } else { - if (daemonize) { - syslog(LOG_ERR, "Could not read from TNC, exiting now"); - } else { - printf("Error: Could not read from TNC, exiting now\r\n"); - } - - cleanup(); - exit(1); - } - } - } - } - } - } - } else { - should_continue = false; - } - } - cleanup(); - exit(1); + if (fdi == TNC_FD_INDEX) { + int tnc_len = read(attached_tnc, serial_buffer, sizeof(serial_buffer)); + if (tnc_len > 0) { + if (verbose) printf("Data from TNC: %d bytes.\r\n", tnc_len); + for (int i = 0; i < tnc_len; i++) { + kiss_serial_read(serial_buffer[i]); + } + } else { + if (daemonize) { + syslog(LOG_ERR, "Could not read from TNC, exiting now"); + } else { + printf("Error: Could not read from TNC, exiting now\r\n"); + } + + cleanup(); + exit(1); + } + } + } + } + } + } else { + should_continue = false; + } + } + cleanup(); + exit(1); } -const char *argp_program_version = "tncattach 0.1.9"; +const char *argp_program_version = "tncattach 0.1.2"; const char *argp_program_bug_address = ""; -static char doc[] = "\r\nAttach TNC devices as system network interfaces\vTo attach the TNC connected to /dev/ttyUSB0 as an ethernet device with an MTU of 512 bytes and assign an IPv4 address, while filtering IPv6 traffic, use:\r\n\r\n\ttncattach /dev/ttyUSB0 115200 -m 512 -e --noipv6 --ipv4 10.0.0.1/24\r\n\r\nStation identification can be performed automatically to comply with Part 97 rules. See the README for a complete description. Use the --id and --interval options, which should commonly be set to your callsign, and 600 seconds."; +static char doc[] = "\r\nAttach TNC devices as system network interfaces\vAs an example, to attach the TNC connected to /dev/ttyUSB0 as a full ethernet device with an MTU of 576 bytes and assign an IPv4 address, use the following command:\r\n\r\n\ttncattach /dev/ttyUSB0 115200 -m 576 -e --ipv4 10.0.0.1/24\r\n\r\nTo create an interface that doesn't use ethernet, but transports IP directly, and filters IPv6 packets out, a command like the following can be used:\r\n\r\n\ttncattach /dev/ttyUSB0 115200 --noipv6 --ipv4 10.0.0.1/24"; static char args_doc[] = "port baudrate"; -static struct argp_option options[] = { - { "mtu", 'm', "MTU", 0, "Specify interface MTU", 1}, - { "ethernet", 'e', 0, 0, "Create a full ethernet device", 2}, - { "ipv4", 'i', "IP_ADDRESS", 0, "Configure an IPv4 address on interface", 3}, - { "ipv6", '6', "IP6_ADDRESS", 0, "Configure an IPv6 address on interface", 4}, - { "ll", 'l', 0, 0, "Add a link-local Ipv6 address", 5}, - { "noipv6", 'n', 0, 0, "Filter IPv6 traffic from reaching TNC", 6}, - { "noup", 1, 0, 0, "Only create interface, don't bring it up", 7}, - { "kisstcp", 'T', 0, 0, "Use KISS over TCP instead of serial port", 8}, - { "tcphost", 'H', "TCP_HOST", 0, "Host to connect to when using KISS over TCP", 9}, - { "tcpport", 'P', "TCP_PORT", 0, "TCP port when using KISS over TCP", 10}, - { "interval", 't', "SECONDS", 0, "Maximum interval between station identifications", 11}, - { "id", 's', "CALLSIGN", 0, "Station identification data", 12}, - { "daemon", 'd', 0, 0, "Run tncattach as a daemon", 13}, - { "verbose", 'v', 0, 0, "Enable verbose output", 14}, - { 0 } +static struct argp_option options[] = { + { "mtu", 'm', "MTU", 0, "Specify interface MTU"}, + { "daemon", 'd', 0, 0, "Run tncattach as a daemon"}, + { "ethernet", 'e', 0, 0, "Create a full ethernet device"}, + { "ipv4", 'i', "IP_ADDRESS", 0, "Configure an IPv4 address on interface"}, + { "noipv6", 'n', 0, 0, "Filter IPv6 traffic from reaching TNC"}, + { "noup", 1, 0, 0, "Only create interface, don't bring it up"}, + { "verbose", 'v', 0, 0, "Enable verbose output"}, + { 0 } }; #define N_ARGS 2 struct arguments { - char *args[N_ARGS]; - char *ipv4; - char *ipv6; - char *id; - bool valid_id; - int id_interval; - int baudrate; - int tcpport; - int mtu; - bool tap; - bool daemon; - bool verbose; - bool set_ipv4; - bool set_netmask; - bool set_ipv6; - bool link_local_v6; - bool set_netmask_v6; - bool noipv6; - bool noup; - bool kiss_over_tcp; - bool set_tcp_host; - bool set_tcp_port; + char *args[N_ARGS]; + char *ipv4; + int baudrate; + int mtu; + bool tap; + bool daemon; + bool verbose; + bool set_ipv4; + bool set_netmask; + bool noipv6; + bool noup; }; static error_t parse_opt(int key, char *arg, struct argp_state *state) { - struct arguments *arguments = state->input; + struct arguments *arguments = state->input; - switch (key) { - case 'v': - arguments->verbose = true; - break; + switch (key) { + case 'v': + arguments->verbose = true; + break; - case 'e': - arguments->tap = true; - break; + case 'e': + arguments->tap = true; + break; - case 'm': - arguments->mtu = atoi(arg); - if (arguments->mtu < MTU_MIN || arguments->mtu > MTU_MAX) { - printf("Error: Invalid MTU specified\r\n\r\n"); - argp_usage(state); - } + case 'm': + arguments->mtu = atoi(arg); + if (arguments->mtu < MTU_MIN || arguments->mtu > MTU_MAX) { + printf("Error: Invalid MTU specified\r\n\r\n"); + argp_usage(state); + } + break; - if((arguments->set_ipv6 || arguments->link_local_v6) && arguments->mtu < 1280) - { - printf("IPv6 and/or link-local IPv6 was requested, but the MTU provided is lower than 1280\n"); - exit(EXIT_FAILURE); - } + case 'i': + arguments->ipv4 = arg; + arguments->set_ipv4 = true; - break; + if (strchr(arg, '/')) { + char* net = strchr(arg, '/'); + int pos = net-arg; + ipv4_addr = (char*)malloc(pos+1); + int mask = atoi(net+1); + strncpy(ipv4_addr, arg, pos); + switch (mask) { + case 0: + netmask = "0.0.0.0"; + break; + case 1: + netmask = "128.0.0.0"; + break; + case 2: + netmask = "192.0.0.0"; + break; + case 3: + netmask = "224.0.0.0"; + break; + case 4: + netmask = "240.0.0.0"; + break; + case 5: + netmask = "248.0.0.0"; + break; + case 6: + netmask = "252.0.0.0"; + break; + case 7: + netmask = "254.0.0.0"; + break; + case 8: + netmask = "255.0.0.0"; + break; + case 9: + netmask = "255.128.0.0"; + break; + case 10: + netmask = "255.192.0.0"; + break; + case 11: + netmask = "255.224.0.0"; + break; + case 12: + netmask = "255.240.0.0"; + break; + case 13: + netmask = "255.248.0.0"; + break; + case 14: + netmask = "255.252.0.0"; + break; + case 15: + netmask = "255.254.0.0"; + break; + case 16: + netmask = "255.255.0.0"; + break; + case 17: + netmask = "255.255.128.0"; + break; + case 18: + netmask = "255.255.192.0"; + break; + case 19: + netmask = "255.255.224.0"; + break; + case 20: + netmask = "255.255.240.0"; + break; + case 21: + netmask = "255.255.248.0"; + break; + case 22: + netmask = "255.255.252.0"; + break; + case 23: + netmask = "255.255.254.0"; + break; + case 24: + netmask = "255.255.255.0"; + break; + case 25: + netmask = "255.255.255.128"; + break; + case 26: + netmask = "255.255.255.192"; + break; + case 27: + netmask = "255.255.255.224"; + break; + case 28: + netmask = "255.255.255.240"; + break; + case 29: + netmask = "255.255.255.248"; + break; + case 30: + netmask = "255.255.255.252"; + break; + case 31: + netmask = "255.255.255.254"; + break; + case 32: + netmask = "255.255.255.255"; + break; + + default: + printf("Error: Invalid subnet mask specified\r\n"); + cleanup(); + exit(1); + } - case 't': - arguments->id_interval = atoi(arg); - if (arguments->id_interval < 0) { - printf("Error: Invalid identification interval specified\r\n\r\n"); - argp_usage(state); - } - break; + arguments->set_netmask = true; + } else { + arguments->set_netmask = false; + ipv4_addr = (char*)malloc(strlen(arg)+1); + strcpy(ipv4_addr, arg); + } - case 's': - arguments->id = arg; - if (strlen(arg) < 1 || strlen(arg) > arguments->mtu) { - printf("Error: Invalid identification string specified\r\n\r\n"); - argp_usage(state); - } else { - arguments->valid_id = true; - } - break; + break; - case 'i': - arguments->ipv4 = arg; - arguments->set_ipv4 = true; + case 'n': + arguments->noipv6 = true; + break; - if (strchr(arg, '/')) { - char* net = strchr(arg, '/'); - int pos = net-arg; - ipv4_addr = (char*)malloc(pos+1); - int mask = atoi(net+1); - strncpy(ipv4_addr, arg, pos); - switch (mask) { - case 0: - netmask = "0.0.0.0"; - break; - case 1: - netmask = "128.0.0.0"; - break; - case 2: - netmask = "192.0.0.0"; - break; - case 3: - netmask = "224.0.0.0"; - break; - case 4: - netmask = "240.0.0.0"; - break; - case 5: - netmask = "248.0.0.0"; - break; - case 6: - netmask = "252.0.0.0"; - break; - case 7: - netmask = "254.0.0.0"; - break; - case 8: - netmask = "255.0.0.0"; - break; - case 9: - netmask = "255.128.0.0"; - break; - case 10: - netmask = "255.192.0.0"; - break; - case 11: - netmask = "255.224.0.0"; - break; - case 12: - netmask = "255.240.0.0"; - break; - case 13: - netmask = "255.248.0.0"; - break; - case 14: - netmask = "255.252.0.0"; - break; - case 15: - netmask = "255.254.0.0"; - break; - case 16: - netmask = "255.255.0.0"; - break; - case 17: - netmask = "255.255.128.0"; - break; - case 18: - netmask = "255.255.192.0"; - break; - case 19: - netmask = "255.255.224.0"; - break; - case 20: - netmask = "255.255.240.0"; - break; - case 21: - netmask = "255.255.248.0"; - break; - case 22: - netmask = "255.255.252.0"; - break; - case 23: - netmask = "255.255.254.0"; - break; - case 24: - netmask = "255.255.255.0"; - break; - case 25: - netmask = "255.255.255.128"; - break; - case 26: - netmask = "255.255.255.192"; - break; - case 27: - netmask = "255.255.255.224"; - break; - case 28: - netmask = "255.255.255.240"; - break; - case 29: - netmask = "255.255.255.248"; - break; - case 30: - netmask = "255.255.255.252"; - break; - case 31: - netmask = "255.255.255.254"; - break; - case 32: - netmask = "255.255.255.255"; - break; + case 'd': + arguments->daemon = true; + arguments->verbose = false; + break; - default: - printf("Error: Invalid subnet mask specified\r\n"); - cleanup(); - exit(1); - } + case 1: + arguments->noup = true; + break; - arguments->set_netmask = true; - } else { - arguments->set_netmask = false; - ipv4_addr = (char*)malloc(strlen(arg)+1); - strcpy(ipv4_addr, arg); - } + case ARGP_KEY_ARG: + // Check if there's now too many text arguments + if (state->arg_num >= N_ARGS) argp_usage(state); - break; - case '6': - if(arguments->noipv6) - { - perror("Sorry, but you had noipv6 set yet want to use ipv6?\n"); - exit(EXIT_FAILURE); - } + // If not add to args + arguments->args[state->arg_num] = arg; + break; - char* ipPart_s = strtok(arg, "/"); - char* prefixPart_s = strtok(NULL, "/"); - printf("ipPart_s: %s\n", ipPart_s); + case ARGP_KEY_END: + // Check if there's too few text arguments + if (state->arg_num < N_ARGS) argp_usage(state); + break; - if(!prefixPart_s) - { - printf("No prefix length was provided\n"); - exit(1); - } - printf("prefixPart_s: %s\n", prefixPart_s); + default: + return ARGP_ERR_UNKNOWN; + } - long prefixLen_l = strtol(prefixPart_s, NULL, 10); // TODO: Add handling here for errors (using errno) - - if(prefixLen_l == 0) { - printf("Prefix length '%s' is not numeric\n", prefixPart_s); - exit(EXIT_FAILURE); - } - else if(!(prefixLen_l >= 0 && prefixLen_l <= 128)) - { - printf("Prefix length '%s' is not within valid range of 0-128\n", prefixPart_s); - exit(EXIT_FAILURE); - } - - arguments->ipv6 = ipPart_s; - - arguments->set_ipv6 = true; - - // Copy across global IPv6 address - ipv6_addr = malloc(strlen(arguments->ipv6)+1); - strcpy(ipv6_addr, arguments->ipv6); - - // Set global IPv6 prefix length - ipv6_prefixLen = prefixLen_l; - - printf("MTU was %d, setting to minimum of %d as is required for IPv6\n", arguments->mtu, 1280); - arguments->mtu = 1280; - break; - - case 'l': - if(arguments->noipv6) - { - perror("Sorry, but you had noipv6 set yet want to use ipv6 link-local?\n"); - exit(EXIT_FAILURE); - } - arguments->link_local_v6 = true; - - printf("MTU was %d, setting to minimum of %d as is required for IPv6\n", arguments->mtu, 1280); - arguments->mtu = 1280; - break; - - case 'n': - arguments->noipv6 = true; - if(arguments->set_ipv6) - { - printf("Requested no IPv6 yet you have set the IPv6 to '%s'\n", arguments->ipv6); - exit(1); - } - break; - - case 'd': - arguments->daemon = true; - arguments->verbose = false; - break; - - case 'T': - arguments->kiss_over_tcp = true; - break; - - case 'H': - arguments->set_tcp_host = true; - tcp_host = (char*)malloc(strlen(arg)+1); - strcpy(tcp_host, arg); - break; - - case 'P': - arguments->set_tcp_port = true; - tcp_port = atoi(arg); - break; - - case 1: - arguments->noup = true; - break; - - case ARGP_KEY_ARG: - // Check if there's now too many text arguments - if (state->arg_num >= N_ARGS) argp_usage(state); - - // If not add to args - arguments->args[state->arg_num] = arg; - break; - - case ARGP_KEY_END: - // Check if there's too few text arguments - if (!arguments->kiss_over_tcp && state->arg_num < N_ARGS) argp_usage(state); - - // Check if text arguments were given when - // KISS over TCP was specified - if (arguments->kiss_over_tcp && state->arg_num != 0) argp_usage(state); - - break; - - default: - return ARGP_ERR_UNKNOWN; - } - - return 0; + return 0; } static void become_daemon() { @@ -604,8 +406,8 @@ static void become_daemon() { pid = fork(); if (pid < 0) { - perror("Fork failed"); - exit(EXIT_FAILURE); + perror("Fork failed"); + exit(EXIT_FAILURE); } if (pid > 0) { @@ -629,90 +431,45 @@ static void become_daemon() { static struct argp argp = {options, parse_opt, args_doc, doc}; int main(int argc, char **argv) { - struct arguments arguments; - signal(SIGINT, signal_handler); + struct arguments arguments; + signal(SIGINT, signal_handler); - arguments.baudrate = BAUDRATE_DEFAULT; - arguments.mtu = MTU_DEFAULT; - arguments.tap = false; - arguments.verbose = false; - arguments.set_ipv4 = false; - arguments.set_netmask = false; - arguments.set_ipv6 = false; - arguments.link_local_v6 = false; - arguments.set_netmask_v6 = false; - arguments.noipv6 = false; - arguments.daemon = false; - arguments.noup = false; - arguments.id_interval = -1; - arguments.valid_id = false; - arguments.kiss_over_tcp = false; + arguments.baudrate = BAUDRATE_DEFAULT; + arguments.mtu = MTU_DEFAULT; + arguments.tap = false; + arguments.verbose = false; + arguments.set_ipv4 = false; + arguments.set_netmask = false; + arguments.noipv6 = false; + arguments.daemon = false; + arguments.noup = false; - argp_parse(&argp, argc, argv, 0, 0, &arguments); + argp_parse(&argp, argc, argv, 0, 0, &arguments); + arguments.baudrate = atoi(arguments.args[1]); - if (arguments.kiss_over_tcp) kiss_over_tcp = true; + if (arguments.daemon) daemonize = true; + if (arguments.verbose) verbose = true; + if (arguments.tap) device_type = IF_TAP; + if (arguments.noipv6) noipv6 = true; + if (arguments.set_ipv4) set_ipv4 = true; + if (arguments.set_netmask) set_netmask = true; + if (arguments.noup) noup = true; + mtu = arguments.mtu; - if (!kiss_over_tcp) { - arguments.baudrate = atoi(arguments.args[1]); - } else { - if (!(arguments.set_tcp_host && arguments.set_tcp_port)) { - if (!arguments.set_tcp_host) printf("Error: KISS over TCP was requested, but no host was specified\r\n"); - if (!arguments.set_tcp_port) printf("Error: KISS over TCP was requested, but no port was specified\r\n"); - exit(1); - } - } - - if (arguments.daemon) daemonize = true; - if (arguments.verbose) verbose = true; - if (arguments.tap) device_type = IF_TAP; - if (arguments.noipv6) noipv6 = true; - if (arguments.set_ipv4) set_ipv4 = true; - if (arguments.set_netmask) set_netmask = true; - if (arguments.set_ipv6) set_ipv6 = true; - if (arguments.noup) noup = true; - mtu = arguments.mtu; - - if (arguments.id_interval >= 0) { - if (!arguments.valid_id) { - printf("Error: Periodic identification requested, but no valid indentification data specified\r\n"); - cleanup(); - exit(1); - } else { - id_interval = arguments.id_interval; - id = malloc(strlen(arguments.id)); - strcpy(id, arguments.id); - } - } else if (arguments.valid_id && arguments.id_interval == -1) { - printf("Error: Periodic identification requested, but no indentification interval specified\r\n"); - cleanup(); - exit(1); - } - - attached_if = open_tap(); - - if (!arguments.kiss_over_tcp) { - attached_tnc = open_port(arguments.args[0]); - if (!setup_port(attached_tnc, arguments.baudrate)) { - printf("Error during serial port setup"); - return 0; - } - } else { - attached_tnc = open_tcp(tcp_host, tcp_port); - } - - printf("TNC interface configured as %s\r\n", if_name); - - fds[IF_FD_INDEX].fd = attached_if; - fds[IF_FD_INDEX].events = POLLIN; - fds[TNC_FD_INDEX].fd = attached_tnc; - fds[TNC_FD_INDEX].events = POLLIN; - - if (daemonize) { - become_daemon(); - syslog(LOG_NOTICE, "tncattach daemon running"); - } - - read_loop(); - - return 0; -} + attached_if = open_tap(); + attached_tnc = open_port(arguments.args[0]); + if (setup_port(attached_tnc, arguments.baudrate)) { + printf("TNC interface configured as %s\r\n", if_name); + fds[IF_FD_INDEX].fd = attached_if; + fds[IF_FD_INDEX].events = POLLIN; + fds[TNC_FD_INDEX].fd = attached_tnc; + fds[TNC_FD_INDEX].events = POLLIN; + if (daemonize) { + become_daemon(); + syslog(LOG_NOTICE, "tncattach daemon running"); + } + read_loop(); + } + + return 0; +} \ No newline at end of file