diff --git a/src/Network/CMakeLists.txt b/src/Network/CMakeLists.txt
index 274dd739a..e16e3ddf5 100644
--- a/src/Network/CMakeLists.txt
+++ b/src/Network/CMakeLists.txt
@@ -62,11 +62,16 @@ set(HEADERS
 	rul.hxx
 	)
 
+if (CycloneDDS_FOUND)
+  set(SOURCES ${SOURCES} dds_props.cxx)
+  set(HEADERS ${HEADERS} dds_props.hxx)
+endif()
+
 if(ENABLE_IAX)
     list(APPEND SOURCES fgcom.cxx)
     list(APPEND HEADERS fgcom.hxx)
 endif()
-   	
+
 flightgear_component(Network "${SOURCES}" "${HEADERS}")
 
 if (CycloneDDS_FOUND)
diff --git a/src/Network/DDS/CMakeLists.txt b/src/Network/DDS/CMakeLists.txt
index 8372f6d73..b96b64e60 100644
--- a/src/Network/DDS/CMakeLists.txt
+++ b/src/Network/DDS/CMakeLists.txt
@@ -4,12 +4,14 @@ set(SOURCES
         dds_ctrls.c
         dds_gui.c
         dds_fdm.c
+        dds_props.c
 	)
 
 set(HEADERS
         dds_ctrls.h
         dds_fdm.h
         dds_gui.h
+        dds_props.h
 	)
 
 add_executable(fg_dds_log
diff --git a/src/Network/DDS/dds_fwd.hxx b/src/Network/DDS/dds_fwd.hxx
new file mode 100644
index 000000000..ddbe9f44e
--- /dev/null
+++ b/src/Network/DDS/dds_fwd.hxx
@@ -0,0 +1,10 @@
+// dds_fwd.hxx
+//
+// This file is in the Public Domain, and comes with no warranty.
+
+#pragma once
+
+#include "dds_gui.h"
+#include "dds_fdm.h"
+#include "dds_ctrls.h"
+
diff --git a/src/Network/DDS/dds_props.c b/src/Network/DDS/dds_props.c
new file mode 100644
index 000000000..79ef5628d
--- /dev/null
+++ b/src/Network/DDS/dds_props.c
@@ -0,0 +1,46 @@
+/****************************************************************
+
+  Generated by Eclipse Cyclone DDS IDL to C Translator
+  File name: dds_props.c
+  Source: dds_props.idl
+  Cyclone DDS: V0.7.0
+
+*****************************************************************/
+#include "dds_props.h"
+
+
+static const dds_key_descriptor_t FG_DDS_PROP_keys[1] =
+{
+  { "id", 0 }
+};
+
+static const uint32_t FG_DDS_PROP_ops [] =
+{
+  DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_SGN | DDS_OP_FLAG_KEY, offsetof (FG_DDS_PROP, id),
+  DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_SGN, offsetof (FG_DDS_PROP, type),
+  DDS_OP_ADR | DDS_OP_TYPE_UNI | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_SGN, offsetof (FG_DDS_PROP, val._d), 9u, (31u << 16) + 4u,
+  DDS_OP_JEQ | DDS_OP_TYPE_BOO | 0, FG_DDS_BOOL, offsetof (FG_DDS_PROP, val._u.Bool),
+  DDS_OP_JEQ | DDS_OP_TYPE_4BY | DDS_OP_FLAG_SGN | 0, FG_DDS_NONE, offsetof (FG_DDS_PROP, val._u.Int32),
+  DDS_OP_JEQ | DDS_OP_TYPE_4BY | DDS_OP_FLAG_SGN | 0, FG_DDS_INT, offsetof (FG_DDS_PROP, val._u.Int32),
+  DDS_OP_JEQ | DDS_OP_TYPE_8BY | DDS_OP_FLAG_SGN | 0, FG_DDS_LONG, offsetof (FG_DDS_PROP, val._u.Int64),
+  DDS_OP_JEQ | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP | 0, FG_DDS_FLOAT, offsetof (FG_DDS_PROP, val._u.Float32),
+  DDS_OP_JEQ | DDS_OP_TYPE_8BY | DDS_OP_FLAG_FP | 0, FG_DDS_DOUBLE, offsetof (FG_DDS_PROP, val._u.Float64),
+  DDS_OP_JEQ | DDS_OP_TYPE_STR | 0, FG_DDS_ALIAS, offsetof (FG_DDS_PROP, val._u.String),
+  DDS_OP_JEQ | DDS_OP_TYPE_STR | 0, FG_DDS_STRING, offsetof (FG_DDS_PROP, val._u.String),
+  DDS_OP_JEQ | DDS_OP_TYPE_STR | 0, FG_DDS_UNSPECIFIED, offsetof (FG_DDS_PROP, val._u.String),
+  DDS_OP_ADR | DDS_OP_TYPE_STR, offsetof (FG_DDS_PROP, guid),
+  DDS_OP_RTS
+};
+
+const dds_topic_descriptor_t FG_DDS_PROP_desc =
+{
+  sizeof (FG_DDS_PROP),
+  8u,
+  DDS_TOPIC_FIXED_KEY | DDS_TOPIC_NO_OPTIMIZE | DDS_TOPIC_CONTAINS_UNION,
+  1u,
+  "FG::DDS_PROP",
+  FG_DDS_PROP_keys,
+  14,
+  FG_DDS_PROP_ops,
+  "<MetaData version=\"1.0.0\"><Module name=\"FG\"><Enum name=\"propType\"><Element name=\"DDS_NONE\" value=\"0\"/><Element name=\"DDS_ALIAS\" value=\"1\"/><Element name=\"DDS_BOOL\" value=\"2\"/><Element name=\"DDS_INT\" value=\"3\"/><Element name=\"DDS_LONG\" value=\"4\"/><Element name=\"DDS_FLOAT\" value=\"5\"/><Element name=\"DDS_DOUBLE\" value=\"6\"/><Element name=\"DDS_STRING\" value=\"7\"/><Element name=\"DDS_UNSPECIFIED\" value=\"8\"/></Enum><Union name=\"propValue\"><SwitchType><Type name=\"propType\"/></SwitchType><Case name=\"Bool\"><Boolean/><Label value=\"DDS_BOOL\"/></Case><Case name=\"Int32\"><Long/><Label value=\"DDS_NONE\"/><Label value=\"DDS_INT\"/></Case><Case name=\"Int64\"><LongLong/><Label value=\"DDS_LONG\"/></Case><Case name=\"Float32\"><Float/><Label value=\"DDS_FLOAT\"/></Case><Case name=\"Float64\"><Double/><Label value=\"DDS_DOUBLE\"/></Case><Case name=\"String\"><String/><Label value=\"DDS_ALIAS\"/><Label value=\"DDS_STRING\"/><Label value=\"DDS_UNSPECIFIED\"/></Case></Union><Struct name=\"DDS_PROP\"><Member name=\"id\"><Long/></Member><Member name=\"type\"><Type name=\"propType\"/></Member><Member name=\"val\"><Type name=\"propValue\"/></Member><Member name=\"guid\"><String/></Member></Struct></Module></MetaData>"
+};
diff --git a/src/Network/DDS/dds_props.h b/src/Network/DDS/dds_props.h
new file mode 100644
index 000000000..eab4fa324
--- /dev/null
+++ b/src/Network/DDS/dds_props.h
@@ -0,0 +1,75 @@
+/****************************************************************
+
+  Generated by Eclipse Cyclone DDS IDL to C Translator
+  File name: dds_props.h
+  Source: dds_props.idl
+  Cyclone DDS: V0.7.0
+
+*****************************************************************/
+
+#include "dds/ddsc/dds_public_impl.h"
+
+#ifndef _DDSL_DDS_PROPS_H_
+#define _DDSL_DDS_PROPS_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FG_DDS_PROP_REQUEST -1
+typedef enum FG_propType
+{
+  FG_DDS_NONE,
+  FG_DDS_ALIAS,
+  FG_DDS_BOOL,
+  FG_DDS_INT,
+  FG_DDS_LONG,
+  FG_DDS_FLOAT,
+  FG_DDS_DOUBLE,
+  FG_DDS_STRING,
+  FG_DDS_UNSPECIFIED
+} FG_propType;
+
+#define FG_propType__alloc() \
+((FG_propType*) dds_alloc (sizeof (FG_propType)));
+
+
+typedef struct FG_propValue
+{
+  FG_propType _d;
+  union
+  {
+    bool Bool;
+    int32_t Int32;
+    int64_t Int64;
+    float Float32;
+    double Float64;
+    char * String;
+  } _u;
+} FG_propValue;
+
+#define FG_propValue__alloc() \
+((FG_propValue*) dds_alloc (sizeof (FG_propValue)));
+
+
+typedef struct FG_DDS_PROP
+{
+  int32_t id;
+  FG_propType type;
+  FG_propValue val;
+  char * guid;
+} FG_DDS_PROP;
+
+extern const dds_topic_descriptor_t FG_DDS_PROP_desc;
+
+#define FG_DDS_PROP__alloc() \
+((FG_DDS_PROP*) dds_alloc (sizeof (FG_DDS_PROP)));
+
+#define FG_DDS_PROP_free(d,o) \
+dds_sample_free ((d), &FG_DDS_PROP_desc, (o))
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _DDSL_DDS_PROPS_H_ */
diff --git a/src/Network/DDS/dds_props.idl b/src/Network/DDS/dds_props.idl
new file mode 100644
index 000000000..02716fd93
--- /dev/null
+++ b/src/Network/DDS/dds_props.idl
@@ -0,0 +1,85 @@
+// format dfescription: https://www.omg.org/spec/IDL/4.2/PDF
+
+// Adapted from net_fdm.hxx
+module FG
+{
+
+// defining it this way also generates accompanying #defines in the header file.
+const short DDS_PROP_REQUEST = -1;
+
+enum propType
+{
+    DDS_NONE,	// The node hasn't been assigned a value yet
+    DDS_ALIAS,	// The node "points" to another node
+    DDS_BOOL,
+    DDS_INT,	// 32-bit integer
+    DDS_LONG,	// 64-bit integer
+    DDS_FLOAT,	// 32-bit floating point number
+    DDS_DOUBLE,	// 64-bit floating point number
+    DDS_STRING,	// UTF-8 string
+    DDS_UNSPECIFIED // Resolves to STRING
+};
+
+union propValue switch ( propType )
+{
+    case DDS_BOOL:
+        boolean Bool;
+
+    case DDS_NONE:
+    case DDS_INT:
+        long Int32;
+
+    case DDS_LONG:
+        long long Int64;
+
+    case DDS_FLOAT:
+        float Float32;
+
+    case DDS_DOUBLE:
+        double Float64;
+
+    case DDS_ALIAS:
+    case DDS_STRING:
+    case DDS_UNSPECIFIED:
+        string String;
+};
+
+// Initial property request sequence
+// for properties where the id is not yet known:
+// 1. Set id to FG_DDS_PROP_REQUEST
+// 2. set guid to the 16-byte participants GUID
+// 3. Set type to STRING
+// 4. Set val.String to the propery path
+// 5. Send the package.
+//
+// 6. wait for an answer
+//    * Check whether guid matches the participants GUID.
+//    * The index of the requested propery path is stored in the id variable
+//      which should be used by successive request as the id for that property.
+//    * The type variable indicates the type of the value of the property.
+//    * The val union holds the value of the propery.
+//
+// Successive property request sequence:
+// 1. Set id to the id of the requested property
+// 2. Send the package.
+//
+// 3. wait for an answer
+//    * Check whether id matches the requested property id.
+//    * The type variable indicates the type of the value of the property.
+//    * The val union holds the value of the propery.
+//
+// guid is not defined as an array of 16 unsigned characters to keep the
+// sample small for successive requests. This way only a FG_DDS_PROP_REQUEST
+// sample will reserve the full 16-bytes.
+struct DDS_PROP
+{
+    long id;		// property index and DDS id
+
+    propType type;
+    propValue val;
+
+    string guid;	// requesters globally unique identifier
+};
+#pragma keylist DDS_PROP id
+
+}; // module FG
diff --git a/src/Network/dds_props.cxx b/src/Network/dds_props.cxx
new file mode 100644
index 000000000..62ce7fa68
--- /dev/null
+++ b/src/Network/dds_props.cxx
@@ -0,0 +1,167 @@
+// dds_props.cxx -- FGFS "DDS" properties protocal class
+//
+// Written by Erik Hofman, started April 2021
+//
+// Copyright (C) 2021 by Erik Hofman <erik@ehofman.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/io/iochannel.hxx>
+#include <simgear/props/props.hxx>
+
+#include <simgear/io/SGDataDistributionService.hxx>
+
+#include <Main/fg_props.hxx>
+#include <Main/globals.hxx>
+
+#include "dds_props.hxx"
+
+// open hailing frequencies
+bool FGDDSProps::open() {
+    if (is_enabled()) {
+        SG_LOG(SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
+                << "is already in use, ignoring");
+        return false;
+    }
+
+    SGIOChannel *io = get_io_channel();
+
+    SG_DDS_Topic *dds = static_cast<SG_DDS_Topic*>(io);
+    dds->setup<FG_DDS_PROP>(&FG_DDS_PROP_desc);
+
+    // always send and recieve.
+    if (! io->open(SG_IO_BI)) {
+        SG_LOG(SG_IO, SG_ALERT, "Error opening channel communication layer.");
+        return false;
+    }
+
+    set_enabled(true);
+
+    return true;
+}
+
+// process work for this port
+bool FGDDSProps::process() {
+    SGIOChannel *io = get_io_channel();
+    FG_DDS_PROP prop;
+
+    int length = sizeof(prop);
+    char *buf = reinterpret_cast<char*>(&prop);
+
+    if (get_direction() == SG_IO_IN)
+    {
+        // act as a client: send a request and wait for an answer.
+
+    }
+    else if (get_direction() == SG_IO_OUT)
+    {
+        // act as a server: read requests and send the results.
+        while (io->read(buf, length) == length)
+        {
+            if (prop.id == FG_DDS_PROP_REQUEST)
+            {
+                auto it = by_path.find(prop.val._u.String);
+                if (it == by_path.end())
+                {
+                    SGPropertyNode_ptr props = globals->get_props();
+                    SGPropertyNode_ptr p = props->getNode(prop.val._u.String);
+                    if (p)
+                    {
+                        prop.id = by_id.size();
+                        by_id[prop.id] = p;
+                        by_path[prop.val._u.String] = p;
+                    }
+                    setProp(prop, p);
+                }
+                else
+                {
+                    prop.id = std::distance(by_path.begin(), it);
+                    setProp(prop, it->second);
+                }
+            }
+            else
+            {
+                SGPropertyNode_ptr p = by_id[prop.id];
+                setProp(prop, p);
+            }
+
+            // send the response.
+            if (! io->write(buf, length)) {
+                SG_LOG(SG_IO, SG_ALERT, "Error writing data.");
+            }
+        } // while
+    }
+
+    return true;
+}
+
+// close the channel
+bool FGDDSProps::close() {
+    SGIOChannel *io = get_io_channel();
+
+    set_enabled(false);
+
+    if (! io->close()) {
+        return false;
+    }
+
+    return true;
+}
+
+void FGDDSProps::setProp(FG_DDS_PROP& prop, SGPropertyNode_ptr p)
+{
+    if (p)
+    {
+        simgear::props::Type type = p->getType();
+        if (type == simgear::props::BOOL) {
+            prop.type = FG_DDS_BOOL;
+            prop.val._u.Bool = p->getBoolValue();
+        } else if (type == simgear::props::INT) {
+            prop.type = FG_DDS_INT;
+            prop.val._u.Int32 = p->getIntValue();
+        } else if (type == simgear::props::LONG) {
+            prop.type = FG_DDS_LONG;
+            prop.val._u.Int64 = p->getLongValue();
+        } else if (type == simgear::props::FLOAT) {
+            prop.type = FG_DDS_FLOAT;
+            prop.val._u.Float32 = p->getFloatValue();
+        } else if (type == simgear::props::DOUBLE) {
+            prop.type = FG_DDS_DOUBLE;
+            prop.val._u.Float64 = p->getDoubleValue();
+        } else if (type == simgear::props::ALIAS) {
+            prop.type = FG_DDS_ALIAS;
+            prop.val._u.String = const_cast<char*>(p->getStringValue());
+        } else if (type == simgear::props::STRING) {
+            prop.type = FG_DDS_STRING;
+            prop.val._u.String = const_cast<char*>(p->getStringValue());
+        } else if (type == simgear::props::UNSPECIFIED) {
+            prop.type = FG_DDS_UNSPECIFIED;
+            prop.val._u.String = const_cast<char*>(p->getStringValue());
+        } else {
+            prop.type = FG_DDS_NONE;
+            prop.val._u.Int32 = 0;
+        }
+    }
+}
+
+
diff --git a/src/Network/dds_props.hxx b/src/Network/dds_props.hxx
new file mode 100644
index 000000000..0c874d1d0
--- /dev/null
+++ b/src/Network/dds_props.hxx
@@ -0,0 +1,57 @@
+// dds_props.hxx -- FGFS "DDS" properties protocal class
+//
+// Written by Erik Hofman, started April 2021
+//
+// Copyright (C) 2021 by Erik Hofman <erik@ehofman.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+
+#pragma once
+
+#include <string>
+#include <map>
+
+#include <simgear/compiler.h>
+
+#include <simgear/props/propsfwd.hxx>
+
+#include "protocol.hxx"
+#include "DDS/dds_props.h"
+
+
+class FGDDSProps : public FGProtocol {
+
+    std::map<uint32_t,SGPropertyNode_ptr> by_id;
+    std::map<std::string,SGPropertyNode_ptr> by_path;
+
+    static void setProp(FG_DDS_PROP& prop, SGPropertyNode_ptr p);
+    
+public:
+
+    FGDDSProps() = default;
+    ~FGDDSProps() = default;
+
+    // open hailing frequencies
+    bool open();
+
+    // process work for this port
+    bool process();
+
+    // close the channel
+    bool close();
+};