| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952 |
- /*******************************************************************************
- * Copyright (c) 2009, Rockwell Automation, Inc.
- * All rights reserved.
- *
- ******************************************************************************/
- /** @file generic_networkhandler.c
- * @author Martin Melik Merkumians
- * @brief This file includes all platform-independent functions of the network handler to reduce code duplication
- *
- * The generic network handler delegates platform-dependent tasks to the platform network handler
- */
- #include <assert.h>
- #include <inttypes.h>
- #include <stdbool.h>
- #include <signal.h>
- #include "generic_networkhandler.h"
- #include "typedefs.h"
- #include "trace.h"
- #include "opener_error.h"
- #include "encap.h"
- #include "ciptcpipinterface.h"
- #include "opener_user_conf.h"
- #include "cipqos.h"
- #define MAX_NO_OF_TCP_SOCKETS 10
- /** @brief Ethernet/IP standard port */
- /* ----- Windows size_t PRI macros ------------- */
- #if defined(__MINGW32__) || defined(HPMICRO) /* This is a Mingw compiler or HPMICRO target (GCC) */
- #define PRIuSZT PRIuPTR
- #define PRIxSZT PRIxPTR
- #else
- /* Even the Visual Studio compilers / libraries since VS2015 know that now. */
- #define PRIuSZT "zu"
- #define PRIxSZT "zx"
- #endif /* if defined(__MINGW32__) */
- #if defined(_WIN32)
- /* Most network functions take their I/O buffers as (char *) pointers that
- * triggers a warning with our CipOctet (aka unsigned char) buffers. */
- #define NWBUF_CAST (void *)
- #else
- #define NWBUF_CAST
- #endif
- #ifndef MSG_NOSIGNAL
- #define MSG_NOSIGNAL 0
- #define MSG_NOSIGNAL_PRAGMA_MESSAGE "MSG_NOSIGNAL not defined. Check if your system stops on SIGPIPE, as this can happen with the send() function"
- #if defined(_WIN32)
- #pragma message(MSG_NOSIGNAL_PRAGMA_MESSAGE)
- #else
- #pragma GCC warning MSG_NOSIGNAL_PRAGMA_MESSAGE
- #endif /* defined(_WIN32) */
- #endif
- SocketTimer g_timestamps[OPENER_NUMBER_OF_SUPPORTED_SESSIONS];
- //EipUint8 g_ethernet_communication_buffer[PC_OPENER_ETHERNET_BUFFER_SIZE]; /**< communication buffer */
- /* global vars */
- fd_set master_socket;
- fd_set read_socket;
- int highest_socket_handle;
- int g_current_active_tcp_socket;
- struct timeval g_time_value;
- MilliSeconds g_actual_time;
- MilliSeconds g_last_time;
- NetworkStatus g_network_status;
- /** @brief Size of the timeout checker function pointer array
- */
- #define OPENER_TIMEOUT_CHECKER_ARRAY_SIZE 10
- /** @brief function pointer array for timer checker functions
- */
- TimeoutCheckerFunction timeout_checker_array[OPENER_TIMEOUT_CHECKER_ARRAY_SIZE];
- /** @brief handle any connection request coming in the TCP server socket.
- *
- */
- void CheckAndHandleTcpListenerSocket(void);
- /** @brief Checks and processes request received via the UDP unicast socket, currently the implementation is port-specific
- *
- */
- void CheckAndHandleUdpUnicastSocket(void);
- /** @brief Checks and handles incoming messages via UDP broadcast
- *
- */
- void CheckAndHandleUdpGlobalBroadcastSocket(void);
- /** @brief check if on the UDP consuming socket data has been received and if yes handle it correctly
- *
- */
- void CheckAndHandleConsumingUdpSocket(void);
- /** @brief Handles data on an established TCP connection, processed connection is given by socket
- *
- * @param socket The socket to be processed
- * @return kEipStatusOk on success, or kEipStatusError on failure
- */
- EipStatus HandleDataOnTcpSocket(int socket);
- void CheckEncapsulationInactivity(int socket_handle);
- void RemoveSocketTimerFromList(const int socket_handle);
- /*************************************************
- * Function implementations from now on
- *************************************************/
- EipStatus NetworkHandlerInitialize(void)
- {
- if (kEipStatusOk != NetworkHandlerInitializePlatform()) {
- return kEipStatusError;
- }
- SocketTimerArrayInitialize(g_timestamps, OPENER_NUMBER_OF_SUPPORTED_SESSIONS);
- /* Activate the current DSCP values to become the used set of values. */
- CipQosUpdateUsedSetQosValues();
- /* Make sure the multicast configuration matches the current IP address. */
- CipTcpIpCalculateMulticastIp(&g_tcpip);
- /* Freeze IP and network mask matching to the socket setup. This is needed
- * for the off subnet multicast routing check later. */
- g_network_status.ip_address = g_tcpip.interface_configuration.ip_address;
- g_network_status.network_mask = g_tcpip.interface_configuration.network_mask;
- /* Initialize encapsulation layer here because it accesses the IP address. */
- EncapsulationInit();
- /* clear the master and temp sets */
- FD_ZERO(&master_socket);
- FD_ZERO(&read_socket);
- /* create a new TCP socket */
- if ((g_network_status.tcp_listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler tcp_listener: error allocating socket, %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return kEipStatusError;
- }
- int set_socket_option_value = 1; //Represents true for used set socket options
- /* Activates address reuse */
- if (setsockopt(g_network_status.tcp_listener, SOL_SOCKET, SO_REUSEADDR, (char *)&set_socket_option_value, sizeof(set_socket_option_value)) == -1) {
- OPENER_TRACE_ERR("networkhandler tcp_listener: error setting socket option SO_REUSEADDR\n");
- return kEipStatusError;
- }
- if (SetSocketToNonBlocking(g_network_status.tcp_listener) < 0) {
- OPENER_TRACE_ERR("networkhandler tcp_listener: error setting socket to non-blocking on new socket\n");
- return kEipStatusError;
- }
- /* create a new UDP socket */
- if ((g_network_status.udp_global_broadcast_listener = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == kEipInvalidSocket) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler udp_global_broadcast_listener: error allocating socket, %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return kEipStatusError;
- }
- /* create a new UDP socket */
- if ((g_network_status.udp_unicast_listener = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == kEipInvalidSocket) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler udp_unicast_listener: error allocating socket, %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return kEipStatusError;
- }
- /* Activates address reuse */
- set_socket_option_value = 1;
- if (setsockopt(g_network_status.udp_global_broadcast_listener, SOL_SOCKET, SO_REUSEADDR, (char *)&set_socket_option_value,
- sizeof(set_socket_option_value)) == -1) {
- OPENER_TRACE_ERR("networkhandler udp_global_broadcast_listener: error setting socket option SO_REUSEADDR\n");
- return kEipStatusError;
- }
- if (SetSocketToNonBlocking(g_network_status.udp_global_broadcast_listener) < 0) {
- OPENER_TRACE_ERR("networkhandler udp_global_broadcast_listener: error setting socket to non-blocking on new socket\n");
- return kEipStatusError;
- }
- /* Activates address reuse */
- set_socket_option_value = 1;
- if (setsockopt(g_network_status.udp_unicast_listener, SOL_SOCKET, SO_REUSEADDR, (char *)&set_socket_option_value, sizeof(set_socket_option_value)) == -1) {
- OPENER_TRACE_ERR("networkhandler udp_unicast_listener: error setting socket option SO_REUSEADDR\n");
- return kEipStatusError;
- }
- if (SetSocketToNonBlocking(g_network_status.udp_unicast_listener) < 0) {
- OPENER_TRACE_ERR("networkhandler udp_unicast_listener: error setting socket to non-blocking\n");
- return kEipStatusError;
- }
- struct sockaddr_in my_address = { .sin_family = AF_INET, .sin_port = htons(kOpenerEthernetPort), .sin_addr.s_addr = g_network_status.ip_address };
- /* bind the new socket to port 0xAF12 (CIP) */
- if ((bind(g_network_status.tcp_listener, (struct sockaddr *)&my_address, sizeof(struct sockaddr))) == -1) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler tcp_listener: error with TCP bind: %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return kEipStatusError;
- }
- if ((bind(g_network_status.udp_unicast_listener, (struct sockaddr *)&my_address, sizeof(struct sockaddr))) == -1) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler udp_unicast_listener: error with UDP bind: %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return kEipStatusError;
- }
- /* have QoS DSCP explicit appear on UDP responses to unicast messages */
- if (SetQosOnSocket(g_network_status.udp_unicast_listener, CipQosGetDscpPriority(kConnectionObjectPriorityExplicit)) != 0) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler udp_unicast_listener: error set QoS %d: %d - %s\n", g_network_status.udp_unicast_listener, error_code, error_message);
- FreeErrorMessage(error_message);
- /* print message but don't abort by intent */
- }
- struct sockaddr_in global_broadcast_address = { .sin_family = AF_INET, .sin_port = htons(kOpenerEthernetPort), .sin_addr.s_addr = htonl(INADDR_ANY) };
- /* enable the UDP socket to receive broadcast messages */
- set_socket_option_value = 1;
- if (0 > setsockopt(g_network_status.udp_global_broadcast_listener, SOL_SOCKET, SO_BROADCAST, (char *)&set_socket_option_value, sizeof(int))) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler udp_global_broadcast_listener: error with setting broadcast receive: %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return kEipStatusError;
- }
- if ((bind(g_network_status.udp_global_broadcast_listener, (struct sockaddr *)&global_broadcast_address, sizeof(struct sockaddr))) == -1) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler udp_global_broadcast_listener: error with UDP bind: %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return kEipStatusError;
- }
- /* have QoS DSCP explicit appear on UDP responses to broadcast messages */
- if (SetQosOnSocket(g_network_status.udp_global_broadcast_listener, CipQosGetDscpPriority(kConnectionObjectPriorityExplicit)) != 0) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler udp_global_broadcast_listener: error set QoS %d: %d - %s\n", g_network_status.udp_global_broadcast_listener,
- error_code, error_message);
- FreeErrorMessage(error_message);
- /* print message but don't abort by intent */
- }
- /* Make QoS DSCP explicit already appear on SYN connection establishment.
- * A newly accept()ed TCP socket inherits the setting from this socket.
- */
- if (SetQosOnSocket(g_network_status.tcp_listener, CipQosGetDscpPriority(kConnectionObjectPriorityExplicit)) != 0) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler tcp_listener: error set QoS %d: %d - %s\n", g_network_status.tcp_listener, error_code, error_message);
- FreeErrorMessage(error_message);
- /* print message but don't abort by intent */
- }
- /* switch socket in listen mode */
- if ((listen(g_network_status.tcp_listener, MAX_NO_OF_TCP_SOCKETS)) == -1) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler tcp_listener: error with listen: %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return kEipStatusError;
- }
- /* add the listener socket to the master set */
- FD_SET(g_network_status.tcp_listener, &master_socket);
- FD_SET(g_network_status.udp_unicast_listener, &master_socket);
- FD_SET(g_network_status.udp_global_broadcast_listener, &master_socket);
- /* keep track of the biggest file descriptor */
- highest_socket_handle =
- GetMaxSocket(g_network_status.tcp_listener, g_network_status.udp_global_broadcast_listener, 0, g_network_status.udp_unicast_listener);
- g_last_time = GetMilliSeconds(); /* initialize time keeping */
- g_network_status.elapsed_time = 0;
- return kEipStatusOk;
- }
- void CloseUdpSocket(int socket_handle)
- {
- OPENER_TRACE_STATE("Closing UDP socket %d\n", socket_handle);
- CloseSocket(socket_handle);
- }
- void CloseTcpSocket(int socket_handle)
- {
- OPENER_TRACE_STATE("Closing TCP socket %d\n", socket_handle);
- ShutdownSocketPlatform(socket_handle);
- RemoveSocketTimerFromList(socket_handle);
- CloseSocket(socket_handle);
- }
- void RemoveSocketTimerFromList(const int socket_handle)
- {
- SocketTimer *socket_timer = NULL;
- while (NULL != (socket_timer = SocketTimerArrayGetSocketTimer(g_timestamps, OPENER_NUMBER_OF_SUPPORTED_SESSIONS, socket_handle))) {
- SocketTimerClear(socket_timer);
- }
- }
- EipBool8 CheckSocketSet(int socket)
- {
- EipBool8 return_value = false;
- if (FD_ISSET(socket, &read_socket)) {
- if (FD_ISSET(socket, &master_socket)) {
- return_value = true;
- } else {
- OPENER_TRACE_INFO("socket: %d closed with pending message\n", socket);
- }
- FD_CLR(socket, &read_socket);
- /* remove it from the read set so that later checks will not find it */
- }
- return return_value;
- }
- void CheckAndHandleTcpListenerSocket(void)
- {
- int new_socket = kEipInvalidSocket;
- /* see if this is a connection request to the TCP listener*/
- if (true == CheckSocketSet(g_network_status.tcp_listener)) {
- OPENER_TRACE_INFO("networkhandler: new TCP connection\n");
- new_socket = accept(g_network_status.tcp_listener, NULL, NULL);
- if (new_socket == kEipInvalidSocket) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler: error on accept: %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return;
- }
- OPENER_TRACE_INFO(">>> network handler: accepting new TCP socket: %d \n", new_socket);
- SocketTimer *socket_timer = SocketTimerArrayGetEmptySocketTimer(g_timestamps, OPENER_NUMBER_OF_SUPPORTED_SESSIONS);
- // OPENER_TRACE_INFO("Current time stamp: %ld\n", g_actual_time);
- // for(size_t i = 0; i < OPENER_NUMBER_OF_SUPPORTED_SESSIONS; i++) {
- // OPENER_TRACE_INFO("Socket: %d - Last Update: %ld\n",
- // g_timestamps[i].socket,
- // g_timestamps[i].last_update);
- // }
- OPENER_ASSERT(socket_timer != NULL);
- FD_SET(new_socket, &master_socket);
- /* add newfd to master set */
- if (new_socket > highest_socket_handle) {
- OPENER_TRACE_INFO("New highest socket: %d\n", new_socket);
- highest_socket_handle = new_socket;
- }
- OPENER_TRACE_STATE("networkhandler: opened new TCP connection on fd %d\n", new_socket);
- }
- }
- EipStatus NetworkHandlerProcessCyclic(void)
- {
- read_socket = master_socket;
- g_time_value.tv_sec = 0;
- g_time_value.tv_usec =
- (g_network_status.elapsed_time < kOpenerTimerTickInMilliSeconds ? kOpenerTimerTickInMilliSeconds - g_network_status.elapsed_time : 0) *
- 1000; /* 10 ms */
- int ready_socket = select(highest_socket_handle + 1, &read_socket, 0, 0, &g_time_value);
- if (ready_socket == kEipInvalidSocket) {
- if (EINTR == errno) /* we have somehow been interrupted. The default behavior is to go back into the select loop. */
- {
- return kEipStatusOk;
- } else {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler: error with select: %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return kEipStatusError;
- }
- }
- if (ready_socket > 0) {
- CheckAndHandleTcpListenerSocket();
- CheckAndHandleUdpUnicastSocket();
- CheckAndHandleUdpGlobalBroadcastSocket();
- CheckAndHandleConsumingUdpSocket();
- for (int socket = 0; socket <= highest_socket_handle; socket++) {
- if (true == CheckSocketSet(socket)) {
- /* if it is still checked it is a TCP receive */
- if (kEipStatusError == HandleDataOnTcpSocket(socket)) /* if error */
- {
- CloseTcpSocket(socket);
- RemoveSession(socket); /* clean up session and close the socket */
- }
- }
- }
- }
- for (int socket = 0; socket <= highest_socket_handle; socket++) {
- CheckEncapsulationInactivity(socket);
- }
- /* Check if all connections from one originator times out */
- //CheckForTimedOutConnectionsAndCloseTCPConnections();
- //OPENER_TRACE_INFO("Socket Loop done\n");
- g_actual_time = GetMilliSeconds();
- g_network_status.elapsed_time += g_actual_time - g_last_time;
- g_last_time = g_actual_time;
- //OPENER_TRACE_INFO("Elapsed time: %u\n", g_network_status.elapsed_time);
- /* check if we had been not able to update the connection manager for several kOpenerTimerTickInMilliSeconds.
- * This should compensate the jitter of the windows timer
- */
- if (g_network_status.elapsed_time >= kOpenerTimerTickInMilliSeconds) {
- /* call manage_connections() in connection manager every kOpenerTimerTickInMilliSeconds ms */
- ManageConnections(g_network_status.elapsed_time);
- /* Call timeout checker functions registered in timeout_checker_array */
- for (size_t i = 0; i < OPENER_TIMEOUT_CHECKER_ARRAY_SIZE; i++) {
- if (NULL != timeout_checker_array[i]) {
- (timeout_checker_array[i])(g_network_status.elapsed_time);
- }
- }
- g_network_status.elapsed_time = 0;
- }
- return kEipStatusOk;
- }
- EipStatus NetworkHandlerFinish(void)
- {
- CloseTcpSocket(g_network_status.tcp_listener);
- CloseUdpSocket(g_network_status.udp_unicast_listener);
- CloseUdpSocket(g_network_status.udp_global_broadcast_listener);
- return kEipStatusOk;
- }
- void CheckAndHandleUdpGlobalBroadcastSocket(void)
- {
- /* see if this is an unsolicited inbound UDP message */
- if (true == CheckSocketSet(g_network_status.udp_global_broadcast_listener)) {
- struct sockaddr_in from_address = { 0 };
- socklen_t from_address_length = sizeof(from_address);
- OPENER_TRACE_STATE("networkhandler: unsolicited UDP message on EIP global broadcast socket\n");
- /* Handle UDP broadcast messages */
- CipOctet incoming_message[PC_OPENER_ETHERNET_BUFFER_SIZE] = { 0 };
- int received_size = recvfrom(g_network_status.udp_global_broadcast_listener, NWBUF_CAST incoming_message, sizeof(incoming_message), 0,
- (struct sockaddr *)&from_address, &from_address_length);
- if (received_size <= 0) { /* got error */
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler: error on recvfrom UDP global broadcast port: %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return;
- }
- OPENER_TRACE_INFO("Data received on global broadcast UDP:\n");
- const EipUint8 *receive_buffer = &incoming_message[0];
- int remaining_bytes = 0;
- ENIPMessage outgoing_message;
- InitializeENIPMessage(&outgoing_message);
- EipStatus need_to_send = HandleReceivedExplictUdpData(g_network_status.udp_unicast_listener,
- /* sending from unicast port, due to strange behavior of the broadcast port */
- &from_address, receive_buffer, received_size, &remaining_bytes, false, &outgoing_message);
- receive_buffer += received_size - remaining_bytes;
- received_size = remaining_bytes;
- if (need_to_send > 0) {
- OPENER_TRACE_INFO("UDP broadcast reply sent:\n");
- /* if the active socket matches a registered UDP callback, handle a UDP packet */
- if (sendto(g_network_status.udp_unicast_listener, /* sending from unicast port, due to strange behavior of the broadcast port */
- (char *)outgoing_message.message_buffer, outgoing_message.used_message_length, 0, (struct sockaddr *)&from_address,
- sizeof(from_address)) != outgoing_message.used_message_length) {
- OPENER_TRACE_INFO("networkhandler: UDP response was not fully sent\n");
- }
- }
- if (remaining_bytes > 0) {
- OPENER_TRACE_ERR("Request on broadcast UDP port had too many data (%d)", remaining_bytes);
- }
- }
- }
- void CheckAndHandleUdpUnicastSocket(void)
- {
- /* see if this is an unsolicited inbound UDP message */
- if (true == CheckSocketSet(g_network_status.udp_unicast_listener)) {
- struct sockaddr_in from_address = { 0 };
- socklen_t from_address_length = sizeof(from_address);
- OPENER_TRACE_STATE("networkhandler: unsolicited UDP message on EIP unicast socket\n");
- /* Handle UDP broadcast messages */
- CipOctet incoming_message[PC_OPENER_ETHERNET_BUFFER_SIZE] = { 0 };
- int received_size = recvfrom(g_network_status.udp_unicast_listener, NWBUF_CAST incoming_message, sizeof(incoming_message), 0,
- (struct sockaddr *)&from_address, &from_address_length);
- if (received_size <= 0) { /* got error */
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler: error on recvfrom UDP unicast port: %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return;
- }
- OPENER_TRACE_INFO("Data received on UDP unicast:\n");
- EipUint8 *receive_buffer = &incoming_message[0];
- int remaining_bytes = 0;
- ENIPMessage outgoing_message;
- InitializeENIPMessage(&outgoing_message);
- EipStatus need_to_send = HandleReceivedExplictUdpData(g_network_status.udp_unicast_listener, &from_address, receive_buffer, received_size,
- &remaining_bytes, true, &outgoing_message);
- receive_buffer += received_size - remaining_bytes;
- received_size = remaining_bytes;
- if (need_to_send > 0) {
- OPENER_TRACE_INFO("UDP unicast reply sent:\n");
- /* if the active socket matches a registered UDP callback, handle a UDP packet */
- if (sendto(g_network_status.udp_unicast_listener, (char *)outgoing_message.message_buffer, outgoing_message.used_message_length, 0,
- (struct sockaddr *)&from_address, sizeof(from_address)) != outgoing_message.used_message_length) {
- OPENER_TRACE_INFO("networkhandler: UDP unicast response was not fully sent\n");
- }
- }
- if (remaining_bytes > 0) {
- OPENER_TRACE_ERR("Request on broadcast UDP port had too many data (%d)", remaining_bytes);
- }
- }
- }
- EipStatus SendUdpData(const struct sockaddr_in *const address, const ENIPMessage *const outgoing_message)
- {
- #if defined(OPENER_TRACE_ENABLED)
- static char ip_str[INET_ADDRSTRLEN];
- OPENER_TRACE_INFO("UDP packet to be sent to: %s:%d\n", inet_ntop(AF_INET, &address->sin_addr, ip_str, sizeof ip_str), ntohs(address->sin_port));
- #endif
- int sent_length = sendto(g_network_status.udp_io_messaging, (char *)outgoing_message->message_buffer, outgoing_message->used_message_length, 0,
- (struct sockaddr *)address, sizeof(*address));
- if (sent_length < 0) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler: error with sendto in SendUDPData: %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return kEipStatusError;
- }
- if (sent_length != outgoing_message->used_message_length) {
- OPENER_TRACE_WARN("data length sent_length mismatch; probably not all data was sent in SendUdpData, sent %d of %d\n", sent_length,
- outgoing_message->used_message_length);
- return kEipStatusError;
- }
- return kEipStatusOk;
- }
- EipStatus HandleDataOnTcpSocket(int socket)
- {
- OPENER_TRACE_INFO("Entering HandleDataOnTcpSocket for socket: %d\n", socket);
- int remaining_bytes = 0;
- long data_sent = PC_OPENER_ETHERNET_BUFFER_SIZE;
- /* We will handle just one EIP packet here the rest is done by the select
- * method which will inform us if more data is available in the socket
- because of the current implementation of the main loop this may not be
- the fastest way and a loop here with a non blocking socket would better
- fit*/
- /*Check how many data is here -- read the first four bytes from the connection */
- CipOctet incoming_message[PC_OPENER_ETHERNET_BUFFER_SIZE] = { 0 };
- long number_of_read_bytes = recv(socket, NWBUF_CAST incoming_message, 4, 0); /*TODO we may have to set the socket to a non blocking socket */
- SocketTimer *const socket_timer = SocketTimerArrayGetSocketTimer(g_timestamps, OPENER_NUMBER_OF_SUPPORTED_SESSIONS, socket);
- if (number_of_read_bytes == 0) {
- OPENER_TRACE_ERR("networkhandler: socket: %d - connection closed by client.\n", socket);
- RemoveSocketTimerFromList(socket);
- RemoveSession(socket);
- return kEipStatusError;
- }
- if (number_of_read_bytes < 0) {
- int error_code = GetSocketErrorNumber();
- if (OPENER_SOCKET_WOULD_BLOCK == error_code) {
- return kEipStatusOk;
- }
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler: error on recv: %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return kEipStatusError;
- }
- const EipUint8 *read_buffer = &incoming_message[2]; /* at this place EIP stores the data length */
- size_t data_size = GetUintFromMessage(&read_buffer) + ENCAPSULATION_HEADER_LENGTH - 4; /* -4 is for the 4 bytes we have already read*/
- /* (NOTE this advances the buffer pointer) */
- if ((PC_OPENER_ETHERNET_BUFFER_SIZE - 4) < data_size) { /*TODO can this be handled in a better way?*/
- OPENER_TRACE_ERR("too large packet received will be ignored, will drop the data\n");
- /* Currently we will drop the whole packet */
- do {
- OPENER_TRACE_INFO("Entering consumption loop, remaining data to receive: %ld\n", data_sent);
- number_of_read_bytes = recv(socket, NWBUF_CAST & incoming_message[0], data_sent, 0);
- if (number_of_read_bytes == 0) /* got error or connection closed by client */
- {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler: socket: %d - connection closed by client: %d - %s\n", socket, error_code, error_message);
- FreeErrorMessage(error_message);
- RemoveSocketTimerFromList(socket);
- return kEipStatusError;
- }
- if (number_of_read_bytes < 0) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- if (OPENER_SOCKET_WOULD_BLOCK == error_code) {
- return kEipStatusOk;
- }
- OPENER_TRACE_ERR("networkhandler: error on recv: %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return kEipStatusError;
- }
- data_size -= number_of_read_bytes;
- if ((data_size < PC_OPENER_ETHERNET_BUFFER_SIZE) && (data_size != 0)) {
- data_sent = data_size;
- }
- } while (0 < data_size);
- SocketTimerSetLastUpdate(socket_timer, g_actual_time);
- return kEipStatusOk;
- }
- number_of_read_bytes = recv(socket, NWBUF_CAST & incoming_message[4], data_size, 0);
- if (0 == number_of_read_bytes) /* got error or connection closed by client */
- {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler: socket: %d - connection closed by client: %d - %s\n", socket, error_code, error_message);
- FreeErrorMessage(error_message);
- RemoveSocketTimerFromList(socket);
- RemoveSession(socket);
- return kEipStatusError;
- }
- if (number_of_read_bytes < 0) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- if (OPENER_SOCKET_WOULD_BLOCK == error_code) {
- return kEipStatusOk;
- }
- OPENER_TRACE_ERR("networkhandler: error on recv: %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return kEipStatusError;
- }
- if ((unsigned)number_of_read_bytes == data_size) {
- /*we got the right amount of data */
- data_size += 4;
- /*TODO handle partial packets*/
- OPENER_TRACE_INFO("Data received on TCP: %" PRIuSZT "\n", data_size);
- g_current_active_tcp_socket = socket;
- struct sockaddr sender_address;
- memset(&sender_address, 0, sizeof(sender_address));
- socklen_t fromlen = sizeof(sender_address);
- if (getpeername(socket, (struct sockaddr *)&sender_address, &fromlen) < 0) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler: could not get peername: %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- }
- ENIPMessage outgoing_message;
- InitializeENIPMessage(&outgoing_message);
- EipStatus need_to_send = HandleReceivedExplictTcpData(socket, incoming_message, data_size, &remaining_bytes, &sender_address, &outgoing_message);
- if (NULL != socket_timer) {
- SocketTimerSetLastUpdate(socket_timer, g_actual_time);
- }
- g_current_active_tcp_socket = kEipInvalidSocket;
- if (remaining_bytes != 0) {
- OPENER_TRACE_WARN("Warning: received packet was to long: %d Bytes left!\n", remaining_bytes);
- }
- if (need_to_send > 0) {
- OPENER_TRACE_INFO("TCP reply: send %" PRIuSZT " bytes on %d\n", outgoing_message.used_message_length, socket);
- data_sent = send(socket, (char *)outgoing_message.message_buffer, outgoing_message.used_message_length, MSG_NOSIGNAL);
- SocketTimerSetLastUpdate(socket_timer, g_actual_time);
- if (data_sent != outgoing_message.used_message_length) {
- OPENER_TRACE_WARN("TCP response was not fully sent: exp %" PRIuSZT ", sent %ld\n", outgoing_message.used_message_length, data_sent);
- }
- }
- return kEipStatusOk;
- } else {
- /* we got a fragmented packet currently we cannot handle this will
- * for this we would need a network buffer per TCP socket
- *
- * However with typical packet sizes of EIP this shouldn't be a big issue.
- */
- /*TODO handle fragmented packets */
- }
- return kEipStatusError;
- }
- /** @brief Create the UDP socket for the implicit IO messaging, one socket handles all connections
- *
- * @return the socket handle if successful, else kEipInvalidSocket */
- int CreateUdpSocket(void)
- {
- /* create a new UDP socket */
- g_network_status.udp_io_messaging = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (g_network_status.udp_io_messaging == kEipInvalidSocket) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler: cannot create UDP socket: %d- %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return kEipInvalidSocket;
- }
- if (SetSocketToNonBlocking(g_network_status.udp_io_messaging) < 0) {
- OPENER_TRACE_ERR("networkhandler udp_io_messaging: error setting socket to non-blocking on new socket\n");
- CloseUdpSocket(g_network_status.udp_io_messaging);
- OPENER_ASSERT(false); /* This should never happen! */
- return kEipInvalidSocket;
- }
- OPENER_TRACE_INFO("networkhandler: UDP socket %d\n", g_network_status.udp_io_messaging);
- int option_value = 1;
- if (setsockopt(g_network_status.udp_io_messaging, SOL_SOCKET, SO_REUSEADDR, (char *)&option_value, sizeof(option_value)) < 0) {
- OPENER_TRACE_ERR("error setting socket option SO_REUSEADDR on %s UDP socket\n");
- CloseUdpSocket(g_network_status.udp_io_messaging);
- return kEipInvalidSocket;
- }
- /* The bind on UDP sockets is necessary as the ENIP spec wants the source port to be specified to 2222 */
- struct sockaddr_in source_addr = { .sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_ANY), .sin_port = htons(kOpenerEipIoUdpPort) };
- if (bind(g_network_status.udp_io_messaging, (struct sockaddr *)&source_addr, sizeof(source_addr)) < 0) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("error on bind UDP for producing messages: %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- CloseUdpSocket(g_network_status.udp_io_messaging);
- return kEipInvalidSocket;
- }
- /* add new socket to the master list */
- FD_SET(g_network_status.udp_io_messaging, &master_socket);
- if (g_network_status.udp_io_messaging > highest_socket_handle) {
- OPENER_TRACE_INFO("New highest socket: %d\n", g_network_status.udp_io_messaging);
- highest_socket_handle = g_network_status.udp_io_messaging;
- }
- return g_network_status.udp_io_messaging;
- }
- /** @brief Set the Qos the socket for implicit IO messaging
- *
- * @return 0 if successful, else the error code */
- int SetQos(CipUsint qos_for_socket)
- {
- if (SetQosOnSocket(g_network_status.udp_io_messaging, CipQosGetDscpPriority(qos_for_socket)) != 0) { /* got error */
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler: error on set QoS on socket: %d - %s\n", error_code, error_message);
- return error_code;
- }
- return 0;
- }
- /** @brief Set the socket options for Multicast Producer
- *
- * @return 0 if successful, else the error code */
- int SetSocketOptionsMulticastProduce(void)
- {
- if (g_tcpip.mcast_ttl_value != 1) {
- /* we need to set a TTL value for the socket */
- if (setsockopt(g_network_status.udp_io_messaging, IPPROTO_IP, IP_MULTICAST_TTL, NWBUF_CAST & g_tcpip.mcast_ttl_value, sizeof(g_tcpip.mcast_ttl_value)) <
- 0) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler: could not set the TTL to: %d, error: %d - %s\n", g_tcpip.mcast_ttl_value, error_code, error_message);
- FreeErrorMessage(error_message);
- return error_code;
- }
- }
- /* Need to specify the interface for outgoing multicast packets on a
- device with multiple interfaces. */
- struct in_addr my_addr = { .s_addr = g_network_status.ip_address };
- if (setsockopt(g_network_status.udp_io_messaging, IPPROTO_IP, IP_MULTICAST_IF, NWBUF_CAST & my_addr.s_addr, sizeof my_addr.s_addr) < 0) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler: could not set the multicast interface, error: %d "
- "- %s\n",
- error_code, error_message);
- FreeErrorMessage(error_message);
- return error_code;
- }
- return 0;
- }
- /** @brief Get the peer address
- *
- * @return peer address if successful, else any address (0) */
- EipUint32 GetPeerAddress(void)
- {
- struct sockaddr_in peer_address;
- socklen_t peer_address_length = sizeof(peer_address);
- if (getpeername(g_current_active_tcp_socket, (struct sockaddr *)&peer_address, &peer_address_length) < 0) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler: could not get peername: %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- return htonl(INADDR_ANY);
- }
- return peer_address.sin_addr.s_addr;
- }
- void CheckAndHandleConsumingUdpSocket(void)
- {
- DoublyLinkedListNode *iterator = connection_list.first;
- CipConnectionObject *current_connection_object = NULL;
- /* see a message of the registered UDP socket has been received */
- while (NULL != iterator) {
- current_connection_object = (CipConnectionObject *)iterator->data;
- iterator = iterator->next; /* do this at the beginning as the close function may can make the entry invalid */
- if ((kEipInvalidSocket != current_connection_object->socket[kUdpCommuncationDirectionConsuming]) &&
- (true == CheckSocketSet(current_connection_object->socket[kUdpCommuncationDirectionConsuming]))) {
- OPENER_TRACE_INFO("Processing UDP consuming message\n");
- struct sockaddr_in from_address = { 0 };
- socklen_t from_address_length = sizeof(from_address);
- CipOctet incoming_message[PC_OPENER_ETHERNET_BUFFER_SIZE] = { 0 };
- int received_size = recvfrom(g_network_status.udp_io_messaging, NWBUF_CAST incoming_message, sizeof(incoming_message), 0,
- (struct sockaddr *)&from_address, &from_address_length);
- if (0 == received_size) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- OPENER_TRACE_ERR("networkhandler: socket: %d - connection closed by client: %d - %s\n",
- current_connection_object->socket[kUdpCommuncationDirectionConsuming], error_code, error_message);
- FreeErrorMessage(error_message);
- current_connection_object->connection_close_function(current_connection_object);
- continue;
- }
- if (0 > received_size) {
- int error_code = GetSocketErrorNumber();
- char *error_message = GetErrorMessage(error_code);
- if (OPENER_SOCKET_WOULD_BLOCK == error_code) {
- return; // No fatal error, resume execution
- }
- OPENER_TRACE_ERR("networkhandler: error on recv: %d - %s\n", error_code, error_message);
- FreeErrorMessage(error_message);
- current_connection_object->connection_close_function(current_connection_object);
- continue;
- }
- HandleReceivedConnectedData(incoming_message, received_size, &from_address);
- }
- }
- }
- void CloseSocket(const int socket_handle)
- {
- OPENER_TRACE_INFO("networkhandler: closing socket %d\n", socket_handle);
- if (kEipInvalidSocket != socket_handle) {
- FD_CLR(socket_handle, &master_socket);
- CloseSocketPlatform(socket_handle);
- }
- OPENER_TRACE_INFO("networkhandler: closing socket done %d\n", socket_handle);
- }
- int GetMaxSocket(int socket1, int socket2, int socket3, int socket4)
- {
- if ((socket1 > socket2) && (socket1 > socket3) && (socket1 > socket4)) {
- return socket1;
- }
- if ((socket2 > socket1) && (socket2 > socket3) && (socket2 > socket4)) {
- return socket2;
- }
- if ((socket3 > socket1) && (socket3 > socket2) && (socket3 > socket4)) {
- return socket3;
- }
- return socket4;
- }
- void CheckEncapsulationInactivity(int socket_handle)
- {
- if (0 < g_tcpip.encapsulation_inactivity_timeout) { //*< Encapsulation inactivity timeout is enabled
- SocketTimer *socket_timer = SocketTimerArrayGetSocketTimer(g_timestamps, OPENER_NUMBER_OF_SUPPORTED_SESSIONS, socket_handle);
- if (NULL != socket_timer) {
- MilliSeconds diff_milliseconds = g_actual_time - SocketTimerGetLastUpdate(socket_timer);
- if (diff_milliseconds >= (MilliSeconds)(1000UL * g_tcpip.encapsulation_inactivity_timeout)) {
- CipSessionHandle encapsulation_session_handle = GetSessionFromSocket(socket_handle);
- CloseClass3ConnectionBasedOnSession(encapsulation_session_handle);
- CloseTcpSocket(socket_handle);
- RemoveSession(socket_handle);
- }
- }
- }
- }
- void RegisterTimeoutChecker(TimeoutCheckerFunction timeout_checker_function)
- {
- for (size_t i = 0; i < OPENER_TIMEOUT_CHECKER_ARRAY_SIZE; i++) {
- if (NULL == timeout_checker_array[i]) { // find empty array element
- timeout_checker_array[i] = timeout_checker_function; // add function pointer to array
- break;
- }
- }
- }
|