💾 Archived View for complete.org › using-gensio-and-ser2net captured on 2024-08-24 at 23:36:29. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2024-08-18)

-=-=-=-=-=-=-

Using gensio and ser2net

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/

4: https://nmap.org/ncat/

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.

5: /debian/

`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!

Network basics

netcat replacement

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
...

Running a TCP port forwarder using inetd.conf

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.

8: /yggdrasil/

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.

Serial port support

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.

Running real ssh over a serial port

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

Reliable program over a serial port

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.

NNCP over serial

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.

9: /nncp/

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/

Further Reading

11: https://github.com/cminyard/gensio

12: https://github.com/cminyard/ser2net

--------------------------------------------------------------------------------

Links to this note

13: /how-gapped-is-your-air/

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.

15: /nncp/

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.

More on www.complete.org

Homepage

Interesting Topics

How This Site is Built

About John Goerzen

Web version of this site

(c) 2022-2024 John Goerzen