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

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
****************************************************************************
*/