7  Non-acoustic links

The networks we explored in the last few chapters were completely underwater. All links were underwater acoustic links. If we wanted to replace some of the acoustic links with underwater optical or RF links, or even through-the-air cellular, WiFi or RF links, that could easily be done, as long as you had a modem driver (a specific type of agent) that supported the device that provided the link. Cellular, WiFi and other devices often already have TCP/IP network stacks running on them, to provide seamless connectivity to the Internet. UnetStack can leverage the existing network stack in these devices without having to develop new modem drivers, by translating Unet datagrams to UDP/IP datagrams, tunneling them through the IP network, and translating them back to Unet datagrams at the other end.

7.1 UDP link

The UdpLink agent offers the LINK service over an IP network.

To see how this works, let us revisit the MISSION 2013 network from Chapter 6. Recall that node 21 was a gateway node with surface expression, and was connected to the Internet via a 3G cellular IP connection. During the experiment, we had no direct acoustic connectivity between nodes 21 and 31, and hence we routed all communication to node 31 via node 28.

Let us consider a scenario where node 31 also has a surface expression and 3G cellular IP connectivity. In this case, it would be nice to have a direct link from node 21 to node 31 via UDP/IP. Let’s see how to set that up.

Fire up the mission2013-network.groovy network simulation (or if you already have it running from the last chapter, terminate and restart it, so that we have no routes in our routing tables). Connect to node 21’s shell and add the UdpLink agent:

> container.add 'udplink', new UdpLink();
> udplink
« UDP/IP Link »

Link protocol over UDP/IP for use over wired/wireless IP networks.

[org.arl.unet.DatagramParam]
  MTU ⤇ 2147483647
  RTU ⤇ 1450

[org.arl.unet.link.LinkParam]
  dataRate = 1.0E8

[org.arl.unet.link.UdpLinkParam]
  advertise = 30
  broadcastAddress = 192.168.1.255
  delayAfter = 8
  delayBy = 0
  monitorTimeout = 200
  port = 5100
  retries = 2
  timeout = 0.5

Then set up a route to node 31 via the UDP link, and check the routing table:

> addroute 31, 31, udplink
OK
> routes
    uuid      to nextHop         link reliability hops dataRate metric enabled  auto  poll
------------------------------------------------------------------------------------------
  imtf08      31      31      udplink        true    1 100000000    8.0    true  true   0.0
TipUDP link parameters

The following parameters can be used to configure UDP links:

  • port – UDP port number
  • broadcastAddress – broadcast IP address
  • monitorTimeout – UDP monitor socket timeout (ms)
  • timeout – reliable delivery ACK timeout (seconds)
  • retries – maximum retries for reliable delivery
  • advertise – time delay between advertisements (seconds)
  • delayAfter – number of UDP transmissions before a short delay
  • delayBy – short delay (in ms) between groups of UDP transmissions

The default values for most of the parameters are typically suitable for most links. The delayAfter and delayBy parameters provide a way to throttle UDP traffic, as some networks drop UDP packets if they arrive too quickly.

Similarly, connect to node 31’s shell and add the UdpLink agent as well as a route to node 21 via the UDP link:

> container.add 'udplink', new UdpLink();
> addroute 21, 21, udplink
OK
> routes
    uuid      to nextHop         link reliability hops dataRate metric enabled  auto  poll
------------------------------------------------------------------------------------------
  m5p6i5      21      21      udplink        true    1 100000000    8.0    true  true   0.0

Go back to node 21’s shell and see if you can ping node 31 via the UDP link:

> ping 31
PING 31
Response from 31: seq=1 time=0 ms
1 packets transmitted, 1 packets received, 0% packet loss

Then send it a text message:

> tell 31, 'hello!'
OK

and on node 31, you’ll see:

[21]: hello!

You’ll also notice that the communication is much faster, since the UDP/IP latency is low and data rate is much higher.

CautionManually mapping a peer’s address & port

UdpLink normally discovers its peers automatically using UDP broadcast packets. This works out-of-the-box on most networks, but broadcast discovery fails in some environments — VPNs and firewalled networks often block broadcast, and multiple simulated nodes sharing a single host’s loopback interface cannot discover each other either (which is exactly the situation in the simulation used throughout this section). In these cases you must manually register each peer’s IP address and UDP port.

To register node 31 as a peer on node 21, we first need node 31’s IP address and the UDP port its udplink is listening on. The port is available as the port parameter on node 31:

> udplink.port
59904

Then, on node 21, we set the peer’s address and port using the indexed udplink[address] parameters. Because both simulated nodes run on this host, we use the loopback address 127.0.0.1; on a real deployment you would use node 31’s actual IP address:

> udplink[31].address = '127.0.0.1';
> udplink[31].port = 59904;

The reverse mapping (registering node 21’s address and port on node 31) is set up the same way. Once both peers are registered, datagrams flow over the UDP link exactly as if they had been discovered automatically.

7.4 Automatic route failover

For a clear demonstration, we want two routes to the same destination over two different links. Node 28 has good acoustic connectivity to node 21, so let us pretend node 28 also acquired a surface expression and IP connectivity: add a UdpLink agent on node 28 (and register the peer addresses manually, as before, if broadcast discovery is unavailable). On node 21, we then set up two routes to node 28 — one over the UDP link, and one over the direct acoustic link:

> addroute to: 28, nextHop: 28, link: udplink, reliability: true, poll: 30
OK
> addroute 28, 28, uwlink
OK
> routes
    uuid      to nextHop         link reliability hops dataRate metric enabled  auto  poll
-------------------------------------------------------------------------------------------
   8bemz      28      28      udplink        true    1 100000000    8.0    true  true  30.0
  41hmia      28      28       uwlink        true    1       965    3.0    true  true   0.0

The poll: 30 on the UDP route will matter later, when the link recovers. Both routes are enabled, and the UDP route has the better metric, so it carries the traffic. A reliable datagram to node 28 confirms this — the delivery notification arrives almost instantly:

> router << new DatagramReq(to: 28, data: [1,2,3], reliability: true)
AGREE
router >> DatagramDeliveryNtf:INFORM[id:019f3688-cc12-7143-a428-b4b25f01a9d9]

Now let us break the UDP link. On node 28, kill the udplink agent (container.kill udplink) — as far as node 21 is concerned, the IP connection has silently dropped; nothing tells it that the route is now useless. Send another reliable datagram from node 21:

> router << new DatagramReq(to: 28, data: [1,2,3], reliability: true)
AGREE
router >> DatagramDeliveryNtf:INFORM[id:019f3688-d0bb-7b9e-8763-59dbb93071ec]

The datagram was still delivered — but this time the notification took a few seconds to arrive. Behind the scenes, quite a lot happened. The router first forwarded the datagram over the UDP route, as usual. The reliable transfer on udplink timed out and failed, and the link reported a DatagramFailureNtf back to the router. Rather than passing the failure on to us, the router looked for the next best route to node 28 excluding the failed link, found the acoustic route, and quietly retransmitted the datagram over uwlink — the delivery notification we see is from the acoustic delivery. (The router retries this way for up to router.retryTimeout seconds, 30 by default, after the original request.)

The failed transfer had a second consequence: udplink declared the link down by publishing a LinkStatusNtf, and since our routes were added with auto: true (the default for addroute), the router responded by disabling the UDP route:

> routes
    uuid      to nextHop         link reliability hops dataRate metric enabled  auto  poll
-------------------------------------------------------------------------------------------
   8bemz      28      28      udplink        true    1 100000000    8.0   false  true  30.0
  41hmia      28      28       uwlink        true    1       965    3.0    true  true   0.0

From here on, datagrams to node 28 go straight over the acoustic route, with no failed UDP attempt first — the disabled route is simply skipped.

The story completes when the UDP link comes back (restart the udplink agent on node 28). This is where the poll setting earns its keep: every 30 seconds, the router probes the next hop of the disabled route with a small reliable datagram over its link. As soon as one of these probes succeeds, the link reports itself up (LinkStatusNtf again), and the router re-enables the route:

> routes
    uuid      to nextHop         link reliability hops dataRate metric enabled  auto  poll
-------------------------------------------------------------------------------------------
   8bemz      28      28      udplink        true    1 100000000    8.0    true  true  30.0
  41hmia      28      28       uwlink        true    1       965    3.0    true  true   0.0

Traffic switches back to the UDP route, and the acoustic route returns to being a standby. Links that actively announce themselves (such as UdpLink peers discovered through broadcast advertisements) may raise the link-up notification on their own, without polling; the poll option covers links that stay silent until spoken to.

NoteFailover and reliability

The router honors end-to-end reliability only on single-hop routes, so its failure-triggered retry can only switch between single-hop alternatives, as in this example. On multi-hop routes, a reliable DatagramReq is refused — per-hop link reliability still protects each hop (set the route’s reliability flag), but end-to-end delivery guarantees across multiple hops are the job of the transport layer (Chapter 23).