IRIDIUM

IRIDIUM is a low-level networking protocol that sits underneath both MERCURY and WOLFRAM.

It provides both connectionless and virtual-circuit communication of arbitrarily sized messages between nodes, offering flow control, retransmission, bandwidth reservation (where available), and optional sequencing of messages within a virtual circuit. It also provides for the recipient of a message to reply, with the reply and the acknowledgement of receipt of the original request being carried together, and the reply being automatically tied to the request.

I plan to implement it on top of UDP for Internet use, and on top of raw byte streams for use over serial and parallel links (with full bandwidth reservation capability) before long to provide real time networking.

It is a design requirement that small requests should be tranmissable in a single UDP datagram, with no requirement to negotiate any kind of connection with the server, and that small responses should likewise fit in a single UDP datagram. This is to minimise the latency for various common message transactions.

Consider a DNS server; a single UDP datagram containing a request comes in, and the response goes out as a single UDP datagram. There is no need to establish any connection state with the DNS server beforehand. Therefore, DNS servers manage to serve a vast number of clients with very little resource usage, and very low latency.

When a virtual circuit is established over UDP, a unique source port is allocated for that VC. This is to allow bandwidth reservation for that VC using RSVP, if available.

When a virtual circuit is established over UDPv6, as well as the unique source port, the IPv6 flow field can be used to enable bandwidth reservation with RSVPv6.

The basic operations made available by IRIDIUM, specified in terms of a Java-like garbage collected host language (although not assuming any object orientation), are:

IridiumHost resolveUDPv4Host( byte[4] address, unsigned short port, unsigned int estimatedOutboundBandwidth, unsigned int estimatedInboundBandwidth, unsigned int estimatedMTU, unsigned float estimatedDatagramLoss, unsigned float estimatedDelay)
IridiumHost resolveUDPv6Host( byte[16] address, unsigned short port, unsigned int estimatedOutboundBandwidth, unsigned int estimatedInboundBandwidth, unsigned int estimatedMTU, unsigned float estimatedDatagramLoss, unsigned float estimatedDelay)
IridiumHost resolveStreamHost( platform dependent identifier of a serial or parallel stream device, eg a UNIX fd, unsigned int outboundBandwidth, unsigned int inboundBandwidth, unsigned int MTU, float estimatedDatagramLoss, float estimatedDelay)

Returns a Host object for the given remote node. If there is already a Host object for that node, then it should return the same Host object rather than creating a new one. The reason for this is that a Host object contains counters used for flow control to the remote host across all virtual circuits and connectionless traffic. Different UDP destination port numbers return different IridiumHost objects, but sharing the same underlying flow control counters.

No actual communication is performed at this stage.

For stream connections, the bandwidth of the stream and the MTU must be specified. For all protocols, existing estimates of network capability can be provided as starting points for the flow control and path MTU discovery algorithms - but all zeroes can be given if no existing information is known.

void sendMessage ( IridiumHost destination, byte[] message, AsynchRequestCallback callback, boolean idempotent, byte dropPriority, byte deliveryPriority)
void sendMessage ( IridiumVC destination, byte[] message, AsynchRequestCallback callback, boolean idempotent, byte dropPriority, byte deliveryPriority)

Send a message to a host (in connectionless mode) or down a virtual circuit.

If the callback is null, then the message is delivered synchronously; the call only returns when the message has been processed, or known to fail. If a callback is specified, then the call returns immediately, and the callback is invoked when the message is successfully delivered, or delivery fails.

For a message with a drop priority above zero, even in synchronous mode the request can return immediately because IRIDIUM is not guaranteeing delivery anyway.

The idempotent flag, if set, means that IRIDIUM need not try to avoid the message arriving more than once.

The dropPriority is a QoS parameter; if it is 0, then the message must be delivered, and the IRIDIUM layer will ask the remote end to return an acknowledgement. If the ack is not received in reasonable time, then it will be retransmitted until there is reasonable certainty the remote host is not reachable, at which point it will throw an exception. If it is not zero, then it lets the underlying protocol know that in the case of congestion causing router buffers to overflow, the message can be dropped; messages with a higher drop priority should be dropped in preference to those with a lower priority.

The deliveryPriority is another QoS parameter; if a link in the underlying physical network is congested, then messages with a higher deliveryPriority should be sent down the link first.

When implemented on top of a datagram protocol like UDP, then if the message is small enough, it may be sent as a single datagram. Otherwise, it is instead sent with a protocol styled upon NetBLT, where a single datagram is sent indicating the size of the message (along with a message ID) followed by a stream of data packets, each tagged with their offset into the message. In this case, the protocol inherently guarantees delivery, even if not requested to do so by the application.

If the transmission is going to take some time, then the asynch request callback may be called from time to time to be told transmission is in progress, and given both a percentage progress and an estimated time to completion (based upon the bandwidth estimates kept by the flow control system).

byte[] sendRequest ( IridiumHost destination, byte[] message, AsynchRequestCallback callback, boolean idempotent, byte deliveryPriority)
byte[] sendRequest ( IridiumVC destination, byte[] message, AsynchRequestCallback callback, boolean idempotent, byte deliveryPriority)

Send a message to a host in connectionless mode, or down a virtual circuit, then get a response back.

The sending of the original message is done exactly as per sendMessage, except that the far end must send back a response. If the code at the far end that generates the response completes quickly enough, then the response message counts as the acknowledgement of receipt of the request; if it appears to be taking longer, then an acknowledgement datagram may be necessary to prevent the client from retransmitting the request.

The response is then sent back in much the same manner as any other message, except marked as a response, and specifying the message ID it is in response to.

If an aysnchronous request callback is specified, then the request returns null immediately, and later invokes the callback with the return value (or an exception in the case of a timeout or other communication problem).

There are eight possiblities; each of the request and the response may either be small enough to fit in a single datagram, or not. And the response may be ready in time to piggyback the acknowledgement of receipt, or not. Each combination offers different possibilities for piggybacking acknowledgements on requests, or acknowledgements not being needed (in the case of the large-message transfer protocol being required on the request).

If either transmission of the request or reception of the response is going to take some time, the asynch request callback (if specified) may be invoked with the percentage completion of either phase, and an esimated time to completion of that phase.

unsigned int getOutboundBandwidthEstimate (IridiumHost destination)
unsigned int getInboundBandwidthEstimate (IridiumHost destination)
unsigned float getDelayEstimate (IridiumHost destination)
unsigned float getDatagramLossEstimate (IridiumHost destination)
unsigned boolean supportsBandwidthReservation (IridiumHost destination)
unsigned long getTotalBytesSentTo (IridiumHost destination)
unsigned long getTotalBytesReceivedFrom (IridiumHost destination)

Retrieve approximate details about the network between here and the host.

The delay is the time taken (in seconds) for a zero-byte message to get to the remote host; not a round trip time.

The bandwidth estimate is the number of bytes that can be transferred in a second.

The datagram loss estimate is the approximate fraction of datagrams that get lost in transit.

supportsBandwidthReservation indicates whether there appears to be support for reserved bandwidth in virtual circuits to the remote host. Bear in mind that this may change unpredictably; a current value is no guarantee of future values.

These statistics may take some communication with the far end to actually settle to useful values.

IridiumVC openVC ( IridiumHost destination, byte[] message, AsynchRequestCallback callback, byte deliveryPriority, boolean orderMessages, unsigned int outboundBandwidthRequired, unsigned int inboundBandwidthRequired, IridiumVCHandler handler)

Attempts to open a virtual circuit.

The attempt carries an application message with it, which can contain things like login details that the server may use to decide whether to accept or reject the connection. Unlike TCP, where one must perform a three way handshake before being able to authenticate, making denial of server attacks easier.

All of the parameters are as per the sendMessage and sendRequest procedures, except for orderMessages and bandwidthRequired.

If orderMessages is set, then messages sent on the resulting VC will arrive in the same order they were sent; if the far end receives a message without having received the message before it, it will buffer that message and not pass it to the server application until the missing message has been delivered.

If bandwidthRequired is more than zero, then an attempt will be made to reserve bandwidth. If bandwidth reservation is not possible due to lack of support in the protocol beneath or there is insufficient bandwidth available, then an appropriate exception will be thrown (or passed to the AsynchRequestCallback).

The recipient may refuse the request, in which case an exception is thrown by openConnection, or passed back via the callback.

If the connection is established, then the IridiumVCHandler instance is notified of any incoming messages or requests or connection closures.

closeVC (IridiumVC connection)

Closes a virtual circuit.

IridiumListener listenUDPv4 ( unsigned short port, IridiumHandler handler)
IridiumListener listenUDPv6 ( unsigned short port, IridiumHandler handler)
IridiumListener listenStream ( platform dependent identifier of a serial or parallel stream device, eg a UNIX fd, unsigned int outboundBandwidth, unsigned int inboundBandwidth, unsigned int MTU, IridiumHandler handler)

Sets IRIDIUM listening on a given UDP port or stream device.

The IridiumHandler object has callbacks to handle incoming messages, requests, and VC open requests.

stopListening (IridiumListener listener)
Closes down a listener.

Discussion

Note that the system maintains a table of hosts, for flow control and liveness checking purposes. As opposed to TCP, which does this seperately for each connection. This allows a lot of savings in the protocol's use of the underlying network when there is a lot of communication to the same host, at little more cost than a TCP connection when there is not.

Also, this means that if a client calls sendRequest, which causes a thread to spawn on the server handling the request, death of the client can be spotted by the server, and cause it to throw an exception in all threads currently handling requests from that client host.

To Do

I need to define the above API to provide multicast and anycast communication models, to take advantage of these services when provided by underlying protocols like IPv6 and IPv4 with MBONE...