rlp_bsd.c

Implementation of ACN route layer protocol on BSD sockets API including Operating System and Stack dependent networking functions.

Summary
rlp_bsd.cImplementation of ACN route layer protocol on BSD sockets API including Operating System and Stack dependent networking functions.
Network StrategiesNotes on use of interfaces, ports and multicast addressing (group addressing).
ReceivingTo send or receive from RLP you need at least one RLP socket.
Constants
Integer constants used for pass-by-reference values
Variables
rlpsocks [static]A list of current RLP sockets.
Transmit buffer managementThis has been simplified by observing that there are few if any realistic use-cases for packing multiple PDUs at the root layer We therefore do not support this on send (though we still correctly receive them).
Functions
rlp_initInitialize RLP and networking.
Sending
netx_send_toSend UDP message to given address.
rlp_sendbufFinalizes a buffer and transmit it
rlpSubscribeReturns an rlpsocket_s suitable for sending and registers a callback function for receiving packets.
rlp_packetRx
netxGetMyAddrReturn the IP address for a RLP socket.

Network Strategies

Notes on use of interfaces, ports and multicast addressing (group addressing).

This explanation is relevant to configuration options CF_LOCALIP_ANY and RECEIVE_DEST_ADDRESS, The following applies to UDP on IPv4.  UDP on IPv6 will be similar, but transports other than UDP may be very different.

IP addresses and interfaces

A socket represents an endpoint and we bind it to an “interface” and port.  The interface is represented by an IP address, but this can be misleading because of the way multicast addressing works.

When a packet is received (incoming packet), the stack identifies the “interface” (and therefore the socket to receive the packet), by the incoming destination address in the packet.  However, when this address is a multicast address, there is no interface information there.  Therefore, simply binding a socket to a specific unicast IP address is not helpful.

The exception is the case where there are multiple physical interfaces on separate networks which may be within different multicast zones.  In this case, the stack uses rules based on its routing tables.

For this reason, it is usually best to open any socket intended to receive multicast using IN_ADDR_ANY as the interface and rely on the stack to worry about interfaces.  So while this API allows for sockets to be bound to specific interfaces (IP addresses), there is no guarantee that doing so will prevent multicast traffic from other networks arriving at that socket.

Ports

A socket (in this code) is always bound to a specific port; if bind is called with netx_PORT_EPHEM then the stack will pick an “ephemeral” port which is different from all others currently in use.  Because of the issues with multicasting above, and because there is no way to coordinate the multiple receivers of a multicast group to use a particular ephemeral port, the same port and therefore the same socket, is used for all multicast reception (note a multicast packet can be transmitted from any socket because it is only the destination address which can be multicast).  For example in SDT, all multicast traffic uses the SDT_MULTICAST_PORT as the estination port.

Socket allocation for multicast reception

Because all multicast traffic takes place on the same port, it could theoretically use a single socket.  However, the Linux stack like many others restricts the number of multicast subscriptions that can be applied to a single socket.  This code opens dummy sockets as necessary to increase the number of multicast subscriptions.  As subscriptions are handled at host level and not at the individual socket level, this has the same effect as subscribing the original socket to the group.

Multicast group address in incoming packets

To allow RLP to distinguish traffic for different multicast groups Each time a higher layer registers a new listener corresponding to a specific multicast address, we know that that listener is only concerned with the traffic within that particular group.  If the stack can provide the incoming multicast address (the destination address from an incoming packet we can filter) in the rlp layer and only send that packet to the appropriate listener.  Otherwise all listeners receive all multicast packets, irrespective of their group address.  They can eliminate all the unwanted packets eventually but may need to process a considerable amount of the packet in doing so.

The code here, does not filter incoming packets by destination address, but if it can extract that information (and RECEIVE_DEST_ADDRESS is true) then we do so and pass the group address on to the higher layer incoming packet handler.  NOTE: We only need to pass the group address, not a full address and port structure, because the port (and interface if desired subject to the limitations above) are determined by the socket.

Summary
ReceivingTo send or receive from RLP you need at least one RLP socket.
Constants
Integer constants used for pass-by-reference values
Variables
rlpsocks [static]A list of current RLP sockets.
Transmit buffer managementThis has been simplified by observing that there are few if any realistic use-cases for packing multiple PDUs at the root layer We therefore do not support this on send (though we still correctly receive them).
Functions
rlp_initInitialize RLP and networking.
Sending
netx_send_toSend UDP message to given address.
rlp_sendbufFinalizes a buffer and transmit it
rlpSubscribeReturns an rlpsocket_s suitable for sending and registers a callback function for receiving packets.
rlp_packetRx
netxGetMyAddrReturn the IP address for a RLP socket.

Receiving

To send or receive from RLP you need at least one RLP socket.

Each separate incoming address and port, whether unicast or multicast requires a separate call to rlpSubscribe().  Note though that the handle returned may well not be unique, depending on lower layer implementation details.  In general, for a given port, there may be just one rlpsocket, or one for each separate multicast address, or some intermediate number.  For a shared rlpsocket, if callback and ref do not match values from previous calls for the protocol, the call will fail.  So you should assume one callback and reference for each port used.

At present unicast packets are received via any interface and multicast via the default chosen by the stack, so any unicast address supplied is treated as INADDR_ANY (or its equivalent for other protocols).

If lclad is NULL, the system will assign a new unicast rlpsocket bound to an ephemeral port.

If lclad is supplied with a port value of netx_PORT_EPHEM, then a new ephemeral port is assigned and the actual value is overwritten in lclad.

When using rlpsockets for sending, they determine the source address and port used in the packet.  Because the source must be unicast, even in implementations where multiple rlpsockets are returned for differing multicast subscriptions, any of thes may be used for sending with the same effect.  However, all unsubscribe calls must use the same rlpsocket that was returned by subscribe.

When a packet is received at an rlpsocket, the given callback function is invoked with the contents of the RLP PDU block.  The reference pointer passed to rlpSubscribe() is returned in the handlerRef field of the rcxt structure which is valid for all netx_s and rlp_s fields:

struct rxcontext_s {
 struct netx_context_s {
     netx_addr_t        source;
     struct rxbuf_s     *rxbuf;
 } netx;
 struct rlp_context_s {
     struct rlpsocket_s *rlsk;
     const uint8_t      *srcCID;
     void               *handlerRef;
 } rlp;
 ...
};

Constants

Integer constants used for pass-by-reference values

int_zerointeger with value 0
int_oneinteger with value 1

Variables

rlpsocks [static]

A list of current RLP sockets.

Transmit buffer management

This has been simplified by observing that there are few if any realistic use-cases for packing multiple PDUs at the root layer We therefore do not support this on send (though we still correctly receive them).

Functions

rlp_init

int rlp_init(void)

Initialize RLP and networking.  Need only be called once - subsequent calls do nothing (but do no harm).

Sending

netx_send_to

static int netx_send_to(
   nativesocket_t sk,
   /* contains a flag if port is open and the local port number */ const netx_addr_t *destaddr,
   /* contains dest port and ip numbers */ void *pkt,
   /* pointer data packet if type UPDPacket (return from netx_new_txbuf()) */ ptrdiff_t datalen /* length of data */
)

Send UDP message to given address.  The call returns the number of characters sent, or negitive if an error occurred.

rlp_sendbuf

int rlp_sendbuf(uint8_t *txbuf,
int length,
#if CF_RLP_MAX_CLIENT_PROTOCOLS > 1 protocolID_t protocol,
#endif struct rlpsocket_s *src,
netx_addr_t *dest,
uint8_t *srccid)

Finalizes a buffer and transmit it

txbufa buffer with exactly RLP_OFS_PDU1DATA octets of data unused at the beginning for RLP to put its headers.
lengththe total length of the buffer including these octets.
protocolthe protocol ID of the outermost protocol in the buffer (SDT, E1.31 etc.).  If acn is built with CF_RLP_CLIENTPROTO set, then this argument must be omitted and the configured single client protocol is used.
srcthe RLP socket to send the data from (which determines the source address).
destthe destination address and srccid is the component ID of the sender.

rlpSubscribe

struct rlpsocket_s * rlpSubscribe(netx_addr_t *lclad,
protocolID_t protocol,
rlpcallback_fn *callback,
void *ref)

Returns an rlpsocket_s suitable for sending and registers a callback function for receiving packets.

lclad is the Local address to use (the structure may be used to send to any remote address). lclad may be a multicast address, in which case this address is added to the subscribed multicast addresses.

The rlpsocket_s returned may not be unique and in the case of multicast addresses will probably not be.  The local address should be relinquished by calling rlpunsubscribe when it is no longer needed.

rlp_packetRx

void rlp_packetRx(const uint8_t *buf,
ptrdiff_t length,
struct rxcontext_s *rcxt)

netxGetMyAddr

int netxGetMyAddr(struct rlpsocket_s *cxp,
netx_addr_t *addr)

Return the IP address for a RLP socket.

This is not a useful function for looking up our own IP address as in many cases it will simply return INADDR_ANY.

int rlp_init(void)
Initialize RLP and networking.
static int netx_send_to(
   nativesocket_t sk,
   /* contains a flag if port is open and the local port number */ const netx_addr_t *destaddr,
   /* contains dest port and ip numbers */ void *pkt,
   /* pointer data packet if type UPDPacket (return from netx_new_txbuf()) */ ptrdiff_t datalen /* length of data */
)
Send UDP message to given address.
int rlp_sendbuf(uint8_t *txbuf,
int length,
#if CF_RLP_MAX_CLIENT_PROTOCOLS > 1 protocolID_t protocol,
#endif struct rlpsocket_s *src,
netx_addr_t *dest,
uint8_t *srccid)
Finalizes a buffer and transmit it
struct rlpsocket_s * rlpSubscribe(netx_addr_t *lclad,
protocolID_t protocol,
rlpcallback_fn *callback,
void *ref)
Returns an rlpsocket_s suitable for sending and registers a callback function for receiving packets.
void rlp_packetRx(const uint8_t *buf,
ptrdiff_t length,
struct rxcontext_s *rcxt)
int netxGetMyAddr(struct rlpsocket_s *cxp,
netx_addr_t *addr)
Return the IP address for a RLP socket.
Close