Files
m241Neelima/components/ip_net2-6.9/ipdhcps6/src/ipdhcps6_prefix.c
T
2026-07-30 11:19:44 +05:30

753 lines
26 KiB
C

/*
******************************************************************************
* INTERPEAK SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_RCPL8_2024030303 $ $RCSfile: ipdhcps6_prefix.c,v $ $Revision: 1.25.12.2 $
* $Source: /home/interpeak/CVSRoot/ipdhcps6/src/ipdhcps6_prefix.c,v $
* $Author: jchang1 $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Kenneth Jonsson <kenneth@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#include "ipdhcps6_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_type.h>
#include <ipcom_cstyle.h>
#include <ipcom_clib.h>
#include <ipcom_err.h>
#include <ipcom_list.h>
#include <ipcom_syslog.h>
#include <ipnet.h>
#include <ipnet_routesock.h>
#include "ipdhcps6.h"
#include "ipdhcps6_h.h"
#include "ipdhcps6_host.h"
#include "ipdhcps6_prefix.h"
#include "ipdhcps6_subnet.h"
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
typedef struct Ipdhcps6_prefix_list_data_struct
{
Ipdhcps6_prefix *prefix;
int index;
}
Ipdhcps6_prefix_list_data;
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipdhcps6_prefix_obj_hash
*===========================================================================
* Description: Returns a hash value based on a DHCP option.
* Parameters: opt - The option to return a hash value for.
* Returns:
*
*/
IP_STATIC unsigned
ipdhcps6_prefix_obj_hash(Ipdhcps6_prefix *prefix)
{
return ipcom_hash_update(prefix->key, ipcom_strlen(prefix->key), 0);
}
/*
*===========================================================================
* ipdhcps6_prefix_key_hash
*===========================================================================
* Description: Returns a hash value for a prefix key.
* Parameters: key - The prefix ID number.
* Returns:
*
*/
IP_STATIC unsigned
ipdhcps6_prefix_key_hash(char *key)
{
return ipcom_hash_update(key, ipcom_strlen(key), 0);
}
/*
*===========================================================================
* ipdhcps6_prefix_hash_cmp
*===========================================================================
* Description: Compares a hash object and a hash key for equality.
* Parameters: prefix - A prefix structure.
* key - A pointer to the search key.
* Returns: IP_TRUE if the search key matches the prefix entry.
*
*/
IP_STATIC Ip_bool
ipdhcps6_prefix_hash_cmp(Ipdhcps6_prefix *prefix, char *key)
{
return ipcom_strcmp(prefix->key, key) == 0;
}
/*
*===========================================================================
* ipdhcps6_prefix_free_cb
*===========================================================================
* Description: Frees the specified prefix.
* Parameters: prefix - A prefix structure.
* Returns:
*
*/
IP_STATIC void
ipdhcps6_prefix_free_cb(Ipdhcps6_prefix *prefix, void *unused)
{
(void) unused;
ipcom_free(prefix);
}
/*
*===========================================================================
* ipdhcps6_prefix_list_cb
*===========================================================================
* Description: Makes a deep copy of all prefix entries.
* Parameters: prefix - A prefix structure.
* Returns:
*
*/
IP_STATIC void
ipdhcps6_prefix_list_cb(Ipdhcps6_prefix *prefix, Ipdhcps6_prefix_list_data *list_data)
{
ipcom_memcpy(&list_data->prefix[list_data->index++], prefix, sizeof(Ipdhcps6_prefix));
}
/*
*===========================================================================
* ipdhcps6_prefix_get_ptr
*===========================================================================
* Description: Returns the prefix entry corrensponding to the specified key.
* Parameters: key - The ID number for the prefix to return
* Returns: The matching prefix entry or IP_NULL if no match was found.
*
*/
IP_STATIC Ipdhcps6_prefix *
ipdhcps6_prefix_get_ptr(IP_CONST char *key)
{
return ipcom_hash_get(ipdhcps6->db.prefixes, (void *) key);
}
/*
*===========================================================================
* ipdhcps6_prefix_get_ptr_from_host
*===========================================================================
* Description: Returns the prefix entry on a specific host.
* Parameters: host - The host to which this prefix must be assigned.
* prefix - The address of the prefix to find.
* prefix_len - The length of the prefix to find.
* Returns: The matching prefix entry or IP_NULL if no match was found.
*
*/
IP_STATIC Ipdhcps6_prefix *
ipdhcps6_prefix_get_ptr_from_host(Ipdhcps6_host *host,
struct Ip_in6_addr *prefix,
int prefix_len)
{
Ipdhcps6_prefix *p;
int i;
for (i = 0; i < IPDHCPS6_PREFIX_PER_USER; i++)
{
if (host->user.prefix_ids[i][0] == '\0')
continue;
p = ipdhcps6_prefix_get_ptr(host->user.prefix_ids[i]);
if (p == IP_NULL)
continue;
if (prefix_len == p->prefix_len
&& IP_IN6_ARE_ADDR_EQUAL(prefix, &p->prefix))
return p;
}
return IP_NULL;
}
/*
*===========================================================================
* ipdhcps6_prefix_add_del_rt
*===========================================================================
* Description: Adds/deletes a route to the prefix that is beeing handed out.
* Parameters: ifindex - The interface that have to be used to reach this prefix.
* prefix - The prefix that is beeing handed out.
* prefixlen - The prefix length.
* add - IP_TRUE if the route should be added, it will be deleted
* otherwise.
* Returns: IPCOM_SUCCESS - operation was successful.
* IPCOM_ERR_NO_MEMORY - could not allocate enough memory.
*
*/
IP_STATIC Ip_err
ipdhcps6_prefix_add_del_rt(int ifindex, struct Ip_in6_addr *prefix, int prefixlen , Ip_bool add)
{
struct Ip_sockaddr_in6 *gw;
struct Ip_sockaddr_in6 *net;
struct Ip_sockaddr_in6 *mask;
struct Ipnet_rt_msghdr *rtm = IP_NULL;
Ip_u16 rtm_len;
int rt_sock;
struct Ip_in6_ifreq ifreq6;
Ip_err err;
rt_sock = ipcom_socket(IP_AF_ROUTE, IP_SOCK_RAW, 0);
if (rt_sock == IP_INVALID_SOCKET)
{
IPCOM_LOG0(ERR, "Failed to open routing socket");
err = IPCOM_ERR_SOCKCALLFAILED;
/* Coverity defect fix for NEGATIVE_RETURNS */
return err;
}
(void)ipcom_if_indextoname((Ip_u32)ifindex, ifreq6.ifr_name);
if (ipcom_socketioctl(rt_sock, IP_SIOCGIFDSTADDR_IN6, &ifreq6) < 0)
/* Not a point-to-point interface or no peer address set, ignore t */
return IPCOM_SUCCESS;
/* Peer address present, route any packets destinated to the network defined
by the prefix to the PPP peer */
rtm_len = sizeof(struct Ipnet_rt_msghdr) + sizeof(struct Ip_sockaddr_in6) * 3;
rtm = ipcom_calloc(1, rtm_len);
if (rtm == IP_NULL)
{
IPCOM_LOG0(ERR, "Out of memory while adding route.");
err = IPCOM_ERR_NO_MEMORY;
goto cleanup;
}
net = (struct Ip_sockaddr_in6 *) (rtm + 1);
gw = (struct Ip_sockaddr_in6 *) (net + 1);
mask = (struct Ip_sockaddr_in6 *) (gw + 1);
rtm->rtm_msglen = rtm_len;
rtm->rtm_version = IPNET_RTM_VERSION;
rtm->rtm_type = (Ip_u8)(add ? IPNET_RTM_ADD : IPNET_RTM_DELETE);
rtm->rtm_index = (Ip_u32)ifindex;
rtm->rtm_flags = IPNET_RTF_UP | IPNET_RTF_GATEWAY | IPNET_RTF_DONE
| IPNET_RTF_MASK | IPNET_RTF_DYNAMIC;
rtm->rtm_addrs = IPNET_RTA_DST | IPNET_RTA_GATEWAY | IPNET_RTA_NETMASK;
rtm->rtm_pid = ipcom_getpid();
rtm->rtm_rmx.rmx_expire = IPCOM_ADDR_INFINITE;
/* Network */
IPCOM_SA_LEN_SET(net, sizeof(*net));
net->sin6_family = IP_AF_INET6;
IPDHCPS6_SET_IN6_ADDR(&net->sin6_addr, prefix);
/* Mask */
IPCOM_SA_LEN_SET(mask, sizeof(*mask));
mask->sin6_family = IP_AF_INET6;
ipdhcps6_create_mask(&mask->sin6_addr, prefixlen);
/* Gateway */
ipcom_memcpy(gw, &ifreq6.ifr_ifru.ifru_dstaddr, IPCOM_SA_LEN_GET(&ifreq6.ifr_ifru.ifru_dstaddr));
if (ipcom_send(rt_sock, rtm, rtm->rtm_msglen, 0) < 0)
{
IPCOM_LOG1(ERR, "Failed to send route socket message: %s",
ipcom_strerror(ipcom_errno));
}
err = IPCOM_SUCCESS;
cleanup:
ipcom_socketclose(rt_sock);
ipcom_free(rtm);
return err;
}
/*
*===========================================================================
* ipdhcps6_prefix_add_del_prefixes
*===========================================================================
* Description: Adds or deletes all prefixes that this host should get.
* Parameters: subnet - The subnet the requesting router is located at.
* host - The host entry for the requesting router.
* response_opt - Buffer to where all OPTION_IAPREFIX options
* will be copied.
* response_optlen - Length of the 'response_opt' buffer.
* bytes_written - Number of bytes written to 'opt' or the requeried
* length of 'opt' if failed with IPCOM_ERR_NO_MEMORY.
* Returns: IPCOM_SUCCESS - operation was successful.
* IPCOM_ERR_NO_MEMORY - the 'response_opt' buffer was not large enough.
*
*/
IP_STATIC Ip_err
ipdhcps6_prefix_add_del_prefixes(Ipdhcps6_subnet_entry *subnet,
Ipdhcps6_host *host,
Ipdhcps6_option_iaprefix *response_opt,
Ip_u16 response_optlen,
Ip_u16 *bytes_written,
Ip_bool add)
{
Ipdhcps6_prefix *prefix;
Ip_u16 written;
Ip_err err;
int i;
(void) subnet;
*bytes_written = 0;
for (i = 0; i < IPDHCPS6_PREFIX_PER_USER; i++)
{
if (host->user.prefix_ids[i][0] == '\0')
continue;
prefix = ipdhcps6_prefix_get_ptr(host->user.prefix_ids[i]);
if (prefix == IP_NULL)
{
IPCOM_LOG2(WARNING, "Prefix %s was specified for user %s but not found in the prefix database",
host->user.prefix_ids[i], host->user.name);
continue;
}
if (add)
{
err = ipdhcps6_option_add_iaprefix(response_opt,
(Ip_u16) (response_optlen - *bytes_written),
&written,
prefix);
if (err != IPCOM_SUCCESS)
return err;
*bytes_written += written;
response_opt = (Ipdhcps6_option_iaprefix *) ((Ip_u8 *) response_opt + written);
}
err = ipdhcps6_prefix_add_del_rt(host->ifindex, &prefix->prefix, prefix->prefix_len, add);
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipdhcps6_prefix_renew_prefixes
*===========================================================================
* Description:
* Parameters:
* Returns: IPCOM_SUCCESS - operation was successful.
* IPCOM_ERR_NO_MEMORY - the 'response_opt' buffer was not large enough.
*
*/
IP_STATIC Ip_err
ipdhcps6_prefix_renew_prefixes(Ipdhcps6_subnet_entry *subnet,
Ipdhcps6_host *host,
Ipdhcps6_option *request_ia_pd_opt,
Ipdhcps6_option_iaprefix *response_opt,
Ip_u16 response_optlen,
Ip_u16 *bytes_written)
{
Ipdhcps6_prefix *prefix;
Ipdhcps6_option_iaprefix *response_ptr;
Ipdhcps6_option_iaprefix *request_ptr;
Ipdhcps6_option_iaprefix iaprefix;
const Ip_u16 iaprefix_len = 25;
Ip_u16 id_pd_opt_len;
Ip_u16 request_offset;
Ip_u16 request_iaprefix_len;
(void)subnet;
id_pd_opt_len = (Ip_u16) (ipdhcps6_get_u16(&request_ia_pd_opt->len)
- ip_offsetof(struct Ipdhcps6_option_prefix_delegation_data, options)
- IPDHCPS6_OPTION_HDR_LEN);
ipcom_memset(&prefix, 0, sizeof(prefix));
ipdhcps6_set_u16(&iaprefix.code, IPDHCPS6_OPTION_IAPREFIX);
ipdhcps6_set_u16(&iaprefix.len, iaprefix_len);
*bytes_written = 0;
for (request_offset = 0; request_offset < id_pd_opt_len;)
{
/* Point to the IAPREFIX option to process in the reqeust */
request_ptr = (Ipdhcps6_option_iaprefix *)
((Ip_u8 *) request_ia_pd_opt->data.ia_pd.options + request_offset);
if (response_optlen < *bytes_written + iaprefix_len + IPDHCPS6_OPTION_HDR_LEN)
/* The OPTION_IAPREFIX does not fit into the response buffer */
return IPCOM_ERR_NO_MEMORY;
ipcom_memcpy(IPDHCPS6_OPTION_IAPREFIX_GET_PREFIX_PTR(&iaprefix),
IPDHCPS6_OPTION_IAPREFIX_GET_PREFIX_PTR(request_ptr),
sizeof(struct Ip_in6_addr));
IPDHCPS6_OPTION_IAPREFIX_SET_LEN(&iaprefix, IPDHCPS6_OPTION_IAPREFIX_GET_LEN(request_ptr));
prefix = ipdhcps6_prefix_get_ptr_from_host(host,
IPDHCPS6_OPTION_IAPREFIX_GET_PREFIX_PTR(&iaprefix),
IPDHCPS6_OPTION_IAPREFIX_GET_LEN(&iaprefix));
if (prefix == IP_NULL)
{
/* This prefix is no longer used by the subnet */
iaprefix.preferred_lifetime = 0;
iaprefix.valid_lifetime = 0;
}
else
{
/* Extend the lifetime another period */
ipdhcps6_set_u32(&iaprefix.preferred_lifetime, prefix->preferred);
ipdhcps6_set_u32(&iaprefix.valid_lifetime, prefix->valid);
}
/* Point to where the this IAPREFIX should be written into the response buffer */
response_ptr = (Ipdhcps6_option_iaprefix *) ((Ip_u8 *) response_opt + *bytes_written);
/* Add the option to the response */
ipcom_memcpy(response_ptr, &iaprefix, iaprefix_len + IPDHCPS6_OPTION_HDR_LEN);
*bytes_written += iaprefix_len + IPDHCPS6_OPTION_HDR_LEN;
/* Move to the next IAPREFIX in the reqeust buffer */
request_iaprefix_len = ipdhcps6_get_u16(&request_ptr->len);
request_offset += request_iaprefix_len + IPDHCPS6_OPTION_HDR_LEN;
}
return IPCOM_SUCCESS;
}
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipdhcps6_prefix_process_request
*===========================================================================
* Description: Adds prefix to the requesting router (using OPTION_IA_PD).
* Parameters: msg - The message type in the request.
* host - The entry for the requesting router.
* opt - The OPTION_IA_PD option sent by the requesting router.
* response_opt - Pointer into the response buffer where the
* OPTION_IA_PD options containing the prefixes should be written.
* response_optlen - The length of the 'reponse_opt'
* bytes_written - Number of bytes written to 'opt' or the requeried
* length of 'opt' if failed with IPCOM_ERR_NO_MEMORY.
* Returns: IPCOM_SUCCESS - operation was successful.
* IPCOM_ERR_NO_MEMORY - the 'response_opt'. was to small.
*
*/
IP_GLOBAL Ip_err
ipdhcps6_prefix_process_request(Ip_u16 msg_type,
Ipdhcps6_subnet_entry *subnet,
Ipdhcps6_host *host,
Ipdhcps6_option *opt,
Ipdhcps6_option *response_opt,
Ip_u16 response_optlen,
Ip_u16 *bytes_written)
{
Ip_u16 ia_pd_opt_len;
Ipdhcps6_option_iaprefix *iaprefix;
Ip_err err;
Ip_u16 hdr_len;
*bytes_written = 0;
ia_pd_opt_len = ipdhcps6_get_u16(&opt->len);
hdr_len = ip_offsetof(struct Ipdhcps6_option_prefix_delegation_data, options);
if (ia_pd_opt_len != hdr_len)
{
if (ia_pd_opt_len < hdr_len
|| ipdhcps6_option_check((Ipdhcps6_option *) opt->data.ia_pd.options,
(Ip_u16) (ia_pd_opt_len - hdr_len)))
{
/* Error in length field in the OPTION_IA_PD option, drop the message */
return IPCOM_ERR_FAILED;
}
}
if (subnet == IP_NULL)
{
/* No configuration for the subnet the requesting router is located at */
return ipdhcps6_option_add_status_code(response_opt, response_optlen,
bytes_written, IPDHCPS6_CODE_NO_PREFIX_AVAIL,
"No configuration create for the subnet from where the request originates.");
}
if (ipdhcps6_host_has_prefix(host) == IP_FALSE)
{
return ipdhcps6_option_add_status_code(response_opt, response_optlen,
bytes_written, IPDHCPS6_CODE_NO_PREFIX_AVAIL,
"The host has not been given any prefixes.");
}
ia_pd_opt_len = ip_offsetof(struct Ipdhcps6_option_prefix_delegation_data, options);
if (response_optlen < ia_pd_opt_len + IPDHCPS6_OPTION_HDR_LEN)
/* Not the response buffer is not big enough to hold the IPDHCPS6_OPTION_IA_PD header */
return IPCOM_ERR_NO_MEMORY;
/* Add the IPDHCPS6_OPTION_IA_PD header */
ipdhcps6_set_u16(&response_opt->code, IPDHCPS6_OPTION_IA_PD);
ipcom_memcpy(&response_opt->data.ia_pd.iaid, opt->data.ia_pd.iaid, sizeof(response_opt->data.ia_pd.iaid));
/* Let the client calculate the T1 and T2 times */
ipdhcps6_set_u32(&response_opt->data.ia_pd.t1, 0);
ipdhcps6_set_u32(&response_opt->data.ia_pd.t2, 0);
response_optlen -= ia_pd_opt_len + IPDHCPS6_OPTION_HDR_LEN;
/* Pointer to where the first IPDHCPS6_OPTION_IAPREFIX should be written */
iaprefix = response_opt->data.ia_pd.options;
switch (msg_type)
{
case IPDHCPS6_MSG_SOLICIT:
case IPDHCPS6_MSG_REQUEST:
err = ipdhcps6_prefix_add_del_prefixes(subnet, host, iaprefix, response_optlen, bytes_written, IP_TRUE);
break;
case IPDHCPS6_MSG_RELEASE:
err = ipdhcps6_prefix_add_del_prefixes(subnet, host, iaprefix, response_optlen, bytes_written, IP_FALSE);
break;
case IPDHCPS6_MSG_RENEW:
case IPDHCPS6_MSG_REBIND:
err = ipdhcps6_prefix_renew_prefixes(subnet, host, opt, iaprefix, response_optlen, bytes_written);
break;
default:
/* This message type should not forward here */
return IPCOM_ERR_INVALID_ARG;
}
*bytes_written += ia_pd_opt_len;
ipdhcps6_set_u16(&response_opt->len, *bytes_written);
*bytes_written += IPDHCPS6_OPTION_HDR_LEN;
return err;
}
/*
*===========================================================================
* ipdhcps6_prefix_new_db
*===========================================================================
* Description: Deletes all elements in the subnet database, memory for
* the route table holding the entries is also deleted.
* Parameters: subnets - The subnet database to delete.
* Returns:
*
*/
IP_GLOBAL Ipcom_hash *
ipdhcps6_prefix_new_db(void)
{
return ipcom_hash_new((Ipcom_hash_obj_func) ipdhcps6_prefix_obj_hash,
(Ipcom_hash_key_func) ipdhcps6_prefix_key_hash,
(Ipcom_hash_cmp_func) ipdhcps6_prefix_hash_cmp);
}
/*
*===========================================================================
* ipdhcps6_prefix_delete_db
*===========================================================================
* Description: Frees all prefix entries in the list.
* Parameters:
* Returns:
*
*/
IP_GLOBAL void
ipdhcps6_prefix_delete_db(Ipcom_hash *prefix_db)
{
if(prefix_db != IP_NULL)
{
ipcom_hash_for_each(prefix_db,
(Ipcom_hash_foreach_cb_func) ipdhcps6_prefix_free_cb,
IP_NULL);
ipcom_hash_delete(prefix_db);
}
}
/*
****************************************************************************
* 11 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipdhcps6_prefix_add
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipdhcps6_prefix_add(Ipdhcps6_prefix *prefix)
{
Ipdhcps6_prefix *p = IP_NULL;
Ip_err err;
if (ipdhcps6 == IP_NULL || IP_BIT_ISFALSE(ipdhcps6->flags, IPDHCPS6_FLAG_OPEN))
return IPCOM_ERR_NOT_OPENED;
ipcom_mutex_lock(ipdhcps6->db.mutex);
if (ipdhcps6_prefix_get_ptr(prefix->key) != IP_NULL)
{
err = IPCOM_ERR_DUPLICATE;
goto errout;
}
p = ipcom_malloc(sizeof(Ipdhcps6_prefix));
if (p == IP_NULL)
{
err = IPCOM_ERR_NO_MEMORY;
goto errout;
}
ipcom_memcpy(p, prefix, sizeof(Ipdhcps6_prefix));
if ((err = ipcom_hash_add(ipdhcps6->db.prefixes, p)) != IPCOM_SUCCESS)
goto errout;
ipcom_mutex_unlock(ipdhcps6->db.mutex);
return IPCOM_SUCCESS;
errout:
ipcom_free(p);
ipcom_mutex_unlock(ipdhcps6->db.mutex);
return err;
}
/*
*===========================================================================
* ipdhcps6_subnet_delete
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipdhcps6_prefix_delete(Ipdhcps6_prefix *prefix)
{
Ipdhcps6_prefix *p;
Ip_err err = IPCOM_SUCCESS;
if (ipdhcps6 == IP_NULL || IP_BIT_ISFALSE(ipdhcps6->flags, IPDHCPS6_FLAG_OPEN))
return IPCOM_ERR_NOT_OPENED;
ipcom_mutex_lock(ipdhcps6->db.mutex);
if ((p = ipdhcps6_prefix_get_ptr(prefix->key)) == IP_NULL)
{
err = IPCOM_ERR_NOT_FOUND;
goto cleanup;
}
(void) ipcom_hash_remove(ipdhcps6->db.prefixes, p);
ipcom_free(p);
cleanup:
ipcom_mutex_unlock(ipdhcps6->db.mutex);
return err;
}
/*
*===========================================================================
* ipdhcps6_subnet_list
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipdhcps6_prefix_list(Ipdhcps6_prefix prefix[], int *num_elem)
{
Ipdhcps6_prefix_list_data cb_data;
if (ipdhcps6 == IP_NULL || IP_BIT_ISFALSE(ipdhcps6->flags, IPDHCPS6_FLAG_OPEN))
return IPCOM_ERR_NOT_OPENED;
ipcom_mutex_lock(ipdhcps6->db.mutex);
if (prefix == IP_NULL)
{
*num_elem = (int)ipdhcps6->db.prefixes->elem;
ipcom_mutex_unlock(ipdhcps6->db.mutex);
return IPCOM_SUCCESS;
}
if (*num_elem < (int) ipdhcps6->db.prefixes->elem)
{
ipcom_mutex_unlock(ipdhcps6->db.mutex);
return IPCOM_ERR_NO_MEMORY;
}
*num_elem = (int)ipdhcps6->db.prefixes->elem;
cb_data.prefix = prefix;
cb_data.index = 0;
ipcom_hash_for_each(ipdhcps6->db.prefixes,
(Ipcom_hash_foreach_cb_func) ipdhcps6_prefix_list_cb,
&cb_data);
ipcom_mutex_unlock(ipdhcps6->db.mutex);
return IPCOM_SUCCESS;
}
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/