6lowpan

6LoWHAM: TCP over sensor networks

I stumbled across this article regarding the use of TCP over sensor networks. Now, TCP has been done with AX.25 before, and generally suffers greatly from packet collisions. Apparently (I haven’t read more than the first few paragraphs of this article), implementations TCP can be tuned to improve performance in such networks, which may mean TCP can be made more practical on packet radio networks.

Prior to seeing this, I had thought 6LoWHAM would “tunnel” TCP over a conventional AX.25 connection using I-frames and S-frames to carry TCP segments with some header prepended so that multiple TCP connections between two peers can share the same AX.25 connection.

I’ve printed it out, and made a note of it here… when I get a moment I may give this a closer look. Ultimately I still think multicast communications is the way forward here: radio inherently favours one-to-many communications due to it being a shared medium, but there are definitely situations in which being able to do one-to-one communications applies; and for those, TCP isn’t a bad solution.

Comments having read the article

So, I had a read through it. The take-aways seem to be this:

  • TCP was historically seen as “too heavy” because the MCUs of the day (circa 2002) lacked the RAM needed for TCP data structures. More modern MCUs have orders of magnitude more RAM (32KiB vs 512B) today, and so this is less of an issue.
    • For 6LoWHAM, intended for single-board computers running Linux, this will not be an issue.
  • A lot of early experiments with TCP over sensor networks tried to set a conservative MSS based on the actual link MTU, leading to TCP headers dominating the lower-level frame. Leaning on 6LoWPAN’s ability to fragment IP datagrams lead to much improved performance.
    • 6LoWHAM uses AX.25 which can support 256-byte frames; vs 128-byte 802.15.4 frames on 6LoWPAN. Maybe gains can be made this way, but we’re already a bit ahead on this.
  • Much of the document considered battery-powered nodes, in which the radio transceiver was powered down completely for periods of time to save power, and the effects this had on TCP communications. Optimisations were able to be made that reduced the impact of such power-down events.
    • 6LoWHAM will likely be using conventional VHF/UHF transceivers. Hand-helds often implement a “battery saver” mode — often this is configured inside the device with no external control possible (thus it will not be possible for us to control, or even detect, when the receiver is powered down). Mobile sets often do not implement this, and you do not want to frequently power-cycle a modern mobile transceiver at the sorts of rates that 802.15.4 radios get power-cycled!
  • Performance in ideal conditions favoured TCP, with the article authors managing to achieve 30% of the raw link bandwidth (75kbps of a theoretical 250kbps maximum), with the underlying hardware being fingered as a possible cause for performance issues.
    • Assuming we could manage the same percentage; that would equate to ~360bps on 1200-baud networks, or 2.88kbps on 9600-baud networks.
  • With up to 15% packet loss, TCP and CoAP (its nearest contender) can perform about the same in terms of reliability.
  • A significant factor in effective data rate is CSMA/CA. aioax25 effectively does CSMA/CA too.

Its interesting to note they didn’t try to do anything special with the TCP headers (e.g. Van Jacobson compression). I’ll have to have a look at TCP and see just how much overhead there is in a typical segment, and whether the roughly double MTU of AX.25 will help or not: the article recommends using MSS of approximately 3× the link MTU for “fair” conditions (so ~384 bytes), and 5× in “good” conditions (~640 bytes).

It’s worth noting a 256-byte AX.25 frame takes ~2 seconds to transmit on a 1200-baud link. You really don’t want to make that a habit! So smaller transmissions using UDP-based protocols may still be worthwhile in our application.

SDR experiments, and how early is too early?

Recently, I’ve been doing a lot of work with 6LoWPAN on the 2.4GHz band. I didn’t have anything that would receive arbitrary signals on this frequency, so I decided to splurge. I got myself my first bit of tax-deductible amateur radio equipment: a HackRF One.

It’s been handy, fire up CubicSDR, and immediately you get a picture of what’s happening on the frequency. In the future I hope to get the WIME framework working so I can decode the 802.15.4 frames and pipe them to Wireshark, but so far, this has been handy.

Since I’m not using it every day, I also put it to a second use, DAB+ reception. I used to listen to various stations a lot, and whilst FM stereo is built into my phone, I’ve got nothing that will do medium-wave AM. The HackRF stops short at 1MHz (officially 10MHz), and needs a suitable antenna to do so. However, it occurred to me that it was more than capable of doing DAB+, so after some experimentation, I managed to get qt-dab working.

Since getting that working, I bought a second SDR, a RTL-SDR v3. The idea is I’d be setting this up on the bicycle with a Raspberry Pi 3 which also has a DRAWS board fitted (the successor to the UDRC). I figured I could use this as a second receiver for amateur radio stuff, or use it for FM stereo/DAB+, maybe short wave.

So today, I was testing this: using the RTL-SDR with a Pi 3, seeing whether it would perform acceptably for that task. Interestingly, CubicSDR will de-modulate FM stereo quite happily when you’re running it via a X11 session forwarded over SSH, but it stutters its way though if you try to run it natively. I think the waterfall displays are too much for the machine to cope with: it can render them, but painting them on the screen causes too much CPU load.

qt-dab however works quite well. It occupies about 60% CPU, which means you don’t want to be doing much else. Whether I can do AX.25 packet simultaneously as planned or not is a valid question. Audio quality through the PWM output on the Pi3 is good too — I did try this with an original Pi and got an aural assault courtesy of the noisy 3.3V power rail, but it seems this problem is largely fixed on the Pi3.

In truth, I’ll probably be using the GNURadio framework directly when I get to implementing this on the bicycle. That makes a custom tailored UI a little easier to implement.

The WTF moment though was whilst putting this rig through its paces… I noticed a new station:

ELF Radio, a station dedicated to Christmas Carols

A new station, “ELF Radio” had appeared in multiplex 9A (202.928MHz)… this is exactly what it sounds like, a station dedicated to Christmas carols. We’re not even half-way though October, and they’re already out to flog the genre to death.

Now, Christmas rage was not a thing when I was younger, it seems the marketing world is intent on ruining this tradition by making excuses for starting the sales earlier and earlier… and it seems the “ambience” is part of the package deal that they insist must start long before that Celtic tradition, Halloween! As a result, most of us are thoroughly fed up by the time December rolls around.

Here’s a hint advertisers: playing this crap so soon in the year will not result in higher sales. It’s a sales repellent!

Experiments with RFC-7959 over 6LoWPAN

So, in my workplace we’re developing a small energy/water metering device, which runs on a 6LoWPAN network and runs OpenThread-based firmware. The device itself is largely platform-agnostic, requiring a simple CoAP gateway to provide it with a configuration blob and history write end-point. The gateway service I’m running is intended to talk to WideSky.

One thorny issue we need to solve before deploying these things in the wild, is over-the-air updates. So we need a way to transfer the firmware image to the device over the mesh network. Obviously, this firmware image needs to be digitally signed and hashed using a strong cryptographic hash — I’ve taken care of this already. My problem is downloading an image that will be up to 512kB in size.

Thankfully, the IETF has thought of this, the solution to big(gish) files over CoAP is Block-wise transfers (RFC-7959). This specification gives you the ability to break up a large payload into smaller chunks that are powers of two in size between 16 and 2048 bytes.

6LoWPAN itself though has a limitation: the IEEE 802.15.4 radio specification it is built on cannot send frames bigger than 128 bytes. Thus, any message sent via this network must be that size or smaller. IPv6 has a minimum MTU of 1280 bytes, so how do they manage? They fragment the IPv6 datagram into multiple 802.15.4 frames. The end device re-assembles the fragments as it receives them.

The catch is, if a fragment is lost, you lose the entire datagram, there’s no repeats of individual fragments, the entire datagram must be re-sent. The question in my mind was this: Is it faster to rely on block-wise transfers to break the payload up and make lots of small requests, or is it faster to rely on 6LoWPAN fragmentation?

The test network here has a few parts:

  • The target device, which will be downloading a 512kB firmware image to a separate SPI flash chip.
  • The border router, which provides a secure IPv6 tunnel to a cloud host.
  • The cloud server which runs the CoAP service we’ll be talking to.

The latency between the office (in Brisbane) and the cloud server (in Sydney) isn’t bad, about 30~50ms. The CoAP service is built using node-coap and coap-polka.

My CoAP requests have some overheads:

  • The path being downloaded is about 19 bytes long.
  • There’s an authentication token given as a query string, so this adds an additional 12 bytes.

The data link is not 100% reliable, with the device itself dropping some messages. This is leading to some retransmits. The packet loss is not terrible, but probably in the region of around 5%. Over this slightly lossy link, I timed the download of my 512kB firmware image by my device with varying block size settings.

Note that node-coap seems to report a “Bad Option” error for szx=0 and szx=7, even though both are legitimately within specification. (I’d expect node-coap to pass szx=7 through and allow the application to clamp it to 6, but it seems node-coap‘s behaviour is to report “Bad option”, but then pass the payload through anyway.)

Size Exponent (szx)Block sizeStart time (UTC)End time (UTC)Effective data rate
6102403:27:0403:37:52809B/s
551203:41:2503:53:40713B/s
425603:57:1504:16:16458B/s
312804:17:4604:54:17239B/s
26404:56:0905:54:53150B/s
13223:31:3301:39:4468B/s

It remains to be seen how much multiple hops and outdoor atmospherics affect the situation. A factor here is how quickly the device can turn-around between processing a response and sending the next request, which in this case is governed by the speed of the SPI flash and its driver.

Effects on “busy” networks

So, I haven’t actually done any hard measurements here, but doing testing on a busier network with about 30 nodes, the block size equation tips more in favour of a smaller block size.

I’ll try to quantify the delays at some point, but right now 256 byte blocks are the clear winner, with 512 and 1024 byte block transfers proving highly unreliable. The speed advantage between 1k and 512 bytes in ideal conditions was a big over 10%… which really doesn’t count for much. At 256 bytes, the speed difference was about 43%, quite significant. You’re better off using 512-byte blocks if the network is quiet.

On a busy network, with all the retransmissions, smaller is better. No hard numbers yet, but right now at 256 byte blocks, the effective rate is around 118 bytes/sec. I’ll have to analyse the logs a bit to see where 512/1024 byte block sizes sat, but the fact they rarely completed says it all IMO.

Slow and steady beats fast and flakey!

6LoWHAM: Route discovery thoughts

Thinking about the routing problem a little more… if I wanted to do a purely “native” routing scheme not involving Net/ROM routing update broadcasts, one has to wonder what such a system would look like.

Net/ROM L3 is really just intended to “bootstrap” things… there’s the prospect of using Net/ROM L4 for tunnelling TCP traffic, but really it’s the L3 part that interests me as a way of hopping between fragments of the mesh that may be linkable via a non-6LoWHAM capable digipeater.

Net/ROM’s periodic broadcasts are inefficient, divulging a node’s entire routing table is not an ideal situation.  So what’s the alternative?  IPv6 nodes already send a “neighbour discovery” packet when they don’t know the MAC address of a neighbour, this is a trigger for a “neighbour advertisement” response.

I’m thinking 6LoWHAM will send NAs periodically anyway.  ACMA rules require identifying every 10 minutes.  Since the NA will include the call-sign of the station (in bit-shifted ASCII), doing that every 10 minutes takes care of the ACMA requirement.  An IPv6 NA message is not a big payload.

Given this will be sent to the ff02::1 multicast group, all nodes able to hear the beaconing station will receive it.  Unlike a IEEE 802.11 or 802.3 network though, not all nodes on the mesh will hear it.

The same is true of ND messages.  If the neighbour is in ear-shot and able to respond, it likely will, but that isn’t a guarantee.  Something in the link-local scope will likely be the answer, probably a daemon listening on a UDP port and sending to the ff02::1 group.

Unicast routing

When a station wishes to make contact with a station that’s not an immediate neighbour, I’m thinking of a broadcast similar to how APRS does things.  APRS uses special call-signs WIDEn-m, where the hop-limit is encoded in those messages.

A UDP message would be constructed asking “Who can reach X within N hops?” and sent to ff02::1 to some “well-known” port.

The first second is reserved for responses from nodes that know a route, either through Net/ROM, or maybe they’ve been in contact with that station before.  They respond something along the lines of “X via A,B,C, quality Q”, where A, B, C are digipeaters and Q is some link quality value.

Not sure how I’ll derive Q just yet.  Possibly based on packet loss… we’ll think of something.

If no responses are heard, the routers that heard the message re-broadcast it and listen for replies.  In the re-broadcast, each router appends its 48-bit 6LoWHAM address and a link quality to the message payload.  The hop limit would also get decremented.  That way, it can break cycles, and it gives a direct unicast path for the distant node to respond.

The same algorithm applies: wait a second for immediate responses, then any routers downstream append their addresses/link quality values, decrement the hop limit, and re-broadcast.

Again, any node that overhears the message (including the target node), may respond.  It does so via a direct unicast, sent using conventional AX.25 digipeating.  Any router en route that relays the message may also cache the result.  The “mesh” gets to learn of where everyone is as-required rather than by default with Net/ROM.

If the hop limit reaches zero, no further re-broadcasts are made, the message stops there.

When the source node hears the replies, each reply resets a 100msec timer.  100msec after the last reply, it chooses three “best” routes, and sends a ICMPv6 ND message via each one to the target station.  The station replies to all three back via those routes with an ICMPv6 NA.  If a message is lost via one of those routes, that route is demoted in quality.

Once replies have arrived back at the source, it picks the best route based on the updated quality information, and begins communications via that route.

Multicast routing

This, is more tricky.  I think the link-local should mean what it means on Thread… that is ff02::/16 just gets processed by immediate neighbours that are in direct RF range.

Realm-local (RFC-7346), ff03::/16 should be used for stuff that’s mesh-wide.  Those messages may be repeated by routers provided those routers have at least one subscriber for the given multicast group/port listening.

Multicast Listener Discovery looks to be the tool for that, although it could do with some 6LoWPAN-style optimisation.

I’m thinking the first time a router hears a datagram destined for a particular group, it should send a query out asking “who is listening” to the said group.

Following that first message, it should be up to the downstream node to inform the local routers that it intends to receive messages from a given group.  This should be periodic, maybe hourly, so that routers are not re-broadcasting messages for a node that has gone off-air.

Routers that have no listeners for a group, do not rebroadcast that group’s traffic.  Similarly, if the hop limit has been exhausted, the messages do not get rebroadcast.

6LoWHAM: A draft protocol specification

Today, I decided to get cuddly with the relevant RFCs and see if I could adapt them into something that would work for AX.25. The following roughly describes how one might stuff IPv6 datagrams into AX.25.

Much of this is heavily influenced by RFC-4944 and RFC-6282, the latter of which looks to be the heart-and-soul of Thread.


Stateless Automatic Addressing

We have a mechanism by which an AX.25 call+SSID can be losslessly mapped to a 48-bit MAC address. This is built on Radix-50 and can work as a stand-in for the EUI-48. The pseudo EUI-48 procedure mentioned in section 6 of the RFC-4944 standard is not required.

An EUI-64 is generated from an EUI-48 by chopping the EUI-48 in half and inserting the bytes ff:fe in the middle. So the EUI-48:

00:11:22:33:44:55

becomes the following EUI-64:

00:11:22:ff:fe:33:44:55

SLAAC therefore will work the same way it does for Ethernet.

Frame format

1. AX.25 UI Frame header

Size: (17 + (D*7) bytes, where D is the number of digipeaters being used

  • PID = 1100 0101 (tentative) IPv6
  • Control = 0000 0011
    • Frame type: UI, P/F = 0 (final)
  • Must contain source and destination AX.25 callsigns, may contain up to 8 digipeater AX.25 callsigns.

For a direct station-to-station contact:

  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
├───┴───┴───┴───┴───┴───┴───┴───┼───┴───┴───┴───┴───┴───┴───┴───┤
│       AX.25 Flag (0x7e)       │ Destination AX.25 Call+SSID   │
├───────────────────────────────┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├───────────────────────────────────────────────────────────────┤
│ Source AX.5 Call+SSID                                         │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬───────────────────────────────┤
│                               │          AX.25 PID            │
├───────────────────────────────┴───────────────────────────────┤
╎             AX.25 UI frame payload starts here                ╎
└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘

or for contact via a few digipeaters:

  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
├───┴───┴───┴───┴───┴───┴───┴───┼───┴───┴───┴───┴───┴───┴───┴───┤
│       AX.25 Flag (0x7e)       │ Destination AX.25 Call+SSID   │
├───────────────────────────────┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├───────────────────────────────────────────────────────────────┤
│ Source AX.5 Call+SSID                                         │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬───────────────────────────────┤
│                               │    Digipeater 1 Call+SSID     │
├───────────────────────────────┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├───────────────────────────────────────────────────────────────┤
│ Digipeater 2 Call+SSID                                        │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬───────────────────────────────┤
│                               │          AX.25 PID            │
├───────────────────────────────┴───────────────────────────────┤
╎             AX.25 UI frame payload starts here                ╎
└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘

2. Mesh Addressing Header

To be used when two stations are not able to directly communicate, or when multicasting.

In this scenario, the AX.25 frame source and destination indicate the addresses of the directly-communicating nodes (e.g. source and digipeater, intermediate digipeaters, or digipeater and destination), and the fields given here will be the addresses of the source and destination AX.25 stations.

e.g. sending from VK4MSL-0 to VK4MDL-9 via
VK4RZB-0 and VK4RZA-0:

  1. First transmission:
    • AX.25 Src: VK4MSL-0
    • AX.25 Dst: VK4RZB-0
    • Mesh Src: VK4MSL-0
    • Mesh Dst: VK4MDL-9
    • Hops: 7
  2. Intermediate hop:
    • AX.25 Src: VK4RZB-0
    • AX.25 Dst: VK4RZA-0
    • Mesh Src: VK4MSL-0
    • Mesh Dst: VK4MDL-9
    • Hops: 6
  3. Final delivery:
    • AX.25 Src: VK4RZA-0
    • AX.25 Dst: VK4MDL-9
    • Mesh Src: VK4MSL-0
    • Mesh Dst: VK4MDL-9
    • Hops: 5

Unlike 802.15.4, we do not have 16-bit short addresses. Since these bits would otherwise always be set to 0, we will use these to provide a 6-bit “hops left” field. We shall use the value 63 (0x3f) to indicate when there are 63 or more hops remaining.

We will use the raw 48-bit addresses here. In keeping with amateur radio conventions, the source and destinations are flipped compared to RFC-4944.

Header format (13 bytes):

  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
├───┴───┼───┴───┴───┴───┴───┴───┼───┴───┴───┴───┴───┴───┴───┴───┤
│ 1   0 │       Hops Left       │      Destination Address      │
├───────┴───────────────────────┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬───────────────────────────────┤
│                               │         Source Address        │
├───────────────────────────────┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
│                                                               │
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┬───────────────────────────────┤
│                               │    Remaining AX.25 Payload    │
├───────────────────────────────┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
╎                                                               ╎
└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘

4. Fragmentation header

To be used when a IPv6 datagram is greater than L bytes, where L may be defined to be between 64 and 216 bytes.

This part is identical to that of RFC-4944 (section 5.3). I’ll come back to this bit.

5. IPv6 datagram

This can be encoded in a number of ways depending on requirements:

5.1. Raw IPv6 datagram

  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
├───┴───┼───┴───┴───┴───┴───┴───┼───┴───┴───┴───┴───┴───┴───┴───┤
│ 0   1 │       6LP_IPV6        │                               │
├───────┴───────────────────────┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
╎                  Raw IPv6 datagram with payload.              ╎
└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘

6LP_IPV6 is the value 0x01, as per RFC-4944. The IPv6 datagram is encoded as per RFC-2460, and includes its payload.

The AX.25 frame is finished off with the frame-check sequence.

5.2. Compressed IPv6 datagram

In this format, the datagram fields are compressed, either through making static assumptions, or by deriving them from things such as the AX.25 header, or a previously agreed-to context.

The first field in such payloads is the 6LP_IPHC field:

  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
├───┴───┴───┼───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┤
│ 0   1   1 │               6LP_IPV6 with CID=1                 │
├───────────┴───────────────────┬───────────────────────────────┤
│        Context ID Byte        │                               │
├───────────────────────────────┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
╎             Compressed IPv6 datagram with payload.            ╎
└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘

or without the context ID

  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
├───┴───┴───┼───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┤
│ 0   1   1 │               6LP_IPV6 with CID=0                 │
├───────────┴───────────────────────────────────────────────────┤
╎             Compressed IPv6 datagram with payload.            ╎
└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘

The 6LP_IPHC field is a 13-bit field, optionally followed by a context ID extension byte. The bit allocations are as follows:

  0   1   2   3   4   5   6   7   8   9  10  11  12
├───┴───┼───┼───┴───┼───┼───┼───┴───┴───┼───┼───┼───┤
│  TF   │ NH│ HLIM  │CID│SAC│  SAM      │ M │DAC│DAM│
└───────┴───┴───────┴───┴───┴───────────┴───┴───┴───┘
  • (MSB) 0-1: TF Traffic Class, Flow Label. See 5.2.1 below.
  • 2: NH Next Header encoding
    • =0: Given explicitly
    • =1: Encoded using 6LP_NHC
  • 3-4: HLIM Hop Limit
    • =00: Given explicitly
    • =01: is set to 1
    • =10: is set to 64
    • =11: is set to 255
  • 5: CID Context Identifier Extension
    • =0: No CID byte follows
    • =1: A CID byte follows
  • 6-8: SAC Source Address Compression / SAM Mode
    • =000: No compression applied, whole address given
    • =001: Prefix is link-local prefix, remaining bits are given.
    • =x10: Not used in 6LoWHAM (we don’t support 16-bit addresses)
    • =011: Prefix is link-local, figure the rest out from the source address in the AX.25 header.
    • =100: Unspecified address ::
    • =101: See the context for the prefix, remaining bits are given.
    • =111: Figure out the address from the AX.25 header and context.
  • (LSB) 9-12: M Multicast, DAC Destination Address Compression
    DAM Mode

    • =0000: No compression, not multicast, whole address given
    • =0001: Prefix is link-local prefix, remaining bits are given. Not multicast.
    • =xx10: Not used in 6LoWHAM (we don’t support 16-bit addresses)
    • =0011: Prefix is link-local, figure the rest out from the destination address in the AX.25 header. Not multicast.
    • =0100: Reserved
    • =0101: See the context for the prefix, remaining bits are given. Not multicast.
    • =0111: Figure out the address from the AX.25 header and context. Not multicast.
    • =1000: No compression, multicast address, whole address given
    • =1001: 48-bits of multicast address given, fill in the blanks: ff__::00__:____:____.
    • =1010: 32-bits of multicast address given, fill in the blanks: ff__::00__:____.
    • =1011: 8-bits of multicast address given, fill in the blanks: ff02::00__.
    • =1100: 48-bits RFC-3306/RFC-3956 address, ff__:__LL:PPPP:PPPP:PPPP:PPPP:____:____ where P and L come from the context.
    • =1101: Reserved
    • =1110: Reserved
    • =1111: Reserved

The context ID extension byte has the following format:

  0   1   2   3   4   5   6   7
├───┴───┴───┴───┼───┴───┴───┴───┤
│      SCI      │      DCI      │
└───────────────┴───────────────┘
  • (MSB) 0-3: Source Context Identifier
  • (LSB) 4-7: Destination Context Identifier

These two sub-fields indicate which specific context is being used to fill in the blanks.

5.2.1: Traffic Class and Flow Label

These may be partially or completely omitted depending on the TF setting in the previous field.

  • TF=00:
      0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
    ├───┴───┼───┴───┴───┴───┴───┴───┼───┴───┴───┴───┼───┴───┴───┴───┤
    │  ECN  │         DCSP          │ 0   0   0   0 │               │
    ├───────┴───────────────────────┴───────────────┴ ─ ─ ─ ─ ─ ─ ─ ┤
    │                          Flow Label                           │
    └───────────────────────────────────────────────────────────────┘
    
  • TF=01:
      0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
    ├───┴───┼───┴───┼───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┤
    │  ECN  │ 0   0 │                                               │
    ├───────┴───────┴ ─ ─ ─ ─ ─ ─ ─ ┬───────────────────────────────┤
    │           Flow Label          │                               │
    ├───────────────────────────────┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
    ╎                   Remainder of IPv6 datagram.                 ╎
    └╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘
    
  • TF=10:
      0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
    ├───┴───┼───┴───┴───┴───┴───┴───┼───┴───┴───┴───┴───┴───┴───┴───┤
    │  ECN  │         DCSP          │                               │
    ├───────┴───────────────────────┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
    ╎                   Remainder of IPv6 datagram.                 ╎
    └╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘
    
  • TF=11: Flow label, ECN and DCSP are set to 0.

5.2.2: Next Header

If 6LP_NHC is not explicitly enabled, the next header byte will appear next.

5.2.3: Hop Limit.

Again, if not explicitly defined in the 6LP_IPHC header, the hop-limit byte will appear next.

5.2.4: Source address

The format here is determined by the values of SAC/SAM:

  • 000: Entire IPv6 address, 16 bytes given here.
  • x01: Last 8-bytes of the address given here
  • For all other values, the source address is omitted.

5.2.5: Destination address

The format here is determined by the values of M/DAC/DAM:

  • x000: Entire IPv6 address, 16 bytes given here.
  • 0x01: Last 8-bytes of the address given here.
  • 1001: 6-bytes of address given here, fill-in-the-blanks.
  • 1010: 4-bytes of address given here, fill-in-the-blanks.
  • 1011: Last byte of address given here, fill-in-the-blank.
  • 1100: 6-bytes of address given here, fill-in-the-blanks.
  • For all other values, the destination address is omitted.

6. 6LoWPAN Next Header

This is used to encode selected IPv6 extensions or L4 protocol headers.

6.1. IPv6 extension headers

A select number of IPv6 extensions may be encoded by replacing the usual “Next Header” byte with the following:

  0   1   2   3   4   5   6   7
├───┴───┴───┴───┼───┴───┴───┼───┤
│ 1   1   1   0 │    EID    │ N │
└───────────────┴───────────┴───┘

where EID (bits 4-6) is one of:

  • =0 IPv6 Hop-By-Hop options
  • =1 IPv6 Routing
  • =2 IPv6 Fragment
  • =3 IPv6 Destination Options
  • =4 IPv6 Mobility
  • =7 IPv6 Header

and N (bit 7) indicates whether the header’s payload is followed by another 6LowPAN Next Header, or a regular IPv6 Next Header (with its “Next Header” byte). For EID=7, N MUST be 0.

Length fields within the header payload should be counted in bytes instead of 8-byte blocks.

7. Datagram payload

7.1. Non-UDP payloads

For payloads other than UDP packets, these should be inserted into the AX.25 payload as-is following the extensions.

UDP packets with uncompressed headers should also be inserted
in this manner.

7.2. UDP payloads with header compression

For these payloads, the following UDP header should be used:

  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
├───┴───┴───┴───┴───┼───┼───┴───┼───┴───┴───┴───┴───┴───┴───┴───┤
│ 1   1   1   1   0 │ C │   P   │           Source Port         │
├───────────────────┴───┴───────┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
╎                                                               ╎
├───────────────────────────────────────────────────────────────┤
╎                         Destination Port                      ╎
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
╎                                                               ╎
├───────────────────────────────────────────────────────────────┤
│                     Checksum (unless C=1)                     │
└───────────────────────────────────────────────────────────────┘
  • (MSB): bits 0-4: Compressed UDP header marker. Literal 11110₂
  • Bit 5: C Compressed UDP checksum
    • 0= UDP checksum is given (recommended value)
    • 1= UDP checksum is omitted
  • Bits 6-7: P Ports
    • 00=Both source and destination addresses are
      given in full

        0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
      ├───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┤
      │                         Source Port                           │
      ├───────────────────────────────────────────────────────────────┤
      │                      Destination Port                         │
      └───────────────────────────────────────────────────────────────┘
      
    • 01=Source port is given in full, Least significant 8-bits of destination given, destination port is 0xff00-0xffff (65280-65535)
        0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
      ├───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┤
      │                         Source Port                           │
      ├───────────────────────────────┬───────────────────────────────┤
      │       Destination Port        │                               │
      ├───────────────────────────────┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
      ╎                    Remainder of UDP packet                    ╎
      └╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘
      
    • 10=Destination port is given in full, Least significant 8-bits of source given, source port is 0xff00-0xffff (65280-65535)
        0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
      ├───┴───┴───┴───┴───┴───┴───┴───┼───┴───┴───┴───┴───┴───┴───┴───┤
      │          Source Port          │        Destination Port       │
      ├───────────────────────────────┼───────────────────────────────┤
      │    Destination Port (cont.)   │                               │
      ├───────────────────────────────┴ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
      ╎                    Remainder of UDP packet                    ╎
      └╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘
      
    • 11=Only least significant 4-bits of source and destination ports are given. Port LSB range is 0xf0b0-0xf0bf (61616-61631)
        0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
      ├───┴───┴───┴───┴───┴───┴───┴───┼───┴───┴───┴───┴───┴───┴───┴───┤
      │         Source Port           │       Destination Port        │
      └───────────────────────────────┴───────────────────────────────┘
      

The C bit should only be set if the upper-level application asks for it. Whilst 802.15.4 does its own CRC as does AX.25, the field is mandatory in UDP and the recommendation is to only drop it if the application says it’s okay.

6LoWHAM: Routing

I’ve been doing more pondering on the routing side of things.  The initial thought was to use Net/ROM L3 to figure out the source route of who can hear whom.  Getting access to that via BPQ’s interfaces may not be easy unless we happen to eavesdrop on the broadcasts, and of course, there’s no service discovery in BPQ.

The thought came to me, what does ICMPv6 offer me in terms of routing?  If I can ask “who has a route to node X?” or announce “I know how to reach X”… I could just skip Net/ROM L3 altogether, since there’s a good chance both will co-exist quite happily on the same AX.25 network, we just take a ship-pass-in-the-night approach.

ICMPv6 Router Advertisements basically just say “I’m a router and this is my local prefix”, similarly, ICMPv6 Router Discovery messages just ask “Who’s a router?” … not greatly helpful.

RFC-4191 gets close, specifically the Route Information Option, but this is again, targeted at reaching a node on a differing subnet to the local one, and only applies to RAs.

This is a service that 802.15.4 actually provides to 6LoWPAN (RFC-4944), so from the point of view of IPv6, a 802.15.4 network translated through 6LoWPAN “looks” like one L2 network, it just needs to know a node’s extended address which is what made me think about Net/ROM L3 in the first place.

RFC-6775 looks to enhance things a little bit, by considering the fact that it’s not just one big happy family however, not everyone can talk to each-other, and links come and go.

One thing is clear, not everybody will be a router.  Specifically, a node should definitely not advertise itself as a router unless it can hear at least two other nodes, or knows routes to nodes learned either through static configuration or via Net/ROM node advertisements.

Two nodes can just exclusively use link-layer communications, so there’s no need for either to be “routers”.  Soon as a third joins in, potentially all three could be routers if they can all hear each-other, but if you have a linear topology where only the central node can hear the other two, it is logical that that node becomes a router, and not the others.

The question is then, if one of those peripheral nodes disappears, what should the router do?  I’m thinking it should remain a router for a limited period of time (configurable, but maybe measured in hours), just in case that node returns or other nodes appear.  After some time, it may “demote” itself to non-routing node status and relinquish control of the on-mesh prefix.

Where a node promotes itself to router, if an existing on-mesh prefix is in use, it should continue to use that, otherwise it should derive a suitable ULA prefix for use.

It may also follow that a ULA is configured for certain nodes, and they are configured to remain in the router role, regardless of the number of neighbours.  Repeater sites would be prime candidates for this.  They’re in a position where they should have good coverage, and thus should be prime candidates to be routers.

Since we can do multi-hop source routing with AX.25, there’s scope to perhaps exploit this in a higher level protocol which we might build on UDP messaging, as it doesn’t look like the existing standards provide for this sort of route path all that well.  TCP/IP is after all destination routed whilst AX.25 is source routed.

I think maybe tomorrow (which is predicted to be wet), it’ll be a good day to sit down and prototype something that maybe takes care of the IP messaging side of things and at least gets two AX.25 stations exchanging messages, then we can start to build something atop that.

6LoWHAM: ARQ over a “mesh” network

So earlier, I had mentioned that it’s really not desirable to have ARQ (automatic repeat request) on a link carrying TCP datagrams.  My comment is based on this observation:

http://sites.inka.de/bigred/devel/tcp-tcp.html

In that article, the discussion is about one TCP connection being tunnelled over another TCP connection.  Basically it comes down to the lower layer buffering and re-sending the TCP datagrams just as the upper layer gives up on hearing a reply and re-sends its own attempt.

Now, end-to-end ACKs have been done on long chains of AX.25 networks before.  It’s generally accepted to be an unreliable mechanism.  UDP for sure can benefit, but then many protocols that use UDP already do their own handling of lost messages.  CoAP for instance does its own ARQ, as does TFTP.

Gerald Wagenknecht, Markus Anwander and Torsten Braun discuss some of the impacts of this on a 802.15.4 network in their thesis “Hop-to-Hop Reliability in IP-based Wireless Sensor Networks – a Cross-Layer Approach“.  In this, they talk about a variant of TCP called TSS: TCP Support for Sensor Networks.  This was discussed at depth in a thesis by Adam Dunkels, “Towards TCP/IP for Wireless Sensor Networks“.

This latter document, was apparently the inspiration for 6LoWPAN.  Section 4.4.3 discusses the approaches to handling ARQ in TCP.  Section 9.6 goes into further detail on how ARQ might be handled elsewhere in the network.

Thankfully in our case, it’s only the network that’s constrained, the nodes themselves will be no smaller than a Raspberry Pi which would have held its own against the PC that Adam Dunkels used to write that thesis!

In short, it looks as if just routing IP packets is not going to cut it, we need to actually handle the TCP side of things as well.  As for other protocols like CoAP, I guess the answer is be patient.  The timeout settings defined in RFC-7252 are usually tuneable, and it may be desirable to back those off just a little for use over AX.25.

6LoWHAM Research

So, doing some more digging here.  One question people might ask is what kind of applications would I use over this network?

Bear in mind that it’s running at 1200 baud!  If we use HTTP at all, tiny is the word!  No bloated images, and definitely no big heavy JavaScript frameworks like ReactJS, Angular, DoJo or JQuery.  You can forget watching Netflicks in 4k over this link.

HTTP really isn’t designed for low-bandwidth links, as Steve Netting demonstrated:

The page itself is bad enough, but even then, it’s loaded after a minute.  The real slow bit is the 20kB GIF.

So yeah, slow-scan television, the ability to send weather radar images over, that is something I was thinking of, but not like that!

HTTP uses pretty verbose headers:

GET /qld/forecasts/brisbane.shtml?ref=hdr HTTP/1.1
Host: www.bom.gov.au
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-AU,en-GB;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://www.bom.gov.au/products/IDR664.loop.shtml
Cookie: bom_meteye_windspeed_units_knots=yes
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Pragma: no-cache
Cache-Control: no-cache

HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Encoding: gzip
Content-Type: text/html; charset=UTF-8
Server: Apache
Vary: Accept-Encoding
Content-Length: 6321
Date: Sat, 20 Oct 2018 10:56:12 GMT
Connection: keep-alive

That request is 508 bytes and the response headers are 216 bytes.  It’d be inappropriate on 6LoWPAN as you’d be fragmenting that packet left right and centre in order to squeeze it into the 128-byte 802.15.4 frames.

In that video, ICMP echo requests were also demonstrated, and those weren’t bad!  Yes, a little slow, but workable.  So to me, it’s not the packet network that’s the problem, it’s just that something big like HTTP is just not appropriate for a 1200-baud radio link.

It might work on 9600 baud packet … maybe.  My Kantronics KPC3 doesn’t do 9600 baud over the air.

CoAP was designed for tight messages.  It is UDP based, so your TCP connection overhead disappears, and the “options” are encoded as individual bytes in many cases.  There are other UDP-based protocols that would work fine too, as well as older TCP protocols such as Telnet.

A request, and reply in CoAP look something like this:

Hex dump of request:
00000000  40 01 00 01 3b 65 78 61  6d 70 6c 65 2e 63 6f 6d   @...;exa mple.com
00000010  81 63 03 52 46 77 11 3c                            .c.RFw.< 

Hex dump of response:
    00000000  60 45 00 01 c1 3c ff a1  1a 00 01 11 70 a1 01 a3   `E...<.. ....p...
    00000010  04 18 64 02 6b 31 39 32  2e 31 36 38 2e 30 2e 31   ..d.k192 .168.0.1
    00000020  03 64 65 74 68 30                                  .deth0

Or in more human readable form:

Request:
Constrained Application Protocol, Confirmable, GET, MID:1
    01.. .... = Version: 1
    ..00 .... = Type: Confirmable (0)
    .... 0000 = Token Length: 0
    Code: GET (1)
    Message ID: 1
    Opt Name: #1: Uri-Host: example.com
        Opt Desc: Type 3, Critical, Unsafe
        0011 .... = Opt Delta: 3
        .... 1011 = Opt Length: 11
        Uri-Host: example.com
    Opt Name: #2: Uri-Path: c
        Opt Desc: Type 11, Critical, Unsafe
        1000 .... = Opt Delta: 8
        .... 0001 = Opt Length: 1
        Uri-Path: c
    Opt Name: #3: Uri-Path: RFw
        Opt Desc: Type 11, Critical, Unsafe
        0000 .... = Opt Delta: 0
        .... 0011 = Opt Length: 3
        Uri-Path: RFw
    Opt Name: #4: Content-Format: application/cbor
        Opt Desc: Type 12, Elective, Safe
        0001 .... = Opt Delta: 1
        .... 0001 = Opt Length: 1
        Content-type: application/cbor
    [Uri-Path: coap://example.com/c/RFw]

Response:
Constrained Application Protocol, Acknowledgement, 2.05 Content, MID:1
    01.. .... = Version: 1
    ..10 .... = Type: Acknowledgement (2)
    .... 0000 = Token Length: 0
    Code: 2.05 Content (69)
    Message ID: 1
    Opt Name: #1: Content-Format: application/cbor
        Opt Desc: Type 12, Elective, Safe
        1100 .... = Opt Delta: 12
        .... 0001 = Opt Length: 1
        Content-type: application/cbor
    End of options marker: 255
    Payload: Payload Content-Format: application/cbor, Length: 31
        Payload Desc: application/cbor
        [Payload Length: 31]
Concise Binary Object Representation
    Map: (1 entries)
        Unsigned Integer: 70000
            Map: (1 entries)
                ...0 0001 = Unsigned Integer: 1
                    Map: (3 entries)
                        ...0 0100 = Unsigned Integer: 4
                            Unsigned Integer: 100
                        ...0 0010 = Unsigned Integer: 2
                            Text String: 192.168.0.1
                        ...0 0011 = Unsigned Integer: 3
                            Text String: eth0

That there, also shows another tool to data packing: CBOR.  CBOR is basically binary JSON.  Just like JSON it is schemaless, it has objects, arrays, strings, booleans, nulls and numbers (CBOR differentiates between integers of various sizes and floats).  Unlike JSON, it is tight.  The CBOR blob in this response would look like this as JSON (in the most compact representation possible):

{70000:{4:100,2:"192.168.0.1",3:"eth0"}}

The entire exchange is 190 bytes, less than a quarter of the size of just the HTTP request alone.  I think that would work just fine over 1200 baud packet.  As a bonus, you can also multicast, try doing that with HTTP.

So you’d be writing higher-level services that would use this instead of JSON-REST interfaces.  There’s a growing number of libraries that can consume this sort of thing, and IoT is pushing that further.  I think it’s doable.

Now, on the routing front, I’ve been digging up a bit on Net/ROM.  Net/ROM is actually two parts, Net/ROM Level 3 does the routing and level 4 does the circuit switching.  It’s the “Level 3” bit we want.

Coming up with a definitive specification of the protocol has been a bit tough, it doesn’t help that there is a company called NetROM, but I did manage to find this document.  In a way, if I could make my software behave like a Net/ROM node, I could piggy-back off that to discover neighbours.  Thus this protocol would co-exist along side Net/ROM networks that may be completely oblivious to TCP/IP.

This is preferable to just re-inventing the wheel…yes I know non-circular wheels are so much fun!  Really, once Net/ROM L3 has figured out where everyone is, IP routing just becomes a matter of correctly addressing the AX.25 frame so the next hop receives the message.

VK4RZB at Mt. Coot-tha is one such node running TheNet.  Easy enough to do tests on as it’s a mere stone throw away from my home QTH.

There’s a little consideration to make about how to label the AX.25 frame.  Obviously, it’ll be a UI frame, but what PID field should I use?  My instinct suggests that I should just label it as “ARPA Internet Protocol”, since it is Internet Protocol traffic, just IPv6 instead of v4.  Not all the codes are taken though, 0xc9 is free, so I could be cheeky and use that instead.  If the idea takes off, we can talk with the TAPR then.

Mapping call-signs to “hardware” addresses

This is another brain dump of ideas.

So, part of me wants to consider the idea of using amateur radio as a transmission mechanism for 6LoWPAN.  The idea being that we use NET/ROM and AX.25 or similar schemes as a transport mechanism for delivering shortened IPv6 packets.  Over this, we can use standard TCP/IP programming to write applications.

Protocols designed for low-bandwidth constrained networks are ideal here, so things like CoAP where emphasis is placed on compact representation.  6LoWPAN normally runs over IEEE 802.15.4 which has a payload limit of 128 bytes.  AX.25 has a limit of 256 bytes, so is already doing better.

The thinking is that I “encode” the call-sign into a “hardware” address.  MAC addresses are nominally 48-bits, although the IEEE is trying to phase that out in favour of 64-bit EUIs.  Officially the IEEE looks after this, so we want to avoid doing things that might clash with their system.

A EUI-48 (MAC) address is 6-bytes long, where the first 3 bytes identify the type of address and the organisation, and the latter 3 bytes identify an individual device.  The least significant two bits of the first byte are flags that decide whether the address is unicast or local, and whether it is globally administered (by the IEEE) or locally administered.

To avoid complications, we should probably keep the unicast bit cleared to indicate that these addresses are unicast addresses.

Some might argue that the ITU assigns prefixes to countries, and these countries have national bodies that hand out callsigns, thus we could consider callsigns as “globally administered”.  Truth is, the IEEE has nothing to do with the process, and could very legitimately assign the EUI-48 prefix 56-4b-34 to a company… in that hypothetical scenario, there goes all the addresses that might represent amateur operators stationed in Queensland.  So let’s call these “locally administered”, since there are suffixes the user may choose (e.g. “/P”).

That gives us 46-bits to play with.  7-bit ASCII just fits 6 characters, which would just fit the callsigns used in AX.25 with enough room for a 4-bit SSID.  We don’t need all 128 characters though, and a scheme based on DEC’s Radix50 can pack in far more.

We can get 8 arbitrary Radix50 characters into 43 bits, which gives us 3 left over which can be used as the user wishes.  We’ll probably call it the SSID, but unlike AX.25, will be limited from 0-7.  The user can always use the least significant character in their callsign field for an additional 6 bits, which gives them 9 bits to play with.  (i.e. “VK4MSL-1″#0 to encode the AX.25 SSID “VK4MSL-10”)

Flip the multicast bit, and we’ve got a group address.

SLAAC derives the IPv6 address from the EUI-48, so the IPv6 address will effectively encode the callsigns of the two communicating stations.  If both are on the same “mesh”, then we can probably borrow ideas from 6LoWPAN for shortening that address.

6LoWHAM Background

So, I’ll admit to looking at AX.25 with the typical modems available (the classical 1200-baud AFSK and the more modern G3RUH modem which runs at a blistering 9600 baud… look out 5G!) years ago and wondering “what’s the point”?

It was Brisbane Area WICEN’s involvement in the International Rally of Queensland that changed my view somewhat.  This was an event that, until CAMS knocked it on the head, ran annually in the Imbil State Forest up in the Sunshine Coast hinterland.

There, WICEN used it for forwarding the scores of drivers as they passed through each stage of the rally.  A checkpoint would be at the start and finish of each stage, and a packet network would be set up with digipeaters in strategic locations and a base station, often located at the Imbil school.

The organisers of IRoQ did experiment with other ways of getting scores through, including hiring bandwidth on satellites, flying planes around in circles over the area, and other shenanigans.  Although these systems had faster throughput speeds, one thing they had which we did not have, was latency.  The score would arrive back at base long before the car had left the check point.

This freed up the analogue FM network for reporting other more serious matters.

In addition to this kind of work, WICEN also help out with horse endurance rides.  Traditionally we’ve just relied on good ol’e analogue FM radio, but in events such as the Tom Quilty, there has been a desire to use packet as a mechanism for reporting when horses arrive at given checkpoints and to perhaps enable autonomous stations that can detect horses via RFID and report those “back to base” to deter riders from cheating.

The challenge of AX.25 is two-fold:

  1. With the exception of Linux, no other OS has any kind of baked-in support for it, so writing applications that can interact with it means either implementing your own AX.25 stack or interfacing to some third-party stack such as BPQ.
  2. Due to the specialised stack, applications often have to run as privileged applications, can have problems with firewalling, etc.

The AX.25 protocol does do static routing.  It offers connected-mode links (like TCP) and a connectionless-mode (like UDP), and there are at least two routing protocols I know of that allow for dynamic routing (ROSE, Net/ROM).  There is a standard for doing IPv4 over AX.25, but you still need to manage the allocation of addresses and other details, it isn’t plug-and-play.

Net/ROM would make an ideal way to forward 6LoWPAN traffic, except it only does connected mode, and doing IP over a “TCP-like” link is really a bad idea.  (Anything that does automatic repeat requests really messes with TCP/IP.)

I have no idea whether ROSE does the connectionless mode, but the idea of needing to come up with a 10-digit numeric “address” is a real turn-off.

If the address used can be derived off the call-sign of the operator, that makes life a lot easier.

The IPv6 address format has enough bits to do that.  To me the most obvious way would be to derive a MAC address from a call-sign and an arbitrarily chosen digit (0-7).  It would be reversible of course, and since the MAC address is used in SLAAC, you would see the station’s call-sign in the IPv6 address.

The thinking is that there’s a lot of problems that have been solved in 6LoWPAN.  Discovery of services for example is handled using mechanisms like mDNS and CoRE RD.  We don’t need to forward Internet traffic, although being able to pull up the Mt. Kanigan and Mt. Stapylton radars over such a network would be real handy at times (yes, I know it’ll be slow).

The OS will view the packet network like a VPN, and so writing applications that can talk over packet will be no different to writing any other kind of network software.  Any consumer desktop OS written in the last 16 years has the necessary infrastructure to support it (even Windows 2000, there was a downloadable add-on for it).

Linking two separate “mesh” networks via point-to-point links is also trivial.  Each mesh will of course see the other as “external” but participants on both can nonetheless communicate.

The guts of 6LoWPAN is in RFC-4944.  This specifies details about how the IPv6 datagram is encoded as a IEEE 802.15.4 payload, and how the infrastructure within 802.15.4 is used to route IPv6.  Gnarly details like how fragmentation of a 1280-byte IPv6 datagram into something that will fit the 128-byte maximum 802.15.4 frames is handled here.  For what it’s worth, AX.25 allows 255 bytes (or was it 256?), so we’re ahead there.

Crucially, it is assumed that the 802.15.4 layer can figure out how to get from node A to node Z via B… C…, etc.  802.15.4 networks are managed by a PAN coordinator, which provides various services to the network.

AX.25 makes this “our problem”.  Yes the sender of a frame can direct which digipeaters a frame should be passed to, but they have to figure that out.  It’s like sending an email by UUCP, you need a map of the Internet to figure out what someone’s address is relative to your site.

Plain AX.25 digipeaters will of course be part of the mix, so having the ability for a node stuck on one side of such a digipeater would be worth having, but ultimately, the aim here will be to provide a route discovery mechanism in place that, knowing a few static digipeater routes, can figure out who is able to hear whom, and route traffic accordingly.