Martin Melik Merkumians 6 лет назад
Родитель
Сommit
d8ac347bc3
43 измененных файлов с 2236 добавлено и 894 удалено
  1. 1 1
      source/buildsupport/MINGW/OpENer_PLATFORM_INCLUDES.cmake
  2. 1 1
      source/src/cip/CMakeLists.txt
  3. 22 17
      source/src/cip/appcontype.c
  4. 5 3
      source/src/cip/appcontype.h
  5. 1 1
      source/src/cip/cipassembly.h
  6. 72 66
      source/src/cip/cipcommon.c
  7. 2 2
      source/src/cip/cipconnectionmanager.c
  8. 2 2
      source/src/cip/cipconnectionmanager.h
  9. 260 0
      source/src/cip/cipdlr.c
  10. 84 0
      source/src/cip/cipdlr.h
  11. 171 27
      source/src/cip/cipethernetlink.c
  12. 31 2
      source/src/cip/cipethernetlink.h
  13. 73 29
      source/src/cip/cipidentity.c
  14. 25 10
      source/src/cip/cipidentity.h
  15. 5 4
      source/src/cip/cipioconnection.c
  16. 1 1
      source/src/cip/cipmessagerouter.h
  17. 20 10
      source/src/cip/cipqos.c
  18. 1 1
      source/src/cip/cipqos.h
  19. 2 2
      source/src/cip/cipstring.c
  20. 34 12
      source/src/cip/ciptcpipinterface.c
  21. 9 9
      source/src/cip/ciptcpipinterface.h
  22. 47 20
      source/src/opener_api.h
  23. 1 1
      source/src/ports/MINGW/CMakeLists.txt
  24. 129 55
      source/src/ports/MINGW/main.c
  25. 413 174
      source/src/ports/MINGW/networkconfig.c
  26. 5 0
      source/src/ports/MINGW/networkconfig.h
  27. 3 3
      source/src/ports/MINGW/networkhandler.c
  28. 1 1
      source/src/ports/MINGW/opener_error.c
  29. 4 4
      source/src/ports/MINGW/sample_application/ethlinkcbs.c
  30. 47 37
      source/src/ports/POSIX/main.c
  31. 147 38
      source/src/ports/POSIX/networkconfig.c
  32. 0 6
      source/src/ports/POSIX/networkconfig.h
  33. 1 1
      source/src/ports/POSIX/platform_network_includes.h
  34. 4 4
      source/src/ports/POSIX/sample_application/ethlinkcbs.c
  35. 11 0
      source/src/ports/POSIX/sample_application/opener_user_conf.h
  36. 1 1
      source/src/ports/WIN32/CMakeLists.txt
  37. 157 83
      source/src/ports/WIN32/main.c
  38. 390 226
      source/src/ports/WIN32/networkconfig.c
  39. 0 10
      source/src/ports/WIN32/networkconfig.h
  40. 2 2
      source/src/ports/WIN32/opener_error.c
  41. 4 4
      source/src/ports/WIN32/sample_application/ethlinkcbs.c
  42. 34 15
      source/src/ports/generic_networkhandler.c
  43. 13 9
      source/src/ports/nvdata/nvqos.c

+ 1 - 1
source/buildsupport/MINGW/OpENer_PLATFORM_INCLUDES.cmake

@@ -1,6 +1,6 @@
 macro(opener_platform_spec)
   #find_path( MSINTTYPES_DIR inttypes.h ${PROJECT_SOURCE_DIR}/contrib/msinttypes)
   include_directories(${PORTS_SRC_DIR}/${OpENer_PLATFORM} ${PORTS_SRC_DIR}/${OpENer_PLATFORM}/sample_application )
-  set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DRESTRICT=__restrict -DWIN32" )
+  set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DRESTRICT=__restrict -DWIN32 -D_WIN32_WINNT=_WIN32_WINNT_VISTA" )
 endmacro(opener_platform_spec)
 

+ 1 - 1
source/src/cip/CMakeLists.txt

@@ -12,7 +12,7 @@ opener_common_includes()
 #######################################
 opener_platform_support("INCLUDES")
 
-set( CIP_SRC appcontype.c cipassembly.c cipclass3connection.c cipcommon.c cipconnectionobject.c cipconnectionmanager.c ciperror.h cipethernetlink.c cipidentity.c cipioconnection.c cipmessagerouter.c ciptcpipinterface.c ciptypes.h cipepath.c cipelectronickey.c cipstring.c cipqos.c )
+set( CIP_SRC appcontype.c cipassembly.c cipclass3connection.c cipcommon.c cipconnectionobject.c cipconnectionmanager.c cipdlr.c ciperror.h cipethernetlink.c cipidentity.c cipioconnection.c cipmessagerouter.c ciptcpipinterface.c ciptypes.h cipepath.c cipelectronickey.c cipstring.c cipqos.c )
 
 add_library( CIP ${CIP_SRC} )
 

+ 22 - 17
source/src/cip/appcontype.c

@@ -293,9 +293,10 @@ CipConnectionObject *GetListenOnlyConnection(
         break;
       }
 
-      if ( NULL
-           == GetExistingProducerMulticastConnection(
-             connection_object->produced_path.instance_id) ) {
+      /* Here we look for both Point-to-Point and Multicast IO connections */
+      if ( NULL == GetExistingProducerIoConnection(false,
+             connection_object->produced_path.instance_id) )
+      {
         *extended_error =
           kConnectionManagerExtendedStatusCodeNonListenOnlyConnectionNotOpened;
         break;
@@ -333,27 +334,31 @@ CipConnectionObject *GetListenOnlyConnection(
   return NULL;
 }
 
-CipConnectionObject *GetExistingProducerMulticastConnection(
+CipConnectionObject *GetExistingProducerIoConnection(
+  const bool multicast_only,
   const EipUint32 input_point) {
   DoublyLinkedListNode *node = connection_list.first;
 
   while (NULL != node) {
-    CipConnectionObject *producer_multicast_connection = node->data;
-    if ( true ==
-         ConnectionObjectIsTypeIOConnection(producer_multicast_connection) &&
-         (input_point ==
-          producer_multicast_connection->produced_path.instance_id) &&
-         ( kConnectionObjectConnectionTypeMulticast ==
-           ConnectionObjectGetTToOConnectionType(producer_multicast_connection) )
-         &&
-         (kEipInvalidSocket !=
-          producer_multicast_connection->socket[
-            kUdpCommuncationDirectionProducing]) )
+    CipConnectionObject *producer_io_connection = node->data;
+    if (ConnectionObjectIsTypeIOConnection(producer_io_connection) &&
+        (input_point == producer_io_connection->produced_path.instance_id) &&
+        (kEipInvalidSocket !=
+         producer_io_connection->socket[kUdpCommuncationDirectionProducing]) )
     {
+      ConnectionObjectConnectionType cnxn_type =
+        ConnectionObjectGetTToOConnectionType(producer_io_connection);
       /* we have a connection that produces the same input assembly,
-       * is a multicast producer and manages the connection.
+       * and manages the connection.
        */
-      return producer_multicast_connection;
+      if (kConnectionObjectConnectionTypeMulticast == cnxn_type) {
+        return producer_io_connection;
+      }
+      if (!multicast_only &&
+          kConnectionObjectConnectionTypePointToPoint == cnxn_type)
+      {
+        return producer_io_connection;
+      }
     }
     node = node->next;
   }

+ 5 - 3
source/src/cip/appcontype.h

@@ -30,10 +30,12 @@ CipConnectionObject *GetIoConnectionForConnectionData(
 /** @brief Check if there exists already an exclusive owner or listen only connection
  *         which produces the input assembly.
  *
- *  @param input_point the Input point to be produced
- *  @return if a connection could be found a pointer to this connection if not NULL
+ *  @param  multicast_only  Look only for multi-cast connections
+ *  @param  input_point     the Input point to be produced
+ *  @return   a pointer to the found connection; NULL if nothing found
  */
-CipConnectionObject *GetExistingProducerMulticastConnection(
+CipConnectionObject *GetExistingProducerIoConnection(
+  const bool multicast_only,
   const EipUint32 input_point);
 
 /** @brief check if there exists an producing multicast exclusive owner or

+ 1 - 1
source/src/cip/cipassembly.h

@@ -10,7 +10,7 @@
 #include "ciptypes.h"
 
 /** @brief Assembly class code */
-static const CipUint kCipAssemblyClassCode = 0x04u;
+static const CipUint kCipAssemblyClassCode = 0x04U;
 
 /* public functions */
 

+ 72 - 66
source/src/cip/cipcommon.c

@@ -19,6 +19,9 @@
 #include "ciperror.h"
 #include "cipassembly.h"
 #include "cipmessagerouter.h"
+#if defined(OPENER_IS_DLR_DEVICE) && 0 != OPENER_IS_DLR_DEVICE
+  #include "cipdlr.h"
+#endif
 #include "cipqos.h"
 #include "cpf.h"
 #include "trace.h"
@@ -47,6 +50,10 @@ void CipStackInit(const EipUint16 unique_connection_id) {
   OPENER_ASSERT(kEipStatusOk == eip_status)
   eip_status = CipAssemblyInitialize();
   OPENER_ASSERT(kEipStatusOk == eip_status)
+#if defined(OPENER_IS_DLR_DEVICE) && 0 != OPENER_IS_DLR_DEVICE
+  eip_status = CipDlrInit();
+  OPENER_ASSERT(kEipStatusOk == eip_status);
+#endif
   eip_status = CipQoSInit();
   OPENER_ASSERT(kEipStatusOk == eip_status)
   /* the application has to be initialized at last */
@@ -187,12 +194,12 @@ CipInstance *AddCipInstances(CipClass *RESTRICT const cip_class,
   return first_instance;
 }
 
-CipInstance *AddCipInstance(CipClass *RESTRICT const class,
+CipInstance *AddCipInstance(CipClass *RESTRICT const cip_class,
                             const EipUint32 instance_id) {
-  CipInstance *instance = GetCipInstance(class, instance_id);
+  CipInstance *instance = GetCipInstance(cip_class, instance_id);
 
   if (NULL == instance) {       /*we have no instance with given id*/
-    instance = AddCipInstances(class, 1);
+    instance = AddCipInstances(cip_class, 1);
     instance->instance_number = instance_id;
   }
   return instance;
@@ -208,12 +215,12 @@ CipClass *CreateCipClass(const CipUdint class_code,
                          const int number_of_instances,
                          char *name,
                          const EipUint16 revision,
-                         void (*InitializeCipClass)(CipClass *) ) {
+                         InitializeCipClass initializer) {
 
   OPENER_TRACE_INFO("creating class '%s' with code: 0x%" PRIX32 "\n", name,
                     class_code);
 
-  OPENER_ASSERT(NULL == GetCipClass(class_code) )     /* check if an class with the ClassID already exists */
+  OPENER_ASSERT(NULL == GetCipClass(class_code))  /* check if an class with the ClassID already exists */
   /* should never try to redefine a class*/
 
   /* a metaClass is a class that holds the class attributes and services
@@ -222,83 +229,83 @@ CipClass *CreateCipClass(const CipUdint class_code,
      and contains a pointer to a metaclass
      CIP never explicitly addresses a metaclass*/
 
-  CipClass * const class = (CipClass *) CipCalloc(1, sizeof(CipClass) );       /* create the class object*/
+  CipClass *const cip_class = (CipClass *) CipCalloc(1, sizeof(CipClass) );       /* create the class object*/
   CipClass *const meta_class = (CipClass *) CipCalloc(1, sizeof(CipClass) );       /* create the metaclass object*/
 
   /* initialize the class-specific fields of the Class struct*/
-  class->class_code = class_code;       /* the class remembers the class ID */
-  class->revision = revision;       /* the class remembers the class ID */
-  class->number_of_instances = 0;       /* the number of instances initially zero (more created below) */
-  class->instances = 0;
-  class->number_of_attributes = number_of_instance_attributes;       /* the class remembers the number of instances of that class */
-  class->highest_attribute_number = highest_instance_attribute_number;       /* indicate which attributes are included in instance getAttributeAll */
-  class->number_of_services = number_of_instance_services;       /* the class manages the behavior of the instances */
-  class->services = 0;
-  class->class_name = name;       /* initialize the class-specific fields of the metaClass struct */
+  cip_class->class_code = class_code;       /* the class remembers the class ID */
+  cip_class->revision = revision;       /* the class remembers the class ID */
+  cip_class->number_of_instances = 0;       /* the number of instances initially zero (more created below) */
+  cip_class->instances = 0;
+  cip_class->number_of_attributes = number_of_instance_attributes;       /* the class remembers the number of instances of that class */
+  cip_class->highest_attribute_number = highest_instance_attribute_number;       /* indicate which attributes are included in instance getAttributeAll */
+  cip_class->number_of_services = number_of_instance_services;       /* the class manages the behavior of the instances */
+  cip_class->services = 0;
+  cip_class->class_name = name;       /* initialize the class-specific fields of the metaClass struct */
   meta_class->class_code = 0xffffffff;       /* set metaclass ID (this should never be referenced) */
   meta_class->number_of_instances = 1;       /* the class object is the only instance of the metaclass */
-  meta_class->instances = (CipInstance *) class;
-  meta_class->number_of_attributes = number_of_class_attributes + 7;       /* the metaclass remembers how many class attributes exist*/
-  meta_class->highest_attribute_number = highest_class_attribute_number;       /* indicate which attributes are included in class getAttributeAll*/
-  meta_class->number_of_services = number_of_class_services;       /* the metaclass manages the behavior of the class itself */
+  meta_class->instances = (CipInstance *) cip_class;
+  meta_class->number_of_attributes = number_of_class_attributes + 7;      /* the metaclass remembers how many class attributes exist*/
+  meta_class->highest_attribute_number = highest_class_attribute_number;  /* indicate which attributes are included in class getAttributeAll*/
+  meta_class->number_of_services = number_of_class_services;              /* the metaclass manages the behavior of the class itself */
   meta_class->class_name = (char *) CipCalloc(1, strlen(name) + 6);       /* fabricate the name "meta<classname>"*/
   snprintf(meta_class->class_name, strlen(name) + 6, "meta-%s", name);
 
   /* initialize the instance-specific fields of the Class struct*/
-  class->class_instance.instance_number = 0;       /* the class object is instance zero of the class it describes (weird, but that's the spec)*/
-  class->class_instance.attributes = 0;       /* this will later point to the class attibutes*/
-  class->class_instance.cip_class = meta_class;       /* the class's class is the metaclass (like SmallTalk)*/
-  class->class_instance.next = 0;       /* the next link will always be zero, since there is only one instance of any particular class object */
+  cip_class->class_instance.instance_number = 0;    /* the class object is instance zero of the class it describes (weird, but that's the spec)*/
+  cip_class->class_instance.attributes = 0;         /* this will later point to the class attibutes*/
+  cip_class->class_instance.cip_class = meta_class; /* the class's class is the metaclass (like SmallTalk)*/
+  cip_class->class_instance.next = 0;       /* the next link will always be zero, since there is only one instance of any particular class object */
 
-  meta_class->class_instance.instance_number = 0xffffffff;       /*the metaclass object does not really have a valid instance number*/
-  meta_class->class_instance.attributes = 0;       /* the metaclass has no attributes*/
-  meta_class->class_instance.cip_class = 0;       /* the metaclass has no class*/
-  meta_class->class_instance.next = 0;       /* the next link will always be zero, since there is only one instance of any particular metaclass object*/
+  meta_class->class_instance.instance_number = 0xffffffff;  /*the metaclass object does not really have a valid instance number*/
+  meta_class->class_instance.attributes = NULL;/* the metaclass has no attributes*/
+  meta_class->class_instance.cip_class = NULL; /* the metaclass has no class*/
+  meta_class->class_instance.next = NULL;      /* the next link will always be zero, since there is only one instance of any particular metaclass object*/
 
   /* further initialization of the class object*/
 
-  class->class_instance.attributes = (CipAttributeStruct *) CipCalloc(
+  cip_class->class_instance.attributes = (CipAttributeStruct *) CipCalloc(
     meta_class->number_of_attributes, sizeof(CipAttributeStruct) );
   /* TODO -- check that we didn't run out of memory?*/
 
   meta_class->services = (CipServiceStruct *) CipCalloc(
     meta_class->number_of_services, sizeof(CipServiceStruct) );
 
-  class->services = (CipServiceStruct *) CipCalloc(class->number_of_services,
+  cip_class->services = (CipServiceStruct *) CipCalloc(cip_class->number_of_services,
                                                    sizeof(CipServiceStruct) );
 
   if (number_of_instances > 0) {
-    AddCipInstances(class, number_of_instances);             /*TODO handle return value and clean up if necessary*/
+    AddCipInstances(cip_class, number_of_instances);  /*TODO handle return value and clean up if necessary*/
   }
 
-  if ( (RegisterCipClass(class) ) == kEipStatusError ) {    /* no memory to register class in Message Router */
+  if (RegisterCipClass(cip_class) == kEipStatusError) {/* no memory to register class in Message Router */
     return 0;             /*TODO handle return value and clean up if necessary*/
   }
 
-  AllocateAttributeMasks(meta_class);       /* Allocation of bitmasks for Class Attributes */
-  AllocateAttributeMasks(class);       /* Allocation of bitmasks for Instance Attributes */
-
-  if (InitializeCipClass == NULL) {
-    InsertAttribute( (CipInstance *) class, 1, kCipUint,
-                     (void *) &class->revision, kGetableSingleAndAll );           /* revision */
-    InsertAttribute( (CipInstance *) class, 2, kCipUint,
-                     (void *) &class->number_of_instances,
-                     kGetableSingleAndAll );                                                 /*  largest instance number */
-    InsertAttribute( (CipInstance *) class, 3, kCipUint,
-                     (void *) &class->number_of_instances,
-                     kGetableSingleAndAll );                                                 /* number of instances currently existing*/
-    InsertAttribute( (CipInstance *) class, 4, kCipUint,
-                     (void *) &kCipUintZero, kGetableAll );           /* optional attribute list - default = 0 */
-    InsertAttribute( (CipInstance *) class, 5, kCipUint,
-                     (void *) &kCipUintZero, kNotSetOrGetable );           /* optional service list - default = 0 */
-    InsertAttribute( (CipInstance *) class, 6, kCipUint,
+  AllocateAttributeMasks(meta_class); /* Allocation of bitmasks for Class Attributes */
+  AllocateAttributeMasks(cip_class);  /* Allocation of bitmasks for Instance Attributes */
+
+  if (NULL == initializer) {
+    InsertAttribute( (CipInstance *) cip_class, 1, kCipUint,
+                     (void *) &cip_class->revision, kGetableSingleAndAll ); /* revision */
+    InsertAttribute( (CipInstance *) cip_class, 2, kCipUint,
+                     (void *) &cip_class->number_of_instances,
+                     kGetableSingleAndAll );                        /*  largest instance number */
+    InsertAttribute( (CipInstance *) cip_class, 3, kCipUint,
+                     (void *) &cip_class->number_of_instances,
+                     kGetableSingleAndAll );                        /* number of instances currently existing*/
+    InsertAttribute( (CipInstance *) cip_class, 4, kCipUint,
+                     (void *) &kCipUintZero, kGetableAll );         /* optional attribute list - default = 0 */
+    InsertAttribute( (CipInstance *) cip_class, 5, kCipUint,
+                     (void *) &kCipUintZero, kNotSetOrGetable );    /* optional service list - default = 0 */
+    InsertAttribute( (CipInstance *) cip_class, 6, kCipUint,
                      (void *) &meta_class->highest_attribute_number,
-                     kGetableSingle );                                                           /* max class attribute number*/
-    InsertAttribute( (CipInstance *) class, 7, kCipUint,
-                     (void *) &class->highest_attribute_number,
-                     kGetableSingle );                                                      /* max instance attribute number*/
+                     kGetableSingle );                              /* max class attribute number*/
+    InsertAttribute( (CipInstance *) cip_class, 7, kCipUint,
+                     (void *) &cip_class->highest_attribute_number,
+                     kGetableSingle );                              /* max instance attribute number*/
   } else {
-    InitializeCipClass(class);
+    initializer(cip_class);
   }
 
   /* create the standard class services*/
@@ -310,7 +317,7 @@ CipClass *CreateCipClass(const CipUdint class_code,
     InsertService(meta_class, kGetAttributeSingle, &GetAttributeSingle,
                   "GetAttributeSingle");
   }
-  return class;
+  return cip_class;
 }
 
 void InsertAttribute(CipInstance *const instance,
@@ -320,7 +327,7 @@ void InsertAttribute(CipInstance *const instance,
                      const EipByte cip_flags) {
 
   CipAttributeStruct *attribute = instance->attributes;
-  CipClass *class = instance->cip_class;
+  CipClass *cip_class = instance->cip_class;
 
   OPENER_ASSERT(NULL != attribute)
   /* adding a attribute to a class that was not declared to have any attributes is not allowed */
@@ -331,17 +338,17 @@ void InsertAttribute(CipInstance *const instance,
       attribute->attribute_flags = cip_flags;
       attribute->data = data;
 
-      OPENER_ASSERT(attribute_number <= class->highest_attribute_number)
+      OPENER_ASSERT(attribute_number <= cip_class->highest_attribute_number)
 
       size_t index = CalculateIndex(attribute_number);
 
-      class->get_single_bit_mask[index] |=
+      cip_class->get_single_bit_mask[index] |=
         (cip_flags & kGetableSingle) ?
         1 << (attribute_number) % 8 : 0;
-      class->get_all_bit_mask[index] |=
+      cip_class->get_all_bit_mask[index] |=
         (cip_flags & kGetableAll) ? 1 << (attribute_number) % 8 : 0;
-      class->set_bit_mask[index] |= ( (cip_flags & kSetable) ? 1 : 0 )
-                                    << ( (attribute_number) % 8 );
+      cip_class->set_bit_mask[index] |=
+        ( (cip_flags & kSetable) ? 1 : 0 ) << ( (attribute_number) % 8 );
 
       return;
     }
@@ -349,24 +356,23 @@ void InsertAttribute(CipInstance *const instance,
   }
   OPENER_TRACE_ERR(
     "Tried to insert too many attributes into class: %" PRIu32 " '%s', instance %" PRIu32 "\n",
-    instance->cip_class->class_code, instance->cip_class->class_name,
-    instance->instance_number);
+    cip_class->class_code, cip_class->class_name, instance->instance_number);
   OPENER_ASSERT(0)
   /* trying to insert too many attributes*/
 }
 
-void InsertService(const CipClass *const class,
+void InsertService(const CipClass *const cip_class,
                    const EipUint8 service_number,
                    const CipServiceFunction service_function,
                    char *const service_name) {
 
-  CipServiceStruct *service = class->services;       /* get a pointer to the service array*/
+  CipServiceStruct *service = cip_class->services;  /* get a pointer to the service array*/
   OPENER_TRACE_INFO("%s, number of services:%d, service number:%d\n",
-                    class->class_name, class->number_of_services,
+                    cip_class->class_name, cip_class->number_of_services,
                     service_number);
   OPENER_ASSERT(service != NULL)
   /* adding a service to a class that was not declared to have services is not allowed*/
-  for (int i = 0; i < class->number_of_services; i++)       /* Iterate over all service slots attached to the class */
+  for (int i = 0; i < cip_class->number_of_services; i++) /* Iterate over all service slots attached to the class */
   {
     if (service->service_number == service_number||
         service->service_function == NULL)         /* found undefined service slot*/

+ 2 - 2
source/src/cip/cipconnectionmanager.c

@@ -164,9 +164,9 @@ unsigned int GetPaddedLogicalPath(const EipUint8 **logical_path_segment) {
  *
  * A unique connectionID is formed from the boot-time-specified "incarnation ID"
  * and the per-new-connection-incremented connection number/counter.
- * @return new connection id
+ * @return new 32-bit connection id
  */
-CipUint GetConnectionId(void) {
+CipUdint GetConnectionId(void) {
   static CipUint connection_id = 18;
   connection_id++;
   return ( g_incarnation_id | (connection_id & 0x0000FFFF) );

+ 2 - 2
source/src/cip/cipconnectionmanager.h

@@ -160,7 +160,7 @@ typedef enum {
 #define SEQ_GEQ16(a, b) ( (short)( (a) - (b) ) >= 0 )
 
 /** @brief Connection Manager class code */
-static const CipUint kCipConnectionManagerClassCode = 0x06u;
+static const CipUint kCipConnectionManagerClassCode = 0x06U;
 
 /* public functions */
 
@@ -219,7 +219,7 @@ void AddNewActiveConnection(CipConnectionObject *const connection_object);
 void RemoveFromActiveConnections(CipConnectionObject *const connection_object);
 
 
-CipUint GetConnectionId(void);
+CipUdint GetConnectionId(void);
 
 typedef void (*CloseSessionFunction)(const CipConnectionObject *const
                                      connection_object);

+ 260 - 0
source/src/cip/cipdlr.c

@@ -0,0 +1,260 @@
+/*******************************************************************************
+ * Copyright (c) 2019, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+/** @file
+ * @brief Implements the DLR object
+ * @author Stefan Maetje <stefan.maetje@esd.eu>
+ *
+ *  CIP DLR object
+ *  ==============
+ *
+ *  This module implements the DLR object for a non supervisor and non gateway
+ *  device.
+ *
+ *  Implemented Attributes
+ *  ----------------------
+ *  - Attribute  1: Network Topology
+ *  - Attribute  2: Network Status
+ *  - Attribute 10: Active Supervisor Address
+ *  - Attribute 12: Capability Flags
+ *
+ *  Non-implemented attributes
+ *  --------------------------
+ *  The attributes 3, 4, 5, 6, 7, 8, 9 and 11 are only required for devices
+ *  that are capable of functioning as a ring supervisor. These attributes
+ *  shall not be implemented by non-supervisor devices.
+ *
+ *  The attributes 13, 14, 15 and 16 are only required for devices that are
+ *  capable of functioning as a redundant gateway. These attributes shall
+ *  not be implemented by non-redundant gateway devices.
+ *
+ *  None of the attributes 17, 18 and 19 is required and implemented.
+ *  Because of this the Object Revision stays on level 3 (see @ref
+ *  DLR_CLASS_REVISION).
+ *
+ *  Implemented Services
+ *  --------------------
+ *  - GetAttributesAll
+ *  - GetAttributeSingle
+ */
+/* ********************************************************************
+ * include files
+ */
+#include "cipdlr.h"
+
+#include <string.h>
+
+#include "cipcommon.h"
+#include "opener_api.h"
+#include "trace.h"
+
+/* ********************************************************************
+ * defines
+ */
+/** The implemented class revision is still 3 because the attributes
+ *  mandatory for revision 4 are NOT implemented. */
+#define DLR_CLASS_REVISION  3
+
+/* ********************************************************************
+ * Type declarations
+ */
+
+/* ********************************************************************
+ * module local variables
+ */
+/* Define variables with default values to be used for the
+ *  GetAttributeAll response for not implemented attributes. */
+static const CipUsint s_0xFF_default = 0xFFU;
+static const CipUint s_0xFFFF_default = 0xFFFFU;
+
+static const CipUsint s_0x00_default = 0x00U;
+static const CipUint s_0x0000_default = 0x0000U;
+static const CipUdint s_0x00000000_default = 0x00000000U;
+
+static const CipNodeAddress s_zero_node = {
+  .device_ip = 0,
+  .device_mac = {
+    0, 0, 0, 0, 0, 0,
+  }
+};
+
+/* ********************************************************************
+ * global public variables
+ */
+CipDlrObject g_dlr;  /**< definition of DLR object instance 1 data */
+
+
+/* ********************************************************************
+ * local functions
+ */
+static int EncodeNodeAddress(CipNodeAddress *node_address, CipOctet **message) {
+  int encoded_len = 0;
+  encoded_len += EncodeData(kCipUdint, &node_address->device_ip,
+                            message);
+  encoded_len += EncodeData(kCip6Usint,
+                            &node_address->device_mac,
+                            message);
+  return encoded_len;
+}
+
+static EipStatus GetAttributeSingleDlr(
+  CipInstance *RESTRICT const instance,
+  CipMessageRouterRequest *const message_router_request,
+  CipMessageRouterResponse *const message_router_response,
+  const struct sockaddr *originator_address,
+  const int encapsulation_session) {
+
+  CipAttributeStruct *attribute = GetCipAttribute(
+    instance, message_router_request->request_path.attribute_number);
+  EipByte *message = message_router_response->data;
+
+  message_router_response->data_length = 0;
+  message_router_response->reply_service = (0x80
+                                            | message_router_request->service);
+  message_router_response->general_status = kCipErrorAttributeNotSupported;
+  message_router_response->size_of_additional_status = 0;
+
+  EipUint16 attribute_number = message_router_request->request_path
+                               .attribute_number;
+
+  if ( (NULL != attribute) && (NULL != attribute->data) ) {
+    /* Mask for filtering get-ability */
+    uint8_t get_bit_mask = 0;
+    if (kGetAttributeAll == message_router_request->service) {
+      get_bit_mask = (instance->cip_class->get_all_bit_mask[CalculateIndex(
+                                                              attribute_number)]);
+      message_router_response->general_status = kCipErrorSuccess;
+    } else {
+      get_bit_mask = (instance->cip_class->get_single_bit_mask[CalculateIndex(
+                                                                 attribute_number)
+                      ]);
+    }
+    if ( 0 != ( get_bit_mask & ( 1 << (attribute_number % 8) ) ) ) {
+      /* create a reply message containing the data*/
+      bool use_common_handler;
+      switch (attribute_number) {
+      case 4: /* fall through */
+      case 6: /* fall through */
+      case 7: /* fall through */
+      case 10:
+        use_common_handler = false;
+        OPENER_TRACE_INFO("getAttribute %d\n", attribute_number);
+        break;
+      default:
+        use_common_handler = true;
+        break;
+      }
+
+      /* Call the PreGetCallback if enabled for this attribute and the common handler is not used. */
+      if (!use_common_handler) {
+        if (attribute->attribute_flags & kPreGetFunc && NULL != instance->cip_class->PreGetCallback) {
+          instance->cip_class->PreGetCallback(instance, attribute, message_router_request->service);
+        }
+      }
+
+      switch (attribute_number) {
+        case 4: {
+          /* This attribute is not implemented and only reached by GetAttributesAll. */
+          const size_t kRingSupStructSize = 12u;
+          memset(message_router_response->data, 0, kRingSupStructSize);
+          message_router_response->data += kRingSupStructSize;
+          message_router_response->general_status = kCipErrorSuccess;
+          break;
+        }
+        case 6: /* fall through */
+        case 7: /* fall through */
+        case 10:
+          message_router_response->data_length = EncodeNodeAddress(
+            (CipNodeAddress *)attribute->data,
+            &message);
+          message_router_response->general_status = kCipErrorSuccess;
+          break;
+
+        default:
+          GetAttributeSingle(instance, message_router_request,
+                             message_router_response,
+                             originator_address,
+                             encapsulation_session);
+      }
+
+      /* Call the PostGetCallback if enabled for this attribute and the common handler is not used. */
+      if (!use_common_handler) {
+        if (attribute->attribute_flags & kPostGetFunc && NULL != instance->cip_class->PostGetCallback) {
+          instance->cip_class->PostGetCallback(instance, attribute, message_router_request->service);
+        }
+      }
+
+    }
+  }
+
+  return kEipStatusOkSend;
+}
+
+
+/* ********************************************************************
+ * public functions
+ */
+EipStatus CipDlrInit(void) {
+  CipClass *dlr_class = NULL;
+
+  dlr_class = CreateCipClass(kCipDlrClassCode,
+                             0, /* # class attributes */
+                             7, /* # highest class attribute number */
+                             2, /* # class services */
+                             11,/* # instance attributes */
+                             12,/* # of highest instance attribute */
+                             2, /* # instance services */
+                             1, /* # instances */
+                             "DLR", /* object class name */
+                             DLR_CLASS_REVISION,  /* # class revision */
+                             NULL /* function pointer for initialization */
+                             );
+
+  if (NULL == dlr_class) {
+    return kEipStatusError;
+  }
+
+  /* Add services to the class */
+  InsertService(dlr_class, kGetAttributeSingle,
+                GetAttributeSingleDlr, "GetAttributeSingleDlr");
+  InsertService(dlr_class, kGetAttributeAll,
+                GetAttributeAll, "GetAttributeAll");
+
+  /* Bind attributes to the instance */
+  CipInstance *dlr_instance = GetCipInstance(dlr_class, 1u);
+
+  InsertAttribute(dlr_instance,  1, kCipUsint, &g_dlr.network_topology,
+                  kGetableSingleAndAll);
+  InsertAttribute(dlr_instance,  2, kCipUsint, &g_dlr.network_status,
+                  kGetableSingleAndAll);
+  InsertAttribute(dlr_instance,  3, kCipUsint, (void *)&s_0xFF_default,
+                  kGetableAll);
+  InsertAttribute(dlr_instance,  4, kCipAny, (void *)&s_0x00000000_default,
+                  kGetableAll);
+  InsertAttribute(dlr_instance,  5, kCipUint, (void *)&s_0x0000_default,
+                  kGetableAll);
+  InsertAttribute(dlr_instance,  6, kCipAny, (void *)&s_zero_node,
+                  kGetableAll);
+  InsertAttribute(dlr_instance,  7, kCipAny, (void *)&s_zero_node,
+                  kGetableAll);
+  InsertAttribute(dlr_instance,  8, kCipUint, (void *)&s_0xFFFF_default,
+                  kGetableAll);
+  /* Attribute #9 is not implemented and also NOT part of the GetAttributesAll
+   *  response. Therefore it is not added here! */
+  InsertAttribute(dlr_instance, 10, kCipAny, &g_dlr.active_supervisor_address,
+                  kGetableSingleAndAll);
+  InsertAttribute(dlr_instance, 11, kCipUsint, (void *)&s_0x00_default,
+                  kGetableAll);
+  InsertAttribute(dlr_instance, 12, kCipDword, &g_dlr.capability_flags,
+                  kGetableSingleAndAll);
+
+  /* Set attributes to initial values */
+  /* Assume beacon based DLR device. Also all Revision 3 and higher devices
+   *  are required to support the Flush_Tables and Learning_Update frames
+   *  (see Vol. 2 Section 5-6.2 Revision History of the DLR object).*/
+  g_dlr.capability_flags = (kDlrCapBeaconBased | kDlrCapFlushTableFrame);
+
+  return kEipStatusOk;
+}

+ 84 - 0
source/src/cip/cipdlr.h

@@ -0,0 +1,84 @@
+/******************************************************************************
+ * Copyright (c) 2019, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ *****************************************************************************/
+
+/** @file
+ * @brief Declare public interface of the DLR object
+ *
+ * @author Stefan Maetje <stefan.maetje@esd.eu>
+ *
+ */
+
+#ifndef OPENER_CIPDLR_H_
+#define OPENER_CIPDLR_H_
+
+#include "typedefs.h"
+#include "ciptypes.h"
+
+/** @brief DLR object class code */
+static const CipUint kCipDlrClassCode = 0x47U;
+
+/* ********************************************************************
+ * Type declarations
+ */
+/** @brief Provide bit masks for the Capability Flags attribute (#12)
+ *
+ * The @ref kDlrCapAnnounceBased and @ref kDlrCapBeaconBased capability
+ *  flags are mutually exclusive but one of it must be set.
+ */
+typedef enum {
+  /** Device is an announce based ring node. */
+  kDlrCapAnnounceBased = 0x01,
+  /** Device is a beacon based ring node. */
+  kDlrCapBeaconBased = 0x02,
+  /** The device is capable of providing the supervisor function. */
+  kDlrCapSupervisor = 0x20,
+  /** The device is capable of providing the redundant gateway function. */
+  kDlrCapRedundantGateway = 0x40,
+  /** The device is capable of supporting the Flush_Tables frame. */
+  kDlrCapFlushTableFrame = 0x80,
+} CipDlrCapabilityFlags;
+
+
+/** @brief Node address information for a DLR node
+ *
+ * This is the node address information that uniquely identifies a
+ *  participant of the DLR protocol.
+ */
+typedef struct {
+  CipUdint  device_ip;  /**< IP address of a participating DLR node */
+  CipUsint  device_mac[6]; /**< MAC address of a participating DLR node */
+} CipNodeAddress;
+
+/** @brief Type declaration for the DLR object
+ *
+ * This is the type declaration for the DLR object. It contains only the
+ *  attributes needed for a non supervisor and non redundant gateway
+ *  ring participant.
+ */
+typedef struct {
+  CipUsint  network_topology; /**< Attribute #1: */
+  CipUsint  network_status; /**< Attribute #2: */
+  CipNodeAddress active_supervisor_address; /**< Attribute #10: */
+  CipDword  capability_flags; /**< Attribute #12: */
+} CipDlrObject;
+
+
+/* ********************************************************************
+ * global public variables
+ */
+extern CipDlrObject g_dlr;  /**< declaration of DLR object instance 1 data */
+
+
+/* ********************************************************************
+ * public functions
+ */
+/** @brief Initializing the data structures of the DLR object
+ *
+ * @return kEipStatusOk on success, otherwise kEipStatusError
+ */
+EipStatus CipDlrInit(void);
+
+#endif /* of OPENER_CIPDLR_H_ */

+ 171 - 27
source/src/cip/cipethernetlink.c

@@ -34,7 +34,7 @@
  *  Conditional services are indented and marked with the condition it
  *  depends on like "(0 != OPENER_ETHLINK_CNTRS_ENABLE)"
  *
- *  - GetAttributeAll
+ *  - GetAttributesAll
  *  - GetAttributeSingle
  *    - GetAndClearAttribute  (0 != OPENER_ETHLINK_CNTRS_ENABLE)
  *        This service should only implemented for the attributes 4, 5, 12,
@@ -47,9 +47,6 @@
 #include <string.h>
 
 #include "cipcommon.h"
-#include "cipmessagerouter.h"
-#include "ciperror.h"
-#include "endianconv.h"
 #include "opener_api.h"
 #include "trace.h"
 
@@ -72,15 +69,11 @@
   #define IFACE_LABEL_ACCESS_MODE kGetableAll
 #endif
 
-/** @brief Type definition of the Interface Control attribute (#6)
- *
- *  This is used only internally at the moment.
- */
-typedef struct {
-  CipWord control_bits;
-  CipUint forced_interface_speed;
-} CipEthernetLinkInterfaceControl;
-
+#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && 0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
+  #define IFACE_CTRL_ACCESS_MODE  (kSetAndGetAble | kNvDataFunc)
+#else
+  #define IFACE_CTRL_ACCESS_MODE  kGetableAll
+#endif
 /** @brief Type definition of one entry in the speed / duplex array
  */
 typedef struct speed_duplex_array_entry {
@@ -94,7 +87,7 @@ static int EncodeInterfaceCounters(CipUdint instance_id, CipOctet **message);
 
 static int EncodeMediaCounters(CipUdint instance_id, CipOctet **message);
 
-static int EncodeInterfaceControl(CipOctet **message);
+static int EncodeInterfaceControl(CipUdint instance_id, CipOctet **message);
 
 static int EncodeInterfaceCapability(CipUdint instance_id, CipOctet **message);
 
@@ -117,6 +110,15 @@ EipStatus GetAndClearEthernetLink(
   const int encapsulation_session);
 #endif  /* ... && 0 != OPENER_ETHLINK_CNTRS_ENABLE */
 
+#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && 0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
+EipStatus SetAttributeSingleEthernetLink(
+  CipInstance *instance,
+  CipMessageRouterRequest *message_router_request,
+  CipMessageRouterResponse *message_router_response,
+  const struct sockaddr *originator_address,
+  const int encapsulation_session);
+#endif
+
 
 /** @brief This is the internal table of possible speed / duplex combinations
  *
@@ -184,12 +186,15 @@ static CipUsint dummy_attribute_usint = 0;
 static CipUdint dummy_attribute_udint = 0;
 #endif
 
+#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && 0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
+#else
 /* Constant dummy data for attribute #6 */
-static CipEthernetLinkInterfaceControl interface_control =
+static CipEthernetLinkInterfaceControl s_interface_control =
 {
   .control_bits = 0,
   .forced_interface_speed = 0,
 };
+#endif
 
 /** @brief Definition of the Ethernet Link object instance(s) */
 CipEthernetLinkObject g_ethernet_link[OPENER_ETHLINK_INSTANCE_CNT];
@@ -201,7 +206,8 @@ EipStatus CipEthernetLinkInit(void) {
                                                  2, /* # class services*/
                                                  11, /* # instance attributes*/
                                                  11, /* # highest instance attribute number*/
-                                                 2+OPENER_ETHLINK_CNTRS_ENABLE, /* # instance services*/
+                                                 /* # instance services follow */
+                                                 2+OPENER_ETHLINK_CNTRS_ENABLE+OPENER_ETHLINK_IFACE_CTRL_ENABLE,
                                                  OPENER_ETHLINK_INSTANCE_CNT, /* # instances*/
                                                  "Ethernet Link", /* # class name */
                                                  4, /* # class revision*/
@@ -224,18 +230,28 @@ EipStatus CipEthernetLinkInit(void) {
     g_ethernet_link[idx].interface_caps.capability_bits = kEthLinkCapAutoNeg;
     g_ethernet_link[idx].interface_caps.speed_duplex_selector =
       kEthLinkSpeedDpx_100_FD;
+#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && 0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
+    g_ethernet_link[idx].interface_control.control_bits = kEthLinkIfCntrlAutonegotiate;
+    g_ethernet_link[idx].interface_control.forced_interface_speed = 100;
+#endif
   }
 
   if (ethernet_link_class != NULL) {
     /* add services to the class */
     InsertService(ethernet_link_class, kGetAttributeSingle,
-                  &GetAttributeSingleEthernetLink, "GetAttributeSingle");
+                  &GetAttributeSingleEthernetLink,
+                  "GetAttributeSingleEthernetLink");
     InsertService(ethernet_link_class, kGetAttributeAll, &GetAttributeAll,
                   "GetAttributeAll");
 #if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
     InsertService(ethernet_link_class, kEthLinkGetAndClear,
                   &GetAndClearEthernetLink, "GetAndClear");
 #endif
+#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && 0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
+    InsertService(ethernet_link_class, kSetAttributeSingle,
+                  &SetAttributeSingleEthernetLink,
+                  "SetAttributeSingleEthernetLink");
+#endif
 
     /* bind attributes to the instance */
     for (size_t idx = 0; idx < OPENER_ETHLINK_INSTANCE_CNT; ++idx) {
@@ -259,8 +275,21 @@ EipStatus CipEthernetLinkInit(void) {
       InsertAttribute(ethernet_link_instance, 5, kCipUsint,
                       &dummy_attribute_udint, kGetableAll);
 #endif  /* ... && 0 != OPENER_ETHLINK_CNTRS_ENABLE */
-      InsertAttribute(ethernet_link_instance, 6, kCipUdint, &interface_control,
+#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && 0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
+      if (2 == idx) {
+        /* Interface control of internal switch port is never settable. */
+        InsertAttribute(ethernet_link_instance, 6, kCipAny,
+                        &g_ethernet_link[idx].interface_control,
+                        IFACE_CTRL_ACCESS_MODE & ~kSetable);
+      } else {
+        InsertAttribute(ethernet_link_instance, 6, kCipAny,
+                        &g_ethernet_link[idx].interface_control,
+                        IFACE_CTRL_ACCESS_MODE);
+      }
+#else
+      InsertAttribute(ethernet_link_instance, 6, kCipAny, &s_interface_control,
                       kGetableAll);
+#endif
       InsertAttribute(ethernet_link_instance, 7, kCipUsint,
                       &g_ethernet_link[idx].interface_type, kGetableSingleAndAll);
       InsertAttribute(ethernet_link_instance, 8, kCipUsint,
@@ -326,12 +355,18 @@ static int EncodeMediaCounters(CipUdint instance_id, CipOctet **message) {
   return encoded_len;
 }
 
-static int EncodeInterfaceControl(CipOctet **message) {
+static int EncodeInterfaceControl(CipUdint instance_id, CipOctet **message) {
+#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && 0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
+  CipEthernetLinkInterfaceControl *interface_control =
+  	&g_ethernet_link[instance_id-1].interface_control;
+#else
+  CipEthernetLinkInterfaceControl *interface_control = &s_interface_control;
+#endif
   int encoded_len = 0;
-  encoded_len += EncodeData(kCipWord, &interface_control.control_bits,
+  encoded_len += EncodeData(kCipWord, &interface_control->control_bits,
                             message);
   encoded_len += EncodeData(kCipUint,
-                            &interface_control.forced_interface_speed,
+                            &interface_control->forced_interface_speed,
                             message);
   return encoded_len;
 }
@@ -347,13 +382,13 @@ static int EncodeInterfaceCapability(CipUdint instance_id, CipOctet **message)
   uint16_t selected = g_ethernet_link[instance_id - 1].interface_caps.speed_duplex_selector;
   CipUsint count;
   for (count = 0; selected; count++) { /* count # of bits set */
-	  selected &= selected - 1u;  /* clear the least significant bit set */
+	  selected &= selected - 1U;  /* clear the least significant bit set */
   }
   encoded_len += EncodeData(kCipUsint, &count, message);
 
   for (size_t i = 0; i < NELEMENTS(speed_duplex_table); i++) {
     if (g_ethernet_link[instance_id - 1].interface_caps.speed_duplex_selector &
-        (1u << i)) {
+        (1U << i)) {
       encoded_len += EncodeData(
                         kCipUint,
                         &speed_duplex_table[i].interface_speed,
@@ -399,10 +434,7 @@ EipStatus GetAttributeSingleEthernetLink(
                                                                  attribute_number)
                       ]);
     }
-    OPENER_TRACE_INFO ("Service %" PRIu8 ", get_bit_mask=%02" PRIX8 "\n",
-                       message_router_request->service, get_bit_mask);
     if ( 0 != ( get_bit_mask & ( 1 << (attribute_number % 8) ) ) ) {
-      OPENER_TRACE_INFO("getAttribute %d\n", attribute_number);
 
       /* create a reply message containing the data*/
       bool use_common_handler;
@@ -410,8 +442,9 @@ EipStatus GetAttributeSingleEthernetLink(
       case 4: /* fall through */
       case 5: /* fall through */
       case 6: /* fall through */
-      case 11: /* fall through */
+      case 11:
         use_common_handler = false;
+        OPENER_TRACE_INFO("getAttribute %d\n", attribute_number);
         break;
       default:
         use_common_handler = true;
@@ -440,6 +473,7 @@ EipStatus GetAttributeSingleEthernetLink(
           break;
         case 6:
           message_router_response->data_length = EncodeInterfaceControl(
+            instance->instance_number,
             &message);
           message_router_response->general_status = kCipErrorSuccess;
           break;
@@ -521,3 +555,113 @@ EipStatus GetAndClearEthernetLink(
   return kEipStatusOkSend;
 }
 #endif  /* ... && 0 != OPENER_ETHLINK_CNTRS_ENABLE */
+
+#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && 0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
+static bool IsIfaceControlAllowed
+(
+  CipUdint instance_id,
+  CipEthernetLinkInterfaceControl const *iface_cntrl)
+{
+  const CipUsint duplex_mode =
+       (iface_cntrl->control_bits & kEthLinkIfCntrlForcedDuplex) ? 1 : 0;
+  for (size_t i = 0; i < NELEMENTS(speed_duplex_table); i++) {
+    if (g_ethernet_link[instance_id - 1].interface_caps.speed_duplex_selector &
+        (1U << i)) {
+      if (duplex_mode == speed_duplex_table[i].interface_duplex_mode &&
+          iface_cntrl->forced_interface_speed == speed_duplex_table[i].interface_speed) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+EipStatus SetAttributeSingleEthernetLink(
+  CipInstance *instance,
+  CipMessageRouterRequest *message_router_request,
+  CipMessageRouterResponse *message_router_response,
+  const struct sockaddr *originator_address,
+  const int encapsulation_session) {
+  CipAttributeStruct *attribute = GetCipAttribute(
+    instance, message_router_request->request_path.attribute_number);
+  EipUint16 attribute_number = message_router_request->request_path
+                               .attribute_number;
+
+  if (NULL != attribute) {
+    uint8_t set_bit_mask = (instance->cip_class->set_bit_mask[CalculateIndex(
+                                                                attribute_number)
+                            ]);
+    if ( set_bit_mask & ( 1 << ( (attribute_number) % 8 ) ) ) {
+
+      if (attribute->attribute_flags & kPreSetFunc
+          && instance->cip_class->PreSetCallback) {
+          instance->cip_class->PreSetCallback(instance,
+                                              attribute,
+                                              message_router_request->service);
+      }
+
+      OPENER_TRACE_INFO(" setAttribute %d\n", attribute_number);
+      switch (attribute_number) {
+        case 6: {
+          CipEthernetLinkInterfaceControl if_cntrl;
+          (void)DecodeData(kCipWord, &if_cntrl.control_bits,
+                           &(message_router_request->data));
+          (void)DecodeData(kCipUint, &if_cntrl.forced_interface_speed,
+                           &(message_router_request->data));
+
+          if (if_cntrl.control_bits > kEthLinkIfCntrlMaxValid) {
+            message_router_response->general_status =
+              kCipErrorInvalidAttributeValue;
+
+          } else {
+            if ((0 != (if_cntrl.control_bits & kEthLinkIfCntrlAutonegotiate)) &&
+                ((0 != (if_cntrl.control_bits & kEthLinkIfCntrlForcedDuplex)) ||
+                 (0 != if_cntrl.forced_interface_speed))) {
+              message_router_response->general_status =
+                kCipErrorObjectStateConflict;
+            } else {
+              if (0 == (if_cntrl.control_bits & kEthLinkIfCntrlAutonegotiate)) {
+                /* Need to check if a supported mode is forced. */
+                if (!IsIfaceControlAllowed(instance->instance_number, &if_cntrl)) {
+                  message_router_response->general_status = kCipErrorInvalidAttributeValue;
+                  break;
+                }
+              }
+              CipEthernetLinkInterfaceControl *data = attribute->data;
+              *(data) = if_cntrl;
+              message_router_response->general_status = kCipErrorSuccess;
+            }
+          }
+        }
+        break;
+
+        default:
+          message_router_response->general_status =
+            kCipErrorAttributeNotSetable;
+          break;
+      }
+
+      /* Call the PostSetCallback if enabled. */
+      if (attribute->attribute_flags & (kPostSetFunc | kNvDataFunc)
+          && NULL != instance->cip_class->PostSetCallback) {
+        CipUsint service = message_router_request->service;
+        if (kCipErrorSuccess != message_router_response->general_status) {
+            service |= 0x80;  /* Flag no update, TODO: remove this workaround */
+        }
+        instance->cip_class->PostSetCallback(instance, attribute, service);
+      }
+    } else {
+      message_router_response->general_status = kCipErrorAttributeNotSetable;
+    }
+  } else {
+    /* we don't have this attribute */
+    message_router_response->general_status = kCipErrorAttributeNotSupported;
+  }
+
+  message_router_response->size_of_additional_status = 0;
+  message_router_response->data_length = 0;
+  message_router_response->reply_service = (0x80
+                                            | message_router_request->service);
+  return kEipStatusOkSend;
+}
+#endif

+ 31 - 2
source/src/cip/cipethernetlink.h

@@ -14,12 +14,30 @@
 
 /** @brief This Ethernet Link class code as #define is still needed for a static
  *  initialization. */
-#define CIP_ETHERNETLINK_CLASS_CODE   0xF6u
+#define CIP_ETHERNETLINK_CLASS_CODE   0xF6U
 /** @brief Ethernet Link class code */
 static const CipUint kCipEthernetLinkClassCode = CIP_ETHERNETLINK_CLASS_CODE;
 
 /* public type definitions */
 
+/** @brief Provide values for the Interface Flags (attribute #2) */
+typedef enum {
+  /** Set this bit if your device needs a reset to take over new settings made via
+    * attribute #6. It is duplicates the meaning of kEthLinkCapManualReset */
+  kEthLinkFlagsManualReset = 0x20,
+} CipEthLinkIfaceFlags;
+
+/** @brief Provide values for the Interface Control (attribute #6) Control bits member */
+typedef enum {
+  /** Set this bit to enable Auto-negotiation of ethernet link parameters. */
+  kEthLinkIfCntrlAutonegotiate  = 0x01,
+  /** When Auto-negotiation is disabled set this bit to force Full-Duplex mode else
+   *  Half-Duplex mode is forced. */
+  kEthLinkIfCntrlForcedDuplex = 0x02,
+  /** For convenience declare the sum of valid bits as the maximum allowed value. */
+  kEthLinkIfCntrlMaxValid = kEthLinkIfCntrlAutonegotiate + kEthLinkIfCntrlForcedDuplex,
+} CipEthLinkIfaceControl;
+
 /** @brief Provide values for the Interface Type (attribute #7) */
 typedef enum {
   /** Unknown interface type */
@@ -127,6 +145,14 @@ typedef union {
 } CipEthernetLinkMediaCounters;
 #endif  /* ... && OPENER_ETHLINK_CNTRS_ENABLE != 0 */
 
+/** @brief Type definition of the Interface Control attribute (#6)
+ *
+ */
+typedef struct {
+  CipWord control_bits;
+  CipUint forced_interface_speed;
+} CipEthernetLinkInterfaceControl;
+
 /** @brief Data of an CIP Ethernet Link object */
 typedef struct {
   EipUint32 interface_speed; /**< Attribute #1: 10/100/1000 Mbit/sec */
@@ -136,7 +162,10 @@ typedef struct {
   CipEthernetLinkInterfaceCounters interface_cntrs; /**< Attribute #4: Interface counters 32-bit wide */
   CipEthernetLinkMediaCounters media_cntrs; /**< Attribute #5: Media counters 32-bit wide */
 #endif
-  CipUsint interface_type;  /**< Attribute #7: Type of interface; */
+#if defined(OPENER_ETHLINK_IFACE_CTRL_ENABLE) && 0 != OPENER_ETHLINK_IFACE_CTRL_ENABLE
+  CipEthernetLinkInterfaceControl interface_control;  /** Attribute #6: control link properties */
+#endif
+  CipUsint interface_type;  /**< Attribute #7: Type of interface */
   CipShortString interface_label; /**< Attribute #10: Interface label */
   CipEthernetLinkMetaInterfaceCapability interface_caps; /**< Attribute #11: Interface capabilities */
 } CipEthernetLinkObject;

+ 73 - 29
source/src/cip/cipidentity.c

@@ -51,6 +51,7 @@ CipIdentityObject g_identity =
     OPENER_DEVICE_MINOR_REVISION
   },
   .status = 0, /* Attribute 5: Status */
+  .ext_status = kSelftestingUnknown,  /* Attribute 5: Extended Device Status field */
   .serial_number = 0, /* Attribute 6: Serial Number */
   .product_name = { /* Attribute 7: Product Name */
     sizeof(OPENER_DEVICE_NAME) - 1,
@@ -60,18 +61,69 @@ CipIdentityObject g_identity =
 };
 
 
-/** Private functions, sets the devices serial number
- * @param serial_number The serial number of the device
- */
+/* The Doxygen comment is with the function's prototype in opener_api.h. */
 void SetDeviceSerialNumber(const EipUint32 serial_number) {
   g_identity.serial_number = serial_number;
 }
 
-/** @brief Private function, sets the devices status
- * @param status The serial number of the device
- */
-void SetDeviceStatus(const EipUint16 status) {
+/* The Doxygen comment is with the function's prototype in opener_api.h. */
+void SetDeviceStatus(const CipWord status) {
   g_identity.status = status;
+  g_identity.ext_status = status & kExtStatusMask;
+}
+
+static inline void MergeStatusAndExtStatus(void)
+{
+  CipWord status_flags = g_identity.status & (~kExtStatusMask);
+  CipWord ext_status = g_identity.ext_status & kExtStatusMask;
+
+  /* Any major fault will override the current extended status with kMajorFault.
+    See comment on Major Fault at Vol. 1, Table 5A-2.4. */
+  if(0 != (status_flags & (kMajorRecoverableFault | kMajorUnrecoverableFault))) {
+    ext_status = kMajorFault;
+  }
+  g_identity.status = status_flags | ext_status;
+}
+
+/** @brief Set status flags of the device's Status word
+ *
+ * @param status_flags  flags to set in the Status word
+ *
+ *  This function sets status flags of the device's Status word and combines
+ *  the flag values with the internal ext_status member into a new Status
+ *  value.
+ */
+void CipIdentitySetStatusFlags(const CipWord status_flags) {
+  g_identity.status |= status_flags & (~kExtStatusMask);
+  MergeStatusAndExtStatus();
+}
+
+/** @brief Clear status flags of the device's Status word
+ *
+ * @param status_flags  flags to clear in the Status word
+ *
+ *  This function clears status flags of the device's Status word and combines
+ *  the flag values with the internal ext_status member into a new Status
+ *  value.
+ */
+void CipIdentityClearStatusFlags(const CipWord status_flags) {
+  g_identity.status &= ~(status_flags & (~kExtStatusMask));
+  MergeStatusAndExtStatus();
+}
+
+/** @brief Set the device's Extended Device Status field in the Status word
+ *
+ * @param   extended_status Extended Device Status field
+ *
+ *  This function sets the internal ext_status member of the Identity object
+ *  and combines its value depending on the other Status flags into a new
+ *  Status value.
+ */
+void CipIdentitySetExtendedDeviceStatus(
+  CipIdentityExtendedStatus extended_status) {
+  OPENER_TRACE_INFO("Setting extended status: %x\n", extended_status);
+  g_identity.ext_status = extended_status & kExtStatusMask;
+  MergeStatusAndExtStatus();
 }
 
 /** @brief Reset service
@@ -97,37 +149,36 @@ static EipStatus Reset(CipInstance *instance,
   message_router_response->size_of_additional_status = 0;
   message_router_response->general_status = kCipErrorSuccess;
 
-  if (message_router_request->request_path_size == 1) {
-    switch (message_router_request->data[0]) {
-      case 0: /* Reset type 0 -> emulate device reset / Power cycle */
+  if (message_router_request->request_path_size > 1) {
+    message_router_response->general_status = kCipErrorTooMuchData;
+  }
+  else {
+    CipOctet reset_type = 0;  /* The default type if type parameter was omitted. */
+    if (message_router_request->request_path_size == 1) {
+      reset_type = message_router_request->data[0];
+    }
+    switch (reset_type) {
+      case 0: /* Reset type 0 -> Emulate power cycle */
         if ( kEipStatusError == ResetDevice() ) {
           message_router_response->general_status = kCipErrorInvalidParameter;
         }
         break;
 
-      case 1: /* Reset type 1 -> reset to device settings */
+      case 1: /* Reset type 1 -> Return to factory defaults & power cycle*/
         if ( kEipStatusError == ResetDeviceToInitialConfiguration() ) {
           message_router_response->general_status = kCipErrorInvalidParameter;
         }
         break;
 
-      /* case 2: Not supported Reset type 2 -> Return to factory defaults except communications parameters */
+      /* case 2: Not supported Reset type 2 ->
+        Return to factory defaults except communications parameters & power cycle*/
 
       default:
         message_router_response->general_status = kCipErrorInvalidParameter;
         break;
     }
-  } else /*TODO: Should be if (pa_stMRRequest->DataLength == 0)*/
-  {
-    /* The same behavior as if the data value given would be 0
-       emulate device reset */
-
-    if ( kEipStatusError == ResetDevice() ) {
-      message_router_response->general_status = kCipErrorInvalidParameter;
-    } else {
-      /* eip_status = EIP_OK; */
-    }
   }
+
   message_router_response->data_length = 0;
   return eip_status;
 }
@@ -209,10 +260,3 @@ EipStatus CipIdentityInit() {
 
   return kEipStatusOk;
 }
-
-void CipIdentitySetExtendedDeviceStatus(
-  CipIdentityExtendedStatus extended_status) {
-  OPENER_TRACE_INFO("Setting extended status: %x\n", extended_status);
-  g_identity.status &= ~(0x70);
-  g_identity.status |= extended_status;
-}

+ 25 - 10
source/src/cip/cipidentity.h

@@ -10,7 +10,7 @@
 #include "ciptypes.h"
 
 /** @brief Identity class code */
-static const CipUint kCipIdentityClassCode = 0x01u;
+static const CipUint kCipIdentityClassCode = 0x01U;
 
 /** @brief Status of the CIP Identity object */
 typedef enum {
@@ -33,16 +33,28 @@ typedef enum {
 
 /** @brief Constants for the extended status field in the Status word */
 typedef enum {
-  kSelftestingUnknown = 0x0000,
-  kFirmwareUpdateInProgress = 0x0010,
-  kStatusAtLeastOneFaultedIoConnection = 0x0020,
-  kNoIoConnectionsEstablished = 0x0030,
-  kNonVolatileConfigurationBad = 0x0040,
-  kMajorFault = 0x0050,
-  kAtLeastOneIoConnectionInRunMode = 0x0060,
-  kAtLeastOneIoConnectionEstablishedAllInIdleMode = 0x0070
+  kSelftestingUnknown = 0x0000U,
+  kFirmwareUpdateInProgress = 0x0010U,
+  kStatusAtLeastOneFaultedIoConnection = 0x0020U,
+  kNoIoConnectionsEstablished = 0x0030U,
+  kNonVolatileConfigurationBad = 0x0040U,
+  kMajorFault = 0x0050U,
+  kAtLeastOneIoConnectionInRunMode = 0x0060U,
+  kAtLeastOneIoConnectionEstablishedAllInIdleMode = 0x0070U,
+  kExtStatusMask = 0x00F0U
 } CipIdentityExtendedStatus;
 
+/** @brief Constants for the state member of the Identity object. */
+typedef enum {
+  kStateNonExistent = 0U,
+  kStateSelfTesting = 1U,
+  kStateStandby = 2U,
+  kStateOperational = 3U,
+  kStateMajorRecoverableFault = 4U,
+  kStateMajorUnrecoverableFault = 5U,
+  kStateDefault = 255U
+} CipIdentityState;
+
 /** @brief Declaration of the Identity object's structure type
  */
 typedef struct {
@@ -51,9 +63,10 @@ typedef struct {
   CipUint product_code; /**< Attribute 3: Product Code */
   CipRevision revision; /**< Attribute 4: Revision / CipUsint Major, CipUsint Minor */
   CipWord status; /**< Attribute 5: Status */
+  CipWord ext_status;   /**< Attribute 5: last set extended status, needed for Status handling */
   CipUdint serial_number; /**< Attribute 6: Serial Number, has to be set prior to OpENer's network initialization */
   CipShortString product_name; /**< Attribute 7: Product Name */
-  CipUsint state; /** Attribute 8: state */
+  CipUsint state; /** Attribute 8: state, this member could control the Module Status LED blink pattern */
 } CipIdentityObject;
 
 
@@ -68,6 +81,8 @@ CipIdentityObject g_identity;
  */
 EipStatus CipIdentityInit(void);
 
+void CipIdentitySetStatusFlags(const CipWord status_flags);
+void CipIdentityClearStatusFlags(const CipWord status_flags);
 void CipIdentitySetExtendedDeviceStatus(
   CipIdentityExtendedStatus extended_status);
 

+ 5 - 4
source/src/cip/cipioconnection.c

@@ -447,8 +447,9 @@ EipStatus OpenProducingMulticastConnection(
   CipConnectionObject *connection_object,
   CipCommonPacketFormatData *common_packet_format_data
   ) {
+  /* Here we look for existing multi-cast IO connections only. */
   CipConnectionObject *existing_connection_object =
-    GetExistingProducerMulticastConnection(
+    GetExistingProducerIoConnection(true,
       connection_object->produced_path.instance_id);
 
   int j = 0; /* allocate an unused sockaddr struct to use */
@@ -726,8 +727,8 @@ void CloseIoConnection(CipConnectionObject *connection_object) {
 }
 
 void HandleIoConnectionTimeOut(CipConnectionObject *connection_object) {
-  CheckIoConnectionEvent(connection_object->produced_path.instance_id,
-                         connection_object->consumed_path.instance_id,
+  CheckIoConnectionEvent(connection_object->consumed_path.instance_id,
+                         connection_object->produced_path.instance_id,
                          kIoConnectionEventTimedOut);
 
   ConnectionObjectSetState(connection_object, kConnectionObjectStateTimedOut);
@@ -822,7 +823,7 @@ EipStatus SendConnectedData(CipConnectionObject *connection_object) {
   common_packet_format_data->address_info_item[0].type_id = 0;
   common_packet_format_data->address_info_item[1].type_id = 0;
 
-  ENIPMessage outgoing_message = {0};
+  ENIPMessage outgoing_message;
   InitializeENIPMessage(&outgoing_message);
   EipUint16 reply_length = AssembleIOMessage(common_packet_format_data,
                                              &outgoing_message);

+ 1 - 1
source/src/cip/cipmessagerouter.h

@@ -10,7 +10,7 @@
 #include "ciptypes.h"
 
 /** @brief Message Router class code */
-static const CipUint kCipMessageRouterClassCode = 0x02u;
+static const CipUint kCipMessageRouterClassCode = 0x02U;
 
 /** @brief Structure for storing the Response generated by an explict message.
  *

+ 20 - 10
source/src/cip/cipqos.c

@@ -15,13 +15,13 @@
 #include "opener_api.h"
 #include "trace.h"
 
-#define DEFAULT_DSCP_EVENT 59u
-#define DEFAULT_DSCP_GENERAL 47u
-#define DEFAULT_DSCP_URGENT 55u
-#define DEFAULT_DSCP_SCHEDULED 47u
-#define DEFAULT_DSCP_HIGH 43u
-#define DEFAULT_DSCP_LOW 31u
-#define DEFAULT_DSCP_EXPLICIT 27u
+#define DEFAULT_DSCP_EVENT 59U
+#define DEFAULT_DSCP_GENERAL 47U
+#define DEFAULT_DSCP_URGENT 55U
+#define DEFAULT_DSCP_SCHEDULED 47U
+#define DEFAULT_DSCP_HIGH 43U
+#define DEFAULT_DSCP_LOW 31U
+#define DEFAULT_DSCP_EXPLICIT 27U
 
 /** @brief The QoS object
  *
@@ -88,7 +88,7 @@ EipStatus SetAttributeSingleQoS(
     CipUsint attribute_value_received = GetUsintFromMessage(
       &(message_router_request->data) );
 
-    if( attribute_value_received < 64u ) {
+    if( attribute_value_received < 64U ) {
       OPENER_TRACE_INFO(" setAttribute %d\n", attribute_number);
 
       if(NULL != attribute->data) {
@@ -132,7 +132,7 @@ EipStatus SetAttributeSingleQoS(
 
 CipUsint CipQosGetDscpPriority(ConnectionObjectPriority priority) {
 
-  CipUsint priority_value = s_active_dscp.explicit;
+  CipUsint priority_value;
   switch (priority) {
     case kConnectionObjectPriorityLow:
       priority_value = s_active_dscp.low;
@@ -146,7 +146,7 @@ CipUsint CipQosGetDscpPriority(ConnectionObjectPriority priority) {
     case kConnectionObjectPriorityUrgent:
       priority_value = s_active_dscp.urgent;
       break;
-    case kConnectionObjectPriorityExplicit: /* Fall-through wanted here */
+    case kConnectionObjectPriorityExplicit: /* fall through */
     default:
       priority_value = s_active_dscp.explicit;
       break;
@@ -154,7 +154,17 @@ CipUsint CipQosGetDscpPriority(ConnectionObjectPriority priority) {
   return priority_value;
 }
 
+/** @brief Class level attribute initializer for QoS class
+ *
+ * This function is provided as class level attribute initializer to
+ *  @ref CreateCipClass. It replaces the default initialization of
+ *  @ref CreateCipClass.
+ * Because we do NOT initialize anything here all the class attributes
+ *  which are optional for the QoS class are NOT created!
+ *  This is intended.
+ */
 void InitializeCipQos(CipClass *class) {
+  /* Function is empty by intend. */
 }
 
 EipStatus CipQoSInit() {

+ 1 - 1
source/src/cip/cipqos.h

@@ -17,7 +17,7 @@
 #include "cipconnectionmanager.h"
 
 /** @brief QoS Object class code */
-static const CipUint kCipQoSClassCode = 0x48u;
+static const CipUint kCipQoSClassCode = 0x48U;
 
 /* public types */
 

+ 2 - 2
source/src/cip/cipstring.c

@@ -9,11 +9,11 @@
  *
  * Some functions to create CIP string types from C strings or data buffers.
  */
+#include "cipstring.h"
+
 #include <stdlib.h>
 #include <string.h>
 
-#include "cipstring.h"
-
 #include "trace.h"
 #include "opener_api.h"
 

+ 34 - 12
source/src/cip/ciptcpipinterface.c

@@ -21,10 +21,10 @@
 
 /* Define constants to initialize the config_capability attribute (#2). These
 *   are needed as defines because we use them for static initialization. */
-#define CFG_CAPS_DHCP_CLIENT          0x04u /**< Device has DHCP client */
-#define CFG_CAPS_CFG_SETTABLE         0x10u /**< Interface configuration can be set */
-#define CFG_CAPS_CFG_CHG_NEEDS_RESET  0x40u /**< Interface configuration change needs RESET */
-#define CFG_CAPS_ACD_CAPABLE          0x80u /**< Device supports ACD */
+#define CFG_CAPS_DHCP_CLIENT          0x04U /**< Device has DHCP client */
+#define CFG_CAPS_CFG_SETTABLE         0x10U /**< Interface configuration can be set */
+#define CFG_CAPS_CFG_CHG_NEEDS_RESET  0x40U /**< Interface configuration change needs RESET */
+#define CFG_CAPS_ACD_CAPABLE          0x80U /**< Device supports ACD */
 
 /* OPENER_TCPIP_IFACE_CFG_SETTABLE controls if the interface configuration is fully settable.
 *   Prepare additional defines needed here:
@@ -170,7 +170,7 @@ static bool IsValidDomain(EipByte *domain)
 }
 
 
-/** Check if a IP address is a valid network mask
+/** Check if an IP address is a valid network mask
  *
  *  @param  netmask network mask in network byte order
  *  @return         valid status
@@ -189,7 +189,7 @@ static bool IsValidNetmask(in_addr_t netmask)
     return valid && (INADDR_BROADCAST != netmask);
 }
 
-/** Check if a IP address is in one of the network classes A, B or C
+/** Check if an IP address is in one of the network classes A, B or C
  *
  *  @param  ip_addr IP address in network byte order
  *  @return         status
@@ -202,7 +202,7 @@ static bool IsInClassAbc(in_addr_t ip_addr)
   return IN_CLASSA(ip) || IN_CLASSB(ip) || IN_CLASSC(ip);
 }
 
-/** Check if a IP address is on the loopback network
+/** Check if an IP address is on the loopback network
  *
  *  @param  ip_addr IP address in network byte order
  *  @return         status
@@ -216,6 +216,21 @@ static bool IsOnLoopbackNetwork(in_addr_t ip_addr)
   return (ip & IN_CLASSA_NET) == (INADDR_LOOPBACK & IN_CLASSA_NET);
 }
 
+/** Check if an IP address is either the network or the broadcast address
+ *
+ *  @param  ip_addr   IP address in network byte order
+ *  @param  net_mask  network mask in network byte order
+ *  @return           status
+ *
+ * Check if an IP address is either the network or the broadcast address.
+ *  In this case it is not a valid IP address for a host.
+ *  This check is endian agnostic.
+ */
+static bool IsNetworkOrBroadcastIp(in_addr_t ip_addr, in_addr_t net_mask) {
+  return  ((ip_addr & net_mask) == ip_addr) ||  /* is network address */
+          ((ip_addr | ~net_mask) == ip_addr);   /* is broadcast address */
+}
+
 /** Check the Interface configuration being valid according to EIP specification
  *
  * In Vol. 2 the "Table 5-4.3 Instance Attributes" provides some information
@@ -228,13 +243,14 @@ static bool IsOnLoopbackNetwork(in_addr_t ip_addr)
  *  - MASK: IP is a valid network mask
  *  -  ABC: IP is in class A, B or C
  *  - NLCL: IP is not localhost aka. INADDR_LOOPBACK
+ *  -   NB: IP is neither network or broadcast address (using network_mask)
  *
  * This is the table which checks are applied to what IP:
- *  N0 | MASK | ABC | NLCL | IP address
- *   + |   -  |  +  |   +  | ip_address
- *   - |   +  |  -  |   -  | network_mask
- *   - |   -  |  +  |   +  | gateway
- *   - |   -  |  +  |   -  | name_server / name_server_2
+ *  N0 | MASK | ABC | NLCL | NB | IP address
+ *   + |   -  |  +  |   +  |  + | ip_address
+ *   - |   +  |  -  |   -  |  - | network_mask
+ *   - |   -  |  +  |   +  |  + | gateway
+ *   - |   -  |  +  |   -  |  - | name_server / name_server_2
  * A configured gateway must be reachable according to the network mask.
  */
 static bool IsValidNetworkConfig(const CipTcpIpInterfaceConfiguration *if_cfg)
@@ -256,6 +272,12 @@ static bool IsValidNetworkConfig(const CipTcpIpInterfaceConfiguration *if_cfg)
       IsOnLoopbackNetwork(if_cfg->gateway)) {
     return false;
   }
+  /* Check NB */
+  if (IsNetworkOrBroadcastIp(if_cfg->ip_address, if_cfg->network_mask) ||
+      (INADDR_ANY != ntohl(if_cfg->gateway) &&
+       IsNetworkOrBroadcastIp(if_cfg->gateway, if_cfg->network_mask))) {
+    return false;
+  }
   if (INADDR_ANY != ntohl(if_cfg->gateway) &&
       INADDR_ANY != ntohl(if_cfg->network_mask)) {
     /* gateway is configured. Check if it is reachable. */

+ 9 - 9
source/src/cip/ciptcpipinterface.h

@@ -15,23 +15,23 @@
 #include "ciptypes.h"
 
 /** @brief TCP/IP Interface class code */
-static const CipUint kCipTcpIpInterfaceClassCode = 0xF5u;
+static const CipUint kCipTcpIpInterfaceClassCode = 0xF5U;
 
 /* Declare constants for status attribute (#1) */
 /** Indicates a pending configuration change in the TTL Value and/or Mcast Config attributes.*/
-static const CipDword kTcpipStatusMcastPend = 0x10u;
+static const CipDword kTcpipStatusMcastPend = 0x10U;
 /** Indicates a pending configuration change in the Interface Configuration attribute. */
-static const CipDword kTcpipStatusIfaceCfgPend = 0x20u;
+static const CipDword kTcpipStatusIfaceCfgPend = 0x20U;
 /** Indicates when an IP address conflict has been detected by ACD. */
-static const CipDword kTcpipStatusAcdStatus = 0x40u;
+static const CipDword kTcpipStatusAcdStatus = 0x40U;
 /** Indicates when an IP address conflict has been detected by ACD or the defense failed. */
-static const CipDword kTcpipStatusAcdFault = 0x80u;
+static const CipDword kTcpipStatusAcdFault = 0x80U;
 
 /* Declare constants for config_control attribute (#3) */
-static const CipDword kTcpipCfgCtrlStaticIp   = 0x00u;  /**< IP configuration method is manual IP assignment */
-static const CipDword kTcpipCfgCtrlDhcp       = 0x02u;  /**< IP configuration method is DHCP */
-static const CipDword kTcpipCfgCtrlMethodMask = 0x0fu;  /**< bit mask for the method field */
-static const CipDword kTcpipCfgCtrlDnsEnable  = 0x10u;  /**< enables DNS resolution on originator devices */
+static const CipDword kTcpipCfgCtrlStaticIp   = 0x00U;  /**< IP configuration method is manual IP assignment */
+static const CipDword kTcpipCfgCtrlDhcp       = 0x02U;  /**< IP configuration method is DHCP */
+static const CipDword kTcpipCfgCtrlMethodMask = 0x0FU;  /**< bit mask for the method field */
+static const CipDword kTcpipCfgCtrlDnsEnable  = 0x10U;  /**< enables DNS resolution on originator devices */
 
 /** @brief Multicast Configuration struct, called Mcast config
  *

+ 47 - 20
source/src/opener_api.h

@@ -19,39 +19,63 @@
  */
 
 /** @ingroup CIP_API
- * @brief Configure the data of the network interface of the device
+ * @brief Read network configuration data from specified hardware interface
  *
- *  This function setup the data of the network interface needed by OpENer.
- *  The multicast address is automatically calculated from he given data.
+ *  @param  iface     address of string specifying the hardware interface
+ *  @param  iface_cfg address of interface configuration structure
+ *  @return           kEipStatusOk on success,
+ *                    kEipStatusError on error with @p errno set
+ *
+ * This function reads all information needed to fill the iface_cfg structure
+ *  of type @ref CipTcpIpInterfaceConfiguration from the hardware interface
+ *  specified by the iface string.
  *
- *  @param ip_address    the current IP address of the device
- *  @param subnet_mask  the subnet mask to be used
- *  @param gateway_address     the gateway address
- *  @return kEipStatusOk if the configuring worked otherwise kEipStatusError
  */
-EipStatus
-ConfigureNetworkInterface(const char *const network_interface);
+EipStatus IfaceGetConfiguration
+(
+  const char *iface,
+  CipTcpIpInterfaceConfiguration *iface_cfg
+);
 
 /** @ingroup CIP_API
  * @brief Read and return the MAC address of the Ethernet interface
  *
  *  @param  iface             string of interface name or interface index
  *  @param  physical_address  hardware MAC address of the network interface
- *  @return                     kEipStatusOk: all fine
- *                              kEipStatusError: failure, errno set
+ *  @return                   kEipStatusOk: all fine
+ *                            kEipStatusError: failure, errno set
  */
 EipStatus IfaceGetMacAddress(const char *iface, uint8_t *const physical_address);
 
 /** @ingroup CIP_API
- * @brief Configure the domain name of the device
- * @param domain_name the domain name to be used
+ * @brief Wait for the network interface having an IP address
+ *
+ * @param timeout in seconds; max: INT_MAX/10, -1: wait for ever
+ * @param do_run  stop waiting if this parameter becomes zero
+ * @return        kEipStatusOk on success,
+ *                kEipStatusError on error with @p errno set
+ *
+ * This function waits for the network interface getting an IP address but
+ *  only @p timeout seconds (set to -1 to wait for ever).
+ * The polling wait process can be aborted by setting @p abort_wait to
+ *  a non zero value from another thread.
  */
-void ConfigureDomainName(void);
+EipStatus IfaceWaitForIp
+(
+  const char *iface,
+  int timeout,
+  volatile int *abort_wait
+);
+
 /** @ingroup CIP_API
- * @brief Configure the host name of the device
- * @param host_name the host name to be used
+ * @brief Get host name from platform
+ *
+ * @param hostname  address of CipString destination structure
+ *
+ * This function reads the host name from the platform and returns it
+ *  via the hostname parameter.
  */
-void ConfigureHostName(void);
+void GetHostName(CipString *hostname);
 
 /** @ingroup CIP_API
  * @brief Set the serial number of the device's identity object.
@@ -61,11 +85,14 @@ void ConfigureHostName(void);
 void SetDeviceSerialNumber(const EipUint32 serial_number);
 
 /** @ingroup CIP_API
- * @brief Set the current status of the device.
+ * @brief Set the device's Status word also updating the Extended Device Status
+ *
+ * @param status    complete Identity Object's Status word content
  *
- * @param status new Identity object's Status value
+ *  This function sets the status flags and the internal state of the Extended
+ *  Device Status field in Identity object's ext_status member.
  */
-void SetDeviceStatus(const EipUint16 status);
+void SetDeviceStatus(const CipWord status);
 
 /** @ingroup CIP_API
  * @brief Initialize and setup the CIP-stack

+ 1 - 1
source/src/ports/MINGW/CMakeLists.txt

@@ -18,7 +18,7 @@ add_library( ${PLATFORMLIBNAME} ${PLATFORM_SPEC_SRC})
 
 add_executable(OpENer main.c)
 
-target_link_libraries( OpENer PLATFORM_GENERIC ${PLATFORMLIBNAME} CIP Utils SAMPLE_APP ENET_ENCAP NVDATA ws2_32 IPHLPAPI ${OpENer_CIP_OBJECTS} )
+target_link_libraries( OpENer PLATFORM_GENERIC ${PLATFORMLIBNAME} CIP Utils SAMPLE_APP ENET_ENCAP NVDATA ws2_32 iphlpapi ${OpENer_CIP_OBJECTS} )
 
 # Add additional CIP Objects
 string(COMPARE NOTEQUAL "${OpENer_ADD_CIP_OBJECTS}" "" OpENer_HAS_ADDITIONAL_OBJECT )

+ 129 - 55
source/src/ports/MINGW/main.c

@@ -3,101 +3,175 @@
  * All rights reserved.
  *
  ******************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
 
 #include "generic_networkhandler.h"
 #include "opener_api.h"
-#include "cipcommon.h"
+#include "cipethernetlink.h"
+#include "ciptcpipinterface.h"
 #include "trace.h"
 #include "networkconfig.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
+#include "doublylinkedlist.h"
+#include "cipconnectionobject.h"
 #include "nvdata.h"
 
-extern int newfd;
+#define BringupNetwork(if_name, method, if_cfg, hostname)  (0)
+#define ShutdownNetwork(if_name)  (0)
+
+/** If OpENer is aborted by a signal it returns the sum of the signal number
+ *  and this define. */
+#define RET_SHOW_SIGNAL 200
+
+/******************************************************************************/
+/** @brief Signal handler function for ending stack execution
+ *
+ * @param signal  the signal we received
+ */
+static void LeaveStack(int signal);
 
 /******************************************************************************/
-/*!\brief Signal handler function for ending stack execution
+/** @brief Execute OpENer stack loop function
+ *
+ * @param   thread_arg  dummy argument
+ * @returns             NO_ERROR at the moment
  *
- * @param pa_nSig the signal we received
+ *  The call signature is chosen to be able to pass this function directly as
+ *  parameter for CreateThread().
  */
-void
-LeaveStack(int pa_nSig);
+static DWORD executeEventLoop(LPVOID thread_arg);
+
 
 /*****************************************************************************/
-/*! \brief Flag indicating if the stack should end its execution
+/** @brief Flag indicating if the stack should end its execution
  */
-int g_end_stack = 0;
+volatile int g_end_stack = 0;
 
 /******************************************************************************/
-int main(int argc,
-         char *arg[]) {
-  EipUint16 nUniqueConnectionID;
+int main(int argc, char *arg[]) {
 
   if (argc != 2) {
-    printf("Wrong number of command line parameters!\n");
-    printf("The correct command line parameters are:\n");
-    printf(
-      "./OpENer ip-address\n");
-    printf(
-      "    e.g. ./OpENer 192.168.259.22\n");
-    exit(0);
-  } else {
-    DoublyLinkedListInitialize(&connection_list,
-                               CipConnectionObjectListArrayAllocator,
-                               CipConnectionObjectListArrayFree);
-    /* fetch Internet address info from the platform */
-    ConfigureDomainName();
-    ConfigureHostName();
-    ConfigureNetworkInterface(arg[1] );
+    fprintf(stderr, "Wrong number of command line parameters!\n");
+    fprintf(stderr, "Usage: %s [interface index | interface name]\n", arg[0]);
+    fprintf(stderr, "\te.g. ./OpENer \"Ethernet 2\"\n");
+    exit(EXIT_FAILURE);
   }
 
-  /*for a real device the serial number should be unique per device */
+  DoublyLinkedListInitialize(&connection_list,
+                             CipConnectionObjectListArrayAllocator,
+                             CipConnectionObjectListArrayFree);
+  /* Fetch MAC address from the platform. This tests also if the interface
+   *  is present. */
+  uint8_t iface_mac[6];
+  if (kEipStatusError == IfaceGetMacAddress(arg[1], iface_mac)) {
+    printf("Network interface %s not found.\n", arg[1]);
+    exit(EXIT_FAILURE);
+  }
+
+  /* for a real device the serial number should be unique per device */
   SetDeviceSerialNumber(123456789);
 
-  /* nUniqueConnectionID should be sufficiently random or incremented and stored
+  /* unique_connection_id should be sufficiently random or incremented and stored
    *  in non-volatile memory each time the device boots.
    */
-  nUniqueConnectionID = rand();
+  EipUint16 unique_connection_id = rand();
+
+  /* Setup the CIP Layer. All objects are initialized with the default
+   * values for the attribute contents. */
+  CipStackInit(unique_connection_id);
 
-  /* Setup the CIP Layer */
-  CipStackInit(nUniqueConnectionID);
+  CipEthernetLinkSetMac(iface_mac);
+
+  /* The current host name is used as a default. This value is kept in the
+   *  case NvdataLoad() needs to recreate the TCP/IP object's settings from
+   *  the defaults on the first start without a valid TCP/IP configuration
+   *  file.
+   */
+  GetHostName(&g_tcpip.hostname);
 
   /* The CIP objects are now created and initialized with their default values.
-   *  Now any NV data values are loaded to change the data to the stored
-   *  configuration.
+   *  After that any NV data values are loaded to change the attribute contents
+   *  to the stored configuration.
    */
   if (kEipStatusError == NvdataLoad()) {
     OPENER_TRACE_WARN("Loading of some NV data failed. Maybe the first start?\n");
   }
 
-  /* Setup Network Handles */
-  if ( kEipStatusOk == NetworkHandlerInitialize() ) {
-    g_end_stack = 0;
-#ifndef WIN32
-    /* register for closing signals so that we can trigger the stack to end */
-    signal(SIGHUP, LeaveStack);
-    signal(SIGINT, LeaveStack); /* needed to be able to abort with ^C */
-#endif
-
-    /* The event loop. Put other processing you need done continually in here */
-    while (1 != g_end_stack) {
-      if ( kEipStatusOk != NetworkHandlerProcessOnce() ) {
-        break;
+  /* Bring up network interface or start DHCP client ... */
+  EipStatus status = BringupNetwork(arg[1],
+                       g_tcpip.config_control,
+                       &g_tcpip.interface_configuration,
+                       &g_tcpip.hostname);
+  if (status < 0) {
+    OPENER_TRACE_ERR("BringUpNetwork() failed\n");
+  }
+
+  /* Register signal handler for SIGINT and SIGTERM that are "supported"
+   *  under Windows. */
+  g_end_stack = 0;
+  signal(SIGINT, LeaveStack);
+  signal(SIGTERM, LeaveStack);
+
+  /* Next actions depend on the set network configuration method. */
+  CipDword network_config_method = g_tcpip.config_control & kTcpipCfgCtrlMethodMask;
+  if (kTcpipCfgCtrlStaticIp == network_config_method) {
+    OPENER_TRACE_INFO("Static network configuration done\n");
+  }
+  if (kTcpipCfgCtrlDhcp == network_config_method) {
+    OPENER_TRACE_INFO("DHCP network configuration started\n");
+    /* DHCP should already have been started with BringupNetwork(). Wait
+     * here for IP present (DHCP done) or abort through g_end_stack. */
+    status = IfaceWaitForIp(arg[1], -1, &g_end_stack);
+    OPENER_TRACE_INFO("DHCP wait for interface: status %d, g_end_stack=%d\n",
+                      status, g_end_stack);
+    if (kEipStatusOk == status && 0 == g_end_stack) {
+      /* Read IP configuration received via DHCP from interface and store in
+       *  the TCP/IP object.*/
+      status = IfaceGetConfiguration(arg[1], &g_tcpip.interface_configuration);
+      if (status < 0) {
+        OPENER_TRACE_WARN("Problems getting interface configuration\n");
       }
     }
+  }
+
+
+  /* The network initialization of the EIP stack for the NetworkHandler. */
+  if (!g_end_stack && kEipStatusOk == NetworkHandlerInitialize()) {
+
+    (void) executeEventLoop(NULL);
 
     /* clean up network state */
     NetworkHandlerFinish();
   }
-  /* close remaining sessions and connections, cleanup used data */
+
+  /* close remaining sessions and connections, clean up used data */
   ShutdownCipStack();
 
-  return -1;
+  /* Shut down the network interface now. */
+  (void) ShutdownNetwork(arg[1]);
+
+  if(0 != g_end_stack) {
+    printf("OpENer aborted by signal %d.\n", g_end_stack);
+    return RET_SHOW_SIGNAL+g_end_stack;
+  }
+
+  return EXIT_SUCCESS;
+}
+
+static void LeaveStack(int signal) {
+  if (SIGINT == signal || SIGTERM == signal) {
+    g_end_stack = signal;
+  }
+  OPENER_TRACE_STATE("got signal %d\n", signal);
 }
 
-void LeaveStack(int pa_nSig) {
-  (void) pa_nSig; /* kill unused parameter warning */
-  OPENER_TRACE_STATE("got signal %d\n",pa_nSig);
-  g_end_stack = 1;
+static DWORD executeEventLoop(LPVOID thread_arg) {
+  /* The event loop. Put other processing you need done continually in here */
+  while (0 == g_end_stack) {
+    if ( kEipStatusOk != NetworkHandlerProcessOnce() ) {
+      break;
+    }
+  }
+  return NO_ERROR;
 }

+ 413 - 174
source/src/ports/MINGW/networkconfig.c

@@ -3,207 +3,455 @@
  * All rights reserved.
  *
  ******************************************************************************/
+
+/* ---------- Include files ---------------------------- */
 #define WIN32_LEAN_AND_MEAN
-#include "ciptcpipinterface.h"
 #include "networkconfig.h"
-#include "cipcommon.h"
-#include "ciperror.h"
-#include "opener_api.h"
-#include "trace.h"
-#include "cipethernetlink.h"
+
+#include <errno.h>
+#include <inttypes.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+
 #include <winsock2.h>
 #include <windows.h>
-#include <Ws2tcpip.h>
+#include <ws2tcpip.h>
 #include <iphlpapi.h>
 
-#define WORKING_BUFFER_SIZE 15000
-#define MAX_TRIES 3
+#include "cipcommon.h"
+#include "cipstring.h"
+#include "opener_api.h"
+#include "opener_error.h"
+#include "trace.h"
 
 
-EipStatus ConfigureNetworkInterface(const char *const network_interface) {
+/* ---------- Macro definitions ------------------------ */
+#define MALLOC(x) malloc(x)
+#define FREE(x)   free(x)
 
-  PIP_ADAPTER_INFO pAdapterInfo;
-  PIP_ADAPTER_INFO pAdapter = NULL;
-  CipDword dwRetVal = 0;
+/* ----- Windows types PRI macros ------------- */
+#define PRIDW   "lu"
+#define PRIUL   "lu"
+#define PRIuSZT PRIuPTR
+#define PRIxSZT PRIxPTR
 
-  CipUdint ulOutBufLen = sizeof(IP_ADAPTER_INFO);
-  pAdapterInfo = (IP_ADAPTER_INFO *)CipCalloc(1,sizeof(IP_ADAPTER_INFO) );
-  if (pAdapterInfo == NULL) {
-    printf("Error allocating memory needed to call GetAdaptersinfo\n");
-    return kEipStatusError;
-  }
-  // Make an initial call to GetAdaptersInfo to get
-  // the necessary size into the ulOutBufLen variable
-  if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
-    CipFree(pAdapterInfo);
-    pAdapterInfo = (IP_ADAPTER_INFO *)CipCalloc(ulOutBufLen,sizeof(CipUdint) );
-    if (pAdapterInfo == NULL) {
-      printf("Error allocating memory needed to call GetAdaptersinfo\n");
-      return kEipStatusError;
+
+/* ---------- Local functions implementation ----------- */
+
+/** @brief Extract interface index from string if string is a number
+ *
+ *  @param  iface   interface identifier string
+ *  @return         converted number; on failure errno != 0
+ *
+ * Convert a number (interface index) from the supplied interface identifier
+ *  string if possible. On conversion success @p errno is set to zero.
+ *  On failure @p errno is set to ERANGE or EINVAL.
+ */
+static ULONG StrToIfaceIdx(const char *iface)
+{
+    ULONG iface_idx;
+    char  *end;
+    /* The input string is a decimal interface index number or an
+     *  interface name. */
+    errno = 0;  /* To distinguish success / failure later */
+    iface_idx = strtoul(iface, &end, 10);
+    /* overflow is signaled by (errno == ERANGE) */
+
+    if ((iface == end) ||   /* No digits were found */
+        ('\0' != *end)) {   /* More characters after number */
+        errno = EINVAL;     /* Signal conversion failure */
     }
-  }
+    return iface_idx;
+}
 
+/** @brief Derive the interface index from a given interface alias name string
+ *
+ *  @param  iface     interface identifier string
+ *  @param  if_index  pointer to return the derived interface index
+ *  @return           Success: NO_ERROR, Failure any matching Windows error code
+ *
+ * The interface alias name supplied via the @p iface parameter is first
+ *  converted from a multi-byte string to a wide character string to be able to
+ *  call ConvertInterfaceAliasToLuid(). This function returns the Locally
+ *  Unique IDentifier (LUID) if an interface matching the alias name is found.
+ *  The match doesn't need to be exact. A matching unique prefix of the alias
+ *  is sufficient.
+ * The LUID can then be used to retrieve the interface index needed for the
+ *  other functions by calling ConvertInterfaceLuidToIndex().
+ */
+static DWORD ConvertToIndexFromFakeAlias(const char *iface, PNET_IFINDEX iface_idx)
+{
+    WCHAR       *p_if_alias;
+    NET_LUID    if_luid;
+
+    size_t mbtowc_rc = mbstowcs(NULL, iface, 0);
+    if ((size_t)-1 == mbtowc_rc) {  /* Invalid byte sequence encountered */
+        return ERROR_NO_UNICODE_TRANSLATION;
+    }
 
-  if ( (dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) ) == NO_ERROR ) {
-    pAdapter = pAdapterInfo;
-    while (pAdapter) {
-      if (strcmp( pAdapter->IpAddressList.IpAddress.String,
-                  network_interface) == 0) {
-        for (int i = 0; i < 6; i++) {
-          memcpy(&g_ethernet_link->physical_address, pAdapter->Address,
-                 6 * sizeof(CipUsint) );
-        }
+    size_t wc_cnt = mbtowc_rc+1U; /* +1U for nul character */
+    p_if_alias = MALLOC(sizeof(WCHAR) * wc_cnt);
+    if (NULL == p_if_alias) {
+        return ERROR_OUTOFMEMORY;
+    }
 
-        g_tcpip.interface_configuration.ip_address = inet_addr(
-          pAdapter->IpAddressList.IpAddress.String);
-        g_tcpip.interface_configuration.network_mask = inet_addr(
-          pAdapter->IpAddressList.IpMask.String);
-        g_tcpip.interface_configuration.gateway = inet_addr(
-          pAdapter->GatewayList.IpAddress.String);
+    (void) mbstowcs(p_if_alias, iface, wc_cnt);
+    DWORD cnv_status = ConvertInterfaceAliasToLuid(p_if_alias, &if_luid);
+    if (NETIO_SUCCESS(cnv_status)) {
+        cnv_status = ConvertInterfaceLuidToIndex(&if_luid, iface_idx);
+    }
+    FREE(p_if_alias);
+    return cnv_status;
+}
 
-        /* Calculate the CIP multicast address. The multicast address is
-         * derived from the current IP address. */
-        CipTcpIpCalculateMulticastIp(&g_tcpip);
-      }
-      pAdapter = pAdapter->Next;
+/** @brief Determines the interface index number from string input
+ *
+ *  @param  iface     string with interface index number or interface alias name
+ *  @param  iface_idx address of interface index destination variable
+ *  @param            EipStatusOk on success, EipStatusError on failure
+ *
+ * This function tries to determine a Windows interface index from the @ref iface
+ *  string.
+ *
+ * At first it is tried to evaluate the input as a decimal number if that
+ *  succeeds the function returns the converted number and EipStatusOk.
+ *
+ * If the input string is not a number it is assumed to be a Windows interface
+ *  alias name. This function then in turn calls ConvertToIndexFromFakeAlias()
+ *  to find an interface matching that alias.
+ */
+static EipStatus DetermineIfaceIndexByString(const char *iface, PNET_IFINDEX iface_idx)
+{
+  *iface_idx = StrToIfaceIdx(iface);
+
+  BOOL arg_is_numerical = (0 == errno);
+  if (!arg_is_numerical) {
+    DWORD cnv_status = ConvertToIndexFromFakeAlias(iface, iface_idx);
+    if (NO_ERROR != cnv_status) {
+      char *error_message = GetErrorMessage(cnv_status);
+      OPENER_TRACE_ERR("ConvertToIndexFromFakeAlias() failed: %" PRIDW " - %s\n",
+                       cnv_status, error_message);
+      FreeErrorMessage(error_message);
+      return kEipStatusError;
     }
   }
-  else {
-    printf("GetAdaptersInfo failed with error: %d\n", dwRetVal);
+  return kEipStatusOk;
+}
 
+/** @brief Retrieve an IP_ADAPTER_ADDRESSES table in an allocated memory block
+ *
+ *  @param  flags         specify what kind of information to include in the
+ *                        result
+ *  @param  pp_addr_table pointer to the location where to put the
+ *                        PIP_ADAPTER_ADDRESSES pointer
+ *
+ * This function encapsulates the needed memory allocation and retry logic that
+ *  is needed to call GetAdaptersAddresses() successfully and to retrieve the
+ *  complete IP_ADAPTER_ADDRESSES table.
+ * The @p flags parameter is used to tell the GetAdaptersAddresses() function
+ *  which information to include and what information to exclude from the
+ *  result.
+ */
+EipStatus RetrieveAdapterAddressesTable(ULONG flags, PIP_ADAPTER_ADDRESSES *pp_addr_table) {
+  PIP_ADAPTER_ADDRESSES p_addr_table;
+  ULONG ret_val;
+  /* Start allocating with a guessed minimum size. */
+  ULONG outBufLen = 16 * sizeof(IP_ADAPTER_ADDRESSES);
+  do {
+    p_addr_table = (PIP_ADAPTER_ADDRESSES)MALLOC(outBufLen);
+    if (NULL == p_addr_table) {
+      OPENER_TRACE_ERR("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
+      return kEipStatusError;
+    }
+    ret_val = GetAdaptersAddresses(AF_INET, flags, NULL, p_addr_table, &outBufLen);
+
+    if (ERROR_BUFFER_OVERFLOW == ret_val) {
+      FREE(p_addr_table);
+      p_addr_table = NULL;
+    }
+  } while (ERROR_BUFFER_OVERFLOW == ret_val);
+
+  if (NO_ERROR != ret_val || NULL == p_addr_table) {
+    if (NULL != p_addr_table) {
+      FREE(p_addr_table);
+      p_addr_table = NULL;
+    }
+    char *error_message = GetErrorMessage(ret_val);
+    OPENER_TRACE_ERR("GetAdaptersAddresses() failed: %" PRIUL " - %s\n",
+                     ret_val, error_message);
+    FreeErrorMessage(error_message);
+    return kEipStatusError;
   }
-  CipFree(pAdapterInfo);
-  CipFree(pAdapter);
+  *pp_addr_table = p_addr_table;
   return kEipStatusOk;
 }
 
-void ConfigureDomainName() {
-  // This was a parameter!
-  int interface_index = 0;
+/** @brief Converts a wide-character string to a CIP string.
+ *
+ * @param src Source wide-character string.
+ *
+ * @param dest Destination CIP string.
+ *
+ * @return kEipStatusOk if the conversion was successful;
+ *         kEipStatusError if a memory allocation error occurred or
+ *         the source string was too large.
+ */
+static DWORD WideToCipString(const WCHAR *const src,
+                             CipString *const dest) {
+  void *buf = NULL;
+
+  OPENER_ASSERT(src != NULL);
+  OPENER_ASSERT(dest != NULL);
+
+  /*
+   * Evaluate the source string, ensuring two properties:
+   *  1) the source string can be encoded as multi-byte sequence
+   *  2) the number of characters fits in EipUint16, excluding
+   *    the nul terminator.
+   */
+  const size_t num_chars = wcstombs(NULL, src, 0);
+  if ((size_t)-1 == num_chars) {
+    return ERROR_NO_UNICODE_TRANSLATION;
+  }
+  if (num_chars >= UINT16_MAX) {
+    return ERROR_BUFFER_OVERFLOW;
+  }
 
-  CipDword dwSize = 0;
-  int i = 0;
-  // Set the flags to pass to GetAdaptersAddresses
-  CipUdint flags = GAA_FLAG_INCLUDE_PREFIX;
-  CipDword dwRetVal = 0;
-  // default to unspecified address family (both)
-  CipUdint family = AF_UNSPEC;
+  /* New buffer includes nul termination. */
+  const size_t buffer_size = num_chars + 1U;
 
-  LPVOID lpMsgBuf = NULL;
+  if (num_chars) {
+    /* Allocate a new destination buffer. */
+    buf = MALLOC(buffer_size);
+    if (NULL == buf) {
+      return ERROR_OUTOFMEMORY;
+    }
 
-  PIP_ADAPTER_ADDRESSES pAddresses = NULL;
-  PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
-  IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = NULL;
-  CipUdint outBufLen = 0;
-  CipUdint tries = 0;
+    /* Transfer the string to the new buffer. */
+    size_t cnv_chars = wcstombs(buf, src, buffer_size);
+    OPENER_ASSERT(cnv_chars == num_chars);
+  }
 
-  family = AF_INET;
-  // Allocate a 15 KB buffer to start with.
-  outBufLen = WORKING_BUFFER_SIZE;
+  /* Release the any previous string content. */
+  FreeCipString(dest);
 
-  do {
+  /* Transfer the new content to the destination. */
+  dest->length = num_chars;
+  dest->string = buf;
 
-    pAddresses = (IP_ADAPTER_ADDRESSES *)CipCalloc(1,outBufLen);
-    if (pAddresses == NULL) {
-      printf
-        ("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
-      exit(1);
-    }
+  return ERROR_SUCCESS;
+}
 
-    dwRetVal =
-      GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);
+/** @brief Extract IPv4 IP address from SOCKET_ADDRESS structure as CipUdint
+ *
+ *  @param  socket_address  pointer to a Windows SOCKET_ADDRESS structure
+ *  @return                 IPv4 address taken from @p socket_address
+ */
+static CipUdint GetIpFromSocketAddress(const SOCKET_ADDRESS *socket_address) {
+  SOCKADDR_IN *sin = ((SOCKADDR_IN*)socket_address->lpSockaddr);
+  return sin->sin_addr.S_un.S_addr;
+}
 
-    if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
-      CipFree(pAddresses);
-      pAddresses = NULL;
-    }
-    else {
-      break;
+
+/* ---------- Public functions implementation ---------- */
+
+/* For Doxygen descriptions see opener_api.h. */
+EipStatus IfaceGetMacAddress(const char *iface, uint8_t *physical_address) {
+  ULONG  iface_idx;
+
+  if(kEipStatusOk != DetermineIfaceIndexByString(iface, &iface_idx)) {
+    return kEipStatusError;
+  }
+
+  /* Select what to include in or exclude from the adapter addresses table. */
+  const ULONG flags = GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST |
+                      GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER |
+                      GAA_FLAG_SKIP_FRIENDLY_NAME;
+  PIP_ADAPTER_ADDRESSES p_addr_table = NULL;
+  if (kEipStatusOk != RetrieveAdapterAddressesTable(flags, &p_addr_table)) {
+    return kEipStatusError;
+  }
+
+  /* Now search the right interface in the adapter addresses table. */
+  PIP_ADAPTER_ADDRESSES p_addr_entry = p_addr_table;
+  while (NULL != p_addr_entry) {
+    if (iface_idx == p_addr_entry->IfIndex) {
+      /* Get MAC address from matched interface */
+      OPENER_TRACE_INFO("MAC address: %02" PRIX8 "-%02" PRIX8 "-%02" PRIX8
+                                    "-%02" PRIX8 "-%02" PRIX8 "-%02" PRIX8 "\n",
+        p_addr_entry->PhysicalAddress[0], p_addr_entry->PhysicalAddress[1],
+        p_addr_entry->PhysicalAddress[2], p_addr_entry->PhysicalAddress[3],
+        p_addr_entry->PhysicalAddress[4], p_addr_entry->PhysicalAddress[5]);
+        memcpy_s(physical_address, 6,
+                p_addr_entry->PhysicalAddress, p_addr_entry->PhysicalAddressLength);
+      break;  /* leave search after iface_idx match */
     }
+    p_addr_entry = p_addr_entry->Next;
+  }
+  FREE(p_addr_table);
 
-    tries++;
+  /* Return success if we searched the table and had a match. */
+  return (p_addr_entry) ? kEipStatusOk : kEipStatusError;
+}
 
-  } while ( (dwRetVal == ERROR_BUFFER_OVERFLOW) && (tries < MAX_TRIES) );
+/* For Doxygen descriptions see opener_api.h. */
+EipStatus IfaceGetConfiguration(const char *iface,
+                                CipTcpIpInterfaceConfiguration *iface_cfg)
+{
+  ULONG  iface_idx;
 
-  if (dwRetVal == NO_ERROR) {
-    // If successful, output some information from the data we received
-    pCurrAddresses = pAddresses;
-    while (pCurrAddresses) {
-      if (interface_index == pCurrAddresses->IfIndex) {
-        pDnServer = pCurrAddresses->FirstDnsServerAddress;
-        if (pDnServer) {
-          for (i = 0; pDnServer != NULL; i++) {
-            pDnServer = pDnServer->Next;
-          }
-        }
+  if(kEipStatusOk != DetermineIfaceIndexByString(iface, &iface_idx)) {
+    return kEipStatusError;
+  }
 
-        char pStringBuf[INET_ADDRSTRLEN];
-        if (i != 0) {
+  /* Select what to include in or exclude from the adapter addresses table. */
+  const ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
+                      GAA_FLAG_INCLUDE_GATEWAYS | GAA_FLAG_INCLUDE_PREFIX;
+  PIP_ADAPTER_ADDRESSES p_addr_table = NULL;
+  if (kEipStatusOk != RetrieveAdapterAddressesTable(flags, &p_addr_table)) {
+    return kEipStatusError;
+  }
 
-          if (NULL != g_tcpip.interface_configuration.domain_name.string) {
-            /* if the string is already set to a value we have to free the resources
-             * before we can set the new value in order to avoid memory leaks.
-             */
-            CipFree(g_tcpip.interface_configuration.domain_name.string);
-          }
-          g_tcpip.interface_configuration.domain_name.length = strlen(
-            pCurrAddresses->DnsSuffix);
-          if (g_tcpip.interface_configuration.domain_name.length) {
-            g_tcpip.interface_configuration.domain_name.string =
-              (CipByte *)CipCalloc(
-                g_tcpip.interface_configuration.domain_name.length + 1,
-                sizeof(CipUsint) );
-            strcpy(g_tcpip.interface_configuration.domain_name.string,
-                   pCurrAddresses->DnsSuffix);
-          }
-          else {
-            g_tcpip.interface_configuration.domain_name.string = NULL;
-          }
-        }
-        else{ g_tcpip.interface_configuration.domain_name.length = 0;}
+  CipTcpIpInterfaceConfiguration local_cfg;
+  memset(&local_cfg, 0x00, sizeof local_cfg);
 
+  /* Now search the right interface in the adapter addresses table. */
+  PIP_ADAPTER_ADDRESSES p_addr_entry = p_addr_table;
+  while (NULL != p_addr_entry) {
+    if (iface_idx == p_addr_entry->IfIndex) {
+
+      if (IfOperStatusUp != p_addr_entry->OperStatus) {
+        OPENER_TRACE_ERR("IfaceGetConfiguration(): Interface '%s' is not up.\n", iface);
+        FREE(p_addr_table);
+        return kEipStatusError;
       }
-      pCurrAddresses = pCurrAddresses->Next;
-    }
-  }
-  else {
-    OPENER_TRACE_INFO("Call to GetAdaptersAddresses failed with error: %d\n",
-                      dwRetVal);
-    if (dwRetVal == ERROR_NO_DATA) {
-      OPENER_TRACE_INFO(
-        "\tNo addresses were found for the requested parameters\n");
-    }
-    else {
-
-      if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
-                        FORMAT_MESSAGE_FROM_SYSTEM |
-                        FORMAT_MESSAGE_IGNORE_INSERTS,
-                        NULL, dwRetVal,
-                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-                        // Default language
-                        (LPTSTR)&lpMsgBuf, 0, NULL) ) {
-        OPENER_TRACE_INFO("\tError: %s", lpMsgBuf);
-        CipFree(lpMsgBuf);
-        if (pAddresses) {
-          CipFree(pAddresses);
+      /* Extract ip_addr, netmask, gateway, nameserver, nameserver 2, domain ... */
+      {
+        PIP_ADAPTER_UNICAST_ADDRESS pUnicast = p_addr_entry->FirstUnicastAddress;
+        if (NULL != pUnicast) {
+          local_cfg.ip_address = GetIpFromSocketAddress(&pUnicast->Address);
+        }
+      }
+      {
+        PIP_ADAPTER_PREFIX pPrefix = p_addr_entry->FirstPrefix;
+        if (NULL != pPrefix) {
+          local_cfg.network_mask = htonl(0xffffffff << (32U - pPrefix->PrefixLength));
         }
-        exit(1);
       }
+      {
+        PIP_ADAPTER_GATEWAY_ADDRESS pGateway = p_addr_entry->FirstGatewayAddress;
+        if (NULL != pGateway) {
+          local_cfg.gateway = GetIpFromSocketAddress(&pGateway->Address);
+        }
+      }
+      {
+        IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = p_addr_entry->FirstDnsServerAddress;
+        if (NULL != pDnServer) {
+          local_cfg.name_server = GetIpFromSocketAddress(&pDnServer->Address);
+          pDnServer = pDnServer->Next;
+          if (NULL != pDnServer) {
+            local_cfg.name_server_2= GetIpFromSocketAddress(&pDnServer->Address);
+          }
+        }
+      }
+      DWORD ret_val = WideToCipString(p_addr_entry->DnsSuffix,
+                                      &local_cfg.domain_name);
+      if (NO_ERROR != ret_val) {
+        char *error_message = GetErrorMessage(ret_val);
+        OPENER_TRACE_ERR("WideToCipString(DnsSuffix) failed with error: %"
+                         PRIDW " - %s\n", ret_val, error_message);
+        FreeErrorMessage(error_message);
+        FREE(p_addr_table);
+        return kEipStatusError;
+      }
+      break;  /* leave search after iface_idx match */
     }
+    p_addr_entry = p_addr_entry->Next;
+  }
+  FREE(p_addr_table);
+
+  if (p_addr_entry) {
+    /* Free first and then making a shallow copy of local_cfg.domain_name is
+     *  ok, because local_cfg goes out of scope on return. */
+    FreeCipString(&iface_cfg->domain_name);
+    *iface_cfg = local_cfg;
   }
 
-  if (pAddresses) {
-    CipFree(pAddresses);
+  /* Return success if we searched the table and had a match. */
+  return (p_addr_entry) ? kEipStatusOk : kEipStatusError;
+}
+
+/* For Doxygen descriptions see opener_api.h. */
+EipStatus IfaceWaitForIp(const char *iface, int timeout, volatile int *abort_wait) {
+  ULONG  iface_idx;
+
+  if(kEipStatusOk != DetermineIfaceIndexByString(iface, &iface_idx)) {
+    return kEipStatusError;
   }
 
+  {
+    PMIB_IPADDRTABLE  pmib_ipaddr_table = NULL;
+    ULONG addr_table_sz = 0;
+    uint32_t ipaddr;
+
+#define WAIT_CYCLE_MS 100
+    /* Calculate cycles of SleepEx(WAIT_CYCLE_MS) needed. */
+    timeout *= (1000/WAIT_CYCLE_MS);
+    do {
+      DWORD dw_ret;
+      ipaddr = 0U;
+
+      do {
+        dw_ret = GetIpAddrTable(pmib_ipaddr_table, &addr_table_sz, FALSE);
+        if (ERROR_INSUFFICIENT_BUFFER == dw_ret) {
+          if (pmib_ipaddr_table) {
+            FREE(pmib_ipaddr_table);
+          }
+          pmib_ipaddr_table = MALLOC(addr_table_sz);
+          if (NULL == pmib_ipaddr_table) {
+            OPENER_TRACE_ERR("Memory allocation failed for "
+                             "MIB_IPADDRTABLE struct\n");
+            return kEipStatusError;
+          }
+        }
+      } while (ERROR_INSUFFICIENT_BUFFER == dw_ret);
+      if (NO_ERROR != dw_ret) {
+        char *error_message = GetErrorMessage(dw_ret);
+        OPENER_TRACE_ERR("%s() failed with error: %" PRIDW " - %s\n",
+                         __func__, dw_ret, error_message);
+        FreeErrorMessage(error_message);
+        return kEipStatusError;
+      }
+
+      /* Search entry matching the interface index and determine IP address. */
+      for (int i=0; i < (int) pmib_ipaddr_table->dwNumEntries; i++) {
+        if (pmib_ipaddr_table->table[i].dwIndex == iface_idx) {
+          if (0 == (pmib_ipaddr_table->table[i].wType &
+                    (MIB_IPADDR_DELETED | MIB_IPADDR_DISCONNECTED | MIB_IPADDR_TRANSIENT))) {
+            ipaddr = pmib_ipaddr_table->table[i].dwAddr;
+          }
+        }
+      }
+
+      if (timeout > 0) {
+        --timeout;
+      }
+    } while ((0 == ipaddr) && (0 != timeout) && (0 == *abort_wait) &&
+             (0 == SleepEx(WAIT_CYCLE_MS, FALSE)));
 
+    OPENER_TRACE_INFO("IP=%08" PRIx32 ", timeout=%d\n", (uint32_t)ntohl(ipaddr), timeout);
+    if (pmib_ipaddr_table) {
+      FREE(pmib_ipaddr_table);
+    }
+  }
+  return kEipStatusOk;
 }
 
-void ConfigureHostName(void) {
+#define HOST_NAME_MAX 256 /* Should be long enough according rfc1132. */
+void GetHostName(CipString *hostname)
+{
   CipWord wVersionRequested;
   WSADATA wsaData;
   int err;
@@ -213,33 +461,24 @@ void ConfigureHostName(void) {
 
   err = WSAStartup(wVersionRequested, &wsaData);
   if (err != 0) {
-    /* Tell the user that we could not find a usable */
-    /* Winsock DLL.                                  */
-    printf("WSAStartup failed with error: %d\n", err);
+    /* Tell the user that we could not find a usable Winsock DLL.  */
+    char *error_message = GetErrorMessage(err);
+    printf("WSAStartup failed with error: %d - %s\n",
+            err, error_message);
+    FreeErrorMessage(error_message);
     return;
   }
 
-  char hostname[256] = "";
-  gethostname(hostname, sizeof(hostname) );
-
-  //WSACleanup();
-
-
-
-
-  if (NULL != g_tcpip.hostname.string) {
-    /* if the string is already set to a value we have to free the resources
-     * before we can set the new value in order to avoid memory leaks.
-     */
-    CipFree(g_tcpip.hostname.string);
-  }
-  g_tcpip.hostname.length = strlen(hostname);
-  if (g_tcpip.hostname.length) {
-    g_tcpip.hostname.string = (CipByte *) CipCalloc(
-      g_tcpip.hostname.length + 1,
-      sizeof(CipByte) );
-    strcpy(g_tcpip.hostname.string, hostname);
-  } else {
-    g_tcpip.hostname.string = NULL;
+  char name_buf[HOST_NAME_MAX] = "";
+  err = gethostname(name_buf, sizeof(name_buf));
+  if (0 != err) {
+    int error_code = GetSocketErrorNumber();
+    char *error_message = GetErrorMessage(error_code);
+    printf("gethostname() failed, %d - %s\n",
+        error_code,
+        error_message);
+    FreeErrorMessage(error_message);
+    return;
   }
+  SetCipStringByCstr(hostname, name_buf);
 }

+ 5 - 0
source/src/ports/MINGW/networkconfig.h

@@ -0,0 +1,5 @@
+/*******************************************************************************
+ * Copyright (c) 2009, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/

+ 3 - 3
source/src/ports/MINGW/networkhandler.c

@@ -8,7 +8,7 @@
 #include <stdlib.h>
 #include <winsock2.h>
 #include <windows.h>
-#include <Ws2tcpip.h>
+#include <ws2tcpip.h>
 
 #include "networkhandler.h"
 
@@ -55,6 +55,6 @@ int SetQosOnSocket(int socket,
   /* Quote from Vol. 2, Section 5-7.4.2 DSCP Value Attributes:
    *  Note that the DSCP value, if placed directly in the ToS field
    *  in the IP header, must be shifted left 2 bits. */
-  int set_tos = qos_value << 2;
-  return setsockopt(socket, IPPROTO_IP, IP_TOS, &set_tos, sizeof(set_tos) );
+  DWORD set_tos = qos_value << 2;
+  return setsockopt(socket, IPPROTO_IP, IP_TOS, (char *)&set_tos, sizeof(set_tos) );
 }

+ 1 - 1
source/src/ports/MINGW/opener_error.c

@@ -20,7 +20,7 @@ int GetSocketErrorNumber(void) {
 
 char *GetErrorMessage(int error_number) {
   char *error_message = NULL;
-  FormatMessage(
+  FormatMessageA(
     FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
     NULL,
     error_number,

+ 4 - 4
source/src/ports/MINGW/sample_application/ethlinkcbs.c

@@ -52,7 +52,7 @@ static CipUdint media_calls[OPENER_ETHLINK_INSTANCE_CNT];
 /* This is meant as debugging aid and to check if the individual counter     */
 /*  value is sent at the right position in the Get* service response.        */
 
-#define MAKE_CNTR(inst, attr, idx, cnt) ((10000000u * inst) + (100000u * attr) + (1000u * idx) + cnt)
+#define MAKE_CNTR(inst, attr, idx, cnt) ((10000000U * inst) + (100000U * attr) + (1000U * idx) + cnt)
 
 EipStatus EthLnkPreGetCallback
 (
@@ -147,14 +147,14 @@ EipStatus EthLnkPostGetCallback
      *  clear our GetAttributeSingle PreCallback execution counters. */
     switch (attribute->attribute_number) {
     case 4:
-      iface_calls[inst_no-1] = 0u;
+      iface_calls[inst_no-1] = 0U;
       break;
     case 5:
-      media_calls[inst_no-1] = 0u;
+      media_calls[inst_no-1] = 0U;
       /* This is a concession to the conformance test tool that expects
        *  the media counters to be zero after a GetAndClear service. */
       for (int idx = 0; idx < 12; ++idx) {
-        g_ethernet_link[inst_no-1].media_cntrs.cntr32[idx] = 0u;
+        g_ethernet_link[inst_no-1].media_cntrs.cntr32[idx] = 0U;
       }
       break;
     default:

+ 47 - 37
source/src/ports/POSIX/main.c

@@ -17,7 +17,6 @@
 
 #include "generic_networkhandler.h"
 #include "opener_api.h"
-#include "cipcommon.h"
 #include "cipethernetlink.h"
 #include "ciptcpipinterface.h"
 #include "trace.h"
@@ -29,12 +28,16 @@
 #define BringupNetwork(if_name, method, if_cfg, hostname)  (0)
 #define ShutdownNetwork(if_name)  (0)
 
+/** If OpENer is aborted by a signal it returns the sum of the signal number
+ *  and this define. */
+#define RET_SHOW_SIGNAL 200
+
 /******************************************************************************/
 /** @brief Signal handler function for ending stack execution
  *
- * @param signal the signal we received
+ * @param signal  the signal we received
  */
-void LeaveStack(int signal);
+static void LeaveStack(int signal);
 
 /******************************************************************************/
 /** @brief Execute OpENer stack loop function
@@ -53,11 +56,10 @@ static void *executeEventLoop(void *pthread_arg);
 volatile int g_end_stack = 0;
 
 /******************************************************************************/
-int main(int argc,
-         char *arg[]) {
+int main(int argc, char *arg[]) {
 
   cap_t capabilities;
-  cap_value_t capabilies_list[1];
+  cap_value_t capabilities_list[1];
 
   capabilities = cap_get_proc();
   if (NULL == capabilities) {
@@ -65,9 +67,9 @@ int main(int argc,
     exit(EXIT_FAILURE);
   }
 
-  capabilies_list[0] = CAP_NET_RAW;
+  capabilities_list[0] = CAP_NET_RAW;
   if (-1
-      == cap_set_flag(capabilities, CAP_EFFECTIVE, 1, capabilies_list,
+      == cap_set_flag(capabilities, CAP_EFFECTIVE, 1, capabilities_list,
                       CAP_SET) ) {
     cap_free(capabilities);
     printf("Could not set CAP_NET_RAW capability\n");
@@ -81,15 +83,14 @@ int main(int argc,
   }
 
   if (-1 == cap_free(capabilities) ) {
-    printf("Could not free capabilites value\n");
+    printf("Could not free capabilities value\n");
     exit(EXIT_FAILURE);
   }
 
   if (argc != 2) {
-    printf("Wrong number of command line parameters!\n");
-    printf("The correct command line parameters are:\n");
-    printf("./OpENer interfacename\n");
-    printf("    e.g. ./OpENer eth1\n");
+    fprintf(stderr, "Wrong number of command line parameters!\n");
+    fprintf(stderr, "Usage: %s [interface name]\n", arg[0]);
+    fprintf(stderr, "\te.g. ./OpENer eth1\n");
     exit(EXIT_FAILURE);
   }
 
@@ -123,21 +124,22 @@ int main(int argc,
    *  the defaults on the first start without a valid TCP/IP configuration
    *  file.
    */
-  ConfigureHostName();
+  GetHostName(&g_tcpip.hostname);
 
-  /* Now any NV data values are loaded to change the attribute contents to
-   *  the stored configuration.
+  /* The CIP objects are now created and initialized with their default values.
+   *  After that any NV data values are loaded to change the attribute contents
+   *  to the stored configuration.
    */
   if (kEipStatusError == NvdataLoad()) {
     OPENER_TRACE_WARN("Loading of some NV data failed. Maybe the first start?\n");
   }
 
   /* Bring up network interface or start DHCP client ... */
-  int ret = BringupNetwork(arg[1],
+  EipStatus status = BringupNetwork(arg[1],
                        g_tcpip.config_control,
                        &g_tcpip.interface_configuration,
                        &g_tcpip.hostname);
-  if (ret < 0) {
+  if (status < 0) {
     OPENER_TRACE_ERR("BringUpNetwork() failed\n");
   }
 
@@ -153,26 +155,27 @@ int main(int argc,
   }
   if (kTcpipCfgCtrlDhcp == network_config_method) {
     OPENER_TRACE_INFO("DHCP network configuration started\n");
-    /* Wait for DHCP done here ...*/
-    /* TODO: implement this function to wait for IP present
-        or abort through g_end_stack.
-        rc = WaitInterfaceIsUp(arg[1], g_end_stack);
-      */
-    OPENER_TRACE_INFO("DHCP wait for interface: status %d\n", ret);
-
-    /* Read IP configuration received via DHCP from interface and store in
-     *  the TCP/IP object.*/
-    ret = ConfigureNetworkInterface(arg[1]);
-    if (ret < 0) {
-      OPENER_TRACE_WARN("Problems getting interface configuration\n");
+    /* DHCP should already have been started with BringupNetwork(). Wait
+     * here for IP present (DHCP done) or abort through g_end_stack. */
+    status = IfaceWaitForIp(arg[1], -1, &g_end_stack);
+    OPENER_TRACE_INFO("DHCP wait for interface: status %d, g_end_stack=%d\n",
+                      status, g_end_stack);
+    if (kEipStatusOk == status && 0 == g_end_stack) {
+      /* Read IP configuration received via DHCP from interface and store in
+       *  the TCP/IP object.*/
+      status = IfaceGetConfiguration(arg[1], &g_tcpip.interface_configuration);
+      if (status < 0) {
+        OPENER_TRACE_WARN("Problems getting interface configuration\n");
+      }
     }
-    ConfigureDomainName();
   }
 
 
   /* The network initialization of the EIP stack for the NetworkHandler. */
   if (!g_end_stack && kEipStatusOk == NetworkHandlerInitialize()) {
 #ifdef OPENER_RT
+    int ret;
+
     /* Memory lock all*/
     if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
       OPENER_TRACE_ERR("mlockall failed: %m\n");
@@ -230,27 +233,34 @@ int main(int argc,
     /* Unlock memory */
     munlockall();
 #else
-    (void)executeEventLoop(NULL);
+    (void) executeEventLoop(NULL);
 #endif
     /* clean up network state */
     NetworkHandlerFinish();
   }
+
   /* close remaining sessions and connections, clean up used data */
   ShutdownCipStack();
 
   /* Shut down the network interface now. */
   (void) ShutdownNetwork(arg[1]);
 
+  if(0 != g_end_stack) {
+    printf("OpENer aborted by signal %d.\n", g_end_stack);
+    return RET_SHOW_SIGNAL+g_end_stack;
+  }
+
   return EXIT_SUCCESS;
 }
 
-void LeaveStack(int signal) {
-  (void) signal;       /* kill unused parameter warning */
-  OPENER_TRACE_STATE("got signal %d\n",signal);
-  g_end_stack = 1;
+static void LeaveStack(int signal) {
+  if (SIGHUP == signal || SIGINT == signal) {
+    g_end_stack = signal;
+  }
+  OPENER_TRACE_STATE("got signal %d\n", signal);
 }
 
-void *executeEventLoop(void *pthread_arg) {
+static void *executeEventLoop(void *pthread_arg) {
   static int pthread_dummy_ret;
   (void) pthread_arg;
 

+ 147 - 38
source/src/ports/POSIX/networkconfig.c

@@ -8,6 +8,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <time.h>
 
 #include <sys/ioctl.h>
 #include <sys/socket.h>
@@ -16,8 +17,6 @@
 #include <arpa/inet.h>
 #include <unistd.h>
 
-#include "ciptcpipinterface.h"
-#include "cipethernetlink.h"
 #include "cipstring.h"
 #include "networkconfig.h"
 #include "cipcommon.h"
@@ -25,7 +24,8 @@
 #include "trace.h"
 #include "opener_api.h"
 
-EipStatus IfaceGetMacAddress(const char *iface, uint8_t *const physical_address) {
+
+EipStatus IfaceGetMacAddress(const char *iface, uint8_t * const physical_address) {
   struct ifreq ifr;
   size_t if_name_len = strlen(iface);
   EipStatus status = kEipStatusError;
@@ -43,22 +43,28 @@ EipStatus IfaceGetMacAddress(const char *iface, uint8_t *const physical_address)
     close(fd);
   }
   else {
-    OPENER_TRACE_ERR("interface name is too long");
+    errno = ENAMETOOLONG;
+    OPENER_TRACE_ERR("interface name is too long\n");
   }
 
   return status;
 }
 
-EipStatus ConfigureNetworkInterface(const char *const network_interface) {
-
+static EipStatus GetIpAndNetmaskFromInterface
+(
+  const char *iface,
+  CipTcpIpInterfaceConfiguration *iface_cfg
+) {
   struct ifreq ifr;
-  size_t if_name_len = strlen(network_interface);
+  size_t if_name_len = strlen(iface);
   if(if_name_len < sizeof(ifr.ifr_name) ) {
-    memcpy(ifr.ifr_name, network_interface, if_name_len);
+    memcpy(ifr.ifr_name, iface, if_name_len);
     ifr.ifr_name[if_name_len] = 0;
   }
-  else{
-    OPENER_TRACE_INFO("interface name is too long\n");
+  else {
+    errno = ENAMETOOLONG;
+    OPENER_TRACE_ERR("interface name is too long\n");
+    return kEipStatusError;
   }
 
   {
@@ -79,16 +85,19 @@ EipStatus ConfigureNetworkInterface(const char *const network_interface) {
       return kEipStatusError;
     }
 
-    g_tcpip.interface_configuration.ip_address = ipaddr;
-    g_tcpip.interface_configuration.network_mask = netaddr;
+    iface_cfg->ip_address = ipaddr;
+    iface_cfg->network_mask = netaddr;
 
     close(fd);
   }
+  return kEipStatusOk;
+}
 
-  /* Calculate the CIP multicast address. The multicast address is
-   * derived from the current IP address and network mask. */
-  CipTcpIpCalculateMulticastIp(&g_tcpip);
-
+static EipStatus GetGatewayFromRoute
+(
+  const char *iface,
+  CipTcpIpInterfaceConfiguration *iface_cfg
+) {
   static const char route_location[] = "/proc/net/route";
 
   FILE *file_handle = fopen(route_location, "r");
@@ -96,13 +105,13 @@ EipStatus ConfigureNetworkInterface(const char *const network_interface) {
   char *gateway_string = NULL;
 
   if(!file_handle) {
-    exit(EXIT_FAILURE);
+    return kEipStatusError;
   }
   else {
     char *needle_start;
     file_buffer[0] = '\0';  /* To enter the while loop */
     while(NULL ==
-          (needle_start = strstr(file_buffer, network_interface) ) &&
+          (needle_start = strstr(file_buffer, iface) ) &&
           fgets(file_buffer, sizeof(file_buffer), file_handle) ) {
       /* Skip each non matching line */
     }
@@ -115,29 +124,31 @@ EipStatus ConfigureNetworkInterface(const char *const network_interface) {
       gateway_string = strtok_r(NULL, " \t", &strtok_save);
     }
     else {
-      OPENER_TRACE_ERR("network interface: %s not found", network_interface);
-      exit(EXIT_FAILURE);
+      OPENER_TRACE_ERR("network interface: '%s' not found\n", iface);
+      return kEipStatusError;
     }
   }
 
-  CipUdint gateway = 0;
+  unsigned long tmp_gw;
   char *end;
   /* The gateway string is a hex number in network byte order. */
   errno = 0;  /* To distinguish success / failure later */
-  gateway = strtoul(gateway_string, &end, 16);
+  tmp_gw = strtoul(gateway_string, &end, 16);
 
-  if ((errno == ERANGE && gateway == ULONG_MAX) ||  /* overflow */
+  if ((errno == ERANGE && tmp_gw == ULONG_MAX) ||  /* overflow */
       (gateway_string == end) ||  /* No digits were found */
       ('\0' != *end) ) {          /* More characters after number */
-    g_tcpip.interface_configuration.gateway = 0;
+    iface_cfg->gateway = 0;
     return kEipStatusError;
   }
+  CipUdint gateway = tmp_gw;
+
   /* Only reached on strtoul() conversion success */
   if(INADDR_LOOPBACK != gateway) {
-    g_tcpip.interface_configuration.gateway = gateway;
+    iface_cfg->gateway = gateway;
   }
   else {
-    g_tcpip.interface_configuration.gateway = 0;
+    iface_cfg->gateway = 0;
   }
 #if defined(OPENER_TRACE_ENABLED)
   {
@@ -149,7 +160,10 @@ EipStatus ConfigureNetworkInterface(const char *const network_interface) {
   return kEipStatusOk;
 }
 
-void ConfigureDomainName() {
+static EipStatus GetDnsInfoFromResolvConf
+(
+  CipTcpIpInterfaceConfiguration *iface_cfg
+) {
   static const char resolv_conf_file[] = "/etc/resolv.conf";
   FILE *file_handle = fopen(resolv_conf_file, "r");
   char *file_buffer = NULL;
@@ -159,14 +173,14 @@ void ConfigureDomainName() {
     fseek(file_handle, 0, SEEK_END);
     file_length = ftell(file_handle);
     fseek(file_handle, 0, SEEK_SET);
-    file_buffer = malloc(file_length+1u); /* +1u for zero termination */
+    file_buffer = malloc(file_length+1U); /* +1U for zero termination */
     if(file_buffer) {
       size_t rd_sz = fread(file_buffer, 1, file_length, file_handle);
       fclose(file_handle);
       if (rd_sz != file_length) {
         OPENER_TRACE_ERR("Read error on file %s\n", resolv_conf_file);
         free(file_buffer);
-        exit(EXIT_FAILURE);
+        return kEipStatusError;
       }
       file_buffer[file_length] = '\0';  /* zero terminate for sure */
     }
@@ -174,11 +188,11 @@ void ConfigureDomainName() {
       OPENER_TRACE_ERR("Could not allocate memory for reading file %s\n",
                        resolv_conf_file);
       fclose(file_handle);
-      exit(EXIT_FAILURE);
+      return kEipStatusError;
     }
   } else {
     OPENER_TRACE_ERR("Could not open file %s\n", resolv_conf_file);
-    exit(EXIT_FAILURE);
+    return kEipStatusError;
   }
 
   char *value_string;
@@ -186,7 +200,7 @@ void ConfigureDomainName() {
   char *strtok_key;
   char *line;
   CipUdint dmy_dns;
-  CipUdint *dns = &g_tcpip.interface_configuration.name_server;
+  CipUdint *dns = &iface_cfg->name_server;
   /* Split the file_buffer into lines. */
   for (char *strtok_beg = file_buffer;
         NULL != (line = strtok_r(strtok_beg, "\n", &strtok_save));
@@ -206,7 +220,7 @@ void ConfigureDomainName() {
       strtok_r(line, " \t", &strtok_key);
       if (0 == strcmp("search", line) || 0 == strcmp("domain", line)) {
         if (NULL != (value_string = strtok_r(NULL, " \t", &strtok_key)))  {
-          SetCipStringByCstr(&g_tcpip.interface_configuration.domain_name,
+          SetCipStringByCstr(&iface_cfg->domain_name,
                             value_string);
         }
       }
@@ -219,8 +233,8 @@ void ConfigureDomainName() {
           inet_pton(AF_INET, value_string, dns);
           /* Adjust destination for next nameserver occurrence. */
           if (dns != &dmy_dns) {
-            if (dns == &g_tcpip.interface_configuration.name_server) {
-              dns = &g_tcpip.interface_configuration.name_server_2;
+            if (dns == &iface_cfg->name_server) {
+              dns = &iface_cfg->name_server_2;
             }
             else {
               /* After 2 nameserver lines any further nameservers are ignored. */
@@ -233,14 +247,109 @@ void ConfigureDomainName() {
     }
   }
   free(file_buffer);
+  return kEipStatusOk;
+}
+
+EipStatus IfaceGetConfiguration
+(
+  const char *iface,
+  CipTcpIpInterfaceConfiguration *iface_cfg
+) {
+  CipTcpIpInterfaceConfiguration local_cfg;
+  EipStatus status;
+
+  memset(&local_cfg, 0x00, sizeof local_cfg);
+
+  status = GetIpAndNetmaskFromInterface(iface, &local_cfg);
+  if (kEipStatusOk  == status) {
+    status = GetGatewayFromRoute(iface, &local_cfg);
+    if (kEipStatusOk == status) {
+      status = GetDnsInfoFromResolvConf(&local_cfg);
+    }
+  }
+  if (kEipStatusOk == status) {
+    /* Free first and then making a shallow copy of local_cfg.domain_name is
+     *  ok, because local_cfg goes out of scope now. */
+    FreeCipString(&iface_cfg->domain_name);
+    *iface_cfg = local_cfg;
+  }
+  return status;
+}
+
+static int nanosleep_simple32(uint32_t sleep_ns)
+{
+    struct timespec tsv = { 0, (long)sleep_ns };
+    struct timespec trem;
+    int     rc;
+
+    OPENER_ASSERT(sleep_ns < 1000000000UL);
+    do
+    {
+        rc = nanosleep(&tsv, &trem);
+        tsv = trem;
+    }
+    while (-1 == rc && EINTR == errno);
+
+    return rc;
 }
 
-void ConfigureHostName() {
-  char name_buf[HOST_NAME_MAX];
+/* For an API documentation look at opener_api.h. */
+#define WAIT_CYCLE_NS   100000000U
+EipStatus IfaceWaitForIp
+(
+  const char *iface,
+  int timeout,
+  volatile int *p_abort_wait
+) {
+  struct ifreq    ifr;
+  int             rc;
+
+  size_t if_name_len = strlen(iface);
+  if(if_name_len < sizeof(ifr.ifr_name) ) {
+    memcpy(ifr.ifr_name, iface, if_name_len);
+    ifr.ifr_name[if_name_len] = 0;
+  }
+  else {
+    errno = ENAMETOOLONG;
+    OPENER_TRACE_INFO("interface name is too long\n");
+    return kEipStatusError;
+  }
+
+  {
+    int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+    uint32_t ipaddr;
+
+    timeout *= 10;  /* 100ms wait per nanosleep_simple32() */
+    do {
+      ipaddr = 0U;
+
+      if(0 == (rc = ioctl(fd, SIOCGIFADDR, &ifr))) {
+        ipaddr = ( (struct sockaddr_in *) &ifr.ifr_addr )->sin_addr.s_addr;
+      } else {
+        if (EADDRNOTAVAIL != errno) {
+          return rc;
+        }
+      }
+      if (timeout > 0) {
+        --timeout;
+      }
+    } while ((0 == ipaddr) && (0 != timeout) && (0 == *p_abort_wait) &&
+             (0 == nanosleep_simple32(WAIT_CYCLE_NS)));
+
+    OPENER_TRACE_INFO("ip=%08x, timeout=%d\n", ntohl(ipaddr), timeout);
+    close(fd);
+  }
+
+  return rc;
+}
+
+
+void GetHostName(CipString *hostname) {
+  char  name_buf[HOST_NAME_MAX];
 
   int rc = gethostname(name_buf, sizeof name_buf);
   name_buf[HOST_NAME_MAX - 1] = '\0'; /* Ensure termination */
   if (0 == rc) {
-      SetCipStringByCstr(&g_tcpip.hostname, name_buf);
+      SetCipStringByCstr(hostname, name_buf);
   }
 }

+ 0 - 6
source/src/ports/POSIX/networkconfig.h

@@ -3,9 +3,3 @@
  * All rights reserved.
  *
  ******************************************************************************/
-
-EipStatus ConfigureNetworkInterface(const char *const network_interface);
-
-void ConfigureDomainName();
-
-void ConfigureHostName();

+ 1 - 1
source/src/ports/POSIX/platform_network_includes.h

@@ -3,4 +3,4 @@
  * All rights reserved.
  *
  ******************************************************************************/
-
+#include <netinet/in.h>

+ 4 - 4
source/src/ports/POSIX/sample_application/ethlinkcbs.c

@@ -52,7 +52,7 @@ static CipUdint media_calls[OPENER_ETHLINK_INSTANCE_CNT];
 /* This is meant as debugging aid and to check if the individual counter     */
 /*  value is sent at the right position in the Get* service response.        */
 
-#define MAKE_CNTR(inst, attr, idx, cnt) ((10000000u * inst) + (100000u * attr) + (1000u * idx) + cnt)
+#define MAKE_CNTR(inst, attr, idx, cnt) ((10000000U * inst) + (100000U * attr) + (1000U * idx) + cnt)
 
 EipStatus EthLnkPreGetCallback
 (
@@ -147,14 +147,14 @@ EipStatus EthLnkPostGetCallback
      *  clear our GetAttributeSingle PreCallback execution counters. */
     switch (attribute->attribute_number) {
     case 4:
-      iface_calls[inst_no-1] = 0u;
+      iface_calls[inst_no-1] = 0U;
       break;
     case 5:
-      media_calls[inst_no-1] = 0u;
+      media_calls[inst_no-1] = 0U;
       /* This is a concession to the conformance test tool that expects
        *  the media counters to be zero after a GetAndClear service. */
       for (int idx = 0; idx < 12; ++idx) {
-        g_ethernet_link[inst_no-1].media_cntrs.cntr32[idx] = 0u;
+        g_ethernet_link[inst_no-1].media_cntrs.cntr32[idx] = 0U;
       }
       break;
     default:

+ 11 - 0
source/src/ports/POSIX/sample_application/opener_user_conf.h

@@ -47,6 +47,7 @@
   /* Enable all the stuff the DLR device depends on */
   #define OPENER_TCPIP_IFACE_CFG_SETTABLE 1
   #define OPENER_ETHLINK_CNTRS_ENABLE     1
+  #define OPENER_ETHLINK_IFACE_CTRL_ENABLE  1
   #define OPENER_ETHLINK_LABEL_ENABLE     1
   #define OPENER_ETHLINK_INSTANCE_CNT     3
 #endif
@@ -100,6 +101,16 @@
   #define OPENER_ETHLINK_CNTRS_ENABLE 0
 #endif
 
+/** @brief Set this define if you need Interface Control for Ethernet Link object
+ *
+ * This define enables the Interface Control attribute (#6) as a settable
+ *  attribute which is required for a DLR device. This also enables the storage
+ *  of the attribute in NV data storage area.
+ */
+#ifndef OPENER_ETHLINK_IFACE_CTRL_ENABLE
+  #define OPENER_ETHLINK_IFACE_CTRL_ENABLE 0
+#endif
+
 
 
 /** @brief Define the number of objects that may be used in connections

+ 1 - 1
source/src/ports/WIN32/CMakeLists.txt

@@ -18,7 +18,7 @@ add_library( ${PLATFORMLIBNAME} ${PLATFORM_SPEC_SRC})
 
 add_executable(OpENer main.c)
 
-target_link_libraries( OpENer PLATFORM_GENERIC ${PLATFORMLIBNAME} CIP Utils  SAMPLE_APP ENET_ENCAP NVDATA ws2_32 ${OpENer_CIP_OBJECTS} )
+target_link_libraries( OpENer PLATFORM_GENERIC ${PLATFORMLIBNAME} CIP Utils  SAMPLE_APP ENET_ENCAP NVDATA ws2_32 Iphlpapi ${OpENer_CIP_OBJECTS} )
 
 # Add additional CIP Objects
 string(COMPARE NOTEQUAL "${OpENer_ADD_CIP_OBJECTS}" "" OpENer_HAS_ADDITIONAL_OBJECT )

+ 157 - 83
source/src/ports/WIN32/main.c

@@ -1,103 +1,177 @@
 /*******************************************************************************
- * Copyright (c) 2009, Rockwell Automation, Inc.
- * All rights reserved.
- *
- ******************************************************************************/
+* Copyright (c) 2009, Rockwell Automation, Inc.
+* All rights reserved.
+*
+******************************************************************************/
 #include <stdio.h>
 #include <stdlib.h>
 #include <signal.h>
 
 #include "generic_networkhandler.h"
 #include "opener_api.h"
-#include "cipcommon.h"
+#include "cipethernetlink.h"
+#include "ciptcpipinterface.h"
 #include "trace.h"
 #include "networkconfig.h"
+#include "doublylinkedlist.h"
+#include "cipconnectionobject.h"
 #include "nvdata.h"
 
-extern int newfd;
+#define BringupNetwork(if_name, method, if_cfg, hostname)  (0)
+#define ShutdownNetwork(if_name)  (0)
+
+/** If OpENer is aborted by a signal it returns the sum of the signal number
+*  and this define. */
+#define RET_SHOW_SIGNAL 200
+
+/******************************************************************************/
+/** @brief Signal handler function for ending stack execution
+*
+* @param signal  the signal we received
+*/
+static void LeaveStack(int signal);
 
 /******************************************************************************/
-/*!\brief Signal handler function for ending stack execution
- *
- * @param pa_nSig the signal we received
- */
-void
-LeaveStack(int pa_nSig);
+/** @brief Execute OpENer stack loop function
+*
+* @param   thread_arg  dummy argument
+* @returns             NO_ERROR at the moment
+*
+*  The call signature is chosen to be able to pass this function directly as
+*  parameter for CreateThread().
+*/
+static DWORD executeEventLoop(LPVOID thread_arg);
+
 
 /*****************************************************************************/
-/*! \brief Flag indicating if the stack should end its execution
- */
-int g_end_stack = 0;
+/** @brief Flag indicating if the stack should end its execution
+*/
+volatile int g_end_stack = 0;
 
 /******************************************************************************/
-int main(int argc,
-         char *arg[]) {
-  EipUint16 nUniqueConnectionID;
-
-  if (argc != 2) {
-    printf("Wrong number of command line parameters!\n");
-    printf("The correct command line parameters are:\n");
-    printf(
-      "./OpENer index\n");
-    printf(
-      "    e.g. ./OpENer index\n");
-    exit(0);
-  } else {
-    DoublyLinkedListInitialize(&connection_list,
-                               CipConnectionObjectListArrayAllocator,
-                               CipConnectionObjectListArrayFree);
-    /* fetch Internet address info from the platform */
-    ConfigureDomainName(atoi(arg[1]) );
-    ConfigureHostName(atoi(arg[1]) );
-    ConfigureIpMacAddress(atoi(arg[1]) );
-  }
-
-  /*for a real device the serial number should be unique per device */
-  SetDeviceSerialNumber(123456789);
-
-  /* nUniqueConnectionID should be sufficiently random or incremented and stored
-   *  in non-volatile memory each time the device boots.
-   */
-  nUniqueConnectionID = rand();
-
-  /* Setup the CIP Layer */
-  CipStackInit(nUniqueConnectionID);
-
-  /* The CIP objects are now created and initialized with their default values.
-   *  Now any NV data values are loaded to change the data to the stored
-   *  configuration.
-   */
-  if (kEipStatusError == NvdataLoad()) {
-    OPENER_TRACE_WARN("Loading of some NV data failed. Maybe the first start?\n");
-  }
-
-  /* Setup Network Handles */
-  if ( kEipStatusOk == NetworkHandlerInitialize() ) {
-    g_end_stack = 0;
-#ifndef WIN32
-    /* register for closing signals so that we can trigger the stack to end */
-    signal(SIGHUP, LeaveStack);
-    signal(SIGINT, LeaveStack); /* needed to be able to abort with ^C */
-#endif
-
-    /* The event loop. Put other processing you need done continually in here */
-    while (1 != g_end_stack) {
-      if ( kEipStatusOk != NetworkHandlerProcessOnce() ) {
-        break;
-      }
-    }
-
-    /* clean up network state */
-    NetworkHandlerFinish();
-  }
-  /* close remaining sessions and connections, cleanup used data */
-  ShutdownCipStack();
-
-  return -1;
+int main(int argc, char *arg[]) {
+
+	if (argc != 2) {
+		fprintf(stderr, "Wrong number of command line parameters!\n");
+		fprintf(stderr, "Usage: %s [interface index | interface name]\n", arg[0]);
+		fprintf(stderr, "\te.g. ./OpENer \"Ethernet 2\"\n");
+		exit(EXIT_FAILURE);
+	}
+
+	DoublyLinkedListInitialize(&connection_list,
+		CipConnectionObjectListArrayAllocator,
+		CipConnectionObjectListArrayFree);
+	/* Fetch MAC address from the platform. This tests also if the interface
+	*  is present. */
+	uint8_t iface_mac[6];
+	if (kEipStatusError == IfaceGetMacAddress(arg[1], iface_mac)) {
+		printf("Network interface %s not found.\n", arg[1]);
+		exit(EXIT_FAILURE);
+	}
+
+	/* for a real device the serial number should be unique per device */
+	SetDeviceSerialNumber(123456789);
+
+	/* unique_connection_id should be sufficiently random or incremented and stored
+	*  in non-volatile memory each time the device boots.
+	*/
+	EipUint16 unique_connection_id = rand();
+
+	/* Setup the CIP Layer. All objects are initialized with the default
+	* values for the attribute contents. */
+	CipStackInit(unique_connection_id);
+
+	CipEthernetLinkSetMac(iface_mac);
+
+	/* The current host name is used as a default. This value is kept in the
+	*  case NvdataLoad() needs to recreate the TCP/IP object's settings from
+	*  the defaults on the first start without a valid TCP/IP configuration
+	*  file.
+	*/
+	GetHostName(&g_tcpip.hostname);
+
+	/* The CIP objects are now created and initialized with their default values.
+	*  After that any NV data values are loaded to change the attribute contents
+	*  to the stored configuration.
+	*/
+	if (kEipStatusError == NvdataLoad()) {
+		OPENER_TRACE_WARN("Loading of some NV data failed. Maybe the first start?\n");
+	}
+
+	/* Bring up network interface or start DHCP client ... */
+	EipStatus status = BringupNetwork(arg[1],
+		g_tcpip.config_control,
+		&g_tcpip.interface_configuration,
+		&g_tcpip.hostname);
+	if (status < 0) {
+		OPENER_TRACE_ERR("BringUpNetwork() failed\n");
+	}
+
+	/* Register signal handler for SIGINT and SIGTERM that are "supported"
+	*  under Windows. */
+	g_end_stack = 0;
+	signal(SIGINT, LeaveStack);
+	signal(SIGTERM, LeaveStack);
+
+	/* Next actions depend on the set network configuration method. */
+	CipDword network_config_method = g_tcpip.config_control & kTcpipCfgCtrlMethodMask;
+	if (kTcpipCfgCtrlStaticIp == network_config_method) {
+		OPENER_TRACE_INFO("Static network configuration done\n");
+	}
+	if (kTcpipCfgCtrlDhcp == network_config_method) {
+		OPENER_TRACE_INFO("DHCP network configuration started\n");
+		/* DHCP should already have been started with BringupNetwork(). Wait
+		* here for IP present (DHCP done) or abort through g_end_stack. */
+		status = IfaceWaitForIp(arg[1], -1, &g_end_stack);
+		OPENER_TRACE_INFO("DHCP wait for interface: status %d, g_end_stack=%d\n",
+			status, g_end_stack);
+		if (kEipStatusOk == status && 0 == g_end_stack) {
+			/* Read IP configuration received via DHCP from interface and store in
+			*  the TCP/IP object.*/
+			status = IfaceGetConfiguration(arg[1], &g_tcpip.interface_configuration);
+			if (status < 0) {
+				OPENER_TRACE_WARN("Problems getting interface configuration\n");
+			}
+		}
+	}
+
+
+	/* The network initialization of the EIP stack for the NetworkHandler. */
+	if (!g_end_stack && kEipStatusOk == NetworkHandlerInitialize()) {
+
+		(void)executeEventLoop(NULL);
+
+		/* clean up network state */
+		NetworkHandlerFinish();
+	}
+
+	/* close remaining sessions and connections, clean up used data */
+	ShutdownCipStack();
+
+	/* Shut down the network interface now. */
+	(void)ShutdownNetwork(arg[1]);
+
+	if (0 != g_end_stack) {
+		printf("OpENer aborted by signal %d.\n", g_end_stack);
+		return RET_SHOW_SIGNAL + g_end_stack;
+	}
+
+	return EXIT_SUCCESS;
 }
 
-void LeaveStack(int pa_nSig) {
-  (void) pa_nSig; /* kill unused parameter warning */
-  OPENER_TRACE_STATE("got signal %d\n",pa_nSig);
-  g_end_stack = 1;
+static void LeaveStack(int signal) {
+	if (SIGINT == signal || SIGTERM == signal) {
+		g_end_stack = signal;
+	}
+	OPENER_TRACE_STATE("got signal %d\n", signal);
 }
+
+static DWORD executeEventLoop(LPVOID thread_arg) {
+	/* The event loop. Put other processing you need done continually in here */
+	while (0 == g_end_stack) {
+		if (kEipStatusOk != NetworkHandlerProcessOnce()) {
+			break;
+		}
+	}
+	return NO_ERROR;
+}

+ 390 - 226
source/src/ports/WIN32/networkconfig.c

@@ -3,199 +3,191 @@
  * All rights reserved.
  *
  ******************************************************************************/
+
+/* ---------- Include files ---------------------------- */
 #define WIN32_LEAN_AND_MEAN
-#include "ciptcpipinterface.h"
 #include "networkconfig.h"
-#include "cipcommon.h"
-#include "ciperror.h"
-#include "opener_api.h"
-#include "trace.h"
+
+#include <errno.h>
+#include <inttypes.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+
 #include <winsock2.h>
 #include <windows.h>
+#include <ws2tcpip.h>
 #include <iphlpapi.h>
-#include <Ws2tcpip.h>
-
-#define WORKING_BUFFER_SIZE 15000
-#define MAX_TRIES 3
 
-#pragma comment(lib, "IPHLPAPI.lib")
-#pragma comment(lib, "Ws2_32.lib")
+#include "cipcommon.h"
+#include "cipstring.h"
+#include "opener_api.h"
+#include "opener_error.h"
+#include "trace.h"
 
-static EipStatus WideToCipString(const WCHAR *const src,
-                                 CipString *const dest);
 
-static CipUdint GetDnsServerAddress(
-  const IP_ADAPTER_DNS_SERVER_ADDRESS_XP *const RESTRICT in);
+/* ---------- Macro definitions ------------------------ */
+#define MALLOC(x) malloc(x)
+#define FREE(x)   free(x)
 
+/* ----- Windows types PRI macros ------------- */
+#define PRIDW   "lu"
+#define PRIUL   "lu"
+#define PRIuSZT PRIuPTR
+#define PRIxSZT PRIxPTR
 
-void ConfigureIpMacAddress(const CipUint interface_index) {
 
-  PIP_ADAPTER_INFO pAdapterInfo;
-  PIP_ADAPTER_INFO pAdapter = NULL;
-  CipDword dwRetVal = 0;
+/* ---------- Local functions implementation ----------- */
 
-  CipUdint ulOutBufLen = sizeof(IP_ADAPTER_INFO);
-  pAdapterInfo = (IP_ADAPTER_INFO *)CipCalloc(1,sizeof(IP_ADAPTER_INFO) );
-  if (pAdapterInfo == NULL) {
-    printf("Error allocating memory needed to call GetAdaptersinfo\n");
-    return 1;
-  }
-  // Make an initial call to GetAdaptersInfo to get
-  // the necessary size into the ulOutBufLen variable
-  if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
-    CipFree(pAdapterInfo);
-    pAdapterInfo = (IP_ADAPTER_INFO *)CipCalloc(ulOutBufLen,sizeof(CipUdint) );
-    if (pAdapterInfo == NULL) {
-      printf("Error allocating memory needed to call GetAdaptersinfo\n");
-      return 1;
+/** @brief Extract interface index from string if string is a number
+ *
+ *  @param  iface   interface identifier string
+ *  @return         converted number; on failure errno != 0
+ *
+ * Convert a number (interface index) from the supplied interface identifier
+ *  string if possible. On conversion success @p errno is set to zero.
+ *  On failure @p errno is set to ERANGE or EINVAL.
+ */
+static ULONG StrToIfaceIdx(const char *iface)
+{
+    ULONG iface_idx;
+    char  *end;
+    /* The input string is a decimal interface index number or an
+     *  interface name. */
+    errno = 0;  /* To distinguish success / failure later */
+    iface_idx = strtoul(iface, &end, 10);
+    /* overflow is signaled by (errno == ERANGE) */
+
+    if ((iface == end) ||   /* No digits were found */
+        ('\0' != *end)) {   /* More characters after number */
+        errno = EINVAL;     /* Signal conversion failure */
     }
-  }
-
+    return iface_idx;
+}
 
-  if ( (dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) ) == NO_ERROR ) {
-    pAdapter = pAdapterInfo;
-    while (pAdapter) {
-      if (pAdapter->Index == interface_index) {
-        for (size_t i = 0; i < 6; i++) {
-          memcpy(&g_ethernet_link->physical_address, pAdapter->Address,
-                 6 * sizeof(CipUsint) );
-        }
+/** @brief Derive the interface index from a given interface alias name string
+ *
+ *  @param  iface     interface identifier string
+ *  @param  if_index  pointer to return the derived interface index
+ *  @return           Success: NO_ERROR, Failure any matching Windows error code
+ *
+ * The interface alias name supplied via the @p iface parameter is first
+ *  converted from a multi-byte string to a wide character string to be able to
+ *  call ConvertInterfaceAliasToLuid(). This function returns the Locally
+ *  Unique IDentifier (LUID) if an interface matching the alias name is found.
+ *  The match doesn't need to be exact. A matching unique prefix of the alias
+ *  is sufficient.
+ * The LUID can then be used to retrieve the interface index needed for the
+ *  other functions by calling ConvertInterfaceLuidToIndex().
+ */
+static DWORD ConvertToIndexFromFakeAlias(const char *iface, PNET_IFINDEX iface_idx)
+{
+    WCHAR       *p_if_alias;
+    NET_LUID    if_luid;
+
+    size_t mbtowc_rc = mbstowcs(NULL, iface, 0);
+    if ((size_t)-1 == mbtowc_rc) {  /* Invalid byte sequence encountered */
+        return ERROR_NO_UNICODE_TRANSLATION;
+    }
 
-        inet_pton(AF_INET, pAdapter->IpAddressList.IpAddress.String,
-                  &g_tcpip.interface_configuration.ip_address);
-        inet_pton(AF_INET, pAdapter->IpAddressList.IpMask.String,
-                  &g_tcpip.interface_configuration.network_mask);
-        inet_pton(AF_INET, pAdapter->GatewayList.IpAddress.String,
-                  &g_tcpip.interface_configuration.gateway);
+    size_t wc_cnt = mbtowc_rc+1U; /* +1U for nul character */
+    p_if_alias = MALLOC(sizeof(WCHAR) * wc_cnt);
+    if (NULL == p_if_alias) {
+        return ERROR_OUTOFMEMORY;
+    }
 
-        /* Calculate the CIP multicast address. The multicast address is
-         * derived from the current IP address. */
-        CipTcpIpCalculateMulticastIp(&g_tcpip);
-      }
-      pAdapter = pAdapter->Next;
+    (void) mbstowcs(p_if_alias, iface, wc_cnt);
+    DWORD cnv_status = ConvertInterfaceAliasToLuid(p_if_alias, &if_luid);
+    if (NETIO_SUCCESS(cnv_status)) {
+        cnv_status = ConvertInterfaceLuidToIndex(&if_luid, iface_idx);
     }
-  }
-  else {
-    printf("GetAdaptersInfo failed with error: %d\n", dwRetVal);
+    FREE(p_if_alias);
+    return cnv_status;
+}
 
+/** @brief Determines the interface index number from string input
+ *
+ *  @param  iface     string with interface index number or interface alias name
+ *  @param  iface_idx address of interface index destination variable
+ *  @param            EipStatusOk on success, EipStatusError on failure
+ *
+ * This function tries to determine a Windows interface index from the @ref iface
+ *  string.
+ *
+ * At first it is tried to evaluate the input as a decimal number if that
+ *  succeeds the function returns the converted number and EipStatusOk.
+ *
+ * If the input string is not a number it is assumed to be a Windows interface
+ *  alias name. This function then in turn calls ConvertToIndexFromFakeAlias()
+ *  to find an interface matching that alias.
+ */
+static EipStatus DetermineIfaceIndexByString(const char *iface, PNET_IFINDEX iface_idx)
+{
+  *iface_idx = StrToIfaceIdx(iface);
+
+  BOOL arg_is_numerical = (0 == errno);
+  if (!arg_is_numerical) {
+    DWORD cnv_status = ConvertToIndexFromFakeAlias(iface, iface_idx);
+    if (NO_ERROR != cnv_status) {
+      char *error_message = GetErrorMessage(cnv_status);
+      OPENER_TRACE_ERR("ConvertToIndexFromFakeAlias() failed: %" PRIDW " - %s\n",
+                       cnv_status, error_message);
+      FreeErrorMessage(error_message);
+      return kEipStatusError;
+    }
   }
-  CipFree(pAdapterInfo);
-  CipFree(pAdapter);
+  return kEipStatusOk;
 }
 
-void ConfigureDomainName(const CipUint interface_index) {
-
-  CipDword dwSize = 0;
-  int i = 0;
-  // Set the flags to pass to GetAdaptersAddresses
-  CipUdint flags = GAA_FLAG_INCLUDE_PREFIX;
-  CipDword dwRetVal = 0;
-  // default to unspecified address family (both)
-  CipUdint family = AF_UNSPEC;
-
-  LPVOID lpMsgBuf = NULL;
-
-  PIP_ADAPTER_ADDRESSES pAddresses = NULL;
-  PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
-  IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = NULL;
-  CipUdint outBufLen = 0;
-  CipUdint tries = 0;
-
-  family = AF_INET;
-  // Allocate a 15 KB buffer to start with.
-  outBufLen = WORKING_BUFFER_SIZE;
-
+/** @brief Retrieve an IP_ADAPTER_ADDRESSES table in an allocated memory block
+ *
+ *  @param  flags         specify what kind of information to include in the
+ *                        result
+ *  @param  pp_addr_table pointer to the location where to put the
+ *                        PIP_ADAPTER_ADDRESSES pointer
+ *
+ * This function encapsulates the needed memory allocation and retry logic that
+ *  is needed to call GetAdaptersAddresses() successfully and to retrieve the
+ *  complete IP_ADAPTER_ADDRESSES table.
+ * The @p flags parameter is used to tell the GetAdaptersAddresses() function
+ *  which information to include and what information to exclude from the
+ *  result.
+ */
+EipStatus RetrieveAdapterAddressesTable(ULONG flags, PIP_ADAPTER_ADDRESSES *pp_addr_table) {
+  PIP_ADAPTER_ADDRESSES p_addr_table;
+  ULONG ret_val;
+  /* Start allocating with a guessed minimum size. */
+  ULONG outBufLen = 16 * sizeof(IP_ADAPTER_ADDRESSES);
   do {
-
-    pAddresses = (IP_ADAPTER_ADDRESSES *)CipCalloc(1,outBufLen);
-    if (pAddresses == NULL) {
-      printf
-        ("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
-      exit(1);
+    p_addr_table = (PIP_ADAPTER_ADDRESSES)MALLOC(outBufLen);
+    if (NULL == p_addr_table) {
+      OPENER_TRACE_ERR("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
+      return kEipStatusError;
     }
+    ret_val = GetAdaptersAddresses(AF_INET, flags, NULL, p_addr_table, &outBufLen);
 
-    dwRetVal =
-      GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);
-
-    if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
-      CipFree(pAddresses);
-      pAddresses = NULL;
-    }
-    else {
-      break;
+    if (ERROR_BUFFER_OVERFLOW == ret_val) {
+      FREE(p_addr_table);
+      p_addr_table = NULL;
     }
+  } while (ERROR_BUFFER_OVERFLOW == ret_val);
 
-    tries++;
-
-  } while ( (dwRetVal == ERROR_BUFFER_OVERFLOW) && (tries < MAX_TRIES) );
-
-  if (dwRetVal == NO_ERROR) {
-    // If successful, output some information from the data we received
-    pCurrAddresses = pAddresses;
-    while (pCurrAddresses) {
-      if (interface_index == pCurrAddresses->IfIndex) {
-        pDnServer = pCurrAddresses->FirstDnsServerAddress;
-        if (pDnServer) {
-          for (i = 0; pDnServer != NULL; i++) {
-            pDnServer = pDnServer->Next;
-          }
-        }
-
-        char pStringBuf[INET_ADDRSTRLEN];
-        if (i != 0) {
-          WideToCipString(pCurrAddresses->DnsSuffix,
-                          &g_tcpip.interface_configuration.domain_name);
-
-          g_tcpip.interface_configuration.name_server =
-            GetDnsServerAddress(pCurrAddresses->FirstDnsServerAddress);
-          g_tcpip.interface_configuration.name_server_2 =
-            (pCurrAddresses->FirstDnsServerAddress != NULL)
-            ? GetDnsServerAddress(
-              pCurrAddresses->FirstDnsServerAddress->Next)
-            : 0;
-        }
-        else{ g_tcpip.interface_configuration.domain_name.length = 0;}
-
-      }
-      pCurrAddresses = pCurrAddresses->Next;
+  if (NO_ERROR != ret_val || NULL == p_addr_table) {
+    if (NULL != p_addr_table) {
+      FREE(p_addr_table);
+      p_addr_table = NULL;
     }
+    char *error_message = GetErrorMessage(ret_val);
+    OPENER_TRACE_ERR("GetAdaptersAddresses() failed: %" PRIUL " - %s\n",
+                     ret_val, error_message);
+    FreeErrorMessage(error_message);
+    return kEipStatusError;
   }
-  else {
-    OPENER_TRACE_INFO("Call to GetAdaptersAddresses failed with error: %d\n",
-                      dwRetVal);
-    if (dwRetVal == ERROR_NO_DATA) {
-      OPENER_TRACE_INFO(
-        "\tNo addresses were found for the requested parameters\n");
-    }
-    else {
-
-      if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
-                        FORMAT_MESSAGE_FROM_SYSTEM |
-                        FORMAT_MESSAGE_IGNORE_INSERTS,
-                        NULL, dwRetVal,
-                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-                        // Default language
-                        (LPTSTR)&lpMsgBuf, 0, NULL) ) {
-        OPENER_TRACE_INFO("\tError: %s", lpMsgBuf);
-        CipFree(lpMsgBuf);
-        if (pAddresses) {
-          CipFree(pAddresses);
-        }
-        exit(1);
-      }
-    }
-  }
-
-  if (pAddresses) {
-    CipFree(pAddresses);
-  }
+  *pp_addr_table = p_addr_table;
+  return kEipStatusOk;
 }
 
-
 /** @brief Converts a wide-character string to a CIP string.
  *
  * @param src Source wide-character string.
@@ -206,77 +198,260 @@ void ConfigureDomainName(const CipUint interface_index) {
  *         kEipStatusError if a memory allocation error occurred or
  *         the source string was too large.
  */
-static EipStatus WideToCipString(const WCHAR *const src,
-                                 CipString *const dest) {
+static DWORD WideToCipString(const WCHAR *const src,
+                             CipString *const dest) {
   void *buf = NULL;
 
   OPENER_ASSERT(src != NULL);
   OPENER_ASSERT(dest != NULL);
 
   /*
-   * Evaluate the source string, ensuring the number of characters fit in
-   * EipUint16, excluding the null terminator.
+   * Evaluate the source string, ensuring two properties:
+   *  1) the source string can be encoded as multi-byte sequence
+   *  2) the number of characters fits in EipUint16, excluding
+   *    the nul terminator.
    */
-  const size_t num_chars = wcslen(src);
+  const size_t num_chars = wcstombs(NULL, src, 0);
+  if ((size_t)-1 == num_chars) {
+    return ERROR_NO_UNICODE_TRANSLATION;
+  }
   if (num_chars >= UINT16_MAX) {
-    return kEipStatusError;
+    return ERROR_BUFFER_OVERFLOW;
   }
 
-  /* New buffer includes null termination. */
-  const size_t buffer_size = num_chars + 1;
+  /* New buffer includes nul termination. */
+  const size_t buffer_size = num_chars + 1U;
 
   if (num_chars) {
     /* Allocate a new destination buffer. */
-    buf = CipCalloc(buffer_size, 1);
-    if (buf == NULL) {
-      return kEipStatusError;
+    buf = MALLOC(buffer_size);
+    if (NULL == buf) {
+      return ERROR_OUTOFMEMORY;
     }
 
     /* Transfer the string to the new buffer. */
-    size_t converted_chars;
-    const errno_t result =
-      wcstombs_s(&converted_chars, buf, buffer_size, src, num_chars);
-    OPENER_ASSERT(result == 0);
+    size_t cnv_chars = wcstombs(buf, src, buffer_size);
+    OPENER_ASSERT(cnv_chars == num_chars);
   }
 
   /* Release the any previous string content. */
-  if (dest->string != NULL) {
-    CipFree(dest->string);
-  }
+  FreeCipString(dest);
 
   /* Transfer the new content to the destination. */
   dest->length = num_chars;
   dest->string = buf;
 
-  /* Output sanity checks. */
-  if (dest->length) {
-    const size_t len = strnlen_s(dest->string, buffer_size);
-    OPENER_ASSERT(len < buffer_size);
-    OPENER_ASSERT(dest->length == len);
-    OPENER_ASSERT(dest->string != NULL);
-  } else {
-    OPENER_ASSERT(dest->string == NULL);
+  return ERROR_SUCCESS;
+}
+
+/** @brief Extract IPv4 IP address from SOCKET_ADDRESS structure as CipUdint
+ *
+ *  @param  socket_address  pointer to a Windows SOCKET_ADDRESS structure
+ *  @return                 IPv4 address taken from @p socket_address
+ */
+static CipUdint GetIpFromSocketAddress(const SOCKET_ADDRESS *socket_address) {
+  SOCKADDR_IN *sin = ((SOCKADDR_IN*)socket_address->lpSockaddr);
+  return sin->sin_addr.S_un.S_addr;
+}
+
+
+/* ---------- Public functions implementation ---------- */
+
+/* For Doxygen descriptions see opener_api.h. */
+EipStatus IfaceGetMacAddress(const char *iface, uint8_t *physical_address) {
+  ULONG  iface_idx;
+
+  if(kEipStatusOk != DetermineIfaceIndexByString(iface, &iface_idx)) {
+    return kEipStatusError;
   }
 
-  return kEipStatusOk;
+  /* Select what to include in or exclude from the adapter addresses table. */
+  const ULONG flags = GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST |
+                      GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER |
+                      GAA_FLAG_SKIP_FRIENDLY_NAME;
+  PIP_ADAPTER_ADDRESSES p_addr_table = NULL;
+  if (kEipStatusOk != RetrieveAdapterAddressesTable(flags, &p_addr_table)) {
+    return kEipStatusError;
+  }
+
+  /* Now search the right interface in the adapter addresses table. */
+  PIP_ADAPTER_ADDRESSES p_addr_entry = p_addr_table;
+  while (NULL != p_addr_entry) {
+    if (iface_idx == p_addr_entry->IfIndex) {
+      /* Get MAC address from matched interface */
+      OPENER_TRACE_INFO("MAC address: %02" PRIX8 "-%02" PRIX8 "-%02" PRIX8
+                                    "-%02" PRIX8 "-%02" PRIX8 "-%02" PRIX8 "\n",
+        p_addr_entry->PhysicalAddress[0], p_addr_entry->PhysicalAddress[1],
+        p_addr_entry->PhysicalAddress[2], p_addr_entry->PhysicalAddress[3],
+        p_addr_entry->PhysicalAddress[4], p_addr_entry->PhysicalAddress[5]);
+        memcpy_s(physical_address, 6,
+                p_addr_entry->PhysicalAddress, p_addr_entry->PhysicalAddressLength);
+      break;  /* leave search after iface_idx match */
+    }
+    p_addr_entry = p_addr_entry->Next;
+  }
+  FREE(p_addr_table);
+
+  /* Return success if we searched the table and had a match. */
+  return (p_addr_entry) ? kEipStatusOk : kEipStatusError;
 }
 
+/* For Doxygen descriptions see opener_api.h. */
+EipStatus IfaceGetConfiguration(const char *iface,
+                                CipTcpIpInterfaceConfiguration *iface_cfg)
+{
+  ULONG  iface_idx;
 
-/** @brief Extracts a DNS server IP address.
- *
- * @param in DNS server address structure from GetAdapterAddresses().
- *
- * @return The IPv4 address in network byte order.
- */
-static CipUdint GetDnsServerAddress(
-  const IP_ADAPTER_DNS_SERVER_ADDRESS_XP *const RESTRICT in) {
-  return (in != NULL)
-         ? ( (SOCKADDR_IN *)in->Address.lpSockaddr )->sin_addr.S_un.S_addr
-         : 0;
+  if(kEipStatusOk != DetermineIfaceIndexByString(iface, &iface_idx)) {
+    return kEipStatusError;
+  }
+
+  /* Select what to include in or exclude from the adapter addresses table. */
+  const ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
+                      GAA_FLAG_INCLUDE_GATEWAYS | GAA_FLAG_INCLUDE_PREFIX;
+  PIP_ADAPTER_ADDRESSES p_addr_table = NULL;
+  if (kEipStatusOk != RetrieveAdapterAddressesTable(flags, &p_addr_table)) {
+    return kEipStatusError;
+  }
+
+  CipTcpIpInterfaceConfiguration local_cfg;
+  memset(&local_cfg, 0x00, sizeof local_cfg);
+
+  /* Now search the right interface in the adapter addresses table. */
+  PIP_ADAPTER_ADDRESSES p_addr_entry = p_addr_table;
+  while (NULL != p_addr_entry) {
+    if (iface_idx == p_addr_entry->IfIndex) {
+
+      if (IfOperStatusUp != p_addr_entry->OperStatus) {
+        OPENER_TRACE_ERR("IfaceGetConfiguration(): Interface '%s' is not up.\n", iface);
+        FREE(p_addr_table);
+        return kEipStatusError;
+      }
+      /* Extract ip_addr, netmask, gateway, nameserver, nameserver 2, domain ... */
+      {
+        PIP_ADAPTER_UNICAST_ADDRESS pUnicast = p_addr_entry->FirstUnicastAddress;
+        if (NULL != pUnicast) {
+          local_cfg.ip_address = GetIpFromSocketAddress(&pUnicast->Address);
+        }
+      }
+      {
+        PIP_ADAPTER_PREFIX pPrefix = p_addr_entry->FirstPrefix;
+        if (NULL != pPrefix) {
+          local_cfg.network_mask = htonl(0xffffffff << (32U - pPrefix->PrefixLength));
+        }
+      }
+      {
+        PIP_ADAPTER_GATEWAY_ADDRESS pGateway = p_addr_entry->FirstGatewayAddress;
+        if (NULL != pGateway) {
+          local_cfg.gateway = GetIpFromSocketAddress(&pGateway->Address);
+        }
+      }
+      {
+        IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = p_addr_entry->FirstDnsServerAddress;
+        if (NULL != pDnServer) {
+          local_cfg.name_server = GetIpFromSocketAddress(&pDnServer->Address);
+          pDnServer = pDnServer->Next;
+          if (NULL != pDnServer) {
+            local_cfg.name_server_2= GetIpFromSocketAddress(&pDnServer->Address);
+          }
+        }
+      }
+      DWORD ret_val = WideToCipString(p_addr_entry->DnsSuffix,
+                                      &local_cfg.domain_name);
+      if (NO_ERROR != ret_val) {
+        char *error_message = GetErrorMessage(ret_val);
+        OPENER_TRACE_ERR("WideToCipString(DnsSuffix) failed with error: %"
+                         PRIDW " - %s\n", ret_val, error_message);
+        FreeErrorMessage(error_message);
+        FREE(p_addr_table);
+        return kEipStatusError;
+      }
+      break;  /* leave search after iface_idx match */
+    }
+    p_addr_entry = p_addr_entry->Next;
+  }
+  FREE(p_addr_table);
+
+  if (p_addr_entry) {
+    /* Free first and then making a shallow copy of local_cfg.domain_name is
+     *  ok, because local_cfg goes out of scope on return. */
+    FreeCipString(&iface_cfg->domain_name);
+    *iface_cfg = local_cfg;
+  }
+
+  /* Return success if we searched the table and had a match. */
+  return (p_addr_entry) ? kEipStatusOk : kEipStatusError;
 }
 
+/* For Doxygen descriptions see opener_api.h. */
+EipStatus IfaceWaitForIp(const char *iface, int timeout, volatile int *abort_wait) {
+  ULONG  iface_idx;
+
+  if(kEipStatusOk != DetermineIfaceIndexByString(iface, &iface_idx)) {
+    return kEipStatusError;
+  }
+
+  {
+    PMIB_IPADDRTABLE  pmib_ipaddr_table = NULL;
+    ULONG addr_table_sz = 0;
+    uint32_t ipaddr;
+
+#define WAIT_CYCLE_MS 100
+    /* Calculate cycles of SleepEx(WAIT_CYCLE_MS) needed. */
+    timeout *= (1000/WAIT_CYCLE_MS);
+    do {
+      DWORD dw_ret;
+      ipaddr = 0U;
+
+      do {
+        dw_ret = GetIpAddrTable(pmib_ipaddr_table, &addr_table_sz, FALSE);
+        if (ERROR_INSUFFICIENT_BUFFER == dw_ret) {
+          if (pmib_ipaddr_table) {
+            FREE(pmib_ipaddr_table);
+          }
+          pmib_ipaddr_table = MALLOC(addr_table_sz);
+          if (NULL == pmib_ipaddr_table) {
+            OPENER_TRACE_ERR("Memory allocation failed for "
+                             "MIB_IPADDRTABLE struct\n");
+            return kEipStatusError;
+          }
+        }
+      } while (ERROR_INSUFFICIENT_BUFFER == dw_ret);
+      if (NO_ERROR != dw_ret) {
+        char *error_message = GetErrorMessage(dw_ret);
+        OPENER_TRACE_ERR("%s() failed with error: %" PRIDW " - %s\n",
+                         __func__, dw_ret, error_message);
+        FreeErrorMessage(error_message);
+        return kEipStatusError;
+      }
+
+      /* Search entry matching the interface index and determine IP address. */
+      for (int i=0; i < (int) pmib_ipaddr_table->dwNumEntries; i++) {
+        if (pmib_ipaddr_table->table[i].dwIndex == iface_idx) {
+          if (0 == (pmib_ipaddr_table->table[i].wType &
+                    (MIB_IPADDR_DELETED | MIB_IPADDR_DISCONNECTED | MIB_IPADDR_TRANSIENT))) {
+            ipaddr = pmib_ipaddr_table->table[i].dwAddr;
+          }
+        }
+      }
 
-void ConfigureHostName(const CipUint interface_index) {
+      if (timeout > 0) {
+        --timeout;
+      }
+    } while ((0 == ipaddr) && (0 != timeout) && (0 == *abort_wait) &&
+             (0 == SleepEx(WAIT_CYCLE_MS, FALSE)));
+
+    OPENER_TRACE_INFO("IP=%08" PRIx32 ", timeout=%d\n", (uint32_t)ntohl(ipaddr), timeout);
+    if (pmib_ipaddr_table) {
+      FREE(pmib_ipaddr_table);
+    }
+  }
+  return kEipStatusOk;
+}
+
+#define HOST_NAME_MAX 256 /* Should be long enough according rfc1132. */
+void GetHostName(CipString *hostname)
+{
   CipWord wVersionRequested;
   WSADATA wsaData;
   int err;
@@ -286,35 +461,24 @@ void ConfigureHostName(const CipUint interface_index) {
 
   err = WSAStartup(wVersionRequested, &wsaData);
   if (err != 0) {
-    /* Tell the user that we could not find a usable */
-    /* Winsock DLL.                                  */
-    printf("WSAStartup failed with error: %d\n", err);
-    return 1;
+    /* Tell the user that we could not find a usable Winsock DLL.  */
+    char *error_message = GetErrorMessage(err);
+    printf("WSAStartup failed with error: %d - %s\n",
+            err, error_message);
+    FreeErrorMessage(error_message);
+    return;
   }
 
-  char hostname[256] = "";
-  int status = 0;
-  status = gethostname(hostname, sizeof(hostname) );
-
-  WSACleanup();
-
-
-
-
-  if (NULL != g_tcpip.hostname.string) {
-    /* if the string is already set to a value we have to free the resources
-     * before we can set the new value in order to avoid memory leaks.
-     */
-    CipFree(g_tcpip.hostname.string);
-  }
-  g_tcpip.hostname.length = strlen(hostname);
-  if (g_tcpip.hostname.length) {
-    /* Storage space for the string must include NULL termination. */
-    size_t buf_length = g_tcpip.hostname.length + 1;
-    g_tcpip.hostname.string = (CipByte *) CipCalloc( buf_length,
-                                                     sizeof(CipByte) );
-    strcpy_s(g_tcpip.hostname.string, buf_length, hostname);
-  } else {
-    g_tcpip.hostname.string = NULL;
+  char name_buf[HOST_NAME_MAX] = "";
+  err = gethostname(name_buf, sizeof(name_buf));
+  if (0 != err) {
+    int error_code = GetSocketErrorNumber();
+    char *error_message = GetErrorMessage(error_code);
+    printf("gethostname() failed, %d - %s\n",
+        error_code,
+        error_message);
+    FreeErrorMessage(error_message);
+    return;
   }
+  SetCipStringByCstr(hostname, name_buf);
 }

+ 0 - 10
source/src/ports/WIN32/networkconfig.h

@@ -3,13 +3,3 @@
  * All rights reserved.
  *
  ******************************************************************************/
-
-#include "cipethernetlink.h"
-
-EipStatus ConfigureNetworkInterface(const char *ip_address,
-                                    const char *subnet_mask,
-                                    const char *gateway);
-
-void ConfigureDomainName(const char *domain_name);
-
-void ConfigureHostName(const char *const RESTRICT hostname);

+ 2 - 2
source/src/ports/WIN32/opener_error.c

@@ -20,12 +20,12 @@ int GetSocketErrorNumber(void) {
 
 char *GetErrorMessage(int error_number) {
   char *error_message = NULL;
-  FormatMessage(
+  FormatMessageA(
     FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
     NULL,
     error_number,
     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-    &error_message,
+    (LPSTR)&error_message,
     0,
     NULL);
   return error_message;

+ 4 - 4
source/src/ports/WIN32/sample_application/ethlinkcbs.c

@@ -52,7 +52,7 @@ static CipUdint media_calls[OPENER_ETHLINK_INSTANCE_CNT];
 /* This is meant as debugging aid and to check if the individual counter     */
 /*  value is sent at the right position in the Get* service response.        */
 
-#define MAKE_CNTR(inst, attr, idx, cnt) ((10000000u * inst) + (100000u * attr) + (1000u * idx) + cnt)
+#define MAKE_CNTR(inst, attr, idx, cnt) ((10000000U * inst) + (100000U * attr) + (1000U * idx) + cnt)
 
 EipStatus EthLnkPreGetCallback
 (
@@ -147,14 +147,14 @@ EipStatus EthLnkPostGetCallback
      *  clear our GetAttributeSingle PreCallback execution counters. */
     switch (attribute->attribute_number) {
     case 4:
-      iface_calls[inst_no-1] = 0u;
+      iface_calls[inst_no-1] = 0U;
       break;
     case 5:
-      media_calls[inst_no-1] = 0u;
+      media_calls[inst_no-1] = 0U;
       /* This is a concession to the conformance test tool that expects
        *  the media counters to be zero after a GetAndClear service. */
       for (int idx = 0; idx < 12; ++idx) {
-        g_ethernet_link[inst_no-1].media_cntrs.cntr32[idx] = 0u;
+        g_ethernet_link[inst_no-1].media_cntrs.cntr32[idx] = 0U;
       }
       break;
     default:

+ 34 - 15
source/src/ports/generic_networkhandler.c

@@ -12,6 +12,7 @@
  */
 
 #include <assert.h>
+#include <inttypes.h>
 #include <stdbool.h>
 
 #include "generic_networkhandler.h"
@@ -27,6 +28,25 @@
 
 #define MAX_NO_OF_TCP_SOCKETS 10
 
+/* ----- Windows size_t PRI macros ------------- */
+#if defined (__MINGW32__) /* This is a Mingw compiler */
+#define PRIuSZT PRIuPTR
+#define PRIxSZT PRIxPTR
+#else
+/* Even the Visual Studio compilers / libraries since VS2015 know that now. */
+#define PRIuSZT "zu"
+#define PRIxSZT "zx"
+#endif  /* if defined(__MINGW32__) */
+
+#if defined(_WIN32)
+/* Most network functions take their I/O buffers as (char *) pointers that
+ *  triggers a warning with our CipOctet (aka unsigned char) buffers. */
+#define NWBUF_CAST  (void *)
+#else
+#define NWBUF_CAST
+#endif
+
+
 /** @brief handle any connection request coming in the TCP server socket.
  *
  */
@@ -192,9 +212,8 @@ EipStatus NetworkHandlerInitialize(void) {
                sizeof(struct sockaddr) ) ) == -1 ) {
     int error_code = GetSocketErrorNumber();
     char *error_message = GetErrorMessage(error_code);
-    OPENER_TRACE_ERR( "error with UDP unicast bind: %d - %s\n", error_code,
-                      GetErrorMessage(
-                        error_code) );
+    OPENER_TRACE_ERR( "error with UDP unicast bind: %d - %s\n",
+                     error_code, error_message);
     FreeErrorMessage(error_message);
     return kEipStatusError;
   }
@@ -480,7 +499,7 @@ void CheckAndHandleUdpGlobalBroadcastSocket(void) {
     /* Handle UDP broadcast messages */
     CipOctet incoming_message[PC_OPENER_ETHERNET_BUFFER_SIZE] = {0};
     int received_size = recvfrom(g_network_status.udp_global_broadcast_listener,
-                                 incoming_message,
+                                 NWBUF_CAST incoming_message,
                                  sizeof(incoming_message),
                                  0, (struct sockaddr *) &from_address,
                                  &from_address_length);
@@ -545,7 +564,7 @@ void CheckAndHandleUdpUnicastSocket(void) {
     /* Handle UDP broadcast messages */
     CipOctet incoming_message[PC_OPENER_ETHERNET_BUFFER_SIZE] = {0};
     int received_size = recvfrom(g_network_status.udp_unicast_listener,
-                                 incoming_message,
+                                 NWBUF_CAST incoming_message,
                                  sizeof(incoming_message),
                                  0, (struct sockaddr *) &from_address,
                                  &from_address_length);
@@ -662,7 +681,7 @@ EipStatus HandleDataOnTcpSocket(int socket) {
   /*Check how many data is here -- read the first four bytes from the connection */
   CipOctet incoming_message[PC_OPENER_ETHERNET_BUFFER_SIZE] = {0};
 
-  long number_of_read_bytes = recv(socket, incoming_message, 4,
+  long number_of_read_bytes = recv(socket, NWBUF_CAST incoming_message, 4,
                                    0); /*TODO we may have to set the socket to a non blocking socket */
 
   SocketTimer *socket_timer = SocketTimerArrayGetSocketTimer(
@@ -708,7 +727,7 @@ EipStatus HandleDataOnTcpSocket(int socket) {
       OPENER_TRACE_INFO(
         "Entering consumption loop, remaining data to receive: %ld\n",
         data_sent);
-      number_of_read_bytes = recv(socket, &incoming_message[0],
+      number_of_read_bytes = recv(socket, NWBUF_CAST &incoming_message[0],
                                   data_sent, 0);
 
       if (number_of_read_bytes == 0) /* got error or connection closed by client */
@@ -745,7 +764,7 @@ EipStatus HandleDataOnTcpSocket(int socket) {
     return kEipStatusOk;
   }
 
-  number_of_read_bytes = recv(socket, &incoming_message[4],
+  number_of_read_bytes = recv(socket, NWBUF_CAST &incoming_message[4],
                               data_size, 0);
 
   if (0 == number_of_read_bytes) /* got error or connection closed by client */
@@ -779,7 +798,7 @@ EipStatus HandleDataOnTcpSocket(int socket) {
     /*we got the right amount of data */
     data_size += 4;
     /*TODO handle partial packets*/
-    OPENER_TRACE_INFO("Data received on TCP: %zu\n", data_size);
+    OPENER_TRACE_INFO("Data received on TCP: %" PRIuSZT "\n", data_size);
 
     g_current_active_tcp_socket = socket;
 
@@ -795,7 +814,7 @@ EipStatus HandleDataOnTcpSocket(int socket) {
       FreeErrorMessage(error_message);
     }
 
-    ENIPMessage outgoing_message = {0};
+    ENIPMessage outgoing_message;
     InitializeENIPMessage(&outgoing_message);
     EipStatus need_to_send = HandleReceivedExplictTcpData(
       socket, incoming_message, data_size, &remaining_bytes,
@@ -817,7 +836,7 @@ EipStatus HandleDataOnTcpSocket(int socket) {
     }
 
     if (need_to_send > 0) {
-      OPENER_TRACE_INFO("TCP reply: send %zu bytes on %d\n",
+      OPENER_TRACE_INFO("TCP reply: send %" PRIuSZT " bytes on %d\n",
                         outgoing_message.used_message_length,
                         socket);
 
@@ -830,7 +849,7 @@ EipStatus HandleDataOnTcpSocket(int socket) {
       SocketTimerSetLastUpdate(socket_timer, g_actual_time);
       if (data_sent != outgoing_message.used_message_length) {
         OPENER_TRACE_WARN(
-          "TCP response was not fully sent: exp %zu, sent %ld\n",
+          "TCP response was not fully sent: exp %" PRIuSZT ", sent %ld\n",
           outgoing_message.used_message_length,
           data_sent);
       }
@@ -937,7 +956,7 @@ int CreateUdpSocket(UdpCommuncationDirection communication_direction,
         == g_tcpip.mcast_config.starting_multicast_address) {
       if (1 != g_tcpip.mcast_ttl_value) { /* we need to set a TTL value for the socket */
         if ( setsockopt(new_socket, IPPROTO_IP, IP_MULTICAST_TTL,
-                        &g_tcpip.mcast_ttl_value,
+                        NWBUF_CAST &g_tcpip.mcast_ttl_value,
                         sizeof(g_tcpip.mcast_ttl_value) ) < 0 ) {
           int error_code = GetSocketErrorNumber();
           char *error_message = GetErrorMessage(error_code);
@@ -955,7 +974,7 @@ int CreateUdpSocket(UdpCommuncationDirection communication_direction,
         struct in_addr my_addr =
         { .s_addr = g_tcpip.interface_configuration.ip_address };
         if ( setsockopt(new_socket, IPPROTO_IP, IP_MULTICAST_IF,
-                        &my_addr.s_addr,
+                        NWBUF_CAST &my_addr.s_addr,
                         sizeof my_addr.s_addr ) < 0 ) {
           int error_code = GetSocketErrorNumber();
           char *error_message = GetErrorMessage(error_code);
@@ -1026,7 +1045,7 @@ void CheckAndHandleConsumingUdpSockets(void) {
 
       int received_size = recvfrom(
         current_connection_object->socket[kUdpCommuncationDirectionConsuming],
-        incoming_message, sizeof(incoming_message), 0,
+        NWBUF_CAST incoming_message, sizeof(incoming_message), 0,
         (struct sockaddr *) &from_address, &from_address_length);
       if (0 == received_size) {
         int error_code = GetSocketErrorNumber();

+ 13 - 9
source/src/ports/nvdata/nvqos.c

@@ -29,18 +29,22 @@
  */
 int NvQosLoad(CipQosObject *p_qos)
 {
-  CipQosObject  qos;
   FILE  *p_file;
   int   rd_cnt = 0;
   int   rc;
 
+  uint64_t dscp_urgent = 0;
+  uint64_t dscp_scheduled = 0;
+  uint64_t dscp_high = 0;
+  uint64_t dscp_low = 0;
+  uint64_t dscp_explicit = 0;
+
   rc = ConfFileOpen(false, QOS_CFG_NAME, &p_file);
   if (0 == rc) {
     /* Read input data */
-    memset(&qos, 0x00, sizeof qos);
     rd_cnt = fscanf(p_file, " %" SCNu8 ", %" SCNu8 ", %" SCNu8 ", %" SCNu8 ", %" SCNu8 "\n",
-                    &qos.dscp.urgent, &qos.dscp.scheduled, &qos.dscp.high,
-                    &qos.dscp.low, &qos.dscp.explicit);
+                    &dscp_urgent, &dscp_scheduled, &dscp_high,
+                    &dscp_low, &dscp_explicit);
 
     /* Need to try to close all stuff in any case. */
     rc = ConfFileClose(&p_file);
@@ -48,11 +52,11 @@ int NvQosLoad(CipQosObject *p_qos)
   if (0 == rc) {
     /* If all data were read copy them to the global QoS object. */
     if (5 == rd_cnt) {
-      p_qos->dscp.urgent = qos.dscp.urgent;
-      p_qos->dscp.scheduled = qos.dscp.scheduled;
-      p_qos->dscp.high = qos.dscp.high;
-      p_qos->dscp.low = qos.dscp.low;
-      p_qos->dscp.explicit = qos.dscp.explicit;
+      p_qos->dscp.urgent = dscp_urgent;
+      p_qos->dscp.scheduled = dscp_scheduled;
+      p_qos->dscp.high = dscp_high;
+      p_qos->dscp.low = dscp_low;
+      p_qos->dscp.explicit = dscp_explicit;
     }
   }
   return rc;