diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/kissattach.c | 234 | ||||
-rw-r--r-- | src/main.rs | 136 |
2 files changed, 370 insertions, 0 deletions
diff --git a/src/kissattach.c b/src/kissattach.c new file mode 100644 index 0000000..c39923d --- /dev/null +++ b/src/kissattach.c @@ -0,0 +1,234 @@ +// Taken from ax25-tools 0.0.10-rc4 kissattach.c +// SPDX-License-Identifier: GPL-2.0-only +#include <stdio.h> +#define __USE_XOPEN +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <termios.h> +#include <fcntl.h> +#include <signal.h> +#include <ctype.h> +#include <netdb.h> +#include <syslog.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/ioctl.h> + +#include <net/if.h> +#include <netax25/ax25.h> + +#define FALSE 0 +#define TRUE 1 + +static struct speed_struct { + int user_speed; + speed_t termios_speed; +} speed_table[] = { + {300, B300}, + {600, B600}, + {1200, B1200}, + {2400, B2400}, + {4800, B4800}, + {9600, B9600}, + {19200, B19200}, + {38400, B38400}, +#ifdef B57600 + {57600, B57600}, +#endif +#ifdef B115200 + {115200, B115200}, +#endif +#ifdef B230400 + {230400, B230400}, +#endif +#ifdef B460800 + {460800, B460800}, +#endif + {-1, B0} +}; + +static int tty_speed(int fd, int speed) +{ + struct termios term; + struct speed_struct *s; + + for (s = speed_table; s->user_speed != -1; s++) + if (s->user_speed == speed) + break; + + if (s->user_speed == -1) { + fprintf(stderr, "tty_speed: invalid speed %d\n", speed); + return FALSE; + } + + if (tcgetattr(fd, &term) == -1) { + perror("tty_speed: tcgetattr"); + return FALSE; + } + + cfsetispeed(&term, s->termios_speed); + cfsetospeed(&term, s->termios_speed); + + if (tcsetattr(fd, TCSANOW, &term) == -1) { + perror("tty_speed: tcsetattr"); + return FALSE; + } + + return TRUE; +} + +static int ax25_aton_entry(const char *name, char *buf) +{ + int ct = 0; + int ssid = 0; + const char *p = name; + char c; + + while (ct < 6) { + c = toupper(*p); + + if (c == '-' || c == '\0') + break; + + if (!isalnum(c)) { + printf("axutils: invalid symbol in callsign '%s'\n", name); + return -1; + } + + buf[ct] = c << 1; + + p++; + ct++; + } + + while (ct < 6) { + buf[ct] = ' ' << 1; + ct++; + } + + if (*p != '\0') { + p++; + + if (sscanf(p, "%d", &ssid) != 1 || ssid < 0 || ssid > 15) { + printf("axutils: SSID must follow '-' and be numeric in the range 0-15 - '%s'\n", name); + return -1; + } + } + + buf[6] = ((ssid + '0') << 1) & 0x1E; + + return 0; +} + +static int setifcall(int fd, char *name) +{ + char call[7]; + + if (ax25_aton_entry(name, call) == -1) + return FALSE; + + if (ioctl(fd, SIOCSIFHWADDR, call) != 0) { + close(fd); + perror("SIOCSIFHWADDR"); + return FALSE; + } + + return TRUE; +} + + +static int startiface(char *dev, int mtu, int allow_broadcast) +{ + struct ifreq ifr; + int fd; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + return FALSE; + } + + strcpy(ifr.ifr_name, dev); + + ifr.ifr_mtu = mtu; + + if (ioctl(fd, SIOCSIFMTU, &ifr) < 0) { + perror("SIOCSIFMTU"); + return FALSE; + } + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { + perror("SIOCGIFFLAGS"); + return FALSE; + } + + ifr.ifr_flags &= IFF_NOARP; + ifr.ifr_flags |= IFF_UP; + ifr.ifr_flags |= IFF_RUNNING; + if (allow_broadcast) + ifr.ifr_flags |= IFF_BROADCAST; /* samba broadcasts are a pain.. */ + else + ifr.ifr_flags &= ~(IFF_BROADCAST); /* samba broadcasts are a pain.. */ + + if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) { + perror("SIOCSIFFLAGS"); + return FALSE; + } + + close(fd); + + return TRUE; +} + +int32_t kissattach( + char *callsign, + int32_t speed, + int32_t mtu, + char *kttyname, + int32_t allow_broadcast) +{ + int fd; + int disc = N_AX25; + char dev[64]; + + if ((fd = open(kttyname, O_RDONLY | O_NONBLOCK)) == -1) { + perror("open"); + return 0; + } + + if (speed != 0 && !tty_speed(fd, speed)) + return 0; + + if (ioctl(fd, TIOCSETD, &disc) == -1) { + //Error setting line discipline + perror("TIOCSETD"); + fprintf(stderr, "Are you sure you have enabled %s support in the kernel\n", + disc == N_AX25 ? "MKISS" : "6PACK"); + fprintf(stderr, "or, if you made it a module, that the module is loaded?\n"); + return 0; + } + + if (ioctl(fd, SIOCGIFNAME, dev) == -1) { + perror("SIOCGIFNAME"); + return 0; + } + + if (!setifcall(fd, callsign)) + return 0; + + /* Now set the encapsulation */ + int v = 4; + if (ioctl(fd, SIOCSIFENCAP, &v) == -1) { + perror("SIOCSIFENCAP"); + return 0; + } + + /* ax25 ifaces should not really need to have an IP address assigned to */ + if (!startiface(dev, mtu, allow_broadcast)) + return 0; + + return 1; +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..01b0f71 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,136 @@ +// Copyright 2018 Matthias P. Braendli +// SPDX-License-Identifier: GPL-2.0-only +extern crate libc; + +use std::fs::{OpenOptions, File}; +use std::ffi::{CStr, CString}; +use std::os::unix::io::AsRawFd; +use std::thread; +use std::net::TcpStream; +use std::str::FromStr; +use std::io::{Read, Write}; + +extern { + fn kissattach( + callsign: * const libc::c_char, + speed: libc::int32_t, + mtu: libc::int32_t, + kttyname: * const libc::c_char, + allow_broadcast: libc::int32_t) -> libc::int32_t; +} + +fn create_pts_pair() -> std::io::Result<File> { + let master_file = OpenOptions::new() + .read(true) + .write(true) + .open("/dev/ptmx")?; + + unsafe { + let master_fd = master_file.as_raw_fd(); + if libc::grantpt(master_fd) == -1 { + return Err(std::io::Error::last_os_error()); + } + if libc::unlockpt(master_fd) == -1 { + return Err(std::io::Error::last_os_error()); + } + } + + Ok(master_file) +} + +fn usage() { + eprintln!("fldigi-kissattach"); + eprintln!(" Usage:"); + eprintln!(" fldigi-kissattach CALLSIGN-SSID FLDIGI_IP:FLDIGI_PORT MTU"); + eprintln!(""); + eprintln!(" Example:"); + eprintln!(" fldigi-kissattach HB9EGM-1 127.0.0.1:7342 120"); + + std::process::exit(1); +} + +fn main() { + let args : Vec<String> = std::env::args().collect(); + if args.len() != 4 { + usage(); + } + + let callsign = args[1].clone(); + let fldigi_connect = args[2].clone(); + let mtu = i32::from_str(&args[3]).unwrap(); + eprintln!("Creating PTY pair"); + + let mut master_file = match create_pts_pair() { + Ok(fd) => fd, + Err(e) => panic!("create_pts_pair failed: {}", e) + }; + + eprintln!("PTS master: {:?}", master_file); + + let slavename; + unsafe { + slavename = libc::ptsname(master_file.as_raw_fd()); + } + + if slavename.is_null() { + panic!("Cannot get PTS slave name"); + } + unsafe { + let slice = CStr::from_ptr(slavename); + eprintln!("PTS slave: {:?}", slice); + } + + + let callsign = CString::new(callsign).expect("Failed to convert callsign to CString"); + let speed : i32 = 9600; + let allow_broadcast : i32 = 1; + + let success = unsafe { + kissattach( + callsign.as_ptr(), + speed, + mtu, + slavename, + allow_broadcast) + }; + + if success == 0 { + panic!("kissattach failed"); + } + + + let mut radio = TcpStream::connect(fldigi_connect).unwrap(); + + let mut pty_tx_side = master_file.try_clone() + .expect("Cannot clone PTY file"); + + let mut radio_tx = radio.try_clone().unwrap(); + + let writer = thread::spawn(move || { + loop { + // read from pty, write to fldigi + + let mut buf = [0; 1024]; + let num_bytes_rx = pty_tx_side.read(&mut buf).unwrap(); + + let txdata = &buf[0..num_bytes_rx]; + radio_tx.write(txdata).unwrap(); + } + }); + + loop { + // read from fldigi, write to master_file + + let mut buf = [0; 1024]; + let num_bytes_rx = radio.read(&mut buf).unwrap(); + + if num_bytes_rx == 0 { + break; + } + + let txdata = &buf[0..num_bytes_rx]; + master_file.write(txdata).unwrap(); + } + + writer.join().unwrap(); +} |