Detailed Description
This user guide details how to use the UDP Transformation API.
Introduction
The UDP transform feature enables custom transformation of incoming and outgoing UDP payloads based on transformation rules between a pair of source and destination IP addresses. Some examples of transformations are encrypted data or logging.
This user-guide explains how to implement and use transformations in an application and is organized as follows:
Overview
The UDP transformation feature enables custom transformation of incoming and outgoing UDP payloads. For the purpose of this document a UDP payload is defined as a sequence of octets sent or received as a single UDP datagram excluding UDP headers, typically UDP port numbers, and trailers, such as the optional used checksum.
An outgoing payload is the UDP payload passed to the network stack. The transformation feature allows a custom transformation of this payload just before it is sent. The UDP transport receives payloads to send from an upstream layer. In RTI Connext DDS Micro this layer is typically RTPS which creates payloads containing one or more RTPS messages. The transformation feature enables transformation of the entire RTPS payload before it is passed to the network stack.
The same RTPS payload may be sent to one or more locators. A locator identifies a destination address, such as an IPv4 address, a port, such as a UDP port, and a transport kind. The address and port is used by the UDP transport to reach a destination. However, only the destination address is used to determine which transformation to apply.
An incoming payload is the UDP payload received from the network stack. The transformation feature enables transformation of the UDP payload received from the network stack before it is passed to the upstream interface, typically RTPS. The UDP transport only receives payloads destined for one of its network interface addresses, but may receive UDP payloads destined for many different ports. The transformation does not take a port into account, only the source address. In RTI Connext DDS Micro the payload is typically a RTPS payload containing one or more RTPS messages.
UDP transformations are registered with RTI Connext DDS Micro and used by the UDP transport to determine how to transform payloads based on a source or destination address. Please refer to Creating a Transformation Library for details on how to implement transformations and Creating Transformation Rules for how to add rules.
Transformations are local resources. There is no exchange between different UDP transports regarding what a transformation does to a payload. This is considered a-priori knowledge and depends on the implementation of the transformation. Any negotiation of e.g. keys must be handled before the UDP transport is registered. Thus, if a sender and receiver does not apply consistent rules they may not be able to communicate or incorrect data may be the result. Note that while information is typically in the direction from a DDS_DataWriter to a DDS_DataReader, a reliable DDS_DataReader also send protocol data to a DDS_DataWriter. These messages are also transformed.
Network Interface Selection
When a DDS_DomainParticipant is created it first creates an instance of each transport configured in the DDS_DomainParticipantQos::transports Qos policy. Thus, each UDP transport registered with RTI Connext DDS Micro must have a unique name (up to 7 characters). Each registered transport can be configured to use all or some of the available interfaces using the allow_interface and deny_interface properties. The registered transports may now be used for either discovery data (specified in DDS_DomainParticipantQos::discovery), user_traffic (specified in DDS_DomainParticipantQos::user_traffic) or both. The DDS_DomainParticipant also queries the transport for which addresses it is capable of sending to.
When a participant creates multiple instances of the UDP transport it is important instances uses non-overlapping networking interface resources.
Data Reception
Which transport to use for discovery data is determined by the DDS_DomainParticipantQos::discovery Qos policy. For each transport listed the DDS_DomainParticipant reserves a network address to listen to. This network address is sent as part of the discovery data and is used by other DDS_DomainParticipant as the address to send discovery data for this DDS_DomainParticipant. Because a UDP transformation only looks at source and destination addresses, if different transformations are needed for discovery and user-data different UDP transport registrations must be used and hence different network interfaces.
Data Transmission
Which address to send data to is based on the locators received as part of discovery and the peer list.
Received locators are analyzed and a transport locally registered with a DDS_DomainParticipant is selected based on the locator kind, address and mask. The first matching transport is selected. If a matching transport is not found the locator is discarded. NOTE: A transport is not a matching criteria at the same level as a Qos policy. If a discovered entity requests user data on a transport that doesn’t exist it is not unmatched.
The peer list as specified by the application is a list of locators to send participant discovery announcements to. If the transport to use is not specified, e.g. “udp1@192.168.1.1”, but instead “192.168.1.1” then all transports that understand this address will send to it. Thus, in the case the latter is used and two different UDP transports are registered they will both send to the same address. However, one transport may send transformed data and the other not depending on the destination address.
Creating a Transformation Library
The transformation library is responsible for creating and performing transformations. Note that a library is a logical concept and does not refer to an actual library in for example UNIX. A library in this context is a collection of routines that together creates, manages and performs transformations. How these routines are compiled and linked with an application using RTI Connext DDS Micro is out of scope of this user guide.
The transformation library must be registered with RTI Connext DDS Micro's run-time and must implement the required interfaces. This ensures proper life-cycle management of transformation resources as well as clear guidelines regarding concurrency and memory management.
From RTI Connext DDS Micro ’s run-time point of view the transformation library must implement methods so that:
- A library can be initialized.
- A library can be instantiated.
- An instance of the library performs and manages transformations.
The first two tasks are handled by RTI Connext DDS Micro’s run-time factory interface which is common for all libraries managed by RTI Connext DDS Micro. The 3rd task is handled by the transformation interface which is specific to UDP transformations.
The following describes the relationship between the different interfaces:
- A library is initialized once when it is registered with RTI Connext DDS Micro.
- A library is finalized once when it is unregistered from RTI Connext DDS Micro.
- Multiple library instances can be created. If a library is used twice, for example registered with two different transports, two different library contexts are created using the factory interface. RTI Connext DDS Micro assumes that concurrent access to two different instances is allowed.
- Different instances of the library can be deleted independently. An instance is deleted using the factory interface.
- A library instance creates specific source or destination transformations. Each transformation is expected to transform a payload to exactly one destination or from one source.
The following relationship is true between the UDP transport and a UDP transformation library:
- Each registered UDP transport may make use of one or more UDP transformation libraries.
- A DDS DomainParticipant creates one instance of each registered UDP transport.
- Each instance of the UDP transport creates one instance of each enabled transformation library registered with the UDP transport.
- Each Transformation rule created by the UDP transport creates one send or one receive transformation.
Creating Transformation Rules
Transformation rules decide how a payload should be transformed based on either a source or destination address. Before a UDP transport is registered it must be configured with the transformation libraries to use as well as which library to use for each source and destination address. For each UDP payload sent or received an instance of the UDP transport searches for a matching source or destination rule to determine which transformation to apply.
The transformation rules are added to the UDP_InterfaceFactoryProperty before registration takes place.
If no transformation rules have been configured all payloads are treated as regular UDP packets.
If no send rules have been asserted the payload is sent as is. If all outgoing messages are to be transformed a single entry is sufficient (address = 0, mask = 0).
If no receive rules have been asserted it is passed upstream as is. If all incoming messages are to be transformed a single entry is sufficient (address = 0, mask = 0).
If no matching rule is found the packet is dropped and an error is logged.
NOTE: UDP_InterfaceFactoryProperty is immutable after the UDP transport has been registered.
Interoperability
When the UDP transformations has enabled at least one transformation it will only inter-operate with another UDP transport which also has at least one transformation.
UDP transformations does not interoperate with RTI Connext Pro.
Error Handling
The transformation rules are applied on a local basis and correctness is based on configuration. It is not possible to detect that a peer participant is configured for different behavior and errors cannot be detected by the UDP transport itself. However, the transformation interface can return errors which are logged.
Example Code
Example Header file MyUdpTransform.h
#ifndef MyUdpTransform_h
#define MyUdpTransform_h
#include "rti_me_c.h"
#include "netio/netio_udp.h"
struct MyUdpTransformFactoryProperty
{
struct RT_ComponentFactoryProperty _parent;
};
extern struct RT_ComponentFactoryI*
MyUdpTransformFactory_get_interface(void);
extern RTI_BOOL
const char *const name,
struct MyUdpTransformFactoryProperty *property);
extern RTI_BOOL
const char *const name,
struct MyUdpTransformFactoryProperty **);
#endif
Example Source file MyUdpTransform.c
#include <stdio.h>
#include "MyUdpTransform.h"
{
struct RT_ComponentFactory
_parent;
struct MyUdpTransformFactoryProperty *
property;
};
{
};
static struct RT_ComponentFactoryI MyUdpTransformFactory_fv_Intf;
{
if (t == NULL)
{
return NULL;
}
RT_Component_initialize(&t->
_parent._parent,
&MyUdpTransform_fv_Intf._parent,
0,
(property ? &property->_parent : NULL),
NULL);
{
}
{
t = NULL;
}
return t;
}
{
}
void **const context,
const struct NETIO_Address *const destination,
const struct NETIO_Netmask *const netmask,
void *user_data,
RTI_INT32 *const ec)
{
UNUSED_ARG(self);
UNUSED_ARG(destination);
UNUSED_ARG(user_data);
UNUSED_ARG(property);
UNUSED_ARG(ec);
UNUSED_ARG(netmask);
*context = (void*)user_data;
return RTI_TRUE;
}
void *context,
const struct NETIO_Address *const destination,
const struct NETIO_Netmask *const netmask,
RTI_INT32 *const ec)
{
UNUSED_ARG(udptf);
UNUSED_ARG(context);
UNUSED_ARG(destination);
UNUSED_ARG(ec);
UNUSED_ARG(netmask);
return RTI_TRUE;
}
void **const context,
const struct NETIO_Address *const source,
const struct NETIO_Netmask *const netmask,
void *user_data,
RTI_INT32 *const ec)
{
UNUSED_ARG(self);
UNUSED_ARG(source);
UNUSED_ARG(user_data);
UNUSED_ARG(property);
UNUSED_ARG(ec);
UNUSED_ARG(netmask);
*context = (void*)user_data;
return RTI_TRUE;
}
void *context,
const struct NETIO_Address *const source,
const struct NETIO_Netmask *const netmask,
RTI_INT32 *const ec)
{
UNUSED_ARG(udptf);
UNUSED_ARG(context);
UNUSED_ARG(source);
UNUSED_ARG(ec);
UNUSED_ARG(netmask);
return RTI_TRUE;
}
void *context,
const struct NETIO_Address *const source,
const NETIO_Packet_T *const in_packet,
NETIO_Packet_T **out_packet,
RTI_INT32 *const ec)
{
char *buf_ptr,*buf_end;
char *from_buf_ptr,*from_buf_end;
UNUSED_ARG(context);
UNUSED_ARG(source);
*ec = 0;
if (!NETIO_Packet_initialize_from(
&self->packet,in_packet,
self->buffer,self->max_buffer_length,
0,NETIO_Packet_get_payload_length(in_packet)))
{
return RTI_FALSE;
}
*out_packet = &self->packet;
buf_ptr = NETIO_Packet_get_head(&self->packet);
buf_end = NETIO_Packet_get_tail(&self->packet);
from_buf_ptr = NETIO_Packet_get_head(in_packet);
from_buf_end = NETIO_Packet_get_tail(in_packet);
while (from_buf_ptr < from_buf_end)
{
if (context == (void*)1)
{
*buf_ptr = ~(*from_buf_ptr);
}
else if (context == (void*)2)
{
*buf_ptr = (*from_buf_ptr)+1;
}
++buf_ptr;
++from_buf_ptr;
}
return RTI_TRUE;
}
void *context,
const struct NETIO_Address *const destination,
const NETIO_Packet_T *const in_packet,
NETIO_Packet_T **packet_out,
RTI_INT32 *const ec)
{
char *buf_ptr,*buf_end;
char *from_buf_ptr,*from_buf_end;
UNUSED_ARG(context);
UNUSED_ARG(destination);
*ec = 0;
if (!NETIO_Packet_initialize_from(
&self->packet,in_packet,
self->buffer,8192,
0,NETIO_Packet_get_payload_length(in_packet)))
{
return RTI_FALSE;
}
*out_packet = &self->packet;
buf_ptr = NETIO_Packet_get_head(&self->packet);
buf_end = NETIO_Packet_get_tail(&self->packet);
from_buf_ptr = NETIO_Packet_get_head(in_packet);
from_buf_end = NETIO_Packet_get_tail(in_packet);
while (from_buf_ptr < from_buf_end)
{
if (context == (void*)1)
{
*buf_ptr = ~(*from_buf_ptr);
}
else if (context == (void*)2)
{
*buf_ptr = (*from_buf_ptr)-1;
}
++buf_ptr;
++from_buf_ptr;
}
return RTI_TRUE;
}
{
RT_COMPONENTI_BASE,
};
struct RT_ComponentProperty *property,
struct RT_ComponentListener *listener)
{
UNUSED_ARG(listener);
}
struct RT_ComponentFactory *factory,
RT_Component_T *component)
{
UNUSED_ARG(factory);
}
MUST_CHECK_RETURN
RTI_PRIVATE struct RT_ComponentFactory*
struct RT_ComponentFactoryListener *listener)
{
UNUSED_ARG(property);
UNUSED_ARG(listener);
fac->
_parent.intf = &MyUdpTransformFactory_fv_Intf;
fac->
property = (
struct MyUdpTransformFactoryProperty*)property;
}
struct RT_ComponentFactoryProperty **property,
struct RT_ComponentFactoryListener **listener)
{
UNUSED_ARG(property);
UNUSED_ARG(listener);
if (listener != NULL)
{
*listener = NULL;
}
if (property != NULL)
{
*
property = (
struct RT_ComponentFactoryProperty*)fac->
property;
}
return;
}
RTI_PRIVATE struct RT_ComponentFactoryI MyUdpTransformFactory_fv_Intf =
{
UDP_INTERFACE_INTERFACE_ID,
NULL
};
struct RT_ComponentFactoryI*
MyUdpTransformFactory_get_interface(void)
{
return &MyUdpTransformFactory_fv_Intf;
}
RTI_BOOL
const char *const name,
struct MyUdpTransformFactoryProperty *property)
{
MyUdpTransformFactory_get_interface(),
&property->_parent, NULL);
}
RTI_BOOL
const char *const name,
struct MyUdpTransformFactoryProperty **property)
{
(struct RT_ComponentFactoryProperty**)property,
NULL);
}
Example configuration of rules
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
void
MyAppApplication_help(char *appname)
{
printf("%s [options]\n", appname);
printf("options:\n");
printf("-h - This text\n");
printf("-domain <id> - DomainId (default: 0)\n");
printf("-udp_intf <intf> - udp interface (no default)\n");
printf("-peer <address> - peer address (no default)\n");
printf("-count <count> - count (default -1)\n");
printf("-sleep <ms> - sleep between sends (default 1s)\n");
printf("\n");
}
struct MyAppApplication*
MyAppApplication_create(const char *local_participant_name,
const char *remote_participant_name,
DDS_Long domain_id,
char *udp_intf,
char *peer,
{
struct MyAppApplication *application = NULL;
RT_Registry_T *registry = NULL;
UNUSED_ARG(local_participant_name);
UNUSED_ARG(remote_participant_name);
application = (struct MyAppApplication *)malloc(sizeof(struct MyAppApplication));
if (application == NULL)
{
printf("failed to allocate application\n");
goto done;
}
application->sleep_time = sleep_time;
application->count = count;
{
printf("failed to get number of components\n");
goto done;
}
{
printf("failed to increase number of components\n");
goto done;
}
{
printf("failed to register wh\n");
goto done;
}
{
printf("failed to register rh\n");
goto done;
}
{
printf("failed to register T0\n");
goto done;
}
{
printf("failed to register T0\n");
goto done;
}
{
printf("failed to unregister udp\n");
goto done;
}
if (udp_property == NULL)
{
printf("failed to allocate udp properties\n");
goto done;
}
*udp_property = UDP_INTERFACE_FACTORY_PROPERTY_DEFAULT;
if (udp_intf != NULL)
{
}
0xc0a80ae8,0xffffff00,"T0",(void*)2))
{
printf("Failed to assert source rule\n");
goto done;
}
0xc0a80ae8,0xffffff00,"T0",(void*)2))
{
printf("Failed to assert source rule\n");
goto done;
}
0xc0a81465,0xffffff00,"T1",(void*)1))
{
printf("Failed to assert source rule\n");
goto done;
}
0xc0a81465,0xffffff00,"T1",(void*)1))
{
printf("Failed to assert source rule\n");
goto done;
}
(struct RT_ComponentFactoryProperty*)udp_property, NULL))
{
printf("failed to register udp\n");
goto done;
}
if (peer == NULL)
{
peer = "127.0.0.1";
}
"dpde",
&discovery_plugin_properties._parent,
NULL))
{
printf("failed to register dpde\n");
goto done;
}
{
printf("failed to set discovery plugin name\n");
goto done;
}
application->participant =
&dp_qos, NULL,
if (application->participant == NULL)
{
printf("failed to create participant\n");
goto done;
}
sprintf(application->type_name, "HelloWorld");
application->type_name,
HelloWorldTypePlugin_get());
{
printf("failed to register type: %s\n", "test_type");
goto done;
}
sprintf(application->topic_name, "HelloWorld");
application->topic =
application->topic_name,
application->type_name,
if (application->topic == NULL)
{
printf("topic == NULL\n");
goto done;
}
done:
if (!success)
{
if (udp_property != NULL)
{
free(udp_property);
}
free(application);
application = NULL;
}
return application;
}
MyAppApplication_enable(struct MyAppApplication * application)
{
{
printf("failed to enable entity\n");
}
return retcode;
}
void
MyAppApplication_delete(struct MyAppApplication *application)
{
RT_Registry_T *registry = NULL;
{
printf("failed to delete conteined entities (retcode=%d)\n",retcode);
}
application->type_name) != HelloWorldTypePlugin_get())
{
printf("failed to unregister type: %s\n", application->type_name);
return;
}
application->participant);
{
printf("failed to delete participant: %d\n", retcode);
return;
}
{
printf("failed to unregister dpde\n");
return;
}
{
printf("failed to unregister rh\n");
return;
}
{
printf("failed to unregister wh\n");
return;
}
free(application);
}
Examples
The following examples illustrate how this feature can be used in a system with a mixture of different types of UDP transport configurations.
For the purpose of the examples the following terminology is used:
- Plain communication - No transformations have been applied.
- Transformed User Data - Only the user-data is transformed, discovery is plain.
- Transformed Discovery - Only the discovery data is transformed, user-data is plain.
- Transformed Data - Both discovery and user-data are transformed. Unless stated otherwise the transformations are different.
A transformation Tn is a transformation such that an outgoing payload transformed with Tn can be transformed back to its original state by applying Tn to the incoming data.
A network interface can be either a physical or virtual.
Plain Communication Between 2 Nodes
In this system two Nodes, A and B, are communicating with plain communication. Node A has one interface a0 and Node B has one interface b0.
Node A:
- Register the UDP transport Ua with allow_interface = a0.
- DomainParticipantQos.transports.enabled_transports = “Ua”
- DomainParticipantQos.discovery.enabled_transports = ”Ua://”
- DomainParticipantQos.user_data.enabled_transports = ”Ua://”
Node B:
- Register the UDP transport Ub with allow_interface = b0.
- DomainParticipantQos.transports.enabled_transports = “Ub”
- DomainParticipantQos.discovery.enabled_transports = ”Ub://”
- DomainParticipantQos.user_data.enabled_transports = ”Ub://”
Transformed User Data Between 2 Nodes
In this system two Nodes, A and B, are communicating with transformed user data. Node A has two interfaces a0 and a1 and Node B has two interfaces b0 and b1. Since each node has only one peer a single transformation is sufficient.
Node A:
- Add a destination transformation T0 to Ua0, indicating that all sent data is transformed with T0.
- Add a source transformation T1 to Ua0, indicating that all received data is transformed with T1.
- Register the UDP transport Ua0 with allow_interface = a0.
- Register the UDP transport Ua1 with allow_interface = a1.
- No transformations are registered with Ua1.
- DomainParticipantQos.transports.enabled_transports = “Ua0”,”Ua1”
- DomainParticipantQos.discovery.enabled_transports = ”Ua1://”
- DomainParticipantQos.user_traffic.enabled_transports = ”Ua0://”
Node B:
- Add a destination transformation T1 to Ub0, indicating that all sent data is transformed with T1.
- Add a source transformation T0 to Ub0, indicating that all received data is transformed with T0.
- Register the UDP transport Ub0 with allow_interface = b0.
- Register the UDP transport Ub1 with allow_interface = b1.
- No transformations are registered with Ub1.
- DomainParticipantQos.transports.enabled_transports = “Ub0”,”Ub1”
- DomainParticipantQos.discovery.enabled_transports = ”Ub1://”
- DomainParticipantQos.user_traffic.enabled_transports = ”Ub0://”
Ua0 and Ub0 performs transformations and are used for user-data. Ua1 and Ub1 are used for discovery and no transformations takes place.
Transformed Discovery Data Between 2 Nodes
In this system two Nodes, A and B, are communicating with transformed user data. Node A has two interfaces a0 and a1 and Node B has two interfaces b0 and b1. Since each node has only one peer a single transformation is sufficient.
Node A:
- Add a destination transformation T0 to Ua0, indicating that all sent data is transformed with T0.
- Add a source transformation T1 to Ua0, indicating that all received data is transformed with T1.
- Register the UDP transport Ua0 with allow_interface = a0.
- Register the UDP transport Ua1 with allow_interface = a1.
- No transformations are registered with Ua1.
- DomainParticipantQos.transports.enabled_transports = “Ua0”,”Ua1”
- DomainParticipantQos.discovery.enabled_transports = ”Ua0://”
- DomainParticipantQos.user_data.enabled_transports = ”Ua1://”
Node B:
- Add a destination transformation T1 to Ub0, indicating that all sent data is transformed with T1.
- Add a source transformation T0 to Ub0, indicating that all received data is transformed with T0.
- Register the UDP transport Ub0 with allow_interface = b0.
- Register the UDP transport Ub1 with allow_interface = b1.
- No transformations are registered with Ub1.
- DomainParticipantQos.transports.enabled_transports = “Ub0”,”Ub1”
- DomainParticipantQos.discovery.enabled_transports = ”Ub0://”
- DomainParticipantQos.user_data.enabled_transports = ”Ub1://”
Ua0 and Ub0 performs transformations and are used for discovery. Ua1 and Ub1 are used for user-data and no transformation takes place.
Transformed Data Between 2 Nodes (same transformation)
In this system two Nodes, A and B, are communicating with transformed data using the same transformation for user and discovery data. Node A has one interface a0 and Node B has one interface b0.
Node A:
- Add a destination transformation T0 to Ua0, indicating that all sent data is transformed with T0.
- Add a source transformation T1 to Ua0, indicating that all received data is transformed with T1.
- Register the UDP transport Ua0 with allow_interface = a0.
- DomainParticipantQos.transports.enabled_transports = “Ua0”
- DomainParticipantQos.discovery.enabled_transports = ”Ua0://”
- DomainParticipantQos.user_data.enabled_transports = ”Ua0://”
Node B:
- Add a destination transformation T1 to Ub0, indicating that all sent data is transformed with T1.
- Add a source transformation T0 to Ub0, indicating that all received data is transformed with T0.
- Register the UDP transport Ub0 with allow_interface = b0.
- DomainParticipantQos.transports.enabled_transports = “Ub0”
- DomainParticipantQos.discovery.enabled_transports = ”Ub0://”
- DomainParticipantQos.user_data.enabled_transports = ”Ub0://”
Ua0 and Ub0 performs transformations and are used for discovery and for user-data.
Transformed Data Between 2 Nodes (different transformations)
In this system two Nodes, A and B, are communicating with transformed data using different transformations for user and discovery data. Node A has two interfaces a0 and a1 and Node B has two interface b0 and b1.
Node A:
- Add a destination transformation T0 to Ua0, indicating that all sent data is transformed with T0.
- Add a source transformation T1 to Ua0, indicating that all received data is transformed with T1.
- Add a destination transformation T2 to Ua1, indicating that all sent data is transformed with T2.
- Add a source transformation T3 to Ua1, indicating that all received data is transformed with T3.
- Register the UDP transport Ua0 with allow_interface = a0.
- Register the UDP transport Ua1 with allow_interface = a1.
- DomainParticipantQos.transports.enabled_transports = “Ua0”,”Ua1”
- DomainParticipantQos.discovery.enabled_transports = ”Ua0://”
- DomainParticipantQos.user_data.enabled_transports = ”Ua1://”
Node B:
- Add a destination transformation T1 to Ub0, indicating that all sent data is transformed with T1.
- Add a source transformation T0 to Ub0, indicating that all received data is transformed with T0.
- Add a destination transformation T3 to Ub1, indicating that all sent data is transformed with T3.
- Add a source transformation T2 to Ub1, indicating that all received data is transformed with T2.
- Register the UDP transport Ub0 with allow_interface = b0.
- Register the UDP transport Ub1 with allow_interface = b1.
- DomainParticipantQos.transports.enabled_transports = “Ub0”,”Ub1”
- DomainParticipantQos.discovery.enabled_transports = ”Ub0://”
- DomainParticipantQos.user_data.enabled_transports = ”Ub1://”
Ua0 and Ub0 performs transformations and are used for discovery. Ua1 and Ub1 performs transformations and are used for user-data.
OS Configuration
In systems with serveral network interfaces RTI Connext DDS Micro cannot ensure which network interface should be used to send a packet. Depending on the UDP transformations configured this might be a problem.
To illustrate this problem let's assume a system with two nodes A and B. Node A has two network interfaces a0 and a1 and Node B has two network interfaces b0 and b1. In this system Node A is communicating with Node B using a transformation for discovery and a different transformation for user data.
Node A:
- Add a destination transformation T0 to Ua0, indicating that sent data to b0 is transformed with T0.
- Add a source transformation T1 to Ua0, indicating that received data from b0 is transformed with T1.
- Add a destination transformation T2 to Ua1, indicating that sent data to b1 is transformed with T2.
- Add a source transformation T3 to Ua1, indicating that received data from b1 is transformed with T3.
- Register the UDP transport Ua0 with allow_interface = a0.
- Register the UDP transport Ua1 with allow_interface = a1.
- DomainParticipantQos.transports.enabled_transports = “Ua0”,”Ua1”
- DomainParticipantQos.discovery.enabled_transports = ”Ua0://”
- DomainParticipantQos.user_data.enabled_transports = ”Ua1://”
Node B:
- Add a destination transformation T1 to Ub0, indicating that sent data to a0 is transformed with T1.
- Add a source transformation T0 to Ub0, indicating that received data from a0 transformed with T0.
- Add a destination transformation T3 to Ub1, indicating that sent data to a1 is transformed with T3.
- Add a source transformation T2 to Ub1, indicating that received data from a1 transformed with T2.
- Register the UDP transport Ub0 with allow_interface = b0.
- Register the UDP transport Ub1 with allow_interface = b1.
- DomainParticipantQos.transports.enabled_transports = “Ub0”,”Ub1”
- DomainParticipantQos.discovery.enabled_transports = ”Ub0://”
- DomainParticipantQos.user_data.enabled_transports = ”Ub1://”
Node A sends a discovery packet to Node B to interface b0, this packet will be transformed using T0 as specified Node A configuration. When this packet is received in Node B, it will be transformed using either T0 or T2 depending on the source address. Node's A OS will use a0 or a1 to send this packet but RTI Connext DDS Micro cannot ensure which one will be used. In case the OS sends the packet using a1, the wrong transformation will be applied in Node B.
Some systems has the possibility to configure the source address that should be used when a packet is sent. In POSIX systems command 'ip route add <string> dev <interface>' can be used.
By typing command 'ip route add < b0 ip >/32 dev a0' in Node A the OS will send all packets to Node B b0 IP address using interface a0. This would ensure that the correct transformation is applied in Node B. The same should be done to ensure that user data is sent with the right address 'ip route add < b1 ip >/32 dev a1'. Of course similar configuration is needed in Node B.