Compare commits

..

25 Commits

Author SHA1 Message Date
markqvist
bb4b1917d5
Merge pull request #13 from CoelacanthusHex/fix-makefile
chore: refine Makefile with Makefile Conventions
2023-05-31 18:20:45 +02:00
Coelacanthus
54b4ae14f8
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 <CoelacanthusHex@gmail.com>
2023-05-27 22:35:58 +08:00
Mark Qvist
d9b3d2b6ae
Update README.md 2021-10-11 15:29:06 +02:00
Mark Qvist
bb9ff10158 Version updated to 0.1.9 2020-09-28 11:39:28 +02:00
Mark Qvist
5129dda626 Argparse variable init. Fixes #4. 2020-09-28 11:18:02 +02:00
Mark Qvist
b4a80a1e7a Cleaned install output 2020-09-28 11:11:27 +02:00
Mark Qvist
8944821ba8 Added mandb update to makefile 2020-09-28 11:07:10 +02:00
Mark Qvist
ef700b3244
Merge pull request #5 from yalla/master
Created manualpage for tncattach
2020-09-28 10:17:17 +02:00
Alexander W. Janssen
015f8a73fb now makefile for real. 2020-09-12 14:34:03 +02:00
Alexander W. Janssen
5bdc5d5ccc created manualpage and adapted makefile for installing manpage 2020-09-12 14:31:42 +02:00
Mark Qvist
de83bc9f21 Fixed accidental removal of line 2020-06-24 14:44:30 +02:00
Mark Qvist
671ea5dda0 Fixed deprecated bzero and bcopy calls 2020-06-24 14:28:50 +02:00
Mark Qvist
07eeed45f5 Cleaned up indentation 2020-06-24 14:22:42 +02:00
Mark Qvist
c2beeee944 Cleaned up TCP options 2020-06-24 14:15:49 +02:00
Mark Qvist
eae91f349b Updated readme to include KISS over TCP 2020-06-24 14:13:40 +02:00
Mark Qvist
b996f38689 Fixed typo 2020-06-24 14:02:02 +02:00
Mark Qvist
d3ff2f207a Updated makefile 2020-06-24 14:01:35 +02:00
Mark Qvist
26f1e48b19 Renamed TCP files 2020-06-24 14:00:23 +02:00
Mark Qvist
5c8ddcd992 Merge branch 'valentintintin-tcp-kiss' 2020-06-24 12:31:17 +02:00
Mark Qvist
dada2f3775 Makefile update 2020-06-24 12:28:49 +02:00
Valentin Saugnier
41086b2e0c Add TCP Kiss 2020-06-16 21:55:25 +02:00
Mark Qvist
1c7b30b995 Updated version number 2020-06-01 23:01:25 +02:00
Mark Qvist
30207d6691 Added ARP configuration for interface 2020-06-01 22:54:15 +02:00
Mark Qvist
b438e5fb5a Implemented setting txqueuelen on created interface 2020-06-01 22:25:10 +02:00
Mark Qvist
e7a831b41d Updated readme 2020-05-28 16:25:41 +02:00
10 changed files with 1121 additions and 756 deletions

View File

@ -6,4 +6,10 @@
#define MTU_MIN 74 #define MTU_MIN 74
#define MTU_MAX 1522 #define MTU_MAX 1522
#define MTU_DEFAULT 329 #define MTU_DEFAULT 329
#define TXQUEUELEN 10
// ARP timings, in seconds
#define ARP_BASE_REACHABLE_TIME 300
#define ARP_RETRANS_TIME 5

110
KISS.c
View File

@ -19,67 +19,67 @@ extern int device_type;
extern void cleanup(void); extern void cleanup(void);
void kiss_frame_received(int frame_len) { 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) ) { 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); int written = write(attached_if, frame_buffer, frame_len);
if (written == -1) { 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); 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) { } else if (written != frame_len) {
if (!daemonize) printf("Error: Could only write %d of %d bytes to interface", written, frame_len); if (!daemonize) printf("Error: Could only write %d of %d bytes to interface", written, frame_len);
cleanup(); cleanup();
exit(1); exit(1);
} }
if (verbose && !daemonize) printf("Got %d bytes from TNC, wrote %d bytes to interface\r\n", frame_len, written); 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) { void kiss_serial_read(uint8_t sbyte) {
if (IN_FRAME && sbyte == FEND && kiss_command == CMD_DATA) { if (IN_FRAME && sbyte == FEND && kiss_command == CMD_DATA) {
IN_FRAME = false; IN_FRAME = false;
kiss_frame_received(frame_len); kiss_frame_received(frame_len);
} else if (sbyte == FEND) { } else if (sbyte == FEND) {
IN_FRAME = true; IN_FRAME = true;
kiss_command = CMD_UNKNOWN; kiss_command = CMD_UNKNOWN;
frame_len = 0; frame_len = 0;
} else if (IN_FRAME && frame_len < MAX_PAYLOAD) { } else if (IN_FRAME && frame_len < MAX_PAYLOAD) {
// Have a look at the command byte first // Have a look at the command byte first
if (frame_len == 0 && kiss_command == CMD_UNKNOWN) { if (frame_len == 0 && kiss_command == CMD_UNKNOWN) {
// Strip of port nibble // Strip of port nibble
kiss_command = sbyte & 0x0F; kiss_command = sbyte & 0x0F;
} else if (kiss_command == CMD_DATA) { } else if (kiss_command == CMD_DATA) {
if (sbyte == FESC) { if (sbyte == FESC) {
ESCAPE = true; ESCAPE = true;
} else { } else {
if (ESCAPE) { if (ESCAPE) {
if (sbyte == TFEND) sbyte = FEND; if (sbyte == TFEND) sbyte = FEND;
if (sbyte == TFESC) sbyte = FESC; if (sbyte == TFESC) sbyte = FESC;
ESCAPE = false; ESCAPE = false;
} }
if (frame_len < MAX_PAYLOAD) { if (frame_len < MAX_PAYLOAD) {
frame_buffer[frame_len++] = sbyte; frame_buffer[frame_len++] = sbyte;
} }
} }
} }
} }
} }
int kiss_write_frame(int serial_port, uint8_t* buffer, int frame_len) { int kiss_write_frame(int serial_port, uint8_t* buffer, int frame_len) {
int write_len = 0; int write_len = 0;
write_buffer[write_len++] = FEND; write_buffer[write_len++] = FEND;
write_buffer[write_len++] = CMD_DATA; write_buffer[write_len++] = CMD_DATA;
for (int i = 0; i < frame_len; i++) { for (int i = 0; i < frame_len; i++) {
uint8_t byte = buffer[i]; uint8_t byte = buffer[i];
if (byte == FEND) { if (byte == FEND) {
write_buffer[write_len++] = FESC; write_buffer[write_len++] = FESC;
write_buffer[write_len++] = TFEND; write_buffer[write_len++] = TFEND;
} else if (byte == FESC) { } else if (byte == FESC) {
write_buffer[write_len++] = FESC; write_buffer[write_len++] = FESC;
write_buffer[write_len++] = TFESC; write_buffer[write_len++] = TFESC;
} else { } else {
write_buffer[write_len++] = byte; write_buffer[write_len++] = byte;
} }
} }
write_buffer[write_len++] = FEND; write_buffer[write_len++] = FEND;
return write(serial_port, write_buffer, write_len); return write(serial_port, write_buffer, write_len);
} }

View File

@ -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. 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 ```sh
# If you don't already have a compiler installed # If you don't already have a compiler installed
@ -34,14 +34,17 @@ Usage: tncattach [OPTION...] port baudrate
Attach TNC devices as system network interfaces Attach TNC devices as system network interfaces
-d, --daemon Run tncattach as a daemon -m, --mtu=MTU Specify interface MTU
-e, --ethernet Create a full ethernet device -e, --ethernet Create a full ethernet device
-i, --ipv4=IP_ADDRESS Configure an IPv4 address on interface -i, --ipv4=IP_ADDRESS Configure an IPv4 address on interface
-m, --mtu=MTU Specify interface MTU
-n, --noipv6 Filter IPv6 traffic from reaching TNC -n, --noipv6 Filter IPv6 traffic from reaching TNC
--noup Only create interface, don't bring it up --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 -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 -v, --verbose Enable verbose output
-?, --help Give this help list -?, --help Give this help list
--usage Give a short usage message --usage Give a short usage message
@ -50,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. 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. 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. 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.
@ -75,6 +80,13 @@ Create an ethernet device with a USB-connected TNC, set the MTU, filter IPv6 tra
sudo tncattach /dev/ttyUSB0 115200 --ethernet --mtu 576 --noipv6 --ipv4 10.92.0.10/24 sudo tncattach /dev/ttyUSB0 115200 --ethernet --mtu 576 --noipv6 --ipv4 10.92.0.10/24
``` ```
Create an ethernet device with a TCP-connected TNC, set the MTU, filter IPv6 traffic, and set an IPv4 address:
```sh
# Attach interface
sudo tncattach -T -H localhost -P 8001 --ethernet --mtu 576 --noipv6 --ipv4 10.92.0.10/24
```
You can interact with the interface like any other using the __ip__ or __ifconfig__ utilities: You can interact with the interface like any other using the __ip__ or __ifconfig__ utilities:
```sh ```sh
@ -111,6 +123,7 @@ tnc0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 400
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
``` ```
## Worth Knowing on Raspbian ## 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: 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:
@ -120,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 # of /etc/dhcpcd.conf to prevent dhcpcd from changing MTU
denyinterfaces tnc0 denyinterfaces tnc0
``` ```
## Support tncattach development
You can help support the continued development of open, free and private communications systems by donating via one of the following channels:
- Ethereum: 0x81F7B979fEa6134bA9FD5c701b3501A2e61E897a
- Bitcoin: 3CPmacGm34qYvR6XWLVEJmi2aNe3PZqUuq
- Ko-Fi: https://ko-fi.com/markqvist

284
Serial.c
View File

@ -3,172 +3,172 @@
extern void cleanup(); extern void cleanup();
int open_port(char* port) { int open_port(char* port) {
int fd; int fd;
fd = open(port, O_RDWR | O_NOCTTY | O_SYNC | O_NDELAY); fd = open(port, O_RDWR | O_NOCTTY | O_SYNC | O_NDELAY);
if (fd == -1) { if (fd == -1) {
perror("The serial port could not be opened"); perror("The serial port could not be opened");
cleanup(); cleanup();
exit(1); exit(1);
} else { } else {
fcntl(fd, F_SETFL, 0); fcntl(fd, F_SETFL, 0);
} }
return fd; return fd;
} }
int close_port(int fd) { int close_port(int fd) {
return close(fd); return close(fd);
} }
void set_speed(void *tty_s, int speed) { void set_speed(void *tty_s, int speed) {
cfsetospeed(tty_s, speed); cfsetospeed(tty_s, speed);
cfsetispeed(tty_s, speed); cfsetispeed(tty_s, speed);
} }
bool setup_port(int fd, int speed) { bool setup_port(int fd, int speed) {
struct termios tty; struct termios tty;
if (tcgetattr(fd, &tty) != 0) { if (tcgetattr(fd, &tty) != 0) {
perror("Error setting port speed, could not read port parameters"); perror("Error setting port speed, could not read port parameters");
return false; return false;
} }
switch (speed) { switch (speed) {
case 0: case 0:
set_speed(&tty, B0); set_speed(&tty, B0);
break; break;
case 50: case 50:
set_speed(&tty, B50); set_speed(&tty, B50);
break; break;
case 75: case 75:
set_speed(&tty, B75); set_speed(&tty, B75);
break; break;
case 110: case 110:
set_speed(&tty, B110); set_speed(&tty, B110);
break; break;
case 134: case 134:
set_speed(&tty, B134); set_speed(&tty, B134);
break; break;
case 150: case 150:
set_speed(&tty, B150); set_speed(&tty, B150);
break; break;
case 200: case 200:
set_speed(&tty, B200); set_speed(&tty, B200);
break; break;
case 300: case 300:
set_speed(&tty, B300); set_speed(&tty, B300);
break; break;
case 600: case 600:
set_speed(&tty, B600); set_speed(&tty, B600);
break; break;
case 1200: case 1200:
set_speed(&tty, B1200); set_speed(&tty, B1200);
break; break;
case 2400: case 2400:
set_speed(&tty, B2400); set_speed(&tty, B2400);
break; break;
case 4800: case 4800:
set_speed(&tty, B4800); set_speed(&tty, B4800);
break; break;
case 9600: case 9600:
set_speed(&tty, B9600); set_speed(&tty, B9600);
break; break;
case 19200: case 19200:
set_speed(&tty, B19200); set_speed(&tty, B19200);
break; break;
case 38400: case 38400:
set_speed(&tty, B38400); set_speed(&tty, B38400);
break; break;
case 57600: case 57600:
set_speed(&tty, B57600); set_speed(&tty, B57600);
break; break;
case 115200: case 115200:
set_speed(&tty, B115200); set_speed(&tty, B115200);
break; break;
case 230400: case 230400:
set_speed(&tty, B230400); set_speed(&tty, B230400);
break; break;
default: default:
printf("Error: Invalid port speed %d specified\r\n", speed); printf("Error: Invalid port speed %d specified\r\n", speed);
cleanup(); cleanup();
exit(1); exit(1);
return false; return false;
} }
// Set 8-bit characters, no parity, one stop bit // Set 8-bit characters, no parity, one stop bit
tty.c_cflag |= CS8; tty.c_cflag |= CS8;
tty.c_cflag &= ~PARENB; tty.c_cflag &= ~PARENB;
tty.c_cflag &= ~CSTOPB; tty.c_cflag &= ~CSTOPB;
// Disable hardware flow control // Disable hardware flow control
tty.c_cflag &= ~CRTSCTS; tty.c_cflag &= ~CRTSCTS;
// Enable reading and ignore modem // Enable reading and ignore modem
// control lines // control lines
tty.c_cflag |= CREAD | CLOCAL; tty.c_cflag |= CREAD | CLOCAL;
// Disable canonical mode, echo // Disable canonical mode, echo
// and signal characters. // and signal characters.
tty.c_lflag &= ~ICANON; tty.c_lflag &= ~ICANON;
tty.c_lflag &= ~ECHO; tty.c_lflag &= ~ECHO;
tty.c_lflag &= ~ECHOE; tty.c_lflag &= ~ECHOE;
tty.c_lflag &= ~ECHONL; tty.c_lflag &= ~ECHONL;
tty.c_lflag &= ~ISIG; tty.c_lflag &= ~ISIG;
// Disable processing of input, // Disable processing of input,
// just pass the raw data. // just pass the raw data.
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL);
// Disable XON/XOFF software flow control. // Disable XON/XOFF software flow control.
tty.c_iflag &= ~(IXON | IXOFF | IXANY); tty.c_iflag &= ~(IXON | IXOFF | IXANY);
// Disable processing output bytes // Disable processing output bytes
// and new line conversions // and new line conversions
tty.c_oflag &= ~OPOST; tty.c_oflag &= ~OPOST;
tty.c_oflag &= ~ONLCR; tty.c_oflag &= ~ONLCR;
// Block forever until at least one byte is read. // Block forever until at least one byte is read.
tty.c_cc[VMIN] = 1; tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 0; tty.c_cc[VTIME] = 0;
// TODO: Check these // TODO: Check these
// Prevent conversion of tabs to spaces (NOT PRESENT IN LINUX) // Prevent conversion of tabs to spaces (NOT PRESENT IN LINUX)
// tty.c_oflag &= ~OXTABS; // tty.c_oflag &= ~OXTABS;
// Prevent removal of C-d chars (0x004) in output (NOT PRESENT IN LINUX) // Prevent removal of C-d chars (0x004) in output (NOT PRESENT IN LINUX)
// tty.c_oflag &= ~ONOEOT; // tty.c_oflag &= ~ONOEOT;
if (tcsetattr(fd, TCSANOW, &tty) != 0) { if (tcsetattr(fd, TCSANOW, &tty) != 0) {
perror("Could not configure serial port parameters"); perror("Could not configure serial port parameters");
return false; return false;
} else { } else {
return true; return true;
} }
} }
bool set_port_blocking(int fd, bool should_block) { bool set_port_blocking(int fd, bool should_block) {
struct termios tty; struct termios tty;
memset(&tty, 0, sizeof tty); memset(&tty, 0, sizeof tty);
if (tcgetattr(fd, &tty) != 0) { if (tcgetattr(fd, &tty) != 0) {
perror("Error configuring port blocking behaviour, could not read port parameters"); perror("Error configuring port blocking behaviour, could not read port parameters");
return false; return false;
} else { } else {
// TODO: Implement this correctly // TODO: Implement this correctly
if (should_block) { if (should_block) {
// Block forever until at least one byte is read. // Block forever until at least one byte is read.
tty.c_cc[VMIN] = 1; tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 0; tty.c_cc[VTIME] = 0;
} else { } else {
// Never block, always return immediately with // Never block, always return immediately with
// whatever is available. // whatever is available.
tty.c_cc[VMIN] = 0; tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 0; tty.c_cc[VTIME] = 0;
} }
if (tcsetattr(fd, TCSANOW, &tty) != 0) { if (tcsetattr(fd, TCSANOW, &tty) != 0) {
perror("Could not set port parameters while configuring blocking behaviour"); perror("Could not set port parameters while configuring blocking behaviour");
return false; return false;
} else { } else {
return true; return true;
} }
} }
} }

279
TAP.c
View File

@ -15,125 +15,184 @@ extern char* netmask;
extern void cleanup(); extern void cleanup();
int open_tap(void) { int open_tap(void) {
struct ifreq ifr; struct ifreq ifr;
int fd = open("/dev/net/tun", O_RDWR); int fd = open("/dev/net/tun", O_RDWR);
if (fd < 0) { if (fd < 0) {
perror("Could not open clone device"); perror("Could not open clone device");
exit(1); exit(1);
} else { } else {
memset(&ifr, 0, sizeof(ifr)); memset(&ifr, 0, sizeof(ifr));
// TODO: Enable PI header again? // TODO: Enable PI header again?
if (device_type == IF_TAP) { if (device_type == IF_TAP) {
ifr.ifr_flags = IFF_TAP | IFF_NO_PI; ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
} else if (device_type == IF_TUN) { } else if (device_type == IF_TUN) {
ifr.ifr_flags = IFF_TUN; ifr.ifr_flags = IFF_TUN;
} else { } else {
printf("Error: Unsupported interface type\r\n"); printf("Error: Unsupported interface type\r\n");
cleanup(); cleanup();
exit(1); exit(1);
} }
strcpy(tap_name, "tnc%d"); strcpy(tap_name, "tnc%d");
strncpy(ifr.ifr_name, tap_name, IFNAMSIZ); strncpy(ifr.ifr_name, tap_name, IFNAMSIZ);
if (ioctl(fd, TUNSETIFF, &ifr) < 0) { if (ioctl(fd, TUNSETIFF, &ifr) < 0) {
perror("Could not configure network interface"); perror("Could not configure network interface");
exit(1); exit(1);
} else { } else {
strcpy(if_name, ifr.ifr_name); strcpy(if_name, ifr.ifr_name);
int inet = socket(AF_INET, SOCK_DGRAM, 0); int inet = socket(AF_INET, SOCK_DGRAM, 0);
if (inet == -1) { if (inet == -1) {
perror("Could not open AF_INET socket\r\n"); perror("Could not open AF_INET socket");
cleanup(); cleanup();
exit(1); exit(1);
} else { } else {
if (ioctl(inet, SIOCGIFMTU, &ifr) < 0) { if (ioctl(inet, SIOCGIFMTU, &ifr) < 0) {
perror("Could not get interface flags from kernel"); perror("Could not get interface flags from kernel");
close(inet); close(inet);
cleanup(); cleanup();
exit(1); exit(1);
} else { } else {
ifr.ifr_mtu = mtu; ifr.ifr_mtu = mtu;
if (ioctl(inet, SIOCSIFMTU, &ifr) < 0) { if (ioctl(inet, SIOCSIFMTU, &ifr) < 0) {
perror("Could not configure interface MTU"); perror("Could not configure interface MTU");
close(inet); close(inet);
cleanup(); cleanup();
exit(1); exit(1);
} }
if (!noup) { // Configure TX queue length
if (ioctl(inet, SIOCGIFFLAGS, &ifr) < 0) { if (ioctl(inet, SIOCGIFTXQLEN, &ifr) < 0) {
perror("Could not get interface flags from kernel"); perror("Could not get interface flags from kernel");
close(inet); close(inet);
cleanup(); cleanup();
exit(1); exit(1);
} else { } else {
ifr.ifr_flags |= IFF_UP | IFF_RUNNING; ifr.ifr_qlen = TXQUEUELEN;
if (ioctl(inet, SIOCSIFFLAGS, &ifr) < 0) { if (ioctl(inet, SIOCSIFTXQLEN, &ifr) < 0) {
perror("Could not bring up interface"); perror("Could not set interface TX queue length");
close(inet); close(inet);
cleanup(); cleanup();
exit(1); exit(1);
} else { }
if (set_ipv4) { }
struct ifreq a_ifr;
struct sockaddr_in addr, snm;
memset(&a_ifr, 0, sizeof(a_ifr)); // Configure ARP characteristics
memset(&addr, 0, sizeof(addr)); char path_buf[256];
memset(&snm, 0, sizeof(addr)); if (device_type == IF_TAP) {
strncpy(a_ifr.ifr_name, ifr.ifr_name, IFNAMSIZ); snprintf(path_buf, sizeof(path_buf), "/proc/sys/net/ipv4/neigh/%s/base_reachable_time_ms", ifr.ifr_name);
addr.sin_family = AF_INET; int arp_fd = open(path_buf, O_WRONLY);
snm.sin_family = AF_INET; if (arp_fd < 0) {
perror("Could not open proc entry for ARP parameters");
close(inet);
cleanup();
exit(1);
} else {
if (dprintf(arp_fd, "%d", ARP_BASE_REACHABLE_TIME*1000) <= 0) {
perror("Could not configure interface ARP parameter base_reachable_time_ms");
close(inet);
close(arp_fd);
cleanup();
exit(1);
} else {
close(arp_fd);
}
}
int addr_conversion = inet_pton(AF_INET, ipv4_addr, &(addr.sin_addr)); snprintf(path_buf, sizeof(path_buf), "/proc/sys/net/ipv4/neigh/%s/retrans_time_ms", ifr.ifr_name);
if (addr_conversion != 1) { arp_fd = open(path_buf, O_WRONLY);
printf("Error: Invalid IPv4 address specified\r\n"); if (arp_fd < 0) {
close(inet); perror("Could not open proc entry for ARP parameters");
cleanup(); close(inet);
exit(1); cleanup();
} else { exit(1);
a_ifr.ifr_addr = *(struct sockaddr*)&addr; } else {
if (ioctl(inet, SIOCSIFADDR, &a_ifr) < 0) { if (dprintf(arp_fd, "%d", ARP_RETRANS_TIME*1000) <= 0) {
perror("Could not set IP-address"); perror("Could not configure interface ARP parameter retrans_time_ms");
close(inet); close(inet);
cleanup(); close(arp_fd);
exit(1); cleanup();
} else { exit(1);
if (set_netmask) { } else {
int snm_conversion = inet_pton(AF_INET, netmask, &(snm.sin_addr)); close(arp_fd);
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; // Bring up if requested
} if (!noup) {
} if (ioctl(inet, SIOCGIFFLAGS, &ifr) < 0) {
perror("Could not get interface flags from kernel");
close(inet);
cleanup();
exit(1);
} else {
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
if (ioctl(inet, SIOCSIFFLAGS, &ifr) < 0) {
perror("Could not bring up interface");
close(inet);
cleanup();
exit(1);
} else {
if (set_ipv4) {
struct ifreq a_ifr;
struct sockaddr_in addr, snm;
memset(&a_ifr, 0, sizeof(a_ifr));
memset(&addr, 0, sizeof(addr));
memset(&snm, 0, sizeof(addr));
strncpy(a_ifr.ifr_name, ifr.ifr_name, IFNAMSIZ);
addr.sin_family = AF_INET;
snm.sin_family = AF_INET;
int addr_conversion = inet_pton(AF_INET, ipv4_addr, &(addr.sin_addr));
if (addr_conversion != 1) {
printf("Error: Invalid IPv4 address specified\r\n");
close(inet);
cleanup();
exit(1);
} else {
a_ifr.ifr_addr = *(struct sockaddr*)&addr;
if (ioctl(inet, SIOCSIFADDR, &a_ifr) < 0) {
perror("Could not set IP-address");
close(inet);
cleanup();
exit(1);
} else {
if (set_netmask) {
int snm_conversion = inet_pton(AF_INET, netmask, &(snm.sin_addr));
if (snm_conversion != 1) {
printf("Error: Invalid subnet mask specified\r\n");
close(inet);
cleanup();
exit(1);
} else {
a_ifr.ifr_addr = *(struct sockaddr*)&snm;
if (ioctl(inet, SIOCSIFNETMASK, &a_ifr) < 0) {
perror("Could not set subnet mask");
close(inet);
cleanup();
exit(1);
}
}
}
}
}
}
}
}
}
}
}
return fd;
}
}
} }
int close_tap(int tap_fd) { int close_tap(int tap_fd) {
return close(tap_fd); return close(tap_fd);
} }

37
TCP.c Normal file
View File

@ -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);
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
memcpy(server->h_addr, &serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(port);
if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("Could not connect TCP socket");
exit(1);
}
return sockfd;
}
int close_tcp(int fd) {
return close(fd);
}

9
TCP.h Normal file
View File

@ -0,0 +1,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
int open_tcp(char* ip, int port);
int close_tcp(int fd);

View File

@ -1,26 +1,35 @@
.DEFAULT_GOAL := all .DEFAULT_GOAL := all
.PHONY: all clean install uninstall tncattach .PHONY: all clean install uninstall tncattach
compiler = gcc RM ?= rm
flags = -std=gnu11 -lm INSTALL ?= install
CC ?= gcc
CFLAGS ?= -Wall -std=gnu11 -static-libgcc
LDFLAGS ?=
PREFIX ?= /usr/local
all: tncattach all: tncattach
rebuild: clean all rebuild: clean all
clean: clean:
@echo "Cleaning tncattach build..." @echo "Cleaning tncattach build..."
@rm -f tncattach $(RM) -f tncattach
tncattach: tncattach:
@echo "Making tncattach..." @echo "Making tncattach..."
@echo "Compiling with: ${compiler}" @echo "Compiling with: $(CC)"
${compiler} ${flags} tncattach.c Serial.c KISS.c TAP.c -o tncattach -Wall $(CC) $(CFLAGS) $(LDFLAGS) tncattach.c Serial.c TCP.c KISS.c TAP.c -o tncattach
install: install:
@echo "Installing tncattach..." @echo "Installing tncattach..."
chmod a+x tncattach $(INSTALL) -d $(DESTDIR)/$(PREFIX)/bin
cp ./tncattach /usr/local/sbin/ $(INSTALL) -Dm755 tncattach $(DESTDIR)/$(PREFIX)/bin/tncattach
@echo "Installing man page..."
gzip -9 tncattach.8
$(INSTALL) -d $(DESTDIR)/$(PREFIX)/share/man/man8
$(INSTALL) -Dm644 tncattach.8.gz $(DESTDIR)/$(PREFIX)/share/man/man8/tncattach.8.gz
uninstall: uninstall:
@echo "Uninstalling tncattach" @echo "Uninstalling tncattach"
rm /usr/local/sbin/tncattach $(RM) $(DESTDIR)/$(PREFIX)/bin/tncattach
$(RM) $(DESTDIR)/$(PREFIX)/share/man/man8/tncattach.8.gz

165
tncattach.8 Normal file
View File

@ -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<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> 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

File diff suppressed because it is too large Load Diff