Nov 182018
 

So today I was meant to be helping re-build a deck, but that got postponed to next weekend.  Thus, I had an extra free day I wasn’t counting on.

I wound up looking at LinBPQ in detail, to see if I can get it to run.  I downloaded the sources, and sure enough, they do compile on my x86-64 laptop, but does it work?  Not a chance.  Starts parsing the configuration file, then boompa, SEGFAULT.

I run the binary through gdb, and see this:

GNU gdb (Gentoo 8.1 p1) 8.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://bugs.gentoo.org/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /home/stuartl/projects/6lowham/linbpq/linbpq...done.
(gdb) r
Starting program: /home/stuartl/projects/6lowham/linbpq/linbpq 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
G8BPQ AX25 Packet Switch System Version 6.0.17.1 November 2018
Copyright � 2001-2018 John Wiseman G8BPQ
Current Directory is /var/lib/linbpq

Configuration file Preprocessor.
Using Configuration file /var/lib/linbpq/bpq32.cfg
Conversion (probably) successful


Program received signal SIGSEGV, Segmentation fault.
0x00005555555f8a7b in Start () at cMain.c:1190
1190                    *(ptr3++) = *(ptr2++);
(gdb) bt full
#0  0x00005555555f8a7b in Start () at cMain.c:1190
        cfg = 0x555555b91c40
        ptr1 = 0x555555ba60c0
        PORT = 0x5555558f6aa0 
        FULLPORT = 0x558f7928
        NEXTPORT = 0x5555558f6de0 <DATAAREA+832>
        EXTPORT = 0x7ffff6eb7953 <_IO_file_overflow+291>
        APPL = 0x5555558f49e0 
        ROUTE = 0x559085e8
        DEST = 0x870b07e2ddd5f300
        CMD = 0x5555558d79e0 
        PortSlot = 2
        ptr2 = 0x555555ba6849 "K4MSL Test station \r"
        ptr3 = 0x55912549 
        ptr4 = 0x5555558d7183 <COMMANDS+1667> "         \003"
        CWPTR = 0x5555558f6b18 <DATAAREA+120>
        i = 0
        n = 119
        int3 = 1435466024
#1  0x000055555563e35c in main (argc=1, argv=0x7fffffffe518) at LinBPQ.c:598
        i = 1
        user = 0x0
        conn = 0x7ffff7ffa298
        STAT = {st_dev = 140737354131120, st_ino = 140737488347784, st_nlink = 140737488347780, st_mode = 4160741648, 
          st_uid = 32767, st_gid = 4143745959, __pad0 = 32767, st_rdev = 140737488348192, st_size = 140737488347784, 
          st_blksize = 1700966438, st_blocks = 26577600, st_atim = {tv_sec = 140737354113688, tv_nsec = 140737488348000}, 
          st_mtim = {tv_sec = 140737354113448, tv_nsec = 140737488347780}, st_ctim = {tv_sec = 140737488347984, 
            tv_nsec = 140737354131160}, __glibc_reserved = {1, 4150715120, 0}}
        PORTVEC = 0x7ffff7ffe6b0

Ookay then… so invalid pointers, what fun!  More to the point, have a close look at the underlined addresses… I’m beginning to understand why it was called BPQ32.

The culprit for this wound up being little gems like this:

			//	Round to word boundary (for ARM5 etc)

			int3 = (int)ptr3;
			int3 += 3;
			int3 &= 0xfffffffc;
			ptr3 = (UCHAR *)int3;

There were a few other instances of this, and variations on the theme too, but one way or the other, linbpq basically assumes that all pointers are 32-bits, and so are ints.

Four hours later, I finally had something that started, but there are probably lots of landmines for anyone running the binary to inadvertently stomp on.  The code is pointer-arithmetic city!  Much of the time, code is casting pointers to unsigned int, or back again.  If I submitted code like that at work, they’d have me hauled ’round the back of the building and shot!

I’m left wondering if it’s worth getting to understand, or should I shove it in a VM, write some code based on my understanding of the protocols, do some integration testing with it, then abandon LinBPQ for something I can have confidence in.

The use and re-use of certain variables makes me wonder if the code is actually a port from the DOS-based BPQCode which was likely written in 8086 assembler.  This would make a lot of sense as to why I’m seeing the sorts of software coding patterns I’m seeing in that code.  The logic seems to have been ported to C just enough to get it to compile and work like the assembly version.

Reasonable enough… but there’s a lot of technical debt there still waiting to be paid back.  On paper, there’s a lot of benefit in using LinBPQ as the back-end, and I am thankful that John Wiseman made the decision to release the code under the GPLv3 so that I can at least investigate the possibility of using that code here.

I’ve thrown what I’ve got up on Github for now, and there’s a Gentoo overlay for installing it.  Add the overlay and run emerge linbpq, and you should find yourself with an installation of LinBPQ that just needs some OpenRC scripts and some work with an editor on /var/lib/linbpq/bpq32.cfg to get going.

If I get further on the code front, I might look at some init scripts, both OpenRC and systemd ones, then I can produce a few Debian binaries so you can run apt-get install linbpq on your Raspberry Pi and have a packet station going quickly.

Nov 152018
 

Having discussed the idea with a few people, both on the linux-hams mailing list and off-list, I’m starting to formalise a few plans for how this might work.

One option is to augment existing software stacks and inter-operate not just over-the-air, but at an API level.  Brisbane WICEN have a fleet of TNCs all running TheNet X1J, which was a popular Net/ROM software stack for TAPR TNC2-compatible TNCs in the early 90s.  Slowly, these are being replaced with Raspberry Pis equipped with Pi-TNCs and running LinBPQ.

These two inter-operate quite well, and the plan looks to be, to slowly upgrade all the sites to LinBPQ nodes.

Now, 6LoWHAM on TNCs that are nearly as old as I am just isn’t going to fly, but if I can link up to LinBPQ, this alternate protocol can be packaged up and installed along-side LinBPQ in an unobtrusive manner.

There are two things I need to be able to do:

  • Send and receive raw AX.25 frames
  • Read the routing table from LinBPQ

Sending and receiving raw frames

Looking at the interfaces that LinBPQ (and BPQ32) offers, the most promising option looks to be the AGWPE-compatible interface.  The protocol is essentially a TCP link over which the AX.25 frames are encapsulated and sent.

There’s a good description of the protocol here, and looking at the sources for LinBPQ (third link from the bottom of the page), it looks as if the necessary bits of the protocol are present to send and receive raw frames.

In particular, to send raw UI frames, I need to send these as ‘M’ (direct) or ‘V’ frames (via digipeater), and to receive them, I need to make use of the monitoring mode (‘m’ frame).

Reading the routing table

This, is where things will be “fun”.  The AGWPE interface does offer a “heard” frame, which can report on what stations have been heard.  This I think isn’t going to be the holy grail I’m after, although it’ll be a start, maybe.

Alternatively, a way around this might be to “eavesdrop” on the Net/ROM routing frames.  In monitor mode, I should theoretically hear all traffic, including these Net/ROM beacons.  It’s not as nice as being able to simply read LinBPQ’s routing table, but at least I don’t have to generate the Net/ROM messages.

The other way would be to connect to the terminal interface on LinBPQ, and use the NODES command, parsing that.  Ugly, but it’ll get me by.  On that same page is NRR… which looks to be similar in function to TCP/IP’s traceroute.  The feature is also supported by JNOS 2.0, which was released in 2006.  Not old by packet radio standards, but old enough.

Identifying if a remote station supports 6LoWHAM

Now, this is the tricky bit.  Identifying an immediate neighbour is easy enough, you can simply send an ICMPv6 neighbour solicitation message and see if they respond.  In fact, I’m thinking that could be the immediate first step.  There’s no support for service discovery as such, but nodes could advertise an “alias” (just one).

The best bet may be a suck-it-and-see approach.  We should be able to “digipeat” via intermediate nodes as if they were plain L2 AX.25 digipeaters, thus if we have a reason to contact a given node (i.e. there’s unicast traffic queued up to be sent there), we can just try routing an AX.25 frame with a ICMPv6 neighbour solicitation and see if we get a neighbour advertisement.

This carries a risk though: a station may not react well to unknown traffic and may try to parse the message as something it is not.  Thus for unicast, it is not a fail-safe method.

Multicast traffic however will be a challenge, and much of IPv6 relies on multicast.  The Net/ROM station will not know anything about this, as it simply wasn’t a concept back in the day.

For subnets like ff03::1, which on Thread networks usually means “all full-function Thread devices”, this could be sent via non-6LoWHAM digipeaters by broadcasting via that digipeater to the AX.25 station alias “6LHMC” (6LoWHAM Multicast).

This could be used to provide tunnelling of multicast traffic where a route to a station has been discovered via Net/ROM and we need to safely test whether the station can in fact understand 6LoWHAM traffic without the risk of crashing it.

I think the next step might be to look at how a normal IPv6 node would “register” interest in a multicast group so that routers between it and the sender of such a group know where to forward traffic.  IPv6 does have such a mechanism, and I think understanding how multicast traverses subnets is going to be key to making this work.