RTI Connext DDS Micro  Version 2.4.11
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
UDP Transformations

Modules

 MyUdpTransform
 UDP Transform Example.

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:

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:  

The following relationship is true between the UDP transport and a UDP transformation library:

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
MyUdpTransformFactory_register(RT_Registry_T *registry,
const char *const name,
struct MyUdpTransformFactoryProperty *property);
extern RTI_BOOL
MyUdpTransformFactory_unregister(RT_Registry_T *registry,
const char *const name,
struct MyUdpTransformFactoryProperty **);
#endif

Example Source file MyUdpTransform.c

/*ce
* \file
* \defgroup UDPTransformExampleModule MyUdpTransform
* \ingroup UserManuals_UDPTransform
* \brief UDP Transform Example
*
* \details
*
* The UDP interface is implemented as a NETIO interface and NETIO interface
* factory.
*/
/*ce \addtogroup UDPTransformExampleModule
* @{
*/
#include <stdio.h>
#include "MyUdpTransform.h"
/*ce
* \brief The UDP Transformation factory class
*
* \details
* All Transformation components must have a factory. A factory creates one
* instance of the component as needed. In the case of UDP transformations,
* \rtime creates one instance per UDP transport instance.
*/
{
/*ce
* \brief Base-class. All \rtime Factories must inherit from RT_ComponentFactory.
*/
struct RT_ComponentFactory _parent;
/*ce
* \brief A pointer to the properties of the factory.
*
* \details
*
* When a factory is registered with \rtime it can be registered with
* properties specific to the component. However \rtime does not
* make a copy ( that would require additional methods). Furthermore, it
* may not be desirable to make a copy. Instead, this decision is
* left to the implementer of the component. \rtime does not access
* any custom properties.
*/
struct MyUdpTransformFactoryProperty *property;
};
/*ce
* \brief The custom UDP transformation class.
*
* \details
* The MyUdpTransformFactory creates one instance of this class for each
* UDP interface created. In this example one packet buffer (NETIO_Packet_T),
* is allocated and a buffer to hold the transformed data (\ref buffer)
*
* Only one transformation can be done at a time and it is synchronous. Thus,
* it is sufficient with one buffer to transform input and output per
* instance of the MyUdpTransform.
*/
{
/*ce
* \brief Base-class. All UDP transforms must inherit from UDP_Transform
*/
/*ce \brief A reference to its own factory, if properties must be accessed
*/
/*ce \brief NETIO_Packet to hold a transformed payload.
*
* \details
*
* \rtime uses a NETIO_Packet_T to abstract data payload and this is
* what is being passed betweem the UDP transport and the transformation.
* The transformation must convert a payload into a NETIO_Packet. This
* is done with NETIO_Packet_initialize_from. This function saves all
* state except the payload buffer.
*/
NETIO_Packet_T packet;
/*ce \brief The payload to assign to NETIO_Packet_T
*
* \details
*
* A transformation cannot do in-place transformations because the input
* buffer may be sent multiple times (for example due to reliability).
* A transformation instance can only transform one buffer at a time
* (send or receive). The buffer must be large enough to hold a transformed
* payload. When the the transformation is created it receives a
* \ref UDP_TransformProperty. This property has the max send and
* receive buffers for transport and can be used to sise the buffer.
* Please refer to \ref UDP_InterfaceFactoryProperty::max_send_message_size
* and \ref UDP_InterfaceFactoryProperty::max_message_size.
*/
char *buffer;
/*ce \brief The maximum length of the buffer. NOTE: The buffer must
* be 1 byte larger than the largest buffer.
*/
RTI_SIZE_T max_buffer_length;
};
/*ce \brief Forward declaration of the interface implementation
*/
static struct UDP_TransformI MyUdpTransform_fv_Intf;
/*ce \brief Forward declaration of the interface factory implementation
*/
static struct RT_ComponentFactoryI MyUdpTransformFactory_fv_Intf;
/*ce \brief Method to create an instance of MyUdpTransform
*
* \param[in] factory The factory creating this instance
* \param[in] property Generic UDP_Transform properties
*
* \return A pointer to MyUdpTransform on sucess, NULL on failure.
*/
const struct UDP_TransformProperty *const property)
{
struct MyUdpTransform *t;
if (t == NULL)
{
return NULL;
}
/* All component instances must initialize the parent using this
* call.
*/
RT_Component_initialize(&t->_parent._parent,
&MyUdpTransform_fv_Intf._parent,
0,
(property ? &property->_parent : NULL),
NULL);
/* Allocate a buffer that is the larger of the send and receive
* size.
*/
t->max_buffer_length = property->max_receive_message_size;
{
t->max_buffer_length = property->max_send_message_size;
}
/* Allocate 1 extra byte */
if (t->buffer == NULL)
{
t = NULL;
}
return t;
}
/*ce \brief Method to delete an instance of MyUdpTransform
*
* \param[in] t Transformation instance to delete
*/
{
}
/*ce \brief Method to create a transformation for an destination address
*
* \details
*
* For each asserted destination rule a transform is created by the transformation
* instance. This method determines how a UDP payload is transformed before
* it is sent to an address that matches destination & netmask.
*
* \param[in] udptf UDP Transform instance that creates the transformation
* \param[out] context Pointer to a transformation context
* \param[in] destination Destination address for the transformation
\param[in] netmask The netmask to apply to this destination.
* \param[in] user_data The user_data the rule was asserted with
* \param[in] property UDP transform specific properties
* \param[out] ec User defined error code
*
* \return RTI_TRUE on success, RTI_FALSE on failure
*/
RTI_PRIVATE RTI_BOOL
UDP_Transform_T *const udptf,
void **const context,
const struct NETIO_Address *const destination,
const struct NETIO_Netmask *const netmask,
void *user_data,
const struct UDP_TransformProperty *const property,
RTI_INT32 *const ec)
{
struct MyUdpTransform *self = (struct MyUdpTransform*)udptf;
UNUSED_ARG(self);
UNUSED_ARG(destination);
UNUSED_ARG(user_data);
UNUSED_ARG(property);
UNUSED_ARG(ec);
UNUSED_ARG(netmask);
/* Save the user-data to determine which transform to apply later */
*context = (void*)user_data;
return RTI_TRUE;
}
/*ce \brief Method to delete a transformation for an destination address
*
*
* \param[in] udptf UDP Transform instance that created the transformation
* \param[out] context Pointer to a transformation context
* \param[in] destination Destination address for the transformation
* \param[in] netmask The netmask to apply to this destination.
* \param[out] ec User defined error code
*
* \return RTI_TRUE on success, RTI_FALSE on failure
*/
RTI_PRIVATE RTI_BOOL
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;
}
/*ce \brief Method to create a transformation for an source address
*
* \details
*
* For each asserted source rule a transform is created by the transformation
* instance. This method determines how a UDP payload is transformed when
* it is received from an address that matches source & netmask.
*
* \param[in] udptf UDP Transform instance that creates the transformation
* \param[out] context Pointer to a transformation context
* \param[in] source Destination address for the transformation
\param[in] netmask The netmask to apply to this destination.
* \param[in] user_data The user_data the rule was asserted with
* \param[in] property UDP transform specific properties
* \param[out] ec User defined error code
*
* \return RTI_TRUE on success, RTI_FALSE on failure
*/
RTI_PRIVATE RTI_BOOL
void **const context,
const struct NETIO_Address *const source,
const struct NETIO_Netmask *const netmask,
void *user_data,
const struct UDP_TransformProperty *const property,
RTI_INT32 *const ec)
{
struct MyUdpTransform *self = (struct MyUdpTransform*)udptf;
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;
}
/*ce \brief Method to delete a transformation for an source address
*
*
* \param[in] udptf UDP Transform instance that created the transformation
* \param[out] context Pointer to a transformation context
* \param[in] source Source address for the transformation
* \param[in] netmask The netmask to apply to this destination.
* \param[out] ec User defined error code
*
* \return RTI_TRUE on success, RTI_FALSE on failure
*/
RTI_PRIVATE RTI_BOOL
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;
}
/*ce \brief Method to transform data based on a source address
*
* \param[in] udptf UDP_Transform_T that performs the transformation
* \param[in] context Reference to context created by \ref MyUdpTransform_create_source_transform
* \param[in] source Source address for the transformation
* \param[in] in_packet The NETIO packet to transform
* \param[out] out_packet The transformed NETIO packet
* \param[out] ec User defined error code
*
* \return RTI_TRUE on success, RTI_FALSE on failure
*/
RTI_PRIVATE RTI_BOOL
void *context,
const struct NETIO_Address *const source,
const NETIO_Packet_T *const in_packet,
NETIO_Packet_T **out_packet,
RTI_INT32 *const ec)
{
struct MyUdpTransform *self = (struct MyUdpTransform*)udptf;
char *buf_ptr,*buf_end;
char *from_buf_ptr,*from_buf_end;
UNUSED_ARG(context);
UNUSED_ARG(source);
*ec = 0;
/* Assigned the transform buffer to the outgoing packet
* saving state from the incoming packet. In this case the
* outgoing length is the same as the incoming. How to buffer
* is filled in is of no interest to \rtime. All it cares about is
* where it starts and where it ends.
*/
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);
/* Perform a transformation based on the user-data */
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;
}
/*ce \brief Method to transform data based on a destination address
*
* \param[in] udptf UDP_Transform_T that performs the transformation
* \param[in] context Reference to context created by \ref MyUdpTransform_create_destination_transform
* \param[in] destination Source address for the transformation
* \param[in] in_packet The NETIO packet to transform
* \param[out] packet_out The transformed NETIO packet
* \param[out] ec User defined error code
*
* \return RTI_TRUE on success, RTI_FALSE on failure
*/
RTI_PRIVATE RTI_BOOL
void *context,
const struct NETIO_Address *const destination,
const NETIO_Packet_T *const in_packet,
NETIO_Packet_T **packet_out,
RTI_INT32 *const ec)
{
struct MyUdpTransform *self = (struct MyUdpTransform*)udptf;
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;
}
/*ce \brief Definition of the transformation interface
*/
RTI_PRIVATE struct UDP_TransformI MyUdpTransform_fv_Intf =
{
RT_COMPONENTI_BASE,
};
/*ce \brief Method called by \rtime to create an instance of transformation
*/
MUST_CHECK_RETURN RTI_PRIVATE RT_Component_T*
MyUdpTransformFactory_create_component(struct RT_ComponentFactory *factory,
struct RT_ComponentProperty *property,
struct RT_ComponentListener *listener)
{
struct MyUdpTransform *t;
UNUSED_ARG(listener);
(struct MyUdpTransformFactory*)factory,
(struct UDP_TransformProperty*)property);
return &t->_parent._parent;
}
/*ce \brief Method called by \rtime to delete an instance of transformation
*/
struct RT_ComponentFactory *factory,
RT_Component_T *component)
{
UNUSED_ARG(factory);
}
/*ce \brief Method called by \rtime when a factory is registered
*/
MUST_CHECK_RETURN RTI_PRIVATE struct RT_ComponentFactory*
MyUdpTransformFactory_initialize(struct RT_ComponentFactoryProperty* property,
struct RT_ComponentFactoryListener *listener)
{
struct MyUdpTransformFactory *fac;
UNUSED_ARG(property);
UNUSED_ARG(listener);
fac->_parent._factory = &fac->_parent;
fac->_parent.intf = &MyUdpTransformFactory_fv_Intf;
fac->property = (struct MyUdpTransformFactoryProperty*)property;
return &fac->_parent;
}
/*ce \brief Method called by \rtime when a factory is unregistered
*/
MyUdpTransformFactory_finalize(struct RT_ComponentFactory *factory,
struct RT_ComponentFactoryProperty **property,
struct RT_ComponentFactoryListener **listener)
{
struct MyUdpTransformFactory *fac =
(struct MyUdpTransformFactory*)factory;
UNUSED_ARG(property);
UNUSED_ARG(listener);
if (listener != NULL)
{
*listener = NULL;
}
if (property != NULL)
{
*property = (struct RT_ComponentFactoryProperty*)fac->property;
}
return;
}
/*ce \brief Definition of the factory interface
*/
RTI_PRIVATE struct RT_ComponentFactoryI MyUdpTransformFactory_fv_Intf =
{
UDP_INTERFACE_INTERFACE_ID,
NULL
};
struct RT_ComponentFactoryI*
MyUdpTransformFactory_get_interface(void)
{
return &MyUdpTransformFactory_fv_Intf;
}
/*ce \brief Method to register this transformation in a registry
*/
RTI_BOOL
MyUdpTransformFactory_register(RT_Registry_T *registry,
const char *const name,
struct MyUdpTransformFactoryProperty *property)
{
return RT_Registry_register(registry, name,
MyUdpTransformFactory_get_interface(),
&property->_parent, NULL);
}
/*ce \brief Method to unregister this transformation from a registry
*/
RTI_BOOL
MyUdpTransformFactory_unregister(RT_Registry_T *registry,
const char *const name,
struct MyUdpTransformFactoryProperty **property)
{
return RT_Registry_unregister(registry, name,
(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,
DDS_Long sleep_time, DDS_Long count)
{
struct DDS_DomainParticipantQos dp_qos =
struct MyAppApplication *application = NULL;
RT_Registry_T *registry = NULL;
struct UDP_InterfaceFactoryProperty *udp_property = NULL;
struct DPDE_DiscoveryPluginProperty discovery_plugin_properties =
UNUSED_ARG(local_participant_name);
UNUSED_ARG(remote_participant_name);
/* Uncomment to increase verbosity level:
OSAPILog_set_verbosity(OSAPI_LOG_VERBOSITY_WARNING);
*/
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;
}
if (!RT_Registry_register(registry, DDSHST_WRITER_DEFAULT_HISTORY_NAME,
{
printf("failed to register wh\n");
goto done;
}
if (!RT_Registry_register(registry, DDSHST_READER_DEFAULT_HISTORY_NAME,
{
printf("failed to register rh\n");
goto done;
}
if (!MyUdpTransformFactory_register(registry,"T0",NULL))
{
printf("failed to register T0\n");
goto done;
}
if (!MyUdpTransformFactory_register(registry,"T1",NULL))
{
printf("failed to register T0\n");
goto done;
}
/* Configure UDP transport's allowed interfaces */
if (!RT_Registry_unregister(registry, NETIO_DEFAULT_UDP_NAME, NULL, NULL))
{
printf("failed to unregister udp\n");
goto done;
}
udp_property = (struct UDP_InterfaceFactoryProperty *)
malloc(sizeof(struct UDP_InterfaceFactoryProperty));
if (udp_property == NULL)
{
printf("failed to allocate udp properties\n");
goto done;
}
*udp_property = UDP_INTERFACE_FACTORY_PROPERTY_DEFAULT;
/* For additional allowed interface(s), increase maximum and length, and
set interface below:
*/
udp_property->max_send_message_size = 16384;
udp_property->max_message_size = 32768;
if (udp_intf != NULL)
{
REDA_StringSeq_set_maximum(&udp_property->allow_interface,1);
REDA_StringSeq_set_length(&udp_property->allow_interface,1);
*REDA_StringSeq_get_reference(&udp_property->allow_interface,0) =
DDS_String_dup(udp_intf);
}
/* A rule that says: For payloads received from 192.168.10.* (netmask is
* 0xffffff00), apply transformation T0.
*/
&udp_property->source_rules,
0xc0a80ae8,0xffffff00,"T0",(void*)2))
{
printf("Failed to assert source rule\n");
goto done;
}
/* A rule that says: For payloads sent to 192.168.10.* (netmask is
* 0xffffff00), apply transformation T0.
*/
&udp_property->destination_rules,
0xc0a80ae8,0xffffff00,"T0",(void*)2))
{
printf("Failed to assert source rule\n");
goto done;
}
/* A rule that says: For payloads received from 192.168.20.* (netmask is
* 0xffffff00), apply transformation T1.
*/
&udp_property->source_rules,
0xc0a81465,0xffffff00,"T1",(void*)1))
{
printf("Failed to assert source rule\n");
goto done;
}
/* A rule that says: For payloads received from 192.168.20.* (netmask is
* 0xffffff00), apply transformation T1.
*/
&udp_property->destination_rules,
0xc0a81465,0xffffff00,"T1",(void*)1))
{
printf("Failed to assert source rule\n");
goto done;
}
if (!RT_Registry_register(registry, NETIO_DEFAULT_UDP_NAME,
(struct RT_ComponentFactoryProperty*)udp_property, NULL))
{
printf("failed to register udp\n");
goto done;
}
if (peer == NULL)
{
peer = "127.0.0.1"; /* default to loopback */
}
if (!RT_Registry_register(registry,
"dpde",
&discovery_plugin_properties._parent,
NULL))
{
printf("failed to register dpde\n");
goto done;
}
{
printf("failed to set discovery plugin name\n");
goto done;
}
DDS_StringSeq_set_maximum(&dp_qos.discovery.initial_peers,1);
DDS_StringSeq_set_length(&dp_qos.discovery.initial_peers,1);
*DDS_StringSeq_get_reference(&dp_qos.discovery.initial_peers,0) = DDS_String_dup(peer);
DDS_StringSeq_set_maximum(&dp_qos.discovery.enabled_transports,1);
DDS_StringSeq_set_length(&dp_qos.discovery.enabled_transports,1);
/* Use network interface 192.168.10.232 for discovery. T0 is used for
* discovery
*/
*DDS_StringSeq_get_reference(&dp_qos.discovery.enabled_transports,0) = DDS_String_dup("_udp://192.168.10.232");
DDS_StringSeq_set_maximum(&dp_qos.user_traffic.enabled_transports,1);
DDS_StringSeq_set_length(&dp_qos.user_traffic.enabled_transports,1);
/* Use network interface 192.168.20.101 for user-data. T1 is used for
* this interface.
*/
*DDS_StringSeq_get_reference(&dp_qos.user_traffic.enabled_transports,0) = DDS_String_dup("_udp://192.168.20.101");
/* if there are more remote or local endpoints, you need to increase these limits */
application->participant =
&dp_qos, NULL,
if (application->participant == NULL)
{
printf("failed to create participant\n");
goto done;
}
sprintf(application->type_name, "HelloWorld");
retcode = DDS_DomainParticipant_register_type(application->participant,
application->type_name,
HelloWorldTypePlugin_get());
if (retcode != DDS_RETCODE_OK)
{
printf("failed to register type: %s\n", "test_type");
goto done;
}
sprintf(application->topic_name, "HelloWorld");
application->topic =
DDS_DomainParticipant_create_topic(application->participant,
application->topic_name,
application->type_name,
if (application->topic == NULL)
{
printf("topic == NULL\n");
goto done;
}
success = DDS_BOOLEAN_TRUE;
done:
if (!success)
{
if (udp_property != NULL)
{
free(udp_property);
}
free(application);
application = NULL;
}
return application;
}
MyAppApplication_enable(struct MyAppApplication * application)
{
DDS_Entity *entity;
entity = DDS_DomainParticipant_as_entity(application->participant);
retcode = DDS_Entity_enable(entity);
if (retcode != DDS_RETCODE_OK)
{
printf("failed to enable entity\n");
}
return retcode;
}
void
MyAppApplication_delete(struct MyAppApplication *application)
{
RT_Registry_T *registry = NULL;
retcode = DDS_DomainParticipant_delete_contained_entities(application->participant);
if (retcode != DDS_RETCODE_OK)
{
printf("failed to delete conteined entities (retcode=%d)\n",retcode);
}
if (DDS_DomainParticipant_unregister_type(application->participant,
application->type_name) != HelloWorldTypePlugin_get())
{
printf("failed to unregister type: %s\n", application->type_name);
return;
}
application->participant);
if (retcode != DDS_RETCODE_OK)
{
printf("failed to delete participant: %d\n", retcode);
return;
}
if (!RT_Registry_unregister(registry, "dpde", NULL, NULL))
{
printf("failed to unregister dpde\n");
return;
}
if (!RT_Registry_unregister(registry, DDSHST_READER_DEFAULT_HISTORY_NAME, NULL, NULL))
{
printf("failed to unregister rh\n");
return;
}
if (!RT_Registry_unregister(registry, DDSHST_WRITER_DEFAULT_HISTORY_NAME, NULL, NULL))
{
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:

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:

Node B:

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:

Node B:

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:

Node B:

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:

Node B:

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:

Node B:

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:

Node B:

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.


RTI Connext DDS Micro Version 2.4.11 Copyright © Mon Jul 23 2018 Real-Time Innovations, Inc