1913 lines
74 KiB
C
1913 lines
74 KiB
C
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipdhcps6_pool.c,v $ $Revision: 1.29.12.4 $
|
|
* $Source: /home/interpeak/CVSRoot/ipdhcps6/src/ipdhcps6_pool.c,v $
|
|
* $Author: jren $
|
|
* $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_clib.h>
|
|
#include <ipcom_hash.h>
|
|
#include <ipcom_sock.h>
|
|
#include <ipcom_sock6.h>
|
|
#include <ipcom_syslog.h>
|
|
|
|
#include "ipnet.h"
|
|
#include "ipnet_route.h"
|
|
#include "ipdhcps6_h.h"
|
|
#include "ipdhcps6_host.h"
|
|
#include "ipdhcps6_subnet.h"
|
|
#include "ipcom_sysvar.h"
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
typedef struct Ipdhcps6_pooldel_struct
|
|
{
|
|
Ipcom_hash *active_leases;
|
|
Ipdhcps6_addr_pool *pool;
|
|
}
|
|
Ipdhcps6_pooldel;
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_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_pool_obj_hash(Ipdhcps6_lease_addr *lease)
|
|
{
|
|
return ipcom_hash_update(&lease->addr, sizeof(lease->addr), 0);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_key_hash
|
|
*===========================================================================
|
|
* Description: Returns a hash value for a DHCP option code.
|
|
* Parameters: addr - IPv6 address.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipdhcps6_pool_key_hash(struct Ip_in6_addr *addr)
|
|
{
|
|
return ipcom_hash_update(addr, sizeof(*addr), 0);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_cmp
|
|
*===========================================================================
|
|
* Description: Compares a hash object and a hash key for equality.
|
|
* Parameters: opt - An option structure.
|
|
* code - Pointer to the code value the option should have
|
|
* (in network byte order).
|
|
* Returns: IP_TRUE if the code in the option matches the supplied code.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipdhcps6_pool_cmp(Ipdhcps6_lease_addr *lease, struct Ip_in6_addr *addr)
|
|
{
|
|
return IP_IN6_ARE_ADDR_EQUAL(&lease->addr, addr);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_lease_delete
|
|
*===========================================================================
|
|
* Description: Frees the resources held by the lease.
|
|
* Parameters: lease - Lease to delete.
|
|
* head - The hash table head the lease is added to.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipdhcps6_pool_lease_delete(Ipdhcps6_lease_addr *lease, Ipcom_hash *head)
|
|
{
|
|
if (ipcom_hash_remove(head, lease) != IPCOM_SUCCESS)
|
|
IP_PANIC();
|
|
ipcom_tmo2_cancel(&lease->tmo);
|
|
/* remove the current lease from "host" lease list */
|
|
ipcom_list_remove(&lease->next);
|
|
/*lease->host==NULL means abandoned lease*/
|
|
ipcom_free(lease);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_delete_for_each
|
|
*===========================================================================
|
|
* Description: Frees the resources held by the lease.
|
|
* Parameters: lease - Lease to delete.
|
|
* pdel - The hash table data.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipdhcps6_pool_delete_for_each(Ipdhcps6_lease_addr *lease, Ipdhcps6_pooldel *pdel)
|
|
{
|
|
/* NOTE : do not support two or more pools have the same adresses. */
|
|
if ((lease->prefix_len == pdel->pool->info.prefix_len)
|
|
&& ipcom_memcmp(&lease->addr, &pdel->pool->info.first, sizeof(lease->addr)) >= 0
|
|
&& ipcom_memcmp(&lease->addr, &pdel->pool->info.last, sizeof(lease->addr)) <= 0)
|
|
ipdhcps6_pool_lease_delete(lease, pdel->active_leases);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_lease_expired
|
|
*===========================================================================
|
|
* Description: Removes the lease from the database since the client
|
|
* has not renewed it.
|
|
* Parameters: lease - Lease that has expired.
|
|
* Returns: IP_FALSE - Never reschedule.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipdhcps6_pool_lease_expired(Ipdhcps6_lease_addr *lease)
|
|
{
|
|
ipcom_mutex_lock(ipdhcps6->db.mutex);
|
|
ipdhcps6_pool_lease_delete(lease, lease->active_leases);
|
|
ipcom_mutex_unlock(ipdhcps6->db.mutex);
|
|
return IP_FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhsps6_pool_is_addr_part_of
|
|
*===========================================================================
|
|
* Description: Returns if the address is part of the poool.
|
|
* Parameters: pool - The pool to check if the address is part of
|
|
* addr - The adderss to check.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ipdhcps6_addr_pool *
|
|
ipdhcps6_pool_is_addr_part_of(Ipdhcps6_addr_pool *pool,
|
|
struct Ip_in6_addr *addr,
|
|
Ip_u8 prefix_len)
|
|
{
|
|
for (; pool != IP_NULL; pool = pool->next)
|
|
{
|
|
if ((prefix_len == pool->info.prefix_len)
|
|
&& ipcom_memcmp(addr, &pool->info.first, sizeof(*addr)) >= 0
|
|
&& ipcom_memcmp(addr, &pool->info.last, sizeof(*addr)) <= 0)
|
|
return pool;
|
|
}
|
|
|
|
return IP_NULL;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhsps6_pool_check_section18_2_2
|
|
*===========================================================================
|
|
* Description: Returns if the address is part of the subnet.
|
|
* Parameters: sn - The subnet to check if the address is part of
|
|
* addr - The adderss to check.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipdhcps6_pool_check_section18_2_2(Ipdhcps6_subnet_entry *sn,
|
|
struct Ip_in6_addr *addr)
|
|
{
|
|
Ipdhcps6_addr_pool *pool = sn->pools;
|
|
struct Ip_in6_addr mask;
|
|
struct Ip_in6_addr tmp_addr1;
|
|
struct Ip_in6_addr tmp_addr2;
|
|
|
|
ipcom_memset((void *)&mask, 0, sizeof(mask));
|
|
ipnet_route_create_mask((void *)&mask, sn->prefix_len);
|
|
|
|
ipcom_memcpy(&tmp_addr1, addr, sizeof(tmp_addr1));
|
|
ipnet_route_apply_mask((void *)&tmp_addr1, (void *)&mask, 128);
|
|
|
|
for (; pool != IP_NULL; pool = pool->next)
|
|
{
|
|
if (!pool->info.prefix_len)
|
|
{
|
|
/* FIX ME : WHY? - due to the following restrictions/assumptions :
|
|
* 1) the prefix length is 0 for INNA in the pool list.
|
|
* 2) prefix length ONLY can get from the subnet.
|
|
* 3) prefix length is 10(FE80) if using interface name.
|
|
* 4) 'first' and 'last' SHOULD be in the same prefix range.
|
|
* 5) all the pool prefix length in the same subnet
|
|
* SHOULD have the same value. */
|
|
ipcom_memcpy(&tmp_addr2, &pool->info.first, sizeof(tmp_addr2));
|
|
ipnet_route_apply_mask((void *)&tmp_addr2, (void *)&mask, 128);
|
|
|
|
if (ipcom_memcmp(&tmp_addr1, &tmp_addr2, sizeof(tmp_addr1)) == 0)
|
|
return IP_FALSE;
|
|
}
|
|
}
|
|
|
|
return IP_TRUE;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_get_range
|
|
*===========================================================================
|
|
* Description: get the pool addresses range.
|
|
* Parameters: pool - The pool the new address should be allocated from.
|
|
* Returns: pool addresses range
|
|
*
|
|
*/
|
|
IP_STATIC Ip_u32
|
|
ipdhcps6_pool_get_range(Ipdhcps6_addr_pool *pool)
|
|
{
|
|
Ip_u32 range = 0;
|
|
|
|
if (pool->info.prefix_len)
|
|
{
|
|
if (pool->info.prefix_len <= 32)
|
|
{
|
|
range = (ip_ntohl(pool->info.last.in6.addr32[0]) >> (32 - pool->info.prefix_len))
|
|
- (ip_ntohl(pool->info.first.in6.addr32[0]) >> (32 - pool->info.prefix_len))
|
|
+ 1;
|
|
}
|
|
else
|
|
{
|
|
Ip_u8 pmax = (pool->info.prefix_len - 1) / 8;
|
|
Ip_u8 i = 0;
|
|
|
|
for (i = 0; i <= pmax; i++)
|
|
if (pool->info.last.in6.addr8[i] != pool->info.first.in6.addr8[i])
|
|
break;
|
|
|
|
/* first == last */
|
|
if (i > pmax)
|
|
range = 0;
|
|
/* range more than 32 bit */
|
|
else if (pool->info.prefix_len - i * 8 > 32)
|
|
range = ~0u;
|
|
/* range = last - first */
|
|
else
|
|
{
|
|
Ip_u32 first = 0;
|
|
Ip_u32 last = 0;
|
|
Ip_u8 prem = pool->info.prefix_len % 8;
|
|
|
|
for (; i <= pmax; i++)
|
|
{
|
|
first = (first << 8) | pool->info.first.in6.addr8[i];
|
|
last = (last << 8) | pool->info.last.in6.addr8[i];
|
|
}
|
|
|
|
if (prem)
|
|
range = (last >> (8 - prem)) - (first >> (8 - prem)) + 1;
|
|
else
|
|
range = last - first + 1;
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ipcom_memcmp(&pool->info.first,
|
|
&pool->info.last,
|
|
sizeof(struct Ip_in6_addr) - sizeof(Ip_u32)) != 0)
|
|
range = ~0u;
|
|
else
|
|
/* +1, because change of prefix delegation, but do not test */
|
|
range = ip_ntohl(pool->info.last.in6.addr32[3])
|
|
- ip_ntohl(pool->info.first.in6.addr32[3])
|
|
+ 1;
|
|
}
|
|
|
|
return range;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_get_newaddr
|
|
*===========================================================================
|
|
* Description: format a new address according to the index in a special range
|
|
* Parameters: pool - The pool the new address should be allocated from.
|
|
* Returns: void
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipdhcps6_pool_get_newaddr(Ipdhcps6_addr_pool *pool,
|
|
Ip_u32 cur_index,
|
|
struct Ip_in6_addr *addr)
|
|
{
|
|
Ip_u32 tmp_val = 0;
|
|
|
|
ip_assert(IP_NULL != pool);
|
|
ip_assert(IP_NULL != addr);
|
|
|
|
(void)ipcom_memcpy((void *)addr,
|
|
(void *)&pool->info.first,
|
|
sizeof(*addr));
|
|
|
|
if (pool->info.prefix_len)
|
|
{
|
|
struct Ip_in6_addr mask;
|
|
|
|
/* if the configuration is error, then correct it, leave it original */
|
|
ipcom_memset((void *)&mask, 0, sizeof(mask));
|
|
ipnet_route_create_mask((void *)&mask, pool->info.prefix_len);
|
|
ipnet_route_apply_mask((void *)addr, (void *)&mask, 128);
|
|
|
|
if (pool->info.prefix_len <= 32)
|
|
{
|
|
tmp_val = ip_ntohl(pool->info.first.in6.addr32[0]);
|
|
|
|
addr->in6.addr32[0] = tmp_val >> (32 - pool->info.prefix_len);
|
|
tmp_val = addr->in6.addr32[0] + cur_index;
|
|
addr->in6.addr32[0] = tmp_val << (32 - pool->info.prefix_len);
|
|
|
|
addr->in6.addr32[0] = ip_htonl(addr->in6.addr32[0]);
|
|
}
|
|
else
|
|
{
|
|
Ip_u8 pmax = (pool->info.prefix_len - 1) / 8;
|
|
Ip_u8 prem = pool->info.prefix_len % 8;
|
|
Ip_u8 i, j;
|
|
|
|
/* get the different byte position */
|
|
for (i = 0; i <= pmax; i++)
|
|
if (pool->info.last.in6.addr8[i] != pool->info.first.in6.addr8[i])
|
|
break;
|
|
|
|
/* get the last 4 bytes */
|
|
for (j = pmax - 3; j <= pmax; j++)
|
|
tmp_val = (tmp_val << 8) | pool->info.first.in6.addr8[j];
|
|
|
|
/* trim the zero bits in the last byte */
|
|
if (prem)
|
|
{
|
|
tmp_val = tmp_val >> (8 - prem);
|
|
|
|
/* join the first bits before 4 bytes */
|
|
if (pool->info.prefix_len - i * 8 > 32)
|
|
tmp_val |= (((Ip_u32)(pool->info.last.in6.addr8[pmax-4])) << (24 + prem));
|
|
}
|
|
|
|
IPCOM_LOG5(DEBUG,
|
|
"tmp value = 0x%x, index = %d, i = %d, max = %d, rem = %d.",
|
|
tmp_val,
|
|
cur_index,
|
|
i,
|
|
pmax,
|
|
prem);
|
|
|
|
tmp_val += cur_index;
|
|
|
|
if (prem)
|
|
{
|
|
if (pool->info.prefix_len - i * 8 > 32)
|
|
addr->in6.addr8[pmax-4] |= (Ip_u8)(tmp_val >> (24 + prem));
|
|
|
|
tmp_val = (tmp_val << (8 - prem));
|
|
}
|
|
|
|
/* set the last 4 bytes */
|
|
for (j = pmax; j >= pmax - 3; j--)
|
|
{
|
|
addr->in6.addr8[j] = (Ip_u8)tmp_val;
|
|
tmp_val = (tmp_val >> 8);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tmp_val = cur_index + ip_ntohl(pool->info.first.in6.addr32[3]);
|
|
tmp_val = ip_htonl(tmp_val);
|
|
ipcom_memcpy(&addr->in6.addr32[3],
|
|
&tmp_val,
|
|
sizeof(tmp_val));
|
|
}
|
|
|
|
IPCOM_LOG2(DEBUG,
|
|
"got new address %s for %d.",
|
|
ipcom_inet_ntop(IP_AF_INET6,
|
|
addr,
|
|
ipdhcps6->addr_str,
|
|
sizeof(ipdhcps6->addr_str)),
|
|
cur_index);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_get_avaiable_addr
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters: pool - The pool the new address should be allocated from.
|
|
* Returns: IPCOM_SUCCESS - Operation successful
|
|
* IPCOM_ERR_REACHED_MAX - No free addresses in the pool
|
|
*
|
|
*/
|
|
IP_STATIC Ipdhcps6_addr_pool *
|
|
ipdhcps6_pool_get_available_addr(Ipdhcps6_addr_pool *pool,
|
|
Ipcom_hash *active_leases,
|
|
Ipdhcps6_host *host,
|
|
struct Ip_in6_addr *addr,
|
|
Ip_u16 ia_type)
|
|
{
|
|
Ip_u32 search_base;
|
|
Ip_u32 current;
|
|
Ip_u32 range;
|
|
Ip_bool found = IP_FALSE;
|
|
|
|
/* Base the search_base off the DUID so the same host
|
|
always gets the same address (if it is available) */
|
|
search_base = (Ip_u32) ipcom_hash_update(&host->duid->data,
|
|
ipdhcps6_get_u16(&host->duid->len),
|
|
0);
|
|
|
|
while (pool != IP_NULL)
|
|
{
|
|
if ((IPDHCPS6_OPTION_IA_PD == ia_type && 0 == pool->info.prefix_len)
|
|
|| (IPDHCPS6_OPTION_IA_NA == ia_type && 0 != pool->info.prefix_len))
|
|
{
|
|
pool = pool->next;
|
|
continue;
|
|
}
|
|
|
|
range = ipdhcps6_pool_get_range(pool);
|
|
IPCOM_LOG1(DEBUG,
|
|
"got the pool address range %d.",
|
|
range);
|
|
|
|
search_base %= (range? range : 1);
|
|
current = search_base;
|
|
do {
|
|
ipdhcps6_pool_get_newaddr(pool, current, addr);
|
|
if (ipcom_hash_get(active_leases, addr) == IP_NULL)
|
|
{
|
|
found = IP_TRUE;
|
|
break;
|
|
}
|
|
current = (current + 1) % (range? range : 1);
|
|
} while (search_base != current);
|
|
|
|
if (found)
|
|
return pool;
|
|
|
|
pool = pool->next;
|
|
}
|
|
|
|
return IP_NULL;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_create_new_lease
|
|
*===========================================================================
|
|
* Description: Creates a new lease for an IPv6 address in the pool.
|
|
* Parameters: pool - The pool the new address should be allocated from.
|
|
* host - The host that will lease the address.
|
|
* addr - The address to lease or IP_NULL for any address in the pool.
|
|
* iaid - The unique (for the client) indentifier for this address.
|
|
* Returns: IPCOM_SUCCESS - Operation successful
|
|
* IPCOM_ERR_NO_MEM - Out of memory
|
|
* IPCOM_ERR_FAILED - Silently drop this message
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipdhcps6_pool_create_new_lease(Ipdhcps6_addr_pool *pool,
|
|
Ipcom_hash *active_leases,
|
|
Ipdhcps6_lease_addr **nlease,
|
|
Ipdhcps6_host *host,
|
|
struct Ip_in6_addr *addr,
|
|
Ip_u8 prefix_len,
|
|
Ip_u32 prefix_valid,
|
|
Ip_u32 prefix_preferred,
|
|
Ip_u32 iaid,
|
|
Ipdhcps6_option *out_opt,
|
|
Ip_u16 out_opt_buf_len,
|
|
Ip_u16 *bytes_written,
|
|
Ip_u16 ia_type,
|
|
Ip_u8 msg_type)
|
|
{
|
|
Ip_err err;
|
|
Ipdhcps6_lease_addr *lease;
|
|
struct Ip_timeval duration;
|
|
|
|
ip_assert(pool != IP_NULL);
|
|
ip_assert(host != IP_NULL);
|
|
ip_assert(addr != IP_NULL);
|
|
ip_assert(nlease != IP_NULL);
|
|
|
|
*nlease = IP_NULL;
|
|
|
|
if (IP_IN6_IS_ADDR_UNSPECIFIED(addr))
|
|
/* Coverity defect fix for DEADCODE */
|
|
err = ((pool = ipdhcps6_pool_get_available_addr(pool, active_leases, host, addr, ia_type)) != IP_NULL
|
|
? IPCOM_SUCCESS : IPCOM_ERR_REACHED_MAX);
|
|
else
|
|
/* Coverity defect fix for DEADCODE */
|
|
err = (((pool = ipdhcps6_pool_is_addr_part_of(pool, addr, prefix_len)) != IP_NULL)
|
|
? IPCOM_SUCCESS : IPCOM_ERR_INVALID_ARG);
|
|
|
|
switch (err)
|
|
{
|
|
case IPCOM_SUCCESS:
|
|
break;
|
|
case IPCOM_ERR_INVALID_ARG:
|
|
return ipdhcps6_option_add_status_code(out_opt,
|
|
out_opt_buf_len,
|
|
bytes_written,
|
|
IPDHCPS6_CODE_NOT_ON_LINK,
|
|
"Address not on link");
|
|
case IPCOM_ERR_REACHED_MAX:
|
|
/* TAHI3315.29 : only need the status code */
|
|
if (IPDHCPS6_MSG_SOLICIT == msg_type)
|
|
return err;
|
|
return ipdhcps6_option_add_status_code(out_opt,
|
|
out_opt_buf_len,
|
|
bytes_written,
|
|
IPDHCPS6_OPTION_IA_PD == ia_type ? IPDHCPS6_CODE_NO_PREFIX_AVAIL : IPDHCPS6_CODE_NO_ADDRS_AVAIL,
|
|
IPDHCPS6_OPTION_IA_PD == ia_type ? "No more prefix avaiable" : "No more addresses avaiable");
|
|
/* Coverity defect fix for DEADCODE */
|
|
/* default:
|
|
return err;*/
|
|
}
|
|
|
|
lease = ipcom_malloc(sizeof(*lease));
|
|
if (lease == IP_NULL)
|
|
return IPCOM_ERR_NO_MEMORY;
|
|
|
|
lease->iaid = iaid;
|
|
lease->host = host;
|
|
lease->active_leases = active_leases;
|
|
lease->valid = prefix_valid ? prefix_valid : pool->info.valid_lifetime;
|
|
lease->preferred = prefix_preferred ? prefix_preferred : pool->info.preferred_lifetime;
|
|
lease->ia_type = ia_type;
|
|
lease->prefix_len = pool->info.prefix_len; /* always the same */
|
|
|
|
IPCOM_TMO2_INIT(&lease->tmo);
|
|
IPDHCPS6_SET_IN6_ADDR(&lease->addr, addr);
|
|
|
|
IPCOM_LOG6(DEBUG,
|
|
"lease information %s/%u, IA(%u) IAID 0x%x, valid time %d, prefered time %d.",
|
|
ipcom_inet_ntop(IP_AF_INET6, &lease->addr,
|
|
ipdhcps6->addr_str, sizeof(ipdhcps6->addr_str)),
|
|
lease->prefix_len,
|
|
lease->ia_type,
|
|
lease->iaid,
|
|
lease->valid,
|
|
lease->preferred);
|
|
|
|
duration.tv_sec = pool->info.valid_lifetime;
|
|
duration.tv_usec = 0;
|
|
if (ipcom_tmo2_request_timeval(ipdhcps6->timeouts,
|
|
&lease->tmo,
|
|
(Ipcom_tmo2_func) ipdhcps6_pool_lease_expired,
|
|
lease,
|
|
&duration) != IPCOM_SUCCESS
|
|
|| ipcom_hash_add(active_leases,
|
|
lease) != IPCOM_SUCCESS)
|
|
{
|
|
(void)ipcom_tmo2_cancel(&lease->tmo);
|
|
ipcom_free(lease);
|
|
return IPCOM_ERR_NO_MEMORY;
|
|
}
|
|
|
|
/* record it, maybe reset this value if renew in a different address */
|
|
ipcom_list_insert_last(&host->leases_head, &lease->next);
|
|
|
|
*nlease = lease;
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_set_t1_and_t2
|
|
*===========================================================================
|
|
* Description: Sets the T1 and T2 durations.
|
|
* Parameters: pool - The pool the new address should be allocated from.
|
|
* t1 - Will contain the duration when the client must renew
|
|
* from this server
|
|
* t2 - Will contain duration when the client can renew from
|
|
* any server.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipdhcps6_pool_set_t1_and_t2(Ip_u32 preferred,
|
|
Ip_u32 *t1,
|
|
Ip_u32 *t2)
|
|
{
|
|
Ip_u32 pool_t1;
|
|
Ip_u32 pool_t2;
|
|
|
|
/*
|
|
* If the "shortest" preferred lifetime is 0xffffffff
|
|
* ("infinity"), the recommended T1 and T2 values are also
|
|
* 0xffffffff.
|
|
* Care should be taken in setting T1 or T2 to 0xffffffff
|
|
* ("infinity"). A client will never attempt to extend the
|
|
* lifetimes of any addresses in an IA with T1 set to
|
|
* 0xffffffff. A client will never attempt to use a Rebind
|
|
* message to locate a different server to extend the lifetimes
|
|
* of any addresses in an IA with T2 set to 0xffffffff.
|
|
*/
|
|
if (preferred == 0xFFFFFFFF)
|
|
{
|
|
*t1 = preferred;
|
|
*t2 = preferred;
|
|
return;
|
|
}
|
|
|
|
pool_t1 = preferred / 2;
|
|
if (*t1 == 0)
|
|
*t1 = pool_t1;
|
|
else
|
|
*t1 = IP_MIN(*t1, pool_t1);
|
|
|
|
pool_t2 = preferred * 4 / 5;
|
|
if (*t2 == 0 || *t2 < *t1)
|
|
*t2 = pool_t2;
|
|
else
|
|
*t2 = IP_MIN(*t2, pool_t2);
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_process_non_temporary_addr_request
|
|
*===========================================================================
|
|
* Description: Processes a request or renewal of non-temporary address.
|
|
* Parameters: msg_type - DHCPv6 message type.
|
|
* subnet - The subnet the client is located at.
|
|
* host - The host data for the client.
|
|
* in_opt - The IA-NA option sent by the client
|
|
* out_opt - The buffer where the response IA-NA should be written
|
|
* buf_len - The remaining length of the response buffer.
|
|
* bytes_written - Number of bytes written to 'out_opt'
|
|
* on return.
|
|
* Returns: IPCOM_SUCCESS - Operation successful
|
|
* IPCOM_ERR_NO_MEM - Too small response buffer
|
|
* IPCOM_ERR_FAILED - Silently drop this message
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipdhcps6_pool_process_non_temporary_addr_request(Ip_u8 msg_type,
|
|
Ipdhcps6_subnet_entry *subnet,
|
|
Ipdhcps6_host *host,
|
|
Ipdhcps6_option *in_opt,
|
|
Ipdhcps6_option *out_opt,
|
|
Ip_u16 out_opt_buf_len,
|
|
Ip_u16 *bytes_written)
|
|
{
|
|
Ip_u16 in_ia_na_opt_len;
|
|
Ipdhcps6_option_ia_xx *in_ia_na_opt = (Ipdhcps6_option_ia_xx *) in_opt;
|
|
Ipdhcps6_option_ia_xx *out_ia_na_opt = IP_NULL;
|
|
Ip_u32 iaid = 0;
|
|
Ipdhcps6_option *o;
|
|
Ip_u32 t1 = 0, t2 = 0;
|
|
Ip_u32 lease_valid, lease_preferred;
|
|
Ip_bool is_addr_assigned = IP_FALSE;
|
|
Ip_bool is_initial_contact;
|
|
Ipdhcps6_option *current_out_opt = out_opt;
|
|
Ip_u16 current_out_opt_buf_len = out_opt_buf_len;
|
|
Ip_err err = IPCOM_SUCCESS;
|
|
Ip_bool noiaaddrs = IP_FALSE;
|
|
Ip_bool should_contain_iana;
|
|
struct Ip_in6_addr lease_addr = IP_IN6ADDR_ANY_INIT;
|
|
Ipdhcps6_option *conf_opt = IP_NULL;
|
|
|
|
*bytes_written = 0;
|
|
should_contain_iana = (msg_type != IPDHCPS6_MSG_DECLINE&&msg_type != IPDHCPS6_MSG_RELEASE&&msg_type!=IPDHCPS6_MSG_CONFIRM);
|
|
is_initial_contact = (msg_type == IPDHCPS6_MSG_SOLICIT || msg_type == IPDHCPS6_MSG_REQUEST);
|
|
|
|
|
|
if (subnet == IP_NULL)
|
|
{
|
|
if (is_initial_contact)
|
|
return ipdhcps6_option_add_status_code(current_out_opt,
|
|
current_out_opt_buf_len,
|
|
bytes_written,
|
|
IPDHCPS6_CODE_NOT_ON_LINK,
|
|
"Nothing configured for that link");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
if (subnet->pools == IP_NULL)
|
|
{
|
|
if (is_initial_contact)
|
|
return ipdhcps6_option_add_status_code(current_out_opt,
|
|
current_out_opt_buf_len,
|
|
bytes_written,
|
|
IPDHCPS6_CODE_NO_ADDRS_AVAIL,
|
|
"No address pool on that link");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
in_ia_na_opt_len = ipdhcps6_get_u16(&in_ia_na_opt->len) - 12;
|
|
if (in_ia_na_opt_len == 0)
|
|
/* No suggested addresses present */
|
|
{
|
|
o = IP_NULL;
|
|
noiaaddrs = IP_TRUE;
|
|
}
|
|
else
|
|
{
|
|
o = (Ipdhcps6_option *) in_ia_na_opt->options;
|
|
if (ipdhcps6_option_check(o, in_ia_na_opt_len) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "Malformed IA_NA option");
|
|
/* give no reponse back */
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
}
|
|
if (should_contain_iana)
|
|
{
|
|
/*shoud not do here*/
|
|
/* Begin adding the IA-NA option */
|
|
out_ia_na_opt = ipdhcps6_option_add_ia_xx_start(current_out_opt,
|
|
IPDHCPS6_OPTION_IA_NA,
|
|
current_out_opt_buf_len,
|
|
bytes_written);
|
|
if (out_ia_na_opt == IP_NULL)
|
|
return IPCOM_ERR_NO_MEMORY;
|
|
current_out_opt_buf_len -= *bytes_written;
|
|
current_out_opt = (Ipdhcps6_option *) ((Ip_u8 *) current_out_opt + *bytes_written);
|
|
|
|
/* Extact the values suggested by the client */
|
|
iaid = ipdhcps6_get_u32(&in_ia_na_opt->iaid);
|
|
conf_opt = ipdhcps6_option_get_configured(host, subnet, IPDHCPS6_OPTION_CONF_RENEW_TIME);
|
|
if (conf_opt)
|
|
t1 = IP_GET_NTOHL(conf_opt->data.generic);
|
|
conf_opt = ipdhcps6_option_get_configured(host, subnet, IPDHCPS6_OPTION_CONF_REBIND_TIME);
|
|
if (conf_opt)
|
|
t2 = IP_GET_NTOHL(conf_opt->data.generic);
|
|
if (0 == t1)
|
|
t1 = ipdhcps6_get_u32(&in_ia_na_opt->t1);
|
|
if (0 == t2)
|
|
t2 = ipdhcps6_get_u32(&in_ia_na_opt->t2);
|
|
/* init value, no default value */
|
|
lease_valid = 0;
|
|
lease_preferred = 0;
|
|
}
|
|
|
|
/* Process every IA-NA option the client sent */
|
|
for (; o != IP_NULL; o = ipdhcps6_option_next(o, &in_ia_na_opt_len))
|
|
{
|
|
Ip_u16 opt_code;
|
|
Ipdhcps6_option_iaaddr *in_iaaddr;
|
|
Ipdhcps6_option_iaaddr *out_iaaddr;
|
|
Ipdhcps6_lease_addr *lease;
|
|
Ip_bool on_link = IP_FALSE;
|
|
opt_code = ipdhcps6_get_u16(&o->code);
|
|
switch (opt_code)
|
|
{
|
|
case IPDHCPS6_OPTION_IAADDR:
|
|
in_iaaddr = (Ipdhcps6_option_iaaddr *) o;
|
|
|
|
lease = ipcom_hash_get(subnet->active_leases, &in_iaaddr->ipv6_address);
|
|
|
|
if (lease==IP_NULL)
|
|
{
|
|
/* NOTE : not strictly prefix on-link concept, only for SERVER knowledge */
|
|
on_link = (ipdhcps6_pool_is_addr_part_of(subnet->pools, &in_iaaddr->ipv6_address, 0) ? IP_TRUE : IP_FALSE);
|
|
|
|
IPCOM_LOG3(INFO, "message type %d, IA_NA address is %s, on link flag is %d.",
|
|
msg_type,
|
|
ipcom_inet_ntop(IP_AF_INET6, &in_iaaddr->ipv6_address,
|
|
ipdhcps6->addr_str, sizeof(ipdhcps6->addr_str)),
|
|
on_link);
|
|
|
|
if(msg_type==IPDHCPS6_MSG_CONFIRM)
|
|
{
|
|
if (!on_link)
|
|
{
|
|
/* RFC3315 section 18.2.2:
|
|
* If the server is unable to perform
|
|
* this test (for example, the server does not have information about
|
|
* prefixes on the link to which the client is connected), or there were
|
|
* no addresses in any of the IAs sent by the client, the server MUST
|
|
* NOT send a reply to the client.
|
|
*/
|
|
if (ipdhcps6_pool_check_section18_2_2(subnet, &in_iaaddr->ipv6_address))
|
|
return IPCOM_ERR_IGNORED_PACKET;
|
|
|
|
err = ipdhcps6_option_add_status_code(current_out_opt,
|
|
current_out_opt_buf_len,
|
|
bytes_written,
|
|
IPDHCPS6_CODE_NOT_ON_LINK,
|
|
"Not all address is on link!");
|
|
current_out_opt_buf_len -= *bytes_written;
|
|
}
|
|
else
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
current_out_opt = out_opt;
|
|
current_out_opt_buf_len = out_opt_buf_len;
|
|
|
|
out_ia_na_opt = ipdhcps6_option_add_ia_xx_start(current_out_opt,
|
|
IPDHCPS6_OPTION_IA_NA,
|
|
current_out_opt_buf_len,
|
|
bytes_written);
|
|
current_out_opt_buf_len -= *bytes_written;
|
|
current_out_opt = (Ipdhcps6_option *) ((Ip_u8 *) current_out_opt + *bytes_written);
|
|
|
|
if(msg_type == IPDHCPS6_MSG_REBIND||(msg_type==IPDHCPS6_MSG_RENEW&&!on_link))
|
|
{
|
|
|
|
out_iaaddr = ipdhcps6_option_add_iaaddr_start(current_out_opt,
|
|
current_out_opt_buf_len,
|
|
bytes_written);
|
|
current_out_opt_buf_len -= *bytes_written;
|
|
current_out_opt = (Ipdhcps6_option *) ((Ip_u8 *) current_out_opt + *bytes_written);
|
|
|
|
ipdhcps6_option_add_iaaddr_finish(out_iaaddr,
|
|
current_out_opt_buf_len,
|
|
&in_iaaddr->ipv6_address,
|
|
0,
|
|
0);
|
|
}
|
|
else if(msg_type==IPDHCPS6_MSG_RELEASE||msg_type == IPDHCPS6_MSG_DECLINE||msg_type ==IPDHCPS6_MSG_RENEW)
|
|
{
|
|
|
|
err = ipdhcps6_option_add_status_code(current_out_opt,
|
|
current_out_opt_buf_len,
|
|
bytes_written,
|
|
IPDHCPS6_CODE_NO_BINDING,
|
|
"No client binding found!");
|
|
current_out_opt_buf_len -= *bytes_written;
|
|
|
|
}
|
|
else if(msg_type==IPDHCPS6_MSG_SOLICIT||msg_type==IPDHCPS6_MSG_REQUEST)
|
|
{
|
|
if(msg_type==IPDHCPS6_MSG_REQUEST&&!on_link)
|
|
{
|
|
err = ipdhcps6_option_add_status_code(current_out_opt,
|
|
current_out_opt_buf_len,
|
|
bytes_written,
|
|
IPDHCPS6_CODE_NOT_ON_LINK,
|
|
"Not all address is on link!");
|
|
current_out_opt_buf_len -= *bytes_written;
|
|
}
|
|
else
|
|
{
|
|
if (on_link)
|
|
{
|
|
IPDHCPS6_SET_IN6_ADDR(&lease_addr, &in_iaaddr->ipv6_address);
|
|
/* XXX : check the relationship between preferred and valid */
|
|
lease_valid = ip_ntohl(in_iaaddr->valid_lifetime);
|
|
lease_preferred = ip_ntohl(in_iaaddr->preferred_lifetime);
|
|
}
|
|
goto done;
|
|
}
|
|
}
|
|
ipdhcps6_option_add_ia_xx_finish((void *)out_ia_na_opt,
|
|
current_out_opt_buf_len,
|
|
iaid,
|
|
0,
|
|
0);
|
|
}
|
|
*bytes_written = out_opt_buf_len - current_out_opt_buf_len;
|
|
|
|
return err;
|
|
}
|
|
|
|
if(msg_type==IPDHCPS6_MSG_DECLINE)
|
|
{
|
|
if (lease->host==host)
|
|
{
|
|
struct Ip_timeval duration;
|
|
/*Note when the lease is created, it will install into active lease and bind to host*/
|
|
lease->host = IP_NULL;
|
|
lease->iaid = 0;
|
|
ipcom_tmo2_cancel(&lease->tmo);
|
|
IPCOM_TMO2_INIT(&lease->tmo);
|
|
#define IPDHCPC6_ABANDONED_TIME 24*60*60
|
|
/*for one day*/
|
|
/*need to define a macro and documentation*/
|
|
duration.tv_sec = ipcom_sysvar_get_as_int ("ipdhcps6.abandoned_time",IPDHCPC6_ABANDONED_TIME);
|
|
duration.tv_usec = 0;
|
|
if (ipcom_tmo2_request_timeval(ipdhcps6->timeouts,
|
|
&lease->tmo,
|
|
(Ipcom_tmo2_func) ipdhcps6_pool_lease_expired,
|
|
lease, &duration) != IPCOM_SUCCESS)
|
|
{
|
|
ipcom_hash_remove(lease->active_leases, lease);
|
|
ipcom_list_remove(&lease->next);
|
|
ipcom_free(lease);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
if(msg_type==IPDHCPS6_MSG_RELEASE)
|
|
{
|
|
ipdhcps6_pool_lease_delete(lease, lease->active_leases);
|
|
continue;
|
|
}
|
|
if(msg_type==IPDHCPS6_MSG_CONFIRM)
|
|
{
|
|
continue;
|
|
}
|
|
if (IPDHCPS6_MSG_RENEW == msg_type)
|
|
{
|
|
/* RFC3315 section 18.2.3:
|
|
* If the server cannot find a client entry for the IA the
|
|
* server returns the IA containing no addresses with a Status
|
|
* Code option set to NoBinding in the Reply message.
|
|
*/
|
|
/* XXX more strict checking... */
|
|
if (lease->iaid != iaid)
|
|
{
|
|
err = ipdhcps6_option_add_status_code(current_out_opt,
|
|
current_out_opt_buf_len,
|
|
bytes_written,
|
|
IPDHCPS6_CODE_NO_BINDING,
|
|
"No client binding found!");
|
|
current_out_opt_buf_len -= *bytes_written;
|
|
ipdhcps6_pool_set_t1_and_t2(lease->preferred, &t1, &t2);
|
|
ipdhcps6_option_add_ia_xx_finish((void *)out_ia_na_opt,
|
|
current_out_opt_buf_len,
|
|
iaid,
|
|
t1,
|
|
t2);
|
|
*bytes_written = out_opt_buf_len - current_out_opt_buf_len;
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/*here lease is not null*/
|
|
out_iaaddr = ipdhcps6_option_add_iaaddr_start(current_out_opt,
|
|
current_out_opt_buf_len,
|
|
bytes_written);
|
|
if (out_iaaddr == IP_NULL)
|
|
return IPCOM_ERR_NO_MEMORY;
|
|
current_out_opt_buf_len -= *bytes_written;
|
|
current_out_opt = (Ipdhcps6_option *) ((Ip_u8 *) current_out_opt + *bytes_written);
|
|
|
|
|
|
|
|
if (lease->host == host)
|
|
{
|
|
is_addr_assigned = IP_TRUE;
|
|
(void)ipcom_tmo2_reset(&lease->tmo);
|
|
}
|
|
|
|
lease_valid = lease->valid;
|
|
lease_preferred = lease->preferred;
|
|
|
|
ipdhcps6_option_add_iaaddr_finish(out_iaaddr,
|
|
current_out_opt_buf_len,
|
|
&in_iaaddr->ipv6_address,
|
|
lease_preferred,
|
|
lease_valid);
|
|
break;
|
|
default:
|
|
/* Ignore all other options for now */
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
done:
|
|
if(noiaaddrs&&msg_type == IPDHCPS6_MSG_CONFIRM)
|
|
{
|
|
IPCOM_LOG0(INFO, "Discarding message since no ia address in confirm");
|
|
return IPCOM_ERR_IGNORED_PACKET;
|
|
}
|
|
if (!is_addr_assigned&&should_contain_iana)
|
|
{
|
|
Ipdhcps6_option_iaaddr *out_iaaddr;
|
|
Ipdhcps6_lease_addr *lease = IP_NULL;
|
|
Ipcom_list *cur = IP_NULL;
|
|
|
|
*bytes_written = 0;
|
|
|
|
for (cur = ipcom_list_first(&host->leases_head);
|
|
IP_NULL != cur;
|
|
cur = ipcom_list_next(cur))
|
|
{
|
|
lease = ipcom_list_entry(cur, Ipdhcps6_lease_addr, next);
|
|
ip_assert(IP_NULL != lease);
|
|
if ((IPDHCPS6_OPTION_IA_NA == lease->ia_type)
|
|
&& (iaid == lease->iaid))
|
|
break;
|
|
}
|
|
|
|
if (IP_NULL == cur)
|
|
{
|
|
err = ipdhcps6_pool_create_new_lease(subnet->pools,
|
|
subnet->active_leases,
|
|
&lease,
|
|
host,
|
|
&lease_addr,
|
|
0,
|
|
lease_valid,
|
|
lease_preferred,
|
|
iaid,
|
|
current_out_opt,
|
|
current_out_opt_buf_len,
|
|
bytes_written,
|
|
IPDHCPS6_OPTION_IA_NA,
|
|
msg_type);
|
|
if (err != IPCOM_SUCCESS)
|
|
{
|
|
/* TAHI3315.29 : only need the status code, SO use the original pointer */
|
|
if ((IPCOM_ERR_REACHED_MAX == err)
|
|
&& (IPDHCPS6_MSG_SOLICIT == msg_type))
|
|
{
|
|
return ipdhcps6_option_add_status_code(out_opt,
|
|
out_opt_buf_len,
|
|
bytes_written,
|
|
IPDHCPS6_CODE_NO_ADDRS_AVAIL,
|
|
"No more addresses avaiable");
|
|
}
|
|
return err;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
(void)ipcom_tmo2_reset(&lease->tmo);
|
|
IPDHCPS6_SET_IN6_ADDR(&lease_addr, &lease->addr);
|
|
}
|
|
|
|
if(*bytes_written)
|
|
{
|
|
current_out_opt_buf_len -= *bytes_written;
|
|
current_out_opt = (Ipdhcps6_option *) ((Ip_u8 *) current_out_opt + *bytes_written);
|
|
}
|
|
else
|
|
{
|
|
/* No address selected by the suggested once,
|
|
take any address from the pool */
|
|
out_iaaddr = ipdhcps6_option_add_iaaddr_start(current_out_opt,
|
|
current_out_opt_buf_len,
|
|
bytes_written);
|
|
current_out_opt_buf_len -= *bytes_written;
|
|
current_out_opt = (Ipdhcps6_option *) ((Ip_u8 *) current_out_opt + *bytes_written);
|
|
|
|
if (lease != IP_NULL)
|
|
{
|
|
lease_valid = lease->valid;
|
|
lease_preferred = lease->preferred;
|
|
}
|
|
|
|
ipdhcps6_option_add_iaaddr_finish(out_iaaddr,
|
|
current_out_opt_buf_len,
|
|
&lease_addr,
|
|
lease_preferred,
|
|
lease_valid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(should_contain_iana)
|
|
{
|
|
ipdhcps6_pool_set_t1_and_t2(lease_preferred, &t1, &t2);
|
|
ipdhcps6_option_add_ia_xx_finish((void *)out_ia_na_opt,
|
|
current_out_opt_buf_len,
|
|
iaid,
|
|
t1,
|
|
t2);
|
|
/* Set bytes_written to the total amount of bytes written in this function */
|
|
*bytes_written = out_opt_buf_len - current_out_opt_buf_len;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_process_prefix_delegation_request
|
|
*===========================================================================
|
|
* Description: Processes a request or renewal of prefix delegation address.
|
|
* Parameters: msg_type - DHCPv6 message type.
|
|
* subnet - The subnet the client is located at.
|
|
* host - The host data for the client.
|
|
* in_opt - The IA-PD option sent by the client
|
|
* out_opt - The buffer where the response IA-PD should be written
|
|
* out_opt_buf_len - The remaining length of the response buffer.
|
|
* bytes_written - Number of bytes written to 'out_opt'
|
|
* on return.
|
|
* Returns: IPCOM_SUCCESS - Operation successful
|
|
* IPCOM_ERR_NO_MEM - Too small response buffer
|
|
* IPCOM_ERR_FAILED - Silently drop this message
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipdhcps6_pool_process_prefix_delegation_request(Ip_u8 msg_type,
|
|
Ipdhcps6_subnet_entry *subnet,
|
|
Ipdhcps6_host *host,
|
|
Ipdhcps6_option *in_opt,
|
|
Ipdhcps6_option *out_opt,
|
|
Ip_u16 out_opt_buf_len,
|
|
Ip_u16 *bytes_written)
|
|
{
|
|
Ip_u16 in_ia_pd_opt_len;
|
|
Ipdhcps6_option_ia_xx *in_ia_pd_opt = (Ipdhcps6_option_ia_xx *) in_opt;
|
|
Ipdhcps6_option_ia_xx *out_ia_pd_opt = IP_NULL;
|
|
Ip_u32 iaid = 0;
|
|
Ipdhcps6_option *o;
|
|
Ip_u32 t1, t2;
|
|
struct Ip_in6_addr lease_addr = IP_IN6ADDR_ANY_INIT;
|
|
Ip_u32 lease_valid, lease_preferred;
|
|
Ip_u8 lease_prefix_len;
|
|
Ip_bool is_addr_assigned = IP_FALSE;
|
|
Ipdhcps6_option *current_out_opt = out_opt;
|
|
Ip_u16 current_out_opt_buf_len = out_opt_buf_len;
|
|
Ip_err err = IPCOM_SUCCESS;
|
|
Ip_bool noiaaddrs = IP_FALSE;
|
|
Ip_bool should_contain_iapd;
|
|
Ip_bool has_iaprefix_option = IP_FALSE;
|
|
Ipdhcps6_option *conf_opt = IP_NULL;
|
|
|
|
/* so many parameters needed if using function */
|
|
#define IPDHCPS6_ASSIGN_IAPD_PRE \
|
|
do \
|
|
{ \
|
|
ipcom_memset((void *)&lease_addr, 0, sizeof(struct Ip_in6_addr)); \
|
|
lease_valid = 0; \
|
|
lease_preferred = 0; \
|
|
lease_prefix_len = 0; \
|
|
t1 = 0; \
|
|
t2 = 0; \
|
|
if (should_contain_iapd) \
|
|
{ \
|
|
/* Begin adding the IA-PD option */ \
|
|
out_ia_pd_opt = ipdhcps6_option_add_ia_xx_start(current_out_opt, \
|
|
IPDHCPS6_OPTION_IA_PD, \
|
|
current_out_opt_buf_len, \
|
|
bytes_written); \
|
|
if (out_ia_pd_opt == IP_NULL) \
|
|
return IPCOM_ERR_NO_MEMORY; \
|
|
current_out_opt_buf_len -= *bytes_written; \
|
|
current_out_opt = (Ipdhcps6_option *) ((Ip_u8 *) current_out_opt + *bytes_written); \
|
|
/* Extact the values suggested by the client */ \
|
|
iaid = ipdhcps6_get_u32(&in_ia_pd_opt->iaid); \
|
|
conf_opt = ipdhcps6_option_get_configured(host, subnet, IPDHCPS6_OPTION_CONF_RENEW_TIME); \
|
|
if (conf_opt) \
|
|
t1 = IP_GET_NTOHL(conf_opt->data.generic); \
|
|
conf_opt = ipdhcps6_option_get_configured(host, subnet, IPDHCPS6_OPTION_CONF_REBIND_TIME); \
|
|
if (conf_opt) \
|
|
t2 = IP_GET_NTOHL(conf_opt->data.generic); \
|
|
if (0 == t1) \
|
|
t1 = ipdhcps6_get_u32(&in_ia_pd_opt->t1); \
|
|
if (0 == t2) \
|
|
t2 = ipdhcps6_get_u32(&in_ia_pd_opt->t2); \
|
|
} \
|
|
} while(0)
|
|
|
|
#define IPDHCPS6_ASSIGN_IAPD \
|
|
do \
|
|
{ \
|
|
if (!is_addr_assigned&&should_contain_iapd) \
|
|
{ \
|
|
Ipdhcps6_option_iaprefix *out_iaprefix_tmp = IP_NULL; \
|
|
Ipdhcps6_lease_addr *lease_tmp = IP_NULL; \
|
|
Ipcom_list *cur_list = IP_NULL; \
|
|
*bytes_written = 0; \
|
|
for (cur_list = ipcom_list_first(&host->leases_head); \
|
|
IP_NULL != cur_list; \
|
|
cur_list = ipcom_list_next(cur_list)) \
|
|
{ \
|
|
lease_tmp = ipcom_list_entry(cur_list, Ipdhcps6_lease_addr, next); \
|
|
ip_assert(IP_NULL != lease_tmp); \
|
|
if ((IPDHCPS6_OPTION_IA_PD == lease_tmp->ia_type) \
|
|
&& (iaid == lease_tmp->iaid)) \
|
|
break; \
|
|
} \
|
|
if (IP_NULL == cur_list) \
|
|
{ \
|
|
err = ipdhcps6_pool_create_new_lease(subnet->pools, \
|
|
subnet->active_leases, \
|
|
&lease_tmp, \
|
|
host, \
|
|
&lease_addr, \
|
|
lease_prefix_len, \
|
|
lease_valid, \
|
|
lease_preferred, \
|
|
iaid, \
|
|
current_out_opt, \
|
|
current_out_opt_buf_len, \
|
|
bytes_written, \
|
|
IPDHCPS6_OPTION_IA_PD, \
|
|
msg_type); \
|
|
if (err != IPCOM_SUCCESS) \
|
|
{ \
|
|
/* TAHI3315.29 : only need the status code, SO use the original pointer */ \
|
|
if ((IPCOM_ERR_REACHED_MAX == err) \
|
|
&& (IPDHCPS6_MSG_SOLICIT == msg_type)) \
|
|
{ \
|
|
return ipdhcps6_option_add_status_code(out_opt, \
|
|
out_opt_buf_len, \
|
|
bytes_written, \
|
|
IPDHCPS6_CODE_NO_PREFIX_AVAIL, \
|
|
"No more prefix avaiable for solicit"); \
|
|
} \
|
|
return err; \
|
|
} \
|
|
} \
|
|
else \
|
|
{ \
|
|
(void)ipcom_tmo2_reset(&lease_tmp->tmo); \
|
|
IPDHCPS6_SET_IN6_ADDR(&lease_addr, &lease_tmp->addr); \
|
|
lease_preferred = lease_tmp->preferred; \
|
|
lease_prefix_len = lease_tmp->prefix_len; \
|
|
} \
|
|
if(*bytes_written) \
|
|
{ \
|
|
current_out_opt_buf_len -= *bytes_written; \
|
|
current_out_opt = (Ipdhcps6_option *) ((Ip_u8 *) current_out_opt + *bytes_written); \
|
|
} \
|
|
else \
|
|
{ \
|
|
/* No address selected by the suggested once, take any address from the pool */ \
|
|
out_iaprefix_tmp = ipdhcps6_option_add_iaprefix_start(current_out_opt, \
|
|
current_out_opt_buf_len, \
|
|
bytes_written); \
|
|
current_out_opt_buf_len -= *bytes_written; \
|
|
current_out_opt = (Ipdhcps6_option *) ((Ip_u8 *) current_out_opt + *bytes_written); \
|
|
if (lease_tmp != IP_NULL) \
|
|
{ \
|
|
lease_valid = lease_tmp->valid; \
|
|
lease_preferred = lease_tmp->preferred; \
|
|
lease_prefix_len = lease_tmp->prefix_len; \
|
|
} \
|
|
ipdhcps6_option_add_iaprefix_finish(out_iaprefix_tmp, \
|
|
current_out_opt_buf_len, \
|
|
&lease_addr, \
|
|
lease_prefix_len, \
|
|
lease_preferred, \
|
|
lease_valid); \
|
|
} \
|
|
} \
|
|
if(should_contain_iapd) \
|
|
{ \
|
|
ipdhcps6_pool_set_t1_and_t2(lease_preferred, &t1, &t2); \
|
|
ipdhcps6_option_add_ia_xx_finish((void *)out_ia_pd_opt, \
|
|
current_out_opt_buf_len, \
|
|
iaid, \
|
|
t1, \
|
|
t2); \
|
|
/* Set bytes_written to the total amount of bytes written in this function */ \
|
|
*bytes_written = out_opt_buf_len - current_out_opt_buf_len; \
|
|
} \
|
|
} while(0)
|
|
|
|
*bytes_written = 0;
|
|
/* RFC3633, 12.1. Requesting router behavior
|
|
* The Confirm and Decline message types are not used with Prefix
|
|
* Delegation.
|
|
*/
|
|
if (msg_type == IPDHCPS6_MSG_DECLINE || msg_type == IPDHCPS6_MSG_CONFIRM)
|
|
{
|
|
IPCOM_LOG1(INFO, "Discarding for the invalid message type(%d).", msg_type);
|
|
return IPCOM_ERR_IGNORED_PACKET;
|
|
}
|
|
|
|
should_contain_iapd = (msg_type != IPDHCPS6_MSG_RELEASE);
|
|
|
|
if (subnet == IP_NULL)
|
|
{
|
|
if (msg_type == IPDHCPS6_MSG_SOLICIT || msg_type == IPDHCPS6_MSG_REQUEST)
|
|
return ipdhcps6_option_add_status_code(current_out_opt,
|
|
current_out_opt_buf_len,
|
|
bytes_written,
|
|
IPDHCPS6_CODE_NOT_ON_LINK,
|
|
"Nothing configured for that link");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
if (subnet->pools == IP_NULL)
|
|
{
|
|
if (msg_type == IPDHCPS6_MSG_SOLICIT || msg_type == IPDHCPS6_MSG_REQUEST)
|
|
return ipdhcps6_option_add_status_code(current_out_opt,
|
|
current_out_opt_buf_len,
|
|
bytes_written,
|
|
IPDHCPS6_CODE_NO_PREFIX_AVAIL,
|
|
"No prefix pool on that link");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
in_ia_pd_opt_len = ipdhcps6_get_u16(&in_ia_pd_opt->len) - 12;
|
|
if (in_ia_pd_opt_len == 0)
|
|
/* No suggested addresses present */
|
|
{
|
|
o = IP_NULL;
|
|
noiaaddrs = IP_TRUE;
|
|
}
|
|
else
|
|
{
|
|
o = (Ipdhcps6_option *) in_ia_pd_opt->options;
|
|
if (ipdhcps6_option_check(o, in_ia_pd_opt_len) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "Malformed IA_PD option");
|
|
/* give no reponse back */
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
}
|
|
|
|
/* Process every IA-PD option the client sent */
|
|
for (; o != IP_NULL; o = ipdhcps6_option_next(o, &in_ia_pd_opt_len))
|
|
{
|
|
Ip_u16 opt_code;
|
|
Ipdhcps6_option_iaprefix *in_iaprefix = IP_NULL;
|
|
Ipdhcps6_option_iaprefix *out_iaprefix = IP_NULL;
|
|
Ipdhcps6_lease_addr *lease = IP_NULL;
|
|
struct Ip_in6_addr *prefix_addr = IP_NULL;
|
|
|
|
opt_code = ipdhcps6_get_u16(&o->code);
|
|
switch (opt_code)
|
|
{
|
|
case IPDHCPS6_OPTION_IAPREFIX:
|
|
/* for each IAPREFIX will assign a real prefix */
|
|
IPDHCPS6_ASSIGN_IAPD_PRE;
|
|
has_iaprefix_option = IP_TRUE;
|
|
|
|
in_iaprefix = (Ipdhcps6_option_iaprefix *)o;
|
|
prefix_addr = IPDHCPS6_OPTION_IAPREFIX_GET_PREFIX_PTR(in_iaprefix);
|
|
lease_prefix_len = IPDHCPS6_OPTION_IAPREFIX_GET_LEN(in_iaprefix);
|
|
|
|
lease = ipcom_hash_get(subnet->active_leases, (void *)prefix_addr);
|
|
|
|
if (lease==IP_NULL)
|
|
{
|
|
#if 0 /* because TAHI3633.39 change the behavior */
|
|
Ip_bool is_binding_exist = IP_FALSE;
|
|
|
|
/* TAHI3633.39 : Same ID, invalid address.
|
|
* Reply with IA_PD option(w/ IA_PD Prefix lifetime=0).
|
|
* TAHI3633.40 : Different IA_PD ID number.
|
|
* Reply with IA_PD option(w/o IA_PD prefix w/ Status Code NoBinding).*/
|
|
/* RFC3315 section 18.2.3:
|
|
* If the server cannot find a client entry for the IA the
|
|
* server returns the IA containing no addresses with a Status
|
|
* Code option set to NoBinding in the Reply message.
|
|
*/
|
|
if (IPDHCPS6_MSG_RENEW == msg_type)
|
|
{
|
|
Ipdhcps6_lease_addr *le = IP_NULL;
|
|
Ipcom_list *cur = IP_NULL;
|
|
|
|
for (cur = ipcom_list_first(&host->leases_head);
|
|
IP_NULL != cur;
|
|
cur = ipcom_list_next(cur))
|
|
{
|
|
le = ipcom_list_entry(cur, Ipdhcps6_lease_addr, next);
|
|
ip_assert(IP_NULL != le);
|
|
if ((IPDHCPS6_OPTION_IA_PD == le->ia_type)
|
|
&& (iaid == le->iaid))
|
|
{
|
|
is_binding_exist = IP_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
IPCOM_LOG4(DEBUG2, "message type %d, IA_PD address is %s/%u, BINDING flag is %d.",
|
|
msg_type,
|
|
ipcom_inet_ntop(IP_AF_INET6, prefix_addr,
|
|
ipdhcps6->addr_str, sizeof(ipdhcps6->addr_str)),
|
|
lease_prefix_len,
|
|
is_binding_exist);
|
|
#endif
|
|
IPCOM_LOG3(DEBUG2, "message type %d, IA_PD address is %s/%u.",
|
|
msg_type,
|
|
ipcom_inet_ntop(IP_AF_INET6, prefix_addr,
|
|
ipdhcps6->addr_str, sizeof(ipdhcps6->addr_str)),
|
|
lease_prefix_len);
|
|
|
|
current_out_opt = out_opt;
|
|
current_out_opt_buf_len = out_opt_buf_len;
|
|
|
|
out_ia_pd_opt = ipdhcps6_option_add_ia_xx_start(current_out_opt,
|
|
IPDHCPS6_OPTION_IA_PD,
|
|
current_out_opt_buf_len,
|
|
bytes_written);
|
|
current_out_opt_buf_len -= *bytes_written;
|
|
current_out_opt = (Ipdhcps6_option *) ((Ip_u8 *) current_out_opt + *bytes_written);
|
|
|
|
if(msg_type == IPDHCPS6_MSG_REBIND/*||(msg_type==IPDHCPS6_MSG_RENEW&&is_binding_exist)*/)
|
|
{
|
|
out_iaprefix = ipdhcps6_option_add_iaprefix_start(current_out_opt,
|
|
current_out_opt_buf_len,
|
|
bytes_written);
|
|
current_out_opt_buf_len -= *bytes_written;
|
|
current_out_opt = (Ipdhcps6_option *) ((Ip_u8 *) current_out_opt + *bytes_written);
|
|
|
|
ipdhcps6_option_add_iaprefix_finish(out_iaprefix,
|
|
current_out_opt_buf_len,
|
|
prefix_addr,
|
|
lease_prefix_len,
|
|
0,
|
|
0);
|
|
}
|
|
else if(msg_type==IPDHCPS6_MSG_RELEASE||msg_type ==IPDHCPS6_MSG_RENEW)
|
|
{
|
|
err = ipdhcps6_option_add_status_code(current_out_opt,
|
|
current_out_opt_buf_len,
|
|
bytes_written,
|
|
IPDHCPS6_CODE_NO_BINDING,
|
|
"No client binding found!");
|
|
current_out_opt_buf_len -= *bytes_written;
|
|
}
|
|
else if(msg_type==IPDHCPS6_MSG_SOLICIT||msg_type==IPDHCPS6_MSG_REQUEST)
|
|
{
|
|
Ip_bool is_valid_prefix = IP_TRUE;
|
|
Ip_bool on_link = IP_FALSE;
|
|
|
|
if ((IP_IN6_IS_ADDR_UNSPECIFIED(prefix_addr))
|
|
|| (0 == lease_prefix_len))
|
|
is_valid_prefix = IP_FALSE;
|
|
|
|
/* NOTE : not strictly prefix on-link concept, only for SERVER knowledge */
|
|
on_link = (is_valid_prefix && ipdhcps6_pool_is_addr_part_of(subnet->pools,
|
|
prefix_addr,
|
|
lease_prefix_len)
|
|
? IP_TRUE : IP_FALSE);
|
|
|
|
IPCOM_LOG2(DEBUG2, "on link flag is %d, %d.",
|
|
on_link,
|
|
is_valid_prefix);
|
|
|
|
if(msg_type==IPDHCPS6_MSG_REQUEST && !on_link && is_valid_prefix)
|
|
{
|
|
err = ipdhcps6_option_add_status_code(current_out_opt,
|
|
current_out_opt_buf_len,
|
|
bytes_written,
|
|
IPDHCPS6_CODE_NOT_ON_LINK,
|
|
"Not all address is on link!");
|
|
current_out_opt_buf_len -= *bytes_written;
|
|
}
|
|
else
|
|
{
|
|
if (on_link)
|
|
{
|
|
IPDHCPS6_SET_IN6_ADDR(&lease_addr, prefix_addr);
|
|
/* XXX : check the relationship between preferred and valid */
|
|
lease_valid = ip_ntohl(in_iaprefix->valid_lifetime);
|
|
lease_preferred = ip_ntohl(in_iaprefix->preferred_lifetime);
|
|
}
|
|
else /* init it if the 2rd time */
|
|
{
|
|
ipcom_memset((void *)&lease_addr, 0, sizeof(struct Ip_in6_addr));
|
|
lease_prefix_len = 0;
|
|
lease_valid = 0;
|
|
lease_preferred = 0;
|
|
}
|
|
IPDHCPS6_ASSIGN_IAPD;
|
|
break;
|
|
}
|
|
}
|
|
ipdhcps6_option_add_ia_xx_finish((void *)out_ia_pd_opt,
|
|
current_out_opt_buf_len,
|
|
iaid,
|
|
0,
|
|
0);
|
|
|
|
*bytes_written = out_opt_buf_len - current_out_opt_buf_len;
|
|
|
|
return err;
|
|
}
|
|
|
|
if (msg_type==IPDHCPS6_MSG_RELEASE)
|
|
{
|
|
ipdhcps6_pool_lease_delete(lease, lease->active_leases);
|
|
continue;
|
|
}
|
|
|
|
if (IPDHCPS6_MSG_RENEW == msg_type)
|
|
{
|
|
/* RFC3315 section 18.2.3:
|
|
* If the server cannot find a client entry for the IA the
|
|
* server returns the IA containing no addresses with a Status
|
|
* Code option set to NoBinding in the Reply message.
|
|
*/
|
|
/* XXX more strict checking... */
|
|
if ((lease->iaid != iaid)
|
|
|| (lease->prefix_len != lease_prefix_len))
|
|
{
|
|
err = ipdhcps6_option_add_status_code(current_out_opt,
|
|
current_out_opt_buf_len,
|
|
bytes_written,
|
|
IPDHCPS6_CODE_NO_BINDING,
|
|
"No client binding found!");
|
|
current_out_opt_buf_len -= *bytes_written;
|
|
ipdhcps6_pool_set_t1_and_t2(lease->preferred, &t1, &t2);
|
|
ipdhcps6_option_add_ia_xx_finish((void *)out_ia_pd_opt,
|
|
current_out_opt_buf_len,
|
|
iaid,
|
|
t1,
|
|
t2);
|
|
*bytes_written = out_opt_buf_len - current_out_opt_buf_len;
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/*here lease is not null*/
|
|
out_iaprefix = ipdhcps6_option_add_iaprefix_start(current_out_opt,
|
|
current_out_opt_buf_len,
|
|
bytes_written);
|
|
if (out_iaprefix == IP_NULL)
|
|
return IPCOM_ERR_NO_MEMORY;
|
|
current_out_opt_buf_len -= *bytes_written;
|
|
current_out_opt = (Ipdhcps6_option *) ((Ip_u8 *) current_out_opt + *bytes_written);
|
|
|
|
if (lease->host == host)
|
|
{
|
|
is_addr_assigned = IP_TRUE;
|
|
(void)ipcom_tmo2_reset(&lease->tmo);
|
|
}
|
|
|
|
lease_valid = lease->valid;
|
|
lease_preferred = lease->preferred;
|
|
lease_prefix_len = lease->prefix_len; /* should be same with "prefix_len" */
|
|
|
|
ipdhcps6_option_add_iaprefix_finish(out_iaprefix,
|
|
current_out_opt_buf_len,
|
|
prefix_addr,
|
|
lease_prefix_len,
|
|
lease_preferred,
|
|
lease_valid);
|
|
IPDHCPS6_ASSIGN_IAPD;
|
|
break;
|
|
default:
|
|
/* Ignore all other options for now */
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* if there is no IAPREFIX in the options */
|
|
if (IP_FALSE == has_iaprefix_option)
|
|
{
|
|
IPDHCPS6_ASSIGN_IAPD_PRE;
|
|
IPDHCPS6_ASSIGN_IAPD;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
|
|
#undef IPDHCPS6_ASSIGN_IAPD_PRE
|
|
#undef IPDHCPS6_ASSIGN_IAPD
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_delete_from_subnet
|
|
*===========================================================================
|
|
* Description: Frees all resouces held by the pool that is assiged to
|
|
* a specific subnet.
|
|
* Parameters: sn_entry - The subnet to cleaup the pool from.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipdhcps6_pool_delete_from_subnet(Ipdhcps6_subnet_entry *sn_entry)
|
|
{
|
|
if (sn_entry->pools != IP_NULL)
|
|
{
|
|
Ipdhcps6_addr_pool *p, *np;
|
|
|
|
ipcom_hash_for_each(sn_entry->active_leases,
|
|
(Ipcom_hash_foreach_cb_func) ipdhcps6_pool_lease_delete,
|
|
sn_entry->active_leases);
|
|
ipcom_hash_delete(sn_entry->active_leases);
|
|
sn_entry->active_leases = IP_NULL;
|
|
|
|
/* Free all pools */
|
|
for (p = sn_entry->pools; p != IP_NULL; p = np)
|
|
{
|
|
np = p->next;
|
|
ipcom_free(p);
|
|
}
|
|
|
|
sn_entry->pools = IP_NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_delete_by_poolinfo
|
|
*===========================================================================
|
|
* Description: Frees all resouces held by the pool that is assiged to
|
|
* a specific subnet.
|
|
* Parameters: sn_entry - The subnet to cleaup the pool from.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipdhcps6_pool_delete_by_poolinfo(Ipdhcps6_subnet_entry *sn_entry,
|
|
Ipdhcps6_poolinfo *pool)
|
|
{
|
|
if (sn_entry->pools != IP_NULL)
|
|
{
|
|
Ipdhcps6_addr_pool *pp, *p;
|
|
|
|
/* Free the specical pool */
|
|
for (pp = p = sn_entry->pools; p != IP_NULL; pp = p, p = p->next)
|
|
{
|
|
if (0 == ipcom_memcmp(&p->info, pool, sizeof(*pool)))
|
|
{
|
|
Ipdhcps6_pooldel pdel;
|
|
|
|
pdel.active_leases = sn_entry->active_leases;
|
|
pdel.pool = p;
|
|
ipcom_hash_for_each(sn_entry->active_leases,
|
|
(Ipcom_hash_foreach_cb_func) ipdhcps6_pool_delete_for_each,
|
|
(void *)&pdel);
|
|
/* first entry */
|
|
if (p == sn_entry->pools)
|
|
sn_entry->pools = p->next;
|
|
else
|
|
pp->next = p->next;
|
|
ipcom_free(p);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (IP_NULL == sn_entry->pools)
|
|
{
|
|
ipcom_hash_delete(sn_entry->active_leases);
|
|
sn_entry->active_leases = IP_NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_add
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC Ip_err
|
|
ipdhcps6_pool_add(Ipdhcps6_subnet *subnet, Ipdhcps6_poolinfo *poolinfo)
|
|
{
|
|
Ipdhcps6_subnet_entry *sn_entry = IP_NULL;
|
|
Ip_bool perfect_match;
|
|
Ipdhcps6_addr_pool *pool;
|
|
Ip_err err;
|
|
Ipdhcps6_addr_pool *p = IP_NULL;
|
|
|
|
if (ipdhcps6 == IP_NULL || !IP_BIT_ISSET(ipdhcps6->flags, IPDHCPS6_FLAG_OPEN))
|
|
return IPCOM_ERR_NOT_OPENED;
|
|
|
|
if (subnet == IP_NULL)
|
|
/* Pools can only be assigned to specific links and/or subnet */
|
|
return IPCOM_ERR_NOT_FOUND;
|
|
|
|
ipcom_mutex_lock(ipdhcps6->db.mutex);
|
|
|
|
sn_entry = (Ipdhcps6_subnet_entry *) ipcom_route_lookup(ipdhcps6->db.subnets,
|
|
&subnet->prefix,
|
|
&perfect_match);
|
|
err = IPCOM_ERR_NOT_FOUND;
|
|
if (sn_entry == IP_NULL)
|
|
goto cleanup;
|
|
|
|
/* check if it is already configured, ONLY completely same. */
|
|
for (p = sn_entry->pools; p != IP_NULL; p = p->next)
|
|
{
|
|
if (0 == ipcom_memcmp(&p->info, poolinfo, sizeof(*poolinfo)))
|
|
{
|
|
err = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
err = IPCOM_ERR_NO_MEMORY;
|
|
pool = ipcom_malloc(sizeof(Ipdhcps6_addr_pool));
|
|
if (pool == IP_NULL)
|
|
goto cleanup;
|
|
|
|
err = IPCOM_ERR_FAILED;
|
|
if (ipcom_memcmp(&poolinfo->first, &poolinfo->last, sizeof(poolinfo->first)) > 0)
|
|
{
|
|
/* first address must be smaller than the last */
|
|
ipcom_free(pool);
|
|
goto cleanup;
|
|
}
|
|
|
|
ipcom_memcpy(&pool->info, poolinfo, sizeof(*poolinfo));
|
|
|
|
if (sn_entry->active_leases == IP_NULL)
|
|
{
|
|
sn_entry->active_leases = ipcom_hash_new((Ipcom_hash_obj_func) ipdhcps6_pool_obj_hash,
|
|
(Ipcom_hash_key_func) ipdhcps6_pool_key_hash,
|
|
(Ipcom_hash_cmp_func) ipdhcps6_pool_cmp);
|
|
if (sn_entry->active_leases == IP_NULL)
|
|
{
|
|
ipcom_free(pool);
|
|
err = IPCOM_ERR_NO_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
pool->next = sn_entry->pools;
|
|
sn_entry->pools = pool;
|
|
|
|
err = IPCOM_SUCCESS;
|
|
|
|
cleanup:
|
|
ipcom_mutex_unlock(ipdhcps6->db.mutex);
|
|
return err;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_delete
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC Ip_err
|
|
ipdhcps6_pool_delete(Ipdhcps6_subnet *subnet, Ipdhcps6_poolinfo *pool)
|
|
{
|
|
Ipdhcps6_subnet_entry *sn_entry = IP_NULL;
|
|
Ip_bool perfect_match;
|
|
Ip_err err;
|
|
|
|
(void) pool;
|
|
if (ipdhcps6 == IP_NULL || !IP_BIT_ISSET(ipdhcps6->flags, IPDHCPS6_FLAG_OPEN))
|
|
return IPCOM_ERR_NOT_OPENED;
|
|
|
|
if (subnet == IP_NULL)
|
|
/* Pools can only be assigned to specific links and/or subnet */
|
|
return IPCOM_ERR_NOT_FOUND;
|
|
|
|
ipcom_mutex_lock(ipdhcps6->db.mutex);
|
|
|
|
sn_entry = (Ipdhcps6_subnet_entry *) ipcom_route_lookup(ipdhcps6->db.subnets,
|
|
&subnet->prefix,
|
|
&perfect_match);
|
|
err = IPCOM_ERR_NOT_FOUND;
|
|
if (sn_entry == IP_NULL)
|
|
goto cleanup;
|
|
|
|
ipdhcps6_pool_delete_by_poolinfo(sn_entry, pool);
|
|
|
|
err = IPCOM_SUCCESS;
|
|
|
|
cleanup:
|
|
ipcom_mutex_unlock(ipdhcps6->db.mutex);
|
|
return err;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps6_pool_get
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC Ip_err
|
|
ipdhcps6_pool_get(Ipdhcps6_subnet *subnet, Ipdhcps6_poolinfo **pools, Ip_u32 *pools_size)
|
|
{
|
|
Ipdhcps6_subnet_entry *sn_entry = IP_NULL;
|
|
Ip_bool perfect_match;
|
|
Ip_err err;
|
|
Ipdhcps6_addr_pool *p;
|
|
Ip_u32 np = 0;
|
|
Ipdhcps6_poolinfo *pinfo;
|
|
|
|
if (ipdhcps6 == IP_NULL || !IP_BIT_ISSET(ipdhcps6->flags, IPDHCPS6_FLAG_OPEN))
|
|
return IPCOM_ERR_NOT_OPENED;
|
|
|
|
if (subnet == IP_NULL)
|
|
/* Pools can only be assigned to specific links and/or subnet */
|
|
return IPCOM_ERR_NOT_FOUND;
|
|
|
|
ipcom_mutex_lock(ipdhcps6->db.mutex);
|
|
|
|
sn_entry = (Ipdhcps6_subnet_entry *) ipcom_route_lookup(ipdhcps6->db.subnets,
|
|
&subnet->prefix,
|
|
&perfect_match);
|
|
err = IPCOM_ERR_NO_ROUTE;
|
|
if (IP_NULL == sn_entry)
|
|
goto cleanup;
|
|
|
|
err = IPCOM_ERR_NOT_INSTALLED;
|
|
if (sn_entry->pools == IP_NULL)
|
|
goto cleanup;
|
|
|
|
for (p = sn_entry->pools; p != IP_NULL; p = p->next, np++)
|
|
;
|
|
|
|
pinfo = ipcom_calloc(np, sizeof(Ipdhcps6_poolinfo));
|
|
if (!pinfo)
|
|
return IPCOM_ERR_NO_MEMORY;
|
|
|
|
*pools = pinfo;
|
|
*pools_size = np;
|
|
|
|
for (p = sn_entry->pools; p != IP_NULL; p = p->next, pinfo++)
|
|
ipcom_memcpy(pinfo, &p->info, sizeof(p->info));
|
|
|
|
err = IPCOM_SUCCESS;
|
|
|
|
cleanup:
|
|
ipcom_mutex_unlock(ipdhcps6->db.mutex);
|
|
return err;
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|
|
|