OpENer - Open Source EtherNet/IP(TM) I/O Target Stack  2.1
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
cipioconnection.c
Go to the documentation of this file.
1 /*******************************************************************************
2  * Copyright (c) 2011, Rockwell Automation, Inc.
3  * All rights reserved.
4  *
5  ******************************************************************************/
6 
7 #include <string.h>
8 #include <stdbool.h>
9 
10 #include "cipioconnection.h"
11 
12 #include "generic_networkhandler.h"
13 #include "cipconnectionmanager.h"
14 #include "cipassembly.h"
15 #include "cipidentity.h"
16 #include "ciptcpipinterface.h"
17 #include "cipcommon.h"
18 #include "appcontype.h"
19 #include "cpf.h"
20 #include "trace.h"
21 #include "endianconv.h"
22 
23 
24 /*The port to be used per default for I/O messages on UDP.*/
25 const int kOpenerEipIoUdpPort = 0x08AE;
26 
27 /* producing multicast connection have to consider the rules that apply for
28  * application connection types.
29  */
31  CipConnectionObject *connection_object,
32  CipCommonPacketFormatData *common_packet_format_data);
33 
35  UdpCommuncationDirection direction,
36  CipConnectionObject *connection_object,
37  CipCommonPacketFormatData *common_packet_format_data);
38 
40  CipConnectionObject *const connection_object,
41  CipCommonPacketFormatData *const common_packet_format_data);
42 
44  CipConnectionObject *connection_object,
45  CipCommonPacketFormatData *common_packet_format_data);
46 
48 
49 /* Regularly close the IO connection. If it is an exclusive owner or input only
50  * connection and in charge of the connection a new owner will be searched
51  */
52 void CloseIoConnection(CipConnectionObject *connection_object);
53 
54 void HandleIoConnectionTimeOut(CipConnectionObject *connection_object);
55 
63 
65  CipConnectionObject *connection_object,
66  const EipUint8 *data,
67  EipUint16 data_length);
68 
69 /**** Global variables ****/
71 unsigned int g_config_data_length = 0;
76 {
79  io_connection_object) ) {
80  if ( 256 ==
81  ConnectionObjectGetProductionInhibitTime(io_connection_object) ) {
82  OPENER_TRACE_INFO("No PIT segment available\n");
83  /* there was no PIT segment in the connection path; set PIT to one fourth of RPI */
84  ConnectionObjectSetProductionInhibitTime(io_connection_object,
86  io_connection_object)
87  / 4000);
88  } else {
89  /* If a production inhibit time is provided, it needs to be smaller than the Requested Packet Interval */
90  if ( ConnectionObjectGetProductionInhibitTime(io_connection_object)
92  io_connection_object)
93  / 1000) ) {
94  /* see section C-1.4.3.3 */
96  }
97  }
98  }
100 }
101 
102 void SetIoConnectionCallbacks(CipConnectionObject *const io_connection_object) {
103  io_connection_object->connection_close_function = CloseIoConnection;
105  io_connection_object->connection_send_data_function = SendConnectedData;
106  io_connection_object->connection_receive_data_function =
108 }
109 
111  CipConnectionObject *const io_connection_object,
112  CipConnectionObject *const RESTRICT connection_object
113  ) {
114  CipClass *const assembly_class = GetCipClass(kCipAssemblyClassCode);
115  CipInstance *instance = NULL;
116  if ( NULL
117  != ( instance =
119  assembly_class,
120  io_connection_object->consumed_path.instance_id) ) ) {
121  /* consuming Connection Point is present */
122  io_connection_object->consuming_instance = instance;
123  io_connection_object->consumed_connection_path_length = 6;
124  /*io_connection_object->consumed_path.class_id =
125  io_connection_object->connection_path.class_id;
126  io_connection_object->consumed_connection_path.instance_number =
127  io_connection_object->connection_path.connection_point[
128  kConnectionPointConsumer];*/
129  io_connection_object->consumed_path.attribute_id_or_connection_point = 3;
130  int data_size = ConnectionObjectGetOToTConnectionSize(io_connection_object);
131  int diff_size = 0;
132 
133  /* an assembly object should always have an attribute 3 */
134  CipAttributeStruct *attribute = GetCipAttribute(instance,
135  io_connection_object->consumed_path.attribute_id_or_connection_point);
136  OPENER_ASSERT(attribute != NULL)
137  bool is_heartbeat = ( ( (CipByteArray *) attribute->data )->length == 0 );
140  io_connection_object) ) {
141  /* class 1 connection */
142  data_size -= 2; /* remove 16-bit sequence count length */
143  diff_size += 2;
144  }
145 #ifdef OPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER
146  if ( (data_size > 0) && (!is_heartbeat) ) {
147  /* we only have an run idle header if it is not an heartbeat connection */
148  data_size -= 4; /* remove the 4 bytes needed for run/idle header */
149  diff_size += 4;
150  }
151 #endif
152  if ( ( (CipByteArray *) attribute->data )->length != data_size ) {
153  /*wrong connection size */
154  connection_object->correct_originator_to_target_size =
155  ( (CipByteArray *) attribute->data )->length + diff_size;
157  }
158  } else {
160  }
162 }
163 
165  CipConnectionObject *const io_connection_object,
166  CipConnectionObject *const RESTRICT connection_object
167  ) {
169  while (NULL != node &&
171  ConnectionObjectGetTToOConnectionType(io_connection_object) ) {
172  CipConnectionObject *iterator = node->data;
173  if(io_connection_object->produced_path.instance_id ==
174  iterator->produced_path.instance_id) {
175  //Check parameters
176  if( ConnectionObjectGetTToORequestedPacketInterval(io_connection_object)
177  !=
180  }
182  io_connection_object) !=
184  iterator) ) {
185  return
187  }
188  if ( ConnectionObjectGetTToOPriority(io_connection_object) !=
189  ConnectionObjectGetTToOPriority(iterator) ) {
190  return
192  }
193 
195  io_connection_object) !=
198  }
199 
200 
202  io_connection_object) !=
204  {
205  return
207  }
208 
209  if( ConnectionObjectGetProductionInhibitTime(io_connection_object) !=
211  return
213  }
214 
215  }
216 
217  node = node->next;
218  }
219 
220  /*setup producer side*/
221  CipClass *const assembly_class = GetCipClass(kCipAssemblyClassCode);
222  CipInstance *instance = NULL;
223  if ( NULL
224  != ( instance =
226  assembly_class,
227  io_connection_object->produced_path.instance_id) ) ) {
228 
229  io_connection_object->producing_instance = instance;
230  int data_size = ConnectionObjectGetTToOConnectionSize(io_connection_object);
231  int diff_size = 0;
232  /* an assembly object should always have an attribute 3 */
233  io_connection_object->produced_path.attribute_id_or_connection_point = 3;
234  CipAttributeStruct *attribute = GetCipAttribute(instance,
235  io_connection_object->produced_path.attribute_id_or_connection_point);
236  OPENER_ASSERT(attribute != NULL)
237  bool is_heartbeat = ( ( (CipByteArray *) attribute->data )->length == 0 );
240  io_connection_object) ) {
241  /* class 1 connection */
242  data_size -= 2; /* remove 16-bit sequence count length */
243  diff_size += 2;
244  }
245 #ifdef OPENER_PRODUCED_DATA_HAS_RUN_IDLE_HEADER
246  if ( (data_size > 0) && (!is_heartbeat) ) {
247  /* we only have an run idle header if it is not an heartbeat connection */
248  data_size -= 4; /* remove the 4 bytes needed for run/idle header */
249  diff_size += 4;
250  }
251 #endif
252  if ( ( (CipByteArray *) attribute->data )->length != data_size ) {
253  /*wrong connection size*/
254  connection_object->correct_target_to_originator_size =
255  ( (CipByteArray *) attribute->data )->length + diff_size;
257  }
258  } else {
260  }
262 }
263 
276  CipConnectionObject *RESTRICT const connection_object,
277  EipUint16 *const extended_error
278  ) {
279  EipStatus eip_status = kEipStatusOk;
280 
282  connection_object,
283  extended_error);
284  if(NULL == io_connection_object) {
286  }
287 
288  *extended_error = ProcessProductionInhibitTime(io_connection_object);
289 
290  if(0 != *extended_error) {
292  }
293 
294  SetIoConnectionCallbacks(io_connection_object);
295 
296  ConnectionObjectGeneralConfiguration(io_connection_object);
297 
298  ConnectionObjectConnectionType originator_to_target_connection_type =
299  ConnectionObjectGetOToTConnectionType(io_connection_object);
300  ConnectionObjectConnectionType target_to_originator_connection_type =
301  ConnectionObjectGetTToOConnectionType(io_connection_object);
302 
304  OPENER_ASSERT( !(originator_to_target_connection_type ==
306  target_to_originator_connection_type ==
308 
309  io_connection_object->consuming_instance = NULL;
310  io_connection_object->consumed_connection_path_length = 0;
311  io_connection_object->producing_instance = NULL;
312  io_connection_object->produced_connection_path_length = 0;
313 
314 
315  /* we don't need to check for zero as this is handled in the connection path parsing */
316 
317  if (originator_to_target_connection_type !=
318  kConnectionObjectConnectionTypeNull) { /*setup consumer side*/
320  io_connection_object,
321  connection_object);
322  if (kConnectionManagerExtendedStatusCodeSuccess != *extended_error) {
324  }
325  }
326 
327 
328  if (target_to_originator_connection_type !=
329  kConnectionObjectConnectionTypeNull) { /*setup producer side*/
331  io_connection_object,
332  connection_object);
333  if (kConnectionManagerExtendedStatusCodeSuccess != *extended_error) {
335  }
336  }
337 
338 
339  if (NULL != g_config_data_buffer) { /* config data has been sent with this forward open request */
340  *extended_error = HandleConfigData(io_connection_object);
341  if (kConnectionManagerExtendedStatusCodeSuccess != *extended_error) {
343  }
344  }
345 
346  eip_status = OpenCommunicationChannels(io_connection_object);
347  if (kEipStatusOk != eip_status) {
348  *extended_error = 0; /*TODO find out the correct extended error code*/
349  return eip_status;
350  }
351 
352  AddNewActiveConnection(io_connection_object);
354  io_connection_object->consumed_path.instance_id,
355  io_connection_object->produced_path.instance_id,
357  return eip_status;
358 }
359 
367  CipConnectionObject *const connection_object,
368  CipCommonPacketFormatData *const common_packet_format_data
369  ) {
370 
371  int j = 0;
372 
373  if (common_packet_format_data->address_info_item[0].type_id == 0) { /* it is not used yet */
374  j = 0;
375  } else if (common_packet_format_data->address_info_item[1].type_id == 0) {
376  j = 1;
377  }
378 
379  struct sockaddr_in addr =
380  { .sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY, .sin_port = htons(
382 
383  CipUsint qos_for_socket = ConnectionObjectGetTToOPriority(connection_object);
385  &addr,
386  qos_for_socket); /* the address is only needed for bind used if consuming */
387  if (socket == kEipInvalidSocket) {
389  "cannot create UDP socket in OpenPointToPointConnection\n");
390  return kEipStatusError;
391  }
392 
393  connection_object->originator_address = addr; /* store the address of the originator for packet scanning */
394  addr.sin_addr.s_addr = INADDR_ANY; /* restore the address */
395  connection_object->socket[kUdpCommuncationDirectionConsuming] = socket;
396 
397  common_packet_format_data->address_info_item[j].length = 16;
398  common_packet_format_data->address_info_item[j].type_id =
400 
401  common_packet_format_data->address_info_item[j].sin_port = addr.sin_port;
402  /*TODO should we add our own address here? */
403  common_packet_format_data->address_info_item[j].sin_addr = addr.sin_addr
404  .s_addr;
405  memset(common_packet_format_data->address_info_item[j].nasin_zero, 0, 8);
406  common_packet_format_data->address_info_item[j].sin_family = htons(AF_INET);
407 
408  return kEipStatusOk;
409 }
410 
412  CipConnectionObject *connection_object,
413  CipCommonPacketFormatData *common_packet_format_data
414  ) {
415  in_port_t port = htons(kOpenerEipIoUdpPort); /* the default port to be used if no port information is part of the forward open request */
416 
418  == common_packet_format_data->address_info_item[0].type_id) {
419  port = common_packet_format_data->address_info_item[0].sin_port;
420  } else {
422  == common_packet_format_data->address_info_item[1].type_id) {
423  port = common_packet_format_data->address_info_item[1].sin_port;
424  }
425  }
426 
427  connection_object->remote_address.sin_family = AF_INET;
428  connection_object->remote_address.sin_addr.s_addr = 0; /* we don't know the address of the originate will be set in the IApp_CreateUDPSocket */
429  connection_object->remote_address.sin_port = port;
430 
431  CipUsint qos_for_socket = ConnectionObjectGetTToOPriority(connection_object);
433  &connection_object->remote_address,
434  qos_for_socket); /* the address is only needed for bind used if consuming */
435  if (socket == kEipInvalidSocket) {
437  "cannot create UDP socket in OpenPointToPointConnection\n");
438  /* *pa_pnExtendedError = 0x0315; miscellaneous*/
440  }
441  connection_object->socket[kUdpCommuncationDirectionProducing] = socket;
442 
443  return kEipStatusOk;
444 }
445 
447  CipConnectionObject *connection_object,
448  CipCommonPacketFormatData *common_packet_format_data
449  ) {
450  CipConnectionObject *existing_connection_object =
452  connection_object->produced_path.instance_id);
453 
454  int j = 0; /* allocate an unused sockaddr struct to use */
455  if (g_common_packet_format_data_item.address_info_item[0].type_id == 0) { /* it is not used yet */
456  j = 0;
458  == 0) {
459  j = 1;
460  }
461 
462  int port = htons(kOpenerEipIoUdpPort);
464  common_packet_format_data->address_info_item[j].type_id) {
465  port = common_packet_format_data->address_info_item[j].sin_port;
466  }
467 
468  common_packet_format_data->address_info_item[j].type_id =
470 
471  if (NULL == existing_connection_object) { /* we are the first connection producing for the given Input Assembly */
473  connection_object,
474  common_packet_format_data);
475  } else {
476  /* we need to inform our originator on the correct connection id */
477  connection_object->cip_produced_connection_id = existing_connection_object
478  ->
479  cip_produced_connection_id;
480  }
481 
482  /* we have a connection reuse the data and the socket */
483 
485  connection_object->instance_type) {
486  /* exclusive owners take the socket and further manage the connection
487  * especially in the case of time outs.
488  */
489  connection_object->socket[kUdpCommuncationDirectionProducing] =
490  existing_connection_object->socket[kUdpCommuncationDirectionProducing];
491  existing_connection_object->socket[kUdpCommuncationDirectionProducing] =
492  kEipInvalidSocket;
493  } else { /* this connection will not produce the data */
494  connection_object->socket[kUdpCommuncationDirectionProducing] =
495  kEipInvalidSocket;
496  }
497 
498  common_packet_format_data->address_info_item[j].length = 16;
499 
500  connection_object->remote_address.sin_family = AF_INET;
501  connection_object->remote_address.sin_port = common_packet_format_data
503  = port;
504  connection_object->remote_address.sin_addr.s_addr = common_packet_format_data
505  ->address_info_item[j].
506  sin_addr =
508  .
509  starting_multicast_address;
510  memset(common_packet_format_data->address_info_item[j].nasin_zero, 0, 8);
511  common_packet_format_data->address_info_item[j].sin_family = htons(AF_INET);
512 
513  return kEipStatusOk;
514 }
515 
524  UdpCommuncationDirection direction,
525  CipConnectionObject *connection_object,
526  CipCommonPacketFormatData *common_packet_format_data
527  ) {
528  int j = -1;
529 
530  int address_info_item_which_contains_o_to_t = -1;
531  int address_info_item_which_contains_t_to_o = -1;
532 
534  == common_packet_format_data->address_info_item[0].type_id) {
535  address_info_item_which_contains_o_to_t = 0;
537  == common_packet_format_data->address_info_item[1].type_id) {
538  address_info_item_which_contains_o_to_t = 1;
539  } else {
540  OPENER_TRACE_INFO("No O->T Sockaddr info available\n");
541  }
542 
544  == common_packet_format_data->address_info_item[0].type_id) {
545  address_info_item_which_contains_t_to_o = 0;
547  == common_packet_format_data->address_info_item[1].type_id) {
548  address_info_item_which_contains_t_to_o = 1;
549  } else {
550  OPENER_TRACE_INFO("No T->O Sockaddr info available\n");
551  }
552 
553  if(kUdpCommuncationDirectionConsuming == direction) {
554  j = address_info_item_which_contains_o_to_t;
555  }
556 
557  if(kUdpCommuncationDirectionProducing == direction) {
558  j = address_info_item_which_contains_t_to_o;
559  }
560 
561  /*****************/
562 
563  if (-1 == j) {
565  "no suitable addr info item available / O->T: %d, T->O: %d, Selector: %d, direction: %d\n",
566  address_info_item_which_contains_o_to_t,
567  address_info_item_which_contains_t_to_o,
568  j,
569  direction);
570  return kEipStatusError;
571  }
572 
574  common_packet_format_data->address_info_item[j].type_id) { /* we are using an unused item initialize it with the default multicast address */
575  common_packet_format_data->address_info_item[j].sin_family = htons(
576  AF_INET);
577  common_packet_format_data->address_info_item[j].sin_port = htons(
579  common_packet_format_data->address_info_item[j].sin_addr =
581  memset(common_packet_format_data->address_info_item[j].nasin_zero, 0, 8);
582  common_packet_format_data->address_info_item[j].length = 16;
583  }
584 
585  if (htons(AF_INET)
586  != common_packet_format_data->address_info_item[j].sin_family) {
588  "Sockaddr Info Item with wrong sin family value received\n");
589  return kEipStatusError;
590  }
591 
592  /* allocate an unused sockaddr struct to use */
593  struct sockaddr_in socket_address = {0};
594  socket_address.sin_family = ntohs(
595  common_packet_format_data->address_info_item[j].sin_family);
596  socket_address.sin_addr.s_addr =
597  common_packet_format_data->address_info_item[j].sin_addr;
598  socket_address.sin_port = common_packet_format_data->address_info_item[j]
599  .sin_port;
600 
601  CipUsint qos_for_socket = ConnectionObjectGetTToOPriority(connection_object);
602  int socket = CreateUdpSocket(direction, &socket_address, qos_for_socket); /* the address is only needed for bind used if consuming */
603  if (socket == kEipInvalidSocket) {
604  OPENER_TRACE_ERR("cannot create UDP socket in OpenMulticastConnection\n");
605  return kEipStatusError;
606  }
607  connection_object->socket[direction] = socket;
608 
609  if (direction == kUdpCommuncationDirectionConsuming) {
610  common_packet_format_data->address_info_item[j].type_id =
612  connection_object->originator_address = socket_address;
613  } else {
614  common_packet_format_data->address_info_item[j].type_id =
616  connection_object->remote_address = socket_address;
617  }
618 
619  return kEipStatusOk;
620 }
621 
623 
624  CipClass *const assembly_class = GetCipClass(kCipAssemblyClassCode);
625  EipUint16 connection_manager_status = 0;
626  CipInstance *config_instance = GetCipInstance(
627  assembly_class, connection_object->configuration_path.instance_id);
628 
629  if (0 != g_config_data_length) {
630  OPENER_ASSERT(NULL != config_instance)
632  connection_object->configuration_path.instance_id) ) {
633  /* there is a connected connection with the same config point
634  * we have to have the same data as already present in the config point*/
635  CipAttributeStruct *attribute_three = GetCipAttribute(
636  config_instance,
637  3);
638  OPENER_ASSERT(NULL != attribute_three)
639  CipByteArray * attribute_three_data =
640  (CipByteArray *) attribute_three->data;
641  OPENER_ASSERT(NULL != attribute_three_data)
642  if (attribute_three_data->length != g_config_data_length) {
643  connection_manager_status =
646  "Hit an Ownership conflict in cipioconnection.c occurrence 1");
647  } else {
648  /*FIXME check if this is correct */
649  if ( memcmp(attribute_three_data->data, g_config_data_buffer,
651  connection_manager_status =
654  "Hit an Ownership conflict in cipioconnection.c occurrence 2");
655  }
656  }
657  } else {
658  /* put the data on the configuration assembly object with the current
659  design this can be done rather efficiently */
660  if ( kEipStatusOk
661  != NotifyAssemblyConnectedDataReceived(config_instance,
664  OPENER_TRACE_WARN("Configuration data was invalid\n");
665  connection_manager_status =
667  }
668  }
669  }
670  return connection_manager_status;
671 }
672 
673 void CloseIoConnection(CipConnectionObject *connection_object) {
674 
676  connection_object->produced_path.instance_id,
679 
681  ConnectionObjectGetInstanceType(connection_object)
683  ConnectionObjectGetInstanceType(connection_object) ) {
685  == ConnectionObjectGetTToOConnectionType(connection_object) )
686  && (kEipInvalidSocket
687  != connection_object->socket[kUdpCommuncationDirectionProducing]) )
688  {
689  OPENER_TRACE_INFO("Exclusive Owner or Input Only connection closed - Instance type :%d\n", ConnectionObjectGetInstanceType(connection_object));
690  CipConnectionObject *next_non_control_master_connection =
692  connection_object->produced_path.instance_id);
693  if (NULL != next_non_control_master_connection) {
694 
695  OPENER_TRACE_INFO("Transfer socket ownership\n");
696  next_non_control_master_connection->socket[
698  connection_object->socket[kUdpCommuncationDirectionProducing];
699 
700  connection_object->socket[kUdpCommuncationDirectionProducing] =
701  kEipInvalidSocket;
702  /* End */
703 
704  memcpy( &(next_non_control_master_connection->remote_address),
705  &(connection_object->remote_address),
706  sizeof(next_non_control_master_connection->remote_address) );
707  next_non_control_master_connection->eip_level_sequence_count_producing
708  =
709  connection_object->eip_level_sequence_count_producing;
710  next_non_control_master_connection->sequence_count_producing =
711  connection_object->sequence_count_producing;
712  next_non_control_master_connection->transmission_trigger_timer =
713  connection_object->transmission_trigger_timer;
714  } else { /* this was the last master connection close all listen only connections listening on the port */
716  connection_object->produced_path.instance_id,
718  }
719  }
720  }
721 
723  connection_object);
724 }
725 
728  connection_object->consumed_path.instance_id,
730 
732  if(connection_object->last_package_watchdog_timer ==
733  connection_object->inactivity_watchdog_timer) {
736  }
737 
739  == ConnectionObjectGetTToOConnectionType(connection_object) ) {
740  switch (ConnectionObjectGetInstanceType(connection_object) ) {
743  connection_object->produced_path.instance_id,
746  connection_object->produced_path.instance_id,
748  break;
750  if (kEipInvalidSocket
751  != connection_object->socket[kUdpCommuncationDirectionProducing]) { /* we are the controlling input only connection find a new controller*/
752  CipConnectionObject *next_non_control_master_connection =
754  connection_object->produced_path.instance_id);
755  if (NULL != next_non_control_master_connection) {
756  next_non_control_master_connection->socket[
758  connection_object->socket[kUdpCommuncationDirectionProducing];
759  connection_object->socket[kUdpCommuncationDirectionProducing] =
760  kEipInvalidSocket;
761  next_non_control_master_connection->transmission_trigger_timer =
762  connection_object->transmission_trigger_timer;
763  } else { /* this was the last master connection close all listen only connections listening on the port */
765  connection_object->produced_path.instance_id,
767  }
768  }
769  break;
770  default:
771  break;
772  }
773  }
774 
776 }
777 
779 
780  /* TODO think of adding an own send buffer to each connection object in order to preset up the whole message on connection opening and just change the variable data items e.g., sequence number */
781 
782  CipCommonPacketFormatData *common_packet_format_data =
784  /* TODO think on adding a CPF data item to the S_CIP_ConnectionObject in order to remove the code here or even better allocate memory in the connection object for storing the message to send and just change the application data*/
785 
786  connection_object->eip_level_sequence_count_producing++;
787 
788  /* assembleCPFData */
789  common_packet_format_data->item_count = 2;
792  { /* use Sequenced Address Items if not Connection Class 0 */
793  common_packet_format_data->address_item.type_id =
795  common_packet_format_data->address_item.length = 8;
796  common_packet_format_data->address_item.data.sequence_number =
797  connection_object->eip_level_sequence_count_producing;
798  } else {
799  common_packet_format_data->address_item.type_id =
801  common_packet_format_data->address_item.length = 4;
802 
803  }
804  common_packet_format_data->address_item.data.connection_identifier =
805  connection_object->cip_produced_connection_id;
806 
807  common_packet_format_data->data_item.type_id = kCipItemIdConnectedDataItem;
808 
809  CipByteArray *producing_instance_attributes =
810  (CipByteArray *) connection_object->producing_instance->attributes->data;
811  common_packet_format_data->data_item.length = 0;
812 
813  /* notify the application that data will be sent immediately after the call */
814  if ( BeforeAssemblyDataSend(connection_object->producing_instance) ) {
815  /* the data has changed increase sequence counter */
816  connection_object->sequence_count_producing++;
817  }
818 
819  /* set AddressInfo Items to invalid Type */
820  common_packet_format_data->address_info_item[0].type_id = 0;
821  common_packet_format_data->address_info_item[1].type_id = 0;
822 
823  ENIPMessage outgoing_message = {0};
824  InitializeENIPMessage(&outgoing_message);
825  EipUint16 reply_length = AssembleIOMessage(common_packet_format_data,
826  &outgoing_message);
827 
828 
829  outgoing_message.current_message_position -= 2;
830  common_packet_format_data->data_item.length = producing_instance_attributes
831  ->length;
832 #ifdef OPENER_PRODUCED_DATA_HAS_RUN_IDLE_HEADER
833  common_packet_format_data->data_item.length += 4;
834 #endif /* OPENER_PRODUCED_DATA_HAS_RUN_IDLE_HEADER */
835 
838  {
839  common_packet_format_data->data_item.length += 2;
840  AddIntToMessage(common_packet_format_data->data_item.length,
841  &outgoing_message.current_message_position);
842  AddIntToMessage(connection_object->sequence_count_producing,
843  &outgoing_message.current_message_position);
844  } else {
845  AddIntToMessage(common_packet_format_data->data_item.length,
846  &outgoing_message.current_message_position);
847  }
848 
849 #ifdef OPENER_PRODUCED_DATA_HAS_RUN_IDLE_HEADER
851  &(outgoing_message.current_message_position) );
852 #endif /* OPENER_PRODUCED_DATA_HAS_RUN_IDLE_HEADER */
853 
854  memcpy(outgoing_message.current_message_position,
855  producing_instance_attributes->data,
856  producing_instance_attributes->length);
857 
858  outgoing_message.used_message_length +=
859  common_packet_format_data->data_item.length;
860 
861  return SendUdpData(
862  &connection_object->remote_address,
863  connection_object->socket[kUdpCommuncationDirectionProducing],
864  outgoing_message.message_buffer, outgoing_message.used_message_length);
865 }
866 
868  CipConnectionObject *connection_object,
869  const EipUint8 *data,
870  EipUint16 data_length
871  ) {
872 
873  OPENER_TRACE_INFO("Starting data length: %d\n", data_length);
874  bool no_new_data = false;
875  /* check class 1 sequence number*/
878  {
879  EipUint16 sequence_buffer = GetIntFromMessage( &(data) );
880  if ( SEQ_LEQ16(sequence_buffer,
881  connection_object->sequence_count_consuming) ) {
882  no_new_data = true;
883  }
884  connection_object->sequence_count_consuming = sequence_buffer;
885  data_length -= 2;
886  }
887 
888  OPENER_TRACE_INFO("data length after sequence count: %d\n", data_length);
889  if (data_length > 0) {
890  /* we have no heartbeat connection */
891 #ifdef OPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER
892  EipUint32 nRunIdleBuf = GetDintFromMessage( &(data) );
893  OPENER_TRACE_INFO("Run/Idle handler: 0x%x", nRunIdleBuf);
894  const uint32_t kRunBitMask = 0x0001;
895  if((kRunBitMask & nRunIdleBuf) == 1) {
897  } else {
899  }
900  if (g_run_idle_state != nRunIdleBuf) {
901  RunIdleChanged(nRunIdleBuf);
902  }
903  g_run_idle_state = nRunIdleBuf;
904  data_length -= 4;
905 #endif /* OPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER */
906  if(no_new_data) {
907  return kEipStatusOk;
908  }
909 
911  connection_object->consuming_instance, (EipUint8 *const)data,
912  data_length) != 0) {
913  return kEipStatusError;
914  }
915  }
916  return kEipStatusOk;
917 }
918 
920 
921  EipStatus eip_status = kEipStatusOk;
922  /*get pointer to the CPF data, currently we have just one global instance of the struct. This may change in the future*/
923  CipCommonPacketFormatData *common_packet_format_data =
925 
926  ConnectionObjectConnectionType originator_to_target_connection_type =
927  ConnectionObjectGetOToTConnectionType(connection_object);
928 
929  ConnectionObjectConnectionType target_to_originator_connection_type =
930  ConnectionObjectGetTToOConnectionType(connection_object);
931 
932  /* open a connection "point to point" or "multicast" based on the ConnectionParameter */
933  if (originator_to_target_connection_type ==
934  kConnectionObjectConnectionTypeMulticast) /* Multicast consuming */
935  {
937  connection_object, common_packet_format_data)
938  == kEipStatusError) {
939  OPENER_TRACE_ERR("error in OpenMulticast Connection\n");
941  }
942  } else if (originator_to_target_connection_type ==
943  kConnectionObjectConnectionTypePointToPoint) /* Point to Point consuming */
944  {
945  if (OpenConsumingPointToPointConnection(connection_object,
946  common_packet_format_data)
947  == kEipStatusError) {
948  OPENER_TRACE_ERR("error in PointToPoint consuming connection\n");
950  }
951  }
952 
953  if (target_to_originator_connection_type ==
954  kConnectionObjectConnectionTypeMulticast) /* Multicast producing */
955  {
956  if (OpenProducingMulticastConnection(connection_object,
957  common_packet_format_data)
958  == kEipStatusError) {
959  OPENER_TRACE_ERR("error in OpenMulticast Connection\n");
961  }
962  } else if (target_to_originator_connection_type ==
963  kConnectionObjectConnectionTypePointToPoint) /* Point to Point producing */
964  {
965 
966  if (OpenProducingPointToPointConnection(connection_object,
967  common_packet_format_data)
968  != kEipStatusOk) {
969  OPENER_TRACE_ERR("error in PointToPoint producing connection\n");
971  }
972  }
973  return eip_status;
974 }
975 
977  CipConnectionObject *connection_object) {
979  connection_object->socket[kUdpCommuncationDirectionConsuming]);
980 
981  connection_object->socket[kUdpCommuncationDirectionConsuming] =
982  kEipInvalidSocket;
983 
985  connection_object->socket[kUdpCommuncationDirectionProducing]);
986 
987  connection_object->socket[kUdpCommuncationDirectionProducing] =
988  kEipInvalidSocket;
989 
990  RemoveFromActiveConnections(connection_object);
991  ConnectionObjectInitializeEmpty(connection_object);
992 }
CipInt sin_family
Definition: cpf.h:59
ConnectionObjectConnectionType
EipStatus EstablishIoConnection(CipConnectionObject *RESTRICT const connection_object, EipUint16 *const extended_error)
Establishes a new IO Type 1 Connection.
EipUint16 type_id
Definition: cpf.h:48
CipInstance * producing_instance
Tracing infrastructure for OpENer.
void CloseCommunicationChannelsAndRemoveFromActiveConnectionsList(CipConnectionObject *connection_object)
close the communication channels of the given connection and remove it from the active connections li...
EipUint8 * g_config_data_buffer
struct sockaddr_in remote_address
EipStatus SendUdpData(struct sockaddr_in *socket_data, int socket_handle, EipUint8 *data, EipUint16 data_length)
Create a producing or consuming UDP socket.
CipAttributeStruct * attributes
Definition: ciptypes.h:229
ConnectionReceiveDataFunction connection_receive_data_function
DoublyLinkedListNode * first
void CloseEncapsulationSessionBySockAddr(const CipConnectionObject *const connection_object)
Definition: encap.c:874
EipUint32 eip_level_sequence_count_producing
CipOctet message_buffer[PC_OPENER_ETHERNET_BUFFER_SIZE]
Definition: enipmessage.h:12
Class is a subclass of Instance.
Definition: ciptypes.h:237
SocketAddressInfoItem address_info_item[2]
Definition: cpf.h:71
#define OPENER_ASSERT(assertion)
EipUint16 length
Definition: cpf.h:49
EipStatus OpenProducingPointToPointConnection(CipConnectionObject *connection_object, CipCommonPacketFormatData *common_packet_format_data)
#define SEQ_LEQ16(a, b)
similar macros for comparing 16 bit sequence numbers
UdpCommuncationDirection
Communication direction of an UDP socket; consuming is receiver, producing is sender.
Definition: typedefs.h:104
CIP Byte Array.
Definition: ciptypes.h:122
EipUint16 length
Definition: ciptypes.h:123
DataItem data_item
Definition: cpf.h:70
ConnectionObjectConnectionType ConnectionObjectGetTToOConnectionType(const CipConnectionObject *const connection_object)
EipUint32 GetDintFromMessage(const EipUint8 **const buffer)
Reads EIP_UINT32 from *buffer and converts little endian to host.
Definition: endianconv.c:83
CipOctet * current_message_position
Definition: enipmessage.h:13
void RemoveFromActiveConnections(CipConnectionObject *const connection_object)
Removes connection from the list of active connections.
void ConnectionObjectSetProductionInhibitTime(CipConnectionObject *const connection_object, const CipUint production_inhibit_time)
CipCommonPacketFormatData g_common_packet_format_data_item
Data storage for the any CPF data Currently we are single threaded and need only one CPF at the time...
Definition: cpf.c:24
CipConnectionObject * GetNextNonControlMasterConnection(const EipUint32 input_point)
check if there exists an producing multicast exclusive owner or listen only connection that should pr...
Definition: appcontype.c:362
EipStatus OpenMulticastConnection(UdpCommuncationDirection direction, CipConnectionObject *connection_object, CipCommonPacketFormatData *common_packet_format_data)
Open a Multicast connection dependent on direction.
CipDword instance_id
Definition: cipepath.h:165
CipDword attribute_id_or_connection_point
Definition: cipepath.h:166
unsigned short in_port_t
Responsible for Endianess conversion.
#define OPENER_TRACE_ERR(...)
Definition: trace.h:86
void InitializeENIPMessage(ENIPMessage *const message)
Definition: enipmessage.c:10
MulticastAddressConfiguration g_multicast_configuration
#9 The multicast configuration for this device
CipConnectionObject * GetExistingProducerMulticastConnection(const EipUint32 input_point)
Check if there exists already an exclusive owner or listen only connection which produces the input a...
Definition: appcontype.c:335
CipUint length
Definition: cpf.h:58
EipStatus NotifyAssemblyConnectedDataReceived(CipInstance *const instance, const EipUint8 *const data, const EipUint16 data_length)
notify an Assembly object that data has been received for it.
Definition: cipassembly.c:118
AddressData data
Definition: cpf.h:44
CipUint sin_port
Definition: cpf.h:60
CipUint type_id
Definition: cpf.h:57
EipStatus SendConnectedData(CipConnectionObject *connection_object)
Send the data from the produced CIP Object of the connection via the socket of the connection object ...
ConnectionCloseFunction connection_close_function
CipUdint ConnectionObjectGetTToORequestedPacketInterval(const CipConnectionObject *const connection_object)
CipUdint sin_addr
Definition: cpf.h:61
void CheckIoConnectionEvent(unsigned int output_assembly_id, unsigned int input_assembly_id, IoConnectionEvent io_connection_event)
Inform the application on changes occurred for a connection.
int AssembleIOMessage(const CipCommonPacketFormatData *const common_packet_format_data_item, ENIPMessage *const outgoing_message)
Definition: cpf.c:758
EipStatus OpenCommunicationChannels(CipConnectionObject *connection_object)
Take the data given in the connection object structure and open the necessary communication channels...
void ConnectionObjectInitializeEmpty(CipConnectionObject *const connection_object)
void CloseAllConnectionsForInputWithSameType(const EipUint32 input_point, const ConnectionObjectInstanceType instance_type)
Close all connection producing the same input and have the same type (i.e., listen only or input only...
Definition: appcontype.c:384
bool ConnectionWithSameConfigPointExists(const EipUint32 config_point)
Check if there is an established connection that uses the same config point.
Definition: appcontype.c:417
CipUint length
Definition: cpf.h:43
DoublyLinkedListNode * next
ConnectionObjectPriority ConnectionObjectGetTToOPriority(const CipConnectionObject *const connection_object)
EipUint16 SetupIoConnectionTargetToOriginatorConnectionPoint(CipConnectionObject *const io_connection_object, CipConnectionObject *const RESTRICT connection_object)
int AddIntToMessage(const EipUint16 data, EipUint8 **const buffer)
converts UINT16 data from host to little endian an writes it to buffer.
Definition: endianconv.c:117
EipStatus
EIP stack status enum.
Definition: typedefs.h:93
uint8_t EipUint8
Definition: typedefs.h:32
uint32_t EipUint32
Definition: typedefs.h:34
CipInstance * consuming_instance
int AddDintToMessage(const EipUint32 data, EipUint8 **const buffer)
Converts UINT32 data from host to little endian and writes it to buffer.
Definition: endianconv.c:132
ConnectionObjectConnectionType ConnectionObjectGetOToTConnectionType(const CipConnectionObject *const connection_object)
#define OPENER_TRACE_INFO(...)
Definition: trace.h:89
void CloseUdpSocket(int socket_handle)
CipConnectionPathEpath produced_path
EipUint16 SetupIoConnectionOriginatorToTargetConnectionPoint(CipConnectionObject *const io_connection_object, CipConnectionObject *const RESTRICT connection_object)
void HandleIoConnectionTimeOut(CipConnectionObject *connection_object)
EipStatus OpenConsumingPointToPointConnection(CipConnectionObject *const connection_object, CipCommonPacketFormatData *const common_packet_format_data)
Open a Point2Point connection dependent on pa_direction.
CipUint ConnectionObjectGetProductionInhibitTime(const CipConnectionObject *const connection_object)
CipIdentitySetExtendedDeviceStatus(CipIdentityExtendedStatus extended_status)
Definition: cipidentity.c:185
uint8_t CipUsint
Definition: typedefs.h:46
CipInstance * GetCipInstance(const CipClass *RESTRICT const cip_class, EipUint32 instance_number)
Get a pointer to an instance.
EipUint32 sequence_number
Definition: cpf.h:38
EipUint16 item_count
Definition: cpf.h:68
CipClass * GetCipClass(const EipUint32 class_id)
Get a pointer to a CIP object with given class code.
CipConnectionPathEpath configuration_path
EipStatus HandleReceivedIoConnectionData(CipConnectionObject *connection_object, const EipUint8 *data, EipUint16 data_length)
CipAttributeStruct * GetCipAttribute(const CipInstance *const instance, const EipUint16 attribute_number)
Get a pointer to an instance's attribute.
Definition: cipcommon.c:373
Public interface of the TCP/IP Interface Object.
DoublyLinkedList connection_list
ConnectionTimeoutFunction connection_timeout_function
EipUint16 HandleConfigData(CipConnectionObject *connection_object)
AddressItem address_item
Definition: cpf.h:69
#define OPENER_TRACE_WARN(...)
Definition: trace.h:87
unsigned int g_config_data_length
void RunIdleChanged(EipUint32 run_idle_value)
Inform the application that the Run/Idle State has been changed by the originator.
int CreateUdpSocket(UdpCommuncationDirection communication_direction, struct sockaddr_in *socket_data, CipUsint qos_for_socket)
create a producing or consuming UDP socket
CipConnectionPathEpath consumed_path
EipUint32 g_run_idle_state
CipUint type_id
Definition: cpf.h:42
size_t ConnectionObjectGetOToTConnectionSize(const CipConnectionObject *const connection_object)
const int kOpenerEipIoUdpPort
EipUint32 connection_identifier
Definition: cpf.h:37
CipConnectionObject * GetIoConnectionForConnectionData(CipConnectionObject *const RESTRICT connection_object, EipUint16 *const extended_error)
check if for the given connection data received in a forward_open request a suitable connection is av...
Definition: appcontype.c:127
EipUint16 GetIntFromMessage(const EipUint8 **const buffer)
Reads EIP_UINT16 from *buffer and converts little endian to host.
Definition: endianconv.c:57
CipUsint nasin_zero[8]
Definition: cpf.h:62
ConnectionSendDataFunction connection_send_data_function
void CloseIoConnection(CipConnectionObject *connection_object)
size_t used_message_length
Definition: enipmessage.h:14
ConnectionObjectTransportClassTriggerProductionTrigger ConnectionObjectGetTransportClassTriggerProductionTrigger(const CipConnectionObject *const connection_object)
void ConnectionObjectSetState(CipConnectionObject *const connection_object, const ConnectionObjectState state)
EipBool8 BeforeAssemblyDataSend(CipInstance *instance)
Inform the application that the data of an assembly object will be sent.
A variant of a CPF packet, including item count, one address item, one data item, and two Sockaddr In...
Definition: cpf.h:67
EipStatus OpenProducingMulticastConnection(CipConnectionObject *connection_object, CipCommonPacketFormatData *common_packet_format_data)
EipUint16 ProcessProductionInhibitTime(CipConnectionObject *io_connection_object)
uint16_t EipUint16
Definition: typedefs.h:33
ConnectionObjectInstanceType ConnectionObjectGetInstanceType(const CipConnectionObject *const connection_object)
struct sockaddr_in originator_address
EipByte * data
Definition: ciptypes.h:124
size_t ConnectionObjectGetTToOConnectionSize(const CipConnectionObject *const connection_object)
void ConnectionObjectGeneralConfiguration(CipConnectionObject *const connection_object)
Generate the ConnectionIDs and set the general configuration parameter in the given connection object...
void SetIoConnectionCallbacks(CipConnectionObject *const io_connection_object)
void CheckForTimedOutConnectionsAndCloseTCPConnections(const CipConnectionObject *const connection_object, CloseSessionFunction CloseSessions)
void AddNewActiveConnection(const CipConnectionObject *const connection_object)
Insert the given connection object to the list of currently active and managed connections.
ConnectionObjectTransportClassTriggerTransportClass ConnectionObjectGetTransportClassTriggerTransportClass(const CipConnectionObject *const connection_object)
ConnectionObjectConnectionSizeType ConnectionObjectGetTToOConnectionSizeType(const CipConnectionObject *const connection_object)