Merge branch 'valentintintin-tcp-kiss'
This commit is contained in:
commit
5c8ddcd992
|
@ -2,6 +2,9 @@ TNC Attach
|
||||||
==========
|
==========
|
||||||
Attach KISS TNC devices as network interfaces in Linux. This program allows you to attach TNCs or any KISS-compatible device as a network interface. This program does not need any kernel modules, and has no external dependencies outside the standard Linux and GNU C libraries.
|
Attach KISS TNC devices as network interfaces in Linux. This program allows you to attach TNCs or any KISS-compatible device as a network interface. This program does not need any kernel modules, and has no external dependencies outside the standard Linux and GNU C libraries.
|
||||||
|
|
||||||
|
## Version edited
|
||||||
|
Add capability of using TCP Kiss.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Currently it is recommended to compile and install __tncattach__ from source with the below commands.
|
Currently it is recommended to compile and install __tncattach__ from source with the below commands.
|
||||||
|
@ -30,10 +33,11 @@ sudo make install
|
||||||
Using __tncattach__ is simple. Run the program from the command line, specifying which serial port the TNC is connected to, and the serial port baud-rate, and __tncattach__ takes care of the rest. In most cases, depending on what you intend to do, you probably want to use some of the options, though. See the examples section below for usage examples.
|
Using __tncattach__ is simple. Run the program from the command line, specifying which serial port the TNC is connected to, and the serial port baud-rate, and __tncattach__ takes care of the rest. In most cases, depending on what you intend to do, you probably want to use some of the options, though. See the examples section below for usage examples.
|
||||||
|
|
||||||
```
|
```
|
||||||
Usage: tncattach [OPTION...] port baudrate
|
Usage: tncattach [OPTION...] (serial_port|host) (baudrate|port)
|
||||||
|
|
||||||
Attach TNC devices as system network interfaces
|
Attach TNC devices as system network interfaces
|
||||||
|
|
||||||
|
-o, --kisstcp Use TCP Kiss (such as Direwolf port 8001)
|
||||||
-d, --daemon Run tncattach as a daemon
|
-d, --daemon Run tncattach as a daemon
|
||||||
-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
|
||||||
|
@ -73,6 +77,7 @@ Create an ethernet device with a USB-connected TNC, set the MTU, filter IPv6 tra
|
||||||
```sh
|
```sh
|
||||||
# Attach interface
|
# Attach interface
|
||||||
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
|
||||||
|
sudo tncattach localhost 8001 -o --ethernet --mtu 576 --noipv6 --ipv4 10.92.0.10/24
|
||||||
```
|
```
|
||||||
|
|
||||||
You can interact with the interface like any other using the __ip__ or __ifconfig__ utilities:
|
You can interact with the interface like any other using the __ip__ or __ifconfig__ utilities:
|
||||||
|
@ -95,6 +100,7 @@ Create a point-to-point link:
|
||||||
```sh
|
```sh
|
||||||
# Attach interface
|
# Attach interface
|
||||||
sudo tncattach /dev/ttyUSB0 115200 --mtu 400 --noipv6 --noup
|
sudo tncattach /dev/ttyUSB0 115200 --mtu 400 --noipv6 --noup
|
||||||
|
sudo tncattach localhost 8001 -o --mtu 400 --noipv6 --noup
|
||||||
|
|
||||||
# Configure IP addresses for point-to-point link
|
# Configure IP addresses for point-to-point link
|
||||||
sudo ifconfig tnc0 10.93.0.1 pointopoint 10.93.0.2
|
sudo ifconfig tnc0 10.93.0.1 pointopoint 10.93.0.2
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
#include "Tcp.h"
|
||||||
|
|
||||||
|
int open_tcp(char* ip, int port) {
|
||||||
|
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
if (sockfd < 0) {
|
||||||
|
perror("ERROR opening socket");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hostent *server;
|
||||||
|
struct sockaddr_in serv_addr;
|
||||||
|
|
||||||
|
server = gethostbyname(ip);
|
||||||
|
|
||||||
|
if (server == NULL) {
|
||||||
|
fprintf(stderr,"ERROR, no such host\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bzero((char *) &serv_addr, sizeof(serv_addr));
|
||||||
|
serv_addr.sin_family = AF_INET;
|
||||||
|
bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
|
||||||
|
serv_addr.sin_port = htons(port);
|
||||||
|
|
||||||
|
if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
|
||||||
|
perror("ERROR connecting");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sockfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int close_tcp(int fd) {
|
||||||
|
return close(fd);
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include "Constants.h"
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
|
int open_tcp(char* ip, int port);
|
||||||
|
int close_tcp(int fd);
|
2
makefile
2
makefile
|
@ -14,7 +14,7 @@ clean:
|
||||||
tncattach:
|
tncattach:
|
||||||
@echo "Making tncattach..."
|
@echo "Making tncattach..."
|
||||||
@echo "Compiling with: ${compiler}"
|
@echo "Compiling with: ${compiler}"
|
||||||
${compiler} ${flags} tncattach.c Serial.c KISS.c TAP.c -o tncattach -Wall
|
${compiler} ${flags} tncattach.c Serial.c Tcp.c KISS.c TAP.c -o tncattach -Wall
|
||||||
|
|
||||||
install:
|
install:
|
||||||
@echo "Installing tncattach..."
|
@echo "Installing tncattach..."
|
||||||
|
|
890
tncattach.c
890
tncattach.c
|
@ -8,6 +8,7 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include "Constants.h"
|
#include "Constants.h"
|
||||||
#include "Serial.h"
|
#include "Serial.h"
|
||||||
|
#include "Tcp.h"
|
||||||
#include "KISS.h"
|
#include "KISS.h"
|
||||||
#include "TAP.h"
|
#include "TAP.h"
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ 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 use_net_kiss = false;
|
||||||
char* ipv4_addr;
|
char* ipv4_addr;
|
||||||
char* netmask;
|
char* netmask;
|
||||||
int mtu;
|
int mtu;
|
||||||
|
@ -45,442 +47,452 @@ time_t last_id = 0;
|
||||||
bool tx_since_last_id = false;
|
bool tx_since_last_id = false;
|
||||||
|
|
||||||
void cleanup(void) {
|
void cleanup(void) {
|
||||||
close_port(attached_tnc);
|
if (use_net_kiss) {
|
||||||
close_tap(attached_if);
|
close_tcp(attached_tnc);
|
||||||
|
} else {
|
||||||
|
close_port(attached_tnc);
|
||||||
|
}
|
||||||
|
close_tap(attached_if);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 time_now(void) {
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
if (now == -1) {
|
if (now == -1) {
|
||||||
if (daemonize) {
|
if (daemonize) {
|
||||||
syslog(LOG_ERR, "Could not get system time, exiting now");
|
syslog(LOG_ERR, "Could not get system time, exiting now");
|
||||||
} else {
|
} else {
|
||||||
printf("Error: Could not get system time, exiting now\r\n");
|
printf("Error: Could not get system time, exiting now\r\n");
|
||||||
}
|
}
|
||||||
cleanup();
|
cleanup();
|
||||||
exit(1);
|
exit(1);
|
||||||
} else {
|
} else {
|
||||||
return now;
|
return now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void transmit_id(void) {
|
void transmit_id(void) {
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
int id_len = strlen(id);
|
int id_len = strlen(id);
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
if (!daemonize) {
|
if (!daemonize) {
|
||||||
printf("Transmitting %d bytes of identification data on %s: %s\r\n", id_len, if_name, id);
|
printf("Transmitting %d bytes of identification data on %s: %s\r\n", id_len, if_name, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* id_frame = malloc(strlen(id));
|
uint8_t* id_frame = malloc(strlen(id));
|
||||||
memcpy(id_frame, id, id_len);
|
memcpy(id_frame, id, id_len);
|
||||||
kiss_write_frame(attached_tnc, id_frame, id_len);
|
kiss_write_frame(attached_tnc, id_frame, id_len);
|
||||||
last_id = now;
|
last_id = now;
|
||||||
tx_since_last_id = false;
|
tx_since_last_id = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool should_id(void) {
|
bool should_id(void) {
|
||||||
if (id_interval != -1) {
|
if (id_interval != -1) {
|
||||||
time_t now = time_now();
|
time_t now = time_now();
|
||||||
return now > last_id + id_interval;
|
return now > last_id + id_interval;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void signal_handler(int signal) {
|
void signal_handler(int signal) {
|
||||||
if (daemonize) syslog(LOG_NOTICE, "tncattach daemon exiting");
|
if (daemonize) syslog(LOG_NOTICE, "tncattach daemon exiting");
|
||||||
|
|
||||||
// Transmit final ID if necessary
|
// Transmit final ID if necessary
|
||||||
if (id_interval != -1 && tx_since_last_id) transmit_id();
|
if (id_interval != -1 && tx_since_last_id) transmit_id();
|
||||||
|
|
||||||
cleanup();
|
cleanup();
|
||||||
exit(0);
|
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();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int poll_timeout = 1000;
|
cleanup();
|
||||||
while (should_continue) {
|
exit(1);
|
||||||
int poll_result = poll(fds, 2, poll_timeout);
|
}
|
||||||
if (poll_result != -1) {
|
|
||||||
if (poll_result == 0) {
|
|
||||||
// No resources are ready for reading,
|
|
||||||
// run scheduled tasks instead.
|
|
||||||
if (id_interval != -1 && tx_since_last_id) {
|
|
||||||
time_t now = time_now();
|
|
||||||
if (now > last_id + id_interval) transmit_id();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int fdi = 0; fdi < N_FDS; fdi++) {
|
|
||||||
if (fds[fdi].revents != 0) {
|
|
||||||
// Check for hangup event
|
|
||||||
if (fds[fdi].revents & POLLHUP) {
|
|
||||||
if (fdi == IF_FD_INDEX) {
|
|
||||||
if (daemonize) {
|
|
||||||
syslog(LOG_ERR, "Received hangup from interface");
|
|
||||||
} else {
|
|
||||||
printf("Received hangup from interface\r\n");
|
|
||||||
}
|
|
||||||
cleanup();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (fdi == TNC_FD_INDEX) {
|
|
||||||
if (daemonize) {
|
|
||||||
syslog(LOG_ERR, "Received hangup from TNC");
|
|
||||||
} else {
|
|
||||||
printf("Received hangup from TNC\r\n");
|
|
||||||
}
|
|
||||||
cleanup();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for error event
|
int poll_timeout = 1000;
|
||||||
if (fds[fdi].revents & POLLERR) {
|
while (should_continue) {
|
||||||
if (fdi == IF_FD_INDEX) {
|
int poll_result = poll(fds, 2, poll_timeout);
|
||||||
if (daemonize) {
|
if (poll_result != -1) {
|
||||||
syslog(LOG_ERR, "Received error event from interface");
|
if (poll_result == 0) {
|
||||||
} else {
|
// No resources are ready for reading,
|
||||||
perror("Received error event from interface\r\n");
|
// run scheduled tasks instead.
|
||||||
}
|
if (id_interval != -1 && tx_since_last_id) {
|
||||||
cleanup();
|
time_t now = time_now();
|
||||||
exit(1);
|
if (now > last_id + id_interval) transmit_id();
|
||||||
}
|
}
|
||||||
if (fdi == TNC_FD_INDEX) {
|
} else {
|
||||||
if (daemonize) {
|
for (int fdi = 0; fdi < N_FDS; fdi++) {
|
||||||
syslog(LOG_ERR, "Received error event from TNC");
|
if (fds[fdi].revents != 0) {
|
||||||
} else {
|
// Check for hangup event
|
||||||
perror("Received error event from TNC\r\n");
|
if (fds[fdi].revents & POLLHUP) {
|
||||||
}
|
if (fdi == IF_FD_INDEX) {
|
||||||
cleanup();
|
if (daemonize) {
|
||||||
exit(1);
|
syslog(LOG_ERR, "Received hangup from interface");
|
||||||
}
|
} else {
|
||||||
}
|
printf("Received hangup from interface\r\n");
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (fdi == TNC_FD_INDEX) {
|
||||||
|
if (daemonize) {
|
||||||
|
syslog(LOG_ERR, "Received hangup from TNC");
|
||||||
|
} else {
|
||||||
|
printf("Received hangup from TNC\r\n");
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If data is ready, read it
|
// Check for error event
|
||||||
if (fds[fdi].revents & POLLIN) {
|
if (fds[fdi].revents & POLLERR) {
|
||||||
if (fdi == IF_FD_INDEX) {
|
if (fdi == IF_FD_INDEX) {
|
||||||
int if_len = read(attached_if, if_buffer, sizeof(if_buffer));
|
if (daemonize) {
|
||||||
if (if_len > 0) {
|
syslog(LOG_ERR, "Received error event from interface");
|
||||||
if (if_len >= min_frame_size) {
|
} else {
|
||||||
if (!noipv6 || (noipv6 && !is_ipv6(if_buffer))) {
|
perror("Received error event from interface\r\n");
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (fdi == TNC_FD_INDEX) {
|
||||||
|
if (daemonize) {
|
||||||
|
syslog(LOG_ERR, "Received error event from TNC");
|
||||||
|
} else {
|
||||||
|
perror("Received error event from TNC\r\n");
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int tnc_written = kiss_write_frame(attached_tnc, if_buffer, if_len);
|
// If data is ready, read it
|
||||||
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 (fds[fdi].revents & POLLIN) {
|
||||||
tx_since_last_id = true;
|
if (fdi == IF_FD_INDEX) {
|
||||||
|
int if_len = read(attached_if, if_buffer, sizeof(if_buffer));
|
||||||
|
if (if_len > 0) {
|
||||||
|
if (if_len >= min_frame_size) {
|
||||||
|
if (!noipv6 || (noipv6 && !is_ipv6(if_buffer))) {
|
||||||
|
|
||||||
if (should_id()) transmit_id();
|
int tnc_written = kiss_write_frame(attached_tnc, if_buffer, if_len);
|
||||||
}
|
if (verbose && !daemonize) printf("Got %d bytes from interface, wrote %d bytes (KISS-framed and escaped) to TNC\r\n", if_len, tnc_written);
|
||||||
}
|
tx_since_last_id = true;
|
||||||
} 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) {
|
}
|
||||||
for (int i = 0; i < tnc_len; i++) {
|
} else {
|
||||||
kiss_serial_read(serial_buffer[i]);
|
if (daemonize) {
|
||||||
}
|
syslog(LOG_ERR, "Could not read from network interface, exiting now");
|
||||||
} else {
|
} else {
|
||||||
if (daemonize) {
|
printf("Error: Could not read from network interface, exiting now\r\n");
|
||||||
syslog(LOG_ERR, "Could not read from TNC, exiting now");
|
}
|
||||||
} else {
|
cleanup();
|
||||||
printf("Error: Could not read from TNC, exiting now\r\n");
|
exit(1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
cleanup();
|
|
||||||
exit(1);
|
if (fdi == TNC_FD_INDEX) {
|
||||||
}
|
int tnc_len = read(attached_tnc, serial_buffer, sizeof(serial_buffer));
|
||||||
}
|
if (tnc_len > 0) {
|
||||||
}
|
for (int i = 0; i < tnc_len; i++) {
|
||||||
}
|
kiss_serial_read(serial_buffer[i]);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
} 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.7";
|
const char *argp_program_version = "tncattach 0.1.7";
|
||||||
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\vTo attach the TNC connected to /dev/ttyUSB0 as an ethernet device with an MTU of 512 bytes and assign an IPv4 address, while filtering IPv6 traffic, use:\r\n\r\n\ttncattach /dev/ttyUSB0 115200 -m 512 -e --noipv6 --ipv4 10.0.0.1/24\r\n\r\nStation identification can be performed automatically to comply with Part 97 rules. See the README for a complete description. Use the --id and --interval options, which should commonly be set to your callsign, and 600 seconds.";
|
static char doc[] = "\r\nAttach TNC devices as system network interfaces\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 baudrateOrPort";
|
||||||
static struct argp_option options[] = {
|
static struct argp_option options[] = {
|
||||||
{ "mtu", 'm', "MTU", 0, "Specify interface MTU"},
|
{ "mtu", 'm', "MTU", 0, "Specify interface MTU"},
|
||||||
{ "daemon", 'd', 0, 0, "Run tncattach as a daemon"},
|
{ "daemon", 'd', 0, 0, "Run tncattach as a daemon"},
|
||||||
{ "ethernet", 'e', 0, 0, "Create a full ethernet device"},
|
{ "ethernet", 'e', 0, 0, "Create a full ethernet device"},
|
||||||
{ "ipv4", 'i', "IP_ADDRESS", 0, "Configure an IPv4 address on interface"},
|
{ "ipv4", 'i', "IP_ADDRESS", 0, "Configure an IPv4 address on interface"},
|
||||||
{ "noipv6", 'n', 0, 0, "Filter IPv6 traffic from reaching TNC"},
|
{ "noipv6", 'n', 0, 0, "Filter IPv6 traffic from reaching TNC"},
|
||||||
{ "noup", 1, 0, 0, "Only create interface, don't bring it up"},
|
{ "noup", 1, 0, 0, "Only create interface, don't bring it up"},
|
||||||
{ "interval", 't', "SECONDS", 0, "Maximum interval between station identifications"},
|
{ "interval", 't', "SECONDS", 0, "Maximum interval between station identifications"},
|
||||||
{ "id", 's', "CALLSIGN", 0, "Station identification data"},
|
{ "id", 's', "CALLSIGN", 0, "Station identification data"},
|
||||||
{ "verbose", 'v', 0, 0, "Enable verbose output"},
|
{ "verbose", 'v', 0, 0, "Enable verbose output"},
|
||||||
{ 0 }
|
{ "kisstcp", 'o', 0, 0, "Does not use Serial but TCP connexion"},
|
||||||
|
{ 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;
|
||||||
char *id;
|
char *id;
|
||||||
bool valid_id;
|
bool valid_id;
|
||||||
int id_interval;
|
int id_interval;
|
||||||
int baudrate;
|
int baudrateOrPort;
|
||||||
int mtu;
|
int mtu;
|
||||||
bool tap;
|
bool tap;
|
||||||
bool daemon;
|
bool daemon;
|
||||||
bool verbose;
|
bool verbose;
|
||||||
bool set_ipv4;
|
bool set_ipv4;
|
||||||
bool set_netmask;
|
bool set_netmask;
|
||||||
bool noipv6;
|
bool noipv6;
|
||||||
bool noup;
|
bool noup;
|
||||||
|
bool useNetKiss;
|
||||||
};
|
};
|
||||||
|
|
||||||
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 't':
|
case 't':
|
||||||
arguments->id_interval = atoi(arg);
|
arguments->id_interval = atoi(arg);
|
||||||
if (arguments->id_interval < 0) {
|
if (arguments->id_interval < 0) {
|
||||||
printf("Error: Invalid identification interval specified\r\n\r\n");
|
printf("Error: Invalid identification interval specified\r\n\r\n");
|
||||||
argp_usage(state);
|
argp_usage(state);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 's':
|
case 's':
|
||||||
arguments->id = arg;
|
arguments->id = arg;
|
||||||
if (strlen(arg) < 1 || strlen(arg) > arguments->mtu) {
|
if (strlen(arg) < 1 || strlen(arg) > arguments->mtu) {
|
||||||
printf("Error: Invalid identification string specified\r\n\r\n");
|
printf("Error: Invalid identification string specified\r\n\r\n");
|
||||||
argp_usage(state);
|
argp_usage(state);
|
||||||
} else {
|
} else {
|
||||||
arguments->valid_id = true;
|
arguments->valid_id = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'i':
|
case 'i':
|
||||||
arguments->ipv4 = arg;
|
arguments->ipv4 = arg;
|
||||||
arguments->set_ipv4 = true;
|
arguments->set_ipv4 = true;
|
||||||
|
|
||||||
if (strchr(arg, '/')) {
|
if (strchr(arg, '/')) {
|
||||||
char* net = strchr(arg, '/');
|
char* net = strchr(arg, '/');
|
||||||
int pos = net-arg;
|
int pos = net-arg;
|
||||||
ipv4_addr = (char*)malloc(pos+1);
|
ipv4_addr = (char*)malloc(pos+1);
|
||||||
int mask = atoi(net+1);
|
int mask = atoi(net+1);
|
||||||
strncpy(ipv4_addr, arg, pos);
|
strncpy(ipv4_addr, arg, pos);
|
||||||
switch (mask) {
|
switch (mask) {
|
||||||
case 0:
|
case 0:
|
||||||
netmask = "0.0.0.0";
|
netmask = "0.0.0.0";
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
netmask = "128.0.0.0";
|
netmask = "128.0.0.0";
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
netmask = "192.0.0.0";
|
netmask = "192.0.0.0";
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
netmask = "224.0.0.0";
|
netmask = "224.0.0.0";
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
netmask = "240.0.0.0";
|
netmask = "240.0.0.0";
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
netmask = "248.0.0.0";
|
netmask = "248.0.0.0";
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
netmask = "252.0.0.0";
|
netmask = "252.0.0.0";
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
netmask = "254.0.0.0";
|
netmask = "254.0.0.0";
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
netmask = "255.0.0.0";
|
netmask = "255.0.0.0";
|
||||||
break;
|
break;
|
||||||
case 9:
|
case 9:
|
||||||
netmask = "255.128.0.0";
|
netmask = "255.128.0.0";
|
||||||
break;
|
break;
|
||||||
case 10:
|
case 10:
|
||||||
netmask = "255.192.0.0";
|
netmask = "255.192.0.0";
|
||||||
break;
|
break;
|
||||||
case 11:
|
case 11:
|
||||||
netmask = "255.224.0.0";
|
netmask = "255.224.0.0";
|
||||||
break;
|
break;
|
||||||
case 12:
|
case 12:
|
||||||
netmask = "255.240.0.0";
|
netmask = "255.240.0.0";
|
||||||
break;
|
break;
|
||||||
case 13:
|
case 13:
|
||||||
netmask = "255.248.0.0";
|
netmask = "255.248.0.0";
|
||||||
break;
|
break;
|
||||||
case 14:
|
case 14:
|
||||||
netmask = "255.252.0.0";
|
netmask = "255.252.0.0";
|
||||||
break;
|
break;
|
||||||
case 15:
|
case 15:
|
||||||
netmask = "255.254.0.0";
|
netmask = "255.254.0.0";
|
||||||
break;
|
break;
|
||||||
case 16:
|
case 16:
|
||||||
netmask = "255.255.0.0";
|
netmask = "255.255.0.0";
|
||||||
break;
|
break;
|
||||||
case 17:
|
case 17:
|
||||||
netmask = "255.255.128.0";
|
netmask = "255.255.128.0";
|
||||||
break;
|
break;
|
||||||
case 18:
|
case 18:
|
||||||
netmask = "255.255.192.0";
|
netmask = "255.255.192.0";
|
||||||
break;
|
break;
|
||||||
case 19:
|
case 19:
|
||||||
netmask = "255.255.224.0";
|
netmask = "255.255.224.0";
|
||||||
break;
|
break;
|
||||||
case 20:
|
case 20:
|
||||||
netmask = "255.255.240.0";
|
netmask = "255.255.240.0";
|
||||||
break;
|
break;
|
||||||
case 21:
|
case 21:
|
||||||
netmask = "255.255.248.0";
|
netmask = "255.255.248.0";
|
||||||
break;
|
break;
|
||||||
case 22:
|
case 22:
|
||||||
netmask = "255.255.252.0";
|
netmask = "255.255.252.0";
|
||||||
break;
|
break;
|
||||||
case 23:
|
case 23:
|
||||||
netmask = "255.255.254.0";
|
netmask = "255.255.254.0";
|
||||||
break;
|
break;
|
||||||
case 24:
|
case 24:
|
||||||
netmask = "255.255.255.0";
|
netmask = "255.255.255.0";
|
||||||
break;
|
break;
|
||||||
case 25:
|
case 25:
|
||||||
netmask = "255.255.255.128";
|
netmask = "255.255.255.128";
|
||||||
break;
|
break;
|
||||||
case 26:
|
case 26:
|
||||||
netmask = "255.255.255.192";
|
netmask = "255.255.255.192";
|
||||||
break;
|
break;
|
||||||
case 27:
|
case 27:
|
||||||
netmask = "255.255.255.224";
|
netmask = "255.255.255.224";
|
||||||
break;
|
break;
|
||||||
case 28:
|
case 28:
|
||||||
netmask = "255.255.255.240";
|
netmask = "255.255.255.240";
|
||||||
break;
|
break;
|
||||||
case 29:
|
case 29:
|
||||||
netmask = "255.255.255.248";
|
netmask = "255.255.255.248";
|
||||||
break;
|
break;
|
||||||
case 30:
|
case 30:
|
||||||
netmask = "255.255.255.252";
|
netmask = "255.255.255.252";
|
||||||
break;
|
break;
|
||||||
case 31:
|
case 31:
|
||||||
netmask = "255.255.255.254";
|
netmask = "255.255.255.254";
|
||||||
break;
|
break;
|
||||||
case 32:
|
case 32:
|
||||||
netmask = "255.255.255.255";
|
netmask = "255.255.255.255";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
|
||||||
printf("Error: Invalid subnet mask specified\r\n");
|
|
||||||
cleanup();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
arguments->set_netmask = true;
|
default:
|
||||||
} else {
|
printf("Error: Invalid subnet mask specified\r\n");
|
||||||
arguments->set_netmask = false;
|
cleanup();
|
||||||
ipv4_addr = (char*)malloc(strlen(arg)+1);
|
exit(1);
|
||||||
strcpy(ipv4_addr, arg);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
arguments->set_netmask = true;
|
||||||
|
} else {
|
||||||
|
arguments->set_netmask = false;
|
||||||
|
ipv4_addr = (char*)malloc(strlen(arg)+1);
|
||||||
|
strcpy(ipv4_addr, arg);
|
||||||
|
}
|
||||||
|
|
||||||
case 'n':
|
break;
|
||||||
arguments->noipv6 = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'd':
|
case 'n':
|
||||||
arguments->daemon = true;
|
arguments->noipv6 = true;
|
||||||
arguments->verbose = false;
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
case 'd':
|
||||||
arguments->noup = true;
|
arguments->daemon = true;
|
||||||
break;
|
arguments->verbose = false;
|
||||||
|
break;
|
||||||
|
|
||||||
case ARGP_KEY_ARG:
|
case 'o':
|
||||||
// Check if there's now too many text arguments
|
arguments->useNetKiss = true;
|
||||||
if (state->arg_num >= N_ARGS) argp_usage(state);
|
break;
|
||||||
|
|
||||||
// If not add to args
|
case 1:
|
||||||
arguments->args[state->arg_num] = arg;
|
arguments->noup = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ARGP_KEY_END:
|
case ARGP_KEY_ARG:
|
||||||
// Check if there's too few text arguments
|
// Check if there's now too many text arguments
|
||||||
if (state->arg_num < N_ARGS) argp_usage(state);
|
if (state->arg_num >= N_ARGS) argp_usage(state);
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
// If not add to args
|
||||||
return ARGP_ERR_UNKNOWN;
|
arguments->args[state->arg_num] = arg;
|
||||||
}
|
break;
|
||||||
|
|
||||||
return 0;
|
case ARGP_KEY_END:
|
||||||
|
// Check if there's too few text arguments
|
||||||
|
if (state->arg_num < N_ARGS) argp_usage(state);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ARGP_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void become_daemon() {
|
static void become_daemon() {
|
||||||
|
@ -488,8 +500,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) {
|
||||||
|
@ -513,63 +525,71 @@ 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.baudrateOrPort = 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.id_interval = -1;
|
||||||
arguments.valid_id = false;
|
arguments.valid_id = false;
|
||||||
|
|
||||||
argp_parse(&argp, argc, argv, 0, 0, &arguments);
|
argp_parse(&argp, argc, argv, 0, 0, &arguments);
|
||||||
arguments.baudrate = atoi(arguments.args[1]);
|
arguments.baudrateOrPort = atoi(arguments.args[1]);
|
||||||
|
|
||||||
if (arguments.daemon) daemonize = true;
|
if (arguments.daemon) daemonize = true;
|
||||||
if (arguments.verbose) verbose = true;
|
if (arguments.verbose) verbose = true;
|
||||||
if (arguments.tap) device_type = IF_TAP;
|
if (arguments.tap) device_type = IF_TAP;
|
||||||
if (arguments.noipv6) noipv6 = true;
|
if (arguments.noipv6) noipv6 = true;
|
||||||
if (arguments.set_ipv4) set_ipv4 = true;
|
if (arguments.set_ipv4) set_ipv4 = true;
|
||||||
if (arguments.set_netmask) set_netmask = true;
|
if (arguments.set_netmask) set_netmask = true;
|
||||||
if (arguments.noup) noup = true;
|
if (arguments.noup) noup = true;
|
||||||
mtu = arguments.mtu;
|
if (arguments.useNetKiss) use_net_kiss = true;
|
||||||
|
mtu = arguments.mtu;
|
||||||
|
|
||||||
if (arguments.id_interval >= 0) {
|
if (arguments.id_interval >= 0) {
|
||||||
if (!arguments.valid_id) {
|
if (!arguments.valid_id) {
|
||||||
printf("Error: Periodic identification requested, but no valid indentification data specified\r\n");
|
printf("Error: Periodic identification requested, but no valid indentification data specified\r\n");
|
||||||
cleanup();
|
cleanup();
|
||||||
exit(1);
|
exit(1);
|
||||||
} else {
|
} else {
|
||||||
id_interval = arguments.id_interval;
|
id_interval = arguments.id_interval;
|
||||||
id = malloc(strlen(arguments.id));
|
id = malloc(strlen(arguments.id));
|
||||||
strcpy(id, arguments.id);
|
strcpy(id, arguments.id);
|
||||||
}
|
}
|
||||||
} else if (arguments.valid_id && arguments.id_interval == -1) {
|
} else if (arguments.valid_id && arguments.id_interval == -1) {
|
||||||
printf("Error: Periodic identification requested, but no indentification interval specified\r\n");
|
printf("Error: Periodic identification requested, but no indentification interval specified\r\n");
|
||||||
cleanup();
|
cleanup();
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
attached_if = open_tap();
|
attached_if = open_tap();
|
||||||
attached_tnc = open_port(arguments.args[0]);
|
|
||||||
if (setup_port(attached_tnc, arguments.baudrate)) {
|
if (arguments.useNetKiss) {
|
||||||
printf("TNC interface configured as %s\r\n", if_name);
|
attached_tnc = open_tcp(arguments.args[0], arguments.baudrateOrPort);
|
||||||
fds[IF_FD_INDEX].fd = attached_if;
|
} else {
|
||||||
fds[IF_FD_INDEX].events = POLLIN;
|
attached_tnc = open_port(arguments.args[0]);
|
||||||
fds[TNC_FD_INDEX].fd = attached_tnc;
|
if (!setup_port(attached_tnc, arguments.baudrateOrPort)) {
|
||||||
fds[TNC_FD_INDEX].events = POLLIN;
|
printf("Error during serial port setup");
|
||||||
if (daemonize) {
|
return 0;
|
||||||
become_daemon();
|
}
|
||||||
syslog(LOG_NOTICE, "tncattach daemon running");
|
}
|
||||||
}
|
printf("TNC interface configured as %s\r\n", if_name);
|
||||||
read_loop();
|
fds[IF_FD_INDEX].fd = attached_if;
|
||||||
}
|
fds[IF_FD_INDEX].events = POLLIN;
|
||||||
|
fds[TNC_FD_INDEX].fd = attached_tnc;
|
||||||
return 0;
|
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