/* ipdiameter_routing.c - Diameter packet routing file */ /* * Copyright (c) 2012 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 -------------------- 01p,31jan12,hfc fix defect WIND00327678 01o,23nov10,hfc fix buildwarning.Defect WIND00242868 01n,19aug09,tlu Coverity fix 01m,14jul09,tlu Fix freeing memory when deleting a route (WIND00170412) 01l,03apr09,tlu Fix segmentation fault when no route is configured 01k,10mar09,tlu Fix Linux build warning 02j,17feb09,tlu Ensure a valid next hop exists for a selected route 02i,09jul08,tlu VSB build fix 02h,22may08,ggz Fix for defect WIND00124175 02g,08apr08,ggz Additional buffer IPDIAMETER_ADDITIONAL_BUFFER for result AVP 02f,08feb08,ggz Added peers failed state check 02e,29feb08,ggz Modified packet forwarding function return codes 02d,20feb08,ggz Added rerouting and failover support 02c,20feb08,ggz Moved packet adding to retransmission queue below the sending packet in request forwarding function 02b,20feb08,ggz Free the packet if the request sending fails in forward function. 02a,15feb08,ggz Route record should use hostid instead of peer's hostid 01z,05feb14,ggz Fix for memory leak in route delete function 01y,05feb14,ggz Fix for memory leak in route forward function in case packet is not added to the Trans. Queue 01x,05feb08,ggz Added error handling for permanent failures (for policy enforcement) 02w,02feb08,ggz Added recursive locks for policy and routing enforcement 01v,02feb08,ggz Added policy enforcment support 01u,20jan08,ggz Modifications in routing path 01t,13jan08,ggz Replaced public declarations with a static for local functions 01r,13jan07,ggz Moved public function declarations to header file 01q,10jan08,ggz Added protocol error handling 01p,17dec07,ggz Compiler warning clean up 01o,14dec07,ggz Added routing loop detection 01n,12dec07,ggz Added ipdiameter_get_avp_data function 01m,10dec07,tlu Add code to handle the case for an agent to initiate TCP connection to the other Diameter node 01l,06dec07,ggz Modifications to remove redudant ipdiameter_routing_proxy() function 01k,05dec07,tlu Fix length parameter passed to request pkt allocation function and some clean up 01j,05nov07,ggz Init allocated pending structure with 0 in request routing and forwarding functions. Some cleanup. 01i,03nov07,ggz In case of failure to allocate pending_packet_entry call ipdiameter_request_pkt_free instead of ipcom_free() 01h,29nov07,ggz Modifications in ipdiameter_routing_request_forward() 01g,28nov07,ggz Renamed ipdiameter_peer_agent_send_pkt() to ipdiameter_peer_agent_send_reponse_pkt() 01f,28nov07,ggz Modified the ipdiameter_peer_agent_send_pkt() to call ipdiameter_pkt_send() to send out a packet 01e,22nov07,ggz Cleaned up some debug information 01d,14nov07,tlu Modify how to add Route-Record AVP and proxy-info AVP and modify how to check for a duplicated request pkt 01c,12nov07,ggz vendor_id is passed as a pointer to ip_ntohl() in ipdiameter_routing_packet(). Fixed. 01b,14aug07,cwu Remove double free packet memory 01a,16jul07,cwu Created. */ /* **************************************************************************** * INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include "ipdiameter.h" #include "ipdiameter_routing.h" /* **************************************************************************** * EXTERN PROTOTYPES **************************************************************************** */ IP_EXTERN Ipdiameter_data ipdiameter; /* **************************************************************************** * DEFINES **************************************************************************** */ /* This is a compile flag, turning on it will build debug message into image */ #undef DIAMETER_DEBUG /* **************************************************************************** * LOCAL PROTOTYPES **************************************************************************** */ IP_STATIC Ip_err ipdiameter_get_avp_data(Ipdiameter_AVP_header *avp, Ip_u32 avp_code, Ip_u32 *data_length, Ip_u8 **data); IP_STATIC Ip_bool ipdiameter_agent_check_duplicate_retransmitted_packets( Ipdiameter_header *hdr, Ip_u8 *origin_host, Ipdiameter_peer_entry *peer_originator); IP_STATIC Ip_bool ipdiameter_agent_check_duplicate_pkt(Ipdiameter_header *hdr); IP_STATIC Ip_bool ipdiameter_routing_local_processing(Ipdiameter_header *packet); IP_STATIC Ip_bool ipdiameter_routing_redirect(Ipdiameter_header *packet, Ipdiameter_peer_entry *peer, Ipdiameter_route_entry *route); IP_STATIC Ip_err ipdiameter_routing_request_forward(Ipdiameter_header *packet, Ipdiameter_peer_entry *peer, Ipdiameter_peer_entry *new_peer); IP_STATIC Ip_bool ipdiameter_routing_relay(Ipdiameter_header *packet, Ipdiameter_peer_entry *peer, Ipdiameter_route_entry *route); IP_STATIC Ip_err ipdiameter_rerouting_request_forward(Ipdiameter_header *packet, Ipdiameter_peer_entry *peer, Ipdiameter_peer_entry *new_peer); /* **************************************************************************** * PUBLIC FUNCTIONS **************************************************************************** */ Ip_bool ipdiameter_peer_agent_send_response_pkt(Ipdiameter_peer_entry *peer, Ipdiameter_header *packet); /* *=========================================================================== * ipdiameter_create_route *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ipdiameter_route_entry * ipdiameter_create_route(Ipdiameter_route_entry *route, char *server) { Ipdiameter_route_entry *route_entry = IP_NULL; Ipdiameter_route_server *route_server = IP_NULL; Ipdiameter_route_server *route_server_entry = IP_NULL; Ip_bool server_found = IP_FALSE; #ifdef DIAMETER_DEBUG ipcom_printf("ipdiameter_create_route: function entry\n"); #endif if (ipdiameter.opened == IP_FALSE) { IPCOM_LOG0(INFO, "ipdiameter_create_route, diameter is not opened yet\n"); return IP_NULL; } if (ipdiameter.route_num >= IPDIAMETER_MAX_ROUTE_ENTRIES) { IPCOM_LOG0(INFO, "ipdiameter_create_route: max route entries reached!\n"); return IP_NULL; } route_entry = ipdiameter_find_route(route); if (route_entry != IP_NULL) { /* we check if the server parameter is already in the server list*/ for (route_server = IPCOM_LIST_FIRST(&route_entry->server_head); route_server != IP_NULL; route_server = IPCOM_LIST_NEXT(&route_server->server_list)) { if(0==ipcom_strcmp(route_server->server_id, server)) { server_found = IP_TRUE; break; } } /* if found, it is the same route entry, we do not need to do anything */ if (IP_TRUE == server_found) { IPCOM_LOG0(INFO, "ipdiameter_create_route, route entry exists already\n"); return IP_NULL; } /* if not found, add the new server into server list */ route_server_entry = ipcom_malloc (sizeof(Ipdiameter_route_server)); if (route_server_entry == IP_NULL) { IPCOM_LOG1(ERR, "ipdiameter_create_route: out of memory ('server' %ld bytes)", sizeof(Ipdiameter_route_server)); return IP_NULL; } /* coverity[fixed_size_dest] : FALSE */ ipcom_strcpy(route_server_entry->server_id, server); /* currently all the metric value is set to 1 */ route_server_entry->metric = 1; IPDIAMETER_ROUTE_LOCK(route_entry); ipcom_list_insert_last(&route_entry->server_head, &route_server_entry->server_list); route_entry->server_num++; IPDIAMETER_ROUTE_UNLOCK(route_entry); return route_entry; } /* new route */ route_entry = (Ipdiameter_route_entry *)ipcom_malloc(sizeof(Ipdiameter_route_entry)); if (route_entry == IP_NULL) { IPCOM_LOG1(ERR, "ipdiameter_create_route: out of memory ('route' %ld bytes)", sizeof(Ipdiameter_route_entry)); return IP_NULL; } /* set route entry data and insert into global ipdiameter route entry list */ ipcom_memcpy(route_entry, route, sizeof(Ipdiameter_route_entry)); route_server_entry = ipcom_malloc(sizeof(Ipdiameter_route_server)); if (route_server_entry == IP_NULL) { IPCOM_LOG1(ERR, "ipdiameter_create_route: out of memory ('server' %ld bytes)", sizeof(Ipdiameter_route_server)); ipcom_free(route_entry); return IP_NULL; } #ifdef IPDIAMETER_ENABLE_REENTRANT (void)ipcom_mutex_create(&(route_entry->code_mutex)); #endif /* add new server list */ /* coverity[fixed_size_dest] : FALSE */ ipcom_strcpy(route_server_entry->server_id, server); route_server_entry->metric = 1; IPDIAMETER_LOCK(); ipcom_list_init(&route_entry->server_head); ipcom_list_insert_last(&route_entry->server_head, &route_server_entry->server_list); route_entry->server_num++; ipcom_list_insert_last(&ipdiameter.route_head, &route_entry->route_list); ipdiameter.route_num++ ; IPDIAMETER_UNLOCK(); return route_entry; } /* *=========================================================================== * ipdiameter_delete_route *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_bool ipdiameter_delete_route(Ipdiameter_route_entry *route, char *server) { Ipdiameter_route_entry *route_entry = IP_NULL; Ipdiameter_route_server *route_server = IP_NULL; Ip_bool server_found = IP_FALSE; #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_delete_route: function entry\n"); #endif if (ipdiameter.opened == IP_FALSE) { IPCOM_LOG0(INFO, "ipdiameter_delete_route, diameter not opened yet\n"); return IPCOM_ERR_NOT_OPENED; } if (ipdiameter.route_num <= 0) { IPCOM_LOG0(INFO, "ipdiameter_delete_route: no route entry exist! \n"); return IP_NULL; } route_entry = ipdiameter_find_route(route); /* route entry found */ if (route_entry != IP_NULL) { /* is the server in the server list? */ for (route_server = IPCOM_LIST_FIRST(&route_entry->server_head); route_server !=IP_NULL; route_server = IPCOM_LIST_NEXT(&route_server->server_list)) { if (0 == ipcom_strcmp(route_server->server_id, server)) { server_found = IP_TRUE; break; } } /* if server is not found, do nothing */ if (server_found == IP_FALSE) { IPCOM_LOG0(INFO, "ipdiameter_delete_route: server not found\n"); return IP_FALSE; } /* server found, remove the server from the server list */ IPDIAMETER_LOCK(); ipcom_list_remove(&route_server->server_list); ipcom_free(route_server); route_entry->server_num--; /* if this is the last server in this route entry, delete route entry */ if (0 == route_entry->server_num) { ipcom_list_remove(&route_entry->route_list); #ifdef IPDIAMETER_ENABLE_REENTRANT (void)ipcom_mutex_delete(&(route_entry->code_mutex)); #endif ipcom_free(route_entry); ipdiameter.route_num --; } IPDIAMETER_UNLOCK(); return IP_TRUE; } IPCOM_LOG0(INFO, "ipdiameter_delete_route: route entry not found\n"); return IP_FALSE; } /* *=========================================================================== * ipdiameter_find_route *=========================================================================== * Description: * Parameters: * Returns: found entry or IP_NULL * */ IP_PUBLIC Ipdiameter_route_entry *ipdiameter_find_route(Ipdiameter_route_entry *route) { Ipdiameter_route_entry *route_entry = IP_NULL; IPDIAMETER_LOCK(); for (route_entry = IPCOM_LIST_FIRST(&ipdiameter.route_head); route_entry != IP_NULL; route_entry = IPCOM_LIST_NEXT(&route_entry->route_list)) { /* Find a route entry with same realm, vendor-id and applicaiton-id. */ if ((0 == ipcom_strcmp(route_entry->realm_id, route->realm_id)) && (route_entry->vendor_id == route->vendor_id) && ( (route_entry->application_id == route->application_id) || (0 ==route->application_id))) { IPDIAMETER_UNLOCK(); return route_entry; } } IPDIAMETER_UNLOCK(); IPCOM_LOG0(DEBUG, "ipdiameter_find_route: failed to find the route\n "); return IP_NULL; } /* *=========================================================================== * ipdiameter_peer_agent_send_response_pkt *=========================================================================== * Description: * Parameters: * Returns: * */ Ip_bool ipdiameter_peer_agent_send_response_pkt(Ipdiameter_peer_entry *peer, Ipdiameter_header *packet) { Ip_err retval = IPCOM_SUCCESS; Ip_fd fd = IP_INVALID_SOCKET; if (peer == IP_NULL) { IPCOM_LOG0(ERR, "ipdiameter_peer_pkt_send: invalid peer specified\n"); return IP_FALSE; } /* Pick matching IPv4 or IPv6 socket. */ #ifdef IPCOM_USE_INET if (peer->peer_addr.sa.sa_family == IP_AF_INET) fd = peer->fd; #endif #ifdef IPCOM_USE_INET6 if (peer->peer_addr.sa.sa_family == IP_AF_INET6) fd = peer->fd6; #endif if (fd < 0) { #ifdef DIAMETER_DEBUG ipcom_printf("ipdiameter_peer_agent_pkt_send: No valid socket descriptor \ to send to peer %s", ipdiameter_addr_to_str(&peer->peer_addr)); #endif return IP_FALSE; } /* Send the packet out */ IPDIAMETER_PEER_LOCK(peer); retval = ipdiameter_pkt_send(fd, peer->ssl, packet); IPDIAMETER_PEER_UNLOCK(peer); if (retval != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipdiameter_peer_pkt_send: failed to send out the package\n"); return IP_FALSE; } return IP_TRUE; } /* *=========================================================================== * ipdiameter_routing_local_processing *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_bool ipdiameter_routing_local_processing(Ipdiameter_header *packet) { #ifdef DIAMETER_DEBUG ipcom_printf("ipdiameter_routing_local_processing: request message is locally handled here\n"); #endif return IP_TRUE; } /* *=========================================================================== * ipdiameter_routing_rquest_forward *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipdiameter_routing_request_forward(Ipdiameter_header *packet, Ipdiameter_peer_entry * peer, Ipdiameter_peer_entry *new_peer) { Ip_size_t length = 0; Ip_u32 original_pkt_length = 0; Ip_u32 avp_length_original_host = 0; Ip_u32 avp_length_proxy_host = 0; Ip_u32 avp_length_proxy_state = 0; Ip_u32 avp_length_proxy_info = 0; char *local_test_state = "proxy_processing"; Ipdiameter_header *new_request_packet_header = IP_NULL; Ipdiameter_agent_pending_packet_entry *pending_packet_entry = IP_NULL; Ipdiameter_request_entry *new_request_entry = IP_NULL; Ip_err retval = IPCOM_SUCCESS; /* Get the new request packet length, it is the original length + new AVP length */ avp_length_original_host = (Ip_u32)(8 + ((Ip_s32)ipcom_strlen(peer->hostname)/4 )*4 \ + (((Ip_s32)ipcom_strlen(peer->hostname)%4) > 0 ? 1 : 0) * 4); avp_length_proxy_host = (Ip_u32) (8 + ((Ip_s32)ipcom_strlen(ipdiameter.host_id)/4)*4 \ + (((Ip_s32)ipcom_strlen(ipdiameter.host_id)%4) > 0 ? 1 : 0) * 4); avp_length_proxy_state = (Ip_u32) (8 + ((Ip_s32)ipcom_strlen(local_test_state)/4)*4 \ + (((Ip_s32)ipcom_strlen(local_test_state)%4 ) > 0 ? 1 : 0) * 4); avp_length_proxy_info = 8 + avp_length_proxy_host + avp_length_proxy_state; #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_request_forward: original peer host avp length: %d !\n", avp_length_original_host); ipcom_printf(" ipdiameter_routing_request_forward: proxy host avp length %d\n", avp_length_proxy_host); ipcom_printf(" ipdiameter_routing_request_forward: proxy state avp length %d\n", avp_length_proxy_state); ipcom_printf(" ipdiameter_routing_request_forward: proxy info avp length %d\n", avp_length_proxy_info); #endif original_pkt_length = ipdiameter_get_hdr_length( (Ip_u32 *)packet ); /* Allocate a packet with additional buffer IPDIAMETER_ADDITIONAL_BUFFER. This buffer is currently used for DIAMETER_AVP_CODE_Result_Code in case of error processing. Increase it if needed */ length = original_pkt_length + avp_length_original_host + avp_length_proxy_info + IPDIAMETER_ADDITIONAL_BUFFER; #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_request_forward: new packet length %d\n",length); #endif new_request_packet_header = ipdiameter_request_pkt_malloc((Ip_u32)length); if(IP_NULL == new_request_packet_header) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_request_forward: no enough memory :%d !\n",length); #endif return IPCOM_ERR_NO_MEMORY; } /* add the pending packet entry to the Diameter global data structure */ pending_packet_entry = (Ipdiameter_agent_pending_packet_entry *) ipcom_malloc(sizeof(Ipdiameter_agent_pending_packet_entry)); if(IP_NULL == pending_packet_entry) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_request_forward: \ no enough memory for pending packet entry!\n"); #endif ipdiameter_request_pkt_free(new_request_packet_header); return IPCOM_ERR_NO_MEMORY; } /* copy the original request packet content to the new packet buffer */ ipcom_memcpy(new_request_packet_header, packet, original_pkt_length); /* add Route-Record AVP */ (void)ipdiameter_avp_add_array(new_request_packet_header, DIAMETER_AVP_CODE_Route_Record, DIAMETER_AVP_M_FLAG, IP_NULL, (Ip_u8 *)ipdiameter.host_id, (Ip_u32)ipcom_strlen(ipdiameter.host_id)); /* add proxy-info grouped AVP */ { Ip_u8 buf[IPDIAMETER_MAX_AVP_SIZE]; Ipdiameter_AVP_header *proxy_info_avp; ipcom_memset(buf, 0, IPDIAMETER_MAX_AVP_SIZE); proxy_info_avp = (Ipdiameter_AVP_header *)buf; ipdiameter_set_avp_length((Ip_u32 *)proxy_info_avp, 8); /* add proxy-host AVP */ (void)ipdiameter_grouped_avp_add_array(proxy_info_avp, DIAMETER_AVP_CODE_Proxy_Host, DIAMETER_AVP_M_FLAG, IP_NULL, (Ip_u8 *)ipdiameter.host_id, (Ip_u32)ipcom_strlen(ipdiameter.host_id)); /* add proxy-state AVP */ (void)ipdiameter_grouped_avp_add_array(proxy_info_avp, DIAMETER_AVP_CODE_Proxy_State, DIAMETER_AVP_M_FLAG, IP_NULL, (Ip_u8 *)local_test_state, (Ip_u32)ipcom_strlen(local_test_state)); /* add proxy-info AVP to the packet */ length = ipdiameter_get_avp_length((Ip_u32 *)proxy_info_avp); /* take the AVP header part off */ length -= 8; (void)ipdiameter_avp_add_array(new_request_packet_header/*packet*/, DIAMETER_AVP_CODE_Proxy_Info, DIAMETER_AVP_M_FLAG, IP_NULL, buf+8, (Ip_u32)length); } /* set the new hbh_id value */ new_request_packet_header->hbh_id = ipdiameter_get_unique_id(); pending_packet_entry->original_peer = peer; pending_packet_entry->new_peer = new_peer; pending_packet_entry->original_hbh_id = packet->hbh_id; pending_packet_entry->new_hbh_id = new_request_packet_header->hbh_id; /* Save the next hop server for the realm rerouting */ ipcom_strcpy(pending_packet_entry->next_hope_server, new_peer->hostname); new_request_entry = (Ipdiameter_request_entry *) ((Ip_u8 *)new_request_packet_header + sizeof(Ipdiameter_header) - sizeof(Ipdiameter_request_entry)); new_request_entry->agent_pkt_entry = pending_packet_entry; if ((new_peer->state == DIAMETER_PEER_STATE_R_OPEN) || (new_peer->state == DIAMETER_PEER_STATE_I_OPEN)) goto transmit; retval = ipdiameter_peer_add_tx_pending_pkt(new_peer, (struct Ipdiameter_header *)new_request_packet_header); if (retval != IPCOM_SUCCESS) { ipdiameter_request_pkt_free(new_request_packet_header); IPCOM_LOG0(ERR, "ipdiameter_routing_request_forward: fail to add package to \ peer's transmiting pending list\n"); return IPCOM_ERR_IGNORED_PACKET; } retval = ipdiameter_peer_fsm(new_peer, IP_NULL, DIAMETER_PEER_EV_START); if (retval != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipdiameter_routing_request_forward: fail to run peer FSM\n"); } return IPCOM_SUCCESS; transmit: /* forward the request packet to the peer */ if(IPCOM_SUCCESS != ipdiameter_peer_pkt_send(new_peer, new_request_packet_header)) { ipdiameter_request_pkt_free(new_request_packet_header); #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_request_forward: packet \ %x failed to be sent to fd: %d\n", new_request_packet_header, new_peer->fd); #endif return IPCOM_ERR_FAILED; } ipdiameter_peer_add_req(new_peer, (struct Ipdiameter_header *)new_request_packet_header); #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_request_forward: packet \ %x is successfully forwarded to: %x\n", new_request_packet_header, peer); #endif return IPCOM_SUCCESS; } /* *=========================================================================== * ipdiameter_routing_redirect *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_bool ipdiameter_routing_redirect ( Ipdiameter_header *packet, Ipdiameter_peer_entry *peer, Ipdiameter_route_entry *route) { /* not supported feature, do nothing */ #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_redirect: processing...\n"); #endif return IP_TRUE; #if 0 /* This code is kept for later development */ Ip_u32 length = 0; Ip_err retval = IPCOM_SUCCESS; Ipdiameter_route_server *route_server = IP_NULL; Ipdiameter_header *answer_packet_header = IP_NULL; /* add every host AVP into the answer packet */ for( route_server = IPCOM_LIST_FIRST(&route->server_head); route_server !=IP_NULL; route_server = IPCOM_LIST_NEXT(&route_server->server_list) ) { length = (Ip_u32)ipcom_strlen(route_server->server_id); if( IPCOM_SUCCESS != ipdiameter_avp_add_array(packet, DIAMETER_AVP_CODE_Redirect_Host, DIAMETER_AVP_M_FLAG, IP_NULL, (Ip_u8 *)route_server->server_id, length) ) { ipcom_printf("ipdiameter_routing_redirect: failed to add Redirect_Host AVP\n"); return IP_FALSE; } ipcom_printf("ipdiameter_routing_redirect: add one Redirect_Host AVP\n"); } /* Add Result-Code AVP */ retval = ipdiameter_avp_add_u32(packet, DIAMETER_AVP_CODE_Result_Code, DIAMETER_AVP_M_FLAG, IP_NULL, DIAMETER_REDIRECT_INDICATION); if (retval != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipdiameter_routing_packet: failed to add Result-Code AVP\n"); return IP_FALSE; } packet->flags = IPDIAMETER_E_FLAG; if(IP_FALSE == ipdiameter_peer_pkt_send(peer, packet)) { ipcom_printf(" ipdiameter_routing_packet: failed to send the error reply packet!\n"); return IP_FALSE; } return IP_TRUE; #endif } /* *=========================================================================== * ipdiameter_routing_relay *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_bool ipdiameter_routing_relay (Ipdiameter_header *packet, Ipdiameter_peer_entry *peer, Ipdiameter_route_entry *route) { /* not supported feature, do nothing */ #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_relay: processing...\n"); #endif return IP_TRUE; } /* *=========================================================================== * ipdiameter_agent_check_duplicate_pkt *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_bool ipdiameter_agent_check_duplicate_pkt(Ipdiameter_header *hdr) { Ipdiameter_peer_entry *peer, *next_peer; Ip_u32 hbh_id = (Ip_u32)ip_ntohl(hdr->hbh_id); int i; IPDIAMETER_LOCK(); for(peer = IPCOM_LIST_FIRST(&ipdiameter.peer_head); peer != IP_NULL; peer = next_peer ) { next_peer = IPCOM_LIST_NEXT(&peer->peer_list); /* Check whether the packet is already in re-tx pending list */ if (ipdiameter_peer_find_req(peer, hbh_id) != IP_NULL) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_agent_check_duplicate_pkt: find duplicate pkt\n"); #endif IPDIAMETER_UNLOCK(); return IP_TRUE; } IPDIAMETER_PEER_LOCK(peer); /* check whether the package is already in the tx pending list */ for (i = 0; i < (Ip_s32)peer->package_num; i++) if (peer->packages[i] != IP_NULL) if ( ((Ipdiameter_header *)(peer->packages[i]))->hbh_id == hdr->hbh_id ) { #ifdef DIAMETER_DEBUG ipcom_printf("ipdiameter_agent_check_duplicate_pkt: package (%d) already in the pending list\n", hdr->hbh_id); #endif IPDIAMETER_PEER_UNLOCK(peer); IPDIAMETER_UNLOCK(); return IP_TRUE; } IPDIAMETER_PEER_UNLOCK(peer); } IPDIAMETER_UNLOCK(); return IP_FALSE; } /* *=========================================================================== * ipdiameter_routing_packet *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_bool ipdiameter_routing_packet(Ipdiameter_header *packet, Ipdiameter_peer_entry *peer) { Ip_u8 *origin_host = IP_NULL; Ip_u32 origin_host_len = 0; Ip_u8 *user_name = IP_NULL; Ip_u32 user_name_len = 0; Ip_u8 *dest_realm = IP_NULL; Ip_u32 dest_realm_len = 0; Ip_u8 *dest_host = IP_NULL; Ip_u32 dest_host_len = 0; Ip_u8 *route_avp_host = IP_NULL; Ip_u32 route_avp_host_len = 0; Ip_u8 *vendor_id = IP_NULL; Ip_u32 vendor_id_len = 0; Ipdiameter_AVP_header *avp; Ip_u8 *app_id = IP_NULL; Ip_u32 app_id_len = 0; Ip_err ret_val; Ipdiameter_peer_entry *new_peer = IP_NULL; Ipdiameter_peer_entry *route_entry_peer = IP_NULL; Ipdiameter_route_entry *route = IP_NULL; Ipdiameter_route_server *route_server = IP_NULL; Ipdiameter_route_entry route_tmp; for (avp = ipdiameter_avp_get_next(packet, IP_NULL); avp; avp = ipdiameter_avp_get_next(packet, (Ip_u8 *)avp)) { /* Check for request packet loop. Adding this code as a separate function will decrease the performance */ if(IPCOM_ERR_NOT_FOUND == ipdiameter_get_avp_data(avp, DIAMETER_AVP_CODE_Route_Record, &route_avp_host_len, &route_avp_host)) continue; /* Go to the next AVP */ if((route_avp_host_len > 0) && (0 == ipcom_strcmp(ipdiameter.host_id, (char *)route_avp_host))) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: request packet - loop detected!\n"); #endif if(IP_TRUE == ipdiameter_proxy_process_protocol_errors((struct Ipdiameter_header *)packet, peer, DIAMETER_LOOP_DETECTED)) return IP_TRUE; else return IP_FALSE; } } if(IP_TRUE == ipdiameter_agent_check_duplicate_pkt(packet)) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: this is a duplicate packet!\n"); #endif return IP_FALSE; } /* Validate the host */ if(IPCOM_ERR_BAD_PACKET == ipdiameter_get_avp(packet, DIAMETER_AVP_CODE_Origin_Host, &origin_host_len, &origin_host)) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: invalid packet - original host AVP!\n"); #endif return IP_FALSE; } /* Variable isFromNewPeer is set by function ipdiameter_agent_check_duplicate_pkt. The finction below should always be located next */ if(packet->flags & IPDIAMETER_T_FLAG) { if((origin_host_len > 0) && (IP_TRUE == ipdiameter_agent_check_duplicate_retransmitted_packets(packet, origin_host, peer))) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: this is a retransmitted failover duplicate packet!\n"); #endif return IP_TRUE; } } if(origin_host_len > 0) { if(IP_NULL != ipdiameter_find_policy(IP_FALSE, (char *)origin_host)) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: origin-host AVP is in black list!\n"); #endif if(IP_TRUE == ipdiameter_proxy_process_permanent_errors((struct Ipdiameter_header *)packet, peer, DIAMETER_AUTHORIZATION_REJECTED)) return IP_TRUE; else return IP_FALSE; } } if(IPCOM_ERR_BAD_PACKET == ipdiameter_get_avp(packet, DIAMETER_AVP_CODE_User_Name, &user_name_len, &user_name)) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: invalid packet - user name AVP!\n"); #endif return IP_FALSE; } if(user_name_len > 0) { if(IP_NULL != ipdiameter_find_policy(IP_TRUE, (char *) user_name)) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: user-name AVP is in black list!\n"); #endif if(IP_TRUE == ipdiameter_proxy_process_permanent_errors((struct Ipdiameter_header *)packet, peer, DIAMETER_AUTHORIZATION_REJECTED)) return IP_TRUE; else return IP_FALSE; } } /* Validate AVPs in the packet for routing */ if(IPCOM_ERR_BAD_PACKET == ipdiameter_get_avp(packet, DIAMETER_AVP_CODE_Destination_Realm, &dest_realm_len, &dest_realm)) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: invalid packet - realm AVP!\n"); #endif return IP_FALSE; } if(IPCOM_ERR_BAD_PACKET == ipdiameter_get_avp(packet, DIAMETER_AVP_CODE_Destination_Host, &dest_host_len, &dest_host)) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: invalid packet - destination host AVP!\n"); #endif return IP_FALSE; } if (IPCOM_ERR_BAD_PACKET == ipdiameter_get_avp(packet, DIAMETER_AVP_CODE_Vendor_Id, &vendor_id_len, &vendor_id)) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: invalid packet - vendor AVP!\n"); #endif return IP_FALSE; } /* Processing local request, RFC3588 6.1.4 */ if(( dest_host_len == 0 ) && ( dest_realm_len == 0 )) { ipdiameter_routing_local_processing(packet); return IP_TRUE; } if( dest_host_len > 0 ) /* We have a dest-host avp, we must handle it first */ { /* If it matches the local host, then it is resolved immediately */ if(0 == ipcom_strcasecmp( (char *)dest_host, ipdiameter.host_id)) { ipdiameter_routing_local_processing(packet); #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: dest-host is local!\n"); #endif return IP_TRUE; } /* If it matches one of the peers, then it is forwarded to peer */ route_entry_peer = ipdiameter_find_peer_host(dest_host); if((IP_NULL != route_entry_peer) && (route_entry_peer->state != DIAMETER_PEER_STATE_FAILED)) { /* Forward the request packet to the peer */ ret_val = ipdiameter_routing_request_forward(packet, peer, route_entry_peer); if(ret_val == IPCOM_SUCCESS) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: Packet has been forwarded or placed in trans/retrans queue. peer %s\n", route_entry_peer->hostname); #endif return IP_TRUE; } else if(ret_val == IPCOM_ERR_NO_MEMORY) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: Packet forwarding failed - no memory\n"); #endif goto failed_to_forward; } } #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing: We can not match the dest-host AVP, we will do \ routing based on dest-realm AVP\n"); #endif } /* If the dest-realm AVP exists, try to do the realm routing */ if(dest_realm_len > 0) { /* We will attempt to route the request, get and validate mandatory AVPs for routing */ ipcom_memset( &route_tmp, 0, sizeof(route_tmp )); ipcom_strcpy( route_tmp.realm_id, (char *)dest_realm ); if(vendor_id_len > 0) route_tmp.vendor_id = (Ip_u32)ip_ntohl(*(Ip_u32 *)vendor_id); else route_tmp.vendor_id = 0; for (avp = ipdiameter_avp_get_next(packet, IP_NULL); avp; avp = ipdiameter_avp_get_next(packet, (Ip_u8 *)avp)) { app_id_len = 0; if(IPCOM_ERR_NOT_FOUND == ipdiameter_get_avp_data(avp, DIAMETER_AVP_CODE_Auth_Application_Id, &app_id_len, &app_id)) if(IPCOM_ERR_NOT_FOUND == ipdiameter_get_avp_data(avp, DIAMETER_AVP_CODE_Acct_Application_Id, &app_id_len, &app_id)) if (IPCOM_ERR_NOT_FOUND == ipdiameter_get_avp_data(avp, DIAMETER_AVP_CODE_Vendor_Specific_App_Id, &app_id_len, &app_id)) if(app_id_len == 0) continue; route_tmp.application_id = (Ip_u32)ip_ntohl(*(Ip_u32 *)app_id); #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: routing table lookup for app_id %d\n", app_id); #endif route = ipdiameter_find_route(&route_tmp); if (route == IP_NULL) continue; /* Ensure there is a valid next hop for this route */ for(route_server = IPCOM_LIST_FIRST(&route->server_head); route_server != IP_NULL; route_server = IPCOM_LIST_NEXT(&route_server->server_list)) { new_peer = ipdiameter_find_peer_host((Ip_u8 *)route_server->server_id); if ((IP_NULL != new_peer) && (new_peer->state != DIAMETER_PEER_STATE_FAILED)) { /* Found a valid next hop */ break; } } if (route_server == IP_NULL) /* No valid next hop for the route, set route to NULL */ route = IP_NULL; if( IP_NULL != route ) { break; } } if (IP_NULL == route) /* If no route has been found. Try to find a default route */ route = ipdiameter_find_default_route(); /* No route found */ if (IP_NULL == route) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: no routing entry has been found!\n"); #endif goto failed_to_forward; /* No route found packet to be routed */ } } /* No route found */ if (IP_NULL == route) { goto failed_to_forward; /* No route found packet to be routed */ } switch(route->action_type) { case DIAMETER_LOCAL: ipdiameter_routing_local_processing(packet); #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: dest-realm is matched locally!\n"); #endif break; case DIAMETER_REDIRECT: ipdiameter_routing_redirect(packet, peer, route); #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: a redirect route entry found!\n"); #endif break; case DIAMETER_PROXY: #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: proxy route entry found!\n"); #endif IPDIAMETER_ROUTE_LOCK(route); route_server = IPCOM_LIST_FIRST(&route->server_head); for(route_server = route_server; route_server != IP_NULL; route_server = IPCOM_LIST_NEXT(&route_server->server_list)) { new_peer = ipdiameter_find_peer_host((Ip_u8 *)route_server->server_id); if ((IP_NULL != new_peer) && (new_peer->state != DIAMETER_PEER_STATE_FAILED)) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: peer found. peer: %s\n", new_peer->hostname); #endif /* One of multiple destinations is OK. Consider the proxing succesfull */ ret_val = ipdiameter_routing_request_forward(packet, peer, new_peer); if(ret_val == IPCOM_SUCCESS) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: Packet has been forwarded or placed in trans/retrans queue. server_id: %s\n" ,route_server->server_id); #endif IPDIAMETER_ROUTE_UNLOCK(route); return IP_TRUE; } else if(ret_val == IPCOM_ERR_NO_MEMORY) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: Packet forwarding failed - no memory\n"); #endif IPDIAMETER_ROUTE_UNLOCK(route); goto failed_to_forward; } /* For all other cases try to reroute */ } } IPDIAMETER_ROUTE_UNLOCK(route); #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: Failed to route the packet!\n"); #endif goto failed_to_forward; case DIAMETER_RELAY: ipdiameter_routing_relay(packet, peer, route); #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: a relay route entry found!\n"); #endif default: #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_packet: route entry with wrong type!\n"); #endif break; } failed_to_forward: if (IP_TRUE == ipdiameter_proxy_process_protocol_errors((struct Ipdiameter_header *) packet, peer, DIAMETER_UNABLE_TO_DELIVER)) return IP_TRUE; else return IP_FALSE; } /* *=========================================================================== * ipdiameter_agent_request_entry_find *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ipdiameter_request_entry *ipdiameter_agent_request_entry_find(Ipdiameter_peer_entry *peer, Ipdiameter_header *hdr) { Ipdiameter_request_entry *request_entry = IP_NULL; IPDIAMETER_PEER_LOCK(peer); for(request_entry = IPCOM_LIST_FIRST(&peer->request_head); request_entry != IP_NULL; request_entry = IPCOM_LIST_NEXT(&(request_entry->request_list))) { if(request_entry->agent_pkt_entry->new_hbh_id == hdr->hbh_id) { IPDIAMETER_PEER_UNLOCK(peer); #ifdef DIAMETER_DEBUG ipcom_printf(" !!!!!!!!! we find reply entry!!!!!!!!\n"); #endif return request_entry; } } IPDIAMETER_PEER_UNLOCK(peer); #ifdef DIAMETER_DEBUG ipcom_printf(" !!!!!!!!! we can not find reply entry!!!!!!!!\n"); #endif return IP_NULL; } /* *=========================================================================== * ipdiameter_agent_packet_response *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_bool ipdiameter_agent_packet_response (Ipdiameter_request_entry *entry, Ipdiameter_header *hdr) { hdr->hbh_id = entry->agent_pkt_entry->original_hbh_id ; if(IP_FALSE == ipdiameter_peer_agent_send_response_pkt(entry->agent_pkt_entry->original_peer, hdr)) { #ifdef DIAMETER_DEBUG ipcom_printf("ipdiameter_routing_request_forward: packet:%x failed to be sent to: %d\n", hdr, entry->agent_pkt_entry->original_peer->fd); #endif ipdiameter_reqent_free(entry); /* free request entry here */ return IP_FALSE; } /* free the pending packet entry */ ipdiameter_reqent_free(entry); return IP_TRUE; } /* *=========================================================================== * ipdiameter_agent_packet_response *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipdiameter_get_avp_data( Ipdiameter_AVP_header *avp, Ip_u32 avp_code, Ip_u32 *data_length, Ip_u8 **data) { Ip_u32 avp_length, avp_header_length; Ip_u8 *data_v; Ip_u32 avp_code_v = 0; *data_length = 0; *data = IP_NULL; avp_code_v = (Ip_u32)ip_ntohl(avp->code); avp_length = ipdiameter_get_avp_length((Ip_u32 *)avp); if (avp_code_v == avp_code) { if ((avp->flags & DIAMETER_AVP_V_FLAG) != 0) { avp_header_length = 12; data_v = (Ip_u8 *)avp->data; } else { avp_header_length = 8; data_v = (Ip_u8 *)&avp->vendor_id; } /* data length */ *data_length = avp_length - avp_header_length; *data = data_v; return IPCOM_SUCCESS; } return IPCOM_ERR_NOT_FOUND; } /* *=========================================================================== * ipdiameter_find_policy *=========================================================================== * Description: * Parameters: * Returns: found entry or IP_NULL * */ IP_PUBLIC Ipdiameter_policy_entry *ipdiameter_find_policy(Ip_bool username, char *name) { Ipdiameter_policy_entry* policy_entry = IP_NULL; if(name == IP_NULL) { /* This case should not happen */ #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_find_policy: Invalid request\n"); #endif return IP_NULL; } IPDIAMETER_LOCK(); for(policy_entry = IPCOM_LIST_FIRST(&ipdiameter.policy_head); policy_entry != IP_NULL; policy_entry = IPCOM_LIST_NEXT(&(policy_entry->policy_list))) { if (username) { if((policy_entry->have_uname) && (0 == ipcom_strcmp(name, policy_entry->name.username))) { IPDIAMETER_UNLOCK(); return policy_entry; } } else { if(!(policy_entry->have_uname) && (0 == ipcom_strcmp(name, policy_entry->name.hostid))) { IPDIAMETER_UNLOCK(); return policy_entry; } } } /* No entry has is found */ IPDIAMETER_UNLOCK(); return IP_NULL; } /* *=========================================================================== * ipdiameter_del_route *=========================================================================== * Description: * Parameters: * Returns: found entry or IP_NULL * */ IP_PUBLIC Ip_bool ipdiameter_del_route(Ipdiameter_route_entry *route) { Ipdiameter_route_entry *route_entry = IP_NULL; Ipdiameter_route_server *route_server = IP_NULL; Ipdiameter_route_server *route_server_next = IP_NULL; #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_delete_route: function entry\n"); #endif if (ipdiameter.opened == IP_FALSE) { IPCOM_LOG0(INFO, "ipdiameter_delete_route, diameter not opened yet\n"); return IPCOM_ERR_NOT_OPENED; } if (ipdiameter.route_num <= 0) { IPCOM_LOG0(INFO, "ipdiameter_delete_route: no route entry exist! \n"); return IP_NULL; } route_entry = ipdiameter_find_route(route); /* Route entry found */ if (route_entry != IP_NULL) { IPDIAMETER_LOCK(); for (route_server = IPCOM_LIST_FIRST(&route_entry->server_head); route_server !=IP_NULL; route_server = route_server_next) { route_server_next = IPCOM_LIST_NEXT(&route_server->server_list); ipcom_list_remove(&route_server->server_list); ipcom_free(route_server); route_entry->server_num--; } ipcom_list_remove(&route_entry->route_list); ipcom_free(route_entry); ipdiameter.route_num --; IPDIAMETER_UNLOCK(); return IP_TRUE; } IPCOM_LOG0(INFO, "ipdiameter_delete_route: route entry not found\n"); return IP_FALSE; } /* *=========================================================================== * ipdiameter_proxy_process_protocol_errors *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_bool ipdiameter_proxy_process_protocol_errors(struct Ipdiameter_header *packet, Ipdiameter_peer_entry *peer, Ip_u32 error_type) { Ip_err retval = IPCOM_SUCCESS; #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_proxy_process_protocol_errors: process error %d\n",error_type); #endif switch(error_type) { case DIAMETER_UNABLE_TO_DELIVER: case DIAMETER_LOOP_DETECTED: retval = ipdiameter_avp_add_u32((Ipdiameter_header *) packet, DIAMETER_AVP_CODE_Result_Code, DIAMETER_AVP_M_FLAG, IP_NULL, error_type); break; case DIAMETER_REALM_NOT_SERVED: case DIAMETER_APPLICATION_UNSUPPORTED: case DIAMETER_COMMAND_UNSUPPORTED: case DIAMETER_TOO_BUSY: case DIAMETER_REDIRECT_INDICATION: case DIAMETER_INVALID_HDR_BITS: case DIAMETER_INVALID_AVP_BITS: case DIAMETER_UNKNOWN_PEER: #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_proxy_process_protocol_errors: unsupported error type %d!,\n",error_type); #endif return IP_FALSE; default: #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_proxy_process_protocol_errors: unkown error type %d!,\n",error_type); #endif return IP_FALSE; } if(IPCOM_SUCCESS != retval) { IPCOM_LOG0(ERR, " ipdiameter_proxy_process_protocol_errors: failed to add Result-Code AVP!\n"); return IP_FALSE; } ((Ipdiameter_header *)packet)->flags = IPDIAMETER_E_FLAG; if(IPCOM_SUCCESS != ipdiameter_peer_pkt_send(peer, (Ipdiameter_header *)packet)) { ipcom_printf(" ipdiameter_proxy_process_protocol_errors: failed to send the error reply packet!\n"); return IP_FALSE; } return IP_TRUE; } /* *=========================================================================== * ipdiameter_proxy_process_permanent_errors *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_bool ipdiameter_proxy_process_permanent_errors(struct Ipdiameter_header *packet, Ipdiameter_peer_entry *peer, Ip_u32 error_type) { Ip_err retval = IPCOM_SUCCESS; #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_proxy_process_permanent_errors: process error %d\n",error_type); #endif switch(error_type) { case DIAMETER_AUTHORIZATION_REJECTED: retval = ipdiameter_avp_add_u32((Ipdiameter_header *) packet, DIAMETER_AVP_CODE_Result_Code, DIAMETER_AVP_M_FLAG, IP_NULL, error_type); break; case DIAMETER_AVP_UNSUPPORTED: case DIAMETER_UNKNOWN_SESSION_ID: case DIAMETER_INVALID_AVP_VALUE: case DIAMETER_MISSING_AVP: case DIAMETER_RESOURCES_EXCEEDED: case DIAMETER_CONTRADICTING_AVPS: case DIAMETER_AVP_NOT_ALLOWED: case DIAMETER_AVP_OCCURS_TOO_MANY_TIMES: case DIAMETER_NO_COMMON_APPLICATION: case DIAMETER_UNSUPPORTED_VERSION: case DIAMETER_UNABLE_TO_COMPLY: case DIAMETER_INVALID_BIT_IN_HEADER: case DIAMETER_INVALID_AVP_LENGTH: case DIAMETER_INVALID_MESSAGE_LENGTH: case DIAMETER_INVALID_AVP_BIT_COMBO: case DIAMETER_NO_COMMON_SECURITY: #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_proxy_process_permanent_errors: unsupported error type %d!,\n",error_type); #endif return IP_FALSE; default: #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_proxy_process_permanent_errors: unkown error type %d!,\n",error_type); #endif return IP_FALSE; } if(IPCOM_SUCCESS != retval) { IPCOM_LOG0(ERR, " ipdiameter_proxy_process_permanent_errors: failed to add Result-Code AVP!\n"); return IP_FALSE; } ((Ipdiameter_header *)packet)->flags = ((Ipdiameter_header *)packet)->flags & (~IPDIAMETER_R_FLAG); if(IPCOM_SUCCESS != ipdiameter_peer_pkt_send(peer, (Ipdiameter_header *)packet)) { ipcom_printf(" ipdiameter_proxy_process_protocol_errors: failed to send the error reply packet!\n"); return IP_FALSE; } return IP_TRUE; } /* *=========================================================================== * ipdiameter_create_policy *=========================================================================== * Description: * Parameters: * Returns: Success or failed * */ IP_PUBLIC Ip_err ipdiameter_create_policy(Ipdiameter_policy_entry *policy) { Ipdiameter_policy_entry* policy_entry = IP_NULL; if (policy == IP_NULL) { ipcom_printf("ipdiameter_create_policy: the function is called with NULL policy pointer"); return IPCOM_ERR_FAILED; } if(policy->have_uname) { if (IP_NULL != ipdiameter_find_policy(policy->have_uname, policy->name.username)) { ipcom_printf("ipdiameter_create_policy: The routing entry already exists\n"); return IPCOM_ERR_FAILED; } }else { if (IP_NULL != ipdiameter_find_policy(policy->have_uname, policy->name.hostid)) { ipcom_printf("ipdiameter_create_policy: The routing entry already exists\n"); return IPCOM_ERR_FAILED; } } policy_entry = (Ipdiameter_policy_entry *)ipcom_malloc(sizeof(Ipdiameter_policy_entry)); if (policy_entry == IP_NULL) { ipcom_printf("ipdiameter_create_policy: out of memory (%ld bytes)", sizeof(Ipdiameter_policy_entry)); return IPCOM_ERR_FAILED; } /* Fill in policy data */ ipcom_memset(policy_entry, 0, sizeof(Ipdiameter_policy_entry)); ipcom_memcpy(policy_entry, policy, sizeof(Ipdiameter_policy_entry)); #ifdef IPDIAMETER_ENABLE_REENTRANT (void)ipcom_mutex_create(&(policy_entry->code_mutex)); #endif IPDIAMETER_LOCK(); ipcom_list_insert_last(&ipdiameter.policy_head, (Ipcom_list *)policy_entry); ipdiameter.policy_num++; IPDIAMETER_UNLOCK(); return IPCOM_SUCCESS; } /* *=========================================================================== * ipdiameter_delete_policy *=========================================================================== * Description: * Parameters: * Returns: found entry or IP_NULL * */ IP_PUBLIC Ip_err ipdiameter_delete_policy(Ipdiameter_policy_entry *policy) { Ipdiameter_policy_entry* policy_entry_tmp = IP_NULL; if (policy == IP_NULL) { ipcom_printf("ipdiameter_delete_policy: the function is called with a NULL policy pointer\n"); return IPCOM_ERR_FAILED; } if(policy->have_uname) { if (IP_NULL == (policy_entry_tmp = ipdiameter_find_policy(policy->have_uname, policy->name.username))) { ipcom_printf("ipdiameter_delete_policy: The policy entry does not exist\n"); return IPCOM_ERR_FAILED; } }else { if (IP_NULL == (policy_entry_tmp = ipdiameter_find_policy(policy->have_uname, policy->name.hostid))) { ipcom_printf("ipdiameter_delete_policy: The policy entry does not exist\n"); return IPCOM_ERR_FAILED; } } IPDIAMETER_LOCK(); ipcom_list_remove((Ipcom_list*) policy_entry_tmp); ipdiameter.policy_num--; IPDIAMETER_UNLOCK(); #ifdef IPDIAMETER_ENABLE_REENTRANT (void)ipcom_mutex_delete(&(policy_entry_tmp->code_mutex)); #endif ipcom_free(policy_entry_tmp); return IPCOM_SUCCESS; } #ifndef IP_PORT_VXWORKS /* for Linux recursive semaphore */ /* *=========================================================================== * ipdiameter_route_lck *=========================================================================== * Description: This function provides a recursive semaphore lock. * Parameters: * Returns: * */ IP_PUBLIC void ipdiameter_route_lck(Ipdiameter_route_entry *route) { Ip_pid_t cpid = ipcom_getpid(); #ifdef DIAMETER_DEBUG ipcom_printf("Calling ipdiameter_route_lck: route %x, pid %d, counter %d, cpid %d\n", route, route->pid, route->s_counter, cpid); #endif if (route->pid == 0) { /* No one has semaphore, take it */ route->pid = cpid; route->s_counter = 1; ipcom_mutex_lock(route->code_mutex); } else if (route->pid != cpid) /* block a different process/task */ ipcom_mutex_lock(route->code_mutex); else /* the semaphore is taken by the same process, increase the counter */ route->s_counter ++; #ifdef DIAMETER_DEBUG ipcom_printf("ipdiameter_route_lck: peer %x, pid %d, counter %d, cpid %d\n", route, route->pid, route->s_counter, cpid); #endif } /* *=========================================================================== * ipdiameter_route_unlck *=========================================================================== * Description: This function provides a recursive semaphore unlock. * Parameters: * Returns: * */ IP_PUBLIC void ipdiameter_route_unlck(Ipdiameter_route_entry *route) { Ip_pid_t cpid = ipcom_getpid(); #ifdef DIAMETER_DEBUG ipcom_printf("Calling ipdiameter_peer_unlck: route %x, pid %d, counter %d, cpid %d\n", route, route->pid, route->s_counter, cpid); #endif if (route->pid != cpid) { IPCOM_LOG0(ERR, "ipdiameter_peer_unlck: released by a different process "); return; } /* decrease the counter */ route->s_counter --; if (route->s_counter == 0) { /* clear the semaphore PID */ route->pid = 0; /* release the semaphore */ ipcom_mutex_unlock(route->code_mutex); } #ifdef DIAMETER_DEBUG ipcom_printf("ipdiameter_peer_unlck: peer %x, pid %d, counter %d, cpid %d\n", route, route->pid, route->s_counter, cpid); #endif } /* *=========================================================================== * ipdiameter_policy_lck *=========================================================================== * Description: This function provides a recursive semaphore lock. * Parameters: * Returns: * */ IP_PUBLIC void ipdiameter_policy_lck(Ipdiameter_policy_entry *policy) { Ip_pid_t cpid = ipcom_getpid(); #ifdef DIAMETER_DEBUG ipcom_printf("Calling ipdiameter_policy_lck: policy %x, pid %d, counter %d, cpid %d\n", policy, policy->pid, policy->s_counter, cpid); #endif if (policy->pid == 0) { /* No one has semaphore, take it */ policy->pid = cpid; policy->s_counter = 1; ipcom_mutex_lock(policy->code_mutex); } else if (policy->pid != cpid) /* block a different process/task */ ipcom_mutex_lock(policy->code_mutex); else /* the semaphore is taken by the same process, increase the counter */ policy->s_counter ++; #ifdef DIAMETER_DEBUG ipcom_printf("ipdiameter_peer_lck: peer %x, pid %d, counter %d, cpid %d\n", policy, policy->pid, policy->s_counter, cpid); #endif } /* *=========================================================================== * ipdiameter_policy_unlck *=========================================================================== * Description: This function provides a recursive semaphore unlock. * Parameters: * Returns: * */ IP_PUBLIC void ipdiameter_policy_unlck(Ipdiameter_policy_entry *policy) { Ip_pid_t cpid = ipcom_getpid(); #ifdef DIAMETER_DEBUG ipcom_printf("Calling ipdiameter_policy_unlck: route %x, pid %d, counter %d, cpid %d\n", policy, policy->pid, policy->s_counter, cpid); #endif if (policy->pid != cpid) { IPCOM_LOG0(ERR, "ipdiameter_policy_unlck: released by a different process "); return; } /* decrease the counter */ policy->s_counter --; if (policy->s_counter == 0) { /* clear the semaphore PID */ policy->pid = 0; /* release the semaphore */ ipcom_mutex_unlock(policy->code_mutex); } #ifdef DIAMETER_DEBUG ipcom_printf("ipdiameter_peer_unlck: peer %x, pid %d, counter %d, cpid %d\n", policy, policy->pid, policy->s_counter, cpid); #endif } #endif /* !IP_PORT_VXWORKS */ /* *=========================================================================== * ipdiameter_find_default_route *=========================================================================== * Description: * Parameters: * Returns: found entry or IP_NULL * */ IP_PUBLIC Ipdiameter_route_entry *ipdiameter_find_default_route(void) { Ipdiameter_route_entry *route_entry = IP_NULL; #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_find_default_route: function entry\n"); #endif IPDIAMETER_LOCK(); for (route_entry = IPCOM_LIST_FIRST(&ipdiameter.route_head); route_entry != IP_NULL; route_entry = IPCOM_LIST_NEXT(&route_entry->route_list)) { if (0 == ipcom_strcmp(route_entry->realm_id, "*")) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_find_default_route: routing based on default route\n"); #endif IPDIAMETER_UNLOCK(); return route_entry; } } IPDIAMETER_UNLOCK(); IPCOM_LOG0(DEBUG, " ipdiameter_find_default_route: failed to find the route\n "); return IP_NULL; } /* *=========================================================================== * ipdiameter_rerouting_packet *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_bool ipdiameter_rerouting_packet(Ipdiameter_header *packet, Ipdiameter_peer_entry *peer) { /* This routine will attempt to reroute the packets from transmisison and retransmission queue. it rerouted then the packets will be moved into new peer's queue. If rerouting fails the calling function is responsible for freeing the queue and generateing of error messages. */ Ip_u8 *dest_realm = IP_NULL; Ip_u32 dest_realm_len = 0; Ip_u8 *dest_host = IP_NULL; Ip_u32 dest_host_len = 0; Ip_u8 *vendor_id = IP_NULL; Ip_u32 vendor_id_len = 0; Ipdiameter_AVP_header *avp; Ip_u8 *app_id = IP_NULL; Ip_u32 app_id_len = 0; Ipdiameter_peer_entry *new_peer = IP_NULL; Ipdiameter_route_entry *route = IP_NULL; Ipdiameter_route_server *route_server = IP_NULL; Ipdiameter_request_entry *reroute_request_entry = IP_NULL; Ipdiameter_route_entry route_tmp; Ip_err ret_val; /* Validate AVPs in the packet for routing */ if(IPCOM_ERR_BAD_PACKET == ipdiameter_get_avp(packet, DIAMETER_AVP_CODE_Destination_Realm, &dest_realm_len, &dest_realm)) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_rerouting_packet: invalid packet - realm AVP!\n"); #endif return IP_FALSE; } if(IPCOM_ERR_BAD_PACKET == ipdiameter_get_avp(packet, DIAMETER_AVP_CODE_Destination_Host, &dest_host_len, &dest_host)) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_rerouting_packet: invalid packet - destination host AVP!\n"); #endif return IP_FALSE; } if (IPCOM_ERR_BAD_PACKET == ipdiameter_get_avp(packet, DIAMETER_AVP_CODE_Vendor_Id, &vendor_id_len, &vendor_id)) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_rerouting_packet: invalid packet - vendor AVP!\n"); #endif return IP_FALSE; } if( dest_host_len > 0 ) /* we have a dest-host avp, we must handle it first */ { /* No rerouting for peers. Directly connected peers have unique host ID. if it failed then there canot be any other peer with the smae host ID. Try realm based rerouting. */ goto realm_routing; } realm_routing: /* If the dest-realm AVP exists, try to do the realm routing */ if(dest_realm_len > 0) { /* We will attempt to route the request, get and validate mandatory AVPs for routing */ ipcom_memset( &route_tmp, 0, sizeof(route_tmp )); ipcom_strcpy( route_tmp.realm_id, (char *)dest_realm ); if(vendor_id_len > 0) route_tmp.vendor_id = (Ip_u32)ip_ntohl(*(Ip_u32 *)vendor_id); else route_tmp.vendor_id = 0; for (avp = ipdiameter_avp_get_next(packet, IP_NULL); avp; avp = ipdiameter_avp_get_next(packet, (Ip_u8 *)avp)) { app_id_len = 0; if(IPCOM_ERR_NOT_FOUND == ipdiameter_get_avp_data(avp, DIAMETER_AVP_CODE_Auth_Application_Id, &app_id_len, &app_id)) if(IPCOM_ERR_NOT_FOUND == ipdiameter_get_avp_data(avp, DIAMETER_AVP_CODE_Acct_Application_Id, &app_id_len, &app_id)) if (IPCOM_ERR_NOT_FOUND == ipdiameter_get_avp_data(avp, DIAMETER_AVP_CODE_Vendor_Specific_App_Id, &app_id_len, &app_id)) if(app_id_len == 0) continue; route_tmp.application_id = (Ip_u32)ip_ntohl(*(Ip_u32 *)app_id); #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_rerouting_packet: routing table lookup for app_id %d\n", app_id); #endif route = ipdiameter_find_route(&route_tmp); if (route == IP_NULL) continue; /* Ensure there is a valid next hop for this route */ for(route_server = IPCOM_LIST_FIRST(&route->server_head); route_server != IP_NULL; route_server = IPCOM_LIST_NEXT(&route_server->server_list)) { new_peer = ipdiameter_find_peer_host((Ip_u8 *)route_server->server_id); if ((IP_NULL != new_peer) && (new_peer->state != DIAMETER_PEER_STATE_FAILED)) { /* Found a valid next hop */ break; } } if (route_server == IP_NULL) /* No valid next hop for the route, set route to NULL */ route = IP_NULL; if( IP_NULL != route ) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_rerouting_packet: routing entry has been found!\n"); #endif break; } } if(IP_NULL == route) /* If no route has been found. Try to find a default route */ route = ipdiameter_find_default_route(); /* No route found */ if ( IP_NULL == route ) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_rerouting_packet: no routing entry has been found!\n"); #endif goto failed_to_forward; /* No route found packet to be routed */ } } if ( IP_NULL == route ) { goto failed_to_forward; /* No route found packet to be routed */ } switch(route->action_type) { case DIAMETER_LOCAL: ipdiameter_routing_local_processing(packet); #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_rerouting_packet: dest-realm is matched locally!\n"); #endif break; case DIAMETER_REDIRECT: ipdiameter_routing_redirect(packet, peer, route); #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_rerouting_packet: a redirect route entry found!\n"); #endif break; case DIAMETER_PROXY: #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_rerouting_packet: proxy route entry found!\n"); #endif /* Packet rerouting. Exctract the next_hop_server from packet */ reroute_request_entry = (Ipdiameter_request_entry *) ((Ip_u8 *)packet + sizeof(Ipdiameter_header) - sizeof(Ipdiameter_request_entry)); IPDIAMETER_ROUTE_LOCK(route); for(route_server = IPCOM_LIST_FIRST(&route->server_head); route_server !=IP_NULL; route_server = IPCOM_LIST_NEXT(&route_server->server_list)) { /* Get the previouse used next hop server from the existing packet */ if (0 == ipcom_strcmp(reroute_request_entry->agent_pkt_entry->next_hope_server, route_server->server_id)) { /* Go to the next server since this one has been tried */ break; } } if(IP_NULL != route_server) { /* if route_server == IP_NULL then routing entry has been found but not a next hope server. Either this is a new route or the next hop has been deleted. Should we try from the beggining? - no */ /* Next hope has been found in the routing table. Try the next server */ route_server = IPCOM_LIST_NEXT(&route_server->server_list); } for(route_server = route_server; route_server != IP_NULL; route_server = IPCOM_LIST_NEXT(&route_server->server_list)) { new_peer = ipdiameter_find_peer_host((Ip_u8 *)route_server->server_id); if((IP_NULL != new_peer) && (new_peer->state != DIAMETER_PEER_STATE_FAILED)) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_rerouting_packet: peer found. peer: %s\n", new_peer->hostname); #endif /* One of multiple destinations is OK. Consider the proxing succesfull */ ret_val = ipdiameter_rerouting_request_forward(packet, peer, new_peer); #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_rerouting_packet: peer found. peer: %s\n", new_peer->hostname); #endif if(ret_val == IPCOM_SUCCESS) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_rerouting_packet: Packet has been forwarded or has been placed \ in trans/retrans queue. server_id: %s\n" ,route_server->server_id); #endif IPDIAMETER_ROUTE_UNLOCK(route); return IP_TRUE; } else if(ret_val == IPCOM_ERR_NO_MEMORY) { IPDIAMETER_ROUTE_UNLOCK(route); goto failed_to_forward; /* For all other cases try to reroute */ } } } IPDIAMETER_ROUTE_UNLOCK(route); #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_rerouting_packet: no next hop server or peer for this server is found!\n"); #endif goto failed_to_forward; case DIAMETER_RELAY: ipdiameter_routing_relay(packet, peer, route); #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_rerouting_packet: a relay route entry found!\n"); #endif default: #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_rerouting_packet: route entry with wrong type!\n"); #endif break; } failed_to_forward: return IP_FALSE; } /* *=========================================================================== * ipdiameter_routing_rquest_forward *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipdiameter_rerouting_request_forward(Ipdiameter_header *packet, Ipdiameter_peer_entry *peer, Ipdiameter_peer_entry *new_peer) { Ipdiameter_header *new_request_packet_header = IP_NULL; Ipdiameter_request_entry *new_request_entry = IP_NULL; Ip_err retval = IPCOM_SUCCESS; new_request_entry = (Ipdiameter_request_entry *) ((Ip_u8 *)packet + sizeof(Ipdiameter_header) - sizeof(Ipdiameter_request_entry)); new_request_entry->agent_pkt_entry->new_peer = new_peer; /* Save the forwarding next hope server id for rerouting */ ipcom_strcpy(new_request_entry->agent_pkt_entry->next_hope_server, new_peer->hostname); new_request_packet_header = packet; if ((new_peer->state == DIAMETER_PEER_STATE_R_OPEN) || (new_peer->state == DIAMETER_PEER_STATE_I_OPEN)) goto transmit; retval = ipdiameter_peer_add_tx_pending_pkt(new_peer, (struct Ipdiameter_header *)new_request_packet_header); if (retval != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipdiameter_rerouting_request_forward: fail to add package to peer's transmiting pending list\n"); return IPCOM_ERR_IGNORED_PACKET; } retval = ipdiameter_peer_fsm(new_peer, IP_NULL, DIAMETER_PEER_EV_START); if (retval != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipdiameter_rerouting_request_forward: fail to run peer FSM\n"); } return IPCOM_SUCCESS; transmit: /* forward the request packet to the peer */ if(IPCOM_SUCCESS != ipdiameter_peer_pkt_send(new_peer, new_request_packet_header)) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_rerouting_request_forward: packet \ %x failed to be sent to fd: %d\n", new_request_packet_header, new_peer->fd); #endif return IPCOM_ERR_FAILED; } ipdiameter_peer_add_req(new_peer, (struct Ipdiameter_header *)new_request_packet_header); #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_routing_request_forward: packet %x is successfully forwarded to: %x\n", new_request_packet_header, peer); #endif return IPCOM_SUCCESS; } /* *=========================================================================== * ipdiameter_proxy_process_received_protocol_errors *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_bool ipdiameter_proxy_process_received_protocol_errors (struct Ipdiameter_header *packet, struct Ipdiameter_request_entry *reqent, Ipdiameter_peer_entry *peer) { Ip_u32 *error_type= IP_NULL; Ip_u32 error_type_len = 0; /* Validate the AVP and exctract the result code */ if(IPCOM_ERR_BAD_PACKET == ipdiameter_get_avp((Ipdiameter_header *) packet, DIAMETER_AVP_CODE_Result_Code, &error_type_len, (Ip_u8 **) &error_type)) { #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_proxy_process_received_protocol_errors: invalid packet - error in result code AVP!\n"); #endif return IP_FALSE; } *error_type = (Ip_u32)ip_ntohl(*error_type); #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_proxy_process_received_protocol_errors: process error %d\n",*error_type); #endif switch(*error_type) { case DIAMETER_UNABLE_TO_DELIVER: /* Try to reroute the request. */ if(IP_TRUE == ipdiameter_rerouting_packet(&((Ipdiameter_request_entry *)reqent)->hdr, peer)) { /* The packet forward function has deleted the request packet from old peers list */ return IP_TRUE; /* No farther processing of the packet */ } else return IP_FALSE; /* This will forward the respond message to the previouse peer with the same error code */ case DIAMETER_LOOP_DETECTED: /* Don't process this event localy */ return IP_FALSE; /* This will forward the respond message to the previouse peer with the same error code */ case DIAMETER_REALM_NOT_SERVED: case DIAMETER_APPLICATION_UNSUPPORTED: case DIAMETER_COMMAND_UNSUPPORTED: case DIAMETER_TOO_BUSY: case DIAMETER_REDIRECT_INDICATION: case DIAMETER_INVALID_HDR_BITS: case DIAMETER_INVALID_AVP_BITS: case DIAMETER_UNKNOWN_PEER: #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_proxy_process_received_protocol_errors: unsupported error type %d!,\n", *error_type); #endif break; default: #ifdef DIAMETER_DEBUG ipcom_printf(" ipdiameter_proxy_process_received_protocol_errors: unkown error type %d!,\n",*error_type); #endif break; } return IP_FALSE;; } /* *=========================================================================== * ipdiameter_agent_check_duplicate_retransmitted_packets *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_bool ipdiameter_agent_check_duplicate_retransmitted_packets(Ipdiameter_header *hdr, Ip_u8 *origin_host, Ipdiameter_peer_entry *peer_originator) { Ipdiameter_request_entry *reqent; Ipdiameter_peer_entry *peer, *next_peer; Ip_u32 req_id, e2e_id; Ip_u8 *origin_host_rtr = IP_NULL; Ip_u32 origin_host_len_rtr = 0; int i; e2e_id = (Ip_u32)ip_ntohl(hdr->e2e_id); IPDIAMETER_LOCK(); for(peer = IPCOM_LIST_FIRST(&ipdiameter.peer_head); peer != IP_NULL; peer = next_peer ) { next_peer = IPCOM_LIST_NEXT(&peer->peer_list); /* Check whether the packet is already in re-tx pending list The End-to-End Identifier field in the Diameter header along with the Origin-Host AVP MUST be used to identify duplicate messages.*/ IPDIAMETER_PEER_LOCK(peer); for (reqent = IPCOM_LIST_FIRST(&peer->request_head); reqent != IP_NULL; reqent = IPCOM_LIST_NEXT(&reqent->request_list)) { req_id = (Ip_u32)ip_ntohl(reqent->hdr.e2e_id); /* Validate the host */ if (IPCOM_SUCCESS == ipdiameter_get_avp(&reqent->hdr, DIAMETER_AVP_CODE_Origin_Host, &origin_host_len_rtr, &origin_host_rtr)) { /* Find a request entry with same ID that the packet has */ if ((e2e_id == req_id) && (0 == ipcom_strcmp((char *)origin_host, (char *)origin_host_rtr)) && (reqent->agent_pkt_entry->original_peer != peer_originator)) { /* Repair the failed path and return. No more processing is needed for this massage*/ reqent->agent_pkt_entry->original_peer = peer_originator; reqent->agent_pkt_entry->original_hbh_id = hdr->hbh_id; IPDIAMETER_PEER_UNLOCK(peer); IPDIAMETER_UNLOCK(); return IP_TRUE; } } } /* Check whether the package is already in the pending list */ for (i = 0; i <(Ip_s32) peer->package_num; i++) { if (peer->packages[i] != IP_NULL) { reqent = (Ipdiameter_request_entry *) ((Ip_u8 *)peer->packages[i] + sizeof(Ipdiameter_header) - sizeof(Ipdiameter_request_entry)); req_id = (Ip_u32)ip_ntohl(reqent->hdr.e2e_id); /* Validate the host */ if (IPCOM_SUCCESS == ipdiameter_get_avp(&reqent->hdr, DIAMETER_AVP_CODE_Origin_Host, &origin_host_len_rtr, &origin_host_rtr)) { /* Find a request entry with same ID that the packet has */ if ((e2e_id == req_id) && (0 == ipcom_strcmp((char *)origin_host, (char *)origin_host_rtr)) && (reqent->agent_pkt_entry->original_peer != peer_originator)) { /* Repair the failed path and return. No more processing is needed for this massage*/ reqent->agent_pkt_entry->original_peer = peer_originator; reqent->agent_pkt_entry->original_hbh_id = hdr->hbh_id; IPDIAMETER_PEER_UNLOCK(peer); IPDIAMETER_UNLOCK(); return IP_TRUE; } } } } IPDIAMETER_PEER_UNLOCK(peer); } IPDIAMETER_UNLOCK(); return IP_FALSE; } /* **************************************************************************** * END OF FILE **************************************************************************** */