/* ipdiameter.c - Diameter node common routines */ /* * Copyright (c) 2007 Wind River Systems, Inc. * * The right to copy, distribute or otherwise make use of this software * may be licensed only pursuant to the terms of an applicable Wind River * license agreement. No license to Wind River intellectual property rights * is granted herein. All rights not licensed by Wind River are reserved * by Wind River. */ /* modification history -------------------- 01t,23nov10,hfc fir buildwarning.Defect WIND00242868 01s,16jul10,hfc secureKeyDB support for diameter 01r,24mar10,zhr LP64 bits adaption 01q,19aug09,tlu Coverity fix 01p,10mar09,tlu Fix Linux build warning 01o,24apr08,ggz Added ipdiameter_set_wfd function 01n,17apr08,tlu Fix for show routines 01m,03apr08,ggz Added wdfs support for non blocking connection establisment 01l,24jan08,tlu Add policy show routine 01k,13dec07,tlu Limit session configuration parameters to client only in the node show routine 01j,14nov07,tlu Set floating point flag for Diameter task 01i,17oct07,tlu Port to Linux 01h,19sep07,tlu Set TLS CA rootdir field to NULL if the configuration parameter is an empty string 01g,09aug07,tlu Add TLS support 01f,02aug07,tlu Initialize session ID seed and free the memory dynamically allocated by ipcom_sysvar_get() 01e,04jul07,tlu Add supported application IDs as a configuration parameter and add display supported applications to node show routine 01d,28jun07,tlu Add local host IP address as configuration parameters 01c,20jun07,tlu Add port number as an argument for open socket functions 01b,13jun07,tlu Update after code inspection 01a,05apr07,tlu Created. */ /* **************************************************************************** * DESCRIPTION **************************************************************************** */ /* **************************************************************************** * CONFIGURATION **************************************************************************** */ #include "ipdiameter_config.h" /* **************************************************************************** * INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO /* ipcom */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* ipdiameter */ #include "ipdiameter.h" /* **************************************************************************** * DEFINES **************************************************************************** */ /* This is a compile flag, turning on it will build debug message into image */ #undef DIAMETER_DEBUG /* **************************************************************************** * LOCAL TYPES **************************************************************************** */ typedef struct Ipdiameter_selectfd_struct { Ipcom_list fd_list; Ip_fd fd; Ipdiameter_peer_entry *peer; /* the peer that fd is associated with */ } Ipdiameter_selectfd; /* *=========================================================================== * Global ipdiameter data *=========================================================================== * All global IPDiameter data is located in this structure. */ IP_PUBLIC Ipdiameter_data ipdiameter; /* **************************************************************************** * LOCAL Data **************************************************************************** */ IP_STATIC Ip_bool daemon_started = IP_FALSE; IP_PUBLIC Ip_bool diameterStopFlag; /* diameter node daemon name */ #define IPDIAMETER_PROC_NAME 20 IP_STATIC char procname[IPDIAMETER_PROC_NAME]; /* FDs the daemon is selecting on */ IP_STATIC Ip_fd_set fds; /* Read */ IP_STATIC Ip_fd_set wfds; /* Write */ IP_STATIC Ip_u32 max; /* **************************************************************************** * LOCAL PROTOTYPES **************************************************************************** */ #ifdef IPCOM_USE_INET IP_STATIC Ip_fd ipdiameter_tcp4rx_socket(Ip_u16 port); #endif #ifdef IPCOM_USE_INET6 IP_STATIC Ip_fd ipdiameter_tcp6rx_socket(Ip_u16 port); #endif IP_STATIC Ip_err ipdiameter_common_init(Ipdiameter_peer_type node_type); /* **************************************************************************** * LOCAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipdiameter_tcp4rx_socket *=========================================================================== * Description: Open Diameter IPv4 TCP listening socket * Parameters: * Returns: * */ #ifdef IPCOM_USE_INET IP_STATIC Ip_fd ipdiameter_tcp4rx_socket(Ip_u16 port) { Ip_fd fd; int opt_val = 1; /* Get DIAMETER IPv4 TCP socket. */ fd = ipcom_socket(IP_AF_INET, IP_SOCK_STREAM, 0); if (fd != IP_INVALID_SOCKET) { struct Ip_sockaddr_in addr; /* Bind socket to a local random port. */ ipcom_memset(&addr, 0, sizeof(addr)); IPCOM_SA_LEN_SET(&addr, sizeof(struct Ip_sockaddr_in)); addr.sin_family = IP_AF_INET; addr.sin_port = ip_htons(port); addr.sin_addr.s_addr = IP_INADDR_ANY; if (ipcom_setsockopt(fd, IP_SOL_SOCKET, IP_SO_REUSEADDR, (void*) &opt_val, sizeof(opt_val))) { IPCOM_LOG1(WARNING, "ipdiameter_tcp4rx_socket: ipcom_setsockopt(IP_SO_REUSEADDR) failed for AF_INET: %s", ipcom_strerror(ipcom_errno)); } if (ipcom_bind(fd, (struct Ip_sockaddr *)&addr, sizeof(addr)) < 0) { IPCOM_LOG1(ERR, "ipdiameter_tcp4rx_socket: failed to bind TCP socket, errno = %d"IP_LF, ipcom_errno); ipcom_socketclose(fd); return IP_INVALID_SOCKET; } } else { IPCOM_LOG1(ERR, "ipdiameter_tcp4rx_socket: failed to open IPv4 TCP listening socket, errno = %d"IP_LF, ipcom_errno); return IP_INVALID_SOCKET; } /* move the socket to LISTEN state */ if (ipcom_listen(fd, 1) != 0) { IPCOM_LOG1(ERR, "ipdiameter_tcp4rx_socket: cannot listen on socket %d\n", fd); ipcom_socketclose(fd); return IP_INVALID_SOCKET; } return fd; } #endif /* *=========================================================================== * ipdiameter_tcp6rx_socket *=========================================================================== * Description: Open Diameter IPv6 TCP listening socket * Parameters: * Returns: * */ #ifdef IPCOM_USE_INET6 IP_STATIC Ip_fd ipdiameter_tcp6rx_socket(Ip_u16 port) { Ip_fd fd6; int opt_val = 1; /* Get DIAMETER IPv6 TCP socket. */ fd6 = ipcom_socket(IP_AF_INET6, IP_SOCK_STREAM, 0); if (fd6 != IP_INVALID_SOCKET) { struct Ip_sockaddr_in6 addr6; /* Bind socket to a local random port. */ ipcom_memset(&addr6, 0, sizeof(addr6)); IPCOM_SA_LEN_SET(&addr6, sizeof(struct Ip_sockaddr_in6)); addr6.sin6_family = IP_AF_INET6; addr6.sin6_port = ip_htons(port); if (ipcom_bind(fd6, (struct Ip_sockaddr *)&addr6, sizeof(addr6)) < 0) { IPCOM_LOG1(ERR, "ipdiameter_tcp6rx_socket: failed to bind IPv6 TCP socket, errno = %d\n", ipcom_errno); ipcom_socketclose(fd6); return IP_INVALID_SOCKET; } if (ipcom_setsockopt(fd6, IP_SOL_SOCKET, IP_SO_REUSEADDR, (void*) &opt_val, sizeof(opt_val))) { IPCOM_LOG1(WARNING, "ipdiameter_tcp6rx_socket: ipcom_setsockopt(IP_SO_REUSEADDR) failed for AF_INET6: %s", ipcom_strerror(ipcom_errno)); } } else { IPCOM_LOG1(ERR, "ipdiameter_tcp6rx_socket: failed to open IPv6 TCP listening socket, errno = %d\n", ipcom_errno); return IP_INVALID_SOCKET; } /* move the socket to LISTEN state */ if (ipcom_listen(fd6, 1) != 0) { IPCOM_LOG1(ERR, "ipdiameter_tcp6rx_socket: cannot listen on socket %d\n", fd6); ipcom_socketclose(fd6); return IP_INVALID_SOCKET; } return fd6; } #endif /* *=========================================================================== * ipdiameterd *=========================================================================== * Description: Diameter node daemon * Parameters: * Returns: * */ IPCOM_PROCESS(ipdiameterd); IPCOM_PROCESS(ipdiameterd) { Ip_err ret; Ip_bool ipdiameter_open_ok = IP_FALSE; Ip_fd ipd_fd = IP_INVALID_SOCKET; Ip_fd_set read_set, write_set; Ip_s32 num; struct Ip_timeval tv, *ptv = IP_NULL; Ipdiameter_selectfd *selectfd, *nselectfd; Ip_socklen_t errlen; int so_errno = 0; int off = 0; #ifdef IPCOM_USE_INET Ip_fd tcp_fd = IP_INVALID_SOCKET; #endif #ifdef IPCOM_USE_INET6 Ip_fd tcp_fd6 = IP_INVALID_SOCKET; #endif ipcom_proc_init(); #ifdef DIAMETER_DEBUG ipcom_printf("%s :: starting daemon\n", procname); #endif diameterStopFlag = IP_FALSE; errlen = sizeof(int); /***** Open IPDIAMETER. *****/ ret = ipdiameter_common_open(); if(ret != IPCOM_SUCCESS) { IPCOM_LOG1(ERR, "ipdiameterd :: ipdiameter_common_open() failed, err = %ld", ret); goto badout; } ipdiameter_open_ok = IP_TRUE; #ifdef DIAMETER_DEBUG ipcom_printf("ipdiameterd :: open ok\n"); #endif /***** Get diameter tcp listening socket */ #ifdef IPCOM_USE_INET tcp_fd = ipdiameter.fd; #ifdef DIAMETER_DEBUG ipcom_printf("ipdiameterd :: tcp listening fd %d\n", tcp_fd); #endif #endif #ifdef IPCOM_USE_INET6 tcp_fd6 = ipdiameter.fd6; #ifdef DIAMETER_DEBUG ipcom_printf("ipdiameterd :: tcp listening fd6 %d\n", tcp_fd6); #endif #endif /* IPD init. */ if(ipcom_ipd_init(procname, IPCOM_SUCCESS, &ipd_fd) != IPCOM_SUCCESS) { IPCOM_LOG1(ERR, "%s: ipcom_ipd_init() failed", procname); goto badout; } /***** Input loop. *****/ IP_FD_ZERO(&fds); max = (Ip_u32)ipd_fd; IP_FD_SET(ipd_fd, &fds); IP_FD_ZERO(&wfds); #ifdef IPCOM_USE_INET max = IP_MAX(max, (Ip_u32)tcp_fd); IP_FD_SET(tcp_fd, &fds); #endif #ifdef IPCOM_USE_INET6 max = IP_MAX(max, tcp_fd6); IP_FD_SET(tcp_fd6, &fds); #endif /* Set daemon_started after set select fds */ daemon_started = IP_TRUE; /* set the timeout value */ memset(&tv, 0, sizeof(struct Ip_timeval)); tv.tv_sec = 1; tv.tv_usec = 0; ptv = &tv; #ifdef DIAMETER_DEBUG ipcom_printf("**** Started loop **** \n"); #endif while (1) { /* Check diametercStop() wasn't called */ if (diameterStopFlag) { IPCOM_LOG0(INFO, "Diameter node daemon to close. Closing down"); goto badout; } read_set = fds; write_set = wfds; num = ipcom_tmo2_select(ipdiameter.tmo_head, (int)max + 1, &read_set, &write_set, IP_NULL, ptv); if (diameterStopFlag) { IPCOM_LOG0(INFO, "Diameter node daemon to close. Closing down"); goto badout; } switch (num) { case -1: IPCOM_LOG0(WARNING, "diameterd select error"); continue; case 0: continue; default: #ifdef DIAMETER_DEBUG ipcom_printf("Received message from a socket\n"); #endif #ifdef IPCOM_USE_INET if (IP_FD_ISSET(tcp_fd, &read_set)) { IPCOM_LOG0 (INFO, "Diameter IPv4 listening fd receives message"); if (ipdiameter_ctrl_recv(tcp_fd) != IPCOM_SUCCESS) IPCOM_LOG1(WARNING, "Diameter daemon IPv4 TCP socket %d rcv an error msg\n", tcp_fd); } #endif #ifdef IPCOM_USE_INET6 if (IP_FD_ISSET(tcp_fd6, &read_set)) { IPCOM_LOG0(INFO, "Diameter IPv6 listening fd receives message"); if (ipdiameter_ctrl_recv(tcp_fd6) != IPCOM_SUCCESS) IPCOM_LOG1(WARNING, "Diameter daemon IPv6 TCP socket %d rcv error msg\n", tcp_fd6); } #endif /* go through all data sockets */ for (selectfd = IPCOM_LIST_FIRST(&ipdiameter.fd_head); selectfd != IP_NULL; selectfd = nselectfd) { /* get next fd */ nselectfd = IPCOM_LIST_NEXT(&selectfd->fd_list); /* Check if the fd is set. */ if (IP_FD_ISSET(selectfd->fd, &read_set)) { #ifdef DIAMETER_DEBUG ipcom_printf("Receives message on fd %d, from peer %x\n", selectfd->fd, selectfd->peer); #endif if (ipdiameter_recv(selectfd->fd, selectfd->peer) != IPCOM_SUCCESS) { #ifdef DIAMETER_DEBUG ipcom_printf("Diameter daemon rx wrong data, fd %d, from peer %x\n", selectfd->fd, selectfd->peer); #endif } } else if (IP_FD_ISSET(selectfd->fd, &write_set)) { if (ipdiameter.node_type == DIAMETER_PEER_PROXY_AGENT) { if (selectfd->peer->state == DIAMETER_PEER_STATE_WAIT_CONN_ACK) { /* Process non blocking socket connection errors */ if (0 == ipcom_getsockopt(selectfd->fd, IP_SOL_SOCKET, IP_SO_ERROR, &so_errno, &errlen)); { if (so_errno == IP_ERRNO_ENETUNREACH) { /* This is the case when a nonblocking connection has been initiated by the peer_fsm. */ /* Set the socket back to the blocking mode */ if (ipcom_socketioctl(selectfd->fd, IP_FIONBIO, (char*)&off)) { IPCOM_LOG0(WARNING, "IPCOM_PROCESS: socket IP_FIONBIO set failed\n"); } ipdiameter_peer_fsm(selectfd->peer, IP_NULL, DIAMETER_PEER_EV_I_RCV_CONN_NACK); so_errno = 0; /* Reset the local so_errno */ } /* Process non blocking socket connection event */ else /*(so_errno == 0)*/ { IP_FD_CLR(selectfd->fd, &wfds); /* Set the socket back to the blocking mode */ if (ipcom_socketioctl(selectfd->fd, IP_FIONBIO, (char*)&off)) { IPCOM_LOG0(WARNING, "IPCOM_PROCESS: socket IP_FIONBIO set failed\n"); } ipdiameter_peer_fsm(selectfd->peer, IP_NULL, DIAMETER_PEER_EV_I_RCV_CONN_ACK); } } } } } } if (IP_FD_ISSET(ipd_fd, &read_set)) { int event; event = ipcom_ipd_input(ipd_fd); switch(event) { case IPCOM_IPD_EVENT_KILL: IPCOM_LOG0(INFO, "received IPD kill message"); goto badout; default: break; } } } /* switch */ } /* while */ badout: if(ipd_fd == IP_INVALID_SOCKET) (void)ipcom_ipd_init(procname, IPCOM_ERR_FAILED, &ipd_fd); if (ipdiameter_open_ok) { (void)ipdiameter.close(); } (void)ipdiameter.exit(); (void)ipcom_ipd_exit(procname, ipd_fd); #ifdef DIAMETER_DEBUG ipcom_printf("%s :: ending", procname); #endif ipcom_proc_exit(); } /* *=========================================================================== * ipdiameter_common_init *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipdiameter_common_init(Ipdiameter_peer_type node_type) { Ip_err retval = IPCOM_SUCCESS; ipcom_memset(&ipdiameter, 0, sizeof(Ipdiameter_data)); /* Set node type */ ipdiameter.node_type = node_type; /* Initialize session ID seed */ IP_U64_SET(ipdiameter.session_id_seed, ipcom_time(0), 0); /* Initialize lists. */ ipcom_list_init(&ipdiameter.session_head); ipcom_list_init(&ipdiameter.peer_head); ipcom_list_init(&ipdiameter.fd_head); ipcom_list_init(&ipdiameter.route_head); ipcom_list_init(&ipdiameter.policy_head); /* Initialize the timeout related parameters to default values */ ipdiameter.session_timeout = IPDIAMETER_SESSION_TIMEOUT; ipdiameter.auth_lifetime = IPDIAMETER_AUTH_LIFETIME; ipdiameter.auth_grace_period = IPDIAMETER_AUTH_GRACE_PERIOD; ipdiameter.peer_reconn_interval = IPDIAMETER_PEER_RECONN_INTERVAL; ipdiameter.max_reconn_attemp = IPDIAMETER_MAX_PEER_RECONN_ATTEMPT; ipdiameter.req_retx_interval = IPDIAMETER_REQ_RETX_INTERVAL; ipdiameter.max_req_retx = IPDIAMETER_MAX_REQ_RETX; /* Create timeout queue */ ipdiameter.tmo_head = ipcom_tmo2_new(); if (ipdiameter.tmo_head == IP_NULL) { IPCOM_LOG0(ERR, "ipdiameter_common_init: fail to create timeout queue\n"); IPDIAMETER_LEAVE(IPCOM_ERR_NO_MEMORY); } /* create semaphore */ #ifdef IPDIAMETER_ENABLE_REENTRANT (void)ipcom_mutex_create(&ipdiameter.code_mutex); #endif leave: return retval; } /* *=========================================================================== * ipdiameter_common_open *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipdiameter_common_open(void) { Ip_err retval = IPCOM_SUCCESS; char *diameterid, *realm, *addr; Ip_s32 address_family; char *appl_list, *appl, *last; Ip_size_t length; IPDIAMETER_LOCK(); /** Get IPDIAMETER configuration parameters **/ /* Get diameter identifier */ diameterid = ipcom_sysvar_get("ipdiameter.identifier", IP_NULL, IP_NULL); if (diameterid != IP_NULL) { length = ipcom_strlen(diameterid); if ( (length < IPDIAMETER_MAX_FQDN_SIZE-1) && (length > 0) ) { ipcom_strcpy(ipdiameter.host_id, diameterid); ipcom_free(diameterid); } else { IPCOM_LOG0(ERR, "ipdiameter_common_open: wrong config parameter DIAMETER_IDENTIFIER\n"); ipcom_free(diameterid); IPDIAMETER_LEAVE(IPCOM_ERR_INVALID_CONFIG); } } else { IPCOM_LOG0(ERR, "ipdiameter_common_open: wrong config parameter DIAMETER_IDENTIFIER\n"); IPDIAMETER_LEAVE(IPCOM_ERR_INVALID_CONFIG); } /* Get diameter node realm */ realm = ipcom_sysvar_get("ipdiameter.realm", IP_NULL, IP_NULL); if (realm != IP_NULL) { length = ipcom_strlen(realm); if ( (length < IPDIAMETER_MAX_FQDN_SIZE-1) && (length > 0) ) { ipcom_strcpy(ipdiameter.realm_id, realm); ipcom_free(realm); } else { IPCOM_LOG0(ERR, "ipdiameter_common_open: wrong size of config parameter DIAMETER_HOST_REALM\n"); ipcom_free(realm); IPDIAMETER_LEAVE(IPCOM_ERR_INVALID_CONFIG); } } else { IPCOM_LOG0(ERR, "ipdiameter_common_open: wrong config parameter DIAMETER_HOST_REALM\n"); IPDIAMETER_LEAVE(IPCOM_ERR_INVALID_CONFIG); } /* Get diameter node's address family */ address_family = ipcom_sysvar_get_as_int("ipdiameter.domain", -1); if ((address_family) != IP_AF_INET && (address_family != IP_AF_INET6)) { IPCOM_LOG0(ERR, "ipdiameter_common_open: wrong config parameter DIAMETER_HOST_DOMAIN\n"); IPDIAMETER_LEAVE(IPCOM_ERR_INVALID_CONFIG); } if (address_family == IP_AF_INET) { #ifdef IPCOM_USE_INET addr = ipcom_sysvar_get("ipdiameter.ipv4_address", IP_NULL, IP_NULL); if (addr != IP_NULL) { if (ipcom_inet_pton(IP_AF_INET, addr, &ipdiameter.addr.sin.sin_addr) == 1) { ipdiameter.addr.sa.sa_family = IP_AF_INET; IPCOM_SA_LEN_SET(&ipdiameter.addr.sa, sizeof(struct Ip_sockaddr_in)); } ipcom_free(addr); } else { IPCOM_LOG0(ERR, "ipdiameter_common_open: wrong config parameter DIAMETER_HOST_IPV4_ADDRESS\n"); IPDIAMETER_LEAVE(IPCOM_ERR_INVALID_CONFIG); } #endif } else { #ifdef IPCOM_USE_INET6 addr = ipcom_sysvar_get("ipdiameter.ipv6_address", IP_NULL, IP_NULL); if (addr != IP_NULL) { if (ipcom_inet_pton(IP_AF_INET6, addr, &ipdiameter.addr.sin6.sin6_addr) == 1) { ipdiameter.addr.sa.sa_family = IP_AF_INET6; IPCOM_SA_LEN_SET(&ipdiameter.addr.sa, sizeof(struct Ip_sockaddr_in6)); } ipcom_free(addr); } else { IPCOM_LOG0(ERR, "ipdiameter_common_open: wrong config parameter DIAMETER_HOST_IPV6_ADDRESS\n"); IPDIAMETER_LEAVE(IPCOM_ERR_INVALID_CONFIG); } #endif } /* Get local node TCP listening port number */ ipdiameter.tcp_port = (Ip_u16)ipcom_sysvar_get_as_int("ipdiameter.tcp_port", -1); /* Get supported aplication ID list */ last = appl_list = ipcom_sysvar_get("ipdiameter.appl_list",IP_NULL,IP_NULL); if (appl_list == IP_NULL) IPCOM_LOG0(INFO, "ipdiameter_common_open: no application configured"); else { while (IP_NULL != (appl = ipcom_strtok_r(last == appl_list?appl_list: IP_NULL, ",", &last))) { Ip_u32 appl_id = (Ip_u32)ipcom_atoi(appl); if (!(IPDIAMETER_VALID_APP_ID(appl_id))) { IPCOM_LOG1(ERR, "ipdiameter_common_open:, invalid appl ID %d\n", appl_id); IPDIAMETER_LEAVE(IPCOM_ERR_INVALID_CONFIG); } ipdiameter.appl_ids[ipdiameter.num_appls] = appl_id; ipdiameter.num_appls++; } ipcom_free(appl_list); } /* Get diameter vendor ID */ ipdiameter.vendor_id = (Ip_u32)ipcom_sysvar_get_as_int("ipdiameter.vendorid", -1); if ((Ip_s32)(ipdiameter.vendor_id ) < 0) { IPCOM_LOG0(ERR, "ipdiameter_common_open: wrong config parameter DIAMETER_VENDOR_ID\n"); IPDIAMETER_LEAVE(IPCOM_ERR_INVALID_CONFIG); } /* Get diameter peer reconnect interval in seconds */ ipdiameter.peer_reconn_interval = (Ip_u16)ipcom_sysvar_get_as_int("ipdiameter.timeout.reconninterval", -1); /* Get diameter max peer reconnection attempts */ ipdiameter.max_reconn_attemp = (Ip_u16)ipcom_sysvar_get_as_int("ipdiameter.maxreconnattempt", -1); /* Get diameter watchdog timeout in seconds */ ipdiameter.watchdog_timeout = (Ip_u16)ipcom_sysvar_get_as_int("ipdiameter.timeout.watchdog", -1); /* Get diameter request retransmission interval in seconds */ ipdiameter.req_retx_interval = (Ip_u16)ipcom_sysvar_get_as_int("ipdiameter.timeout.reqretxinterval", -1); /* Get diameter max request retransmission */ ipdiameter.max_req_retx = (Ip_u16)ipcom_sysvar_get_as_int("ipdiameter.maxreqretx", -1); /* Get TLS enabled flag */ ipdiameter.tls_enabled = ipcom_sysvar_get_as_int("ipdiameter.tls", -1); if (ipdiameter.tls_enabled != 0 && ipdiameter.tls_enabled != 1) { IPCOM_LOG0(ERR, "ipdiameter_common_open: wrong config parameter DIAMETER_TLS_ENABLED\n"); IPDIAMETER_LEAVE(IPCOM_ERR_INVALID_CONFIG); } if (ipdiameter.tls_enabled) { /* Read in other TLS configuration parameters */ /* Get Local node's certificate file */ ipdiameter.certfile = ipcom_sysvar_get("ipdiameter.tls.certfile", IP_NULL, IP_NULL); if (ipdiameter.certfile == IP_NULL) { IPCOM_LOG0(ERR, "ipdiameter_common_open: wrong config parameter DIAMETER_TLS_CERTIFICATE\n"); IPDIAMETER_LEAVE(IPCOM_ERR_INVALID_CONFIG); } #if defined(IPCOM_USE_KEY_DB) && defined(_WRS_CONFIG_IPDIAMETER_SEC_KEY_DB) /* Get local node's private key Id and store it in keyfile variavle*/ ipdiameter.keyfile = ipcom_sysvar_get("ipdiameter.tls.tlsPKeyId", IP_NULL, IP_NULL); if (ipdiameter.keyfile == IP_NULL) { IPCOM_LOG0(ERR, "ipdiameter_common_open: wrong config parameter DIAMETER_TLS_KEY_ID\n"); IPDIAMETER_LEAVE(IPCOM_ERR_INVALID_CONFIG); } #else /* Get local node's private key file */ ipdiameter.keyfile = ipcom_sysvar_get("ipdiameter.tls.keyfile", IP_NULL, IP_NULL); if (ipdiameter.keyfile == IP_NULL) { IPCOM_LOG0(ERR, "ipdiameter_common_open: wrong config parameter DIAMETER_TLS_KEY_FILE\n"); IPDIAMETER_LEAVE(IPCOM_ERR_INVALID_CONFIG); } /* Get local node's private key file's key password */ ipdiameter.keypasswd = ipcom_sysvar_get("ipdiameter.tls.keypasswd", IP_NULL, IP_NULL); if (ipdiameter.keypasswd == IP_NULL) { IPCOM_LOG0(ERR, "ipdiameter_common_open: wrong config parameter DIAMETER_TLS_KEY_PASSWORD\n"); IPDIAMETER_LEAVE(IPCOM_ERR_INVALID_CONFIG); } #endif /* _WRS_CONFIG_IPDIAMETER_SEC_KEY_DB */ /* Get CA root certificate file */ ipdiameter.cacertfile = ipcom_sysvar_get("ipdiameter.tls.cacertfile", IP_NULL, IP_NULL); if (ipdiameter.cacertfile == IP_NULL) { IPCOM_LOG0(ERR, "ipdiameter_common_open: wrong config parameter DIAMETER_TLS_ROOT_CERTIFICATE\n"); IPDIAMETER_LEAVE(IPCOM_ERR_INVALID_CONFIG); } /* Get certificate root directory */ ipdiameter.rootdir = ipcom_sysvar_get("ipdiameter.tls.rootdir", IP_NULL, IP_NULL); if (ipdiameter.rootdir == IP_NULL) { IPCOM_LOG0(ERR, "ipdiameter_common_open: wrong config parameter DIAMETER_TLS_ROOT_DIR\n"); IPDIAMETER_LEAVE(IPCOM_ERR_INVALID_CONFIG); } else if (ipcom_strcmp(ipdiameter.rootdir, "") == 0) { IPCOM_LOG0(INFO, "ipdiameter_common_open: DIAMETER_TLS_ROOT_DIR is empty string\n"); ipcom_free(ipdiameter.rootdir); ipdiameter.rootdir = IP_NULL; } #ifdef IPSSL /* Init ssl_ctx context */ retval = ipdiameter_setup_ssl_ctx(); if (retval != IPCOM_SUCCESS) IPDIAMETER_LEAVE(retval); #endif /* IPSSL */ } /* Get node specific configuration parameters */ retval = ipdiameter.open(); if (retval != IPCOM_SUCCESS) IPDIAMETER_LEAVE(retval); /* Open Dimaeter tcp listening socket */ #ifdef IPCOM_USE_INET ipdiameter.fd = ipdiameter_tcp4rx_socket(ipdiameter.tcp_port); #endif #ifdef IPCOM_USE_INET6 ipdiameter.fd6 = ipdiameter_tcp6rx_socket(ipdiameter.tcp_port); #endif ipdiameter.opened = IP_TRUE; leave: IPDIAMETER_UNLOCK(); return retval; } /* **************************************************************************** * PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipdiameter_common_create *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipdiameter_common_create(Ipdiameter_peer_type node_type) { Ip_err retval = IPCOM_SUCCESS; /* init Diameter global data structure */ retval = ipdiameter_common_init(node_type); if (retval != IPCOM_SUCCESS) IPCOM_LOG0(ERR, "ipdiameter_common_create: failed on ipdiameter_common_init"); return IPCOM_SUCCESS; } /* *=========================================================================== * ipdiameter_common_start *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipdiameter_common_start(char *proc_name) { Ip_err ret = IPCOM_SUCCESS; Ipcom_proc_attr attr; Ip_u16 length; /* get IPD name from the Diameter node daemon's name */ /* IPD name is one letter shorter than node daemon's name, */ /* i.e. IPD name "ipdiameterc" and node daemon's name "ipdiametercd" */ length = (Ip_u16)ipcom_strlen(proc_name); ipcom_memcpy(procname, proc_name, (Ip_size_t)length-1); procname[length-1] = '\0'; ipcom_proc_attr_init(&attr); attr.priority = IPCOM_PRIORITY_DEFAULT - 1; attr.stacksize = 20000; attr.flags = IPCOM_PROC_FLAG_FP; ret = ipcom_proc_acreate(proc_name, ipdiameterd, &attr, IP_NULL); if (ret != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipdiameter_common_start: Failed on ipcom_proc_acreate"); } return ret; } /* *=========================================================================== * ipdiameter_common_close *=========================================================================== * Description: Close an opened Diameter * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipdiameter_common_close(void) { Ip_err retval = IPCOM_SUCCESS; Ipdiameter_session *session, *session_v; Ipdiameter_peer_entry *peer, *peer_v; Ipdiameter_route_entry *route, *route_v; Ipdiameter_policy_entry *policy, *policy_v; if (ipdiameter.opened == IP_FALSE) return IPCOM_ERR_ALREADY_CLOSED; IPDIAMETER_LOCK(); /* close all sessions */ for (session = IPCOM_LIST_FIRST(&ipdiameter.session_head); session; session = session_v) { session_v = IPCOM_LIST_NEXT(&session->session_list); ipdiameter_delete_session(session); } /* remove all peers */ for (peer = IPCOM_LIST_FIRST(&ipdiameter.peer_head); peer; peer = peer_v) { peer_v = IPCOM_LIST_NEXT(&peer->peer_list); (void)ipdiameter_delete_peer(&peer->peer_addr); } /* remove all routes */ for (route = IPCOM_LIST_FIRST(&ipdiameter.route_head); route; route = route_v) { route_v = IPCOM_LIST_NEXT(&route->route_list); ipdiameter_del_route(route); } /* remove all policies */ for (policy = IPCOM_LIST_FIRST(&ipdiameter.policy_head); policy; policy = policy_v) { policy_v = IPCOM_LIST_NEXT(&policy->policy_list); (void)ipdiameter_delete_policy(policy); } /* free memory dynamically allocated for TLS config parameters */ if (ipdiameter.certfile) ipcom_free(ipdiameter.certfile); if (ipdiameter.keyfile) ipcom_free(ipdiameter.keyfile); if (ipdiameter.keypasswd) ipcom_free(ipdiameter.keypasswd); if (ipdiameter.cacertfile) ipcom_free(ipdiameter.cacertfile); if (ipdiameter.rootdir) ipcom_free(ipdiameter.rootdir); #ifdef IPSSL /* free ssl_ctx context */ if (ipdiameter.ssl_ctx) SSL_CTX_free(ipdiameter.ssl_ctx); #endif /* IPSSL */ /* Close TCP sockets */ #ifdef IPCOM_USE_INET if (ipdiameter.fd != IP_INVALID_SOCKET) ipcom_socketclose(ipdiameter.fd); #endif #ifdef IPCOM_USE_INET6 if (ipdiameter.fd6 != IP_INVALID_SOCKET) ipcom_socketclose(ipdiameter.fd6); #endif /* free user registered callback data and reset callback funciton */ ipdiameter.user_callback = IP_NULL; if (ipdiameter.opaque_callback_data) ipcom_free(ipdiameter.opaque_callback_data); ipdiameter.opaque_callback_data = IP_NULL; ipdiameter.opened = IP_FALSE; IPDIAMETER_UNLOCK(); return retval; } /* *=========================================================================== * ipdiameter_common_exit *=========================================================================== * Description: Exit an opened Diameter * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipdiameter_common_exit(void) { Ip_err retval = IPCOM_SUCCESS; IPDIAMETER_LOCK(); /* delete timeout queue */ if (ipdiameter.tmo_head != IP_NULL) ipcom_tmo2_delete(ipdiameter.tmo_head); IPDIAMETER_UNLOCK(); #ifdef IPDIAMETER_ENABLE_REENTRANT (void)ipcom_mutex_delete(&ipdiameter.code_mutex); #endif /* reset the global data */ ipcom_memset(&ipdiameter, 0, sizeof(Ipdiameter_data)); return retval; } /* *=========================================================================== * ipdiameter_common_stop *=========================================================================== * Description: Stop the Diameter daemon * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipdiameter_common_stop(void) { diameterStopFlag = IP_TRUE; daemon_started = IP_FALSE; return IPCOM_SUCCESS; } /* *=========================================================================== * ipdiameter_node_add_session *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipdiameter_node_add_session(Ipdiameter_session *session) { Ip_err retval = IPCOM_SUCCESS; if (ipdiameter.opened == IP_FALSE) return IPCOM_ERR_NOT_OPENED; IPDIAMETER_LOCK(); if (ipdiameter.session_num > IPDIAMETER_MAX_SESSIONS) IPDIAMETER_LEAVE(IPCOM_ERR_REACHED_MAX); ipcom_list_insert_last(&ipdiameter.session_head, &session->session_list); ipdiameter.session_num++; leave: IPDIAMETER_UNLOCK(); return retval; } /* *=========================================================================== * ipdiameter_node_remove_session *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC void ipdiameter_node_remove_session(Ipdiameter_session *session) { IPDIAMETER_LOCK(); ip_assert(ipdiameter.session_num > 0); ipdiameter.session_num--; IPDIAMETER_UNLOCK(); } /* *=========================================================================== * ipdiameter_node_add_peer *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipdiameter_node_add_peer(Ipdiameter_peer_entry *peer) { Ip_err retval = IPCOM_SUCCESS; if (ipdiameter.opened == IP_FALSE) return IPCOM_ERR_NOT_OPENED; IPDIAMETER_LOCK(); if (ipdiameter.peer_num > IPDIAMETER_MAX_PEERS) IPDIAMETER_LEAVE(IPCOM_ERR_REACHED_MAX); ipcom_list_insert_last(&ipdiameter.peer_head, &peer->peer_list); ipdiameter.peer_num++; leave: IPDIAMETER_UNLOCK(); return retval; } /* *=========================================================================== * ipdiameter_node_remove_peer *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC void ipdiameter_node_remove_peer(Ipdiameter_peer_entry *peer) { IPDIAMETER_LOCK(); ip_assert(ipdiameter.peer_num > 0); ipdiameter.peer_num--; IPDIAMETER_UNLOCK(); } /* *=========================================================================== * ipdiameter_node_show_config *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC void ipdiameter_node_show_config(void) { int i; if (ipdiameter.opened == IP_FALSE) { ipcom_printf("Diameter is not opened\n"); return; } IPDIAMETER_LOCK(); if (daemon_started) ipcom_printf("State: opened and running\n"); else ipcom_printf("State: opened and NOT running\n"); ipcom_printf("Node type: %s\n", ipdiameter_peer_type(ipdiameter.node_type)); if ( ipdiameter.addr.sa.sa_family == IP_AF_INET || ipdiameter.addr.sa.sa_family == IP_AF_INET6 ) ipcom_printf("Node Addr: %s\n", ipdiameter_addr_to_str(&ipdiameter.addr)); else ipcom_printf("Node Addr: Unknow\n"); ipcom_printf("Diameter Id: %s\n", ipdiameter.host_id); ipcom_printf("Realm Id: %s\n", ipdiameter.realm_id); ipcom_printf("Vendor Id: %d\n", ipdiameter.vendor_id); ipcom_printf("TCP listening port: %u\n", ipdiameter.tcp_port); if (ipdiameter.num_appls == 0) ipcom_printf("Supported application: None\n"); for (i = 0; i < (Ip_s32)ipdiameter.num_appls; i++) ipcom_printf("Supported application: (%lu)%s\n", ipdiameter.appl_ids[i], ipdiameter_peer_appl(ipdiameter.appl_ids[i])); ipcom_printf("\nTimers:\n"); if (ipdiameter.node_type == DIAMETER_PEER_CLIENT) { ipcom_printf(" Session timeout: %d\n", ipdiameter.session_timeout); ipcom_printf(" Authorization lifetime: %d\n", ipdiameter.auth_lifetime); ipcom_printf(" Auth Grace Period: %d\n", ipdiameter.auth_grace_period); } ipcom_printf(" Peer reconnection interval: %d\n", ipdiameter.peer_reconn_interval); ipcom_printf(" Max reconnection attemps: %d\n", ipdiameter.max_reconn_attemp); ipcom_printf(" Watchdog timeout: %d\n", ipdiameter.watchdog_timeout); ipcom_printf(" Request retx interval: %d\n", ipdiameter.req_retx_interval); ipcom_printf(" Max request retx: %d\n", ipdiameter.max_req_retx); ipcom_printf("\nTLS:\n"); ipcom_printf(" TLS enabled: %s\n", ipdiameter.tls_enabled ? "Yes" : "No"); ipcom_printf(" Node certficate file: %s\n", ipdiameter.certfile ? ipdiameter.certfile : "N/A"); #if defined(IPCOM_USE_KEY_DB) && defined(_WRS_CONFIG_IPDIAMETER_SEC_KEY_DB) ipcom_printf(" Node private key ID: %s\n", ipdiameter.keyfile ? ipdiameter.keyfile : "N/A"); #else ipcom_printf(" Node private key file: %s\n", ipdiameter.keyfile ? ipdiameter.keyfile : "N/A"); ipcom_printf(" Node key file's key passwd: %s\n", ipdiameter.keypasswd ? ipdiameter.keypasswd : "N/A"); #endif /* _WRS_CONFIG_IPDIAMETER_SEC_KEY_DB */ ipcom_printf(" CA root certificate file: %s\n", ipdiameter.cacertfile ? ipdiameter.cacertfile : "N/A"); ipcom_printf(" Certificate root directory: %s\n", ipdiameter.rootdir ? ipdiameter.rootdir : "N/A"); ipcom_printf("\n"); IPDIAMETER_UNLOCK(); } /* *=========================================================================== * ipdiameter_node_show_peer *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC void ipdiameter_node_show_peer(void) { Ipdiameter_peer_entry *peer; if (ipdiameter.opened == IP_FALSE) { ipcom_printf("Diameter is not opened\n"); return; } IPDIAMETER_LOCK(); if (ipdiameter.peer_num == 0) ipcom_printf("Diameter Peers: None\n"); else ipcom_printf("Diameter Peers: %d\n", ipdiameter.peer_num); for (peer = IPCOM_LIST_FIRST(&ipdiameter.peer_head); peer != IP_NULL; peer = IPCOM_LIST_NEXT(&peer->peer_list)) { ipdiameter_print_peer(peer); ipcom_printf("\n"); } IPDIAMETER_UNLOCK(); } /* *=========================================================================== * ipdiameter_node_show_session *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC void ipdiameter_node_show_session(void) { Ipdiameter_session *session; if (ipdiameter.opened == IP_FALSE) { ipcom_printf("Diameter is not opened\n"); return; } IPDIAMETER_LOCK(); if (ipdiameter.session_num == 0) ipcom_printf("Diameter Sessions: None\n"); else ipcom_printf("Diameter Sessions: %d\n", ipdiameter.session_num); for (session = IPCOM_LIST_FIRST(&ipdiameter.session_head); session != IP_NULL; session = IPCOM_LIST_NEXT(&session->session_list)) { ipdiameter_print_session(session); ipcom_printf("\n"); } IPDIAMETER_UNLOCK(); } /* *=========================================================================== * ipdiameter_select_add_fd *=========================================================================== * Description: Add a new fd to daemon select fds * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipdiameter_select_add_fd(Ip_fd fd, Ipdiameter_peer_entry *peer) { Ipdiameter_selectfd *selectfd = IP_NULL; Ip_err retval = IPCOM_SUCCESS; IPDIAMETER_LOCK(); if (daemon_started == IP_FALSE) IPDIAMETER_LEAVE(IPCOM_ERR_NOT_STARTED); if (ipdiameter.fd_num > IPDIAMETER_MAX_FDS) IPDIAMETER_LEAVE(IPCOM_ERR_REACHED_MAX); /* go through all data sockets */ for (selectfd = IPCOM_LIST_FIRST(&ipdiameter.fd_head); selectfd != IP_NULL; selectfd = IPCOM_LIST_NEXT(&selectfd->fd_list)) { ip_assert(selectfd->peer != peer); ip_assert(selectfd->fd != fd); } /* allocated a new selectfd structure */ selectfd = (Ipdiameter_selectfd *)ipcom_malloc(sizeof(Ipdiameter_selectfd)); if (selectfd == IP_NULL) { IPCOM_LOG1(ERR, "ipdiameter_select_add_fd: out of memory ('selectfd' %ld bytes)", sizeof(Ipdiameter_selectfd)); IPDIAMETER_LEAVE(IPCOM_ERR_NO_MEMORY); } /* Fill in the fields */ selectfd->fd = fd; selectfd->peer = peer; /* add the new selectfd to the list */ ipcom_list_insert_last(&ipdiameter.fd_head, &selectfd->fd_list); ipdiameter.fd_num++; /* reset the max and readfd set */ max = (Ip_u32)IP_MAX(max, (Ip_u32)fd); IP_FD_SET(fd, &fds); leave: IPDIAMETER_UNLOCK(); return retval; } /* *=========================================================================== * ipdiameter_select_del_fd *=========================================================================== * Description: Delete a fd from daemon select fds * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipdiameter_select_del_fd(Ip_fd fd) { Ipdiameter_selectfd *selectfd; Ip_err retval = IPCOM_ERR_NOT_FOUND; IPDIAMETER_LOCK(); for (selectfd = IPCOM_LIST_FIRST(&ipdiameter.fd_head); selectfd != IP_NULL; selectfd = IPCOM_LIST_NEXT(&selectfd->fd_list)) { /* Find a selectfd entry with same fd. */ if (selectfd->fd == fd) { ip_assert(ipdiameter.fd_num > 0); /* remove the fd from the list */ ipcom_list_remove(&selectfd->fd_list); ipdiameter.fd_num--; /* free the memory */ ipcom_free(selectfd); /* remove the fd from the readfd set */ IP_FD_CLR(fd, &fds); /* remove the fd from the writefd set. It should be safe to clear the bit even if it is not set */ IP_FD_CLR(fd, &wfds); IPDIAMETER_LEAVE(IPCOM_SUCCESS); } } leave: IPDIAMETER_UNLOCK(); return retval; } /* *=========================================================================== * ipdiameter_set_wfd *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC void ipdiameter_set_wfd(Ip_fd fd) { IP_FD_SET(fd, &wfds); } /* *=========================================================================== * ipdiameter_node_show_route *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC void ipdiameter_node_show_route(void) { Ipdiameter_route_entry *route; if (ipdiameter.opened == IP_FALSE) { ipcom_printf("Diameter is not opened\n"); return; } IPDIAMETER_LOCK(); if (ipdiameter.route_num == 0) ipcom_printf("Diameter routes: None\n"); else ipcom_printf("Diameter routes: %d\n", ipdiameter.route_num); for( route = IPCOM_LIST_FIRST(&ipdiameter.route_head); route != IP_NULL; route = IPCOM_LIST_NEXT(&route->route_list)) { ipdiameter_print_route(route); ipcom_printf("\n"); } IPDIAMETER_UNLOCK(); } /* *=========================================================================== * ipdiameter_node_show_policy *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC void ipdiameter_node_show_policy(void) { Ipdiameter_policy_entry *policy; if (ipdiameter.opened == IP_FALSE) { ipcom_printf("Diameter is not opened\n"); return; } IPDIAMETER_LOCK(); if (ipdiameter.policy_num == 0) ipcom_printf("Diameter proxy local policies: None\n"); else ipcom_printf("Diameter proxy local policies: %d\n", ipdiameter.policy_num); for( policy = IPCOM_LIST_FIRST(&ipdiameter.policy_head); policy != IP_NULL; policy = IPCOM_LIST_NEXT(&policy->policy_list)) { ipdiameter_print_policy(policy); ipcom_printf("\n"); } IPDIAMETER_UNLOCK(); } /* *=========================================================================== * ipdiameter_version *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC const char * ipdiameter_version(void) { return "@(#) WR DIAMETER - Copyright 2007 Wind River (http://www.windriver.com). All rights reserved."; } /* **************************************************************************** * END OF FILE **************************************************************************** */