mirror of
https://github.com/laanwj/k210-sdk-stuff.git
synced 2024-11-22 09:26:21 +04:00
Add k210 linux utilities
This commit is contained in:
parent
78fb83fe50
commit
d9750dcf73
3
linux/.gitignore
vendored
Normal file
3
linux/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
esptun
|
||||
term
|
||||
*.gdb
|
15
linux/Makefile
Normal file
15
linux/Makefile
Normal file
@ -0,0 +1,15 @@
|
||||
CROSS ?= /opt/riscv64-uclibc/bin/riscv64-buildroot-linux-uclibc-
|
||||
CFLAGS = -fPIC -Wl,-elf2flt=-r -Wall
|
||||
BINARIES = term esptun
|
||||
|
||||
all: $(BINARIES)
|
||||
|
||||
clean:
|
||||
rm -f $(BINARIES)
|
||||
|
||||
term: term.c
|
||||
${CROSS}gcc $< -o $@ $(CFLAGS)
|
||||
|
||||
esptun: esptun.c
|
||||
${CROSS}gcc $< -o $@ $(CFLAGS)
|
||||
|
66
linux/README.md
Normal file
66
linux/README.md
Normal file
@ -0,0 +1,66 @@
|
||||
esptun
|
||||
======
|
||||
|
||||
A tool to tunnel IP packets over UDP over WIFI through an UART connected to a ESP8285 with the
|
||||
standard AT firmware.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
esptun <ifname> <uart> <ssid> <passwd> <host> <port>
|
||||
|
||||
This will create tun interface `ifname`. Then, with the with the ESP WIFI
|
||||
device on `uart` it connects to the AP `ssid` with password `passwd`.
|
||||
It creates a UDP over IP tunnel with the other endpoint `host:port`.
|
||||
|
||||
The new tun device will not be configured, use the `ip` utility to assign an
|
||||
IP address and switch the interface on.
|
||||
|
||||
For example:
|
||||
|
||||
/root/esptun tun0 /dev/ttyS1 "accesspointname" "secretpassword" 192.168.122.21 23232
|
||||
/sbin/ip addr add 10.0.1.2/24 dev tun0
|
||||
/sbin/ip link set tun0 up
|
||||
/sbin/ip route add default via 10.0.1.1 dev tun0
|
||||
|
||||
Host side
|
||||
---------
|
||||
|
||||
On the other endpoint the tunnel is expected to be a host running `socat` or similar to unwrap
|
||||
the tunnel. For example:
|
||||
|
||||
sudo socat UDP:192.168.2.127:23232,bind=192.168.122.21:23232 \
|
||||
TUN:10.0.1.1/24,tun-name=tundudp,iff-no-pi,tun-type=tun,su=$USER,iff-up
|
||||
|
||||
Optionally, enable forwarding and masquerading:
|
||||
|
||||
sudo iptables -t nat -F
|
||||
sudo iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE
|
||||
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
|
||||
|
||||
Linux kernel
|
||||
------------
|
||||
|
||||
Until there is a proper solution, you can use the following kernel branch
|
||||
to get the UART working on Kendryte K210 under Linux:
|
||||
|
||||
https://github.com/laanwj/linux/tree/kendryte-5.6-rc1-wifi
|
||||
|
||||
This is a hack that configures the FPIOA and GPIOHS manually, and configures UART1
|
||||
from the device tree.
|
||||
|
||||
To enable TUN/TAP, enable the following kernel settings:
|
||||
```
|
||||
CONFIG_NET=y
|
||||
CONFIG_INET=y
|
||||
CONFIG_TUN=y
|
||||
```
|
||||
|
||||
While building the root filesystem, make sure you enable at least `ip` and `ping`
|
||||
(and possibly other network tools) for busybox.
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
Copyright (c) 2020 W.J. van der Laan
|
||||
Distributed under the MIT software license,
|
588
linux/esptun.c
Normal file
588
linux/esptun.c
Normal file
@ -0,0 +1,588 @@
|
||||
/** esptun: UDP tunnel over UART, using ESP8285 AT command set.
|
||||
* Based on "simpletun" by Davide Brini.
|
||||
*
|
||||
* Copyright (c) 2020 W.J. van der Laan
|
||||
* Distributed under the MIT software license,
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <asm/termbits.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
/* buffer for reading from UART, must be >= 1500 */
|
||||
#define ESP_BUFSIZE 2000
|
||||
/* buffer for reading from tun/tap interface, must be >= 1500 */
|
||||
#define TAP_BUFSIZE 2000
|
||||
/* baud rate (max 115200*40 = 4608000) */
|
||||
#define BAUDRATE (115200 * 40)
|
||||
|
||||
/** Macro for writing static strings without needing strlen. */
|
||||
#define S(s) (const uint8_t*)(s), (sizeof(s)-1)
|
||||
|
||||
bool debug = false;
|
||||
char *progname;
|
||||
uint8_t esp_buffer[ESP_BUFSIZE];
|
||||
uint8_t tap_buffer[TAP_BUFSIZE];
|
||||
size_t tap2net;
|
||||
size_t net2tap;
|
||||
|
||||
/**
|
||||
* Prints debugging.
|
||||
*/
|
||||
static void log_debug(const char *msg, ...)
|
||||
{
|
||||
va_list argp;
|
||||
|
||||
if (debug) {
|
||||
va_start(argp, msg);
|
||||
vfprintf(stderr, msg, argp);
|
||||
va_end(argp);
|
||||
}
|
||||
}
|
||||
|
||||
#define INFO1 0
|
||||
#define INFO2 1
|
||||
#define WARNING 2
|
||||
/**
|
||||
* Prints warning/info.
|
||||
*/
|
||||
static void log_info(int cls, const char *msg, ...)
|
||||
{
|
||||
va_list argp;
|
||||
int attr;
|
||||
switch (cls) {
|
||||
case INFO1: attr = 95; break;
|
||||
case INFO2: attr = 35; break;
|
||||
case WARNING: attr = 91; break;
|
||||
}
|
||||
fprintf(stderr, "\x1b[%dm", attr);
|
||||
va_start(argp, msg);
|
||||
vfprintf(stderr, msg, argp);
|
||||
va_end(argp);
|
||||
fprintf(stderr, "\x1b[0m");
|
||||
}
|
||||
|
||||
static const char hexchars[16] = "0123456789abcdef";
|
||||
/**
|
||||
* Log a raw response for debugging.
|
||||
*/
|
||||
static void debug_response(const uint8_t *esp_buffer, size_t n) {
|
||||
if (debug) {
|
||||
for(size_t i = 0; i < n; ++i) {
|
||||
if (esp_buffer[i] < 32 || esp_buffer[i] >= 127) {
|
||||
fputc('\\', stderr);
|
||||
fputc('x', stderr);
|
||||
fputc(hexchars[esp_buffer[i] >> 4], stderr);
|
||||
fputc(hexchars[esp_buffer[i] & 0xf], stderr);
|
||||
} else {
|
||||
fputc(esp_buffer[i], stderr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints error message on stderr and exits the program.
|
||||
*/
|
||||
static void my_err(char *msg, ...)
|
||||
{
|
||||
va_list argp;
|
||||
|
||||
fprintf(stderr, "error: ");
|
||||
va_start(argp, msg);
|
||||
vfprintf(stderr, msg, argp);
|
||||
va_end(argp);
|
||||
fprintf(stderr, "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates or reconnects to a tun/tap device. *dev specifies the name of the
|
||||
* interface (e.g. tunX). This will be overwritten with the actual name. The
|
||||
* caller must reserve enough space (IFNAMSIZ) in *dev.
|
||||
*/
|
||||
int tun_alloc(char *dev, int flags)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
int fd, err;
|
||||
const char *clonedev = "/dev/net/tun";
|
||||
|
||||
if ((fd = open(clonedev, O_RDWR)) < 0) {
|
||||
perror("Opening /dev/net/tun");
|
||||
return fd;
|
||||
}
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
|
||||
ifr.ifr_flags = flags;
|
||||
|
||||
if (*dev) {
|
||||
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
|
||||
}
|
||||
|
||||
if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) {
|
||||
perror("ioctl(TUNSETIFF)");
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
strcpy(dev, ifr.ifr_name);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read routine that checks for errors and exits if an error is
|
||||
* returned (including unexpected EOF).
|
||||
*/
|
||||
static int cread(int fd, uint8_t *buf, int n)
|
||||
{
|
||||
int nread;
|
||||
|
||||
if ((nread = read(fd, buf, n)) <= 0) {
|
||||
perror("Reading data");
|
||||
exit(1);
|
||||
}
|
||||
return nread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write routine that checks for errors and exits if an error is
|
||||
* returned (including unexpected EOF).
|
||||
*/
|
||||
static int cwrite(int fd, const uint8_t *buf, int n)
|
||||
{
|
||||
int nwrite;
|
||||
|
||||
if ((nwrite = write(fd, buf, n)) <= 0) {
|
||||
perror("Writing data");
|
||||
exit(1);
|
||||
}
|
||||
return nwrite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures we write exactly n bytes. Exit in case of error or EOF.
|
||||
*/
|
||||
static void write_all(int fd, const uint8_t *buf, int n)
|
||||
{
|
||||
int nwrite, left = n;
|
||||
|
||||
while (left > 0) {
|
||||
nwrite = cwrite(fd, buf, left);
|
||||
left -= nwrite;
|
||||
buf += nwrite;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures we write exactly n bytes, escaping special characters. Exit in case
|
||||
* of error or EOF.
|
||||
*/
|
||||
static void write_esc(int fd, const char *buf, int n)
|
||||
{
|
||||
for (int i = 0; i < n; ++i) {
|
||||
if (buf[i] == '\\' || buf[i] == ',' || buf[i] == '"') {
|
||||
write_all(fd, S("\\"));
|
||||
}
|
||||
write_all(fd, (const uint8_t*)&buf[i], 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a 32-bit unsigned integer.
|
||||
*/
|
||||
static void write_uint(int fd, uint32_t x)
|
||||
{
|
||||
char buf[11];
|
||||
size_t ptr = sizeof(buf);
|
||||
do {
|
||||
ptr -= 1;
|
||||
buf[ptr] = '0' + (x % 10);
|
||||
x /= 10;
|
||||
} while (x != 0);
|
||||
write_all(fd, (const uint8_t*)&buf[ptr], sizeof(buf) - ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints usage and exits.
|
||||
*/
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stderr, "Usage:\n");
|
||||
fprintf(stderr, "%s <ifname> <uart> <ssid> <passwd> <host> <port>\n", progname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set UART attributes and speed.
|
||||
*/
|
||||
static int setup_uart(int fd, int speed)
|
||||
{
|
||||
struct termios2 tty;
|
||||
|
||||
if (ioctl(fd, TCGETS2, &tty) != 0) {
|
||||
my_err("Error from TCGETS2: %s", strerror(errno));
|
||||
}
|
||||
|
||||
tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
|
||||
tty.c_cflag &= ~CSIZE;
|
||||
tty.c_cflag |= CS8; /* 8-bit characters */
|
||||
tty.c_cflag &= ~PARENB; /* no parity bit */
|
||||
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
|
||||
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
|
||||
|
||||
/* custom baudrate setting */
|
||||
tty.c_cflag &= ~CBAUD;
|
||||
tty.c_cflag |= BOTHER;
|
||||
tty.c_ispeed = speed;
|
||||
tty.c_ospeed = speed;
|
||||
|
||||
/* setup for non-canonical mode */
|
||||
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
|
||||
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
|
||||
tty.c_oflag &= ~OPOST;
|
||||
|
||||
/* fetch bytes as they become available */
|
||||
tty.c_cc[VMIN] = 1;
|
||||
tty.c_cc[VTIME] = 1;
|
||||
|
||||
if (ioctl(fd, TCSETS2, &tty) != 0) {
|
||||
my_err("Error from TCSETS2: %s", strerror(errno));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Prefix for receive packet. */
|
||||
static const uint8_t pktprefix[] = {'+', 'I', 'P', 'D', ','};
|
||||
|
||||
struct packet_info {
|
||||
/* header: part after +IPD, and before : */
|
||||
const uint8_t *hdr;
|
||||
size_t hdr_len;
|
||||
/* packet payload */
|
||||
const uint8_t *payload;
|
||||
size_t payload_len;
|
||||
};
|
||||
|
||||
/** Determine if a esp_buffer[0..end] contains a complete ESP8285 response.
|
||||
* If so, return the size (including \r\n line terminators).
|
||||
* If not, return 0.
|
||||
*/
|
||||
static size_t esp_response_is_complete(const uint8_t *b, size_t end, struct packet_info *pinfo)
|
||||
{
|
||||
size_t i;
|
||||
/* Count number of bytes matching prefix. */
|
||||
for (i = 0; i < sizeof(pktprefix) && i < end && b[i] == pktprefix[i]; ++i)
|
||||
;
|
||||
if (i == sizeof(pktprefix)) {
|
||||
/* Received packet.
|
||||
* Syntax is +IPD,<len>[,<remote IP>,<remote port>]:<data>
|
||||
* Look for end of header (':') first.
|
||||
*/
|
||||
for (i = 5; i < end && b[i] != ':'; ++i)
|
||||
;
|
||||
if (i != end) {
|
||||
size_t hdr_end = i + 1;
|
||||
/* Parse length. */
|
||||
size_t length = atoi((const char*)&b[5]);
|
||||
if ((hdr_end + length) <= end) {
|
||||
if (pinfo != NULL) {
|
||||
pinfo->hdr = &b[5];
|
||||
pinfo->hdr_len = i - 5;
|
||||
pinfo->payload = &b[hdr_end];
|
||||
pinfo->payload_len = length;
|
||||
}
|
||||
return hdr_end + length;
|
||||
}
|
||||
}
|
||||
} else if (i == end) {
|
||||
/* Possive start of received packet response. These are not "\r\n"
|
||||
* terminated and may contain any character.
|
||||
*/
|
||||
return 0;
|
||||
} else {
|
||||
/** Other response - look for line terminator. */
|
||||
for (i = 0; i < end && b[i] != '\n'; ++i)
|
||||
;
|
||||
if (i != end) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_prefix(const uint8_t *esp_buffer, size_t len, const uint8_t *prefix, size_t prefix_len)
|
||||
{
|
||||
if (len < prefix_len) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < prefix_len; ++i)
|
||||
{
|
||||
if (esp_buffer[i] != prefix[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Get OK/FAIL status after running a command on the ESP. */
|
||||
static bool esp_read_responses(int fd)
|
||||
{
|
||||
/** TODO handle timeouts. */
|
||||
size_t end = 0;
|
||||
while (true) {
|
||||
ssize_t n = read(fd, &esp_buffer[end], ESP_BUFSIZE - end);
|
||||
if (n <= 0) {
|
||||
perror("Reading from UART");
|
||||
exit(1);
|
||||
}
|
||||
end += n;
|
||||
size_t ptr = 0;
|
||||
while (ptr < end) {
|
||||
const uint8_t *resp = &esp_buffer[ptr];
|
||||
size_t len = esp_response_is_complete(resp, end - ptr, NULL);
|
||||
if (len) {
|
||||
log_debug("<-: ");
|
||||
debug_response(resp, len);
|
||||
log_debug("\n");
|
||||
/* Check for final response */
|
||||
if (is_prefix(resp, len, S("OK"))
|
||||
|| is_prefix(resp, len, S("SEND OK"))) {
|
||||
return true;
|
||||
}
|
||||
if (is_prefix(resp, len, S("FAIL"))
|
||||
|| is_prefix(resp, len, S("ERROR"))
|
||||
|| is_prefix(resp, len, S("ALREADY CONNECTED"))) {
|
||||
return false;
|
||||
}
|
||||
/* Log interesting info responses */
|
||||
if (is_prefix(resp, len, S("+CIPSTA_CUR"))) {
|
||||
log_info(INFO2, " %.*s\n", len - 11 - 2, resp + 11);
|
||||
}
|
||||
if (is_prefix(resp, len, S("WIFI "))) {
|
||||
log_info(INFO2, " %.*s\n", len - 2, resp);
|
||||
}
|
||||
|
||||
/* On non-final response, keep reading. */
|
||||
} else {
|
||||
if (end == ESP_BUFSIZE) {
|
||||
/* Buffer full but command wasn't complete - this isn't good,
|
||||
* exit to prevent looping forever. */
|
||||
my_err("Buffer full with unterminated command");
|
||||
}
|
||||
break;
|
||||
}
|
||||
ptr += len;
|
||||
}
|
||||
/* Remove processed responses from esp_buffer by shifting bytes. */
|
||||
memmove(esp_buffer, &esp_buffer[ptr], end - ptr);
|
||||
end -= ptr;
|
||||
}
|
||||
}
|
||||
|
||||
/** Send a packet to ESP interface. */
|
||||
static void esp_tx_packet(int fd, const uint8_t *esp_buffer, size_t size) {
|
||||
write_all(fd, S("AT+CIPSEND="));
|
||||
write_uint(fd, size);
|
||||
write_all(fd, S("\r\n"));
|
||||
if (esp_read_responses(fd)) {
|
||||
write_all(fd, esp_buffer, size);
|
||||
esp_read_responses(fd);
|
||||
}
|
||||
}
|
||||
|
||||
/** Send a packet to tun interface. */
|
||||
static void tun_tx_packet(int fd, const uint8_t *esp_buffer, size_t size) {
|
||||
if (write(fd, esp_buffer, size) <= 0) {
|
||||
log_info(WARNING, "warning: error writing to tun: %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 7) {
|
||||
usage();
|
||||
}
|
||||
char if_name[IFNAMSIZ] = "";
|
||||
const char *portname = argv[2];
|
||||
const char *ssid = argv[3];
|
||||
const char *passwd = argv[4];
|
||||
|
||||
strncpy(if_name, argv[1], IFNAMSIZ-1);
|
||||
const char *host = argv[5];
|
||||
int port = atoi(argv[6]);
|
||||
|
||||
int esp_fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
|
||||
if (esp_fd < 0) {
|
||||
my_err("Error opening %s: %s", portname, strerror(errno));
|
||||
}
|
||||
|
||||
/* initial: baudrate 115200, 8 bits, no parity, 1 stop bit */
|
||||
setup_uart(esp_fd, 115200);
|
||||
|
||||
/* initialize tun/tap interface */
|
||||
int tun_fd;
|
||||
if ((tun_fd = tun_alloc(if_name, IFF_TUN | IFF_NO_PI)) < 0) {
|
||||
my_err("Error connecting to tun interface %s!\n", if_name);
|
||||
}
|
||||
|
||||
log_info(INFO1, "Successfully connected to interface %s\n", if_name);
|
||||
|
||||
log_info(INFO1, "Initializing device\n");
|
||||
write_all(esp_fd, S("ATE0\r\n"));
|
||||
if (!esp_read_responses(esp_fd)) {
|
||||
my_err("Could not disable echo");
|
||||
}
|
||||
|
||||
write_all(esp_fd, S("AT+CWMODE_CUR=1\r\n"));
|
||||
if (!esp_read_responses(esp_fd)) {
|
||||
my_err("Could not switch to station mode");
|
||||
}
|
||||
|
||||
write_all(esp_fd, S("AT+CIPMUX=0\r\n"));
|
||||
if (!esp_read_responses(esp_fd)) {
|
||||
my_err("Could not switch to single-connection mode");
|
||||
}
|
||||
|
||||
log_info(INFO1, "Changing baudrate to %d\n", BAUDRATE);
|
||||
write_all(esp_fd, S("AT+UART_CUR="));
|
||||
write_uint(esp_fd, BAUDRATE);
|
||||
write_all(esp_fd, S(",8,1,0,0\r\n"));
|
||||
if (!esp_read_responses(esp_fd)) {
|
||||
my_err("Could not set faster baudrate");
|
||||
}
|
||||
setup_uart(esp_fd, BAUDRATE);
|
||||
|
||||
write_all(esp_fd, S("AT\r\n"));
|
||||
if (!esp_read_responses(esp_fd)) {
|
||||
my_err("AT test unsuccesful: new baudrate unstable?");
|
||||
}
|
||||
|
||||
log_info(INFO1, "Connecting to AP\n");
|
||||
write_all(esp_fd, S("AT+CWJAP_CUR=\""));
|
||||
write_esc(esp_fd, ssid, strlen(ssid));
|
||||
write_all(esp_fd, S("\",\""));
|
||||
write_esc(esp_fd, passwd, strlen(passwd));
|
||||
write_all(esp_fd, S("\"\r\n"));
|
||||
if (!esp_read_responses(esp_fd)) {
|
||||
my_err("Could not connect to AP");
|
||||
}
|
||||
|
||||
write_all(esp_fd, S("AT+CIPSTA_CUR?\r\n"));
|
||||
if (!esp_read_responses(esp_fd)) {
|
||||
my_err("Could not query IP");
|
||||
}
|
||||
|
||||
log_info(INFO1, "Opening UDP connection to %s:%d from port %d\n", host, port, port);
|
||||
write_all(esp_fd, S("AT+CIPSTART=\"UDP\",\""));
|
||||
write_esc(esp_fd, host, strlen(host));
|
||||
write_all(esp_fd, S("\","));
|
||||
write_uint(esp_fd, port);
|
||||
write_all(esp_fd, S(","));
|
||||
write_uint(esp_fd, port); /* local port is same as remote port for now */
|
||||
write_all(esp_fd, S("\r\n"));
|
||||
if (!esp_read_responses(esp_fd)) {
|
||||
my_err("Could not open UDP connection");
|
||||
}
|
||||
|
||||
log_info(INFO1, "Starting packet loop, moving to background\n");
|
||||
|
||||
/* Process to background */
|
||||
if (daemon(0, 0) < 0) {
|
||||
my_err("Could not go to background\n");
|
||||
}
|
||||
debug = 0; /* no use to log anything after this */
|
||||
|
||||
struct pollfd fds[2] = {
|
||||
{esp_fd, POLLIN, 0},
|
||||
{tun_fd, POLLIN, 0},
|
||||
};
|
||||
do {
|
||||
for (int i=0; i<2; ++i) {
|
||||
fds[i].revents = 0;
|
||||
}
|
||||
|
||||
if (poll(fds, 2, 0) < 0) {
|
||||
perror("poll");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Handle input from UART */
|
||||
if (fds[0].revents & POLLIN) {
|
||||
/* Read all available responses entirely */
|
||||
size_t end = 0;
|
||||
while (true) {
|
||||
ssize_t n = read(esp_fd, &esp_buffer[end], ESP_BUFSIZE - end);
|
||||
if (n <= 0) {
|
||||
perror("Reading from UART");
|
||||
exit(1);
|
||||
}
|
||||
end += n;
|
||||
|
||||
size_t ptr = 0;
|
||||
while (ptr < end) {
|
||||
const uint8_t *resp = &esp_buffer[ptr];
|
||||
struct packet_info pkt_info;
|
||||
size_t len = esp_response_is_complete(resp, end - ptr, &pkt_info);
|
||||
if (len) {
|
||||
log_debug("<-: ");
|
||||
debug_response(resp, len);
|
||||
log_debug("\n");
|
||||
|
||||
if (is_prefix(resp, len, pktprefix, sizeof(pktprefix))) {
|
||||
/* Received UDP packet. */
|
||||
log_debug("NET2TUN %lu: Read %d bytes from the esp interface\n", net2tap, pkt_info.payload_len);
|
||||
net2tap++;
|
||||
tun_tx_packet(tun_fd, pkt_info.payload, pkt_info.payload_len);
|
||||
}
|
||||
} else {
|
||||
if (end == ESP_BUFSIZE) {
|
||||
/* Buffer full but command wasn't complete - this isn't good,
|
||||
* exit to prevent looping forever. */
|
||||
my_err("Buffer full with unterminated command");
|
||||
}
|
||||
break;
|
||||
}
|
||||
ptr += len;
|
||||
}
|
||||
/* Remove processed responses from esp_buffer */
|
||||
memmove(esp_buffer, &esp_buffer[ptr], end - ptr);
|
||||
end -= ptr;
|
||||
if (!end) {
|
||||
/* All responses processed, back to select. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle input from TUN
|
||||
if (fds[1].revents & POLLIN) {
|
||||
/* data from tun/tap: just read it and write it to the network */
|
||||
size_t nread = cread(tun_fd, tap_buffer, TAP_BUFSIZE);
|
||||
|
||||
log_debug("TUN2NET %lu: Read %d bytes from the tap interface\n", tap2net, nread);
|
||||
|
||||
tap2net++;
|
||||
esp_tx_packet(esp_fd, tap_buffer, nread);
|
||||
}
|
||||
} while (1);
|
||||
|
||||
return 0;
|
||||
}
|
130
linux/term.c
Normal file
130
linux/term.c
Normal file
@ -0,0 +1,130 @@
|
||||
/** term: Minimal interactive serial console.
|
||||
* Usage: term /dev/ttyS1
|
||||
*
|
||||
* Copyright (c) 2020 W.J. van der Laan
|
||||
* Distributed under the MIT software license,
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
|
||||
int set_raw(int fd)
|
||||
{
|
||||
struct termios tty;
|
||||
|
||||
if (tcgetattr(fd, &tty) < 0) {
|
||||
printf("Error from tcgetattr: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
tty.c_iflag &= ~(ICRNL | IXON);
|
||||
tty.c_lflag &= ~(ECHO | ICANON);
|
||||
|
||||
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
|
||||
printf("Error from tcsetattr: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int set_interface_attribs(int fd, int speed)
|
||||
{
|
||||
struct termios tty;
|
||||
|
||||
if (tcgetattr(fd, &tty) < 0) {
|
||||
printf("Error from tcgetattr: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfsetospeed(&tty, (speed_t)speed);
|
||||
cfsetispeed(&tty, (speed_t)speed);
|
||||
|
||||
tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
|
||||
tty.c_cflag &= ~CSIZE;
|
||||
tty.c_cflag |= CS8; /* 8-bit characters */
|
||||
tty.c_cflag &= ~PARENB; /* no parity bit */
|
||||
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
|
||||
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
|
||||
|
||||
/* setup for non-canonical mode */
|
||||
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
|
||||
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
|
||||
tty.c_oflag &= ~OPOST;
|
||||
|
||||
/* fetch bytes as they become available */
|
||||
tty.c_cc[VMIN] = 1;
|
||||
tty.c_cc[VTIME] = 1;
|
||||
|
||||
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
|
||||
printf("Error from tcsetattr: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Pass serial device on command line\n");
|
||||
exit(1);
|
||||
}
|
||||
const char *portname = argv[1];
|
||||
int fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Error opening %s: %s\n", portname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
set_raw(STDIN_FILENO);
|
||||
/*baudrate 115200, 8 bits, no parity, 1 stop bit */
|
||||
set_interface_attribs(fd, B115200);
|
||||
|
||||
struct pollfd fds[2] = {
|
||||
{STDIN_FILENO, POLLIN, 0},
|
||||
{fd, POLLIN, 0},
|
||||
};
|
||||
do {
|
||||
for (int i=0; i<2; ++i) {
|
||||
fds[i].revents = 0;
|
||||
}
|
||||
|
||||
if (poll(fds, 2, 0) < 0) {
|
||||
fprintf(stderr, "Poll error: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (int i=0; i<2; ++i) {
|
||||
if (fds[i].revents & POLLIN) {
|
||||
char ch;
|
||||
int rdlen = read(fds[i].fd, &ch, 1);
|
||||
if (rdlen < 0) {
|
||||
fprintf(stderr, "Error from read: %d: %s\n", rdlen, strerror(errno));
|
||||
exit(1);
|
||||
} else if (rdlen == 0) {
|
||||
/* EOF */
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (i == 1 && ch == '\r') {
|
||||
/* Don't print \r */
|
||||
continue;
|
||||
}
|
||||
if (i == 0 && ch == '\n') {
|
||||
/* Add extra \r before newline */
|
||||
char cr = '\r';
|
||||
write(fds[1-i].fd, &cr, 1);
|
||||
}
|
||||
|
||||
int wlen = write(fds[1-i].fd, &ch, 1);
|
||||
if (wlen <= 0) {
|
||||
fprintf(stderr, "Error from write: %d: %s\n", wlen, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (1);
|
||||
}
|
Loading…
Reference in New Issue
Block a user