From f6e12426d065082cd9040b81180517b29f675064 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 27 May 2020 14:30:05 +0200 Subject: [PATCH 01/32] Implemented automatic station ID --- KISS.c | 19 +++++++------ README.md | 4 +++ tncattach.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 90 insertions(+), 11 deletions(-) diff --git a/KISS.c b/KISS.c index ff72b1d..20bef15 100644 --- a/KISS.c +++ b/KISS.c @@ -13,18 +13,21 @@ 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 (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); + 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); + } } } diff --git a/README.md b/README.md index 17b0b7d..bfaba5a 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,8 @@ Attach TNC devices as system network interfaces -m, --mtu=MTU Specify interface MTU -n, --noipv6 Filter IPv6 traffic from reaching TNC --noup Only create interface, don't bring it up + -s, --id=CALLSIGN Station identification data + -t, --interval=SECONDS Maximum interval between station identifications -v, --verbose Enable verbose output -?, --help Give this help list --usage Give a short usage message @@ -50,6 +52,8 @@ Additionally, it is worth noting that __tncattach__ can filter out IPv6 packets 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. +You can configure tncattach to automatically transmit station identification 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 if the amount of time since the last identification is greater than the configured interval and there is any data to send. Channel capacity will therefore not be wasted on IDs for stations that are not actively transmitting. + ## Examples Create an ethernet device with a USB-connected TNC, set the MTU, filter IPv6 traffic, and set an IPv4 address: diff --git a/tncattach.c b/tncattach.c index 1c83782..67c4742 100644 --- a/tncattach.c +++ b/tncattach.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "Constants.h" #include "Serial.h" #include "KISS.h" @@ -38,6 +39,10 @@ char* netmask; int mtu; int device_type = IF_TUN; +char* id; +int id_interval = -1; +time_t last_id = 0; + void cleanup(void) { close_port(attached_tnc); close_tap(attached_if); @@ -149,6 +154,33 @@ void read_loop(void) { if (if_len > 0) { if (if_len >= min_frame_size) { if (!noipv6 || (noipv6 && !is_ipv6(if_buffer))) { + if (id_interval != -1) { + 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 { + if (now > last_id + id_interval) { + 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; + } + } + } + kiss_write_frame(attached_tnc, if_buffer, if_len); } } @@ -166,7 +198,6 @@ void read_loop(void) { 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]); } @@ -192,9 +223,9 @@ void read_loop(void) { exit(1); } -const char *argp_program_version = "tncattach 0.1.2"; +const char *argp_program_version = "tncattach 0.1.3"; const char *argp_program_bug_address = ""; -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 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. Use the --id and --idinterval options. Identification beacons will be transmitted if the amount of time since the last identification is greater than the configured interval and there is any data to send. Channel capacity will therefore not be wasted on IDs for stations that are not actively transmitting."; static char args_doc[] = "port baudrate"; static struct argp_option options[] = { { "mtu", 'm', "MTU", 0, "Specify interface MTU"}, @@ -203,6 +234,8 @@ static struct argp_option options[] = { { "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"}, + { "interval", 't', "SECONDS", 0, "Maximum interval between station identifications"}, + { "id", 's', "CALLSIGN", 0, "Station identification data"}, { "verbose", 'v', 0, 0, "Enable verbose output"}, { 0 } }; @@ -211,6 +244,9 @@ static struct argp_option options[] = { struct arguments { char *args[N_ARGS]; char *ipv4; + char *id; + bool valid_id; + int id_interval; int baudrate; int mtu; bool tap; @@ -242,6 +278,24 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) { } break; + 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; + + 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; + case 'i': arguments->ipv4 = arg; arguments->set_ipv4 = true; @@ -443,6 +497,8 @@ int main(int argc, char **argv) { arguments.noipv6 = false; arguments.daemon = false; arguments.noup = false; + arguments.id_interval = -1; + arguments.valid_id = false; argp_parse(&argp, argc, argv, 0, 0, &arguments); arguments.baudrate = atoi(arguments.args[1]); @@ -456,6 +512,22 @@ int main(int argc, char **argv) { 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(); attached_tnc = open_port(arguments.args[0]); if (setup_port(attached_tnc, arguments.baudrate)) { From 01599263127bfb9fab4cd1eabba9604685500bac Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 27 May 2020 15:14:37 +0200 Subject: [PATCH 02/32] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bfaba5a..867cc40 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ 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) binaries exist in the releases section. +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/tag/0.1.3). ```sh # If you don't already have a compiler installed From 764746cc4c3a3c0a140cff7d2ac3d8a1eb3949f5 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Thu, 28 May 2020 11:32:46 +0200 Subject: [PATCH 03/32] Fixed inadvertently setting interface flags when setting MTU --- TAP.c | 73 +++++++++++++++++++++++++++++------------------------ tncattach.c | 2 +- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/TAP.c b/TAP.c index 1b6c2fd..77e432c 100644 --- a/TAP.c +++ b/TAP.c @@ -65,52 +65,59 @@ int open_tap(void) { } if (!noup) { - ifr.ifr_flags |= IFF_UP | IFF_RUNNING; - if (ioctl(inet, SIOCSIFFLAGS, &ifr) < 0) { - perror("Could not bring up interface"); + if (ioctl(inet, SIOCGIFFLAGS, &ifr) < 0) { + perror("Could not get interface flags from kernel"); close(inet); cleanup(); exit(1); } else { - if (set_ipv4) { - struct ifreq a_ifr; - struct sockaddr_in addr, snm; + 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; + 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"); + 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 { - 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"); + 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); + } } } } diff --git a/tncattach.c b/tncattach.c index 67c4742..1382879 100644 --- a/tncattach.c +++ b/tncattach.c @@ -223,7 +223,7 @@ void read_loop(void) { exit(1); } -const char *argp_program_version = "tncattach 0.1.3"; +const char *argp_program_version = "tncattach 0.1.4"; 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. Use the --id and --idinterval options. Identification beacons will be transmitted if the amount of time since the last identification is greater than the configured interval and there is any data to send. Channel capacity will therefore not be wasted on IDs for stations that are not actively transmitting."; static char args_doc[] = "port baudrate"; From 01a6fc1cd88359316d54b01893d37ac1525d60a2 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Thu, 28 May 2020 12:20:07 +0200 Subject: [PATCH 04/32] Increased logging in verbose mode --- KISS.c | 1 + tncattach.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/KISS.c b/KISS.c index 20bef15..95a481f 100644 --- a/KISS.c +++ b/KISS.c @@ -28,6 +28,7 @@ void kiss_frame_received(int frame_len) { cleanup(); exit(1); } + if (verbose && !daemonize) printf("Got %d bytes from TNC, wrote %d bytes to interface\r\n", frame_len, written); } } diff --git a/tncattach.c b/tncattach.c index 1382879..c92aa83 100644 --- a/tncattach.c +++ b/tncattach.c @@ -181,7 +181,8 @@ void read_loop(void) { } } - kiss_write_frame(attached_tnc, if_buffer, if_len); + int tnc_written = kiss_write_frame(attached_tnc, if_buffer, if_len); + if (verbose && !daemonize) printf("Got %d bytes from interface, wrote %d bytes to TNC\r\n", if_len, tnc_written); } } } else { From bb045bc6d41f45e73514b9f534199cc9964ae58c Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Thu, 28 May 2020 12:34:46 +0200 Subject: [PATCH 05/32] Increased logging in verbose mode --- tncattach.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tncattach.c b/tncattach.c index c92aa83..91ca717 100644 --- a/tncattach.c +++ b/tncattach.c @@ -182,7 +182,7 @@ void read_loop(void) { } int tnc_written = kiss_write_frame(attached_tnc, if_buffer, if_len); - if (verbose && !daemonize) printf("Got %d bytes from interface, wrote %d bytes to TNC\r\n", if_len, tnc_written); + if (verbose && !daemonize) printf("Got %d bytes from interface, wrote %d bytes (KISS-framed and escaped) to TNC\r\n", if_len, tnc_written); } } } else { From a4b433e2f77c52fbcc933cf7708da70030bc385e Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Thu, 28 May 2020 13:27:06 +0200 Subject: [PATCH 06/32] Implemented part 97 compliant auto-identification --- README.md | 12 ++- tncattach.c | 249 +++++++++++++++++++++++++++++----------------------- 2 files changed, 149 insertions(+), 112 deletions(-) diff --git a/README.md b/README.md index 867cc40..b3bc628 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,17 @@ Additionally, it is worth noting that __tncattach__ can filter out IPv6 packets 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. -You can configure tncattach to automatically transmit station identification 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 if the amount of time since the last identification is greater than the configured interval and there is any data to send. Channel capacity will therefore not be wasted on IDs for stations that are not actively transmitting. +## 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 diff --git a/tncattach.c b/tncattach.c index 91ca717..928bad9 100644 --- a/tncattach.c +++ b/tncattach.c @@ -42,23 +42,13 @@ 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) { close_port(attached_tnc); close_tap(attached_if); } -void signal_handler(int signal) { - if (daemonize) { - cleanup(); - syslog(LOG_NOTICE, "tncattach daemon exiting"); - exit(0); - } else { - cleanup(); - exit(0); - } -} - bool is_ipv6(uint8_t* frame) { if (device_type == IF_TAP) { if (frame[12] == 0x86 && frame[13] == 0xdd) { @@ -79,6 +69,56 @@ bool is_ipv6(uint8_t* frame) { } } +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; + } +} + +void signal_handler(int signal) { + if (daemonize) syslog(LOG_NOTICE, "tncattach daemon exiting"); + + // Transmit final ID if necessary + if (id_interval != -1 && tx_since_last_id) transmit_id(); + + cleanup(); + exit(0); +} + void read_loop(void) { bool should_continue = true; int min_frame_size; @@ -97,120 +137,107 @@ void read_loop(void) { exit(1); } + int poll_timeout = 1000; while (should_continue) { - - int poll_result = poll(fds, 2, -1); + int poll_result = poll(fds, 2, poll_timeout); 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); - } - } + if (poll_result == 0) { + // No resources are ready for reading, + // run scheduled tasks instead. + if (tx_since_last_id) { + time_t now = time_now(); + if (now > last_id + id_interval) transmit_id(); + } - // 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"); + } 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); } - 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"); + 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); } - 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 (id_interval != -1) { - 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 { - if (now > last_id + id_interval) { - 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); - } - } + // 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); + } + } - 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; - } - } + // 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))) { + + 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(); } - - 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); } - } - } 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"); + 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); } - 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"); + 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 { - printf("Error: Could not read from TNC, exiting now\r\n"); + 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); } - - cleanup(); - exit(1); } } } @@ -224,9 +251,9 @@ void read_loop(void) { exit(1); } -const char *argp_program_version = "tncattach 0.1.4"; +const char *argp_program_version = "tncattach 0.1.5"; 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. Use the --id and --idinterval options. Identification beacons will be transmitted if the amount of time since the last identification is greater than the configured interval and there is any data to send. Channel capacity will therefore not be wasted on IDs for stations that are not actively transmitting."; +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 args_doc[] = "port baudrate"; static struct argp_option options[] = { { "mtu", 'm', "MTU", 0, "Specify interface MTU"}, From a4f79f204b6f59f96ce868e0ccc9fbfafab05c0d Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Thu, 28 May 2020 13:42:08 +0200 Subject: [PATCH 07/32] Fixed missing check when runnign without ID --- tncattach.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tncattach.c b/tncattach.c index 928bad9..7140e00 100644 --- a/tncattach.c +++ b/tncattach.c @@ -98,6 +98,7 @@ void transmit_id(void) { kiss_write_frame(attached_tnc, id_frame, id_len); last_id = now; tx_since_last_id = false; + } bool should_id(void) { @@ -144,11 +145,10 @@ void read_loop(void) { if (poll_result == 0) { // No resources are ready for reading, // run scheduled tasks instead. - if (tx_since_last_id) { + 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) { @@ -251,7 +251,7 @@ void read_loop(void) { exit(1); } -const char *argp_program_version = "tncattach 0.1.5"; +const char *argp_program_version = "tncattach 0.1.6"; 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 args_doc[] = "port baudrate"; From 8a70eedfc5d35e3aee9137ee6a31564dc88ec187 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Thu, 28 May 2020 14:15:29 +0200 Subject: [PATCH 08/32] Raspbian MTU setting --- README.md | 9 +++++++++ TAP.c | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b3bc628..813861c 100644 --- a/README.md +++ b/README.md @@ -111,3 +111,12 @@ tnc0: flags=4305 mtu 400 TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 ``` +## Known Issues + +The utility fails to set the interface MTU on some Raspbian versions. After attaching the TNC, check that the correct MTU has been set on the interface with ifconfig or similar. If not, you can set it manually with a command like: + +```sh +# Attach interface +ifconfig tnc0 mtu 478 + +``` \ No newline at end of file diff --git a/TAP.c b/TAP.c index 77e432c..7b5c834 100644 --- a/TAP.c +++ b/TAP.c @@ -50,7 +50,7 @@ int open_tap(void) { cleanup(); exit(1); } else { - if (ioctl(inet, SIOCGIFFLAGS, &ifr) < 0) { + if (ioctl(inet, SIOCGIFMTU, &ifr) < 0) { perror("Could not get interface flags from kernel"); close(inet); cleanup(); From 5f251e1f281de806241540174f32ff5a183f064f Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Thu, 28 May 2020 14:45:07 +0200 Subject: [PATCH 09/32] Raspbian MTU setting description in readme --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 813861c..ff6fd12 100644 --- a/README.md +++ b/README.md @@ -111,12 +111,13 @@ tnc0: flags=4305 mtu 400 TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 ``` -## Known Issues +## Worth Knowing on Raspbian -The utility fails to set the interface MTU on some Raspbian versions. After attaching the TNC, check that the correct MTU has been set on the interface with ifconfig or similar. If not, you can set it manually with a command like: +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: -```sh -# Attach interface -ifconfig tnc0 mtu 478 +``` +# Add the following statement somewhere at the beginning +# of /etc/dhcpcd.conf to prevent dhcpcd from changing MTU +denyinterfaces tnc0 ``` \ No newline at end of file From e7a831b41d9fadd7e082596d4669809396d26f58 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Thu, 28 May 2020 16:25:41 +0200 Subject: [PATCH 10/32] Updated readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ff6fd12..9d73e8f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Attach KISS TNC devices as network interfaces in Linux. This program allows you 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/tag/0.1.3). +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). ```sh # If you don't already have a compiler installed From b438e5fb5a700ef483e50ea34c2afec892963b2f Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 1 Jun 2020 22:25:10 +0200 Subject: [PATCH 11/32] Implemented setting txqueuelen on created interface --- Constants.h | 4 +++- TAP.c | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Constants.h b/Constants.h index 149a711..cd9e3a2 100644 --- a/Constants.h +++ b/Constants.h @@ -6,4 +6,6 @@ #define MTU_MIN 74 #define MTU_MAX 1522 -#define MTU_DEFAULT 329 \ No newline at end of file +#define MTU_DEFAULT 329 + +#define TXQUEUELEN 10 \ No newline at end of file diff --git a/TAP.c b/TAP.c index 7b5c834..c2be769 100644 --- a/TAP.c +++ b/TAP.c @@ -64,6 +64,22 @@ int open_tap(void) { exit(1); } + // 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); + } + } + if (!noup) { if (ioctl(inet, SIOCGIFFLAGS, &ifr) < 0) { perror("Could not get interface flags from kernel"); From 30207d6691e04eab5071aa68a0be441913688a03 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 1 Jun 2020 22:54:15 +0200 Subject: [PATCH 12/32] Added ARP configuration for interface --- Constants.h | 6 +++++- TAP.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/Constants.h b/Constants.h index cd9e3a2..fd366ee 100644 --- a/Constants.h +++ b/Constants.h @@ -8,4 +8,8 @@ #define MTU_MAX 1522 #define MTU_DEFAULT 329 -#define TXQUEUELEN 10 \ No newline at end of file +#define TXQUEUELEN 10 + +// ARP timings, in seconds +#define ARP_BASE_REACHABLE_TIME 300 +#define ARP_RETRANS_TIME 5 \ No newline at end of file diff --git a/TAP.c b/TAP.c index c2be769..45f5d00 100644 --- a/TAP.c +++ b/TAP.c @@ -80,6 +80,49 @@ int open_tap(void) { } } + // 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); + } + } + + 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"); From 1c7b30b995ee13beb8df77eab68fb2ca7273de1d Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 1 Jun 2020 23:01:25 +0200 Subject: [PATCH 13/32] Updated version number --- tncattach.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tncattach.c b/tncattach.c index 7140e00..9eefe80 100644 --- a/tncattach.c +++ b/tncattach.c @@ -251,7 +251,7 @@ void read_loop(void) { exit(1); } -const char *argp_program_version = "tncattach 0.1.6"; +const char *argp_program_version = "tncattach 0.1.7"; 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 args_doc[] = "port baudrate"; From 41086b2e0cc9c87579844be88749fb8ac2caf308 Mon Sep 17 00:00:00 2001 From: Valentin Saugnier Date: Tue, 16 Jun 2020 21:55:25 +0200 Subject: [PATCH 14/32] :sparkles: Add TCP Kiss --- README.md | 8 +- Tcp.c | 36 +++ Tcp.h | 18 ++ makefile | 2 +- tncattach.c | 890 +++++++++++++++++++++++++++------------------------- 5 files changed, 517 insertions(+), 437 deletions(-) create mode 100644 Tcp.c create mode 100644 Tcp.h diff --git a/README.md b/README.md index 9d73e8f..0e00871 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ TNC Attach ========== 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. +## Version edited +Add capability of using TCP Kiss. + ## Installation Currently it is recommended to compile and install __tncattach__ from source with the below commands. @@ -30,10 +33,11 @@ sudo make install Using __tncattach__ is simple. Run the program from the command line, specifying which serial port the TNC is connected to, and the serial port baud-rate, and __tncattach__ takes care of the rest. In most cases, depending on what you intend to do, you probably want to use some of the options, though. See the examples section below for usage examples. ``` -Usage: tncattach [OPTION...] port baudrate +Usage: tncattach [OPTION...] (serial_port|host) (baudrate|port) Attach TNC devices as system network interfaces + -o, --kisstcp Use TCP Kiss (such as Direwolf port 8001) -d, --daemon Run tncattach as a daemon -e, --ethernet Create a full ethernet device -i, --ipv4=IP_ADDRESS Configure an IPv4 address on interface @@ -73,6 +77,7 @@ Create an ethernet device with a USB-connected TNC, set the MTU, filter IPv6 tra ```sh # Attach interface sudo tncattach /dev/ttyUSB0 115200 --ethernet --mtu 576 --noipv6 --ipv4 10.92.0.10/24 +sudo tncattach localhost 8001 -o --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: @@ -95,6 +100,7 @@ Create a point-to-point link: ```sh # Attach interface sudo tncattach /dev/ttyUSB0 115200 --mtu 400 --noipv6 --noup +sudo tncattach localhost 8001 -o --mtu 400 --noipv6 --noup # Configure IP addresses for point-to-point link sudo ifconfig tnc0 10.93.0.1 pointopoint 10.93.0.2 diff --git a/Tcp.c b/Tcp.c new file mode 100644 index 0000000..c396726 --- /dev/null +++ b/Tcp.c @@ -0,0 +1,36 @@ +#include "Tcp.h" + +int open_tcp(char* ip, int port) { + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + + if (sockfd < 0) { + perror("ERROR opening socket"); + exit(1); + } + + struct hostent *server; + struct sockaddr_in serv_addr; + + server = gethostbyname(ip); + + if (server == NULL) { + fprintf(stderr,"ERROR, no such host\n"); + exit(0); + } + + bzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + bcopy((char *)server->h_addr, (char *)&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("ERROR connecting"); + 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 new file mode 100644 index 0000000..9649168 --- /dev/null +++ b/Tcp.h @@ -0,0 +1,18 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "Constants.h" + +#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 f373c4f..08ec491 100644 --- a/makefile +++ b/makefile @@ -14,7 +14,7 @@ clean: tncattach: @echo "Making tncattach..." @echo "Compiling with: ${compiler}" - ${compiler} ${flags} tncattach.c Serial.c KISS.c TAP.c -o tncattach -Wall + ${compiler} ${flags} tncattach.c Serial.c Tcp.c KISS.c TAP.c -o tncattach -Wall install: @echo "Installing tncattach..." diff --git a/tncattach.c b/tncattach.c index 9eefe80..a4d66dc 100644 --- a/tncattach.c +++ b/tncattach.c @@ -8,6 +8,7 @@ #include #include "Constants.h" #include "Serial.h" +#include "Tcp.h" #include "KISS.h" #include "TAP.h" @@ -34,6 +35,7 @@ bool noup = false; bool daemonize = false; bool set_ipv4 = false; bool set_netmask = false; +bool use_net_kiss = false; char* ipv4_addr; char* netmask; int mtu; @@ -45,442 +47,452 @@ time_t last_id = 0; bool tx_since_last_id = false; void cleanup(void) { - close_port(attached_tnc); - close_tap(attached_if); + if (use_net_kiss) { + 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); - } + 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; - } + 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); - } - } + 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; + 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; - } + if (id_interval != -1) { + time_t now = time_now(); + return now > last_id + id_interval; + } else { + return false; + } } void signal_handler(int signal) { - if (daemonize) syslog(LOG_NOTICE, "tncattach daemon exiting"); + if (daemonize) syslog(LOG_NOTICE, "tncattach daemon exiting"); - // Transmit final ID if necessary - if (id_interval != -1 && tx_since_last_id) transmit_id(); + // Transmit final ID if necessary + if (id_interval != -1 && tx_since_last_id) transmit_id(); - cleanup(); - exit(0); + cleanup(); + exit(0); } 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"); - } - - cleanup(); - exit(1); - } + 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"); + } - 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); - } - } + 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); - } - } + 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); + } + } - // 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))) { + // 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); + } + } - 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 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 (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); - } - } + 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 (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 (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); } const char *argp_program_version = "tncattach 0.1.7"; 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 args_doc[] = "port baudrate"; -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"}, - { "interval", 't', "SECONDS", 0, "Maximum interval between station identifications"}, - { "id", 's', "CALLSIGN", 0, "Station identification data"}, - { "verbose", 'v', 0, 0, "Enable verbose output"}, - { 0 } +static char args_doc[] = "port baudrateOrPort"; +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"}, + { "interval", 't', "SECONDS", 0, "Maximum interval between station identifications"}, + { "id", 's', "CALLSIGN", 0, "Station identification data"}, + { "verbose", 'v', 0, 0, "Enable verbose output"}, + { "kisstcp", 'o', 0, 0, "Does not use Serial but TCP connexion"}, + { 0 } }; #define N_ARGS 2 struct arguments { - char *args[N_ARGS]; - char *ipv4; - char *id; - bool valid_id; - int id_interval; - int baudrate; - int mtu; - bool tap; - bool daemon; - bool verbose; - bool set_ipv4; - bool set_netmask; - bool noipv6; - bool noup; + char *args[N_ARGS]; + char *ipv4; + char *id; + bool valid_id; + int id_interval; + int baudrateOrPort; + int mtu; + bool tap; + bool daemon; + bool verbose; + bool set_ipv4; + bool set_netmask; + bool noipv6; + bool noup; + bool useNetKiss; }; 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); - } - 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); + } + break; - 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; + 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; - 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; + 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; - case 'i': - arguments->ipv4 = arg; - arguments->set_ipv4 = true; + case 'i': + arguments->ipv4 = arg; + arguments->set_ipv4 = true; - 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); - } + 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; - arguments->set_netmask = true; - } else { - arguments->set_netmask = false; - ipv4_addr = (char*)malloc(strlen(arg)+1); - strcpy(ipv4_addr, arg); - } + default: + printf("Error: Invalid subnet mask specified\r\n"); + cleanup(); + exit(1); + } - break; + arguments->set_netmask = true; + } else { + arguments->set_netmask = false; + ipv4_addr = (char*)malloc(strlen(arg)+1); + strcpy(ipv4_addr, arg); + } - case 'n': - arguments->noipv6 = true; - break; + break; - case 'd': - arguments->daemon = true; - arguments->verbose = false; - break; + case 'n': + arguments->noipv6 = true; + break; - case 1: - arguments->noup = true; - break; + case 'd': + arguments->daemon = true; + arguments->verbose = false; + break; - case ARGP_KEY_ARG: - // Check if there's now too many text arguments - if (state->arg_num >= N_ARGS) argp_usage(state); + case 'o': + arguments->useNetKiss = true; + break; - // If not add to args - arguments->args[state->arg_num] = arg; - break; + case 1: + arguments->noup = true; + break; - case ARGP_KEY_END: - // Check if there's too few text arguments - if (state->arg_num < N_ARGS) argp_usage(state); - break; + case ARGP_KEY_ARG: + // Check if there's now too many text arguments + if (state->arg_num >= N_ARGS) argp_usage(state); - default: - return ARGP_ERR_UNKNOWN; - } + // If not add to args + arguments->args[state->arg_num] = arg; + break; - return 0; + case ARGP_KEY_END: + // Check if there's too few text arguments + if (state->arg_num < N_ARGS) argp_usage(state); + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; } static void become_daemon() { @@ -488,8 +500,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) { @@ -513,63 +525,71 @@ 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.noipv6 = false; - arguments.daemon = false; - arguments.noup = false; - arguments.id_interval = -1; - arguments.valid_id = false; + arguments.baudrateOrPort = 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; + arguments.id_interval = -1; + arguments.valid_id = false; - argp_parse(&argp, argc, argv, 0, 0, &arguments); - arguments.baudrate = atoi(arguments.args[1]); + argp_parse(&argp, argc, argv, 0, 0, &arguments); + arguments.baudrateOrPort = atoi(arguments.args[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.noup) noup = true; - mtu = arguments.mtu; + 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; + if (arguments.useNetKiss) use_net_kiss = 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); - } + 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(); - 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; + attached_if = open_tap(); + + if (arguments.useNetKiss) { + attached_tnc = open_tcp(arguments.args[0], arguments.baudrateOrPort); + } else { + attached_tnc = open_port(arguments.args[0]); + if (!setup_port(attached_tnc, arguments.baudrateOrPort)) { + printf("Error during serial port setup"); + return 0; + } + } + 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 From dada2f377571ae2a1afa429fc124325714ed139c Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 24 Jun 2020 12:28:49 +0200 Subject: [PATCH 15/32] Makefile update --- makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile b/makefile index f373c4f..b6ad994 100644 --- a/makefile +++ b/makefile @@ -2,7 +2,7 @@ .PHONY: all clean install uninstall tncattach compiler = gcc -flags = -std=gnu11 -lm +flags = -std=gnu11 -static-libgcc all: tncattach rebuild: clean all From 26f1e48b192951c1724d93d159007f498befb32c Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 24 Jun 2020 14:00:23 +0200 Subject: [PATCH 16/32] Renamed TCP files --- TCP.c | 37 +++++++++++++++++++++++++++++++++++++ TCP.h | 9 +++++++++ 2 files changed, 46 insertions(+) create mode 100644 TCP.c create mode 100644 TCP.h diff --git a/TCP.c b/TCP.c new file mode 100644 index 0000000..5ca898e --- /dev/null +++ b/TCP.c @@ -0,0 +1,37 @@ +#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); + } + + bzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + + bcopy((char *)server->h_addr, (char *)&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 new file mode 100644 index 0000000..d386b59 --- /dev/null +++ b/TCP.h @@ -0,0 +1,9 @@ +#include +#include +#include +#include +#include +#include + +int open_tcp(char* ip, int port); +int close_tcp(int fd); \ No newline at end of file From d3ff2f207a7b5c6aa2aa2164b5de69f0ecfc0b82 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 24 Jun 2020 14:01:35 +0200 Subject: [PATCH 17/32] Updated makefile --- Tcp.c | 36 ------------------------------------ Tcp.h | 18 ------------------ makefile | 6 +++--- 3 files changed, 3 insertions(+), 57 deletions(-) delete mode 100644 Tcp.c delete mode 100644 Tcp.h diff --git a/Tcp.c b/Tcp.c deleted file mode 100644 index c396726..0000000 --- a/Tcp.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "Tcp.h" - -int open_tcp(char* ip, int port) { - int sockfd = socket(AF_INET, SOCK_STREAM, 0); - - if (sockfd < 0) { - perror("ERROR opening socket"); - exit(1); - } - - struct hostent *server; - struct sockaddr_in serv_addr; - - server = gethostbyname(ip); - - if (server == NULL) { - fprintf(stderr,"ERROR, no such host\n"); - exit(0); - } - - bzero((char *) &serv_addr, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - bcopy((char *)server->h_addr, (char *)&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("ERROR connecting"); - 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 9649168..0000000 --- a/Tcp.h +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include "Constants.h" - -#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 df99f54..677cd0d 100644 --- a/makefile +++ b/makefile @@ -2,7 +2,7 @@ .PHONY: all clean install uninstall tncattach compiler = gcc -flags = -std=gnu11 -static-libgcc +flags = -Wall -std=gnu11 -static-libgcc all: tncattach rebuild: clean all @@ -14,7 +14,7 @@ clean: tncattach: @echo "Making tncattach..." @echo "Compiling with: ${compiler}" - ${compiler} ${flags} tncattach.c Serial.c Tcp.c KISS.c TAP.c -o tncattach -Wall + ${compiler} ${flags} tncattach.c Serial.c TCP.c KISS.c TAP.c -o tncattach -Wall install: @echo "Installing tncattach..." @@ -23,4 +23,4 @@ install: uninstall: @echo "Uninstalling tncattach" - rm /usr/local/sbin/tncattach \ No newline at end of file + rm /usr/local/sbin/tncattach From b996f386899e21d6ee69404d8b8ed47469da36fe Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 24 Jun 2020 14:02:02 +0200 Subject: [PATCH 18/32] Fixed typo --- TAP.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TAP.c b/TAP.c index 45f5d00..e5db123 100644 --- a/TAP.c +++ b/TAP.c @@ -46,7 +46,7 @@ int open_tap(void) { int inet = socket(AF_INET, SOCK_DGRAM, 0); if (inet == -1) { - perror("Could not open AF_INET socket\r\n"); + perror("Could not open AF_INET socket"); cleanup(); exit(1); } else { From eae91f349bef6eea4401b3f2b0cccdc67dc08a85 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 24 Jun 2020 14:13:40 +0200 Subject: [PATCH 19/32] Updated readme to include KISS over TCP --- README.md | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0e00871..7b87e99 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,6 @@ TNC Attach ========== 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. -## Version edited -Add capability of using TCP Kiss. - ## Installation Currently it is recommended to compile and install __tncattach__ from source with the below commands. @@ -33,19 +30,21 @@ sudo make install Using __tncattach__ is simple. Run the program from the command line, specifying which serial port the TNC is connected to, and the serial port baud-rate, and __tncattach__ takes care of the rest. In most cases, depending on what you intend to do, you probably want to use some of the options, though. See the examples section below for usage examples. ``` -Usage: tncattach [OPTION...] (serial_port|host) (baudrate|port) +Usage: tncattach [OPTION...] port baudrate Attach TNC devices as system network interfaces - -o, --kisstcp Use TCP Kiss (such as Direwolf port 8001) - -d, --daemon Run tncattach as a daemon + -m, --mtu=MTU Specify interface MTU -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 - -s, --id=CALLSIGN Station identification data + -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 @@ -54,6 +53,8 @@ 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. @@ -77,7 +78,13 @@ Create an ethernet device with a USB-connected TNC, set the MTU, filter IPv6 tra ```sh # Attach interface sudo tncattach /dev/ttyUSB0 115200 --ethernet --mtu 576 --noipv6 --ipv4 10.92.0.10/24 -sudo tncattach localhost 8001 -o --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: @@ -100,7 +107,6 @@ Create a point-to-point link: ```sh # Attach interface sudo tncattach /dev/ttyUSB0 115200 --mtu 400 --noipv6 --noup -sudo tncattach localhost 8001 -o --mtu 400 --noipv6 --noup # Configure IP addresses for point-to-point link sudo ifconfig tnc0 10.93.0.1 pointopoint 10.93.0.2 @@ -117,6 +123,7 @@ 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: From c2beeee94441434655a1b200050548bfe951db74 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 24 Jun 2020 14:15:49 +0200 Subject: [PATCH 20/32] Cleaned up TCP options --- tncattach.c | 981 +++++++++++++++++++++++++++------------------------- 1 file changed, 510 insertions(+), 471 deletions(-) diff --git a/tncattach.c b/tncattach.c index a4d66dc..d46b866 100644 --- a/tncattach.c +++ b/tncattach.c @@ -8,8 +8,8 @@ #include #include "Constants.h" #include "Serial.h" -#include "Tcp.h" #include "KISS.h" +#include "TCP.h" #include "TAP.h" #define BAUDRATE_DEFAULT 0 @@ -35,9 +35,13 @@ bool noup = false; bool daemonize = false; bool set_ipv4 = false; bool set_netmask = false; -bool use_net_kiss = false; +bool kiss_over_tcp = false; char* ipv4_addr; char* netmask; + +char* tcp_host; +int tcp_port; + int mtu; int device_type = IF_TUN; @@ -47,549 +51,584 @@ time_t last_id = 0; bool tx_since_last_id = false; void cleanup(void) { - if (use_net_kiss) { - close_tcp(attached_tnc); - } else { - close_port(attached_tnc); - } - close_tap(attached_if); + 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); - } + 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; - } + 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); - } - } + 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; + 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; - } + if (id_interval != -1) { + time_t now = time_now(); + return now > last_id + id_interval; + } else { + return false; + } } void signal_handler(int signal) { - if (daemonize) syslog(LOG_NOTICE, "tncattach daemon exiting"); + if (daemonize) syslog(LOG_NOTICE, "tncattach daemon exiting"); - // Transmit final ID if necessary - if (id_interval != -1 && tx_since_last_id) transmit_id(); + // Transmit final ID if necessary + if (id_interval != -1 && tx_since_last_id) transmit_id(); - cleanup(); - exit(0); + cleanup(); + exit(0); } 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); + } - 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_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); + } + } - // 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))) { - 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; + 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 (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"); - } + 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); + cleanup(); + exit(1); + } + } + } + } + } + } + } else { + should_continue = false; + } + } + cleanup(); + exit(1); } -const char *argp_program_version = "tncattach 0.1.7"; +const char *argp_program_version = "tncattach 0.1.8"; 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 args_doc[] = "port baudrateOrPort"; +static char args_doc[] = "port baudrate"; 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"}, - { "interval", 't', "SECONDS", 0, "Maximum interval between station identifications"}, - { "id", 's', "CALLSIGN", 0, "Station identification data"}, - { "verbose", 'v', 0, 0, "Enable verbose output"}, - { "kisstcp", 'o', 0, 0, "Does not use Serial but TCP connexion"}, - { 0 } + { "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}, + { "noipv6", 'n', 0, 0, "Filter IPv6 traffic from reaching TNC", 4}, + { "noup", 1, 0, 0, "Only create interface, don't bring it up", 5}, + { "kisstcp", 'T', 0, 0, "Use KISS over TCP instead of serial port", 6}, + { "tcphost", 'H', "TCP_HOST", 0, "Host to connect to when using KISS over TCP", 7}, + { "tcpport", 'P', "TCP_PORT", 0, "TCP port when using KISS over TCP", 8}, + { "interval", 't', "SECONDS", 0, "Maximum interval between station identifications", 9}, + { "id", 's', "CALLSIGN", 0, "Station identification data", 10}, + { "daemon", 'd', 0, 0, "Run tncattach as a daemon", 11}, + { "verbose", 'v', 0, 0, "Enable verbose output", 12}, + { 0 } }; #define N_ARGS 2 struct arguments { - char *args[N_ARGS]; - char *ipv4; - char *id; - bool valid_id; - int id_interval; - int baudrateOrPort; - int mtu; - bool tap; - bool daemon; - bool verbose; - bool set_ipv4; - bool set_netmask; - bool noipv6; - bool noup; - bool useNetKiss; + char *args[N_ARGS]; + char *ipv4; + 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 noipv6; + bool noup; + bool kiss_over_tcp; + bool set_tcp_host; + bool set_tcp_port; }; 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; + switch (key) { + case 'v': + arguments->verbose = 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); + } + break; + + 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; + + 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; + + case 'i': + arguments->ipv4 = arg; + arguments->set_ipv4 = true; + + 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); + } + + arguments->set_netmask = true; + } else { + arguments->set_netmask = false; + ipv4_addr = (char*)malloc(strlen(arg)+1); + strcpy(ipv4_addr, arg); + } + + break; + + case 'n': + arguments->noipv6 = true; + 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 'e': - arguments->tap = true; + case 'P': + arguments->set_tcp_port = true; + tcp_port = atoi(arg); 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); - } - break; + case 1: + arguments->noup = true; + break; - 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; + case ARGP_KEY_ARG: + // Check if there's now too many text arguments + if (state->arg_num >= N_ARGS) argp_usage(state); - 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; + // If not add to args + arguments->args[state->arg_num] = arg; + break; - case 'i': - arguments->ipv4 = arg; - arguments->set_ipv4 = true; + 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); - 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; + // Check if text arguments were given when + // KISS over TCP was specified + if (arguments->kiss_over_tcp && state->arg_num != 0) argp_usage(state); - default: - printf("Error: Invalid subnet mask specified\r\n"); - cleanup(); - exit(1); - } + break; - arguments->set_netmask = true; - } else { - arguments->set_netmask = false; - ipv4_addr = (char*)malloc(strlen(arg)+1); - strcpy(ipv4_addr, arg); - } + default: + return ARGP_ERR_UNKNOWN; + } - break; - - case 'n': - arguments->noipv6 = true; - break; - - case 'd': - arguments->daemon = true; - arguments->verbose = false; - break; - - case 'o': - arguments->useNetKiss = true; - 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 (state->arg_num < N_ARGS) argp_usage(state); - break; - - default: - return ARGP_ERR_UNKNOWN; - } - - return 0; + return 0; } static void become_daemon() { - pid_t pid; - pid = fork(); + pid_t pid; + pid = fork(); - if (pid < 0) { - perror("Fork failed"); - exit(EXIT_FAILURE); - } + if (pid < 0) { + perror("Fork failed"); + exit(EXIT_FAILURE); + } - if (pid > 0) { - exit(0); - } + if (pid > 0) { + exit(0); + } - if (setsid() < 0) exit(1); + if (setsid() < 0) exit(1); - signal(SIGCHLD, signal_handler); - signal(SIGHUP, signal_handler); + signal(SIGCHLD, signal_handler); + signal(SIGHUP, signal_handler); - pid = fork(); - if (pid < 0) exit(1); - if (pid > 0) exit(0); + pid = fork(); + if (pid < 0) exit(1); + if (pid > 0) exit(0); - umask(0); - chdir("/"); + umask(0); + chdir("/"); - openlog("tncattach", LOG_PID, LOG_DAEMON); + openlog("tncattach", LOG_PID, LOG_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.baudrateOrPort = 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; - arguments.id_interval = -1; - arguments.valid_id = 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; + arguments.id_interval = -1; + arguments.valid_id = false; - argp_parse(&argp, argc, argv, 0, 0, &arguments); - arguments.baudrateOrPort = atoi(arguments.args[1]); + argp_parse(&argp, argc, argv, 0, 0, &arguments); - 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; - if (arguments.useNetKiss) use_net_kiss = true; - mtu = arguments.mtu; + if (arguments.kiss_over_tcp) kiss_over_tcp = true; - 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.useNetKiss) { - attached_tnc = open_tcp(arguments.args[0], arguments.baudrateOrPort); + if (!kiss_over_tcp) { + arguments.baudrate = atoi(arguments.args[1]); } else { - attached_tnc = open_port(arguments.args[0]); - if (!setup_port(attached_tnc, arguments.baudrateOrPort)) { + 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.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; } - } - 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(); + } else { + attached_tnc = open_tcp(tcp_host, tcp_port); + } - return 0; -} \ No newline at end of file + 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; +} From 07eeed45f56e1999ebe63b9b0bc67470e3be9b09 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 24 Jun 2020 14:22:42 +0200 Subject: [PATCH 21/32] Cleaned up indentation --- KISS.c | 110 +++---- Serial.c | 284 ++++++++-------- TAP.c | 332 +++++++++---------- TCP.c | 2 +- tncattach.c | 916 ++++++++++++++++++++++++++-------------------------- 5 files changed, 822 insertions(+), 822 deletions(-) diff --git a/KISS.c b/KISS.c index 95a481f..40fc495 100644 --- a/KISS.c +++ b/KISS.c @@ -19,67 +19,67 @@ 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 ( (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); + } } 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/Serial.c b/Serial.c index ef91c03..09b0fa8 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 e5db123..5b0477f 100644 --- a/TAP.c +++ b/TAP.c @@ -15,184 +15,184 @@ extern char* netmask; extern void cleanup(); 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); - - int inet = socket(AF_INET, SOCK_DGRAM, 0); - if (inet == -1) { - perror("Could not open AF_INET 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 (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"); + 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); + } - // 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); - } - } + // 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); + } + } - // 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); - } - } + // 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); + } + } - 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); - } - } - } + 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; + // 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; + 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); - } - } - } - } - } - } - } - } - } - } - } + 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); + } + } + } + } + } + } + } + } + } + } + } - 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/TCP.c b/TCP.c index 5ca898e..ce92651 100644 --- a/TCP.c +++ b/TCP.c @@ -33,5 +33,5 @@ int open_tcp(char* ip, int port) { } int close_tcp(int fd) { - return close(fd); + return close(fd); } \ No newline at end of file diff --git a/tncattach.c b/tncattach.c index d46b866..30e9a25 100644 --- a/tncattach.c +++ b/tncattach.c @@ -51,214 +51,214 @@ 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); + 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); - } + 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; - } + 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); - } - } + 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; + 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; - } + if (id_interval != -1) { + time_t now = time_now(); + return now > last_id + id_interval; + } else { + return false; + } } void signal_handler(int signal) { - if (daemonize) syslog(LOG_NOTICE, "tncattach daemon exiting"); + if (daemonize) syslog(LOG_NOTICE, "tncattach daemon exiting"); - // Transmit final ID if necessary - if (id_interval != -1 && tx_since_last_id) transmit_id(); + // Transmit final ID if necessary + if (id_interval != -1 && tx_since_last_id) transmit_id(); - cleanup(); - exit(0); + cleanup(); + exit(0); } 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); + } - 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_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); + } + } - // 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))) { - 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; + 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 (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"); - } + 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); + cleanup(); + exit(1); + } + } + } + } + } + } + } else { + should_continue = false; + } + } + cleanup(); + exit(1); } const char *argp_program_version = "tncattach 0.1.8"; @@ -266,219 +266,219 @@ 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 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}, - { "noipv6", 'n', 0, 0, "Filter IPv6 traffic from reaching TNC", 4}, - { "noup", 1, 0, 0, "Only create interface, don't bring it up", 5}, - { "kisstcp", 'T', 0, 0, "Use KISS over TCP instead of serial port", 6}, - { "tcphost", 'H', "TCP_HOST", 0, "Host to connect to when using KISS over TCP", 7}, - { "tcpport", 'P', "TCP_PORT", 0, "TCP port when using KISS over TCP", 8}, - { "interval", 't', "SECONDS", 0, "Maximum interval between station identifications", 9}, - { "id", 's', "CALLSIGN", 0, "Station identification data", 10}, - { "daemon", 'd', 0, 0, "Run tncattach as a daemon", 11}, - { "verbose", 'v', 0, 0, "Enable verbose output", 12}, - { 0 } + { "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}, + { "noipv6", 'n', 0, 0, "Filter IPv6 traffic from reaching TNC", 4}, + { "noup", 1, 0, 0, "Only create interface, don't bring it up", 5}, + { "kisstcp", 'T', 0, 0, "Use KISS over TCP instead of serial port", 6}, + { "tcphost", 'H', "TCP_HOST", 0, "Host to connect to when using KISS over TCP", 7}, + { "tcpport", 'P', "TCP_PORT", 0, "TCP port when using KISS over TCP", 8}, + { "interval", 't', "SECONDS", 0, "Maximum interval between station identifications", 9}, + { "id", 's', "CALLSIGN", 0, "Station identification data", 10}, + { "daemon", 'd', 0, 0, "Run tncattach as a daemon", 11}, + { "verbose", 'v', 0, 0, "Enable verbose output", 12}, + { 0 } }; #define N_ARGS 2 struct arguments { - char *args[N_ARGS]; - char *ipv4; - char *id; - bool valid_id; - int id_interval; - int baudrate; + char *args[N_ARGS]; + char *ipv4; + 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 noipv6; - bool noup; - bool kiss_over_tcp; + int mtu; + bool tap; + bool daemon; + bool verbose; + bool set_ipv4; + bool set_netmask; + bool noipv6; + bool noup; + bool kiss_over_tcp; bool set_tcp_host; bool set_tcp_port; }; 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); - } - 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); + } + break; - 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; + 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; - 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; + 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; - case 'i': - arguments->ipv4 = arg; - arguments->set_ipv4 = true; + case 'i': + arguments->ipv4 = arg; + arguments->set_ipv4 = true; - 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; + 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); - } + default: + printf("Error: Invalid subnet mask specified\r\n"); + cleanup(); + exit(1); + } - arguments->set_netmask = true; - } else { - arguments->set_netmask = false; - ipv4_addr = (char*)malloc(strlen(arg)+1); - strcpy(ipv4_addr, arg); - } + arguments->set_netmask = true; + } else { + arguments->set_netmask = false; + ipv4_addr = (char*)malloc(strlen(arg)+1); + strcpy(ipv4_addr, arg); + } - break; + break; - case 'n': - arguments->noipv6 = true; - break; + case 'n': + arguments->noipv6 = true; + break; - case 'd': - arguments->daemon = true; - arguments->verbose = false; - break; + case 'd': + arguments->daemon = true; + arguments->verbose = false; + break; - case 'T': - arguments->kiss_over_tcp = true; - break; + case 'T': + arguments->kiss_over_tcp = true; + break; case 'H': arguments->set_tcp_host = true; @@ -491,81 +491,81 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) { tcp_port = atoi(arg); break; - case 1: - arguments->noup = true; - 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); + 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; + // 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); + 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; + break; - default: - return ARGP_ERR_UNKNOWN; - } + default: + return ARGP_ERR_UNKNOWN; + } - return 0; + return 0; } static void become_daemon() { - pid_t pid; - pid = fork(); + pid_t pid; + pid = fork(); - if (pid < 0) { - perror("Fork failed"); - exit(EXIT_FAILURE); - } + if (pid < 0) { + perror("Fork failed"); + exit(EXIT_FAILURE); + } - if (pid > 0) { - exit(0); - } + if (pid > 0) { + exit(0); + } - if (setsid() < 0) exit(1); + if (setsid() < 0) exit(1); - signal(SIGCHLD, signal_handler); - signal(SIGHUP, signal_handler); + signal(SIGCHLD, signal_handler); + signal(SIGHUP, signal_handler); - pid = fork(); - if (pid < 0) exit(1); - if (pid > 0) exit(0); + pid = fork(); + if (pid < 0) exit(1); + if (pid > 0) exit(0); - umask(0); - chdir("/"); + umask(0); + chdir("/"); - openlog("tncattach", LOG_PID, LOG_DAEMON); + openlog("tncattach", LOG_PID, LOG_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.noipv6 = false; - arguments.daemon = false; - arguments.noup = false; - arguments.id_interval = -1; - arguments.valid_id = 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; + arguments.id_interval = -1; + arguments.valid_id = false; - argp_parse(&argp, argc, argv, 0, 0, &arguments); + argp_parse(&argp, argc, argv, 0, 0, &arguments); if (arguments.kiss_over_tcp) kiss_over_tcp = true; @@ -578,57 +578,57 @@ int main(int argc, char **argv) { 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.noup) noup = true; - mtu = arguments.mtu; + + 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 (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); - } + 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(); + attached_if = open_tap(); - if (!arguments.kiss_over_tcp) { - attached_tnc = open_port(arguments.args[0]); + 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 { + } else { attached_tnc = open_tcp(tcp_host, tcp_port); - } + } - printf("TNC interface configured as %s\r\n", if_name); + 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; - + 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"); - } + become_daemon(); + syslog(LOG_NOTICE, "tncattach daemon running"); + } - read_loop(); + read_loop(); - return 0; -} + return 0; +} \ No newline at end of file From 671ea5dda05358423a24e5f1591369f93244ca96 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 24 Jun 2020 14:28:50 +0200 Subject: [PATCH 22/32] Fixed deprecated bzero and bcopy calls --- TCP.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/TCP.c b/TCP.c index ce92651..d53ae64 100644 --- a/TCP.c +++ b/TCP.c @@ -11,17 +11,15 @@ int open_tcp(char* ip, int port) { struct hostent *server; struct sockaddr_in serv_addr; - server = gethostbyname(ip); - if (server == NULL) { perror("Error resolving host"); exit(1); } - bzero((char *) &serv_addr, sizeof(serv_addr)); + memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; - bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length); + 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) { From de83bc9f21db7d23d36930696b3665bbd5b95aa7 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 24 Jun 2020 14:44:30 +0200 Subject: [PATCH 23/32] Fixed accidental removal of line --- TCP.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TCP.c b/TCP.c index d53ae64..c8e3d52 100644 --- a/TCP.c +++ b/TCP.c @@ -11,6 +11,8 @@ int open_tcp(char* ip, int port) { struct hostent *server; struct sockaddr_in serv_addr; + server = gethostbyname(ip); + if (server == NULL) { perror("Error resolving host"); exit(1); From 5bdc5d5ccc32dd1e69109787621be309abe7e5fa Mon Sep 17 00:00:00 2001 From: "Alexander W. Janssen" Date: Sat, 12 Sep 2020 14:31:42 +0200 Subject: [PATCH 24/32] created manualpage and adapted makefile for installing manpage --- tncattach.8 | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 tncattach.8 diff --git a/tncattach.8 b/tncattach.8 new file mode 100644 index 0000000..f41390f --- /dev/null +++ b/tncattach.8 @@ -0,0 +1,165 @@ +.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 From 015f8a73fb060dc2ace58fdd338e713a3f60508d Mon Sep 17 00:00:00 2001 From: "Alexander W. Janssen" Date: Sat, 12 Sep 2020 14:34:03 +0200 Subject: [PATCH 25/32] now makefile for real. --- makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/makefile b/makefile index 677cd0d..c47ae2c 100644 --- a/makefile +++ b/makefile @@ -20,7 +20,9 @@ install: @echo "Installing tncattach..." chmod a+x tncattach cp ./tncattach /usr/local/sbin/ + install -m 644 -o root -g root tncattach.8 /usr/local/man/man8 uninstall: @echo "Uninstalling tncattach" rm /usr/local/sbin/tncattach + rm /usr/local/man/man8/tncattach.8 From 8944821ba8f0f0177b7a7559caaa9115657dd4e0 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 28 Sep 2020 11:07:10 +0200 Subject: [PATCH 26/32] Added mandb update to makefile --- makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/makefile b/makefile index c47ae2c..d996be6 100644 --- a/makefile +++ b/makefile @@ -20,7 +20,9 @@ install: @echo "Installing tncattach..." chmod a+x tncattach cp ./tncattach /usr/local/sbin/ - install -m 644 -o root -g root tncattach.8 /usr/local/man/man8 + mkdir -p /usr/local/man/man8 + install -m 644 -o root -g root tncattach.8 /usr/local/man/man8/tncattach.8 + mandb -f /usr/local/man/man8/tncattach.8 uninstall: @echo "Uninstalling tncattach" From b4a80a1e7a9097551a4c29e5d0dca54a52011f22 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 28 Sep 2020 11:11:27 +0200 Subject: [PATCH 27/32] Cleaned install output --- makefile | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/makefile b/makefile index d996be6..19f623d 100644 --- a/makefile +++ b/makefile @@ -18,11 +18,14 @@ tncattach: install: @echo "Installing tncattach..." - chmod a+x tncattach + @chmod a+x tncattach cp ./tncattach /usr/local/sbin/ - mkdir -p /usr/local/man/man8 - install -m 644 -o root -g root tncattach.8 /usr/local/man/man8/tncattach.8 - mandb -f /usr/local/man/man8/tncattach.8 + @echo "Installing man page..." + @mkdir -p /usr/local/man/man8 + @install -m 644 -o root -g root tncattach.8 /usr/local/man/man8/tncattach.8 + @echo "Updating mandb..." + @mandb -f /usr/local/man/man8/tncattach.8 2> /dev/null 1> /dev/null + @echo "Done" uninstall: @echo "Uninstalling tncattach" From 5129dda62630ffbe9af968c23aeab5d7121c35af Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 28 Sep 2020 11:18:02 +0200 Subject: [PATCH 28/32] Argparse variable init. Fixes #4. --- tncattach.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tncattach.c b/tncattach.c index 30e9a25..85d80ee 100644 --- a/tncattach.c +++ b/tncattach.c @@ -564,6 +564,7 @@ int main(int argc, char **argv) { arguments.noup = false; arguments.id_interval = -1; arguments.valid_id = false; + arguments.kiss_over_tcp = false; argp_parse(&argp, argc, argv, 0, 0, &arguments); From bb9ff10158c04a37cb3221152885a083e3059909 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 28 Sep 2020 11:39:28 +0200 Subject: [PATCH 29/32] Version updated to 0.1.9 --- tncattach.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tncattach.c b/tncattach.c index 85d80ee..f372daf 100644 --- a/tncattach.c +++ b/tncattach.c @@ -261,7 +261,7 @@ void read_loop(void) { exit(1); } -const char *argp_program_version = "tncattach 0.1.8"; +const char *argp_program_version = "tncattach 0.1.9"; 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 args_doc[] = "port baudrate"; From d9b3d2b6aefd50c6c535dbb42a8a80bfa84f1bfd Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 11 Oct 2021 15:29:06 +0200 Subject: [PATCH 30/32] Update README.md --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b87e99..943d8ed 100644 --- a/README.md +++ b/README.md @@ -133,4 +133,11 @@ On some versions of Raspbian (and probably other operating systems), the DHCP cl # of /etc/dhcpcd.conf to prevent dhcpcd from changing MTU denyinterfaces tnc0 -``` \ No newline at end of file +``` + +## 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 From 54b4ae14f85c444557d76386e922399a0bb44ff9 Mon Sep 17 00:00:00 2001 From: Coelacanthus Date: Sat, 27 May 2023 21:56:32 +0800 Subject: [PATCH 31/32] chore: refine Makefile with Makefile Conventions Users can now do the following: - use DESTDIR to specify another root dir - use PREFIX to specify another install path except for /usr/local, e.g. /usr for the system package. - load custom CFLAGS and LDFLAGS from environment variables - use CC to specify the C compiler These features are helpful to package it into Linux Distribution. Signed-off-by: Coelacanthus --- makefile | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/makefile b/makefile index 19f623d..8d6ba66 100644 --- a/makefile +++ b/makefile @@ -1,33 +1,35 @@ .DEFAULT_GOAL := all .PHONY: all clean install uninstall tncattach -compiler = gcc -flags = -Wall -std=gnu11 -static-libgcc +RM ?= rm +INSTALL ?= install +CC ?= gcc +CFLAGS ?= -Wall -std=gnu11 -static-libgcc +LDFLAGS ?= +PREFIX ?= /usr/local all: tncattach rebuild: clean all clean: @echo "Cleaning tncattach build..." - @rm -f tncattach + $(RM) -f tncattach tncattach: @echo "Making tncattach..." - @echo "Compiling with: ${compiler}" - ${compiler} ${flags} tncattach.c Serial.c TCP.c KISS.c TAP.c -o tncattach -Wall + @echo "Compiling with: $(CC)" + $(CC) $(CFLAGS) $(LDFLAGS) tncattach.c Serial.c TCP.c KISS.c TAP.c -o tncattach install: @echo "Installing tncattach..." - @chmod a+x tncattach - cp ./tncattach /usr/local/sbin/ + $(INSTALL) -d $(DESTDIR)/$(PREFIX)/bin + $(INSTALL) -Dm755 tncattach $(DESTDIR)/$(PREFIX)/bin/tncattach @echo "Installing man page..." - @mkdir -p /usr/local/man/man8 - @install -m 644 -o root -g root tncattach.8 /usr/local/man/man8/tncattach.8 - @echo "Updating mandb..." - @mandb -f /usr/local/man/man8/tncattach.8 2> /dev/null 1> /dev/null - @echo "Done" + gzip -9 tncattach.8 + $(INSTALL) -d $(DESTDIR)/$(PREFIX)/share/man/man8 + $(INSTALL) -Dm644 tncattach.8.gz $(DESTDIR)/$(PREFIX)/share/man/man8/tncattach.8.gz uninstall: @echo "Uninstalling tncattach" - rm /usr/local/sbin/tncattach - rm /usr/local/man/man8/tncattach.8 + $(RM) $(DESTDIR)/$(PREFIX)/bin/tncattach + $(RM) $(DESTDIR)/$(PREFIX)/share/man/man8/tncattach.8.gz From c9104217968bebf95dd50e17e5596b3671efd608 Mon Sep 17 00:00:00 2001 From: "Tristan B. Velloza Kildaire" Date: Sun, 22 Dec 2024 15:27:16 +0200 Subject: [PATCH 32/32] Add IPv6 support (command-line and tuntap) (#16) * tncattach - Added `set_ipv6`, `ipv6_addr` and `netmask_v6` globals - Added `--ipv6` option - If `n` is specified (i.e. `--noipv6`) then bail out if the user specified IPv6 addressing with `--ipv6 ` - Added `6` parsing, this is for when we have an `--ipv6` and want to set the address * tncattach - Removed global `netmask_v6` which is never used * TAP - Added initial code that I have been working on * TAP - Removed old (bad) code `trySixSet2(...)` * TAP - Clean up * TAP - Clean up * TAP - Added device type checl * TAP - Randomize the remaining octets of the link-local address * TAP - Seed random number generatro based off of current time * TAP - Removed TODO * TAP_ - Cleaned up a little bit - The `mtu` (on Luinux) must be `1280` of greater, else IPv6 won't work (and the address will not be allowed to be added) * TAP - More clean up * TAP - Moved mtu check for Ipv6 to be earlier * TAP - Added missing `cleanup()` calls * TAP - Aded newline * TAP - Now link-local and normal v6 can be requested independently tncattach - Added `--ll` mode to add link-local * TAP - More cleanup * TAP - Removed code to generate a link-local address * tncattach - Added error handling for mtu with IPv6 support * TAP - Removed uneeded imports * TAP - Cleaned up * Cleaned up * TAP - Removed duplicate import * TAP - Removed error checking code from there * TAP - Cleaned up - Remove dneed for `link_local_v6` tncattach - Removed `link_local_v6` * TAP - Close control socket when done * tncattach - Removed debug print * TAP - Cleaned up * TAP - Cleaned up * TAP - Cleaned up * TAP - Added check for bad open * TAP - Added another check * tncattach - Now parse IPv6 address and prefix in opt-args TAP - Removed parsing from here * Fixed mtu stuff * tncattach - Typo fix * Work * tncattach - WHen parsing the `prefixPart_s` (the prefix length), only continue if the text is a number, if not then bail out with an error * tncattach - Only allow prefix length of between 0 to 128 * TAP - Cleaned up * TAP - Cleaned up * tncattach - Cleaned up --- TAP.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++- TAP.h | 1 + tncattach.c | 103 +++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 202 insertions(+), 12 deletions(-) diff --git a/TAP.c b/TAP.c index 5b0477f..289fd76 100644 --- a/TAP.c +++ b/TAP.c @@ -5,15 +5,72 @@ 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); @@ -43,10 +100,11 @@ int open_tap(void) { 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"); + perror("Could not open control socket"); cleanup(); exit(1); } else { @@ -182,6 +240,54 @@ int open_tap(void) { } } } + + 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); + } } } } @@ -195,4 +301,4 @@ int open_tap(void) { int close_tap(int tap_fd) { return close(tap_fd); -} \ No newline at end of file +} diff --git a/TAP.h b/TAP.h index eeb6a14..07f003a 100644 --- a/TAP.h +++ b/TAP.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "Constants.h" int open_tap(void); diff --git a/tncattach.c b/tncattach.c index f372daf..7d825b9 100644 --- a/tncattach.c +++ b/tncattach.c @@ -34,11 +34,16 @@ 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; @@ -269,15 +274,17 @@ 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}, - { "noipv6", 'n', 0, 0, "Filter IPv6 traffic from reaching TNC", 4}, - { "noup", 1, 0, 0, "Only create interface, don't bring it up", 5}, - { "kisstcp", 'T', 0, 0, "Use KISS over TCP instead of serial port", 6}, - { "tcphost", 'H', "TCP_HOST", 0, "Host to connect to when using KISS over TCP", 7}, - { "tcpport", 'P', "TCP_PORT", 0, "TCP port when using KISS over TCP", 8}, - { "interval", 't', "SECONDS", 0, "Maximum interval between station identifications", 9}, - { "id", 's', "CALLSIGN", 0, "Station identification data", 10}, - { "daemon", 'd', 0, 0, "Run tncattach as a daemon", 11}, - { "verbose", 'v', 0, 0, "Enable verbose output", 12}, + { "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 } }; @@ -285,6 +292,7 @@ static struct argp_option options[] = { struct arguments { char *args[N_ARGS]; char *ipv4; + char *ipv6; char *id; bool valid_id; int id_interval; @@ -296,6 +304,9 @@ struct arguments { 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; @@ -321,6 +332,13 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) { printf("Error: Invalid MTU specified\r\n\r\n"); argp_usage(state); } + + 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); + } + break; case 't': @@ -466,9 +484,70 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) { } break; + case '6': + if(arguments->noipv6) + { + perror("Sorry, but you had noipv6 set yet want to use ipv6?\n"); + exit(EXIT_FAILURE); + } + + char* ipPart_s = strtok(arg, "/"); + char* prefixPart_s = strtok(NULL, "/"); + printf("ipPart_s: %s\n", ipPart_s); + + if(!prefixPart_s) + { + printf("No prefix length was provided\n"); + exit(1); + } + printf("prefixPart_s: %s\n", prefixPart_s); + + 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': @@ -559,6 +638,9 @@ int main(int argc, char **argv) { 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; @@ -586,6 +668,7 @@ int main(int argc, char **argv) { 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; @@ -632,4 +715,4 @@ int main(int argc, char **argv) { read_loop(); return 0; -} \ No newline at end of file +}