encap.c 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. /*******************************************************************************
  2. * Copyright (c) 2009, Rockwell Automation, Inc.
  3. * All rights reserved.
  4. *
  5. ******************************************************************************/
  6. #include <string.h>
  7. #include <stdlib.h>
  8. #include <stdbool.h>
  9. #include "encap.h"
  10. #include "opener_api.h"
  11. #include "opener_user_conf.h"
  12. #include "cpf.h"
  13. #include "endianconv.h"
  14. #include "cipcommon.h"
  15. #include "cipmessagerouter.h"
  16. #include "cipconnectionmanager.h"
  17. #include "cipidentity.h"
  18. #include "ciptcpipinterface.h"
  19. #include "generic_networkhandler.h"
  20. #include "trace.h"
  21. #include "socket_timer.h"
  22. #include "opener_error.h"
  23. /* IP address data taken from TCPIPInterfaceObject*/
  24. const EipUint16 kSupportedProtocolVersion = 1; /**< Supported Encapsulation protocol version */
  25. const CipUdint kEncapsulationHeaderOptionsFlag = 0x00; /**< Mask of which options are supported as of the current CIP specs no other option value as 0 should be supported.*/
  26. const int kEncapsulationHeaderSessionHandlePosition = 4; /**< the position of the session handle within the encapsulation header*/
  27. const EipUint16 kListIdentityDefaultDelayTime = 2000; /**< Default delay time for List Identity response */
  28. const EipUint16 kListIdentityMinimumDelayTime = 500; /**< Minimum delay time for List Identity response */
  29. typedef enum {
  30. kSessionStatusInvalid = -1,
  31. kSessionStatusValid = 0
  32. } SessionStatus;
  33. const int kSenderContextSize = 8; /**< size of sender context in encapsulation header*/
  34. /** @brief definition of known encapsulation commands */
  35. typedef enum {
  36. kEncapsulationCommandNoOperation = 0x0000, /**< only allowed for TCP */
  37. kEncapsulationCommandListServices = 0x0004, /**< allowed for both UDP and TCP */
  38. kEncapsulationCommandListIdentity = 0x0063, /**< allowed for both UDP and TCP */
  39. kEncapsulationCommandListInterfaces = 0x0064, /**< optional, allowed for both UDP and TCP */
  40. kEncapsulationCommandRegisterSession = 0x0065, /**< only allowed for TCP */
  41. kEncapsulationCommandUnregisterSession = 0x0066, /**< only allowed for TCP */
  42. kEncapsulationCommandSendRequestReplyData = 0x006F, /**< only allowed for TCP */
  43. kEncapsulationCommandSendUnitData = 0x0070 /**< only allowed for TCP */
  44. } EncapsulationCommand;
  45. /** @brief definition of capability flags */
  46. typedef enum {
  47. kCapabilityFlagsCipTcp = 0x0020,
  48. kCapabilityFlagsCipUdpClass0or1 = 0x0100
  49. } CapabilityFlags;
  50. #define ENCAP_NUMBER_OF_SUPPORTED_DELAYED_ENCAP_MESSAGES 2 /**< According to EIP spec at least 2 delayed message requests should be supported */
  51. /* Encapsulation layer data */
  52. /** @brief Delayed Encapsulation Message structure */
  53. typedef struct {
  54. EipInt32 time_out; /**< time out in milli seconds */
  55. int socket; /**< associated socket */
  56. struct sockaddr_in receiver;
  57. ENIPMessage outgoing_message;
  58. } DelayedEncapsulationMessage;
  59. EncapsulationServiceInformation g_service_information;
  60. int g_registered_sessions[OPENER_NUMBER_OF_SUPPORTED_SESSIONS];
  61. DelayedEncapsulationMessage g_delayed_encapsulation_messages[ENCAP_NUMBER_OF_SUPPORTED_DELAYED_ENCAP_MESSAGES];
  62. /*** private functions ***/
  63. void HandleReceivedListIdentityCommandTcp(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message);
  64. void HandleReceivedListIdentityCommandUdp(const int socket,
  65. const struct sockaddr_in *const from_address,
  66. const EncapsulationData *const receive_data);
  67. EipStatus HandleReceivedUnregisterSessionCommand(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message);
  68. EipStatus HandleReceivedSendUnitDataCommand(const EncapsulationData *const receive_data, const struct sockaddr *const originator_address,
  69. ENIPMessage *const outgoing_message);
  70. EipStatus HandleReceivedInvalidCommand(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message);
  71. int GetFreeSessionIndex(void);
  72. SessionStatus CheckRegisteredSessions(const EncapsulationData *const receive_data);
  73. void DetermineDelayTime(const EipByte *buffer_start, DelayedEncapsulationMessage *const delayed_message_buffer);
  74. /* @brief Initializes session list and interface information. */
  75. void EncapsulationInit(void) {
  76. DetermineEndianess();
  77. /*initialize random numbers for random delayed response message generation
  78. * we use the ip address as seed as suggested in the spec */
  79. srand(g_tcpip.interface_configuration.ip_address);
  80. /* initialize Sessions to invalid == free session */
  81. for(size_t i = 0; i < OPENER_NUMBER_OF_SUPPORTED_SESSIONS; i++) {
  82. g_registered_sessions[i] = kEipInvalidSocket;
  83. }
  84. for(size_t i = 0; i < ENCAP_NUMBER_OF_SUPPORTED_DELAYED_ENCAP_MESSAGES; i++) {
  85. g_delayed_encapsulation_messages[i].socket = kEipInvalidSocket;
  86. }
  87. /*TODO make the service information configurable*/
  88. /* initialize service information */
  89. g_service_information.type_code = kCipItemIdListServiceResponse;
  90. g_service_information.length = sizeof(g_service_information);
  91. g_service_information.encapsulation_protocol_version = 1;
  92. g_service_information.capability_flags = kCapabilityFlagsCipTcp | kCapabilityFlagsCipUdpClass0or1;
  93. snprintf((char*) g_service_information.name_of_service, sizeof(g_service_information.name_of_service), "Communications");
  94. }
  95. EipStatus HandleReceivedExplictTcpData(int socket, EipUint8 *buffer, size_t length, int *number_of_remaining_bytes, struct sockaddr *originator_address,
  96. ENIPMessage *const outgoing_message) {
  97. OPENER_TRACE_INFO("Handles data for TCP socket: %d\n", socket);
  98. EipStatus return_value = kEipStatusOk;
  99. EncapsulationData encapsulation_data = { 0 };
  100. /* eat the encapsulation header*/
  101. /* the structure contains a pointer to the encapsulated data*/
  102. /* returns how many bytes are left after the encapsulated data*/
  103. const int remaining_bytes = CreateEncapsulationStructure(buffer, length, &encapsulation_data);
  104. if(remaining_bytes >= 0) {
  105. *number_of_remaining_bytes = remaining_bytes;
  106. } else {
  107. OPENER_TRACE_ERR("Fragmented packet detected! Fragmented packets are not supported!\n");
  108. *number_of_remaining_bytes = 0;
  109. return kEipStatusError;
  110. }
  111. if(kEncapsulationHeaderOptionsFlag == encapsulation_data.options) /*TODO generate appropriate error response*/
  112. {
  113. if(*number_of_remaining_bytes >= 0) /* check if the message is corrupt: header size + claimed payload size > than what we actually received*/
  114. {
  115. /* full package or more received */
  116. encapsulation_data.status = kEncapsulationProtocolSuccess;
  117. return_value = kEipStatusOkSend;
  118. /* most of these functions need a reply to be send */
  119. switch(encapsulation_data.command_code){
  120. case (kEncapsulationCommandNoOperation):
  121. OPENER_TRACE_INFO("NOP\n");
  122. /* NOP needs no reply and does nothing */
  123. return_value = kEipStatusOk;
  124. break;
  125. case (kEncapsulationCommandListServices):
  126. OPENER_TRACE_INFO("List services\n");
  127. HandleReceivedListServicesCommand(&encapsulation_data, outgoing_message);
  128. break;
  129. case (kEncapsulationCommandListIdentity):
  130. OPENER_TRACE_INFO("List identity\n");
  131. HandleReceivedListIdentityCommandTcp(&encapsulation_data, outgoing_message);
  132. break;
  133. case (kEncapsulationCommandListInterfaces):
  134. OPENER_TRACE_INFO("List interfaces\n");
  135. HandleReceivedListInterfacesCommand(&encapsulation_data, outgoing_message);
  136. break;
  137. case (kEncapsulationCommandRegisterSession):
  138. OPENER_TRACE_INFO("Register session\n");
  139. HandleReceivedRegisterSessionCommand(socket, &encapsulation_data, outgoing_message);
  140. break;
  141. case (kEncapsulationCommandUnregisterSession):
  142. OPENER_TRACE_INFO("unregister session\n");
  143. return_value = HandleReceivedUnregisterSessionCommand(&encapsulation_data, outgoing_message);
  144. break;
  145. case (kEncapsulationCommandSendRequestReplyData):
  146. OPENER_TRACE_INFO("Send Request/Reply Data\n");
  147. return_value = HandleReceivedSendRequestResponseDataCommand(&encapsulation_data, originator_address, outgoing_message);
  148. break;
  149. case (kEncapsulationCommandSendUnitData):
  150. OPENER_TRACE_INFO("Send Unit Data\n");
  151. return_value = HandleReceivedSendUnitDataCommand(&encapsulation_data, originator_address, outgoing_message);
  152. break;
  153. default:
  154. return_value = HandleReceivedInvalidCommand(&encapsulation_data, outgoing_message);
  155. break;
  156. }
  157. }
  158. }
  159. return return_value;
  160. }
  161. EipStatus HandleReceivedExplictUdpData(const int socket, const struct sockaddr_in *from_address, const EipUint8 *buffer, const size_t buffer_length,
  162. int *number_of_remaining_bytes,
  163. bool unicast, ENIPMessage *const outgoing_message) {
  164. EipStatus return_value = kEipStatusOk;
  165. EncapsulationData encapsulation_data = { 0 };
  166. /* eat the encapsulation header*/
  167. /* the structure contains a pointer to the encapsulated data*/
  168. /* returns how many bytes are left after the encapsulated data*/
  169. const int remaining_bytes = CreateEncapsulationStructure(buffer, buffer_length, &encapsulation_data);
  170. if(remaining_bytes >= 0) {
  171. *number_of_remaining_bytes = remaining_bytes;
  172. } else {
  173. OPENER_TRACE_ERR("Fragmented packet detected! Fragmented packets are not supported!\n");
  174. *number_of_remaining_bytes = 0;
  175. return kEipStatusError;
  176. }
  177. if(kEncapsulationHeaderOptionsFlag == encapsulation_data.options) /*TODO generate appropriate error response*/
  178. {
  179. if(*number_of_remaining_bytes >= 0) /* check if the message is corrupt: header size + claimed payload size > than what we actually received*/
  180. {
  181. /* full package or more received */
  182. encapsulation_data.status = kEncapsulationProtocolSuccess;
  183. return_value = kEipStatusOkSend;
  184. /* most of these functions need a reply to be send */
  185. switch(encapsulation_data.command_code){
  186. case (kEncapsulationCommandListServices):
  187. OPENER_TRACE_INFO("List Service\n");
  188. HandleReceivedListServicesCommand(&encapsulation_data, outgoing_message);
  189. break;
  190. case (kEncapsulationCommandListIdentity):
  191. OPENER_TRACE_INFO("List Identity\n");
  192. if(unicast == true) {
  193. HandleReceivedListIdentityCommandTcp(&encapsulation_data, outgoing_message);
  194. } else {
  195. HandleReceivedListIdentityCommandUdp(socket,
  196. from_address,
  197. &encapsulation_data);
  198. /* as the response has to be delayed do not send it now */
  199. return_value = kEipStatusOk;
  200. }
  201. break;
  202. case (kEncapsulationCommandListInterfaces):
  203. OPENER_TRACE_INFO("List Interfaces\n");
  204. HandleReceivedListInterfacesCommand(&encapsulation_data, outgoing_message);
  205. break;
  206. /* The following commands are not to be sent via UDP */
  207. case (kEncapsulationCommandNoOperation):
  208. case (kEncapsulationCommandRegisterSession):
  209. case (kEncapsulationCommandUnregisterSession):
  210. case (kEncapsulationCommandSendRequestReplyData):
  211. case (kEncapsulationCommandSendUnitData):
  212. default:
  213. OPENER_TRACE_INFO("No command\n");
  214. //TODO: Check this
  215. encapsulation_data.status = kEncapsulationProtocolInvalidCommand;
  216. encapsulation_data.data_length = 0;
  217. break;
  218. }
  219. }
  220. }
  221. return return_value;
  222. }
  223. void SkipEncapsulationHeader(ENIPMessage *const outgoing_message) {
  224. /* Move pointer over Header, but do not add to size */
  225. outgoing_message->current_message_position += ENCAPSULATION_HEADER_LENGTH;
  226. }
  227. void GenerateEncapsulationHeader(const EncapsulationData *const receive_data, const size_t command_specific_data_length, const CipSessionHandle session_handle,
  228. const EncapsulationProtocolErrorCode encapsulation_protocol_status, ENIPMessage *const outgoing_message) {
  229. AddIntToMessage(receive_data->command_code, outgoing_message);
  230. AddIntToMessage(command_specific_data_length, outgoing_message);
  231. AddDintToMessage(session_handle, outgoing_message); //Session handle
  232. AddDintToMessage(encapsulation_protocol_status, outgoing_message); //Status
  233. memcpy(outgoing_message->current_message_position, receive_data->sender_context, kSenderContextSize); // sender context
  234. outgoing_message->current_message_position += kSenderContextSize;
  235. outgoing_message->used_message_length += kSenderContextSize;
  236. AddDintToMessage(0, outgoing_message); // options
  237. }
  238. /** @brief generate reply with "Communications Services" + compatibility Flags.
  239. * @param receive_data pointer to structure with received data
  240. * @param outgoing_message The outgoing ENIP message
  241. */
  242. void HandleReceivedListServicesCommand(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message) {
  243. /* Create encapsulation header */
  244. const size_t kListServicesCommandSpecificDataLength = sizeof(CipUint) + sizeof(g_service_information);
  245. GenerateEncapsulationHeader(receive_data, kListServicesCommandSpecificDataLength, 0,
  246. /* Session handle will be ignored */
  247. kEncapsulationProtocolSuccess,
  248. /* Protocol status */
  249. outgoing_message);
  250. /* Command specific data copy Interface data to msg for sending */
  251. AddIntToMessage(1, outgoing_message); // Item count
  252. AddIntToMessage(g_service_information.type_code, outgoing_message);
  253. AddIntToMessage((EipUint16) (g_service_information.length - 4), outgoing_message);
  254. AddIntToMessage(g_service_information.encapsulation_protocol_version, outgoing_message);
  255. AddIntToMessage(g_service_information.capability_flags, outgoing_message);
  256. memcpy(outgoing_message->current_message_position, g_service_information.name_of_service, sizeof(g_service_information.name_of_service));
  257. outgoing_message->current_message_position += sizeof(g_service_information.name_of_service);
  258. outgoing_message->used_message_length += sizeof(g_service_information.name_of_service);
  259. }
  260. void HandleReceivedListInterfacesCommand(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message) {
  261. /* Encapsulation header */
  262. const size_t kListInterfacesCommandSpecificDataLength = sizeof(CipUint);
  263. GenerateEncapsulationHeader(receive_data, kListInterfacesCommandSpecificDataLength, 0,
  264. /* Session handle will be ignored */
  265. kEncapsulationProtocolSuccess, outgoing_message);
  266. /* Command specific data */
  267. AddIntToMessage(0x0000, outgoing_message); /* Set Item Count to 0: no Target Items follow. */
  268. }
  269. void HandleReceivedListIdentityCommandTcp(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message) {
  270. EncapsulateListIdentityResponseMessage(receive_data, outgoing_message);
  271. }
  272. void HandleReceivedListIdentityCommandUdp(const int socket,
  273. const struct sockaddr_in *const from_address,
  274. const EncapsulationData *const receive_data)
  275. {
  276. DelayedEncapsulationMessage *delayed_message_buffer = NULL;
  277. ENIPMessage *p_outgoing_message = NULL;
  278. for(size_t i = 0; i < ENCAP_NUMBER_OF_SUPPORTED_DELAYED_ENCAP_MESSAGES; i++) {
  279. if(kEipInvalidSocket == g_delayed_encapsulation_messages[i].socket) {
  280. delayed_message_buffer = &(g_delayed_encapsulation_messages[i]);
  281. p_outgoing_message = &(delayed_message_buffer->outgoing_message);
  282. InitializeENIPMessage(p_outgoing_message);
  283. break;
  284. }
  285. }
  286. if(NULL != delayed_message_buffer) {
  287. delayed_message_buffer->socket = socket;
  288. memcpy((&delayed_message_buffer->receiver), from_address, sizeof(struct sockaddr_in));
  289. DetermineDelayTime(receive_data->communication_buffer_start, delayed_message_buffer);
  290. EncapsulateListIdentityResponseMessage(receive_data, p_outgoing_message);
  291. }
  292. }
  293. CipUint ListIdentityGetCipIdentityItemLength(void) {
  294. return sizeof(CipUint) + sizeof(CipInt) + sizeof(CipUint) + sizeof(CipUdint) + 8 * sizeof(CipUsint) + sizeof(CipUint) + sizeof(CipUint) + sizeof(CipUint)
  295. + 2 * sizeof(CipUsint) + sizeof(CipWord) + sizeof(CipUdint) + sizeof(CipUsint) + g_identity.product_name.length + sizeof(CipUsint);
  296. }
  297. void EncodeListIdentityCipIdentityItem(ENIPMessage *const outgoing_message) {
  298. /* Item ID*/
  299. const CipUint kItemIDCipIdentity = 0x0C;
  300. AddIntToMessage(kItemIDCipIdentity, outgoing_message);
  301. AddIntToMessage(ListIdentityGetCipIdentityItemLength(), outgoing_message);
  302. AddIntToMessage(kSupportedProtocolVersion, outgoing_message);
  303. EncapsulateIpAddress(htons(kOpenerEthernetPort), g_tcpip.interface_configuration.ip_address, outgoing_message);
  304. /** Array of USINT - length 8 shall be set to zero */
  305. FillNextNMessageOctetsWithValueAndMoveToNextPosition(0, 8, outgoing_message);
  306. AddIntToMessage(g_identity.vendor_id, outgoing_message);
  307. AddIntToMessage(g_identity.device_type, outgoing_message);
  308. AddIntToMessage(g_identity.product_code, outgoing_message);
  309. AddSintToMessage(g_identity.revision.major_revision, outgoing_message);
  310. AddSintToMessage(g_identity.revision.minor_revision, outgoing_message);
  311. AddIntToMessage(g_identity.status, outgoing_message);
  312. AddDintToMessage(g_identity.serial_number, outgoing_message);
  313. AddSintToMessage((unsigned char) g_identity.product_name.length, outgoing_message);
  314. //TODO Change to EncodeCipString
  315. memcpy(outgoing_message->current_message_position, g_identity.product_name.string, g_identity.product_name.length);
  316. outgoing_message->current_message_position += g_identity.product_name.length;
  317. outgoing_message->used_message_length += g_identity.product_name.length;
  318. AddSintToMessage(g_identity.state, outgoing_message);
  319. }
  320. void EncapsulateListIdentityResponseMessage(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message) {
  321. const CipUint kEncapsulationCommandListIdentityLength = ListIdentityGetCipIdentityItemLength() + sizeof(CipUint) + sizeof(CipUint) + sizeof(CipUint); /* Last element is item count */
  322. GenerateEncapsulationHeader(receive_data, kEncapsulationCommandListIdentityLength, 0,
  323. /* Session handle will be ignored by receiver */
  324. kEncapsulationProtocolSuccess, outgoing_message);
  325. AddIntToMessage(1, outgoing_message); /* Item count: one item */
  326. EncodeListIdentityCipIdentityItem(outgoing_message);
  327. }
  328. void DetermineDelayTime(const EipByte *buffer_start, DelayedEncapsulationMessage *const delayed_message_buffer) {
  329. buffer_start += 12; /* start of the sender context */
  330. EipUint16 maximum_delay_time = GetUintFromMessage((const EipUint8** const ) &buffer_start);
  331. if(0 == maximum_delay_time) {
  332. maximum_delay_time = kListIdentityDefaultDelayTime;
  333. } else if(kListIdentityMinimumDelayTime > maximum_delay_time) { /* if maximum_delay_time is between 1 and 500ms set it to 500ms */
  334. maximum_delay_time = kListIdentityMinimumDelayTime;
  335. }
  336. delayed_message_buffer->time_out = rand() % maximum_delay_time;
  337. }
  338. void EncapsulateRegisterSessionCommandResponseMessage(const EncapsulationData *const receive_data, const CipSessionHandle session_handle,
  339. const EncapsulationProtocolErrorCode encapsulation_protocol_status, ENIPMessage *const outgoing_message) {
  340. /* Encapsulation header */
  341. const size_t kListInterfacesCommandSpecificDataLength = sizeof(CipUint) + sizeof(CipUint);
  342. assert(kListInterfacesCommandSpecificDataLength == 4);
  343. GenerateEncapsulationHeader(receive_data, kListInterfacesCommandSpecificDataLength, session_handle, encapsulation_protocol_status, outgoing_message);
  344. AddIntToMessage(1, outgoing_message); /* protocol version*/
  345. AddIntToMessage(0, outgoing_message); /* Options flag, shall be set to zero */
  346. }
  347. /* @brief Check supported protocol, generate session handle, send replay back to originator.
  348. * @param socket Socket this request is associated to. Needed for double register check
  349. * @param receive_data Pointer to received data with request/response.
  350. */
  351. void HandleReceivedRegisterSessionCommand(int socket, const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message) {
  352. int session_index = 0;
  353. CipSessionHandle session_handle = 0;
  354. EncapsulationProtocolErrorCode encapsulation_protocol_status = kEncapsulationProtocolSuccess;
  355. EipUint16 protocol_version = GetUintFromMessage((const EipUint8** const ) &receive_data->current_communication_buffer_position);
  356. EipUint16 option_flag = GetUintFromMessage((const EipUint8** const ) &receive_data->current_communication_buffer_position);
  357. /* check if requested protocol version is supported and the register session option flag is zero*/
  358. if((0 < protocol_version) && (protocol_version <= kSupportedProtocolVersion) && (0 == option_flag)) { /*Option field should be zero*/
  359. /* check if the socket has already a session open */
  360. for(unsigned int i = 0; i < OPENER_NUMBER_OF_SUPPORTED_SESSIONS; ++i) {
  361. if(g_registered_sessions[i] == socket) {
  362. /* the socket has already registered a session this is not allowed*/
  363. OPENER_TRACE_INFO(
  364. "Error: A session is already registered at socket %d\n",
  365. socket);
  366. session_handle = i + 1; /*return the already assigned session back, the cip spec is not clear about this needs to be tested*/
  367. encapsulation_protocol_status = kEncapsulationProtocolInvalidCommand;
  368. session_index = kSessionStatusInvalid;
  369. break;
  370. }
  371. }
  372. if(kSessionStatusInvalid != session_index) {
  373. session_index = GetFreeSessionIndex();
  374. if(kSessionStatusInvalid == session_index) /* no more sessions available */
  375. {
  376. encapsulation_protocol_status = kEncapsulationProtocolInsufficientMemory;
  377. } else { /* successful session registered */
  378. SocketTimer *socket_timer = SocketTimerArrayGetEmptySocketTimer(g_timestamps,
  379. OPENER_NUMBER_OF_SUPPORTED_SESSIONS);
  380. SocketTimerSetSocket(socket_timer, socket);
  381. SocketTimerSetLastUpdate(socket_timer, g_actual_time);
  382. g_registered_sessions[session_index] = socket; /* store associated socket */
  383. session_handle = (CipSessionHandle)(session_index + 1);
  384. encapsulation_protocol_status = kEncapsulationProtocolSuccess;
  385. }
  386. }
  387. } else { /* protocol not supported */
  388. encapsulation_protocol_status = kEncapsulationProtocolUnsupportedProtocol;
  389. }
  390. EncapsulateRegisterSessionCommandResponseMessage(receive_data, session_handle, encapsulation_protocol_status, outgoing_message);
  391. }
  392. /** @brief Unregister encapsulation session
  393. * @param receive_data Pointer to structure with data and header information.
  394. * @param outgoing_message The outgoing ENIP message
  395. * @return kEipStatusOkSend: a response needs to be sent, others: EIP stack status
  396. *
  397. * Close all corresponding TCP connections and delete session handle.
  398. */
  399. EipStatus HandleReceivedUnregisterSessionCommand(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message) {
  400. OPENER_TRACE_INFO("encap.c: Unregister Session Command\n");
  401. if((0 < receive_data->session_handle) && (receive_data->session_handle <=
  402. OPENER_NUMBER_OF_SUPPORTED_SESSIONS)) {
  403. CipSessionHandle i = receive_data->session_handle - 1;
  404. if(kEipInvalidSocket != g_registered_sessions[i]) {
  405. CloseTcpSocket(g_registered_sessions[i]);
  406. g_registered_sessions[i] = kEipInvalidSocket;
  407. CloseClass3ConnectionBasedOnSession(i + 1);
  408. return kEipStatusOk;
  409. }
  410. }
  411. /* no such session registered */
  412. GenerateEncapsulationHeader(receive_data, 0, receive_data->session_handle, kEncapsulationProtocolInvalidSessionHandle, outgoing_message);
  413. return kEipStatusOkSend;
  414. }
  415. /** @brief Call Connection Manager.
  416. * @param receive_data Pointer to structure with data and header information.
  417. * @param originator_address Address of the originator as received from socket
  418. * @param outgoing_message The outgoing ENIP message
  419. */
  420. EipStatus HandleReceivedSendUnitDataCommand(const EncapsulationData *const receive_data, const struct sockaddr *const originator_address,
  421. ENIPMessage *const outgoing_message) {
  422. EipStatus return_value = kEipStatusOkSend;
  423. /*EipStatus*/return_value = kEipStatusOk; /* TODO: Shouldn't this be kEipStatusOk cause we must not send any response if data_length < 6? */
  424. if(receive_data->data_length >= 6) {
  425. /* Command specific data UDINT .. Interface Handle, UINT .. Timeout, CPF packets */
  426. /* don't use the data yet */
  427. GetDintFromMessage((const EipUint8** const ) &receive_data->current_communication_buffer_position); /* skip over null interface handle*/
  428. GetIntFromMessage((const EipUint8** const ) &receive_data->current_communication_buffer_position); /* skip over unused timeout value*/
  429. ((EncapsulationData* const ) receive_data)->data_length -= 6; /* the rest is in CPF format*/
  430. if(kSessionStatusValid == CheckRegisteredSessions(receive_data)) /* see if the EIP session is registered*/
  431. {
  432. return_value = NotifyConnectedCommonPacketFormat(receive_data, originator_address, outgoing_message);
  433. } else { /* received a package with non registered session handle */
  434. InitializeENIPMessage(outgoing_message);
  435. GenerateEncapsulationHeader(receive_data, 0, receive_data->session_handle, kEncapsulationProtocolInvalidSessionHandle, outgoing_message);
  436. return_value = kEipStatusOkSend; /* TODO: Needs to be here if line with first TODO of this function is adjusted. */
  437. }
  438. }
  439. return return_value;
  440. }
  441. /** @brief Call UCMM or Message Router if UCMM not implemented.
  442. * @param receive_data Pointer to structure with data and header information.
  443. * @param originator_address Address of the originator as received from socket
  444. * @param outgoing_message The outgoing ENIP message
  445. * @return status kEipStatusOk .. success.
  446. * kEipStatusOkSend .. success & need to send response
  447. * kEipStatusError .. error
  448. */
  449. EipStatus HandleReceivedSendRequestResponseDataCommand(const EncapsulationData *const receive_data, const struct sockaddr *const originator_address,
  450. ENIPMessage *const outgoing_message) {
  451. EipStatus return_value = kEipStatusOkSend;
  452. /* EipStatus*/return_value = kEipStatusOk; /* TODO: Shouldn't this be kEipStatusOk cause we must not send any response if data_length < 6? */
  453. if(receive_data->data_length >= 6) {
  454. /* Command specific data UDINT .. Interface Handle, UINT .. Timeout, CPF packets */
  455. /* don't use the data yet */
  456. GetDintFromMessage((const EipUint8** const ) &receive_data->current_communication_buffer_position); /* skip over null interface handle*/
  457. GetIntFromMessage((const EipUint8** const ) &receive_data->current_communication_buffer_position); /* skip over unused timeout value*/
  458. ((EncapsulationData* const ) receive_data)->data_length -= 6; /* the rest is in CPF format*/
  459. if(kSessionStatusValid == CheckRegisteredSessions(receive_data)) /* see if the EIP session is registered*/
  460. {
  461. return_value = NotifyCommonPacketFormat(receive_data, originator_address, outgoing_message);
  462. } else { /* received a package with non registered session handle */
  463. InitializeENIPMessage(outgoing_message);
  464. GenerateEncapsulationHeader(receive_data, 0, receive_data->session_handle, kEncapsulationProtocolInvalidSessionHandle, outgoing_message);
  465. return_value = kEipStatusOkSend; /* TODO: Needs to be here if line with first TODO of this function is adjusted. */
  466. }
  467. }
  468. return return_value;
  469. }
  470. EipStatus HandleReceivedInvalidCommand(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message) {
  471. /* Encapsulation header */
  472. GenerateEncapsulationHeader(receive_data, 0, receive_data->session_handle, kEncapsulationProtocolInvalidCommand, outgoing_message);
  473. return kEipStatusOkSend;
  474. }
  475. /** @brief search for available sessions an return index.
  476. * @return return index of free session in anRegisteredSessions.
  477. * kInvalidSession .. no free session available
  478. */
  479. int GetFreeSessionIndex(void) {
  480. for(int session_index = 0; session_index < OPENER_NUMBER_OF_SUPPORTED_SESSIONS; session_index++) {
  481. if(kEipInvalidSocket == g_registered_sessions[session_index]) {
  482. return session_index;
  483. }
  484. }
  485. return kSessionStatusInvalid;
  486. }
  487. /** @brief copy data from pa_buf in little endian to host in structure.
  488. * @param receive_buffer Received message
  489. * @param receive_buffer_length Length of the data in receive_buffer. Might be more than one message
  490. * @param encapsulation_data structure to which data shall be copied
  491. * @return return difference between bytes in pa_buf an data_length
  492. * 0 .. full package received
  493. * >0 .. more than one packet received
  494. * <0 .. only fragment of data portion received
  495. */
  496. int_fast32_t CreateEncapsulationStructure(const EipUint8 *receive_buffer,
  497. size_t receive_buffer_length,
  498. EncapsulationData *const encapsulation_data)
  499. {
  500. encapsulation_data->communication_buffer_start = (EipUint8*) receive_buffer;
  501. encapsulation_data->command_code = GetUintFromMessage(&receive_buffer);
  502. encapsulation_data->data_length = GetUintFromMessage(&receive_buffer);
  503. encapsulation_data->session_handle = GetUdintFromMessage(&receive_buffer);
  504. encapsulation_data->status = GetUdintFromMessage(&receive_buffer);
  505. memcpy(encapsulation_data->sender_context, receive_buffer, kSenderContextSize);
  506. receive_buffer += kSenderContextSize;
  507. encapsulation_data->options = GetUdintFromMessage(&receive_buffer);
  508. encapsulation_data->current_communication_buffer_position = (EipUint8*) receive_buffer;
  509. /* Ensure buffer length fits in an int32 before casting in the return expression. */
  510. OPENER_ASSERT(INT32_MAX >= receive_buffer_length);
  511. return ( (int32_t)receive_buffer_length - ENCAPSULATION_HEADER_LENGTH -
  512. encapsulation_data->data_length );
  513. }
  514. /** @brief Check if received package belongs to registered session.
  515. * @param receive_data Received data.
  516. * @return 0 .. Session registered
  517. * kInvalidSession .. invalid session -> return unsupported command received
  518. */
  519. SessionStatus CheckRegisteredSessions(const EncapsulationData *const receive_data) {
  520. /* Skip the check when fuzzing
  521. in order to increase our code coverage
  522. we are simply bypassing all the session checks
  523. */
  524. #ifdef FUZZING_AFL
  525. return kSessionStatusValid;
  526. #endif
  527. if((0 < receive_data->session_handle) && (receive_data->session_handle <=
  528. OPENER_NUMBER_OF_SUPPORTED_SESSIONS)) {
  529. if(kEipInvalidSocket != g_registered_sessions[receive_data->session_handle - 1]) {
  530. return kSessionStatusValid;
  531. }
  532. }
  533. return kSessionStatusInvalid;
  534. }
  535. void CloseSessionBySessionHandle(const CipConnectionObject *const connection_object) {
  536. OPENER_TRACE_INFO("encap.c: Close session by handle\n");
  537. CipSessionHandle session_handle = connection_object->associated_encapsulation_session;
  538. CloseTcpSocket(g_registered_sessions[session_handle - 1]);
  539. g_registered_sessions[session_handle - 1] = kEipInvalidSocket;
  540. OPENER_TRACE_INFO("encap.c: Close session by handle done\n");
  541. }
  542. void CloseSession(int socket) {
  543. OPENER_TRACE_INFO("encap.c: Close session\n");
  544. for(unsigned int i = 0; i < OPENER_NUMBER_OF_SUPPORTED_SESSIONS; ++i) {
  545. if(g_registered_sessions[i] == socket) {
  546. CloseTcpSocket(socket);
  547. g_registered_sessions[i] = kEipInvalidSocket;
  548. CloseClass3ConnectionBasedOnSession(i + 1);
  549. break;
  550. }
  551. }OPENER_TRACE_INFO("encap.c: Close session done\n");
  552. }
  553. void RemoveSession(const int socket) {
  554. OPENER_TRACE_INFO("encap.c: Removing session\n");
  555. for(unsigned int i = 0; i < OPENER_NUMBER_OF_SUPPORTED_SESSIONS; ++i) {
  556. if(g_registered_sessions[i] == socket) {
  557. g_registered_sessions[i] = kEipInvalidSocket;
  558. CloseClass3ConnectionBasedOnSession(i + 1);
  559. break;
  560. }
  561. }OPENER_TRACE_INFO("encap.c: Session removed\n");
  562. }
  563. void EncapsulationShutDown(void) {
  564. OPENER_TRACE_INFO("encap.c: Encapsulation shutdown\n");
  565. for(size_t i = 0; i < OPENER_NUMBER_OF_SUPPORTED_SESSIONS; ++i) {
  566. if(kEipInvalidSocket != g_registered_sessions[i]) {
  567. CloseTcpSocket(g_registered_sessions[i]);
  568. g_registered_sessions[i] = kEipInvalidSocket;
  569. }
  570. }
  571. }
  572. void ManageEncapsulationMessages(const MilliSeconds elapsed_time) {
  573. for(size_t i = 0; i < ENCAP_NUMBER_OF_SUPPORTED_DELAYED_ENCAP_MESSAGES; i++) {
  574. if(kEipInvalidSocket != g_delayed_encapsulation_messages[i].socket) {
  575. g_delayed_encapsulation_messages[i].time_out -= elapsed_time;
  576. if(0 >= g_delayed_encapsulation_messages[i].time_out) {
  577. /* If delay is reached or passed, send the UDP message */
  578. sendto(g_delayed_encapsulation_messages[i].socket, (char*) g_delayed_encapsulation_messages[i].outgoing_message.message_buffer,
  579. g_delayed_encapsulation_messages[i].outgoing_message.used_message_length, 0, (struct sockaddr*) &(g_delayed_encapsulation_messages[i].receiver),
  580. sizeof(struct sockaddr));
  581. g_delayed_encapsulation_messages[i].socket = kEipInvalidSocket;
  582. }
  583. }
  584. }
  585. }
  586. void CloseEncapsulationSessionBySockAddr(const CipConnectionObject *const connection_object) {
  587. for(size_t i = 0; i < OPENER_NUMBER_OF_SUPPORTED_SESSIONS; ++i) {
  588. if(kEipInvalidSocket != g_registered_sessions[i]) {
  589. struct sockaddr_in encapsulation_session_addr = { 0 };
  590. socklen_t addrlength = sizeof(encapsulation_session_addr);
  591. if(getpeername(g_registered_sessions[i], (struct sockaddr*) &encapsulation_session_addr, &addrlength) < 0) { /* got error */
  592. int error_code = GetSocketErrorNumber();
  593. char *error_message = GetErrorMessage(error_code);
  594. OPENER_TRACE_ERR(
  595. "encap.c: error on getting peer name on closing session: %d - %s\n",
  596. error_code, error_message);
  597. FreeErrorMessage(error_message);
  598. }
  599. if(encapsulation_session_addr.sin_addr.s_addr == connection_object->originator_address.sin_addr.s_addr) {
  600. CloseSession(g_registered_sessions[i]);
  601. }
  602. }
  603. }
  604. }
  605. CipSessionHandle GetSessionFromSocket(const int socket_handle) {
  606. for(CipSessionHandle i = 0; i < OPENER_NUMBER_OF_SUPPORTED_SESSIONS; ++i) {
  607. if(socket_handle == g_registered_sessions[i]) {
  608. return i;
  609. }
  610. }
  611. return OPENER_NUMBER_OF_SUPPORTED_SESSIONS;
  612. }
  613. void CloseClass3ConnectionBasedOnSession(CipSessionHandle encapsulation_session_handle) {
  614. DoublyLinkedListNode *node = connection_list.first;
  615. while(NULL != node) {
  616. CipConnectionObject *connection_object = node->data;
  617. if(kConnectionObjectTransportClassTriggerTransportClass3 == ConnectionObjectGetTransportClassTriggerTransportClass(connection_object)
  618. && connection_object->associated_encapsulation_session == encapsulation_session_handle) {
  619. connection_object->connection_close_function(connection_object);
  620. }
  621. node = node->next;
  622. }
  623. }