2384 lines
83 KiB
C
2384 lines
83 KiB
C
/* 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 <ipcom_type.h>
|
|
#include <ipcom_os.h>
|
|
#include <ipcom_list.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_time.h>
|
|
#include <ipcom_clib.h>
|
|
|
|
#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
|
|
****************************************************************************
|
|
*/
|