cipconnectionmanager.c 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453
  1. /*******************************************************************************
  2. * Copyright (c) 2009, Rockwell Automation, Inc.
  3. * All rights reserved.
  4. *
  5. ******************************************************************************/
  6. #include <string.h>
  7. #include <stdbool.h>
  8. #include "cipconnectionmanager.h"
  9. #include "opener_user_conf.h"
  10. #include "cipcommon.h"
  11. #include "cipmessagerouter.h"
  12. #include "ciperror.h"
  13. #include "endianconv.h"
  14. #include "opener_api.h"
  15. #include "encap.h"
  16. #include "cipidentity.h"
  17. #include "trace.h"
  18. #include "cipclass3connection.h"
  19. #include "cipioconnection.h"
  20. #include "cipassembly.h"
  21. #include "cpf.h"
  22. #include "appcontype.h"
  23. #include "encap.h"
  24. #include "generic_networkhandler.h"
  25. #include "cipepath.h"
  26. #include "cipelectronickey.h"
  27. #include "cipqos.h"
  28. /* values needed from the CIP identity object */
  29. extern EipUint16 vendor_id_;
  30. extern EipUint16 device_type_;
  31. extern EipUint16 product_code_;
  32. extern CipRevision revision_;
  33. #define CIP_CONN_TYPE_MASK 0x6000 /**< Bit mask filter on bit 13 & 14 */
  34. const size_t g_kForwardOpenHeaderLength = 36; /**< the length in bytes of the forward open command specific data till the start of the connection path (including con path size)*/
  35. /** @brief Compares the logical path on equality */
  36. #define EQLOGICALPATH(x,y) ( ( (x) & 0xfc )==(y) )
  37. static const int g_kNumberOfConnectableObjects = 2 +
  38. OPENER_CIP_NUM_APPLICATION_SPECIFIC_CONNECTABLE_OBJECTS;
  39. extern DoublyLinkedList connection_list;
  40. typedef struct {
  41. EipUint32 class_id;
  42. OpenConnectionFunction open_connection_function;
  43. } ConnectionManagementHandling;
  44. /* global variables private */
  45. /** List holding information on the object classes and open/close function
  46. * pointers to which connections may be established.
  47. */
  48. ConnectionManagementHandling g_connection_management_list[2 +
  49. OPENER_CIP_NUM_APPLICATION_SPECIFIC_CONNECTABLE_OBJECTS
  50. ];
  51. /** buffer connection object needed for forward open */
  52. CipConnectionObject g_dummy_connection_object;
  53. /** @brief Holds the connection ID's "incarnation ID" in the upper 16 bits */
  54. EipUint32 g_incarnation_id;
  55. /* private functions */
  56. EipStatus ForwardOpen(
  57. CipInstance *instance,
  58. CipMessageRouterRequest *message_router_request,
  59. CipMessageRouterResponse *message_router_response,
  60. struct sockaddr *originator_address);
  61. EipStatus ForwardClose(
  62. CipInstance *instance,
  63. CipMessageRouterRequest *message_router_request,
  64. CipMessageRouterResponse *message_router_response,
  65. struct sockaddr *originator_address);
  66. EipStatus GetConnectionOwner(
  67. CipInstance *instance,
  68. CipMessageRouterRequest *message_router_request,
  69. CipMessageRouterResponse *message_router_response,
  70. struct sockaddr *originator_address);
  71. EipStatus AssembleForwardOpenResponse(
  72. CipConnectionObject *connection_object,
  73. CipMessageRouterResponse *message_router_response,
  74. EipUint8 general_status,
  75. EipUint16 extended_status);
  76. EipStatus AssembleForwardCloseResponse(
  77. EipUint16 connection_serial_number,
  78. EipUint16 originatior_vendor_id,
  79. EipUint32 originator_serial_number,
  80. CipMessageRouterRequest *message_router_request,
  81. CipMessageRouterResponse *message_router_response,
  82. EipUint16 extended_error_code);
  83. /** @brief check if the data given in the connection object match with an already established connection
  84. *
  85. * The comparison is done according to the definitions in the CIP specification Section 3-5.5.2:
  86. * The following elements have to be equal: Vendor ID, Connection Serial Number, Originator Serial Number
  87. * @param connection_object connection object containing the comparison elements from the forward open request
  88. * @return
  89. * - NULL if no equal established connection exists
  90. * - pointer to the equal connection object
  91. */
  92. CipConnectionObject *CheckForExistingConnection(
  93. const CipConnectionObject *const connection_object);
  94. /** @brief Compare the electronic key received with a forward open request with the device's data.
  95. *
  96. * @param key_format format identifier given in the forward open request
  97. * @param key_data pointer to the electronic key data received in the forward open request
  98. * @param extended_status the extended error code in case an error happened
  99. * @return general status on the establishment
  100. * - EIP_OK ... on success
  101. * - On an error the general status code to be put into the response
  102. */
  103. EipStatus CheckElectronicKeyData(
  104. EipUint8 key_format,
  105. void *key_data,
  106. EipUint16 *extended_status);
  107. /** @brief Parse the connection path of a forward open request
  108. *
  109. * This function will take the connection object and the received data stream and parse the connection path.
  110. * @param connection_object pointer to the connection object structure for which the connection should
  111. * be established
  112. * @param message_router_request pointer to the received request structure. The position of the data stream pointer has to be at the connection length entry
  113. * @param extended_status the extended error code in case an error happened
  114. * @return general status on the establishment
  115. * - EIP_OK ... on success
  116. * - On an error the general status code to be put into the response
  117. */
  118. EipUint8 ParseConnectionPath(
  119. CipConnectionObject *connection_object,
  120. CipMessageRouterRequest *message_router_request,
  121. EipUint16 *extended_error);
  122. ConnectionManagementHandling *GetConnectionManagementEntry(const EipUint32 class_id);
  123. void InitializeConnectionManagerData(void);
  124. void AddNullAddressItem(
  125. CipCommonPacketFormatData *common_data_packet_format_data);
  126. /** @brief gets the padded logical path TODO: enhance documentation
  127. * @param logical_path_segment TheLogical Path Segment
  128. *
  129. * @return The padded logical path
  130. */
  131. unsigned int GetPaddedLogicalPath(const EipUint8 **logical_path_segment) {
  132. unsigned int padded_logical_path = *(*logical_path_segment)++;
  133. if ( (padded_logical_path & 3) == 0 ) {
  134. padded_logical_path = *(*logical_path_segment)++;
  135. } else if ( (padded_logical_path & 3) == 1 ) {
  136. (*logical_path_segment)++; /* skip pad */
  137. padded_logical_path = *(*logical_path_segment)++;
  138. padded_logical_path |= *(*logical_path_segment)++ << 8;
  139. } else {
  140. OPENER_TRACE_ERR("illegal logical path segment\n");
  141. }
  142. return padded_logical_path;
  143. }
  144. /** @brief Generate a new connection Id utilizing the Incarnation Id as
  145. * described in the EIP specs.
  146. *
  147. * A unique connectionID is formed from the boot-time-specified "incarnation ID"
  148. * and the per-new-connection-incremented connection number/counter.
  149. * @return new connection id
  150. */
  151. CipUint GetConnectionId(void) {
  152. static CipUint connection_id = 18;
  153. connection_id++;
  154. return ( g_incarnation_id | (connection_id & 0x0000FFFF) );
  155. }
  156. void InitializeConnectionManager(CipClass *class) {
  157. CipClass *meta_class = class->class_instance.cip_class;
  158. InsertAttribute( (CipInstance *) class, 1, kCipUint,
  159. (void *) &class->revision,
  160. kGetableSingleAndAll ); /* revision */
  161. InsertAttribute( (CipInstance *) class, 2, kCipUint,
  162. (void *) &class->number_of_instances, kGetableSingleAndAll ); /* largest instance number */
  163. InsertAttribute( (CipInstance *) class, 3, kCipUint,
  164. (void *) &class->number_of_instances, kGetableSingle ); /* number of instances currently existing*/
  165. InsertAttribute( (CipInstance *) class, 4, kCipUint, (void *) &kCipUintZero,
  166. kNotSetOrGetable ); /* optional attribute list - default = 0 */
  167. InsertAttribute( (CipInstance *) class, 5, kCipUint, (void *) &kCipUintZero,
  168. kNotSetOrGetable ); /* optional service list - default = 0 */
  169. InsertAttribute( (CipInstance *) class, 6, kCipUint,
  170. (void *) &meta_class->highest_attribute_number,
  171. kGetableSingleAndAll ); /* max class attribute number*/
  172. InsertAttribute( (CipInstance *) class, 7, kCipUint,
  173. (void *) &class->highest_attribute_number,
  174. kGetableSingleAndAll ); /* max instance attribute number*/
  175. }
  176. EipStatus ConnectionManagerInit(EipUint16 unique_connection_id) {
  177. InitializeConnectionManagerData();
  178. CipClass *connection_manager = CreateCipClass(
  179. g_kCipConnectionManagerClassCode, /* class ID */
  180. 0, /* # of class attributes */
  181. 7, /* # highest class attribute number*/
  182. 2, /* # of class services */
  183. 0, /* # of instance attributes */
  184. 14, /* # highest instance attribute number*/
  185. 5, /* # of instance services */
  186. 1, /* # of instances */
  187. "connection manager", /* class name */
  188. 1, /* revision */
  189. &InitializeConnectionManager); /* # function pointer for initialization*/
  190. if (connection_manager == NULL) {
  191. return kEipStatusError;
  192. }
  193. InsertService(connection_manager, kGetAttributeSingle, &GetAttributeSingle,
  194. "GetAttributeSingle");
  195. InsertService(connection_manager, kGetAttributeAll, &GetAttributeAll,
  196. "GetAttributeAll");
  197. InsertService(connection_manager, kForwardOpen, &ForwardOpen, "ForwardOpen");
  198. InsertService(connection_manager, kForwardClose, &ForwardClose,
  199. "ForwardClose");
  200. InsertService(connection_manager, kGetConnectionOwner, &GetConnectionOwner,
  201. "GetConnectionOwner");
  202. g_incarnation_id = ( (EipUint32) unique_connection_id ) << 16;
  203. AddConnectableObject(kCipMessageRouterClassCode, EstablishClass3Connection);
  204. AddConnectableObject(kCipAssemblyClassCode, EstablishIoConnection);
  205. return kEipStatusOk;
  206. }
  207. EipStatus HandleReceivedConnectedData(
  208. EipUint8 *data,
  209. int data_length,
  210. struct sockaddr_in *from_address
  211. ) {
  212. if ( ( CreateCommonPacketFormatStructure(data, data_length,
  213. &g_common_packet_format_data_item) )
  214. == kEipStatusError ) {
  215. return kEipStatusError;
  216. } else {
  217. /* check if connected address item or sequenced address item received, otherwise it is no connected message and should not be here */
  218. if ( (g_common_packet_format_data_item.address_item.type_id
  219. == kCipItemIdConnectionAddress)
  220. || (g_common_packet_format_data_item.address_item.type_id
  221. == kCipItemIdSequencedAddressItem) ) { /* found connected address item or found sequenced address item -> for now the sequence number will be ignored */
  222. if (g_common_packet_format_data_item.data_item.type_id
  223. == kCipItemIdConnectedDataItem) { /* connected data item received */
  224. CipConnectionObject *connection_object = GetConnectedObject(
  225. g_common_packet_format_data_item.address_item.data
  226. .connection_identifier);
  227. if (connection_object == NULL) {
  228. return kEipStatusError;
  229. }
  230. /* only handle the data if it is coming from the originator */
  231. if (connection_object->originator_address.sin_addr.s_addr
  232. == from_address->sin_addr.s_addr) {
  233. if ( SEQ_GT32(
  234. g_common_packet_format_data_item.address_item.data.
  235. sequence_number,
  236. connection_object->eip_level_sequence_count_consuming) ) {
  237. /* reset the watchdog timer */
  238. ConnectionObjectResetInactivityWatchdogTimerValue(connection_object);
  239. /* only inform assembly object if the sequence counter is greater or equal */
  240. connection_object->eip_level_sequence_count_consuming =
  241. g_common_packet_format_data_item.address_item.data
  242. .sequence_number;
  243. if (NULL != connection_object->connection_receive_data_function) {
  244. return connection_object->connection_receive_data_function(
  245. connection_object,
  246. g_common_packet_format_data_item.data_item.data,
  247. g_common_packet_format_data_item.data_item.length);
  248. }
  249. }
  250. } else {
  251. OPENER_TRACE_WARN(
  252. "Connected Message Data Received with wrong address information\n");
  253. }
  254. }
  255. }
  256. }
  257. return kEipStatusOk;
  258. }
  259. /** @brief Function prototype for all Forward Open handle functions
  260. *
  261. */
  262. typedef EipStatus (*HandleForwardOpenRequestFunction)(
  263. CipConnectionObject *connection_object, CipInstance *instance,
  264. CipMessageRouterRequest *message_router_request,
  265. CipMessageRouterResponse *message_router_response);
  266. /** @brief Handles a Null Non Matching Forward Open Request
  267. *
  268. * Null, Non-Matching - Either ping device, or configure a device’s application,
  269. * or return General Status kCipErrorConnectionFailure and
  270. * Extended Status kConnectionManagerExtendedStatusCodeNullForwardOpenNotSupported
  271. */
  272. EipStatus HandleNullNonMatchingForwardOpenRequest(
  273. CipConnectionObject *connection_object,
  274. CipInstance *instance,
  275. CipMessageRouterRequest *message_router_request,
  276. CipMessageRouterResponse *message_router_response);
  277. EipStatus HandleNullNonMatchingForwardOpenRequest(
  278. CipConnectionObject *connection_object,
  279. CipInstance *instance,
  280. CipMessageRouterRequest *message_router_request,
  281. CipMessageRouterResponse *message_router_response
  282. ) {
  283. OPENER_TRACE_INFO("Right now we cannot handle Null requests\n");
  284. return AssembleForwardOpenResponse(
  285. connection_object,
  286. message_router_response,
  287. kCipErrorConnectionFailure,
  288. kConnectionManagerExtendedStatusCodeNullForwardOpenNotSupported);
  289. }
  290. /** @brief Handles a Null Matching Forward Open request
  291. *
  292. * Either reconfigure a target device’s application, or
  293. * return General Status kCipErrorConnectionFailure and
  294. * Extended Status kConnectionManagerExtendedStatusCodeNullForwardOpenNotSupported
  295. */
  296. EipStatus HandleNullMatchingForwardOpenRequest(
  297. CipConnectionObject *connection_object,
  298. CipInstance *instance,
  299. CipMessageRouterRequest *message_router_request,
  300. CipMessageRouterResponse *message_router_response);
  301. EipStatus HandleNullMatchingForwardOpenRequest(
  302. CipConnectionObject *connection_object,
  303. CipInstance *instance,
  304. CipMessageRouterRequest *message_router_request,
  305. CipMessageRouterResponse *message_router_response
  306. ) {
  307. OPENER_TRACE_INFO("Right now we cannot handle Null requests\n");
  308. return AssembleForwardOpenResponse(
  309. connection_object,
  310. message_router_response,
  311. kCipErrorConnectionFailure,
  312. kConnectionManagerExtendedStatusCodeNullForwardOpenNotSupported);
  313. }
  314. /** @brief Handles a Non Null Matching Forward Open Request
  315. *
  316. * Non-Null, Matching request - Return General Status = kCipErrorConnectionFailure,
  317. * Extended Status = kConnectionManagerExtendedStatusCodeErrorConnectionInUseOrDuplicateForwardOpen
  318. */
  319. EipStatus HandleNonNullMatchingForwardOpenRequest(
  320. CipConnectionObject *connection_object,
  321. CipInstance *instance,
  322. CipMessageRouterRequest *message_router_request,
  323. CipMessageRouterResponse *message_router_response);
  324. EipStatus HandleNonNullMatchingForwardOpenRequest(
  325. CipConnectionObject *connection_object,
  326. CipInstance *instance,
  327. CipMessageRouterRequest *message_router_request,
  328. CipMessageRouterResponse *message_router_response
  329. ) {
  330. OPENER_TRACE_INFO("Right now we cannot handle reconfiguration requests\n");
  331. return AssembleForwardOpenResponse(
  332. connection_object,
  333. message_router_response,
  334. kCipErrorConnectionFailure,
  335. kConnectionManagerExtendedStatusCodeErrorConnectionInUseOrDuplicateForwardOpen);
  336. }
  337. /** @brief Handles a Non Null Non Matching Forward Open Request
  338. *
  339. * Non-Null, Non-Matching request - Establish a new connection
  340. */
  341. EipStatus HandleNonNullNonMatchingForwardOpenRequest(
  342. CipConnectionObject *connection_object,
  343. CipInstance *instance,
  344. CipMessageRouterRequest *message_router_request,
  345. CipMessageRouterResponse *message_router_response);
  346. EipStatus HandleNonNullNonMatchingForwardOpenRequest(
  347. CipConnectionObject *connection_object,
  348. CipInstance *instance,
  349. CipMessageRouterRequest *message_router_request,
  350. CipMessageRouterResponse *message_router_response
  351. ) {
  352. EipUint16 connection_status = kConnectionManagerExtendedStatusCodeSuccess;
  353. /*check if the trigger type value is invalid or ok */
  354. if(kConnectionObjectTransportClassTriggerProductionTriggerInvalid == ConnectionObjectGetTransportClassTriggerProductionTrigger(&g_dummy_connection_object)) {
  355. return AssembleForwardOpenResponse(
  356. &g_dummy_connection_object,
  357. message_router_response,
  358. kCipErrorConnectionFailure,
  359. kConnectionManagerExtendedStatusCodeErrorTransportClassAndTriggerCombinationNotSupported);
  360. }
  361. EipUint32 temp = ParseConnectionPath(&g_dummy_connection_object,
  362. message_router_request,
  363. &connection_status);
  364. if (kEipStatusOk != temp) {
  365. return AssembleForwardOpenResponse(&g_dummy_connection_object,
  366. message_router_response, temp,
  367. connection_status);
  368. }
  369. /*parsing is now finished all data is available and check now establish the connection */
  370. ConnectionManagementHandling *connection_management_entry =
  371. GetConnectionManagementEntry( /* Gets correct open connection function for the targeted object */
  372. g_dummy_connection_object.configuration_path.class_id);
  373. if (NULL != connection_management_entry) {
  374. temp = connection_management_entry->open_connection_function(
  375. &g_dummy_connection_object, &connection_status);
  376. } else {
  377. temp = kEipStatusError;
  378. connection_status =
  379. kConnectionManagerExtendedStatusCodeInconsistentApplicationPathCombo;
  380. }
  381. if (kEipStatusOk != temp) {
  382. OPENER_TRACE_INFO("connection manager: connect failed\n");
  383. /* in case of error the dummy objects holds all necessary information */
  384. return AssembleForwardOpenResponse(&g_dummy_connection_object,
  385. message_router_response, temp,
  386. connection_status);
  387. } else {
  388. OPENER_TRACE_INFO("connection manager: connect succeeded\n");
  389. /* in case of success the new connection is added at the head of the connection list */
  390. return AssembleForwardOpenResponse(connection_list.first->data,
  391. message_router_response,
  392. kCipErrorSuccess, 0);
  393. }
  394. }
  395. /** @brief Array of Forward Open handle function pointers
  396. *
  397. * File scope variable
  398. * The first dimension handles if the request was a non-null request (0) or a null request (1),
  399. * the second dimension handles if the request was a non-matchin (0) or matching request (1)
  400. */
  401. static const HandleForwardOpenRequestFunction
  402. handle_forward_open_request_functions[2][2] =
  403. { { HandleNonNullNonMatchingForwardOpenRequest,
  404. HandleNonNullMatchingForwardOpenRequest }, {
  405. HandleNullNonMatchingForwardOpenRequest,
  406. HandleNullMatchingForwardOpenRequest
  407. } };
  408. /** @brief Check if resources for new connection available, generate ForwardOpen Reply message.
  409. *
  410. * Forward Open four cases
  411. * Non-Null/Not matching - open a connection
  412. * Non-Null/Matching - error
  413. * Null/Not matching - ping a device/configure
  414. * Null/Matching - reconfigure
  415. *
  416. * Null connection - both O->T and T->O connection parameter field are null
  417. * Non-Null connection - one or both O->T and T->O connection parameter field are not null
  418. * Matching - Connection Triad matches an existing connection
  419. * (Connection Serial Number, Originator Vendor ID and Originator Serial Number)
  420. *
  421. * @param instance pointer to CIP object instance
  422. * @param message_router_request pointer to Message Router Request.
  423. * @param message_router_response pointer to Message Router Response.
  424. * @return >0 .. success, 0 .. no reply to send back
  425. * -1 .. error
  426. */
  427. EipStatus ForwardOpen(
  428. CipInstance *instance,
  429. CipMessageRouterRequest *message_router_request,
  430. CipMessageRouterResponse *message_router_response,
  431. struct sockaddr *originator_address
  432. ) {
  433. (void) instance; /*suppress compiler warning */
  434. bool is_null_request = false; /* 1 = Null Request, 0 = Non-Null Request */
  435. bool is_matching_request = false; /* 1 = Matching Request, 0 = Non-Matching Request */
  436. /*first check if we have already a connection with the given params */
  437. ConnectionObjectInitializeFromMessage(&(message_router_request->data),
  438. &g_dummy_connection_object);
  439. memcpy( &(g_dummy_connection_object.originator_address), originator_address,
  440. sizeof(g_dummy_connection_object.originator_address) );
  441. ConnectionObjectConnectionType o_to_t_connection_type = ConnectionObjectGetOToTConnectionType(&g_dummy_connection_object);
  442. ConnectionObjectConnectionType t_to_o_connection_type = ConnectionObjectGetTToOConnectionType(&g_dummy_connection_object);
  443. /* Check if both connection types are valid, otherwise send error response */
  444. if (kConnectionObjectConnectionTypeInvalid == o_to_t_connection_type) {
  445. return AssembleForwardOpenResponse(
  446. &g_dummy_connection_object, message_router_response,
  447. kCipErrorConnectionFailure,
  448. kConnectionManagerExtendedStatusCodeErrorInvalidOToTConnectionType);
  449. }
  450. if (kConnectionObjectConnectionTypeInvalid == t_to_o_connection_type) {
  451. return AssembleForwardOpenResponse(
  452. &g_dummy_connection_object, message_router_response,
  453. kCipErrorConnectionFailure,
  454. kConnectionManagerExtendedStatusCodeErrorInvalidTToOConnectionType);
  455. }
  456. /* Check if request is a Null request or a Non-Null request */
  457. if (kConnectionObjectConnectionTypeNull == o_to_t_connection_type
  458. && kConnectionObjectConnectionTypeNull == t_to_o_connection_type) {
  459. is_null_request = true;
  460. OPENER_TRACE_INFO("We have a Null request\n");
  461. } else {
  462. is_null_request = false;
  463. OPENER_TRACE_INFO("We have a Non-Null request\n");
  464. }
  465. /* Check if we have a matching or non matching request */
  466. if ( ( NULL != CheckForExistingConnection(&g_dummy_connection_object) ) ) {
  467. OPENER_TRACE_INFO("We have a Matching request\n");
  468. is_matching_request = true;
  469. } else {
  470. OPENER_TRACE_INFO("We have a Non-Matching request\n");
  471. is_matching_request = false;
  472. }
  473. HandleForwardOpenRequestFunction choosen_function =
  474. handle_forward_open_request_functions[is_null_request][is_matching_request];
  475. return choosen_function(&g_dummy_connection_object, instance,
  476. message_router_request, message_router_response);
  477. }
  478. EipStatus ForwardClose(
  479. CipInstance *instance,
  480. CipMessageRouterRequest *message_router_request,
  481. CipMessageRouterResponse *message_router_response,
  482. struct sockaddr *originator_address
  483. ) {
  484. /*Suppress compiler warning*/
  485. (void) instance;
  486. /* check connection_serial_number && originator_vendor_id && originator_serial_number if connection is established */
  487. ConnectionManagerExtendedStatusCode connection_status =
  488. kConnectionManagerExtendedStatusCodeErrorConnectionTargetConnectionNotFound;
  489. /* set AddressInfo Items to invalid TypeID to prevent assembleLinearMsg to read them */
  490. g_common_packet_format_data_item.address_info_item[0].type_id = 0;
  491. g_common_packet_format_data_item.address_info_item[1].type_id = 0;
  492. message_router_request->data += 2; /* ignore Priority/Time_tick and Time-out_ticks */
  493. EipUint16 connection_serial_number = GetIntFromMessage(
  494. &message_router_request->data);
  495. EipUint16 originator_vendor_id = GetIntFromMessage(
  496. &message_router_request->data);
  497. EipUint32 originator_serial_number = GetDintFromMessage(
  498. &message_router_request->data);
  499. OPENER_TRACE_INFO("ForwardClose: ConnSerNo %d\n", connection_serial_number);
  500. DoublyLinkedListNode *node = connection_list.first;
  501. while (NULL != node) {
  502. /* this check should not be necessary as only established connections should be in the active connection list */
  503. CipConnectionObject *connection_object = node->data;
  504. if ( (kConnectionObjectStateEstablished == ConnectionObjectGetState(connection_object))
  505. || (kConnectionObjectStateTimedOut == ConnectionObjectGetState(connection_object)) ) {
  506. if ( (connection_object->connection_serial_number
  507. == connection_serial_number)
  508. && (connection_object->originator_vendor_id == originator_vendor_id)
  509. && (connection_object->originator_serial_number
  510. == originator_serial_number) ) {
  511. /* found the corresponding connection object -> close it */
  512. OPENER_ASSERT(NULL != connection_object->connection_close_function);
  513. if ( ( (struct sockaddr_in *) originator_address )->sin_addr.s_addr
  514. == connection_object->originator_address.sin_addr.s_addr ) {
  515. connection_object->connection_close_function(connection_object);
  516. connection_status = kConnectionManagerExtendedStatusCodeSuccess;
  517. } else {
  518. connection_status = kConnectionManagerExtendedStatusWrongCloser;
  519. }
  520. break;
  521. }
  522. }
  523. node = node->next;
  524. }
  525. if(
  526. kConnectionManagerExtendedStatusCodeErrorConnectionTargetConnectionNotFound
  527. ==
  528. connection_status) {
  529. OPENER_TRACE_INFO(
  530. "Connection not found! Requested connection triad: %u, %u, %u\n",
  531. connection_serial_number,
  532. originator_vendor_id,
  533. originator_serial_number);
  534. }
  535. return AssembleForwardCloseResponse(connection_serial_number,
  536. originator_vendor_id,
  537. originator_serial_number,
  538. message_router_request,
  539. message_router_response,
  540. connection_status);
  541. }
  542. /* TODO: Not implemented */
  543. EipStatus GetConnectionOwner(
  544. CipInstance *instance,
  545. CipMessageRouterRequest *message_router_request,
  546. CipMessageRouterResponse *message_router_response,
  547. struct sockaddr *originator_address
  548. ) {
  549. /* suppress compiler warnings */
  550. (void) instance;
  551. (void) message_router_request;
  552. (void) message_router_response;
  553. return kEipStatusOk;
  554. }
  555. EipStatus ManageConnections(MilliSeconds elapsed_time) {
  556. //OPENER_TRACE_INFO("Entering ManageConnections\n");
  557. /*Inform application that it can execute */
  558. HandleApplication();
  559. ManageEncapsulationMessages(elapsed_time);
  560. DoublyLinkedListNode *node = connection_list.first;
  561. while (NULL != node) {
  562. //OPENER_TRACE_INFO("Entering Connection Object loop\n");
  563. CipConnectionObject *connection_object = node->data;
  564. if (kConnectionObjectStateEstablished == ConnectionObjectGetState(connection_object)) {
  565. if ( (NULL != connection_object->consuming_instance) || /* we have a consuming connection check inactivity watchdog timer */
  566. (kConnectionObjectTransportClassTriggerDirectionServer == ConnectionObjectGetTransportClassTriggerDirection(connection_object)) ) /* all server connections have to maintain an inactivity watchdog timer */
  567. {
  568. if (elapsed_time >= connection_object->inactivity_watchdog_timer) {
  569. /* we have a timed out connection perform watchdog time out action*/
  570. OPENER_TRACE_INFO(">>>>>>>>>>Connection ConnNr: %u timed out\n",
  571. connection_object->connection_serial_number);
  572. OPENER_ASSERT(NULL != connection_object->connection_timeout_function);
  573. connection_object->connection_timeout_function(connection_object);
  574. } else {
  575. connection_object->inactivity_watchdog_timer -= elapsed_time;
  576. }
  577. }
  578. /* only if the connection has not timed out check if data is to be send */
  579. if (kConnectionObjectStateEstablished == ConnectionObjectGetState(connection_object)) {
  580. /* client connection */
  581. if ( (0 != connection_object->expected_packet_rate)
  582. && (kEipInvalidSocket
  583. != connection_object->socket[
  584. kUdpCommuncationDirectionProducing
  585. ]) ) /* only produce for the master connection */
  586. {
  587. if ( kConnectionObjectTransportClassTriggerProductionTriggerCyclic
  588. != ConnectionObjectGetTransportClassTriggerProductionTrigger(connection_object) ) {
  589. /* non cyclic connections have to decrement production inhibit timer */
  590. if (0 <= connection_object->production_inhibit_timer) {
  591. connection_object->production_inhibit_timer -= elapsed_time;
  592. }
  593. }
  594. connection_object->transmission_trigger_timer -= elapsed_time;
  595. if (connection_object->transmission_trigger_timer <= 0) { /* need to send package */
  596. OPENER_ASSERT(
  597. NULL != connection_object->connection_send_data_function);
  598. EipStatus eip_status = connection_object
  599. ->connection_send_data_function(
  600. connection_object);
  601. if (eip_status == kEipStatusError) {
  602. OPENER_TRACE_ERR(
  603. "sending of UDP data in manage Connection failed\n");
  604. }
  605. /* reload the timer value */
  606. connection_object->transmission_trigger_timer = connection_object
  607. ->
  608. expected_packet_rate;
  609. if ( kConnectionObjectTransportClassTriggerProductionTriggerCyclic
  610. != ConnectionObjectGetTransportClassTriggerProductionTrigger(connection_object) ) {
  611. /* non cyclic connections have to reload the production inhibit timer */
  612. connection_object->production_inhibit_timer = connection_object
  613. ->
  614. production_inhibit_time;
  615. }
  616. }
  617. }
  618. }
  619. }
  620. node = node->next;
  621. }
  622. return kEipStatusOk;
  623. }
  624. /** @brief Assembles the Forward Open Response
  625. *
  626. * @param connection_object pointer to connection Object
  627. * @param message_router_response pointer to message router response
  628. * @param general_status the general status of the response
  629. * @param extended_status extended status in the case of an error otherwise 0
  630. * @return status
  631. * kEipStatusOk .. no reply need to be sent back
  632. * kEipStatusOkSend .. need to send reply
  633. * kEipStatusError .. error
  634. */
  635. EipStatus AssembleForwardOpenResponse(
  636. CipConnectionObject *connection_object,
  637. CipMessageRouterResponse *message_router_response,
  638. EipUint8 general_status,
  639. EipUint16 extended_status
  640. ) {
  641. /* write reply information in CPF struct dependent of pa_status */
  642. CipCommonPacketFormatData *cip_common_packet_format_data =
  643. &g_common_packet_format_data_item;
  644. EipByte *message = message_router_response->data;
  645. cip_common_packet_format_data->item_count = 2;
  646. cip_common_packet_format_data->data_item.type_id =
  647. kCipItemIdUnconnectedDataItem;
  648. AddNullAddressItem(cip_common_packet_format_data);
  649. message_router_response->reply_service = (0x80 | kForwardOpen);
  650. message_router_response->general_status = general_status;
  651. if (kCipErrorSuccess == general_status) {
  652. OPENER_TRACE_INFO("assembleFWDOpenResponse: sending success response\n");
  653. message_router_response->data_length = 26; /* if there is no application specific data */
  654. message_router_response->size_of_additional_status = 0;
  655. if (cip_common_packet_format_data->address_info_item[0].type_id != 0) {
  656. cip_common_packet_format_data->item_count = 3;
  657. if (cip_common_packet_format_data->address_info_item[1].type_id != 0) {
  658. cip_common_packet_format_data->item_count = 4; /* there are two sockaddrinfo items to add */
  659. }
  660. }
  661. AddDintToMessage(connection_object->cip_consumed_connection_id, &message);
  662. AddDintToMessage(connection_object->cip_produced_connection_id, &message);
  663. } else {
  664. /* we have an connection creation error */
  665. OPENER_TRACE_INFO("AssembleForwardOpenResponse: sending error response\n");
  666. ConnectionObjectSetState(connection_object, kConnectionObjectStateNonExistent);
  667. message_router_response->data_length = 10;
  668. switch (general_status) {
  669. case kCipErrorNotEnoughData:
  670. case kCipErrorTooMuchData: {
  671. message_router_response->size_of_additional_status = 0;
  672. break;
  673. }
  674. default: {
  675. switch (extended_status) {
  676. case
  677. kConnectionManagerExtendedStatusCodeErrorInvalidOToTConnectionSize:
  678. {
  679. message_router_response->size_of_additional_status = 2;
  680. message_router_response->additional_status[0] = extended_status;
  681. message_router_response->additional_status[1] = connection_object
  682. ->
  683. correct_originator_to_target_size;
  684. break;
  685. }
  686. case
  687. kConnectionManagerExtendedStatusCodeErrorInvalidTToOConnectionSize:
  688. {
  689. message_router_response->size_of_additional_status = 2;
  690. message_router_response->additional_status[0] = extended_status;
  691. message_router_response->additional_status[1] = connection_object
  692. ->
  693. correct_target_to_originator_size;
  694. break;
  695. }
  696. default: {
  697. message_router_response->size_of_additional_status = 1;
  698. message_router_response->additional_status[0] = extended_status;
  699. break;
  700. }
  701. }
  702. break;
  703. }
  704. }
  705. }
  706. AddIntToMessage(connection_object->connection_serial_number, &message);
  707. AddIntToMessage(connection_object->originator_vendor_id, &message);
  708. AddDintToMessage(connection_object->originator_serial_number, &message);
  709. if (kCipErrorSuccess == general_status) {
  710. /* set the actual packet rate to requested packet rate */
  711. AddDintToMessage(connection_object->o_to_t_requested_packet_interval,
  712. &message);
  713. AddDintToMessage(connection_object->t_to_o_requested_packet_interval,
  714. &message);
  715. }
  716. *message = 0; /* remaining path size - for routing devices relevant */
  717. message++;
  718. *message = 0; /* reserved */
  719. message++;
  720. return kEipStatusOkSend; /* send reply */
  721. }
  722. /**
  723. * @brief Adds a Null Address Item to the common data packet format data
  724. * @param common_data_packet_format_data The CPF data packet where the Null Address Item shall be added
  725. */
  726. void AddNullAddressItem(
  727. CipCommonPacketFormatData *common_data_packet_format_data) {
  728. /* Precondition: Null Address Item only valid in unconnected messages */
  729. assert(
  730. common_data_packet_format_data->data_item.type_id
  731. == kCipItemIdUnconnectedDataItem);
  732. common_data_packet_format_data->address_item.type_id = kCipItemIdNullAddress;
  733. common_data_packet_format_data->address_item.length = 0;
  734. }
  735. /* INT8 assembleFWDCloseResponse(UINT16 pa_ConnectionSerialNr, UINT16 pa_OriginatorVendorID, UINT32 pa_OriginatorSerialNr, S_CIP_MR_Request *pa_MRRequest, S_CIP_MR_Response *pa_MRResponse, S_CIP_CPF_Data *pa_CPF_data, INT8 pa_status, INT8 *pa_msg)
  736. * create FWDClose response dependent on status.
  737. * pa_ConnectionSerialNr requested ConnectionSerialNr
  738. * pa_OriginatorVendorID requested OriginatorVendorID
  739. * pa_OriginatorSerialNr requested OriginalSerialNr
  740. * pa_MRRequest pointer to message router request
  741. * pa_MRResponse pointer to message router response
  742. * pa_CPF_data pointer to CPF Data Item
  743. * pa_status status of FWDClose
  744. * pa_msg pointer to memory where reply has to be stored
  745. * return status
  746. * 0 .. no reply need to ne sent back
  747. * 1 .. need to send reply
  748. * -1 .. error
  749. */
  750. EipStatus AssembleForwardCloseResponse(
  751. EipUint16 connection_serial_number,
  752. EipUint16 originatior_vendor_id,
  753. EipUint32 originator_serial_number,
  754. CipMessageRouterRequest *message_router_request,
  755. CipMessageRouterResponse *message_router_response,
  756. EipUint16 extended_error_code
  757. ) {
  758. /* write reply information in CPF struct dependent of pa_status */
  759. CipCommonPacketFormatData *common_data_packet_format_data =
  760. &g_common_packet_format_data_item;
  761. EipByte *message = message_router_response->data;
  762. common_data_packet_format_data->item_count = 2;
  763. common_data_packet_format_data->data_item.type_id =
  764. kCipItemIdUnconnectedDataItem;
  765. AddNullAddressItem(common_data_packet_format_data);
  766. AddIntToMessage(connection_serial_number, &message);
  767. AddIntToMessage(originatior_vendor_id, &message);
  768. AddDintToMessage(originator_serial_number, &message);
  769. message_router_response->reply_service = (0x80
  770. | message_router_request->service);
  771. message_router_response->data_length = 10; /* if there is no application specific data */
  772. if (kConnectionManagerExtendedStatusCodeSuccess == extended_error_code) {
  773. *message = 0; /* no application data */
  774. message_router_response->general_status = kCipErrorSuccess;
  775. message_router_response->size_of_additional_status = 0;
  776. } else {
  777. *message = *message_router_request->data; /* remaining path size */
  778. if (kConnectionManagerExtendedStatusWrongCloser == extended_error_code) {
  779. message_router_response->general_status = kCipErrorPrivilegeViolation;
  780. } else {
  781. message_router_response->general_status = kCipErrorConnectionFailure;
  782. message_router_response->additional_status[0] = extended_error_code;
  783. message_router_response->size_of_additional_status = 1;
  784. }
  785. }
  786. message++;
  787. *message = 0; /* reserved */
  788. message++;
  789. return kEipStatusOkSend;
  790. }
  791. CipConnectionObject *GetConnectedObject(const EipUint32 connection_id) {
  792. DoublyLinkedListNode *iterator = connection_list.first;
  793. while(NULL != iterator->next) {
  794. if(kConnectionObjectStateEstablished == ConnectionObjectGetState(iterator->data) &&
  795. connection_id == ConnectionObjectGetCipConsumedConnectionID(iterator->data)) {
  796. return iterator->data;
  797. }
  798. iterator = iterator->next;
  799. }
  800. return NULL;
  801. }
  802. CipConnectionObject *GetConnectedOutputAssembly(const EipUint32 output_assembly_id) {
  803. DoublyLinkedListNode *iterator = connection_list.first;
  804. while(NULL != iterator->next) {
  805. if(kConnectionObjectStateEstablished == ConnectionObjectGetState(iterator->data) &&
  806. output_assembly_id == ((CipConnectionObject*)iterator->data)->produced_path.instance_id ) {
  807. return iterator->data;
  808. }
  809. iterator = iterator->next;
  810. }
  811. return NULL;
  812. }
  813. bool EqualConnectionTriad(const CipConnectionObject const* object1, const CipConnectionObject const* object2) {
  814. if ( (object1->connection_serial_number
  815. == object2->connection_serial_number)
  816. && (object1->originator_vendor_id
  817. == object2->originator_vendor_id)
  818. && (object1->originator_serial_number
  819. == object2->originator_serial_number) ) {
  820. return true;
  821. }
  822. return false;
  823. }
  824. CipConnectionObject *CheckForExistingConnection(
  825. const CipConnectionObject *const connection_object) {
  826. DoublyLinkedListNode *iterator = connection_list.first;
  827. while(NULL != iterator) {
  828. if(kConnectionObjectStateEstablished == ConnectionObjectGetState(iterator->data)) {
  829. if(EqualConnectionTriad(connection_object, iterator->data)){
  830. return iterator->data;
  831. }
  832. }
  833. iterator = iterator->next;
  834. }
  835. return NULL;
  836. }
  837. EipStatus CheckElectronicKeyData(
  838. EipUint8 key_format,
  839. void *key_data,
  840. EipUint16 *extended_status
  841. ) {
  842. bool compatiblity_mode = ElectronicKeyFormat4GetMajorRevisionCompatibility(key_data);
  843. /* Default return value */
  844. *extended_status = kConnectionManagerExtendedStatusCodeSuccess;
  845. /* Check key format */
  846. if (4 != key_format) {
  847. *extended_status =
  848. kConnectionManagerExtendedStatusCodeErrorInvalidSegmentTypeInPath;
  849. return kEipStatusError;
  850. }
  851. /* Check VendorID and ProductCode, must match, or 0 */
  852. if ( ( (ElectronicKeyFormat4GetVendorId(key_data) != vendor_id_) && (ElectronicKeyFormat4GetVendorId(key_data) != 0) )
  853. || ( (ElectronicKeyFormat4GetProductCode(key_data) != product_code_)
  854. && (ElectronicKeyFormat4GetProductCode(key_data) != 0) ) ) {
  855. *extended_status =
  856. kConnectionManagerExtendedStatusCodeErrorVendorIdOrProductcodeError;
  857. return kEipStatusError;
  858. } else {
  859. /* VendorID and ProductCode are correct */
  860. /* Check DeviceType, must match or 0 */
  861. if ( (ElectronicKeyFormat4GetDeviceType(key_data) != device_type_)
  862. && (ElectronicKeyFormat4GetDeviceType(key_data) != 0) ) {
  863. *extended_status =
  864. kConnectionManagerExtendedStatusCodeErrorDeviceTypeError;
  865. return kEipStatusError;
  866. } else {
  867. /* VendorID, ProductCode and DeviceType are correct */
  868. if (false == compatiblity_mode) {
  869. /* Major = 0 is valid */
  870. if (0 == ElectronicKeyFormat4GetDeviceType(key_data)) {
  871. return kEipStatusOk;
  872. }
  873. /* Check Major / Minor Revision, Major must match, Minor match or 0 */
  874. if ( (ElectronicKeyFormat4GetMajorRevision(key_data) != revision_.major_revision)
  875. || ( (ElectronicKeyFormat4GetMinorRevision(key_data) != revision_.minor_revision)
  876. && (ElectronicKeyFormat4GetMinorRevision(key_data) != 0) ) ) {
  877. *extended_status =
  878. kConnectionManagerExtendedStatusCodeErrorRevisionMismatch;
  879. return kEipStatusError;
  880. }
  881. } else {
  882. /* Compatibility mode is set */
  883. /* Major must match, Minor != 0 and <= MinorRevision */
  884. if ( (ElectronicKeyFormat4GetMajorRevision(key_data) == revision_.major_revision)
  885. && (ElectronicKeyFormat4GetMinorRevision(key_data) > 0)
  886. && (ElectronicKeyFormat4GetMinorRevision(key_data) <= revision_.minor_revision) ) {
  887. return kEipStatusOk;
  888. } else {
  889. *extended_status =
  890. kConnectionManagerExtendedStatusCodeErrorRevisionMismatch;
  891. return kEipStatusError;
  892. }
  893. } /* end if CompatiblityMode handling */
  894. }
  895. }
  896. return
  897. (*extended_status == kConnectionManagerExtendedStatusCodeSuccess) ?
  898. kEipStatusOk : kEipStatusError;
  899. }
  900. EipUint8 ParseConnectionPath(
  901. CipConnectionObject *connection_object,
  902. CipMessageRouterRequest *message_router_request,
  903. EipUint16 *extended_error
  904. ) {
  905. const EipUint8 *message = message_router_request->data;
  906. const size_t connection_path_size = GetSintFromMessage(&message); /* length in words */
  907. size_t remaining_path = connection_path_size;
  908. CipClass *class = NULL;
  909. CipDword class_id = 0x0;
  910. CipDword instance_id = 0x0;
  911. /* with 256 we mark that we haven't got a PIT segment */
  912. ConnectionObjectSetProductionInhibitTime(connection_object, 256);
  913. if ( (g_kForwardOpenHeaderLength + remaining_path * 2)
  914. < message_router_request->request_path_size ) {
  915. /* the received packet is larger than the data in the path */
  916. *extended_error = 0;
  917. return kCipErrorTooMuchData;
  918. }
  919. if ( (g_kForwardOpenHeaderLength + remaining_path * 2)
  920. > message_router_request->request_path_size ) {
  921. /*there is not enough data in received packet */
  922. *extended_error = 0;
  923. OPENER_TRACE_INFO("Message not long enough for path\n");
  924. return kCipErrorNotEnoughData;
  925. }
  926. if (remaining_path > 0) {
  927. /* first look if there is an electronic key */
  928. if ( kSegmentTypeLogicalSegment == GetPathSegmentType(message) ) {
  929. if ( kLogicalSegmentLogicalTypeSpecial
  930. == GetPathLogicalSegmentLogicalType(message) ) {
  931. if ( kLogicalSegmentSpecialTypeLogicalFormatElectronicKey
  932. == GetPathLogicalSegmentSpecialTypeLogicalType(message) ) {
  933. if ( kElectronicKeySegmentFormatKeyFormat4
  934. == GetPathLogicalSegmentElectronicKeyFormat(message) ) {
  935. /* Check if there is enough data for holding the electronic key segment */
  936. if (remaining_path < 5) {
  937. *extended_error = 0;
  938. OPENER_TRACE_INFO("Message not long enough for electronic key\n");
  939. return kCipErrorNotEnoughData;
  940. }
  941. /* Electronic key format 4 found */
  942. ElectronicKeyFormat4 *electronic_key = ElectronicKeyFormat4New();
  943. GetElectronicKeyFormat4FromMessage(&message, electronic_key);
  944. /* logical electronic key found */
  945. connection_object->electronic_key.key_data = electronic_key;
  946. ElectronicKeyFormat4Delete(&electronic_key);
  947. remaining_path -= 5; /*length of the electronic key*/
  948. OPENER_TRACE_INFO(
  949. "key: ven ID %d, dev type %d, prod code %d, major %d, minor %d\n",
  950. ElectronicKeyFormat4GetVendorId(connection_object->electronic_key.key_data),
  951. ElectronicKeyFormat4GetDeviceType(connection_object->electronic_key.key_data),
  952. ElectronicKeyFormat4GetProductCode(connection_object->electronic_key.key_data),
  953. ElectronicKeyFormat4GetMajorRevision(connection_object->electronic_key.key_data),
  954. ElectronicKeyFormat4GetMinorRevision(connection_object->electronic_key.key_data));
  955. if ( kEipStatusOk
  956. != CheckElectronicKeyData(
  957. connection_object->electronic_key.key_format,
  958. &(connection_object->electronic_key.key_data),
  959. extended_error) ) {
  960. return kCipErrorConnectionFailure;
  961. }
  962. }
  963. } else {
  964. OPENER_TRACE_INFO("no key\n");
  965. }
  966. }
  967. }
  968. //TODO: Refactor this afterwards
  969. if ( kConnectionObjectTransportClassTriggerProductionTriggerCyclic
  970. != ConnectionObjectGetTransportClassTriggerProductionTrigger(connection_object) ) {
  971. /*non cyclic connections may have a production inhibit */
  972. if ( kSegmentTypeNetworkSegment == GetPathSegmentType(message) ) {
  973. NetworkSegmentSubtype network_segment_subtype =
  974. GetPathNetworkSegmentSubtype(message);
  975. if (kNetworkSegmentSubtypeProductionInhibitTimeInMilliseconds
  976. == network_segment_subtype) {
  977. OPENER_TRACE_INFO("PIT segment available - value: %u\n",message[1]);
  978. connection_object->production_inhibit_time = message[1];
  979. message += 2;
  980. remaining_path -= 1;
  981. }
  982. }
  983. }
  984. if (kSegmentTypeLogicalSegment == GetPathSegmentType(message) && kLogicalSegmentLogicalTypeClassId == GetPathLogicalSegmentLogicalType(message) ) {
  985. class_id = CipEpathGetLogicalValue(&message);
  986. class = GetCipClass(class_id);
  987. if (NULL == class) {
  988. OPENER_TRACE_ERR("classid %" PRIx32 " not found\n",
  989. class_id);
  990. if (class_id >= 0xC8) { /*reserved range of class ids */
  991. *extended_error =
  992. kConnectionManagerExtendedStatusCodeErrorInvalidSegmentTypeInPath;
  993. } else {
  994. *extended_error =
  995. kConnectionManagerExtendedStatusCodeInconsistentApplicationPathCombo;
  996. }
  997. return kCipErrorConnectionFailure;
  998. }
  999. OPENER_TRACE_INFO("classid %" PRIx32 " (%s)\n",
  1000. class_id,
  1001. class->class_name);
  1002. } else {
  1003. *extended_error =
  1004. kConnectionManagerExtendedStatusCodeErrorInvalidSegmentTypeInPath;
  1005. return kCipErrorConnectionFailure;
  1006. }
  1007. remaining_path -= 1; /* 1 16Bit word for the class part of the path */
  1008. /* Get instance ID */
  1009. if ( kSegmentTypeLogicalSegment == GetPathSegmentType(message) && kLogicalSegmentLogicalTypeInstanceId == GetPathLogicalSegmentLogicalType(message) ) { /* store the configuration ID for later checking in the application connection types */
  1010. instance_id = CipEpathGetLogicalValue(&message);
  1011. OPENER_TRACE_INFO("Configuration instance id %" PRId32 "\n",
  1012. instance_id);
  1013. if ( NULL == GetCipInstance(class, instance_id) ) {
  1014. /*according to the test tool we should respond with this extended error code */
  1015. *extended_error =
  1016. kConnectionManagerExtendedStatusCodeErrorInvalidSegmentTypeInPath;
  1017. return kCipErrorConnectionFailure;
  1018. }
  1019. /* 1 or 2 16Bit words for the configuration instance part of the path */
  1020. remaining_path -= (instance_id > 0xFF) ? 2 : 1; //TODO: 32 bit case missing
  1021. } else {
  1022. OPENER_TRACE_INFO("no config data\n");
  1023. }
  1024. if ( kConnectionObjectTransportClassTriggerTransportClass3 == ConnectionObjectGetTransportClassTriggerTransportClass(connection_object) ) {
  1025. /*we have Class 3 connection*/
  1026. if (remaining_path > 0) {
  1027. OPENER_TRACE_WARN(
  1028. "Too much data in connection path for class 3 connection\n");
  1029. *extended_error =
  1030. kConnectionManagerExtendedStatusCodeErrorInvalidSegmentTypeInPath;
  1031. return kCipErrorConnectionFailure;
  1032. }
  1033. /* connection end point has to be the message router instance 1 */
  1034. if ( (class_id != kCipMessageRouterClassCode)
  1035. || (1 != instance_id) ) {
  1036. *extended_error =
  1037. kConnectionManagerExtendedStatusCodeInconsistentApplicationPathCombo;
  1038. return kCipErrorConnectionFailure;
  1039. }
  1040. /* Configuration connection point is producing connection point */
  1041. CipConnectionPathEpath connection_epath = {
  1042. .class_id = class_id,
  1043. .instance_id = instance_id,
  1044. .attribute_id_or_connection_point = 0
  1045. };
  1046. memcpy(&(connection_object->configuration_path), &connection_epath, sizeof(connection_object->configuration_path));
  1047. memcpy(&(connection_object->produced_path), &connection_epath, sizeof(connection_object->produced_path));
  1048. /* End class 3 connection handling */
  1049. } else { /* we have an IO connection */
  1050. ConnectionObjectConnectionType originator_to_target_connection_type = ConnectionObjectGetOToTConnectionType(
  1051. connection_object);
  1052. ConnectionObjectConnectionType target_to_originator_connection_type = ConnectionObjectGetTToOConnectionType(
  1053. connection_object);
  1054. connection_object->consumed_connection_path_length = 0;
  1055. connection_object->consumed_connection_path = NULL;
  1056. //connection_object->connection_path.connection_point[1] = 0; /* set not available path to Invalid */
  1057. size_t number_of_encoded_paths = 0;
  1058. if (kConnectionObjectConnectionTypeNull == originator_to_target_connection_type) {
  1059. if (kConnectionObjectConnectionTypeNull == target_to_originator_connection_type) { /* configuration only connection */
  1060. number_of_encoded_paths = 0;
  1061. OPENER_TRACE_WARN("assembly: type invalid\n");
  1062. } else { /* 1 path -> path is for production */
  1063. OPENER_TRACE_INFO("assembly: type produce\n");
  1064. number_of_encoded_paths = 1;
  1065. }
  1066. } else {
  1067. if (kConnectionObjectConnectionTypeNull == target_to_originator_connection_type) { /* 1 path -> path is for consumption */
  1068. OPENER_TRACE_INFO("assembly: type consume\n");
  1069. number_of_encoded_paths = 1;
  1070. } else { /* 2 paths -> 1st for production 2nd for consumption */
  1071. OPENER_TRACE_INFO("assembly: type bidirectional\n");
  1072. number_of_encoded_paths = 2;
  1073. }
  1074. }
  1075. for (size_t i = 0; i < number_of_encoded_paths; i++) /* process up to 2 encoded paths */
  1076. {
  1077. if ( kLogicalSegmentLogicalTypeInstanceId == GetPathLogicalSegmentLogicalType(message) || kLogicalSegmentLogicalTypeConnectionPoint == GetPathLogicalSegmentLogicalType(message) ) /* Connection Point interpreted as InstanceNr -> only in Assembly Objects */
  1078. { /* Attribute Id or Connection Point */
  1079. CipDword attribute_id = CipEpathGetLogicalValue(&message);
  1080. CipConnectionPathEpath connection_epath = {
  1081. .class_id = class_id,
  1082. .instance_id = instance_id,
  1083. .attribute_id_or_connection_point = attribute_id
  1084. };
  1085. /* TODO: Remainder of old implementation, look for better way of doing this */
  1086. if(0 == i){
  1087. memcpy(&(connection_object->produced_path), &connection_epath, sizeof(connection_object->produced_path));
  1088. }
  1089. if(1 == i){
  1090. memcpy(&(connection_object->consumed_path), &connection_epath, sizeof(connection_object->consumed_path));
  1091. }
  1092. OPENER_TRACE_INFO(
  1093. "connection point %" PRIu32 "\n",
  1094. attribute_id);
  1095. if ( NULL
  1096. == GetCipInstance(
  1097. class,
  1098. attribute_id) ) { /* Old code - Probably here the attribute ID marks the instance for the assembly object */
  1099. *extended_error =
  1100. kConnectionManagerExtendedStatusCodeInconsistentApplicationPathCombo;
  1101. return kCipErrorConnectionFailure;
  1102. }
  1103. /* 1 or 2 16Bit word for the connection point part of the path */
  1104. remaining_path -= (attribute_id > 0xFF) ? 2 : 1;
  1105. } else {
  1106. *extended_error =
  1107. kConnectionManagerExtendedStatusCodeErrorInvalidSegmentTypeInPath;
  1108. return kCipErrorConnectionFailure;
  1109. }
  1110. }
  1111. g_config_data_length = 0;
  1112. g_config_data_buffer = NULL;
  1113. while (remaining_path > 0) { /* remaining_path_size something left in the path should be configuration data */
  1114. SegmentType segment_type = GetPathSegmentType(message);
  1115. switch (segment_type) {
  1116. case kSegmentTypeDataSegment: {
  1117. DataSegmentSubtype data_segment_type = GetPathDataSegmentSubtype(
  1118. message);
  1119. switch (data_segment_type) {
  1120. case kDataSegmentSubtypeSimpleData:
  1121. g_config_data_length = message[1] * 2; /*data segments store length 16-bit word wise */
  1122. g_config_data_buffer = (EipUint8 *) message + 2;
  1123. remaining_path -= (g_config_data_length + 2) / 2;
  1124. message += (g_config_data_length + 2);
  1125. break;
  1126. default:
  1127. OPENER_TRACE_ERR("Not allowed in connection manager");
  1128. break;
  1129. }
  1130. }
  1131. break;
  1132. case kSegmentTypeNetworkSegment: {
  1133. NetworkSegmentSubtype subtype = GetPathNetworkSegmentSubtype(
  1134. message);
  1135. switch (subtype) {
  1136. case kNetworkSegmentSubtypeProductionInhibitTimeInMilliseconds:
  1137. if ( kConnectionObjectTransportClassTriggerProductionTriggerCyclic
  1138. != ConnectionObjectGetTransportClassTriggerProductionTrigger(connection_object) ) {
  1139. /* only non cyclic connections may have a production inhibit */
  1140. connection_object->production_inhibit_time = message[1];
  1141. message += 2;
  1142. remaining_path -= 2;
  1143. } else {
  1144. *extended_error = connection_path_size - remaining_path; /*offset in 16Bit words where within the connection path the error happend*/
  1145. return kCipErrorPathSegmentError; /*status code for invalid segment type*/
  1146. }
  1147. default:
  1148. OPENER_TRACE_ERR("Not allowed in connection manager");
  1149. break;
  1150. }
  1151. }
  1152. break;
  1153. default:
  1154. OPENER_TRACE_WARN(
  1155. "No data segment identifier found for the configuration data\n");
  1156. *extended_error = connection_path_size - remaining_path; /*offset in 16Bit words where within the connection path the error happend*/
  1157. return kConnectionManagerGeneralStatusPathSegmentErrorInUnconnectedSend;
  1158. break;
  1159. }
  1160. }
  1161. }
  1162. }
  1163. OPENER_TRACE_INFO("Resulting PIT value: %u\n",
  1164. connection_object->production_inhibit_time);
  1165. /*save back the current position in the stream allowing followers to parse anything thats still there*/
  1166. message_router_request->data = message;
  1167. return kEipStatusOk;
  1168. }
  1169. void CloseConnection(CipConnectionObject *RESTRICT connection_object) {
  1170. ConnectionObjectSetState(connection_object, kConnectionObjectStateNonExistent);
  1171. if ( kConnectionObjectTransportClassTriggerTransportClass3 != ConnectionObjectGetTransportClassTriggerTransportClass(connection_object) ) {
  1172. /* only close the UDP connection for not class 3 connections */
  1173. CloseUdpSocket(
  1174. connection_object->socket[kUdpCommuncationDirectionConsuming]);
  1175. connection_object->socket[kUdpCommuncationDirectionConsuming] =
  1176. kEipInvalidSocket;
  1177. CloseUdpSocket(
  1178. connection_object->socket[kUdpCommuncationDirectionProducing]);
  1179. connection_object->socket[kUdpCommuncationDirectionProducing] =
  1180. kEipInvalidSocket;
  1181. }
  1182. RemoveFromActiveConnections(connection_object);
  1183. }
  1184. void AddNewActiveConnection(const CipConnectionObject *const connection_object) {
  1185. DoublyLinkedListInsertAtTail(&connection_list, connection_object);
  1186. }
  1187. void RemoveFromActiveConnections(CipConnectionObject *const connection_object) {
  1188. for(DoublyLinkedListNode *iterator = connection_list.first; iterator != connection_list.last; iterator = iterator->next) {
  1189. if(iterator->data == connection_object) {
  1190. ConnectionObjectInitializeEmpty(connection_object);
  1191. DoublyLinkedListRemoveNode(&connection_list, &iterator);
  1192. break;
  1193. }
  1194. }
  1195. // if (NULL != connection_object->first_connection_object) {
  1196. // connection_object->first_connection_object->next_connection_object =
  1197. // connection_object->next_connection_object;
  1198. // } else {
  1199. // g_active_connection_list = connection_object->next_connection_object;
  1200. // }
  1201. // if (NULL != connection_object->next_connection_object) {
  1202. // connection_object->next_connection_object->first_connection_object =
  1203. // connection_object->first_connection_object;
  1204. // }
  1205. //
  1206. // connection_object->first_connection_object = NULL;
  1207. // connection_object->next_connection_object = NULL;
  1208. // connection_object->state = kConnectionStateNonExistent;
  1209. }
  1210. EipBool8 IsConnectedOutputAssembly(const EipUint32 instance_number) {
  1211. EipBool8 is_connected = false;
  1212. DoublyLinkedListNode *node = connection_list.first;
  1213. while (NULL != node) {
  1214. CipDword produced_connection_point = ((CipConnectionObject*)node->data)->produced_path.attribute_id_or_connection_point;
  1215. if (instance_number == produced_connection_point) {
  1216. is_connected = true;
  1217. break;
  1218. }
  1219. node = node->next;
  1220. }
  1221. return is_connected;
  1222. }
  1223. EipStatus AddConnectableObject(
  1224. const EipUint32 class_id,
  1225. OpenConnectionFunction open_connection_function
  1226. ) {
  1227. EipStatus status = kEipStatusError;
  1228. /*parsing is now finished all data is available and check now establish the connection */
  1229. for (size_t i = 0; i < g_kNumberOfConnectableObjects; ++i) {
  1230. if ( (0 == g_connection_management_list[i].class_id)
  1231. || (class_id == g_connection_management_list[i].class_id) ) {
  1232. g_connection_management_list[i].class_id = class_id;
  1233. g_connection_management_list[i].open_connection_function =
  1234. open_connection_function;
  1235. status = kEipStatusOk;
  1236. break;
  1237. }
  1238. }
  1239. return status;
  1240. }
  1241. ConnectionManagementHandling *
  1242. GetConnectionManagementEntry(const EipUint32 class_id) {
  1243. ConnectionManagementHandling *connection_management_entry = NULL;
  1244. for (int i = 0; i < g_kNumberOfConnectableObjects; ++i) {
  1245. if (class_id == g_connection_management_list[i].class_id) {
  1246. connection_management_entry = &(g_connection_management_list[i]);
  1247. break;
  1248. }
  1249. }
  1250. return connection_management_entry;
  1251. }
  1252. EipStatus TriggerConnections(
  1253. unsigned int output_assembly,
  1254. unsigned int input_assembly
  1255. ) {
  1256. EipStatus status = kEipStatusError;
  1257. DoublyLinkedListNode *node = connection_list.first;
  1258. while (NULL != node) {
  1259. CipConnectionObject *connection_object = node->data;
  1260. if ( (output_assembly == connection_object->produced_path.attribute_id_or_connection_point)
  1261. && (input_assembly ==
  1262. connection_object->consumed_path.attribute_id_or_connection_point) ) {
  1263. if ( kConnectionObjectTransportClassTriggerProductionTriggerApplicationObject
  1264. == ConnectionObjectGetTransportClassTriggerProductionTrigger(connection_object) ) {
  1265. /* produce at the next allowed occurrence */
  1266. connection_object->transmission_trigger_timer = connection_object
  1267. ->production_inhibit_time;
  1268. status = kEipStatusOk;
  1269. }
  1270. break;
  1271. }
  1272. }
  1273. return status;
  1274. }
  1275. void InitializeConnectionManagerData() {
  1276. memset( g_connection_management_list, 0,
  1277. g_kNumberOfConnectableObjects *
  1278. sizeof(ConnectionManagementHandling) );
  1279. InitializeClass3ConnectionData();
  1280. InitializeIoConnectionData();
  1281. }