> 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.57 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:
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.0The following parameters can be used to configure UDP links:
port– UDP port numberbroadcastAddress– broadcast IP addressmonitorTimeout– UDP monitor socket timeout (ms)timeout– reliable delivery ACK timeout (seconds)retries– maximum retries for reliable deliveryadvertise– time delay between advertisements (seconds)delayAfter– number of UDP transmissions before a short delaydelayBy– 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.0Go 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 lossThen send it a text message:
> tell 31, 'hello!'
OKand 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.
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
59904Then, 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.2 Other links
UnetStack has support for many types of links. At the time of writing, the following non-acoustic links are supported:
- Optical underwater link using Hydromea LUMA optical modem.
- Satellite link via an Iridium 9603 satellite communications module.
- RF link via a SATEL-TR4+ data transceiver module.
The list is rapidly growing as more users leverage UnetStack for connectivity in air.
7.3 Multilink routing
When we added the UdpLink agent in the last section, we set up static routes manually on both nodes. Let’s delete these routes on both nodes (node 21 and node 31):
> delroutes
OKNow let’s see what the route discovery agent does when we ask it to discover routes for us:
Node 21:
> rreq 31
OKWait for a while for routes to be discovered, and then check them:
> routes
uuid to nextHop link reliability hops dataRate metric enabled auto poll
-------------------------------------------------------------------------------------------
2r5bgm 31 31 udplink true 1 100000000 8.0 true true 0.0
neswmw 29 29 uwlink true 1 965 3.0 true true 0.0
ay1il0 28 28 uwlink true 1 965 3.0 true true 0.0
mjhfaw 31 28 uwlink true 2 965 -7.0 true true 0.0
r1yhgk 22 22 uwlink true 1 965 3.0 true true 0.0
ii73zj 31 22 uwlink true 3 965 -12.0 true true 0.0Note that the route discovery resulted in 3 routes to node 31 in this case. The first one is a single-hop UDP (udplink) route. The second one is an acoustic route (using uwlink) via node 28, and the third one is a 3-hop acoustic route via node 22. We can see that the metric for the 2-hop and 3-hop acoustic routes is lower than that of the UDP route, and so the UDP route is used for data transfer. The metric is computed based on a combination of number of hops and the packet loss on a route.
You can check the routing table on node 31:
> routes
uuid to nextHop link reliability hops dataRate metric enabled auto poll
-------------------------------------------------------------------------------------------
yj0iu1 21 21 udplink true 1 100000000 8.0 true true 0.0
7ocgus 29 29 uwlink true 1 965 3.0 true true 0.0
o1thfi 21 29 uwlink true 2 965 -7.0 true true 0.0We can check which route is used for data transfer. From node 21:
> ping 31
PING 31
Response from 31: seq=1 time=500 ms
1 packets transmitted, 1 packets received, 0% packet loss
> trace 31
[21, 31, 21]If a link goes down, the failure may be detected and the route disabled. In that case, UnetStack automatically falls back to the next best route as per the metric. Let us watch that happen.
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.0The 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.0From 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.0Traffic 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.
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).