Compare commits
36 Commits
Author | SHA1 | Date |
---|---|---|
|
c910421796 | |
|
bb4b1917d5 | |
|
54b4ae14f8 | |
|
d9b3d2b6ae | |
|
bb9ff10158 | |
|
5129dda626 | |
|
b4a80a1e7a | |
|
8944821ba8 | |
|
ef700b3244 | |
|
015f8a73fb | |
|
5bdc5d5ccc | |
|
de83bc9f21 | |
|
671ea5dda0 | |
|
07eeed45f5 | |
|
c2beeee944 | |
|
eae91f349b | |
|
b996f38689 | |
|
d3ff2f207a | |
|
26f1e48b19 | |
|
5c8ddcd992 | |
|
dada2f3775 | |
|
41086b2e0c | |
|
1c7b30b995 | |
|
30207d6691 | |
|
b438e5fb5a | |
|
e7a831b41d | |
|
5f251e1f28 | |
|
8a70eedfc5 | |
|
a4f79f204b | |
|
a4b433e2f7 | |
|
bb045bc6d4 | |
|
01a6fc1cd8 | |
|
764746cc4c | |
|
0159926312 | |
|
f6e12426d0 | |
|
506d4659a1 |
|
@ -6,4 +6,10 @@
|
|||
|
||||
#define MTU_MIN 74
|
||||
#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
110
KISS.c
|
@ -13,69 +13,73 @@ 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);
|
||||
}
|
||||
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);
|
||||
}
|
52
README.md
52
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).
|
||||
|
||||
```sh
|
||||
# If you don't already have a compiler installed
|
||||
|
@ -32,12 +34,17 @@ Usage: tncattach [OPTION...] port baudrate
|
|||
|
||||
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
|
||||
-i, --ipv4=IP_ADDRESS Configure an IPv4 address on interface
|
||||
-m, --mtu=MTU Specify interface MTU
|
||||
-n, --noipv6 Filter IPv6 traffic from reaching TNC
|
||||
--noup Only create interface, don't bring it up
|
||||
-T, --kisstcp Use KISS over TCP instead of serial port
|
||||
-H, --tcphost=TCP_HOST Host to connect to when using KISS over TCP
|
||||
-P, --tcpport=TCP_PORT TCP port when using KISS over TCP
|
||||
-t, --interval=SECONDS Maximum interval between station identifications
|
||||
-s, --id=CALLSIGN Station identification data
|
||||
-d, --daemon Run tncattach as a daemon
|
||||
-v, --verbose Enable verbose output
|
||||
-?, --help Give this help list
|
||||
--usage Give a short usage message
|
||||
|
@ -46,10 +53,24 @@ Attach TNC devices as system network interfaces
|
|||
|
||||
The program supports attaching TNCs as point-to-point tunnel devices, or generic ethernet devices. The ethernet mode is suitable for point-to-multipoint setups, and can be enabled with the corresponding command line switch. If you only need point-to-point links, it is advisable to just use the standard point-to-point mode, since it doesn't incur the ethernet header overhead on each packet.
|
||||
|
||||
If you want to connect to a virtual KISS TNC over a TCP connection, you can use the -T option, along with the -H and -P options to specify the host and port.
|
||||
|
||||
Additionally, it is worth noting that __tncattach__ can filter out IPv6 packets from reaching the TNC. Most operating systems attempts to autoconfigure IPv6 when an interface is brought up, which results in a substantial amount of IPv6 traffic generated by router solicitations and similar, which is usually unwanted for packet radio links and similar.
|
||||
|
||||
If you intend to use __tncattach__ on a system with mDNS services enabled (avahi-daemon, for example), you may want to consider modifying your mDNS setup to exclude TNC interfaces, or turning it off entirely, since it will generate a lot of traffic that might be unwanted.
|
||||
|
||||
## Station Identification
|
||||
|
||||
You can configure tncattach to automatically transmit station identification beacons according to a given interval, by using the --id and --interval options. Identification will be transmitted as raw data frames with whatever content has been specified in the --id option. Useful for amateur radio use, or other areas where station identification is necessary.
|
||||
|
||||
Identification beacons will be transmitted when:
|
||||
|
||||
- There is outgoing data to send, and the specified interval has elapsed.
|
||||
- The specified interval elapses, and data has been sent since the last ID beacon.
|
||||
- The program exits, if any data frames have been transmitted since the last ID beacon.
|
||||
|
||||
The above methodology should comply with station identification rules for amateur radio in most parts of the world, and complies with US Part 97 rules.
|
||||
|
||||
## Examples
|
||||
|
||||
Create an ethernet device with a USB-connected TNC, set the MTU, filter IPv6 traffic, and set an IPv4 address:
|
||||
|
@ -59,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
|
||||
```
|
||||
|
||||
Create an ethernet device with a TCP-connected TNC, set the MTU, filter IPv6 traffic, and set an IPv4 address:
|
||||
|
||||
```sh
|
||||
# Attach interface
|
||||
sudo tncattach -T -H localhost -P 8001 --ethernet --mtu 576 --noipv6 --ipv4 10.92.0.10/24
|
||||
```
|
||||
|
||||
You can interact with the interface like any other using the __ip__ or __ifconfig__ utilities:
|
||||
|
||||
```sh
|
||||
|
@ -95,3 +123,21 @@ tnc0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 400
|
|||
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
|
||||
|
||||
```
|
||||
|
||||
## Worth Knowing on Raspbian
|
||||
|
||||
On some versions of Raspbian (and probably other operating systems), the DHCP client daemon _dhcpcd_ interferes with TNC interfaces, by overriding their MTU and trying to auto-configure link-local addresses. You probably don't want this, and it can be disabled by editing the __/etc/dhcpcd.conf__ file, adding a statement telling _dhcpcd_ to ignore your TNC interface:
|
||||
|
||||
```
|
||||
# Add the following statement somewhere at the beginning
|
||||
# of /etc/dhcpcd.conf to prevent dhcpcd from changing MTU
|
||||
|
||||
denyinterfaces tnc0
|
||||
```
|
||||
|
||||
## Support tncattach development
|
||||
You can help support the continued development of open, free and private communications systems by donating via one of the following channels:
|
||||
|
||||
- Ethereum: 0x81F7B979fEa6134bA9FD5c701b3501A2e61E897a
|
||||
- Bitcoin: 3CPmacGm34qYvR6XWLVEJmi2aNe3PZqUuq
|
||||
- Ko-Fi: https://ko-fi.com/markqvist
|
||||
|
|
284
Serial.c
284
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;
|
||||
}
|
||||
}
|
||||
}
|
380
TAP.c
380
TAP.c
|
@ -5,128 +5,300 @@ char tap_name[IFNAMSIZ];
|
|||
extern bool verbose;
|
||||
extern bool noipv6;
|
||||
extern bool set_ipv4;
|
||||
extern bool set_ipv6;
|
||||
extern bool set_linklocal;
|
||||
extern bool set_netmask;
|
||||
extern bool noup;
|
||||
extern int mtu;
|
||||
extern int device_type;
|
||||
extern char if_name[IFNAMSIZ];
|
||||
extern char* ipv4_addr;
|
||||
extern char* ipv6_addr;
|
||||
extern long ipv6_prefixLen;
|
||||
extern char* netmask;
|
||||
extern void cleanup();
|
||||
|
||||
|
||||
void trySixSet
|
||||
(
|
||||
int interfaceIndex,
|
||||
struct in6_addr address,
|
||||
int prefixLen
|
||||
)
|
||||
{
|
||||
char ip_str[INET6_ADDRSTRLEN+1];
|
||||
inet_ntop(AF_INET6, &address, ip_str, INET6_ADDRSTRLEN+1);
|
||||
|
||||
printf
|
||||
(
|
||||
"Adding IPv6 address of '%s/%d' to interface at if_index %d\n",
|
||||
ip_str,
|
||||
prefixLen,
|
||||
interfaceIndex
|
||||
);
|
||||
|
||||
int inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||
if(inet6 < 0)
|
||||
{
|
||||
printf("Error opening control socket for adding IPv6 address to interface\n");
|
||||
cleanup();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct in6_ifreq paramReq;
|
||||
memset(¶mReq, 0, sizeof(struct in6_ifreq));
|
||||
paramReq.ifr6_ifindex = interfaceIndex;
|
||||
paramReq.ifr6_prefixlen = prefixLen;
|
||||
paramReq.ifr6_addr = address;
|
||||
|
||||
|
||||
// Try add the address
|
||||
if(ioctl(inet6, SIOCSIFADDR, ¶mReq) < 0)
|
||||
{
|
||||
printf
|
||||
(
|
||||
"There was an errror assigning address '%s/%d' to if_index %d\n",
|
||||
ip_str,
|
||||
prefixLen,
|
||||
interfaceIndex
|
||||
);
|
||||
cleanup();
|
||||
close(inet6);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("Address '%s/%d' added\n", ip_str, prefixLen);
|
||||
close(inet6);
|
||||
}
|
||||
|
||||
int open_tap(void) {
|
||||
struct ifreq ifr;
|
||||
int fd = open("/dev/net/tun", O_RDWR);
|
||||
struct ifreq ifr;
|
||||
int fd = open("/dev/net/tun", O_RDWR);
|
||||
|
||||
if (fd < 0) {
|
||||
perror("Could not open clone device");
|
||||
exit(1);
|
||||
} else {
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
// TODO: Enable PI header again?
|
||||
if (fd < 0) {
|
||||
perror("Could not open clone device");
|
||||
exit(1);
|
||||
} else {
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
// TODO: Enable PI header again?
|
||||
|
||||
if (device_type == IF_TAP) {
|
||||
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
||||
} else if (device_type == IF_TUN) {
|
||||
ifr.ifr_flags = IFF_TUN;
|
||||
} else {
|
||||
printf("Error: Unsupported interface type\r\n");
|
||||
cleanup();
|
||||
exit(1);
|
||||
}
|
||||
if (device_type == IF_TAP) {
|
||||
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
||||
} else if (device_type == IF_TUN) {
|
||||
ifr.ifr_flags = IFF_TUN;
|
||||
} else {
|
||||
printf("Error: Unsupported interface type\r\n");
|
||||
cleanup();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
strcpy(tap_name, "tnc%d");
|
||||
strncpy(ifr.ifr_name, tap_name, IFNAMSIZ);
|
||||
strcpy(tap_name, "tnc%d");
|
||||
strncpy(ifr.ifr_name, tap_name, IFNAMSIZ);
|
||||
|
||||
if (ioctl(fd, TUNSETIFF, &ifr) < 0) {
|
||||
perror("Could not configure network interface");
|
||||
exit(1);
|
||||
} else {
|
||||
strcpy(if_name, ifr.ifr_name);
|
||||
|
||||
int inet = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (inet == -1) {
|
||||
perror("Could not open AF_INET socket\r\n");
|
||||
cleanup();
|
||||
exit(1);
|
||||
} else {
|
||||
if (ioctl(inet, SIOCGIFFLAGS, &ifr) < 0) {
|
||||
perror("Could not get interface flags from kernel");
|
||||
close(inet);
|
||||
cleanup();
|
||||
exit(1);
|
||||
} else {
|
||||
ifr.ifr_mtu = mtu;
|
||||
if (ioctl(inet, SIOCSIFMTU, &ifr) < 0) {
|
||||
perror("Could not configure interface MTU");
|
||||
close(inet);
|
||||
cleanup();
|
||||
exit(1);
|
||||
}
|
||||
if (ioctl(fd, TUNSETIFF, &ifr) < 0) {
|
||||
perror("Could not configure network interface");
|
||||
exit(1);
|
||||
} else {
|
||||
strcpy(if_name, ifr.ifr_name);
|
||||
|
||||
if (!noup) {
|
||||
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
|
||||
if (ioctl(inet, SIOCSIFFLAGS, &ifr) < 0) {
|
||||
perror("Could not bring up interface");
|
||||
close(inet);
|
||||
cleanup();
|
||||
exit(1);
|
||||
} else {
|
||||
if (set_ipv4) {
|
||||
struct ifreq a_ifr;
|
||||
struct sockaddr_in addr, snm;
|
||||
|
||||
int inet = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (inet == -1) {
|
||||
perror("Could not open control socket");
|
||||
cleanup();
|
||||
exit(1);
|
||||
} else {
|
||||
if (ioctl(inet, SIOCGIFMTU, &ifr) < 0) {
|
||||
perror("Could not get interface flags from kernel");
|
||||
close(inet);
|
||||
cleanup();
|
||||
exit(1);
|
||||
} else {
|
||||
ifr.ifr_mtu = mtu;
|
||||
if (ioctl(inet, SIOCSIFMTU, &ifr) < 0) {
|
||||
perror("Could not configure interface MTU");
|
||||
close(inet);
|
||||
cleanup();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memset(&a_ifr, 0, sizeof(a_ifr));
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
memset(&snm, 0, sizeof(addr));
|
||||
strncpy(a_ifr.ifr_name, ifr.ifr_name, IFNAMSIZ);
|
||||
addr.sin_family = AF_INET;
|
||||
snm.sin_family = AF_INET;
|
||||
// Configure 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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
return 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;
|
||||
|
||||
memset(&a_ifr, 0, sizeof(a_ifr));
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
memset(&snm, 0, sizeof(addr));
|
||||
strncpy(a_ifr.ifr_name, ifr.ifr_name, IFNAMSIZ);
|
||||
addr.sin_family = AF_INET;
|
||||
snm.sin_family = AF_INET;
|
||||
|
||||
int addr_conversion = inet_pton(AF_INET, ipv4_addr, &(addr.sin_addr));
|
||||
if (addr_conversion != 1) {
|
||||
printf("Error: Invalid IPv4 address specified\r\n");
|
||||
close(inet);
|
||||
cleanup();
|
||||
exit(1);
|
||||
} else {
|
||||
a_ifr.ifr_addr = *(struct sockaddr*)&addr;
|
||||
if (ioctl(inet, SIOCSIFADDR, &a_ifr) < 0) {
|
||||
perror("Could not set IP-address");
|
||||
close(inet);
|
||||
cleanup();
|
||||
exit(1);
|
||||
} else {
|
||||
if (set_netmask) {
|
||||
int snm_conversion = inet_pton(AF_INET, netmask, &(snm.sin_addr));
|
||||
if (snm_conversion != 1) {
|
||||
printf("Error: Invalid subnet mask specified\r\n");
|
||||
close(inet);
|
||||
cleanup();
|
||||
exit(1);
|
||||
} else {
|
||||
a_ifr.ifr_addr = *(struct sockaddr*)&snm;
|
||||
if (ioctl(inet, SIOCSIFNETMASK, &a_ifr) < 0) {
|
||||
perror("Could not set subnet mask");
|
||||
close(inet);
|
||||
cleanup();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(set_ipv6 || set_linklocal)
|
||||
{
|
||||
// Firstly, obtain the interface index by `ifr_name`
|
||||
int inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||
if(inet6 < 0)
|
||||
{
|
||||
printf("Error opening control socket for adding IPv6 address to interface\n");
|
||||
cleanup();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(ioctl(inet6, SIOCGIFINDEX, &ifr) < 0)
|
||||
{
|
||||
printf("Could not get interface index for interface '%s'\n", ifr.ifr_name);
|
||||
close(inet6);
|
||||
cleanup();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// if link-local was NOT requested and interface
|
||||
// has been up'd -> then kernel would have added
|
||||
// a link-local already, this removes it
|
||||
if(!set_linklocal & !noup)
|
||||
{
|
||||
// TODO: Get all addresses that start with fe80
|
||||
}
|
||||
// Else it could have been no-up; hence you will have to remove
|
||||
// the link-local yourself
|
||||
// Other else is link-local was requested, then we don't care (whether
|
||||
// up'd or not as it will inevitably be added by the kernel)
|
||||
|
||||
// Convert ASCII IPv6 address to ABI structure
|
||||
struct in6_addr six_addr_itself;
|
||||
memset(&six_addr_itself, 0, sizeof(struct in6_addr));
|
||||
if(inet_pton(AF_INET6, ipv6_addr, &six_addr_itself) < 0)
|
||||
{
|
||||
printf("Error parsing IPv6 address '%s'\n", ipv6_addr);
|
||||
close(inet6);
|
||||
cleanup();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Add user's requested address
|
||||
trySixSet(ifr.ifr_ifindex, six_addr_itself, ipv6_prefixLen);
|
||||
|
||||
close(inet6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int close_tap(int tap_fd) {
|
||||
return close(tap_fd);
|
||||
}
|
||||
return close(tap_fd);
|
||||
}
|
||||
|
|
1
TAP.h
1
TAP.h
|
@ -9,6 +9,7 @@
|
|||
#include <linux/if_tun.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include "Constants.h"
|
||||
|
||||
int open_tap(void);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
31
makefile
31
makefile
|
@ -1,20 +1,35 @@
|
|||
.DEFAULT_GOAL := all
|
||||
.PHONY: all clean install tncattach
|
||||
.PHONY: all clean install uninstall tncattach
|
||||
|
||||
compiler = gcc
|
||||
flags = -std=gnu11 -lm
|
||||
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 tncattach
|
||||
$(RM) -f tncattach
|
||||
|
||||
tncattach:
|
||||
@echo "Making tncattach..."
|
||||
@echo "Compiling with: ${compiler}"
|
||||
${compiler} ${flags} tncattach.c Serial.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: all
|
||||
@echo "Installing tncattach..."
|
||||
install:
|
||||
@echo "Installing tncattach..."
|
||||
$(INSTALL) -d $(DESTDIR)/$(PREFIX)/bin
|
||||
$(INSTALL) -Dm755 tncattach $(DESTDIR)/$(PREFIX)/bin/tncattach
|
||||
@echo "Installing man page..."
|
||||
gzip -9 tncattach.8
|
||||
$(INSTALL) -d $(DESTDIR)/$(PREFIX)/share/man/man8
|
||||
$(INSTALL) -Dm644 tncattach.8.gz $(DESTDIR)/$(PREFIX)/share/man/man8/tncattach.8.gz
|
||||
|
||||
uninstall:
|
||||
@echo "Uninstalling tncattach"
|
||||
$(RM) $(DESTDIR)/$(PREFIX)/bin/tncattach
|
||||
$(RM) $(DESTDIR)/$(PREFIX)/share/man/man8/tncattach.8.gz
|
||||
|
|
|
@ -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
|
969
tncattach.c
969
tncattach.c
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue