/******************************************************************************* ################################################################################ # Copyright (c) [2017-2019] [Radisys] # # # # Licensed under the Apache License, Version 2.0 (the "License"); # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http://www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ################################################################################ *******************************************************************************/ /* This file contains all SCTP related functionality */ #include #include #include #include #include #include #include "cu_sctp.h" /* Global variable declaration */ S8 sockFd; S8 lstnSockFd; U8 socket_type; Bool nonblocking; Bool connUp; int assocId; sockaddr_storage_t local_addr; sockaddr_storage_t remote_addr; U8 la_len; U8 ra_len; SctpParams *sctpCfg; /************************************************************************** * @brief Task Initiation callback function. * * @details * * Function : sctpActvInit * * Functionality: * This function is supplied as one of parameters during SCTP's * task registration. SSI will invoke this function once, after * it creates and attaches this TAPA Task to a system task. * * @param[in] Ent entity, the entity ID of this task. * @param[in] Inst inst, the instance ID of this task. * @param[in] Region region, the region ID registered for memory * usage of this task. * @param[in] Reason reason. * @return ROK - success * RFAILED - failure ***************************************************************************/ S16 sctpActvInit(Ent entity, Inst inst, Region region, Reason reason) { sockFd = 0; lstnSockFd = 0; connUp = FALSE; assocId = 0; nonblocking = FALSE; sctpCfg = &(cuCfgParams.sctpParams); return ROK; } /************************************************************************** * @brief Task Activation callback function. * * @details * * Function : sctpActvTsk * * Functionality: * This function handles all SCTP messages received * This API is registered with SSI during the * Task Registration of DU APP. * * @param[in] Pst *pst, Post structure of the primitive. * @param[in] Buffer *mBuf, Packed primitive parameters in the * buffer. * @return ROK - success * RFAILED - failure * ***************************************************************************/ S16 sctpActvTsk(Pst *pst, Buffer *mBuf) { //TODO: TBD return ROK; } /******************************************************************* * * @brief Converts internet address to sockaddr type * * @details * * Function : getSockAddr * * Functionality: * Converts internet address to sockaddr type * * @params[in] * @return ROK - success * RFAILED - failure * * ****************************************************************/ S16 getSockAddr(char *hostIp, U16 portNum, Bool ipv4Pres, Bool local) { sockaddr_storage_t address; struct hostent *host; void *la_raw; U8 addr_len; /* Getting the transport address for local host name */ if(ipv4Pres) { host = gethostbyname(hostIp); if (host == NULL || host->h_length < 1) { printf("\nBad hostname: %s", hostIp); RETVALUE(NULLP); } addr_len = sizeof(address.v4); la_raw = &address.v4.sin_addr; address.v4.sin_family = AF_INET; address.v4.sin_port = htons(portNum); } else { host = gethostbyname2(hostIp, AF_INET6); if (host == NULL || host->h_length < 1) { printf("\n Bad hostname: %s", hostIp); RETVALUE(RFAILED); } addr_len = sizeof(address.v6); la_raw = &address.v6.sin6_addr; address.v6.sin6_family = AF_INET6; address.v6.sin6_port = htons(portNum); address.v6.sin6_scope_id = 0; } memcpy((U8 *)la_raw, (U8 *)host->h_addr_list[0], host->h_length); if(local) { local_addr = address; la_len = addr_len; } else { remote_addr = address; ra_len = addr_len; } RETVALUE(ROK); } /* End of getSockAddr() */ /******************************************************************* * * @brief Opens a non-blocking socket and binds to local address * * @details * * Function : openSctpEndp * * Functionality: * Opens a non-blocking socket and binds to local address * * @params[in] * @return ROK - success * RFAILED - failure * * ****************************************************************/ S16 openSctpEndp() { sa_family_t la_family; U8 la_len; U8 error; printf("\nEntering openSctpEndp"); /* Getting the transport address for local host name */ if(sctpCfg->cuIpAddr.ipV4Pres) { printf("\nLocal Host Address %s",cuCfgParams.sctpParams.cuIpAddr.ipV4Addr); if(getSockAddr(sctpCfg->cuIpAddr.ipV4Addr, sctpCfg->cuPort, TRUE, TRUE) != ROK ) { printf("\nUnable to get local address"); RETVALUE(RFAILED); } la_len = sizeof(local_addr.v4); la_family = AF_INET; } else { if(getSockAddr(sctpCfg->cuIpAddr.ipV6Addr, sctpCfg->cuPort, FALSE, TRUE) != ROK ) { printf("\nUnable to get local address"); RETVALUE(RFAILED); } la_len = sizeof(local_addr.v6); la_family = AF_INET6; } socket_type = SOCK_STREAM; /* Creating new end point */ lstnSockFd = socket(la_family, socket_type, IPPROTO_SCTP); if (lstnSockFd < 0) { printf("\n Failed to create socket %s", strerror(errno)); RETVALUE(RFAILED); } /* Binding socket to local address and port */ error = bind(lstnSockFd, &local_addr.sa, la_len); if(error != 0) { printf("\n Failed to bind to socket. Error [%s]", strerror(errno)); RETVALUE(RFAILED); } /* Setting socket as non-blocking*/ error = fcntl(lstnSockFd, F_SETFL, O_NONBLOCK); if (error != 0) { printf("\n Failed to set socket as non blocking. Error [%s]", strerror(errno)); RETVALUE(RFAILED); } else { nonblocking = TRUE; } RETVALUE(ROK); } /* End of openSctpEndp() */ /******************************************************************* * * @brief Listens for connection request from peer SCTP * * @details * * Function : sctpConnAccept * * Functionality: * Listens and accepts SCTP connection request from peer. * Here, DU-SCTP will initate connection towards CU-SCTP * * @params[in] * @return ROK - success * RFAILED - failure * * ****************************************************************/ S16 sctpConnAccept() { U8 error; U8 ret; socklen_t len = 0; /* Getting the transport address for remote host name */ if(sctpCfg->duIpAddr.ipV4Pres) { ret = getSockAddr(sctpCfg->duIpAddr.ipV4Addr, sctpCfg->duPort, TRUE, FALSE); } else { ret = getSockAddr(sctpCfg->duIpAddr.ipV6Addr, sctpCfg->duPort, FALSE, FALSE); } if(ret != ROK) { printf("\nUnable to get remote address"); RETVALUE(RFAILED); } printf("\nStart listening on sockFd %d", lstnSockFd); /* Mark sockFd as being able to accept new associations */ error = listen(lstnSockFd, 5); if (error != 0) { printf("\nListen Failure: %s", strerror(errno)); RETVALUE(RFAILED); } while(!connUp) { len = 0; if (!sockFd) { if ((sockFd = accept(lstnSockFd, &remote_addr.sa, &len)) < 0) { if(errno == EAGAIN && nonblocking) { sockFd = 0; continue; } printf("\nFailure in accepting connection request. Error: %s", strerror(errno)); RETVALUE(RFAILED); } else { /* Mark connection UP */ printf("\nCommunication UP. Sock Fd %d", sockFd); connUp = TRUE; } } } RETVALUE(ROK); }/* End of sctpConnAccept */ /******************************************************************* * * @brief Handles an incoming message * * @details * * Function : sctpInmsgHdlr * * Functionality: * Handles an incoming message * * @params[in] Socket file descriptor * Incoming message header * Message Length * * @return ROK - success * RFAILED - failure * * ****************************************************************/ S16 sctpInmsgHdlr(struct msghdr *msg, size_t msgLen) { sctp_cmsg_data_t *data; struct cmsghdr *cmsg; union sctp_notification *sn; for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { data = (sctp_cmsg_data_t *)CMSG_DATA(cmsg); } printf("\nReceived Message"); /* if incoming message is data */ if (!(MSG_NOTIFICATION & msg->msg_flags)) { U8 index = 0; char *recvBuf; char *temp = recvBuf; U8 len = msgLen; U8 temp_len; U8 i; /* Extracting the received message */ while( msgLen > 0) { temp = msg->msg_iov[index].iov_base; temp_len = msg->msg_iov[index].iov_len; if(temp_len > msgLen) { temp[(temp_len = msgLen) - 1] = '\0'; } if((msgLen -= temp_len) > 0) { index++; } for (i = 0; i < temp_len-1 ; ++i) { if (!isprint(temp[i])) temp[i] = '.'; } printf("\nPrinting temp %s", temp); temp = temp + temp_len; index++; } recvBuf[len - 1] = '\0'; printf("\n Message: %s temp %s", recvBuf, temp); /* Send received message to cu_app handler */ cuAppInmsgHdlr(recvBuf, len); } else /* If the incoming message is notification */ { /* Extract and perform necessary action Change the connUp state accordingly */ union sctp_notification *notify; notify = (union sctp_notification *)msg->msg_iov->iov_base; if (SCTP_ASSOC_CHANGE != notify->sn_header.sn_type) { printf("\nReceived unexpected notification: %d", notify->sn_header.sn_type); RETVALUE(RFAILED); } switch(notify->sn_assoc_change.sac_state) { case SCTP_COMM_UP: printf("Received SCTP_COMM_UP\n"); break; case SCTP_COMM_LOST: printf("Received SCTP_COMM_LOST\n"); break; case SCTP_RESTART: printf("Received SCTP_RESTART\n"); break; case SCTP_SHUTDOWN_COMP: printf("Received SCTP_SHUTDOWN_COMP\n"); break; case SCTP_CANT_STR_ASSOC: printf("Received SCTP_CANT_STR_ASSOC\n"); break; } } RETVALUE(ROK); } /******************************************************************* * * @brief Receives message on the socket * * @details * * Function : sctpSockPoll * * Functionality: * Receives message on the socket * * @params[in] * @return ROK - success * RFAILED - failure * * ****************************************************************/ S16 sctpSockPoll() { U8 error; struct msghdr inmessage; struct iovec iov; char incmsg[CMSG_SPACE(sizeof(_sctp_cmsg_data_t))]; sockaddr_storage_t msgname; /* Initialize inmessage with enough space for DATA... */ memset(&inmessage, 0, sizeof(inmessage)); if ((iov.iov_base = malloc(REALLY_BIG)) == NULL) { printf("\n malloc not enough memory!!!"); close(sockFd); connUp = FALSE; RETVALUE(RFAILED); } iov.iov_len = REALLY_BIG; inmessage.msg_iov = &iov; inmessage.msg_iovlen = 1; /* or a control message. */ inmessage.msg_control = incmsg; inmessage.msg_controllen = sizeof(incmsg); inmessage.msg_name = &msgname; inmessage.msg_namelen = sizeof(msgname); while (connUp) { error = recvmsg(sockFd, &inmessage, MSG_WAITALL); if (error < 0) { if (nonblocking && (EAGAIN == errno)) { error = 0; continue; } if (socket_type == SOCK_STREAM) { if (ENOTCONN != errno) { break; } printf("No association is present now!!\n"); close(sockFd); sockFd = 0; connUp = FALSE; } break; } sctpInmsgHdlr(&inmessage, error); inmessage.msg_control = incmsg; inmessage.msg_controllen = sizeof(incmsg); inmessage.msg_name = &msgname; inmessage.msg_namelen = sizeof(msgname); iov.iov_len = REALLY_BIG; }/* End of while(connUp) */ RETVALUE(ROK); }/* End of sctpSockPoll() */ /******************************************************************* * * @brief Send message on SCTP socket * * @details * * Function : sctpOutMsgSend * * Functionality: * Send message on SCTP socket * * @params[in] * @return ROK - success * RFAILED - failure * * ****************************************************************/ S16 sctpOutMsgSend(char *message, U8 msglen) { struct msghdr outmsg; struct iovec iov; int error = 0; do{ if(connUp && msglen != 0) { iov.iov_base = message; iov.iov_len = msglen; outmsg.msg_iov = &iov; outmsg.msg_iovlen = 1; outmsg.msg_control = NULL; outmsg.msg_controllen = 0; outmsg.msg_name = &remote_addr; outmsg.msg_namelen = ra_len; outmsg.msg_flags = 0; error = sendmsg(sockFd, &outmsg, 0); } if(error != msglen) { if(nonblocking && EAGAIN == errno) { continue; } else { printf("\n Error [%s] while sending message on SCTP assoc", strerror(errno)); RETVALUE(RFAILED); } } else { break; } }while(error != msglen); RETVALUE(ROK); } /* End of sctpOutMsgSend */ /******************************************************************* * * @brief SCTP Assoc establishment request from DU * * @details * * Function : sctpAssocReq * * Functionality: * This function opens a socket at DU and * intiates SCTP connection. * * @params[in] * @return ROK - success * RFAILED - failure * * ****************************************************************/ void sctpStartReq() { printf("\nStarting CU SCTP"); /* Open SCTP socket and bind to local address */ if(openSctpEndp() != ROK) { printf("\nFailed while opening SCTP endpoint"); if(lstnSockFd > 0 ) { close(lstnSockFd); lstnSockFd = 0; } /* TODO : Send Assoc establishment failure to cu_app if needed*/ } else if(sctpConnAccept() != ROK) /* send connection request */ { printf("\nFailed while connecting to peer"); close(lstnSockFd); lstnSockFd = 0; assocId = 0; nonblocking = FALSE; /* TODO : Send Assoc establishment failure to cu_app */ } else { /* Send AssocCfm to cu_app */ if(sctpSockPoll() != ROK) { printf("\nFailed while polling"); /* Send failure to cu_app */ } } } /* End of sctpStartReq */ /********************************************************************** End of file **********************************************************************/