mirror of
https://github.com/markqvist/tncattach.git
synced 2024-11-27 06:38:57 -05:00
Implemented part 97 compliant auto-identification
This commit is contained in:
parent
bb045bc6d4
commit
a4b433e2f7
12
README.md
12
README.md
@ -54,7 +54,17 @@ Additionally, it is worth noting that __tncattach__ can filter out IPv6 packets
|
|||||||
|
|
||||||
If you intend to use __tncattach__ on a system with mDNS services enabled (avahi-daemon, for example), you may want to consider modifying your mDNS setup to exclude TNC interfaces, or turning it off entirely, since it will generate a lot of traffic that might be unwanted.
|
If you intend to use __tncattach__ on a system with mDNS services enabled (avahi-daemon, for example), you may want to consider modifying your mDNS setup to exclude TNC interfaces, or turning it off entirely, since it will generate a lot of traffic that might be unwanted.
|
||||||
|
|
||||||
You can configure tncattach to automatically transmit station identification according to a given interval, by using the --id and --interval options. Identification will be transmitted as raw data frames with whatever content has been specified in the --id option. Useful for amateur radio use, or other areas where station identification is necessary. Identification beacons will be transmitted if the amount of time since the last identification is greater than the configured interval and there is any data to send. Channel capacity will therefore not be wasted on IDs for stations that are not actively transmitting.
|
## Station Identification
|
||||||
|
|
||||||
|
You can configure tncattach to automatically transmit station identification beacons according to a given interval, by using the --id and --interval options. Identification will be transmitted as raw data frames with whatever content has been specified in the --id option. Useful for amateur radio use, or other areas where station identification is necessary.
|
||||||
|
|
||||||
|
Identification beacons will be transmitted when:
|
||||||
|
|
||||||
|
- There is outgoing data to send, and the specified interval has elapsed.
|
||||||
|
- The specified interval elapses, and data has been sent since the last ID beacon.
|
||||||
|
- The program exits, if any data frames have been transmitted since the last ID beacon.
|
||||||
|
|
||||||
|
The above methodology should comply with station identification rules for amateur radio in most parts of the world, and complies with US Part 97 rules.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
249
tncattach.c
249
tncattach.c
@ -42,23 +42,13 @@ int device_type = IF_TUN;
|
|||||||
char* id;
|
char* id;
|
||||||
int id_interval = -1;
|
int id_interval = -1;
|
||||||
time_t last_id = 0;
|
time_t last_id = 0;
|
||||||
|
bool tx_since_last_id = false;
|
||||||
|
|
||||||
void cleanup(void) {
|
void cleanup(void) {
|
||||||
close_port(attached_tnc);
|
close_port(attached_tnc);
|
||||||
close_tap(attached_if);
|
close_tap(attached_if);
|
||||||
}
|
}
|
||||||
|
|
||||||
void signal_handler(int signal) {
|
|
||||||
if (daemonize) {
|
|
||||||
cleanup();
|
|
||||||
syslog(LOG_NOTICE, "tncattach daemon exiting");
|
|
||||||
exit(0);
|
|
||||||
} else {
|
|
||||||
cleanup();
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_ipv6(uint8_t* frame) {
|
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) {
|
||||||
@ -79,6 +69,56 @@ bool is_ipv6(uint8_t* frame) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
time_t time_now(void) {
|
||||||
|
time_t now = time(NULL);
|
||||||
|
if (now == -1) {
|
||||||
|
if (daemonize) {
|
||||||
|
syslog(LOG_ERR, "Could not get system time, exiting now");
|
||||||
|
} else {
|
||||||
|
printf("Error: Could not get system time, exiting now\r\n");
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
exit(1);
|
||||||
|
} else {
|
||||||
|
return now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void transmit_id(void) {
|
||||||
|
time_t now = time(NULL);
|
||||||
|
int id_len = strlen(id);
|
||||||
|
if (verbose) {
|
||||||
|
if (!daemonize) {
|
||||||
|
printf("Transmitting %d bytes of identification data on %s: %s\r\n", id_len, if_name, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* id_frame = malloc(strlen(id));
|
||||||
|
memcpy(id_frame, id, id_len);
|
||||||
|
kiss_write_frame(attached_tnc, id_frame, id_len);
|
||||||
|
last_id = now;
|
||||||
|
tx_since_last_id = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool should_id(void) {
|
||||||
|
if (id_interval != -1) {
|
||||||
|
time_t now = time_now();
|
||||||
|
return now > last_id + id_interval;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void signal_handler(int signal) {
|
||||||
|
if (daemonize) syslog(LOG_NOTICE, "tncattach daemon exiting");
|
||||||
|
|
||||||
|
// Transmit final ID if necessary
|
||||||
|
if (id_interval != -1 && tx_since_last_id) transmit_id();
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
void read_loop(void) {
|
void read_loop(void) {
|
||||||
bool should_continue = true;
|
bool should_continue = true;
|
||||||
int min_frame_size;
|
int min_frame_size;
|
||||||
@ -97,120 +137,107 @@ void read_loop(void) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int poll_timeout = 1000;
|
||||||
while (should_continue) {
|
while (should_continue) {
|
||||||
|
int poll_result = poll(fds, 2, poll_timeout);
|
||||||
int poll_result = poll(fds, 2, -1);
|
|
||||||
if (poll_result != -1) {
|
if (poll_result != -1) {
|
||||||
for (int fdi = 0; fdi < N_FDS; fdi++) {
|
if (poll_result == 0) {
|
||||||
if (fds[fdi].revents != 0) {
|
// No resources are ready for reading,
|
||||||
// Check for hangup event
|
// run scheduled tasks instead.
|
||||||
if (fds[fdi].revents & POLLHUP) {
|
if (tx_since_last_id) {
|
||||||
if (fdi == IF_FD_INDEX) {
|
time_t now = time_now();
|
||||||
if (daemonize) {
|
if (now > last_id + id_interval) transmit_id();
|
||||||
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
|
} else {
|
||||||
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");
|
||||||
|
} else {
|
||||||
|
printf("Received hangup from interface\r\n");
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
cleanup();
|
if (fdi == TNC_FD_INDEX) {
|
||||||
exit(1);
|
if (daemonize) {
|
||||||
}
|
syslog(LOG_ERR, "Received hangup from TNC");
|
||||||
if (fdi == TNC_FD_INDEX) {
|
} else {
|
||||||
if (daemonize) {
|
printf("Received hangup from TNC\r\n");
|
||||||
syslog(LOG_ERR, "Received error event from TNC");
|
}
|
||||||
} else {
|
cleanup();
|
||||||
perror("Received error event from TNC\r\n");
|
exit(1);
|
||||||
}
|
}
|
||||||
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");
|
||||||
if (id_interval != -1) {
|
}
|
||||||
time_t now = time(NULL);
|
cleanup();
|
||||||
if (now == -1) {
|
exit(1);
|
||||||
if (daemonize) {
|
}
|
||||||
syslog(LOG_ERR, "Could not get system time, exiting now");
|
if (fdi == TNC_FD_INDEX) {
|
||||||
} else {
|
if (daemonize) {
|
||||||
printf("Error: Could not get system time, exiting now\r\n");
|
syslog(LOG_ERR, "Received error event from TNC");
|
||||||
}
|
} else {
|
||||||
cleanup();
|
perror("Received error event from TNC\r\n");
|
||||||
exit(1);
|
}
|
||||||
} else {
|
cleanup();
|
||||||
if (now > last_id + id_interval) {
|
exit(1);
|
||||||
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));
|
// If data is ready, read it
|
||||||
memcpy(id_frame, id, id_len);
|
if (fds[fdi].revents & POLLIN) {
|
||||||
kiss_write_frame(attached_tnc, id_frame, id_len);
|
if (fdi == IF_FD_INDEX) {
|
||||||
last_id = now;
|
int if_len = read(attached_if, if_buffer, sizeof(if_buffer));
|
||||||
}
|
if (if_len > 0) {
|
||||||
}
|
if (if_len >= min_frame_size) {
|
||||||
|
if (!noipv6 || (noipv6 && !is_ipv6(if_buffer))) {
|
||||||
|
|
||||||
|
int tnc_written = kiss_write_frame(attached_tnc, if_buffer, if_len);
|
||||||
|
if (verbose && !daemonize) printf("Got %d bytes from interface, wrote %d bytes (KISS-framed and escaped) to TNC\r\n", if_len, tnc_written);
|
||||||
|
tx_since_last_id = true;
|
||||||
|
|
||||||
|
if (should_id()) transmit_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
int tnc_written = kiss_write_frame(attached_tnc, if_buffer, if_len);
|
|
||||||
if (verbose && !daemonize) printf("Got %d bytes from interface, wrote %d bytes (KISS-framed and escaped) to TNC\r\n", if_len, tnc_written);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (daemonize) {
|
|
||||||
syslog(LOG_ERR, "Could not read from network interface, exiting now");
|
|
||||||
} else {
|
} else {
|
||||||
printf("Error: Could not read from network interface, exiting now\r\n");
|
if (daemonize) {
|
||||||
|
syslog(LOG_ERR, "Could not read from network interface, exiting now");
|
||||||
|
} else {
|
||||||
|
printf("Error: Could not read from network interface, exiting now\r\n");
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
cleanup();
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (fdi == TNC_FD_INDEX) {
|
if (fdi == TNC_FD_INDEX) {
|
||||||
int tnc_len = read(attached_tnc, serial_buffer, sizeof(serial_buffer));
|
int tnc_len = read(attached_tnc, serial_buffer, sizeof(serial_buffer));
|
||||||
if (tnc_len > 0) {
|
if (tnc_len > 0) {
|
||||||
for (int i = 0; i < tnc_len; i++) {
|
for (int i = 0; i < tnc_len; i++) {
|
||||||
kiss_serial_read(serial_buffer[i]);
|
kiss_serial_read(serial_buffer[i]);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (daemonize) {
|
|
||||||
syslog(LOG_ERR, "Could not read from TNC, exiting now");
|
|
||||||
} else {
|
} else {
|
||||||
printf("Error: Could not read from TNC, exiting now\r\n");
|
if (daemonize) {
|
||||||
|
syslog(LOG_ERR, "Could not read from TNC, exiting now");
|
||||||
|
} else {
|
||||||
|
printf("Error: Could not read from TNC, exiting now\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup();
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -224,9 +251,9 @@ void read_loop(void) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *argp_program_version = "tncattach 0.1.4";
|
const char *argp_program_version = "tncattach 0.1.5";
|
||||||
const char *argp_program_bug_address = "<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. Use the --id and --idinterval options. Identification beacons will be transmitted if the amount of time since the last identification is greater than the configured interval and there is any data to send. Channel capacity will therefore not be wasted on IDs for stations that are not actively transmitting.";
|
static char doc[] = "\r\nAttach TNC devices as system network interfaces\vTo attach the TNC connected to /dev/ttyUSB0 as an ethernet device with an MTU of 512 bytes and assign an IPv4 address, while filtering IPv6 traffic, use:\r\n\r\n\ttncattach /dev/ttyUSB0 115200 -m 512 -e --noipv6 --ipv4 10.0.0.1/24\r\n\r\nStation identification can be performed automatically to comply with Part 97 rules. See the README for a complete description. Use the --id and --interval options, which should commonly be set to your callsign, and 600 seconds.";
|
||||||
static char args_doc[] = "port baudrate";
|
static 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"},
|
||||||
|
Loading…
Reference in New Issue
Block a user