mirror of
https://github.com/markqvist/tncattach.git
synced 2024-09-26 07:06:44 -04:00
Compare commits
35 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
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 |
@ -7,3 +7,9 @@
|
|||||||
#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
110
KISS.c
@ -13,69 +13,73 @@ uint8_t frame_buffer[MAX_PAYLOAD];
|
|||||||
uint8_t write_buffer[MAX_PAYLOAD*2+3];
|
uint8_t write_buffer[MAX_PAYLOAD*2+3];
|
||||||
|
|
||||||
extern bool verbose;
|
extern bool verbose;
|
||||||
|
extern bool daemonize;
|
||||||
extern int attached_if;
|
extern int attached_if;
|
||||||
|
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 (verbose) printf("Got KISS frame\r\n");
|
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) printf("Could not write received KISS frame to network interface, is the interface up?\r\n");
|
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) {
|
||||||
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
52
README.md
52
README.md
@ -4,7 +4,9 @@ Attach KISS TNC devices as network interfaces in Linux. This program allows you
|
|||||||
|
|
||||||
## Installation
|
## 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
|
```sh
|
||||||
# If you don't already have a compiler installed
|
# 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
|
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
|
||||||
|
-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
|
-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
|
||||||
@ -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.
|
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.
|
||||||
|
|
||||||
|
## 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
|
## Examples
|
||||||
|
|
||||||
Create an ethernet device with a USB-connected TNC, set the MTU, filter IPv6 traffic, and set an IPv4 address:
|
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
|
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
|
||||||
@ -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
|
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();
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
270
TAP.c
270
TAP.c
@ -15,118 +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, SIOCGIFFLAGS, &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
|
||||||
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
|
if (ioctl(inet, SIOCGIFTXQLEN, &ifr) < 0) {
|
||||||
if (ioctl(inet, SIOCSIFFLAGS, &ifr) < 0) {
|
perror("Could not get interface flags from kernel");
|
||||||
perror("Could not bring up interface");
|
close(inet);
|
||||||
close(inet);
|
cleanup();
|
||||||
cleanup();
|
exit(1);
|
||||||
exit(1);
|
} else {
|
||||||
} else {
|
ifr.ifr_qlen = TXQUEUELEN;
|
||||||
if (set_ipv4) {
|
if (ioctl(inet, SIOCSIFTXQLEN, &ifr) < 0) {
|
||||||
struct ifreq a_ifr;
|
perror("Could not set interface TX queue length");
|
||||||
struct sockaddr_in addr, snm;
|
close(inet);
|
||||||
|
cleanup();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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
37
TCP.c
Normal 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
9
TCP.h
Normal 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);
|
29
makefile
29
makefile
@ -1,20 +1,35 @@
|
|||||||
.DEFAULT_GOAL := all
|
.DEFAULT_GOAL := all
|
||||||
.PHONY: all clean install 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 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: all
|
install:
|
||||||
@echo "Installing tncattach..."
|
@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
|
||||||
|
165
tncattach.8
Normal file
165
tncattach.8
Normal 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
|
872
tncattach.c
872
tncattach.c
@ -5,9 +5,11 @@
|
|||||||
#include <argp.h>
|
#include <argp.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <time.h>
|
||||||
#include "Constants.h"
|
#include "Constants.h"
|
||||||
#include "Serial.h"
|
#include "Serial.h"
|
||||||
#include "KISS.h"
|
#include "KISS.h"
|
||||||
|
#include "TCP.h"
|
||||||
#include "TAP.h"
|
#include "TAP.h"
|
||||||
|
|
||||||
#define BAUDRATE_DEFAULT 0
|
#define BAUDRATE_DEFAULT 0
|
||||||
@ -33,372 +35,489 @@ bool noup = false;
|
|||||||
bool daemonize = false;
|
bool daemonize = false;
|
||||||
bool set_ipv4 = false;
|
bool set_ipv4 = false;
|
||||||
bool set_netmask = false;
|
bool set_netmask = false;
|
||||||
|
bool kiss_over_tcp = false;
|
||||||
char* ipv4_addr;
|
char* ipv4_addr;
|
||||||
char* netmask;
|
char* netmask;
|
||||||
|
|
||||||
|
char* tcp_host;
|
||||||
|
int tcp_port;
|
||||||
|
|
||||||
int mtu;
|
int mtu;
|
||||||
int device_type = IF_TUN;
|
int device_type = IF_TUN;
|
||||||
|
|
||||||
void cleanup(void) {
|
char* id;
|
||||||
close_port(attached_tnc);
|
int id_interval = -1;
|
||||||
close_tap(attached_if);
|
time_t last_id = 0;
|
||||||
}
|
bool tx_since_last_id = false;
|
||||||
|
|
||||||
void signal_handler(int signal) {
|
void cleanup(void) {
|
||||||
if (daemonize) {
|
if (kiss_over_tcp) {
|
||||||
cleanup();
|
close_tcp(attached_tnc);
|
||||||
syslog(LOG_NOTICE, "tncattach daemon exiting");
|
} else {
|
||||||
exit(0);
|
close_port(attached_tnc);
|
||||||
} else {
|
}
|
||||||
cleanup();
|
close_tap(attached_if);
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_ipv6(uint8_t* frame) {
|
bool is_ipv6(uint8_t* frame) {
|
||||||
if (device_type == IF_TAP) {
|
if (device_type == IF_TAP) {
|
||||||
if (frame[12] == 0x86 && frame[13] == 0xdd) {
|
if (frame[12] == 0x86 && frame[13] == 0xdd) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (device_type == IF_TUN) {
|
} else if (device_type == IF_TUN) {
|
||||||
if (frame[2] == 0x86 && frame[3] == 0xdd) {
|
if (frame[2] == 0x86 && frame[3] == 0xdd) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
printf("Error: Unsupported interface type\r\n");
|
printf("Error: Unsupported interface type\r\n");
|
||||||
cleanup();
|
cleanup();
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t time_now(void) {
|
||||||
|
time_t now = time(NULL);
|
||||||
|
if (now == -1) {
|
||||||
|
if (daemonize) {
|
||||||
|
syslog(LOG_ERR, "Could not get system time, exiting now");
|
||||||
|
} else {
|
||||||
|
printf("Error: Could not get system time, exiting now\r\n");
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
exit(1);
|
||||||
|
} else {
|
||||||
|
return now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void transmit_id(void) {
|
||||||
|
time_t now = time(NULL);
|
||||||
|
int id_len = strlen(id);
|
||||||
|
if (verbose) {
|
||||||
|
if (!daemonize) {
|
||||||
|
printf("Transmitting %d bytes of identification data on %s: %s\r\n", id_len, if_name, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* id_frame = malloc(strlen(id));
|
||||||
|
memcpy(id_frame, id, id_len);
|
||||||
|
kiss_write_frame(attached_tnc, id_frame, id_len);
|
||||||
|
last_id = now;
|
||||||
|
tx_since_last_id = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool should_id(void) {
|
||||||
|
if (id_interval != -1) {
|
||||||
|
time_t now = time_now();
|
||||||
|
return now > last_id + id_interval;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
void read_loop(void) {
|
||||||
bool should_continue = true;
|
bool should_continue = true;
|
||||||
int min_frame_size;
|
int min_frame_size;
|
||||||
if (device_type == IF_TAP) {
|
if (device_type == IF_TAP) {
|
||||||
min_frame_size = ETHERNET_MIN_FRAME_SIZE;
|
min_frame_size = ETHERNET_MIN_FRAME_SIZE;
|
||||||
} else if (device_type == IF_TUN) {
|
} else if (device_type == IF_TUN) {
|
||||||
min_frame_size = TUN_MIN_FRAME_SIZE;
|
min_frame_size = TUN_MIN_FRAME_SIZE;
|
||||||
} else {
|
} else {
|
||||||
if (daemonize) {
|
if (daemonize) {
|
||||||
syslog(LOG_ERR, "Unsupported interface type");
|
syslog(LOG_ERR, "Unsupported interface type");
|
||||||
} else {
|
} else {
|
||||||
printf("Error: Unsupported interface type\r\n");
|
printf("Error: Unsupported interface type\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup();
|
cleanup();
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (should_continue) {
|
int poll_timeout = 1000;
|
||||||
|
while (should_continue) {
|
||||||
|
int poll_result = poll(fds, 2, poll_timeout);
|
||||||
|
if (poll_result != -1) {
|
||||||
|
if (poll_result == 0) {
|
||||||
|
// No resources are ready for reading,
|
||||||
|
// run scheduled tasks instead.
|
||||||
|
if (id_interval != -1 && tx_since_last_id) {
|
||||||
|
time_t now = time_now();
|
||||||
|
if (now > last_id + id_interval) transmit_id();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int fdi = 0; fdi < N_FDS; fdi++) {
|
||||||
|
if (fds[fdi].revents != 0) {
|
||||||
|
// Check for hangup event
|
||||||
|
if (fds[fdi].revents & POLLHUP) {
|
||||||
|
if (fdi == IF_FD_INDEX) {
|
||||||
|
if (daemonize) {
|
||||||
|
syslog(LOG_ERR, "Received hangup from interface");
|
||||||
|
} else {
|
||||||
|
printf("Received hangup from interface\r\n");
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (fdi == TNC_FD_INDEX) {
|
||||||
|
if (daemonize) {
|
||||||
|
syslog(LOG_ERR, "Received hangup from TNC");
|
||||||
|
} else {
|
||||||
|
printf("Received hangup from TNC\r\n");
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int poll_result = poll(fds, 2, -1);
|
// Check for error event
|
||||||
if (poll_result != -1) {
|
if (fds[fdi].revents & POLLERR) {
|
||||||
for (int fdi = 0; fdi < N_FDS; fdi++) {
|
if (fdi == IF_FD_INDEX) {
|
||||||
if (fds[fdi].revents != 0) {
|
if (daemonize) {
|
||||||
// Check for hangup event
|
syslog(LOG_ERR, "Received error event from interface");
|
||||||
if (fds[fdi].revents & POLLHUP) {
|
} else {
|
||||||
if (fdi == IF_FD_INDEX) {
|
perror("Received error event from interface\r\n");
|
||||||
if (daemonize) {
|
}
|
||||||
syslog(LOG_ERR, "Received hangup from interface");
|
cleanup();
|
||||||
} else {
|
exit(1);
|
||||||
printf("Received hangup from interface\r\n");
|
}
|
||||||
}
|
if (fdi == TNC_FD_INDEX) {
|
||||||
cleanup();
|
if (daemonize) {
|
||||||
exit(1);
|
syslog(LOG_ERR, "Received error event from TNC");
|
||||||
}
|
} else {
|
||||||
if (fdi == TNC_FD_INDEX) {
|
perror("Received error event from TNC\r\n");
|
||||||
if (daemonize) {
|
}
|
||||||
syslog(LOG_ERR, "Received hangup from TNC");
|
cleanup();
|
||||||
} else {
|
exit(1);
|
||||||
printf("Received hangup from TNC\r\n");
|
}
|
||||||
}
|
}
|
||||||
cleanup();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for error event
|
// If data is ready, read it
|
||||||
if (fds[fdi].revents & POLLERR) {
|
if (fds[fdi].revents & POLLIN) {
|
||||||
if (fdi == IF_FD_INDEX) {
|
if (fdi == IF_FD_INDEX) {
|
||||||
if (daemonize) {
|
int if_len = read(attached_if, if_buffer, sizeof(if_buffer));
|
||||||
syslog(LOG_ERR, "Received error event from interface");
|
if (if_len > 0) {
|
||||||
} else {
|
if (if_len >= min_frame_size) {
|
||||||
perror("Received error event from interface\r\n");
|
if (!noipv6 || (noipv6 && !is_ipv6(if_buffer))) {
|
||||||
}
|
|
||||||
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
|
int tnc_written = kiss_write_frame(attached_tnc, if_buffer, if_len);
|
||||||
if (fds[fdi].revents & POLLIN) {
|
if (verbose && !daemonize) printf("Got %d bytes from interface, wrote %d bytes (KISS-framed and escaped) to TNC\r\n", if_len, tnc_written);
|
||||||
if (fdi == IF_FD_INDEX) {
|
tx_since_last_id = true;
|
||||||
int if_len = read(attached_if, if_buffer, sizeof(if_buffer));
|
|
||||||
if (if_len > 0) {
|
|
||||||
if (if_len >= min_frame_size) {
|
|
||||||
if (!noipv6 || (noipv6 && !is_ipv6(if_buffer))) {
|
|
||||||
kiss_write_frame(attached_tnc, if_buffer, if_len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (daemonize) {
|
|
||||||
syslog(LOG_ERR, "Could not read from network interface, exiting now");
|
|
||||||
} else {
|
|
||||||
printf("Error: Could not read from network interface, exiting now\r\n");
|
|
||||||
}
|
|
||||||
cleanup();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fdi == TNC_FD_INDEX) {
|
if (should_id()) transmit_id();
|
||||||
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);
|
} else {
|
||||||
for (int i = 0; i < tnc_len; i++) {
|
if (daemonize) {
|
||||||
kiss_serial_read(serial_buffer[i]);
|
syslog(LOG_ERR, "Could not read from network interface, exiting now");
|
||||||
}
|
} else {
|
||||||
} else {
|
printf("Error: Could not read from network interface, exiting now\r\n");
|
||||||
if (daemonize) {
|
}
|
||||||
syslog(LOG_ERR, "Could not read from TNC, exiting now");
|
cleanup();
|
||||||
} else {
|
exit(1);
|
||||||
printf("Error: Could not read from TNC, exiting now\r\n");
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup();
|
if (fdi == TNC_FD_INDEX) {
|
||||||
exit(1);
|
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 {
|
||||||
} else {
|
if (daemonize) {
|
||||||
should_continue = false;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
should_continue = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *argp_program_version = "tncattach 0.1.1";
|
const char *argp_program_version = "tncattach 0.1.9";
|
||||||
const char *argp_program_bug_address = "<mark@unsigned.io>";
|
const char *argp_program_bug_address = "<mark@unsigned.io>";
|
||||||
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 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 char args_doc[] = "port baudrate";
|
||||||
static struct argp_option options[] = {
|
static struct argp_option options[] = {
|
||||||
{ "mtu", 'm', "MTU", 0, "Specify interface MTU"},
|
{ "mtu", 'm', "MTU", 0, "Specify interface MTU", 1},
|
||||||
{ "daemon", 'd', 0, 0, "Run tncattach as a daemon"},
|
{ "ethernet", 'e', 0, 0, "Create a full ethernet device", 2},
|
||||||
{ "ethernet", 'e', 0, 0, "Create a full ethernet device"},
|
{ "ipv4", 'i', "IP_ADDRESS", 0, "Configure an IPv4 address on interface", 3},
|
||||||
{ "ipv4", 'i', "IP_ADDRESS", 0, "Configure an IPv4 address on interface"},
|
{ "noipv6", 'n', 0, 0, "Filter IPv6 traffic from reaching TNC", 4},
|
||||||
{ "noipv6", 'n', 0, 0, "Filter IPv6 traffic from reaching TNC"},
|
{ "noup", 1, 0, 0, "Only create interface, don't bring it up", 5},
|
||||||
{ "noup", 1, 0, 0, "Only create interface, don't bring it up"},
|
{ "kisstcp", 'T', 0, 0, "Use KISS over TCP instead of serial port", 6},
|
||||||
{ "verbose", 'v', 0, 0, "Enable verbose output"},
|
{ "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 }
|
{ 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
#define N_ARGS 2
|
#define N_ARGS 2
|
||||||
struct arguments {
|
struct arguments {
|
||||||
char *args[N_ARGS];
|
char *args[N_ARGS];
|
||||||
char *ipv4;
|
char *ipv4;
|
||||||
int baudrate;
|
char *id;
|
||||||
int mtu;
|
bool valid_id;
|
||||||
bool tap;
|
int id_interval;
|
||||||
bool daemon;
|
int baudrate;
|
||||||
bool verbose;
|
int tcpport;
|
||||||
bool set_ipv4;
|
int mtu;
|
||||||
bool set_netmask;
|
bool tap;
|
||||||
bool noipv6;
|
bool daemon;
|
||||||
bool noup;
|
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) {
|
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) {
|
switch (key) {
|
||||||
case 'v':
|
case 'v':
|
||||||
arguments->verbose = true;
|
arguments->verbose = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'e':
|
case 'e':
|
||||||
arguments->tap = true;
|
arguments->tap = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'm':
|
case 'm':
|
||||||
arguments->mtu = atoi(arg);
|
arguments->mtu = atoi(arg);
|
||||||
if (arguments->mtu < MTU_MIN || arguments->mtu > MTU_MAX) {
|
if (arguments->mtu < MTU_MIN || arguments->mtu > MTU_MAX) {
|
||||||
printf("Error: Invalid MTU specified\r\n\r\n");
|
printf("Error: Invalid MTU specified\r\n\r\n");
|
||||||
argp_usage(state);
|
argp_usage(state);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'i':
|
case 't':
|
||||||
arguments->ipv4 = arg;
|
arguments->id_interval = atoi(arg);
|
||||||
arguments->set_ipv4 = true;
|
if (arguments->id_interval < 0) {
|
||||||
|
printf("Error: Invalid identification interval specified\r\n\r\n");
|
||||||
|
argp_usage(state);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
if (strchr(arg, '/')) {
|
case 's':
|
||||||
char* net = strchr(arg, '/');
|
arguments->id = arg;
|
||||||
int pos = net-arg;
|
if (strlen(arg) < 1 || strlen(arg) > arguments->mtu) {
|
||||||
ipv4_addr = (char*)malloc(pos+1);
|
printf("Error: Invalid identification string specified\r\n\r\n");
|
||||||
int mask = atoi(net+1);
|
argp_usage(state);
|
||||||
strncpy(ipv4_addr, arg, pos);
|
} else {
|
||||||
switch (mask) {
|
arguments->valid_id = true;
|
||||||
case 0:
|
}
|
||||||
netmask = "0.0.0.0";
|
break;
|
||||||
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:
|
case 'i':
|
||||||
printf("Error: Invalid subnet mask specified\r\n");
|
arguments->ipv4 = arg;
|
||||||
cleanup();
|
arguments->set_ipv4 = true;
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
arguments->set_netmask = true;
|
if (strchr(arg, '/')) {
|
||||||
} else {
|
char* net = strchr(arg, '/');
|
||||||
arguments->set_netmask = false;
|
int pos = net-arg;
|
||||||
ipv4_addr = (char*)malloc(strlen(arg)+1);
|
ipv4_addr = (char*)malloc(pos+1);
|
||||||
strcpy(ipv4_addr, arg);
|
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;
|
||||||
|
|
||||||
break;
|
default:
|
||||||
|
printf("Error: Invalid subnet mask specified\r\n");
|
||||||
|
cleanup();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
case 'n':
|
arguments->set_netmask = true;
|
||||||
arguments->noipv6 = true;
|
} else {
|
||||||
break;
|
arguments->set_netmask = false;
|
||||||
|
ipv4_addr = (char*)malloc(strlen(arg)+1);
|
||||||
|
strcpy(ipv4_addr, arg);
|
||||||
|
}
|
||||||
|
|
||||||
case 'd':
|
break;
|
||||||
arguments->daemon = true;
|
|
||||||
arguments->verbose = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
case 'n':
|
||||||
arguments->noup = true;
|
arguments->noipv6 = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ARGP_KEY_ARG:
|
case 'd':
|
||||||
// Check if there's now too many text arguments
|
arguments->daemon = true;
|
||||||
if (state->arg_num >= N_ARGS) argp_usage(state);
|
arguments->verbose = false;
|
||||||
|
break;
|
||||||
|
|
||||||
// If not add to args
|
case 'T':
|
||||||
arguments->args[state->arg_num] = arg;
|
arguments->kiss_over_tcp = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ARGP_KEY_END:
|
case 'H':
|
||||||
// Check if there's too few text arguments
|
arguments->set_tcp_host = true;
|
||||||
if (state->arg_num < N_ARGS) argp_usage(state);
|
tcp_host = (char*)malloc(strlen(arg)+1);
|
||||||
break;
|
strcpy(tcp_host, arg);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
case 'P':
|
||||||
return ARGP_ERR_UNKNOWN;
|
arguments->set_tcp_port = true;
|
||||||
}
|
tcp_port = atoi(arg);
|
||||||
|
break;
|
||||||
|
|
||||||
return 0;
|
case 1:
|
||||||
|
arguments->noup = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARGP_KEY_ARG:
|
||||||
|
// Check if there's now too many text arguments
|
||||||
|
if (state->arg_num >= N_ARGS) argp_usage(state);
|
||||||
|
|
||||||
|
// If not add to args
|
||||||
|
arguments->args[state->arg_num] = arg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARGP_KEY_END:
|
||||||
|
// Check if there's too few text arguments
|
||||||
|
if (!arguments->kiss_over_tcp && state->arg_num < N_ARGS) argp_usage(state);
|
||||||
|
|
||||||
|
// Check if text arguments were given when
|
||||||
|
// KISS over TCP was specified
|
||||||
|
if (arguments->kiss_over_tcp && state->arg_num != 0) argp_usage(state);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ARGP_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void become_daemon() {
|
static void become_daemon() {
|
||||||
@ -406,8 +525,8 @@ static void become_daemon() {
|
|||||||
pid = fork();
|
pid = fork();
|
||||||
|
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
perror("Fork failed");
|
perror("Fork failed");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pid > 0) {
|
if (pid > 0) {
|
||||||
@ -431,45 +550,86 @@ static void become_daemon() {
|
|||||||
|
|
||||||
static struct argp argp = {options, parse_opt, args_doc, doc};
|
static struct argp argp = {options, parse_opt, args_doc, doc};
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
struct arguments arguments;
|
struct arguments arguments;
|
||||||
signal(SIGINT, signal_handler);
|
signal(SIGINT, signal_handler);
|
||||||
|
|
||||||
arguments.baudrate = BAUDRATE_DEFAULT;
|
arguments.baudrate = BAUDRATE_DEFAULT;
|
||||||
arguments.mtu = MTU_DEFAULT;
|
arguments.mtu = MTU_DEFAULT;
|
||||||
arguments.tap = false;
|
arguments.tap = false;
|
||||||
arguments.verbose = false;
|
arguments.verbose = false;
|
||||||
arguments.set_ipv4 = false;
|
arguments.set_ipv4 = false;
|
||||||
arguments.set_netmask = false;
|
arguments.set_netmask = false;
|
||||||
arguments.noipv6 = false;
|
arguments.noipv6 = false;
|
||||||
arguments.daemon = false;
|
arguments.daemon = false;
|
||||||
arguments.noup = false;
|
arguments.noup = false;
|
||||||
|
arguments.id_interval = -1;
|
||||||
|
arguments.valid_id = false;
|
||||||
|
arguments.kiss_over_tcp = false;
|
||||||
|
|
||||||
argp_parse(&argp, argc, argv, 0, 0, &arguments);
|
argp_parse(&argp, argc, argv, 0, 0, &arguments);
|
||||||
arguments.baudrate = atoi(arguments.args[1]);
|
|
||||||
|
|
||||||
if (arguments.daemon) daemonize = true;
|
if (arguments.kiss_over_tcp) kiss_over_tcp = 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;
|
|
||||||
|
|
||||||
attached_if = open_tap();
|
if (!kiss_over_tcp) {
|
||||||
attached_tnc = open_port(arguments.args[0]);
|
arguments.baudrate = atoi(arguments.args[1]);
|
||||||
if (setup_port(attached_tnc, arguments.baudrate)) {
|
} else {
|
||||||
printf("TNC interface configured as %s\r\n", if_name);
|
if (!(arguments.set_tcp_host && arguments.set_tcp_port)) {
|
||||||
fds[IF_FD_INDEX].fd = attached_if;
|
if (!arguments.set_tcp_host) printf("Error: KISS over TCP was requested, but no host was specified\r\n");
|
||||||
fds[IF_FD_INDEX].events = POLLIN;
|
if (!arguments.set_tcp_port) printf("Error: KISS over TCP was requested, but no port was specified\r\n");
|
||||||
fds[TNC_FD_INDEX].fd = attached_tnc;
|
exit(1);
|
||||||
fds[TNC_FD_INDEX].events = POLLIN;
|
}
|
||||||
if (daemonize) {
|
}
|
||||||
become_daemon();
|
|
||||||
syslog(LOG_NOTICE, "tncattach daemon running");
|
|
||||||
}
|
|
||||||
read_loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
attached_tnc = open_tcp(tcp_host, tcp_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("TNC interface configured as %s\r\n", if_name);
|
||||||
|
|
||||||
|
fds[IF_FD_INDEX].fd = attached_if;
|
||||||
|
fds[IF_FD_INDEX].events = POLLIN;
|
||||||
|
fds[TNC_FD_INDEX].fd = attached_tnc;
|
||||||
|
fds[TNC_FD_INDEX].events = POLLIN;
|
||||||
|
|
||||||
|
if (daemonize) {
|
||||||
|
become_daemon();
|
||||||
|
syslog(LOG_NOTICE, "tncattach daemon running");
|
||||||
|
}
|
||||||
|
|
||||||
|
read_loop();
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user