Browse Source

Fix #393: transfer multicast I/O connections also on timeout

When a connection is properly closed we transfer the socket ownership,
i.e., an input-only scanner can continue to receive multicast data from
the adapter if a master scanner closes its connection.  In contrast,
when a master scanner times out we did not properly transfer the socket.

This patch factors out the socket transfer from CloseIoConnection() to a
separate function for use also in HandleIoConnectionTimeOut().

Some minor cleanup for readability has been mixed in to this patch.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Joachim Wiberg 3 năm trước cách đây
mục cha
commit
e3fb0b334b
1 tập tin đã thay đổi với 68 bổ sung79 xóa
  1. 68 79
      source/src/cip/cipioconnection.c

+ 68 - 79
source/src/cip/cipioconnection.c

@@ -693,52 +693,50 @@ EipUint16 HandleConfigData(CipConnectionObject *connection_object) {
   return connection_manager_status;
 }
 
+/*
+ * Returns POSIX OK (0) on successful transfer, otherwise non-zero to
+ * trigger closing of connections and sockets associated with object.
+ */
+static int transfer_master_connection(CipConnectionObject *connection_object) {
+  CipConnectionObject *active;
+
+  active = GetNextNonControlMasterConnection(connection_object->produced_path.instance_id);
+  if (!active)
+    return 1;
+
+  OPENER_TRACE_INFO("Transferring socket ownership\n");
+  active->socket[kUdpCommuncationDirectionProducing] = connection_object->socket[kUdpCommuncationDirectionProducing];
+  connection_object->socket[kUdpCommuncationDirectionProducing] = kEipInvalidSocket;
+
+  memcpy(&(active->remote_address), &(connection_object->remote_address), sizeof(active->remote_address));
+  active->eip_level_sequence_count_producing = connection_object->eip_level_sequence_count_producing;
+  active->sequence_count_producing = connection_object->sequence_count_producing;
+  active->transmission_trigger_timer = connection_object->transmission_trigger_timer;
+
+  return 0;
+}
+
+/* Always sync any changes with HandleIoConnectionTimeout() */
 void CloseIoConnection(CipConnectionObject *connection_object) {
+  ConnectionObjectInstanceType instance_type = ConnectionObjectGetInstanceType(connection_object);
+  ConnectionObjectConnectionType conn_type = ConnectionObjectGetTToOConnectionType(connection_object);
 
   CheckIoConnectionEvent(connection_object->consumed_path.instance_id,
                          connection_object->produced_path.instance_id,
                          kIoConnectionEventClosed);
-  ConnectionObjectSetState(connection_object,
-                           kConnectionObjectStateNonExistent);
-
-  if(kConnectionObjectInstanceTypeIOExclusiveOwner ==
-     ConnectionObjectGetInstanceType(connection_object)
-     || kConnectionObjectInstanceTypeIOInputOnly ==
-     ConnectionObjectGetInstanceType(connection_object) ) {
-    if( (kConnectionObjectConnectionTypeMulticast ==
-         ConnectionObjectGetTToOConnectionType(connection_object) )
-        && (kEipInvalidSocket !=
-            connection_object->socket[kUdpCommuncationDirectionProducing]) ) {
-      OPENER_TRACE_INFO(
-        "Exclusive Owner or Input Only connection closed - Instance type :%d\n",
-        ConnectionObjectGetInstanceType(connection_object) );
-      CipConnectionObject *next_non_control_master_connection =
-        GetNextNonControlMasterConnection(
-          connection_object->produced_path.instance_id);
-      if(NULL != next_non_control_master_connection) {
-
-        OPENER_TRACE_INFO("Transfer socket ownership\n");
-        next_non_control_master_connection->socket[
-          kUdpCommuncationDirectionProducing] =
-          connection_object->socket[kUdpCommuncationDirectionProducing];
-
-        connection_object->socket[kUdpCommuncationDirectionProducing] =
-          kEipInvalidSocket;
-        /* End */
-
-        memcpy(&(next_non_control_master_connection->remote_address),
-               &(connection_object->remote_address),
-               sizeof(next_non_control_master_connection->remote_address) );
-        next_non_control_master_connection->eip_level_sequence_count_producing =
-          connection_object->eip_level_sequence_count_producing;
-        next_non_control_master_connection->sequence_count_producing =
-          connection_object->sequence_count_producing;
-        next_non_control_master_connection->transmission_trigger_timer =
-          connection_object->transmission_trigger_timer;
-      } else { /* this was the last master connection close all listen only connections listening on the port */
-        CloseAllConnectionsForInputWithSameType(
-          connection_object->produced_path.instance_id,
-          kConnectionObjectInstanceTypeIOListenOnly);
+  ConnectionObjectSetState(connection_object, kConnectionObjectStateNonExistent);
+
+  if(kConnectionObjectInstanceTypeIOExclusiveOwner == instance_type ||
+     kConnectionObjectInstanceTypeIOInputOnly == instance_type) {
+    if(kConnectionObjectConnectionTypeMulticast == conn_type &&
+       kEipInvalidSocket != connection_object->socket[kUdpCommuncationDirectionProducing]) {
+      OPENER_TRACE_INFO("Exclusive Owner or Input Only connection closed - Instance type: %d\n", instance_type);
+
+      if(transfer_master_connection(connection_object)) {
+        /* No transfer, this was the last master connection, close all
+         * listen only connections listening on the port */
+        CloseAllConnectionsForInputWithSameType(connection_object->produced_path.instance_id,
+                                                kConnectionObjectInstanceTypeIOListenOnly);
       }
     }
   }
@@ -746,55 +744,46 @@ void CloseIoConnection(CipConnectionObject *connection_object) {
   CloseCommunicationChannelsAndRemoveFromActiveConnectionsList(connection_object);
 }
 
+/* Always sync any changes with CloseIoConnection() */
 void HandleIoConnectionTimeOut(CipConnectionObject *connection_object) {
+  ConnectionObjectInstanceType instance_type = ConnectionObjectGetInstanceType(connection_object);
+  ConnectionObjectConnectionType conn_type = ConnectionObjectGetTToOConnectionType(connection_object);
+  int handover = 0;
+
   CheckIoConnectionEvent(connection_object->consumed_path.instance_id,
                          connection_object->produced_path.instance_id,
                          kIoConnectionEventTimedOut);
-
   ConnectionObjectSetState(connection_object, kConnectionObjectStateTimedOut);
-  if(connection_object->last_package_watchdog_timer ==
-     connection_object->inactivity_watchdog_timer) {
+
+  if(connection_object->last_package_watchdog_timer == connection_object->inactivity_watchdog_timer)
     CheckForTimedOutConnectionsAndCloseTCPConnections(connection_object,
                                                       CloseEncapsulationSessionBySockAddr);
-  }
 
-  if(kConnectionObjectConnectionTypeMulticast ==
-     ConnectionObjectGetTToOConnectionType(connection_object) ) {
-    switch(ConnectionObjectGetInstanceType(connection_object) ) {
-      case kConnectionObjectInstanceTypeIOExclusiveOwner:
-        CloseAllConnectionsForInputWithSameType(
-          connection_object->produced_path.instance_id,
-          kConnectionObjectInstanceTypeIOInputOnly);
-        CloseAllConnectionsForInputWithSameType(
-          connection_object->produced_path.instance_id,
-          kConnectionObjectInstanceTypeIOListenOnly);
-        break;
-      case kConnectionObjectInstanceTypeIOInputOnly:
-        if(kEipInvalidSocket !=
-           connection_object->socket[kUdpCommuncationDirectionProducing]) {                      /* we are the controlling input only connection find a new controller*/
-          CipConnectionObject *next_non_control_master_connection =
-            GetNextNonControlMasterConnection(
-              connection_object->produced_path.instance_id);
-          if(NULL != next_non_control_master_connection) {
-            next_non_control_master_connection->socket[
-              kUdpCommuncationDirectionProducing] =
-              connection_object->socket[kUdpCommuncationDirectionProducing];
-            connection_object->socket[kUdpCommuncationDirectionProducing] =
-              kEipInvalidSocket;
-            next_non_control_master_connection->transmission_trigger_timer =
-              connection_object->transmission_trigger_timer;
-          } else { /* this was the last master connection close all listen only connections listening on the port */
-            CloseAllConnectionsForInputWithSameType(
-              connection_object->produced_path.instance_id,
-              kConnectionObjectInstanceTypeIOListenOnly);
-          }
-        }
-        break;
-      default:
-        break;
+  if(kConnectionObjectInstanceTypeIOExclusiveOwner == instance_type ||
+     kConnectionObjectInstanceTypeIOInputOnly == instance_type) {
+    if(kConnectionObjectConnectionTypeMulticast == conn_type &&
+       kEipInvalidSocket != connection_object->socket[kUdpCommuncationDirectionProducing]) {
+      OPENER_TRACE_INFO("Exclusive Owner or Input Only connection timed out - Instance type: %d\n", instance_type);
+      /* we are the controlling input only connection find a new controller*/
+
+      if(transfer_master_connection(connection_object)) {
+        /* No transfer, this was the last master connection, close all
+         * listen only connections listening on the port */
+        CloseAllConnectionsForInputWithSameType(connection_object->produced_path.instance_id,
+                                                kConnectionObjectInstanceTypeIOListenOnly);
+      } else {
+	handover = 1;
+      }
     }
   }
 
+  if(kConnectionObjectInstanceTypeIOExclusiveOwner == instance_type && !handover) {
+    CloseAllConnectionsForInputWithSameType(connection_object->produced_path.instance_id,
+                                            kConnectionObjectInstanceTypeIOInputOnly);
+    CloseAllConnectionsForInputWithSameType(connection_object->produced_path.instance_id,
+                                            kConnectionObjectInstanceTypeIOListenOnly);
+  }
+
   ConnectionObjectSetState(connection_object, kConnectionObjectStateTimedOut);
 }