// // Copyright (c) 2014-2017 Wind River Systems, Inc. // // SPDX-License-Identifier: Apache-2.0 // #include "sm_heartbeat_msg.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sm_types.h" #include "sm_debug.h" #include "sm_list.h" #include "sm_selobj.h" #include "sm_hw.h" #include "sm_sha512.h" #include "sm_failover.h" #define SM_HEARTBEAT_MSG_MAX_SIZE 128 #define SM_HEARTBEAT_MSG_BUFFER_MAX_SIZE 512 typedef enum { SM_HEARTBEAT_MSG_TYPE_UNKNOWN, SM_HEARTBEAT_MSG_TYPE_ALIVE, } SmHeartbeatMsgTypeT; typedef struct { uint16_t version; uint16_t revision; uint16_t msg_len; uint16_t msg_type; char node_name[SM_NODE_NAME_MAX_CHAR]; uint32_t auth_type; uint8_t auth_vector[SM_AUTHENTICATION_VECTOR_MAX_CHAR]; } __attribute__ ((packed)) SmHeartbeatMsgHeaderT; typedef struct { } __attribute__ ((packed)) SmHeartbeatMsgAliveT; typedef struct { uint16_t msg_size; uint32_t if_state; } __attribute__ ((packed)) SmHeartbeatMsgAliveRev2T; typedef struct { SmHeartbeatMsgHeaderT header; union { SmHeartbeatMsgAliveT alive; SmHeartbeatMsgAliveRev2T if_state_msg; char raw_msg[SM_HEARTBEAT_MSG_MAX_SIZE-sizeof(SmHeartbeatMsgHeaderT)]; } u; } __attribute__ ((packed)) SmHeartbeatMsgT; static char _tx_control_buffer[SM_HEARTBEAT_MSG_BUFFER_MAX_SIZE] __attribute__((aligned)); static char _rx_control_buffer[SM_HEARTBEAT_MSG_BUFFER_MAX_SIZE] __attribute__((aligned)); static SmListT* _callbacks = NULL; // **************************************************************************** // Heartbeat Messaging - Register Callbacks // ======================================== SmErrorT sm_heartbeat_msg_register_callbacks( SmHeartbeatMsgCallbacksT* callbacks ) { SM_LIST_PREPEND( _callbacks, (SmListEntryDataPtrT) callbacks ); return( SM_OKAY ); } // **************************************************************************** // **************************************************************************** // Heartbeat Messaging - Deregister Callbacks // ========================================== SmErrorT sm_heartbeat_msg_deregister_callbacks( SmHeartbeatMsgCallbacksT* callbacks ) { SM_LIST_REMOVE( _callbacks, (SmListEntryDataPtrT) callbacks ); return( SM_OKAY ); } // **************************************************************************** // **************************************************************************** // Heartbeat Messaging - Send message from source // ============================================== static int sm_heartbeat_msg_sendmsg_src_ipv6(int socket, void* msg, size_t msg_len, int flags, struct sockaddr_in6* dst_addr, struct in6_addr* src_Addr) { struct msghdr msg_hdr = {}; struct cmsghdr *cmsg; struct in6_pktinfo *pktinfo; struct iovec iov = {msg, msg_len}; memset( &_tx_control_buffer, 0, sizeof(_tx_control_buffer) ); msg_hdr.msg_iov = &iov; msg_hdr.msg_iovlen = 1; msg_hdr.msg_name = dst_addr; msg_hdr.msg_namelen = sizeof(struct sockaddr_in6); msg_hdr.msg_control = _tx_control_buffer; msg_hdr.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo)); cmsg = CMSG_FIRSTHDR(&msg_hdr); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); pktinfo = (struct in6_pktinfo*) CMSG_DATA(cmsg); pktinfo->ipi6_ifindex = 0; pktinfo->ipi6_addr = *src_Addr; return sendmsg( socket, &msg_hdr, flags ); } // **************************************************************************** // **************************************************************************** // initialize socket // ============================================== static SmErrorT sm_initialize_socket(int& sock) { int result; int flags; // Set socket to non-blocking. flags = fcntl( sock, F_GETFL, 0 ); if( 0 > flags ) { DPRINTFE( "Failed to get flags, error=%s.", strerror( errno ) ); close( sock ); return( SM_FAILED ); } if( 0 > fcntl( sock, F_SETFL, flags | O_NONBLOCK ) ) { DPRINTFE( "Failed to set flags, error=%s.", strerror( errno ) ); close( sock ); return( SM_FAILED ); } // Close on exec. flags = fcntl( sock, F_GETFD, 0 ); if( 0 > flags ) { DPRINTFE( "Failed to get flags, error=%s.", strerror( errno ) ); close( sock ); return( SM_FAILED ); } if( 0 > fcntl( sock, F_SETFD, flags | FD_CLOEXEC ) ) { DPRINTFE( "Failed to set flags, error=%s.", strerror( errno ) ); close( sock ); return( SM_FAILED ); } // Allow address reuse on socket. flags = 1; result = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (void*) &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to set reuseaddr socket option, " "errno=%s.", strerror(errno) ); close( sock ); return( SM_FAILED ); } return SM_OKAY; } // **************************************************************************** // **************************************************************************** // Heartbeat Messaging - Send Alive // ================================ SmErrorT sm_heartbeat_msg_send_alive( SmNetworkTypeT network_type, char node_name[], SmNetworkAddressT* network_address, SmNetworkAddressT* dst_addr, int network_port, char interface_name[], SmAuthTypeT auth_type, char auth_key[], int sender_socket ) { struct sockaddr_in dst_addr4; struct sockaddr_in6 dst_addr6; SmHeartbeatMsgT heartbeat_msg; SmIpv4AddressT* ipv4_address = &(dst_addr->u.ipv4); SmIpv6AddressT* ipv6_address = &(dst_addr->u.ipv6); int result; memset( &heartbeat_msg, 0, sizeof(SmHeartbeatMsgT) ); heartbeat_msg.header.version = htons(SM_VERSION); heartbeat_msg.header.revision = htons(SM_REVISION); heartbeat_msg.header.msg_len = htons(sizeof(SmHeartbeatMsgT)); heartbeat_msg.header.msg_type = htons(SM_HEARTBEAT_MSG_TYPE_ALIVE); snprintf( heartbeat_msg.header.node_name, sizeof(heartbeat_msg.header.node_name), "%s", node_name ); heartbeat_msg.u.if_state_msg.msg_size = htons((uint16_t)sizeof(SmHeartbeatMsgAliveRev2T)); SmHeartbeatMsgIfStateT if_state; if_state = sm_failover_if_state_get(); heartbeat_msg.u.if_state_msg.if_state = htonl(if_state); if( SM_AUTH_TYPE_HMAC_SHA512 == auth_type ) { SmSha512HashT hash; heartbeat_msg.header.auth_type = htonl(auth_type); sm_sha512_hmac( &heartbeat_msg, sizeof(heartbeat_msg), auth_key, strlen(auth_key), &hash ); memcpy( &(heartbeat_msg.header.auth_vector[0]), &(hash.bytes[0]), SM_SHA512_HASH_SIZE ); } else { heartbeat_msg.header.auth_type = htonl(SM_AUTH_TYPE_NONE); } if( SM_NETWORK_TYPE_IPV4_UDP == network_type ) { memset( &dst_addr4, 0, sizeof(dst_addr4) ); dst_addr4.sin_family = AF_INET; dst_addr4.sin_port = htons(network_port); dst_addr4.sin_addr.s_addr = ipv4_address->sin.s_addr; result = sendto( sender_socket, &heartbeat_msg, sizeof(SmHeartbeatMsgT), 0, (struct sockaddr *) &dst_addr4, sizeof(dst_addr4) ); if( 0 > result ) { DPRINTFE( "Failed to send message on socket for interface (%s), " "error=%s.", interface_name, strerror( errno ) ); return( SM_FAILED ); } return( SM_OKAY ); } else if( SM_NETWORK_TYPE_IPV6_UDP == network_type ) { memset( &dst_addr6, 0, sizeof(dst_addr6) ); dst_addr6.sin6_family = AF_INET6; dst_addr6.sin6_port = htons(network_port); dst_addr6.sin6_addr = ipv6_address->sin6; result = sm_heartbeat_msg_sendmsg_src_ipv6( sender_socket, (void*) &heartbeat_msg, sizeof(SmHeartbeatMsgT),0, &dst_addr6, &network_address->u.ipv6.sin6 ); if( 0 > result ) { DPRINTFE( "Failed to send message on socket for interface (%s), " "error=%s.", interface_name, strerror( errno ) ); return( SM_FAILED ); } return( SM_OKAY ); } else { DPRINTFE( "Unsupported network type (%s).", sm_network_type_str( network_type ) ); return( SM_FAILED ); } } // **************************************************************************** // **************************************************************************** // Heartbeat Messaging - Get Ancillary Data // ======================================== static void* sm_heartbeat_msg_get_ancillary_data( struct msghdr* msg_hdr, int cmsg_level, int cmsg_type ) { struct cmsghdr* cmsg; for( cmsg = CMSG_FIRSTHDR(msg_hdr); NULL != cmsg; cmsg = CMSG_NXTHDR( msg_hdr, cmsg) ) { if(( cmsg_level == cmsg->cmsg_level )&& ( cmsg_type == cmsg->cmsg_type )) { return( CMSG_DATA(cmsg) ); } } return( NULL ); } // **************************************************************************** // **************************************************************************** // Heartbeat Messaging - Dispatch Message // ====================================== static void sm_heartbeat_msg_dispatch_msg( SmHeartbeatMsgT* heartbeat_msg, SmNetworkAddressT* network_address, int network_port, char* interface_name ) { SmHeartbeatMsgCallbacksT* callbacks; SmListT* entry = NULL; SmListEntryDataPtrT entry_data; if( SM_VERSION != ntohs(heartbeat_msg->header.version) ) { DPRINTFD( "Received unsupported version (%i).", ntohs(heartbeat_msg->header.version) ); return; } if( SM_AUTH_TYPE_HMAC_SHA512 == ntohl(heartbeat_msg->header.auth_type) ) { uint8_t auth_vector[SM_AUTHENTICATION_VECTOR_MAX_CHAR]; memcpy( auth_vector, &(heartbeat_msg->header.auth_vector[0]), SM_AUTHENTICATION_VECTOR_MAX_CHAR ); memset( &(heartbeat_msg->header.auth_vector[0]), 0, SM_AUTHENTICATION_VECTOR_MAX_CHAR ); SM_LIST_FOREACH( _callbacks, entry, entry_data ) { callbacks = (SmHeartbeatMsgCallbacksT*) entry_data; if( NULL == callbacks->auth ) continue; if( !(callbacks->auth( interface_name, network_address, network_port, heartbeat_msg, sizeof(SmHeartbeatMsgT), auth_vector )) ) { DPRINTFD( "Authentication check failed on message (%i) from " "node (%s).", ntohs(heartbeat_msg->header.msg_type), heartbeat_msg->header.node_name ); return; } else { DPRINTFD( "Authentication check passed on message (%i) from " "node (%s).", ntohs(heartbeat_msg->header.msg_type), heartbeat_msg->header.node_name ); } } } uint16_t msg_size; SmHeartbeatMsgIfStateT if_state = 0; bool perform_if_exg = false; if(2 <= ntohs(heartbeat_msg->header.revision)) { msg_size = ntohs(heartbeat_msg->u.if_state_msg.msg_size); if(sizeof(SmHeartbeatMsgAliveRev2T) == msg_size ) { if_state = ntohl(heartbeat_msg->u.if_state_msg.if_state); perform_if_exg = true; }else { DPRINTFE("Invalid interface state package. size (%d) vs expected (%d)", msg_size, sizeof(SmHeartbeatMsgAliveRev2T)); } } switch( ntohs(heartbeat_msg->header.msg_type) ) { case SM_HEARTBEAT_MSG_TYPE_ALIVE: DPRINTFD( "Alive message received." ); SM_LIST_FOREACH( _callbacks, entry, entry_data ) { callbacks = (SmHeartbeatMsgCallbacksT*) entry_data; if( NULL != callbacks->alive ) { callbacks->alive( heartbeat_msg->header.node_name, network_address, network_port, ntohs(heartbeat_msg->header.version), ntohs(heartbeat_msg->header.revision), interface_name ); } if(perform_if_exg) { if(NULL != callbacks->if_state) { callbacks->if_state(heartbeat_msg->header.node_name, if_state); } else { DPRINTFD("No handler for if state package"); } } } break; default: DPRINTFI( "Unsupported message type (%i) received.", ntohs(heartbeat_msg->header.msg_type) ); break; } } // **************************************************************************** // **************************************************************************** // Heartbeat Messaging - Dispatch Ipv4 Udp // ======================================= static void sm_heartbeat_msg_dispatch_ipv4_udp( int selobj, int64_t unused ) { int network_port; SmNetworkAddressT network_address; char interface_name[SM_INTERFACE_NAME_MAX_CHAR]; SmHeartbeatMsgT heartbeat_msg; SmErrorT error; struct msghdr msg_hdr; struct sockaddr_in src_addr; struct iovec iovec = {(void*)&heartbeat_msg, sizeof(heartbeat_msg)}; int bytes_read; memset( _rx_control_buffer, 0, sizeof(_rx_control_buffer) ); memset( &heartbeat_msg, 0, sizeof(SmHeartbeatMsgT) ); memset( &msg_hdr, 0, sizeof(msg_hdr) ); msg_hdr.msg_name = &src_addr; msg_hdr.msg_namelen= sizeof(src_addr); msg_hdr.msg_iov = &iovec; msg_hdr.msg_iovlen = 1; msg_hdr.msg_control = _rx_control_buffer; msg_hdr.msg_controllen = sizeof(_rx_control_buffer); int retry_i; for( retry_i = 5; retry_i != 0; --retry_i ) { bytes_read = recvmsg( selobj, &msg_hdr, MSG_NOSIGNAL | MSG_DONTWAIT ); if( 0 < bytes_read ) { break; } else if( 0 == bytes_read ) { // For connection oriented sockets, this indicates that the peer // has performed an orderly shutdown. return; } else if(( 0 > bytes_read )&&( EINTR != errno )) { DPRINTFE( "Failed to receive message, errno=%s.", strerror( errno ) ); return; } DPRINTFD( "Interrupted while receiving message, retry=%d, errno=%s.", retry_i, strerror( errno ) ); } if( AF_INET == src_addr.sin_family ) { char network_address_str[SM_NETWORK_ADDRESS_MAX_CHAR]; struct in_pktinfo* pkt_info; pkt_info = (struct in_pktinfo*) sm_heartbeat_msg_get_ancillary_data( &msg_hdr, SOL_IP, IP_PKTINFO ); if( NULL == pkt_info ) { DPRINTFD( "No packet information available." ); return; } error = sm_hw_get_if_name( pkt_info->ipi_ifindex, interface_name ); if( SM_OKAY != error ) { if( SM_NOT_FOUND == error ) { DPRINTFD( "Failed to get interface name for interface index " "(%i), error=%s.", pkt_info->ipi_ifindex, sm_error_str(error) ); } else { DPRINTFE( "Failed to get interface name for interface index " "(%i), error=%s.", pkt_info->ipi_ifindex, sm_error_str(error) ); } return; } network_address.type = SM_NETWORK_TYPE_IPV4_UDP; network_address.u.ipv4.sin.s_addr = src_addr.sin_addr.s_addr; network_port = ntohs(src_addr.sin_port); sm_network_address_str( &network_address, network_address_str ); DPRINTFD( "Received message from ip (%s), port (%i) on " "interface (%s).", network_address_str, network_port, interface_name ); } else { DPRINTFE( "Received unsupported network address type (%i).", src_addr.sin_family ); return; } // Once the message is received, pass it to protocol-independent handler sm_heartbeat_msg_dispatch_msg( &heartbeat_msg, &network_address, network_port, interface_name ); } // **************************************************************************** // **************************************************************************** // Heartbeat Messaging - Dispatch IPv6 Udp // ======================================= static void sm_heartbeat_msg_dispatch_ipv6_udp( int selobj, int64_t unused ) { int network_port; SmNetworkAddressT network_address; char interface_name[SM_INTERFACE_NAME_MAX_CHAR]; SmHeartbeatMsgT heartbeat_msg; SmErrorT error; struct msghdr msg_hdr; struct sockaddr_in6 src_addr; struct iovec iovec = {(void*)&heartbeat_msg, sizeof(heartbeat_msg)}; int bytes_read; memset( _rx_control_buffer, 0, sizeof(_rx_control_buffer) ); memset( &heartbeat_msg, 0, sizeof(SmHeartbeatMsgT) ); memset( &msg_hdr, 0, sizeof(msg_hdr) ); msg_hdr.msg_name = &src_addr; msg_hdr.msg_namelen= sizeof(src_addr); msg_hdr.msg_iov = &iovec; msg_hdr.msg_iovlen = 1; msg_hdr.msg_control = _rx_control_buffer; msg_hdr.msg_controllen = sizeof(_rx_control_buffer); int retry_i; for( retry_i = 5; retry_i != 0; --retry_i ) { bytes_read = recvmsg( selobj, &msg_hdr, MSG_NOSIGNAL | MSG_DONTWAIT ); if( 0 < bytes_read ) { break; } else if( 0 == bytes_read ) { // For connection oriented sockets, this indicates that the peer // has performed an orderly shutdown. return; } else if(( 0 > bytes_read )&&( EINTR != errno )) { DPRINTFE( "Failed to receive message, errno=%s.", strerror( errno ) ); return; } DPRINTFD( "Interrupted while receiving message, retry=%d, errno=%s.", retry_i, strerror( errno ) ); } if( AF_INET6 == src_addr.sin6_family ) { char network_address_str[SM_NETWORK_ADDRESS_MAX_CHAR]; struct in6_pktinfo* pkt_info; pkt_info = (struct in6_pktinfo*) sm_heartbeat_msg_get_ancillary_data( &msg_hdr, SOL_IPV6, IPV6_PKTINFO ); if( NULL == pkt_info ) { DPRINTFD( "No packet information available." ); return; } error = sm_hw_get_if_name( pkt_info->ipi6_ifindex, interface_name ); if( SM_OKAY != error ) { if( SM_NOT_FOUND == error ) { DPRINTFD( "Failed to get interface name for interface index " "(%i), error=%s.", pkt_info->ipi6_ifindex, sm_error_str(error) ); } else { DPRINTFE( "Failed to get interface name for interface index " "(%i), error=%s.", pkt_info->ipi6_ifindex, sm_error_str(error) ); } return; } network_address.type = SM_NETWORK_TYPE_IPV6_UDP; network_address.u.ipv6.sin6 = src_addr.sin6_addr; network_port = ntohs(src_addr.sin6_port); sm_network_address_str( &network_address, network_address_str ); DPRINTFD( "Received message from ip (%s), port (%i) on " "interface (%s).", network_address_str, network_port, interface_name ); } else { DPRINTFE( "Received unsupported network address type (%i).", src_addr.sin6_family ); return; } // Once the message is received, pass it to protocol-independent handler sm_heartbeat_msg_dispatch_msg( &heartbeat_msg, &network_address, network_port, interface_name ); } // **************************************************************************** // **************************************************************************** // Heartbeat Messaging - Open Ipv4 UDP Multicast Socket // ==================================================== static SmErrorT sm_heartbeat_msg_open_ipv4_udp_multicast_socket( SmNetworkAddressT* network_address, SmNetworkAddressT* network_multicast, int network_port, char interface_name[], int* multicast_socket ) { int flags; int sock; int result; struct ifreq ifr; struct sockaddr_in addr; SmIpv4AddressT* ipv4_unicast = &(network_address->u.ipv4); SmIpv4AddressT* ipv4_multicast = &(network_multicast->u.ipv4); *multicast_socket = -1; // Create socket. sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); if( 0 > sock ) { DPRINTFE( "Failed to open socket for interface (%s), errno=%s.", interface_name, strerror(errno) ); return( SM_FAILED ); } SmErrorT error = sm_initialize_socket(sock); if( SM_OKAY != error ) { DPRINTFI("Initialize socket on interface %s failed.", interface_name ); return error; } // Bind address to socket. memset( &addr, 0, sizeof(addr) ); addr.sin_family = AF_INET; addr.sin_port = htons(network_port); addr.sin_addr.s_addr = ipv4_multicast->sin.s_addr; result = bind( sock, (struct sockaddr *) &addr, sizeof(addr) ); if( 0 > result ) { DPRINTFE( "Failed to bind multicast address to socket for " "interface (%s), error=%s.", interface_name, strerror( errno ) ); close( sock ); return( SM_FAILED ); } // Set multicast interface on socket. result = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_IF, (struct in_addr*) &(ipv4_unicast->sin.s_addr), sizeof(struct in_addr) ); if( 0 > result ) { DPRINTFE( "Failed to set unicast address on socket for " "interface (%s), error=%s.", interface_name, strerror( errno ) ); close( sock ); return( SM_FAILED ); } // Bind socket to interface. memset(&ifr, 0, sizeof(ifr)); snprintf( ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface_name ); result = setsockopt( sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr) ); if( 0 > result ) { DPRINTFE( "Failed to bind socket to interface (%s), errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } // Set multicast TTL. flags = 1; result = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_TTL, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to set multicast ttl for interface (%s), errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } // Disable looping of sent multicast packets on socket. flags = 0; result = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_LOOP, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to stop looping of multicast messages for " "interface (%s), errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } // Set socket send priority on interface. flags = IPTOS_CLASS_CS6; result = setsockopt( sock, IPPROTO_IP, IP_TOS, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to set socket send priority for interface (%s) " "multicast messages, errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } flags = 6; result = setsockopt( sock, SOL_SOCKET, SO_PRIORITY, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to set socket send priority for interface (%s) " "multicast messages, errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } // Set socket receive packet information. flags = 1; result = setsockopt( sock, SOL_IP, IP_PKTINFO, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to set socket receive packet information for " "interface (%s) multicast messages, errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } *multicast_socket = sock; return( SM_OKAY ); } // **************************************************************************** // **************************************************************************** // Heartbeat Messaging - Open Ipv4 UDP unicast Socket // ==================================================== static SmErrorT sm_heartbeat_msg_open_ipv4_udp_unicast_socket( SmNetworkAddressT* network_address, int network_port, char interface_name[], int* unicast_socket ) { int flags; int sock; int result; struct ifreq ifr; struct sockaddr_in addr; SmIpv4AddressT* ipv4_unicast = &(network_address->u.ipv4); *unicast_socket = -1; // Create socket. sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); if( 0 > sock ) { DPRINTFE( "Failed to open socket for interface (%s), errno=%s.", interface_name, strerror(errno) ); return( SM_FAILED ); } SmErrorT error = sm_initialize_socket(sock); if( SM_OKAY != error ) { DPRINTFI("Initialize socket on interface %s failed.", interface_name ); return error; } // Bind address to socket. memset( &addr, 0, sizeof(addr) ); addr.sin_family = AF_INET; addr.sin_port = htons(network_port); addr.sin_addr.s_addr = ipv4_unicast->sin.s_addr; result = bind( sock, (struct sockaddr *) &addr, sizeof(addr) ); if( 0 > result ) { DPRINTFE( "Failed to bind address to socket for " "interface (%s), error=%s.", interface_name, strerror( errno ) ); close( sock ); return( SM_FAILED ); } // Bind socket to interface. memset(&ifr, 0, sizeof(ifr)); snprintf( ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface_name ); result = setsockopt( sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr) ); if( 0 > result ) { DPRINTFE( "Failed to bind socket to interface (%s), errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } // Set socket send priority on interface. flags = IPTOS_CLASS_CS6; result = setsockopt( sock, IPPROTO_IP, IP_TOS, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to set socket send priority for interface (%s) " "messages, errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } flags = 6; result = setsockopt( sock, SOL_SOCKET, SO_PRIORITY, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to set socket send priority for interface (%s) " "messages, errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } // Set socket receive packet information. flags = 1; result = setsockopt( sock, SOL_IP, IP_PKTINFO, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to set socket receive packet information for " "interface (%s) messages, errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } *unicast_socket = sock; return( SM_OKAY ); } // **************************************************************************** // **************************************************************************** // Heartbeat Messaging - Open Ipv6 UDP Multicast Socket // ==================================================== static SmErrorT sm_heartbeat_msg_open_ipv6_udp_multicast_socket( SmNetworkAddressT* network_address, SmNetworkAddressT* network_multicast, int network_port, char interface_name[], int* multicast_socket ) { int flags; int sock; int result; int64_t ifindex; struct ifreq ifr; struct sockaddr_in6 addr; SmIpv6AddressT* ipv6_multicast = &(network_multicast->u.ipv6); *multicast_socket = -1; // Create socket. sock = socket( AF_INET6, SOCK_DGRAM, IPPROTO_UDP ); if( 0 > sock ) { DPRINTFE( "Failed to open socket for interface (%s), errno=%s.", interface_name, strerror(errno) ); return( SM_FAILED ); } SmErrorT error = sm_initialize_socket(sock); if( SM_OKAY != error ) { DPRINTFI("Initialize socket on interface %s failed.", interface_name ); return error; } // Bind address to socket. memset( &addr, 0, sizeof(addr) ); addr.sin6_family = AF_INET6; addr.sin6_port = htons(network_port); addr.sin6_addr = ipv6_multicast->sin6; result = bind( sock, (struct sockaddr *) &addr, sizeof(addr) ); if( 0 > result ) { DPRINTFE( "Failed to bind multicast address to socket for " "interface (%s), error=%s.", interface_name, strerror( errno ) ); close( sock ); return( SM_FAILED ); } // Set multicast interface on socket. ifindex = if_nametoindex( interface_name ); result = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex) ); if( 0 > result ) { DPRINTFE( "Failed to set unicast address on socket for " "interface (%s), error=%s.", interface_name, strerror( errno ) ); close( sock ); return( SM_FAILED ); } // Bind socket to interface. memset( &ifr, 0, sizeof(ifr) ); snprintf( ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface_name ); result = setsockopt( sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr) ); if( 0 > result ) { DPRINTFE( "Failed to bind socket to interface (%s), errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } // Set multicast TTL. flags = 1; result = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to set multicast ttl for interface (%s), errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } // Disable looping of sent multicast packets on socket. flags = 0; result = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to stop looping of multicast messages for " "interface (%s), errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } // Set socket send priority on interface. flags = IPTOS_CLASS_CS6; result = setsockopt( sock, IPPROTO_IPV6, IPV6_TCLASS, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to set socket send priority for interface (%s) " "multicast messages, errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } flags = 6; result = setsockopt( sock, SOL_SOCKET, SO_PRIORITY, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to set socket send priority for interface (%s) " "multicast messages, errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } // Set socket receive packet information. flags = 1; result = setsockopt( sock, SOL_IPV6, IPV6_RECVPKTINFO, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to set socket receive packet information for " "interface (%s) multicast messages, errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } *multicast_socket = sock; return( SM_OKAY ); } // **************************************************************************** // **************************************************************************** // Heartbeat Messaging - Open Ipv6 UDP Unicast Socket // ==================================================== static SmErrorT sm_heartbeat_msg_open_ipv6_udp_unicast_socket( SmNetworkAddressT* network_address, int network_port, char interface_name[], int* multicast_socket ) { int flags; int sock; int result; struct ifreq ifr; struct sockaddr_in6 addr; SmIpv6AddressT* ipv6_unicast = &(network_address->u.ipv6); *multicast_socket = -1; // Create socket. sock = socket( AF_INET6, SOCK_DGRAM, IPPROTO_UDP ); if( 0 > sock ) { DPRINTFE( "Failed to open socket for interface (%s), errno=%s.", interface_name, strerror(errno) ); return( SM_FAILED ); } SmErrorT error = sm_initialize_socket(sock); if( SM_OKAY != error ) { DPRINTFI("Initialize socket on interface %s failed.", interface_name ); return error; } // Bind address to socket. memset( &addr, 0, sizeof(addr) ); addr.sin6_family = AF_INET6; addr.sin6_port = htons(network_port); addr.sin6_addr = ipv6_unicast->sin6; result = bind( sock, (struct sockaddr *) &addr, sizeof(addr) ); if( 0 > result ) { DPRINTFE( "Failed to bind multicast address to socket for " "interface (%s), error=%s.", interface_name, strerror( errno ) ); close( sock ); return( SM_FAILED ); } if( 0 > result ) { DPRINTFE( "Failed to set unicast address on socket for " "interface (%s), error=%s.", interface_name, strerror( errno ) ); close( sock ); return( SM_FAILED ); } // Bind socket to interface. memset( &ifr, 0, sizeof(ifr) ); snprintf( ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface_name ); result = setsockopt( sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr) ); if( 0 > result ) { DPRINTFE( "Failed to bind socket to interface (%s), errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } // Set multicast TTL. flags = 1; result = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to set multicast ttl for interface (%s), errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } // Disable looping of sent multicast packets on socket. flags = 0; result = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to stop looping of multicast messages for " "interface (%s), errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } // Set socket send priority on interface. flags = IPTOS_CLASS_CS6; result = setsockopt( sock, IPPROTO_IPV6, IPV6_TCLASS, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to set socket send priority for interface (%s) " "multicast messages, errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } flags = 6; result = setsockopt( sock, SOL_SOCKET, SO_PRIORITY, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to set socket send priority for interface (%s) " "multicast messages, errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } // Set socket receive packet information. flags = 1; result = setsockopt( sock, SOL_IPV6, IPV6_RECVPKTINFO, &flags, sizeof(flags) ); if( 0 > result ) { DPRINTFE( "Failed to set socket receive packet information for " "interface (%s) multicast messages, errno=%s.", interface_name, strerror(errno) ); close( sock ); return( SM_FAILED ); } *multicast_socket = sock; return( SM_OKAY ); } // **************************************************************************** // **************************************************************************** // Heartbeat Messaging - Open Sockets // ================================== SmErrorT sm_heartbeat_msg_open_sockets( SmNetworkTypeT network_type, SmNetworkAddressT* network_address, SmNetworkAddressT* network_multicast, int network_port, char interface_name[], int* multicast_socket, int* unicast_socket ) { SmErrorT error; if( -1 < *multicast_socket ) { DPRINTFD( "Multicast socket for interface (%s) already opened.", interface_name ); return( SM_OKAY ); } *multicast_socket = -1; if( SM_NETWORK_TYPE_IPV4_UDP == network_type ) { int socket_fd; if ( network_multicast->type != SM_NETWORK_TYPE_NIL ) { error = sm_heartbeat_msg_open_ipv4_udp_multicast_socket( network_address, network_multicast, network_port, interface_name, &socket_fd ); if( SM_OKAY != error ) { DPRINTFE( "Failed to open multicast socket for interface (%s), " "error=%s.", interface_name, sm_error_str(error) ); return( error ); } error = sm_selobj_register( socket_fd, sm_heartbeat_msg_dispatch_ipv4_udp, (int64_t) 0 ); if( SM_OKAY != error ) { DPRINTFE( "Failed to register selection object for " "interface (%s), error=%s.", interface_name, sm_error_str(error) ); close( socket_fd ); return( SM_FAILED ); } *multicast_socket = socket_fd; } else { DPRINTFD("Multicast ip for %s is not configured.", interface_name); *multicast_socket = -1; } if( NULL != unicast_socket ) { error = sm_heartbeat_msg_open_ipv4_udp_unicast_socket( network_address, network_port, interface_name, &socket_fd ); if( SM_OKAY != error ) { DPRINTFE( "Failed to open multicast socket for interface (%s), " "error=%s.", interface_name, sm_error_str(error) ); return( error ); } error = sm_selobj_register( socket_fd, sm_heartbeat_msg_dispatch_ipv4_udp, (int64_t) 0); if( SM_OKAY != error ) { DPRINTFE( "Failed to register selection object for " "interface (%s), error=%s.", interface_name, sm_error_str(error) ); close( socket_fd ); return( SM_FAILED ); } *unicast_socket = socket_fd; } return( SM_OKAY ); } else if( SM_NETWORK_TYPE_IPV6_UDP == network_type ) { int socket_fd; if ( network_multicast->type != SM_NETWORK_TYPE_NIL ) { error = sm_heartbeat_msg_open_ipv6_udp_multicast_socket( network_address, network_multicast, network_port, interface_name, &socket_fd ); if( SM_OKAY != error ) { DPRINTFE( "Failed to open multicast socket for interface (%s), " "error=%s.", interface_name, sm_error_str(error) ); return( error ); } error = sm_selobj_register( socket_fd, sm_heartbeat_msg_dispatch_ipv6_udp, 0 ); if( SM_OKAY != error ) { DPRINTFE( "Failed to register selection object for " "interface (%s), error=%s.", interface_name, sm_error_str(error) ); close( socket_fd ); return( SM_FAILED ); } *multicast_socket = socket_fd; } else { DPRINTFD("Multicast ip for %s is not configured.", interface_name); *multicast_socket = -1; } if( NULL != unicast_socket ) { error = sm_heartbeat_msg_open_ipv6_udp_unicast_socket( network_address, network_port, interface_name, &socket_fd ); if( SM_OKAY != error ) { DPRINTFE( "Failed to open unicast socket for interface (%s), " "error=%s.", interface_name, sm_error_str(error) ); return( error ); } error = sm_selobj_register( socket_fd, sm_heartbeat_msg_dispatch_ipv6_udp, 0 ); if( SM_OKAY != error ) { DPRINTFE( "Failed to register selection object for " "interface (%s), error=%s.", interface_name, sm_error_str(error) ); close( socket_fd ); return( SM_FAILED ); } *unicast_socket = socket_fd; } return( SM_OKAY ); } else { DPRINTFE( "Unsupported network type (%s).", sm_network_type_str( network_type ) ); return( SM_FAILED ); } } // **************************************************************************** // **************************************************************************** // Heartbeat Messaging - Close Sockets // =================================== SmErrorT sm_heartbeat_msg_close_sockets( int* multicast_socket ) { SmErrorT error; if( -1 < *multicast_socket ) { error = sm_selobj_deregister( *multicast_socket ); if( SM_OKAY != error ) { DPRINTFE( "Failed to deregister selection object, error=%s.", sm_error_str(error) ); } close( *multicast_socket ); *multicast_socket = -1; } return( SM_OKAY ); } // **************************************************************************** // **************************************************************************** // Heartbeat Message - Initialize // ============================== SmErrorT sm_heartbeat_msg_initialize( void ) { return( SM_OKAY ); } // **************************************************************************** // **************************************************************************** // Heartbeart Message - Finalize // ============================= SmErrorT sm_heartbeat_msg_finalize( void ) { return( SM_OKAY ); } // ****************************************************************************