💾 Archived View for gemini.complete.org › using-gensio-and-ser2net captured on 2024-08-31 at 12:07:09. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2024-07-09)
-=-=-=-=-=-=-
gensio[1] and the related ser2net[2] are generic tools for communication.
1: https://github.com/cminyard/gensio
2: https://github.com/cminyard/ser2net
With gensio, you can set up a sort of pipeline of communication, using sockets, files, stdin/stdout, serial lines, external programs, and so forth. It is akin to a more powerful version of socat[3], netcat[4], redir, or stunnel.
3: http://www.dest-unreach.org/socat/
Generally, with gensio and ser2net, you can:
A communication pipeline is called a "gensio stack" and is given in a string format, separated by commas. A gensio stack is composed of one or more "gensios", which will be of two different types:
The word "gensio" can refer to both a component of the stack, or the multi-language gensio library itself. Built atop the gensio library are the ser2net daemon and gensiot command-line program.
Here's an example of connecting to example.com with telnet:
telnet,tcp,example.com,23
Here, `telnet` is a filter gensio that adds RFC2217 telnet to the connection. `tcp,example.com,23` is a terminal gensio that gives the connector (TCP) and the destination (example.com port 23).
Generally, you will connect gensio stacks to each other. The `gensiot` program (in the `gensio-bin` package on Debian[5]) will, by default, use stdin/stdout (the `stdio` gensio) for one end, and let you specify the other. But, you can specify both.
`ser2net` is a daemon that is based on gensio. Its original purpose was to bridge serial ports to the network (particularly letting you telnet to a certain port and have it bridged to a serial device). However, it is really a multi-purpose gensio daemon and can do anything from terminating TLS to TCP port forwarding. The ser2net configuration file[6] simply defines pairs of gensios; the accepter that listens, and the connector that gives the destination. You can install the `ser2net` Debian package to get it.
6: https://manpages.debian.org/unstable/ser2net/ser2net.yaml.5.en.html
I will mostly demonstrate the `gensiot` program here, but these examples can be easily daemonized by using ser2net.
The gensio(5) manpage[7] gives details on all the gensios. Because gensio supports TLS-based security and authentication, a `gtlssh` program can act as a replacement for the standard ssh, running atop gensio.
7: https://manpages.debian.org/unstable/gensio-bin/gensio.5.en.html
Let's dive in!
These are some of the simplest examples. You can make a listening gensio (similar to `netcat -l`) like this:
gensiot -a tcp,12345
The `-a` says that we are running an accepter (listener). Now, we can open up another terminal and connect to it:
gensiot tcp,localhost,12345
You might be surprised that as you type on either end, you don't see your characters echoed back to the screen as you would with netcat, but you do see them showing up in the other terminal. Also, unlike netcat, characters are sent immediately rather than after you hit enter. You also may note that Ctrl-C doesn't terminate. That's because, when gensiot detects stdio is a terminal, it puts the terminal in raw mode (using the `stdio(raw,self)` gensio). This is, after all, what you'd want if you are using it as a telnet client. You can exit by pressing Ctrl-\ and then hit q.
To get a more netcat-like behavior, use:
gensiot -i 'stdio(self)' tcp,localhost,12345
The `-i` gives the other ("input") gensio, and in this case `stdio(self)` will leave it in a more standard mode. gensiot also uses `stdio(self)` when stdio is not a terminal.
So how about using it as a HTTP client? Here's an example:
gensiot -i 'stdio(self)' tcp,example.com,http GET / HTTP/1.1 Host: example.com Connection: close HTTP/1.1 200 OK ...
Sometimes you want to forward a TCP port. For instance, I had a need to receive a connection on an IPv4 port and forward it out across IPv6 using Yggdrasil[8]. The redir program in Debian is designed to do this, but sadly redir isn't IPv6-aware. This could be accomplished using ser2net, but I happened to do it with gensio using inetd.conf.
Let's say you want to receive a connection on port 12345 on your local machine, and want to forward it to port 23456 on example.com. Assuming you have installed `tcpd` and `openbsd-inetd` (or similar packages for other distributions), you can add this to `/etc/inetd.conf`:
12345 stream tcp nowait nobody /usr/sbin/tcpd /usr/bin/gensiot tcp,example.com,23456
You can further add userspace access control with `/etc/hosts.allow` and `hosts.deny` as usual. Or, of course, firewall it.
The same could be done with ser2net with an acceptor of `tcp,12345` and a connector of `tcp,example.com,23456`.
gensio supports TLS/SSL termination and authentication as well; see the manpages for more details. With that support, it can act as a replacement for TLS terminators such as stunnel4.
gensio has full support for using serial ports and other stream-like devices. By default, gensio monitors the serial port control likes such as DCD and therefore knows when the remote end connects and disconnects.
gensio can add a lightweight support for making serial communications reliable. This includes error detection, retransmission, and sliding window support. The basic gensio for this is:
relpkt,msgdelim,serialdev,/dev/ttyS0
replacing `/dev/ttyS0` with whatever device you want to use, of course.
`msgdelim` provides packetization across a stream with a CRC16, but doesn't provide support for retransmitting missing or corrupted packets. relpkt adds that. It is much lower overhead than PPP while still providing a reliable channel for something that needs it.
For very fast serial links, you may wish to raise the default packet size from 128 bytes with something like this:
relpkt(max_pktsize=4000),msgdelim(writebuf=4005,readbuf=4005),serialdev,/dev/ttyS0,115200n81
The relpkt packet size needs to be at least 5 bytes smaller than the msgdelim packet size.
Ordinarily, running ssh over a serial port can be problematic unless the serial line is exceptionally clean. With gensio, we can make it clean!
On the system to receive the ssh connection, let's set up a proxy from serial to the ssh server running on port 22:
gensiot -i tcp,localhost,22 -a \ 'conacc,relpkt(mode=server,max_pktsize=4000),msgdelim(writebuf=4005,readbuf=4005),serialdev,/dev/ttyS0,115200n81'
Here the `-a` flag says to listen on the serial port. `conacc` and `mode=server` are necessary to go along with that. OK, so how do we connect to it? Add this to `~/.ssh/config`:
Host gensio ProxyCommand gensiot 'relpkt(max_pktsize=4000),msgdelim(writebuf=4005,readbuf=4005),serialdev,/dev/ttyS1,115200n81'
Now you can `ssh gensio` and connect to your remote machine via ssh! This only supports one ssh session. However, if you use ssh's handy connection muxing, you can run a bunch of ssh sessions across this one link. Just add this to the `Host gensio` part:
ControlMaster auto ControlPath ~/.ssh/cm-%r@%h:%p ControlPersist 5m
It is easy enough to run a serial getty on a serial port, but that's not a *reliable* link; it is susceptible to errors and corruption.
But you can do it easily enough. NOTE: there are security implications for what I'm about to show; I don't recommend running an unauthenticated shell in this way!
On one end, run:
gensiot -i pty,bash -a 'conacc,relpkt(mode=server),msgdelim,/dev/ttyUSB2,115200n81'
And to connect to it, run:
gensiot relpkt,msgdelim,/dev/ttyUSB3,115200n81
Instant shell. Again, don't just use this without adding some security. It would be better to run ssh or gtlssh over the port.
You could easily enough combine this with a telnet to localhost if you wanted a login.
While normally running NNCP[9] over a serial port isn't great because nncp-call(er) and nncp-daemon require a reliable and clean connection, we can easily run them over gensio.
On one machine, you can run nncp-daemon like this:
gensiot -i 'stdio,nncp-daemon -ucspi' -a \ 'conacc,relpkt(mode=server,max_pktsize=4000),msgdelim(writebuf=4005,readbuf=4005),serialdev,/dev/ttyUSB2,115200n81'
On another, find the addrs section for the target in nncp.hjson, and add:
gensio: "|gensiot 'relpkt(max_pktsize=4000),msgdelim(writebuf=4005,readbuf=4005),serialdev,/dev/ttyS1,115200n81'"
Additional information on these kinds of uses of NNCP can be found at Tunneling NNCP over other transports[10].
10: /tunneling-nncp-over-other-transports/
11: https://github.com/cminyard/gensio
12: https://github.com/cminyard/ser2net
--------------------------------------------------------------------------------
Sometimes we want better-than-firewall security for things. For instance:
14: /tunneling-nncp-over-other-transports/
NNCP[15] has built-in support for running over TCP, with nncp-daemon and nncp-call/caller. NNCP's own use cases[16] page talks about various use cases for NNCP. Some of them, such as the no link[17] page, cover use of nncp-xfer; others, such as the one-way broadcasting[18] page go over nncp-bundle.
16: https://nncp.mirrors.quux.org/Use-cases.html
17: https://nncp.mirrors.quux.org/UsecaseNoLink.html
18: https://nncp.mirrors.quux.org/UsecaseBroadcast.html
19: /old-and-small-technology/
Old technology is any tech that's, well... old.
(c) 2022-2024 John Goerzen