/* ****************************************************************************** * 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 ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipdhcps6_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #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 **************************************************************************** */