/****************************************************************************** * Copyright (c) 2000-2021 Ericsson Telecom AB * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html * * Contributors: * Peter Dimitrov- initial implementation and initial documentation * Adam Delic * Eduard Czimbalmos * Endre Kulcsar * Gabor Bettesch * Gabor Szalai * Tamas Buti * Zoltan Medve ******************************************************************************/ // // File: SCTPasp_PT.cc // Description: SCTPasp test port source // Rev: // Prodnr: CNL 113 469 // #include "SCTPasp_PT.hh" #include #include #include #include #include #include #include #include #include #include #define BUFLEN 1024 #define MAP_LENGTH 10 #ifdef SCTP_ADAPTION_LAYER #ifdef LKSCTP_1_0_7 #undef LKSCTP_1_0_7 #error LKSCTP_1_0_7 defined but the lksctp older than 1.0.7. Use only -DUSE_SCTP, version is automatically selected #endif #ifdef LKSCTP_1_0_9 #error LKSCTP_1_0_9 defined but the lksctp older than 1.0.7. Use only -DUSE_SCTP, version is automatically selected #undef LKSCTP_1_0_9 #endif #else // 1.0.7 or newer #ifdef SCTP_AUTH_CHUNK // 1.0.9 or newer #ifdef LKSCTP_1_0_7 #undef LKSCTP_1_0_7 #error LKSCTP_1_0_7 defined but the lksctp newer than 1.0.7. Use only -DUSE_SCTP, version is automatically selected #endif #ifndef LKSCTP_1_0_9 #define LKSCTP_1_0_9 #endif #else // 1.0.7 #ifdef LKSCTP_1_0_9 #undef LKSCTP_1_0_9 #error LKSCTP_1_0_9 defined but the lksctp older than 1.0.9. Use only -DUSE_SCTP, version is automatically selected #endif #ifndef LKSCTP_1_0_7 #define LKSCTP_1_0_7 #endif #endif #endif namespace SCTPasp__PortType { /* is any of the bytes from offset .. u8_size in 'u8' non-zero? return offset or -1 if all zero */ static int byte_nonzero(const uint8_t *u8, unsigned int offset, unsigned int u8_size) { int j; for (j = offset; j < u8_size; j++) { if (u8[j] != 0) return j; } return -1; } static int sctp_sockopt_event_subscribe_size = 0; static int determine_sctp_sockopt_event_subscribe_size(void) { uint8_t buf[256]; socklen_t buf_len = sizeof(buf); int sd, rc; /* only do this once */ if (sctp_sockopt_event_subscribe_size != 0) return 0; sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP); if (sd < 0) return sd; rc = getsockopt(sd, IPPROTO_SCTP, SCTP_EVENTS, buf, &buf_len); close(sd); if (rc < 0) return rc; sctp_sockopt_event_subscribe_size = buf_len; //LOGP(DLINP, LOGL_INFO, "sizes of 'struct sctp_event_subscribe': compile-time %zu, kernel: %u\n", // sizeof(struct sctp_event_subscribe), sctp_sockopt_event_subscribe_size); return 0; } /* Attempt to work around Linux kernel ABI breakage * * The Linux kernel ABI for the SCTP_EVENTS socket option has been broken repeatedly. * - until commit 35ea82d611da59f8bea44a37996b3b11bb1d3fd7 ( kernel < 4.11), the size is 10 bytes * - in 4.11 it is 11 bytes * - in 4.12 .. 5.4 it is 13 bytes * - in kernels >= 5.5 it is 14 bytes * * This wouldn't be a problem if the kernel didn't have a "stupid" assumption that the structure * size passed by userspace will match 1:1 the length of the structure at kernel compile time. In * an ideal world, it would just use the known first bytes and assume the remainder is all zero. * But as it doesn't do that, let's try to work around this */ static int sctp_setsockopt_events_linux_workaround(int fd, const struct sctp_event_subscribe *event) { const unsigned int compiletime_size = sizeof(*event); int rc; if (determine_sctp_sockopt_event_subscribe_size() < 0) { //LOGP(DLINP, LOGL_ERROR, "Cannot determine SCTP_EVENTS socket option size\n"); return -1; } if (compiletime_size == sctp_sockopt_event_subscribe_size) { /* no kernel workaround needed */ return setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, event, compiletime_size); } else if (compiletime_size < sctp_sockopt_event_subscribe_size) { /* we are using an older userspace with a more modern kernel and hence need * to pad the data */ uint8_t buf[sctp_sockopt_event_subscribe_size]; memcpy(buf, event, compiletime_size); memset(buf + sizeof(*event), 0, sctp_sockopt_event_subscribe_size - compiletime_size); return setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, buf, sctp_sockopt_event_subscribe_size); } else /* if (compiletime_size > sctp_sockopt_event_subscribe_size) */ { /* we are using a newer userspace with an older kernel and hence need to truncate * the data - but only if the caller didn't try to enable any of the events of the * truncated portion */ rc = byte_nonzero((const uint8_t *)event, sctp_sockopt_event_subscribe_size, compiletime_size); if (rc >= 0) { //LOGP(DLINP, LOGL_ERROR, "Kernel only supports sctp_event_subscribe of %u bytes, " // "but caller tried to enable more modern event at offset %u\n", // sctp_sockopt_event_subscribe_size, rc); return -1; } return setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, event, sctp_sockopt_event_subscribe_size); } } struct SCTPasp__PT_PROVIDER::fd_map_item { // used by map operations int fd; // socket descriptor boolean erased; boolean processing_message; // if true only part of the message is received boolean einprogress; // connection establishment is in progress void * buf; // buffer ssize_t buflen; // length of the buffer ssize_t nr; // number of received bytes struct sockaddr_storage sin; // storing remote address socklen_t saLen; }; struct SCTPasp__PT_PROVIDER::fd_map_server_item // server item { // used by map operations int fd; // socket descriptor boolean erased; CHARSTRING* local_IP_address; unsigned short local_port; }; SCTPasp__PT_PROVIDER::SCTPasp__PT_PROVIDER(const char *par_port_name) : PORT(par_port_name) { simple_mode = FALSE; reconnect = FALSE; reconnect_max_attempts = 6; server_mode = FALSE; debug = FALSE; server_backlog = 1; local_IP_address = "0.0.0.0"; (void) memset(&initmsg, 0, sizeof(struct sctp_initmsg)); initmsg.sinit_num_ostreams = 64; initmsg.sinit_max_instreams = 64; initmsg.sinit_max_attempts = 0; initmsg.sinit_max_init_timeo = 0; (void) memset(&events, 0, sizeof (events)); events.sctp_data_io_event = TRUE; events.sctp_association_event = TRUE; events.sctp_address_event = TRUE; events.sctp_send_failure_event = TRUE; events.sctp_peer_error_event = TRUE; events.sctp_shutdown_event = TRUE; events.sctp_partial_delivery_event = TRUE; #if defined(LKSCTP_1_0_7) || defined(LKSCTP_1_0_9) events.sctp_adaptation_layer_event = TRUE; #else events.sctp_adaption_layer_event = TRUE; #endif local_port_is_present = FALSE; peer_IP_address_is_present = FALSE; peer_port_is_present = FALSE; fd_map=NULL; list_len=0; fd_map_server=NULL; list_len_server=0; fd = -1; local_port=-1; peer_port=-1; receiving_fd=-1; } SCTPasp__PT_PROVIDER::~SCTPasp__PT_PROVIDER() { for(int i=0;i=0) ) reconnect_max_attempts = value; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be positive integer!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "server_mode") == 0) { if (strcasecmp(parameter_value,"yes") == 0) server_mode = TRUE; else if(strcasecmp(parameter_value,"no") == 0) server_mode = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. Only yes and no can be used!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "debug") == 0) { if (strcasecmp(parameter_value,"yes") == 0) debug = TRUE; else if(strcasecmp(parameter_value,"no") == 0) debug = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. Only yes and no can be used!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "server_backlog") == 0) { int value; if ( (sscanf(parameter_value, "%d", &value) == 1) && (value>=0) ) server_backlog = value; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be positive integer!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "local_IP_address") == 0) { local_IP_address = parameter_value; } else if(strcmp(parameter_name, "local_port") == 0) { unsigned short value; if (sscanf(parameter_value, "%hu", &value) == 1) { local_port = value; local_port_is_present = TRUE; } else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be positive integer!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "peer_IP_address") == 0) { peer_IP_address = parameter_value; peer_IP_address_is_present = TRUE; } else if(strcmp(parameter_name, "peer_port") == 0) { unsigned short value; if (sscanf(parameter_value, "%hu", &value) == 1) { peer_port = value; peer_port_is_present = TRUE; } else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be positive integer!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sinit_num_ostreams") == 0) { long value; if ( (sscanf(parameter_value, "%ld", &value) == 1) && (value>=0) ) initmsg.sinit_num_ostreams = value; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be positive integer!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sinit_max_instreams") == 0) { long value; if ( (sscanf(parameter_value, "%ld", &value) == 1) && (value>=0) ) initmsg.sinit_max_instreams = value; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be positive integer!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sinit_max_attempts") == 0) { long value; if ( (sscanf(parameter_value, "%ld", &value) == 1) && (value>=0) ) initmsg.sinit_max_attempts = value; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be positive integer!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sinit_max_init_timeo") == 0) { long value; if ( (sscanf(parameter_value, "%ld", &value) == 1) && (value>=0) ) initmsg.sinit_max_init_timeo = value; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be positive integer!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sctp_association_event") == 0) { if (strcasecmp(parameter_value,"enabled") == 0) events.sctp_association_event = TRUE; else if(strcasecmp(parameter_value,"disabled") == 0) events.sctp_association_event = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be enabled or disabled!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sctp_address_event") == 0) { if (strcasecmp(parameter_value,"enabled") == 0) events.sctp_address_event = TRUE; else if(strcasecmp(parameter_value,"disabled") == 0) events.sctp_address_event = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be enabled or disabled!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sctp_send_failure_event") == 0) { if (strcasecmp(parameter_value,"enabled") == 0) events.sctp_send_failure_event = TRUE; else if(strcasecmp(parameter_value,"disabled") == 0) events.sctp_send_failure_event = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be enabled or disabled!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sctp_peer_error_event") == 0) { if (strcasecmp(parameter_value,"enabled") == 0) events.sctp_peer_error_event = TRUE; else if(strcasecmp(parameter_value,"disabled") == 0) events.sctp_peer_error_event = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be enabled or disabled!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sctp_shutdown_event") == 0) { if (strcasecmp(parameter_value,"enabled") == 0) events.sctp_shutdown_event = TRUE; else if(strcasecmp(parameter_value,"disabled") == 0) events.sctp_shutdown_event = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be enabled or disabled!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sctp_partial_delivery_event") == 0) { if (strcasecmp(parameter_value,"enabled") == 0) events.sctp_partial_delivery_event = TRUE; else if(strcasecmp(parameter_value,"disabled") == 0) events.sctp_partial_delivery_event = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be enabled or disabled!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sctp_adaption_layer_event") == 0) { if (strcasecmp(parameter_value,"enabled") == 0) #if defined(LKSCTP_1_0_7) || defined(LKSCTP_1_0_9) events.sctp_adaptation_layer_event = TRUE; #else events.sctp_adaption_layer_event = TRUE; #endif else if(strcasecmp(parameter_value,"disabled") == 0) #if defined(LKSCTP_1_0_7) || defined(LKSCTP_1_0_9) events.sctp_adaptation_layer_event = FALSE; #else events.sctp_adaption_layer_event = FALSE; #endif else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be enabled or disabled!" , parameter_value, parameter_name); } else TTCN_warning("%s: unknown & unhandled parameter: %s", get_name(), parameter_name); errno = 0; } void SCTPasp__PT_PROVIDER::Handle_Fd_Event_Writable(int my_fd){ int i= map_get_item(my_fd); if(i!=-1 && !simple_mode && !fd_map[i].erased && fd_map[i].einprogress ) { if (connect(fd_map[i].fd, (struct sockaddr *)&fd_map[i].sin, fd_map[i].saLen) == -1) { Handler_Remove_Fd_Write(fd_map[i].fd); if(errno == EISCONN) { SCTPasp__Types::ASP__SCTP__RESULT asp_sctp_result; asp_sctp_result.client__id() = fd_map[i].fd; asp_sctp_result.error__status() = FALSE; asp_sctp_result.error__message() = OMIT_VALUE; incoming_message(asp_sctp_result); fd_map[i].einprogress = FALSE; Handler_Add_Fd_Read(fd_map[i].fd); log("Connection successfully established to (%s):(%d)",(const char*)peer_IP_address, peer_port); } else { close(fd_map[i].fd); fd = -1; TTCN_warning("Connect error!"); SCTPasp__Types::ASP__SCTP__RESULT asp_sctp_result; asp_sctp_result.client__id() = fd_map[i].fd; asp_sctp_result.error__status() = TRUE; asp_sctp_result.error__message() = strerror(errno); incoming_message(asp_sctp_result); map_delete_item_fd(fd_map[i].fd); errno = 0; log("Connection establishment to (%s):(%d) failed !", (const char*)peer_IP_address, peer_port); } } } } void SCTPasp__PT_PROVIDER::Handle_Fd_Event_Error(int my_fd){ Handle_Fd_Event_Readable(my_fd); } CHARSTRING get_ip(struct sockaddr_storage *sa){ char ret_val[INET6_ADDRSTRLEN]; ret_val[0]='\0'; if(sa->ss_family == AF_INET){ struct sockaddr_in* sa4=(struct sockaddr_in*)sa; inet_ntop(AF_INET,&(sa4->sin_addr),ret_val,INET6_ADDRSTRLEN); } else if(sa->ss_family == AF_INET6) { struct sockaddr_in6* sa6=(struct sockaddr_in6*)sa; inet_ntop(AF_INET6,&(sa6->sin6_addr),ret_val,INET6_ADDRSTRLEN); } return CHARSTRING(ret_val); } int get_port(struct sockaddr_storage *sa){ if(sa->ss_family == AF_INET){ struct sockaddr_in* sa4=(struct sockaddr_in*)sa; return ntohs(sa4->sin_port); } else if(sa->ss_family == AF_INET6) { struct sockaddr_in6* sa6=(struct sockaddr_in6*)sa; return ntohs(sa6->sin6_port); } return 0; } void SCTPasp__PT_PROVIDER::Handle_Fd_Event_Readable(int my_fd){ // Accepting new client if(!simple_mode) { for(int i=0;isinfo_ppid); INTEGER i_ppid; if (ui <= (unsigned long)INT_MAX) i_ppid = ui; else { char sbuf[16]; sprintf(sbuf, "%u", ui); i_ppid = INTEGER(sbuf); } incoming_message(SCTPasp__Types::ASP__SCTP( INTEGER(receiving_fd), INTEGER(sri->sinfo_stream), i_ppid, OCTETSTRING(fd_map[i].nr,(const unsigned char *)fd_map[i].buf))); } Free(fd_map[i].buf); fd_map[i].buf = NULL; break; case PARTIAL_RECEIVE: fd_map[i].processing_message = TRUE; break; case EOF_OR_ERROR: if (!server_mode) fd = -1; // setting closed socket to -1 in client mode (and reconnect mode) map_delete_item(i); if (events.sctp_association_event) incoming_message(SCTPasp__Types::ASP__SCTP__ASSOC__CHANGE( INTEGER(receiving_fd), SCTPasp__Types::SAC__STATE(SCTP_COMM_LOST))); log("getmsg() returned with NULL. Socket is closed."); if (reconnect) forced_reconnect(reconnect_max_attempts); break; }//endswitch }// endif } void SCTPasp__PT_PROVIDER::user_map(const char *system_port) { log("Calling user_map(%s).",system_port); if(simple_mode) { if ( server_mode && reconnect ) { error("user_map(): server mode and reconnect mode are mutually exclusive!"); } if ( server_mode && !local_port_is_present ) { error("user_map(): in server mode local_port must be defined!"); } // Server mode: turns on listening if (server_mode) { log("Running in SERVER_MODE."); struct sockaddr_storage sa; socklen_t saLen=sizeof(sa); int sock_type=fill_addr_struct(local_IP_address,local_port,&sa,saLen); fd=create_socket(sock_type); if(bind(fd,(const struct sockaddr *)&sa,saLen)!=0){ error("bind failed: %d, %s", errno, strerror(errno)); } if (listen(fd, server_backlog) == -1) error("Listen error!"); log("Listening @ (%s):(%d)", (const char*)local_IP_address, local_port); Handler_Add_Fd_Read(fd); } else if (reconnect) { log("Running in RECONNECT MODE."); forced_reconnect(reconnect_max_attempts+1); } else { log("Running in CLIENT MODE."); } } else { log("Running in NORMAL MODE."); } log("Leaving user_map()."); } void SCTPasp__PT_PROVIDER::user_unmap(const char *system_port) { log("Calling user_unmap(%s).",system_port); if(!simple_mode) { for(int i=0;icmsg_len = sizeof (*cmsg) + sizeof (*sri); cmsg->cmsg_level = IPPROTO_SCTP; cmsg->cmsg_type = SCTP_SNDRCV; sri->sinfo_stream = (int) send_par.sinfo__stream(); int target; if(!simple_mode) { if (!send_par.client__id().ispresent()) error("In NORMAL mode the client_id field of ASP_SCTP should be set to a valid value and not to omit!"); target = (int) (const INTEGER&) send_par.client__id(); if ( (map_get_item(target)==-1) && (map_get_item_server(target)==-1)) error("Bad client id! %d",target); } else { if (server_mode) { if (!send_par.client__id().ispresent()) error("In server mode the client_id field of ASP_SCTP should be set to a valid value and not to omit!"); } else // client mode { if (send_par.client__id().ispresent()) error("In client mode the client_id field of ASP_SCTP should be set to OMIT!"); } target = fd; if (server_mode) target = (int) (const INTEGER&) send_par.client__id(); if (map_get_item(target)==-1) error("Bad client id! %d",target); } uint32_t ui; if (send_par.sinfo__ppid().get_val().is_native() && send_par.sinfo__ppid() > 0) ui = (int)send_par.sinfo__ppid(); else { OCTETSTRING os = int2oct(send_par.sinfo__ppid(), 4); unsigned char* p = (unsigned char*)&ui; *(p++) = os[3].get_octet(); *(p++) = os[2].get_octet(); *(p++) = os[1].get_octet(); *(p++) = os[0].get_octet(); } sri->sinfo_ppid = htonl(ui); log("Sending SCTP message to file descriptor %d.", target); if (sendmsg(target, &msg, 0) < 0) { SCTPasp__Types::ASP__SCTP__SENDMSG__ERROR asp_sctp_sendmsg_error; if (server_mode) asp_sctp_sendmsg_error.client__id() = target; else asp_sctp_sendmsg_error.client__id() = OMIT_VALUE; asp_sctp_sendmsg_error.sinfo__stream() = send_par.sinfo__stream(); asp_sctp_sendmsg_error.sinfo__ppid() = send_par.sinfo__ppid(); asp_sctp_sendmsg_error.data() = send_par.data(); incoming_message(asp_sctp_sendmsg_error); TTCN_warning("Sendmsg error! Strerror=%s", strerror(errno)); errno = 0; } log("Leaving outgoing_send (ASP_SCTP)."); } SCTPasp__PT_PROVIDER::return_value_t SCTPasp__PT_PROVIDER::getmsg(int fd, struct msghdr *msg) { log("Calling getmsg()."); int index = map_get_item(fd); if ( !fd_map[index].processing_message ) fd_map[index].nr = 0; ssize_t value = recvmsg(fd, msg, 0); if (value <= 0) // EOF or error { log("Leaving getmsg(): EOF or error."); errno = 0; return EOF_OR_ERROR; } fd_map[index].nr += value; log("getmsg(): [%d] bytes received. Receiving buffer now has [%d] bytes.", value, fd_map[index].nr); // Whole message is received, return it. if (msg->msg_flags & MSG_EOR) { log("Leaving getmsg(): whole message is received."); return WHOLE_MESSAGE_RECEIVED; } // Maybe we need a bigger buffer, do realloc(). if (fd_map[index].buflen == fd_map[index].nr) { log("getmsg(): resizing receiving buffer: [%d] bytes -> [%d] bytes", fd_map[index].buflen, (fd_map[index].buflen * 2)); fd_map[index].buf = Realloc(fd_map[index].buf, fd_map[index].buflen * 2); fd_map[index].buflen *= 2; } log("Leaving getmsg(): part of the message is received."); return PARTIAL_RECEIVE; } void SCTPasp__PT_PROVIDER::handle_event(void *buf) { union sctp_notification *snp; snp = (sctp_notification *)buf; switch (snp->sn_header.sn_type) { case SCTP_ASSOC_CHANGE: { log("incoming SCTP_ASSOC_CHANGE event."); struct sctp_assoc_change *sac; sac = &snp->sn_assoc_change; // #ifdef LKSCTP_1_0_7 SCTPasp__Types::SAC__STATE sac_state_ttcn; switch(sac->sac_state) { case SCTP_COMM_UP: sac_state_ttcn = SCTPasp__Types::SAC__STATE::SCTP__COMM__UP; break; case SCTP_COMM_LOST: sac_state_ttcn = SCTPasp__Types::SAC__STATE::SCTP__COMM__LOST; break; case SCTP_RESTART: sac_state_ttcn = SCTPasp__Types::SAC__STATE::SCTP__RESTART; break; case SCTP_SHUTDOWN_COMP: sac_state_ttcn = SCTPasp__Types::SAC__STATE::SCTP__SHUTDOWN__COMP; break; case SCTP_CANT_STR_ASSOC: sac_state_ttcn = SCTPasp__Types::SAC__STATE::SCTP__CANT__STR__ASSOC; break; default: sac_state_ttcn = SCTPasp__Types::SAC__STATE::SCTP__UNKNOWN__SAC__STATE; TTCN_warning("Unexpected sac_state value received %d", sac->sac_state); break; } // #endif if(sac->sac_state == SCTP_COMM_LOST) { if(simple_mode) { if (!server_mode) fd = -1; // setting closed socket to -1 in client mode (and reconnect mode) map_delete_item_fd(receiving_fd); } else { map_delete_item_fd(receiving_fd); map_delete_item_fd_server(receiving_fd); } } if (events.sctp_association_event) incoming_message(SCTPasp__Types::ASP__SCTP__ASSOC__CHANGE( INTEGER(receiving_fd), sac_state_ttcn )); if(simple_mode) { if (reconnect && (sac->sac_state == SCTP_COMM_LOST) ) forced_reconnect(reconnect_max_attempts); } break; } case SCTP_PEER_ADDR_CHANGE:{ log("incoming SCTP_PEER_ADDR_CHANGE event."); struct sctp_paddr_change *spc; spc = &snp->sn_paddr_change; // #ifdef LKSCTP_1_0_7 SCTPasp__Types::SPC__STATE spc_state_ttcn; switch(spc->spc_state) { case SCTP_ADDR_AVAILABLE: spc_state_ttcn = SCTPasp__Types::SPC__STATE::SCTP__ADDR__AVAILABLE; break; case SCTP_ADDR_UNREACHABLE: spc_state_ttcn = SCTPasp__Types::SPC__STATE::SCTP__ADDR__UNREACHABLE; break; case SCTP_ADDR_REMOVED: spc_state_ttcn = SCTPasp__Types::SPC__STATE::SCTP__ADDR__REMOVED; break; case SCTP_ADDR_ADDED: spc_state_ttcn = SCTPasp__Types::SPC__STATE::SCTP__ADDR__ADDED; break; case SCTP_ADDR_MADE_PRIM: spc_state_ttcn = SCTPasp__Types::SPC__STATE::SCTP__ADDR__MADE__PRIM; break; #if defined(LKSCTP_1_0_7) || defined(LKSCTP_1_0_9) case SCTP_ADDR_CONFIRMED: spc_state_ttcn = SCTPasp__Types::SPC__STATE::SCTP__ADDR__CONFIRMED; break; #endif default: spc_state_ttcn = SCTPasp__Types::SPC__STATE::SCTP__UNKNOWN__SPC__STATE; TTCN_warning("Unexpected spc_state value received %d", spc->spc_state); break; } // #endif if (events.sctp_address_event) incoming_message(SCTPasp__Types::ASP__SCTP__PEER__ADDR__CHANGE( INTEGER(receiving_fd), spc_state_ttcn )); break; } case SCTP_REMOTE_ERROR: log("incoming SCTP_REMOTE_ERROR event."); //struct sctp_remote_error *sre; //sre = &snp->sn_remote_error; if (events.sctp_peer_error_event) incoming_message(SCTPasp__Types::ASP__SCTP__REMOTE__ERROR(INTEGER(receiving_fd))); break; case SCTP_SEND_FAILED: log("incoming SCTP_SEND_FAILED event."); //struct sctp_send_failed *ssf; //ssf = &snp->sn_send_failed; if (events.sctp_send_failure_event) incoming_message(SCTPasp__Types::ASP__SCTP__SEND__FAILED(INTEGER(receiving_fd))); break; case SCTP_SHUTDOWN_EVENT: log("incoming SCTP_SHUTDOWN_EVENT event."); //struct sctp_shutdown_event *sse; //sse = &snp->sn_shutdown_event; if (events.sctp_shutdown_event) incoming_message(SCTPasp__Types::ASP__SCTP__SHUTDOWN__EVENT(INTEGER(receiving_fd))); break; #if defined(LKSCTP_1_0_7) || defined(LKSCTP_1_0_9) case SCTP_ADAPTATION_INDICATION: log("incoming SCTP_ADAPTION_INDICATION event."); //struct sctp_adaptation_event *sai; //sai = &snp->sn_adaptation_event; if (events.sctp_adaptation_layer_event) incoming_message(SCTPasp__Types::ASP__SCTP__ADAPTION__INDICATION(INTEGER(receiving_fd))); break; #else case SCTP_ADAPTION_INDICATION: log("incoming SCTP_ADAPTION_INDICATION event."); //struct sctp_adaption_event *sai; //sai = &snp->sn_adaption_event; if (events.sctp_adaption_layer_event) incoming_message(SCTPasp__Types::ASP__SCTP__ADAPTION__INDICATION(INTEGER(receiving_fd))); break; #endif case SCTP_PARTIAL_DELIVERY_EVENT: log("incoming SCTP_PARTIAL_DELIVERY_EVENT event."); //struct sctp_pdapi_event *pdapi; //pdapi = &snp->sn_pdapi_event; if (events.sctp_partial_delivery_event) incoming_message(SCTPasp__Types::ASP__SCTP__PARTIAL__DELIVERY__EVENT(INTEGER(receiving_fd))); break; default: TTCN_warning("Unknown notification type!"); break; } } void SCTPasp__PT_PROVIDER::log(const char *fmt, ...) { if(debug) { va_list ap; va_start(ap, fmt); TTCN_Logger::begin_event(TTCN_DEBUG); TTCN_Logger::log_event("SCTPasp Test Port (%s): ", get_name()); TTCN_Logger::log_event_va_list(fmt, ap); TTCN_Logger::end_event(); va_end(ap); } } void SCTPasp__PT_PROVIDER::error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); TTCN_Logger::begin_event(TTCN_ERROR); TTCN_Logger::log_event("SCTPasp Test Port (%s): ", get_name()); TTCN_Logger::log_event_va_list(fmt, ap); TTCN_Logger::end_event(); va_end(ap); TTCN_error("Fatal error in SCTPasp Test Port %s (see above).", get_name()); } void SCTPasp__PT_PROVIDER::forced_reconnect(int attempts) { struct sockaddr_storage sa; socklen_t saLen; int sock_type=fill_addr_struct(peer_IP_address,peer_port,&sa,saLen); log("[reconnect] Connecting to (%s):(%d)", (const char*)peer_IP_address, peer_port); unsigned int sleep_interval = 1; int i; for(i = 0; i < attempts; i++) { fd=create_socket(sock_type); if (connect(fd, (struct sockaddr *)&sa, saLen) == -1) { close(fd); fd = -1; TTCN_warning("Connect error!"); errno = 0; if( ((i % 2 ) == 0) && (i != 0)) sleep_interval *= 2; sleep(sleep_interval); } else { map_put_item(fd); setNonBlocking(fd); Handler_Add_Fd_Read(fd); log("[reconnect] Connection successfully established to (%s):(%d)", (const char *)peer_IP_address, peer_port); break; } } if (i == attempts) error("Forced reconnect failed! Remote end is unreachable!"); } void SCTPasp__PT_PROVIDER::map_put_item(int fd) { int i=0; while((i=list_len) || (index<0)) error("map_delete_item: index out of range (0-%d): %d",list_len-1,index); if(fd_map[index].fd!=-1) {close(fd_map[index].fd);Handler_Remove_Fd(fd_map[index].fd, EVENT_ALL);} fd_map[index].fd=-1; fd_map[index].erased=TRUE; fd_map[index].einprogress=FALSE; if(fd_map[index].buf) Free(fd_map[index].buf); fd_map[index].buf=NULL; fd_map[index].buflen=0; fd_map[index].processing_message=FALSE; fd_map[index].nr=0; fd_map[index].saLen=0; memset(&fd_map[index].sin,0,sizeof(struct sockaddr_storage)); } void SCTPasp__PT_PROVIDER::map_put_item_server(int fd, const CHARSTRING& local_IP_address, unsigned short local_port) { int i=0; while((i=list_len_server) || (index<0)) error("map_delete_item: index out of range (0-%d): %d",list_len_server-1,index); if(fd_map_server[index].fd!=-1) {close(fd_map_server[index].fd);Handler_Remove_Fd(fd_map_server[index].fd, EVENT_ALL);} fd_map_server[index].fd=-1; fd_map_server[index].erased=TRUE; if(fd_map_server[index].local_IP_address != NULL){ delete fd_map_server[index].local_IP_address;} fd_map_server[index].local_IP_address = NULL; fd_map_server[index].local_port = 0; } int SCTPasp__PT_PROVIDER::fill_addr_struct(const char* name, int port, struct sockaddr_storage* sa, socklen_t& saLen){ //int err = 0; int addrtype = -1; struct sockaddr_in saddr; struct sockaddr_in6 saddr6; memset(sa,0,sizeof(struct sockaddr_storage)); struct sockaddr_in* v4=(sockaddr_in*)sa; struct sockaddr_in6* v6=(sockaddr_in6*)sa; if(inet_pton(AF_INET, name, &(saddr.sin_addr))) { saLen = sizeof(*v4); v4->sin_family = AF_INET; v4->sin_port = htons(port); memcpy(&(v4->sin_addr), &(saddr.sin_addr), sizeof(saddr.sin_addr)); addrtype = AF_INET; } else if(inet_pton(AF_INET6, name, &(saddr6.sin6_addr))) { saLen = sizeof(*v6); v6->sin6_family = AF_INET6; v6->sin6_port = htons(port); memcpy(&(v6->sin6_addr), &(saddr6.sin6_addr), sizeof(saddr6.sin6_addr)); addrtype = AF_INET6; } else { struct addrinfo myaddr, *res; memset(&myaddr,0,sizeof(myaddr)); myaddr.ai_flags = AI_ADDRCONFIG|AI_PASSIVE; myaddr.ai_socktype = SOCK_STREAM; myaddr.ai_protocol = 0; // if ((err = getaddrinfo(name, NULL, &myaddr, &res)) != 0) { if (getaddrinfo(name, NULL, &myaddr, &res) != 0) { //printf("SetSockAddr: getaddrinfo error: %i, %s", err, gai_strerror(err)); error("Can't resolve host name: %s",name); } if (res->ai_addr->sa_family == AF_INET) { // IPv4 struct sockaddr_in *saddr = (struct sockaddr_in *) res->ai_addr; saLen = sizeof(*v4); v4->sin_family = AF_INET; v4->sin_port = htons(port); memcpy(&(v4->sin_addr), &(saddr->sin_addr), sizeof(saddr->sin_addr)); addrtype = AF_INET; } else if (res->ai_addr->sa_family == AF_INET6){ // IPv6 struct sockaddr_in6 *saddr = (struct sockaddr_in6 *) res->ai_addr; saLen = sizeof(*v6); memcpy(v6,saddr,saLen); v6->sin6_port = htons(port); addrtype = AF_INET6; } freeaddrinfo(res); } return addrtype; } int SCTPasp__PT_PROVIDER::create_socket(int addr_family) { int local_fd; log("Creating SCTP socket."); usleep(200000); if ((local_fd = socket(addr_family, SOCK_STREAM, IPPROTO_SCTP)) == -1) error("Socket error: cannot create socket! %d %s %d %d",errno, strerror(errno),addr_family,AF_INET); log("Setting SCTP socket options (initmsg)."); if (setsockopt(local_fd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(struct sctp_initmsg)) < 0) { TTCN_warning("Setsockopt error!"); errno = 0; } log("Setting SCTP socket options (events)."); if (sctp_setsockopt_events_linux_workaround(local_fd, &events) < 0) { TTCN_warning("Setsockopt error!"); errno = 0; } return local_fd; } void SCTPasp__PT_PROVIDER::setNonBlocking(int fd) { int flags = fcntl(fd, F_GETFL); flags |= O_NONBLOCK; int result = fcntl(fd, F_SETFL, flags); if (result==-1) error("SCTPasp__PT::setNonBlocking(): Fcntl() error!"); } }