Subject: Re: gvisor's 32-bit compatibility

From: John Goerzen <[email-masked]>
Date: Fri, 21 Jan 2022 08:45:22 -0600

Hi Sergey, and thanks for all your work on this stuff!

On Fri, Jan 21 2022, Sergey Matveev wrote:

> Greetings!
>
> *** John Goerzen [2022-01-20 15:45]:
>>And according to the FAQ [1] , it is only supported on
>>Linux on x86_64 (with "preliminary" support for arm64).
>
> And indeed its latest commit even does not build on FreeBSD, so I used
> one that was mentioned in golang.zx2c4.com/wireguard/tun/netstack.

I saw that as I was working to package up Yggdrasil for Debian (both for
its own sake and for the sake of being able to package the newer NNCP)
and specifically had to exclude netstack because it was going to be
incompatible.

>
>>vendor/gvisor.dev/gvisor/pkg/tcpip/transport/tcp/snd.go:326:16: constant 9223372036854775807 overflows int
>>
>>That doesn't bode well..  It wouldn't surprise me at all if there are
>>other issues that would keep it from working on 32-bit platforms, or
>>non-Linux ones.
>
> Probably and I won't be surprised too. But I would change constant in
> snd.go from MaxInt64 to MaxInt32 and try to build it again. NNCP
> requires relatively small part of gvisor's overall code, so I think
> there is high probability that everything will work fine after.

So I've got really four problems here:

1) How I use NNCP for personal purposes;

2) How I maintain NNCP for Debian (+ Ubuntu and all the other Debian
derivatives, including Raspberry Pi OS and Raspbian)

3) The level of effort required to maintain NNCP for #1 and #2 on an
ongoing basis

4) I'm not a Go programmer, though it is similar enough to C and Rust
that I can understand things like an int overflow :-)

So perhaps that tweak would be good enough, for now, to get NNCP running
on my Raspberry Pis again.  But then I have to think about:

- Is this patch going to break things on 64-bit systems?  (It would be
  an unusual challenge to patch something only on 32-bit systems in
  Debian.  Possible but not simple)

- Are there going to be future gvisor problems on any of my platforms,
  or any of Debian's?  (which are vast)

- Debian likes to have one version of each library in the repo, so if
  something else needs a version of gvisor that breaks, then we get
  stuck.

> Personally I do not try to support 32-bit systems too, as also many
> other software. For example ZFS officially is not supported on non
> 64-bit systems. Even in Go 32-bit support is not so trivial thing to
> reach: it is not only about "int" width expectations (as I thought some
> years before), but for example sync/atomic clearly states that you have
> to manually align fields in structure on 64-bit boundaries, otherwise
> those atomic instructions may painfully fail. With 64-bit code you do
> not care much about that alignment, because int, int64 and pointers are
> all 64-bit and "automatically" aligned. It is just much easier to write
> 64-bit code. I try to make no expectations about "int" width and clearly
> define int32/int64 where it is appropriate and important, but I do not
> know how many other potential differences (like sync/atomic) there are.

I can understand that perspective, certainly, and it's unfortunate that
Go makes you think about that stuff at that level.  I do think that
32-bit systems are *IDEAL* NNCP nodes.  For instance, projects like
FreedomBox [1] are all about many of the same goals NNCP has, derive
from Debian, and typically run on embedded hardware which is usually
32-bit.  Of course, NNCP is also ideal for Raspberry Pi.  Most Pis are
32-bit, and even the ones that are 64-bit typically run a pure 32-bit
userland.  I personally use it to back up my collection of Pis that do
various things; it is absolutely ideal for that!

[1] https://www.freedombox.org/

>>I'd much rather
>>have a uTP/TCP mismatch than sacrifice platform support in NNCP for
>>sure.
>
> I can add some kind of build switch (GO_CFLAGS="-tags XXX") and add
> support of μTP as a replacement of TCP/IP. Yggdrasil-related code will
> stay the same, but it will just use utp-code for Listen/Dial functions,
> instead of gvisor's tcpip-code. Of course with returned drawback of
> inability to deal with other nodes over TCP/IP, just only with the same
> NNCP-μTP nodes.

That would work for me (if the sync issue can be resolved).  I would
probably set this flag on all Debian platforms to avoid the gvisor mess.

To give you an idea of the requirements for platform support in Debian,
see:

https://buildd.debian.org/status/package.php?p=nncp

Basically all the platforms with the white background are "release"
platforms and, in general, a package is required to build and work on
all those platforms if it's going to be included in the archive.  Nearly
half of them are 32-bit (armel, armhf, i386, and mipsel).

The gray platforms are ones that are active in Debian but don't meet the
requirements for an official stable release, and thus packages aren't
required to work on them.  Most of them don't have NNCP because
golang-go doesn't work on them.  You'll note that Debian GNU/FreeBSD is
a thing!  Some of them are former release platforms (alpha, ia64, m68k,
and powerpc) while others will probably be release platforms in the
future (riscv64).  Some will probably never see a release (hurd-i386).

> I definitely do not wish to remove/revert tcpip, because it works faster
> and honestly acts as real host with IPv6+TCP network stack. I do not
> like utp-implementation at least because it depends on
> github.com/anacrolix/sync library, having no information about its
> acceptable usage, that actually has to be treated as non-free software.

That is a good catch and also a showstopper for Debian.  I will contact
the author to see if he could add a copyright statement and reasonable
license.

> It is pretty small and one can rewrite/replace its usage with something
> clearly free, but someone has to do it (I do not want). And it is not
> clear in what state is github.com/neilalexander/utp's code, that
> explicitly notes that you should use libutp-wrapper library instead
> (depending on github.com/anacrolix/sync again), that is also not an
> option, because requires linking with C++ code, instead of pure-Go one.

That would be fine with me.  neilalexander is active in the Yggdrasil
Matrix channel and I asked him about the state of the utp code:

His reply:

"Well, it *works*, but I'm not sure it's a glowing implementation or is
it perfect"

I also mentioned the anacrolix license issue.  He added:

"Be warned that a lot of anacrolix's code is a horrendous mess of
dependencies and weird wrappers around things (like the god-awful
missinggo library). So you may find there are further steps to that
problem."

But, I could email anacrolix and see if he's willing to adjust the
license if you'd like me to.

Neil also mentioned quic-go.  I took a look at it, and there is an
example at
https://github.com/lucas-clemente/quic-go/blob/master/example/echo/echo.go
that is a fairly basic echo server and client.  Although the TLS layer
isn't disabled there, there is no key validation done
(InsecureSkipVerify: true).  This would be fine for NNCP since it does
its own security.  It is, in fact, common to run Yggdrasil inside TLS -
providing no real addition security, but the performance impact seems
to be minimal.

quic-go is already in Debian which implies something good about platform
support for it.

So there are maybe some options:

1) Stick with TCP and have a big, carefully documented, Yggdrasil on/off
switch.  People that want Yggdrasil on 32-bit platforms could still use
a standalone Yggdrasil node and it would be compatible.  I would disable
Yggdrasil in the Debian build to provide uniform experience across the
Debian platforms.

2) Try to support uTP and TCP with compile-time options (here I would
have to resolve the uTP license issue before this could be included in Debian)

3) Switch to quic-go

It seems to me that #3 would be the easiest from a platform support
perspective.  Maybe there is a marginal performance hit, but really with
NNCP being asynchronous and the overhead of Yggdrasil already, I would
certainly live with that.

> and ACKing of every segment, but that will have awful performance. I did
> not find anything with sliding window support, something like Zmodem at
> least. With direct TCP connection between the nodes there are no

This is at least the third time in the past year I've wanted to
reimplement a UUCP protocol in a more general way :-)

> I met pure-Go QUIC implementation, but as far as I remember TLS can not
> be turned off in it. I won't accept that TLS complication and huge
> unneeded overhead.

Do you mean "overhead" in terms of compiled program size, performance
impact, code complexity, or something else?

It seems code complexity would be reasonable.  The only way to really
know the other two would be to try.

FWIW, on NetBSD, nncp-caller 7.5.1 was 6MB and 8.2.0 is 10MB.  Adding
Yggdrasil+gvisor didn't add all that much to its size.  (both
unstripped).  After stripping, the sizes are 4.3MB and 7.6MB.

John