#include "stdhead.h"
#include "leases.h"
#include "lib.h"

struct lease_details * delete_lease_node (struct lease_details *p, struct lease_details *lease_struct)
{
    extern struct lease_details *lease, *partial_lease;
    struct lease_details *r, *q;

    if (p == lease_struct)
    {
	q = p;
	p = p -> next;
	if (lease_struct == lease)
	    lease = p;
	else if (lease_struct == partial_lease)
	    partial_lease = p;
    }
    else
    {
	r = lease_struct;
	while (r != p)
	{
	    q = r;
	    r = r -> next;
	}
	q -> next = p -> next;
	q = p;
	p = p -> next;
    }
    
    safe_free (q -> client_duid -> duid_type);
    safe_free (q -> client_duid);
    safe_free (q);
    return p;
}

struct lease_details * read_leases_file (char *file_name)
{
    struct lease_details *leases_head = 0, *p = 0, *q = 0;
    struct DUID *d;
    struct DUID1 *d1;
    struct DUID2 *d2;
    struct DUID3 *d3;
    int fd, i;
    u_int32_t temp;
    u_int16_t temp1;
    u_int8_t temp2;
    char ch;
    
    if ((fd = open (file_name, O_RDONLY)) == -1)
    {
#if DEBUG == 2
	printf ("Could not open leases file %s\n", file_name);
#endif
	return NULL;
    }
    
    while (read (fd, &temp, sizeof (u_int32_t)) > 0)
    {
	q = (struct lease_details *) malloc (sizeof (struct lease_details));
	if (!leases_head)
	{
	    leases_head = p = q;
	    q -> next = 0;
	}
	else
	{
	    p -> next = q;
	    p = q;
	}
	
	q -> u_iaid.iaid = temp;
	read (fd, &q -> u_trans_id.trans_id, sizeof (u_int32_t));
	read (fd, &q -> u_t1.t1, sizeof (u_int32_t));
	read (fd, &q -> u_t2.t2, sizeof (u_int32_t));
	read (fd, &q -> u_pref_lifetime.pref_lifetime, sizeof (u_int32_t));
	read (fd, &q -> u_valid_lifetime.valid_lifetime, sizeof (u_int32_t));
	read (fd, &q -> max_renew_count, sizeof (int));
	read (fd, &q -> renew_count, sizeof (int));
	read (fd, &q -> assigned_ipv6_addr, sizeof (struct in6_addr));
	read (fd, &q -> prefix_length, sizeof (u_int8_t));
	
	read (fd, &temp1, sizeof (u_int16_t));
	d = (struct DUID *) malloc (sizeof (struct DUID));
	q -> client_duid = d;
	d -> u_duid_type.duid_type = temp1;
	d -> opt = 0;
	
	if (temp1 == 1)
	{
	    d1 = (struct DUID1 *) malloc (sizeof (struct DUID1));
	    d -> duid_type = d1;
	    
	    read (fd, &temp1, sizeof (u_int16_t));
	    d1 -> u_haddr_type.haddr_type = temp1;

	    read (fd, &temp, sizeof (u_int32_t));
	    d1 -> u_time.time = temp;

	    read (fd, &temp1, sizeof (u_int16_t));
	    d1 -> haddr_len = temp1;
	    d1 -> link_layer_address = (u_int8_t *) malloc (d1 -> haddr_len * sizeof (u_int8_t));
	    
	    for (i = 0; i < d1 -> haddr_len; i++)
	    {
		read (fd, &temp2, sizeof (u_int8_t));
		d1 -> link_layer_address[i] = temp2;
	    }
	}
	else if (temp1 == 2)
	{
	    d2 = (struct DUID2 *) malloc (sizeof (struct DUID2));
	    d -> duid_type = d2;
    
	    read (fd, &temp1, sizeof (u_int16_t));
	    d2 -> u_identifier_length.identifier_length = temp1;
	    d2 -> identifier = (u_int8_t *) malloc (d2 -> u_identifier_length.identifier_length * sizeof (u_int8_t));

	    for (i = 0; i < d2 -> u_identifier_length.identifier_length; i++)
	    {
		read (fd, &temp2, sizeof (u_int8_t));
		d2 -> identifier[i] = temp2;
	    }

	    read (fd, &i, sizeof (int));
	    d2 -> domain_name_len = i;

	    d2 -> domain_name = (char *) malloc (d2 -> domain_name_len * sizeof (char));
	    for (i = 0; i < d2 -> domain_name_len; i++)
	    {
		read (fd, &ch, sizeof (char));
		d2 -> domain_name[i] = ch;
	    }
	}
	else if (temp1 == 3)
	{
	    d3 = (struct DUID3 *) malloc (sizeof (struct DUID3));
	    d -> duid_type = d3;
	    
	    read (fd, &temp1, sizeof (u_int16_t));
	    d3 -> u_haddr_type.haddr_type = temp1;

	    read (fd, &temp1, sizeof (u_int16_t));
	    d3 -> haddr_len = temp1;
	    d3 -> link_layer_address = (u_int8_t *) malloc (d3 -> haddr_len * sizeof (u_int8_t));
	    
	    for (i = 0; i < d3 -> haddr_len; i++)
	    {
		read (fd, &temp2, sizeof (u_int8_t));
		d3 -> link_layer_address[i] = temp2;
	    }
	}
	
	read (fd, &q -> start_time, sizeof (time_t));
    }
    
    return leases_head;
}

void write_leases_file (char *file_name, struct lease_details *lease_struct)
{
    struct lease_details *q = lease_struct;
    struct DUID *d;
    struct DUID1 *d1;
    struct DUID2 *d2;
    struct DUID3 *d3;
    int fd, i;
    char ch;
    
    if ((fd = open (file_name, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
    {
#if DEBUG == 2
	printf ("Could not open leases file %s for writing\n", file_name);
#endif
    }
    
    while (q)
    {
	write (fd, &q -> u_iaid.iaid, sizeof (u_int32_t));
	write (fd, &q -> u_trans_id.trans_id, sizeof (u_int32_t));
	write (fd, &q -> u_t1.t1, sizeof (u_int32_t));
	write (fd, &q -> u_t2.t2, sizeof (u_int32_t));
	write (fd, &q -> u_pref_lifetime.pref_lifetime, sizeof (u_int32_t));
	write (fd, &q -> u_valid_lifetime.valid_lifetime, sizeof (u_int32_t));
	write (fd, &q -> max_renew_count, sizeof (int));
	write (fd, &q -> renew_count, sizeof (int));
	write (fd, &q -> assigned_ipv6_addr, sizeof (struct in6_addr));
	write (fd, &q -> prefix_length, sizeof (u_int8_t));
	
	d = q -> client_duid;
	write (fd, &d -> u_duid_type.duid_type, sizeof (u_int16_t));
	
	switch (d -> u_duid_type.duid_type)
	{
	    case 1 :
		d1 = (struct DUID1 *) d -> duid_type;
		write (fd, &d1 -> u_haddr_type.haddr_type, sizeof (u_int16_t));
		write (fd, &d1 -> u_time.time, sizeof (u_int32_t));
		write (fd, &d1 -> haddr_len, sizeof (u_int16_t));
		for (i = 0; i < d1 -> haddr_len; i++)
		    write (fd, &d1 -> link_layer_address[i], sizeof (u_int8_t));
		break;
		
	    case 2 :
		d2 = (struct DUID2 *) d -> duid_type;
    		write (fd, &d2 -> u_identifier_length.identifier_length, sizeof (u_int16_t));
		for (i = 0; i < d2 -> u_identifier_length.identifier_length; i++)
		    write (fd, &d2 -> identifier[i], sizeof (u_int8_t));
		write (fd, &d2 -> domain_name_len, sizeof (int));
		for (i = 0; i < d2 -> domain_name_len; i++)
		    write (fd, &d2 -> domain_name[i], sizeof (char));
		break;
		
	    case 3 :
		d3 = (struct DUID3 *) d -> duid_type;
		write (fd, &d3 -> u_haddr_type.haddr_type, sizeof (u_int16_t));
		write (fd, &d3 -> haddr_len, sizeof (u_int16_t));
		for (i = 0; i < d3 -> haddr_len; i++)
		    write (fd, &d3 -> link_layer_address[i], sizeof (u_int8_t));
		break;
	}
	
	write (fd, &q -> start_time, sizeof (time_t));
	q = q -> next;
    }
}

void check_leases_for_expiry (void)
{
    extern struct lease_details *lease;
    time_t current_time;
    struct lease_details *p;
    
    time (&current_time);
    p = lease;
    
    while (p)
    {
	if ((p -> start_time + p -> u_valid_lifetime.valid_lifetime) < current_time)
	{
	    // Lease has expired
	    p = delete_lease_node (p, lease);
	}
	else
	    p = p -> next;
    }
}

void get_next_address (struct in6_addr * addr)
{
    int i;

    for (i = 15; i >= 0; i--)
    {
	if (addr->s6_addr[i] == (uint8_t)(-1))
	    addr->s6_addr[i] = 0;
	else
	{
	    addr->s6_addr[i]++;
	    break ;
	}
    }
}

int get_pref_address (struct addr_details * available_addr, struct lease_details * partial_lease, 
			struct lease_details * lease, struct config_head * head,
			struct sockaddr_in6 client_address, struct OPTIONS *opt_ptr)
{
    struct IA_ADDRESS *iaaddr_ptr;
    struct config_user *user_node = 0;
    struct config_range *range_node = 0;
    struct in6_addr check_addr;
    int p_flag = 0, l_flag = 0;
    char zero_iaaddr[16]={0};

    //iaaddr is not MUST payload, if do not have, set it to zero      //bruce hsu
    if(NULL==opt_ptr){
       iaaddr_ptr=zero_iaaddr; 
    }else{
       iaaddr_ptr = (struct IA_ADDRESS *) opt_ptr -> opt_data;
    }
    
    // No address available in configuration file itself!
    if (head -> first_node_type == NO_NODE)
	return 0;
	
    // Check for user nodes first
    if (head -> first_node_type == USER_NODE)
	user_node = head -> next;
    else if (head -> first_node_type == RANGE_NODE)
	range_node = head -> next;
	
    while (user_node)
    {
	// Link local address of client is equal to that listed in the conf file
	// Ignore preferred ipv6 address in this case
	if (!memcmp (user_node -> link_local_addr, &client_address.sin6_addr, sizeof (struct in6_addr)))
	{
	    p_flag = l_flag = 0;
	    p_flag = check_if_addr_in_lease_structure (*user_node -> ipv6_addr, partial_lease);
	    l_flag = check_if_addr_in_lease_structure (*user_node -> ipv6_addr, lease);
	
	    if (!(p_flag || l_flag))
	    {
		// Address available
		memcpy (&available_addr -> ipv6_addr, user_node -> ipv6_addr, sizeof (struct in6_addr));
		available_addr -> t1 = head -> t1;
		available_addr -> t2 = head -> t2;
		available_addr -> pref_life = user_node -> pref_life;
		available_addr -> valid_life = user_node -> valid_life;
		available_addr -> max_renew_count = user_node -> max_renew_count;
		available_addr -> pref_val = user_node -> pref_val;
		return 1;
	    }
	}
	
	if (user_node -> next_node_type == USER_NODE)
	    user_node = user_node -> next;
	else if (user_node -> next_node_type == RANGE_NODE)
	{
	    range_node = user_node -> next;
	    user_node = 0;
	}
	else if (user_node -> next_node_type == NO_NODE)
	    user_node = 0;
    }
    
    // Check in the range nodes
    while (range_node)
    {
	memcpy (&check_addr, range_node -> st_addr, sizeof (struct in6_addr));

	while (memcmp (&check_addr, range_node -> end_addr, sizeof (struct in6_addr)))
	{
	   if (!memcmp (check_addr.s6_addr, iaaddr_ptr -> addr, 16))
	   {
	    p_flag = l_flag = 0;
	    p_flag = check_if_addr_in_lease_structure (check_addr, partial_lease);
	    l_flag = check_if_addr_in_lease_structure (check_addr, lease);
	
	    if (!(p_flag || l_flag))
	    {
		// Address available
	      memcpy (&available_addr -> ipv6_addr, &check_addr, sizeof (struct in6_addr));
		available_addr -> t1 = head -> t1;
	      available_addr -> t2 = head -> t2;
		available_addr -> pref_life = range_node -> pref_life;
    		available_addr -> valid_life = range_node -> valid_life;
	      available_addr -> max_renew_count = range_node -> max_renew_count;
		available_addr -> pref_val = range_node -> pref_val;
	      return 1;
	    }
	   }
	    get_next_address (&check_addr);
	}

	if (range_node -> next_node_type == RANGE_NODE)
	    range_node = range_node -> next;
	else if (range_node -> next_node_type == NO_NODE)
	    range_node = 0;
    }
    
    return 0;
}

int get_available_address (struct addr_details * available_addr, struct lease_details * partial_lease, 
			struct lease_details * lease, struct config_head * head,
			struct sockaddr_in6 client_address)
{
    struct config_user *user_node = 0;
    struct config_range *range_node = 0;
    struct in6_addr check_addr;
    int p_flag = 0, l_flag = 0;
    
    // No address available in configuration file itself!
    if (head -> first_node_type == NO_NODE)
	return 0;
	
    // Check for user nodes first
    if (head -> first_node_type == USER_NODE)
	user_node = head -> next;
    else if (head -> first_node_type == RANGE_NODE)
	range_node = head -> next;
   
    // Traverse through all user nodes
    while (user_node)
    {
	if (user_node -> next_node_type == USER_NODE)
	    user_node = user_node -> next;
	else if (user_node -> next_node_type == RANGE_NODE)
	{
	    range_node = user_node -> next;
	    user_node = 0;
	}
	else if (user_node -> next_node_type == NO_NODE)
	    user_node = 0;
    }
    
    // Check in the range nodes
    while (range_node)
    {
	memcpy (&check_addr, range_node -> st_addr, sizeof (struct in6_addr));

	while (memcmp (&check_addr, range_node -> end_addr, sizeof (struct in6_addr)))
	{
	    p_flag = l_flag = 0;
	    p_flag = check_if_addr_in_lease_structure (check_addr, partial_lease);
	    l_flag = check_if_addr_in_lease_structure (check_addr, lease);
	
	    if (!(p_flag || l_flag))
	    {
		// Address available
	      memcpy (&available_addr -> ipv6_addr, &check_addr, sizeof (struct in6_addr));
		available_addr -> t1 = head -> t1;
	      available_addr -> t2 = head -> t2;
		available_addr -> pref_life = range_node -> pref_life;
    		available_addr -> valid_life = range_node -> valid_life;
	      available_addr -> max_renew_count = range_node -> max_renew_count;
		available_addr -> pref_val = range_node -> pref_val;
	      return 1;
	    }
	    
	    get_next_address (&check_addr);
	}

	if (range_node -> next_node_type == RANGE_NODE)
	    range_node = range_node -> next;
	else if (range_node -> next_node_type == NO_NODE)
	    range_node = 0;
    }
    
    return 0;
}

int check_if_addr_in_lease_structure (struct in6_addr addr, struct lease_details * lease_struct)
{
    struct lease_details *p = lease_struct;

    // No nodes present
    if (!lease_struct)
	return 0;
	
    while (p)
    {
	if (!memcmp (&p -> assigned_ipv6_addr, &addr, sizeof (struct in6_addr)))
	    return 1;
	else
	    p = p -> next;
    }
    
    return 0;
}

void add_node_to_partial_lease_structure (struct DHCP_MESSAGE *advertise, 
					struct addr_details *available_addr)
{
    extern struct lease_details *partial_lease;
    struct OPTIONS *options_ptr;
    struct lease_details *p, *q, *r;
    struct DUID *d;
    struct DUID1 *d1;
    struct DUID2 *d2;
    struct DUID3 *d3;
    struct IA *ia;
    struct IA_ADDRESS *iaaddr;
        
    p = (struct lease_details *) malloc (sizeof (struct lease_details));
    p -> next = 0;
    if (!partial_lease)
	partial_lease = p;
    else
    {
	r = partial_lease;
	while (r)
	{
	    q = r;
	    r = r -> next;
	}
	q -> next = p;
    }

    // Copy transaction id from the advertise message
    p -> u_trans_id.trans_id = advertise -> u_trans_id.trans_id;
    
    // Copy details from addr_details structure
    p -> u_t1.t1 = available_addr -> t1;
    p -> u_t2.t2 = available_addr -> t2;
    p -> u_pref_lifetime.pref_lifetime = available_addr -> pref_life;
    p -> u_valid_lifetime.valid_lifetime = available_addr -> valid_life;
    p -> max_renew_count = available_addr -> max_renew_count;
    p -> renew_count = 1;	// Leased for the first time
    memcpy (&p -> assigned_ipv6_addr, &available_addr -> ipv6_addr, sizeof (struct in6_addr));
    
    // Set start time for partial leases (5 minutes expiry)
    time (&p -> start_time);
    
    // Copy from IA option of advertise message
    options_ptr = get_options_ptr (advertise, OPTION_IA);
    ia = (struct IA *) options_ptr -> opt_data;
    p -> u_iaid.iaid = ia -> u_iaid.iaid;
    
    // Copy from IA address option of advertise message
    options_ptr = get_options_ptr (advertise, OPTION_IAADDR);
    iaaddr = (struct IA_ADDRESS *) options_ptr -> opt_data;
    p -> prefix_length = iaaddr -> prefix_length;
    
    // Copy the client's duid from the option
    options_ptr = get_options_ptr (advertise, OPTION_CLIENTID);
    d = (struct DUID *) options_ptr -> opt_data;
    p -> client_duid = (struct DUID *) malloc (sizeof (struct DUID));
    p -> client_duid -> u_duid_type.duid_type = d -> u_duid_type.duid_type;
    p -> client_duid -> opt = 0;
    
    switch (d -> u_duid_type.duid_type)
    {
	case 1 :
	    p -> client_duid -> duid_type = (struct DUID1 *) malloc (sizeof (struct DUID1));
	    d1 = (struct DUID1 *) d -> duid_type;
	    copy_duid1 (d1, (struct DUID1 *) p -> client_duid -> duid_type);
	    break;

	case 2 :
	    p -> client_duid -> duid_type = (struct DUID2 *) malloc (sizeof (struct DUID2));
	    d2 = (struct DUID2 *) d -> duid_type;
	    copy_duid2 (d2, (struct DUID2 *) p -> client_duid -> duid_type);
	    break;

	case 3 :
	    p -> client_duid -> duid_type = (struct DUID3 *) malloc (sizeof (struct DUID3));
	    d3 = (struct DUID3 *) d -> duid_type;
	    copy_duid3 (d3, (struct DUID3 *) p -> client_duid -> duid_type);
	    break;
    }
}

int check_for_match (struct DHCP_MESSAGE * dhcp_msg_ptr, struct lease_details *lease_struct)
{
    struct lease_details * p_lease = lease_struct;
    struct OPTIONS * options_ptr;
    struct IA * ia_ptr;
    struct IA_ADDRESS * iaaddr_ptr;

    while (p_lease)
    {
	options_ptr = get_options_ptr (dhcp_msg_ptr, OPTION_IAADDR);
      iaaddr_ptr = (struct IA_ADDRESS *) options_ptr -> opt_data;
	if (memcmp (iaaddr_ptr->addr, p_lease -> assigned_ipv6_addr.s6_addr, 16))
	{
	    p_lease = p_lease -> next;
	    continue;
	}
	if (iaaddr_ptr -> prefix_length != p_lease -> prefix_length)
	{
	    p_lease = p_lease->next;
	    continue;
	}
	if (iaaddr_ptr -> u_pref_lifetime.pref_lifetime != p_lease -> u_pref_lifetime.pref_lifetime)
	{
	    p_lease = p_lease -> next;
	    continue;
	}
	if (iaaddr_ptr -> u_valid_lifetime.valid_lifetime != p_lease -> u_valid_lifetime.valid_lifetime)
	{
	    p_lease = p_lease -> next;
	    continue;
	}
	
	options_ptr = get_options_ptr (dhcp_msg_ptr, OPTION_IA);
	if (!options_ptr)
	{
	    p_lease = p_lease -> next;
	    continue;
	}
	ia_ptr = (struct IA *) options_ptr -> opt_data;
	
	if (ia_ptr -> u_iaid.iaid != p_lease -> u_iaid.iaid)
	{
	    p_lease = p_lease -> next;
	    continue;
	}
	if (ia_ptr -> u_t1.t1 != p_lease -> u_t1.t1)
	{
	    p_lease = p_lease -> next;
	    continue;
	}
	if (ia_ptr -> u_t2.t2 == p_lease -> u_t2.t2)
	    return 1;
	
	p_lease = p_lease -> next;
    }
    return 0;
}

int check_for_duid_match (struct DHCP_MESSAGE *dhcp_message, struct lease_details *lease_struct)
{
   struct lease_details * p_lease;
   struct OPTIONS * options_ptr;
   struct DUID *d;
   struct DUID1 *d1;
   struct DUID2 *d2;
   struct DUID3 *d3;
   int client_duid_type;
   
   p_lease = lease_struct;
   options_ptr = get_options_ptr (dhcp_message, OPTION_CLIENTID);
   d = (struct DUID *) options_ptr -> opt_data;
   
   while (p_lease)
   {
	client_duid_type = p_lease -> client_duid -> u_duid_type.duid_type;
	
	switch (client_duid_type)
	{
	   case 1 :
		d1 = (struct DUID1 *) d -> duid_type;
		if (client_duid_type == 1)
		   if (check_duid1 (d1, (struct DUID1 *) p_lease -> client_duid -> duid_type))
			return 1;
		break;

	   case 2 :
		d2 = (struct DUID2 *) d -> duid_type;
		if (client_duid_type == 2)
		   if (check_duid2 (d2, (struct DUID2 *) p_lease -> client_duid -> duid_type))
			return 1;
		break;
		
	   case 3 :
		d3 = (struct DUID3 *) d -> duid_type;
		if (client_duid_type == 3)
		   if (check_duid3 (d3, (struct DUID3 *) p_lease -> client_duid -> duid_type))
			return 1;
		break;
	}
	
	p_lease = p_lease -> next;
   }
}

int move_node_from_partial_to_lease (struct DHCP_MESSAGE *request_message)
{
    extern struct lease_details *partial_lease, *lease;
    struct OPTIONS *opt_ptr;
    struct DUID *d;
    struct DUID1 *d1;
    struct DUID2 *d2;
    struct DUID3 *d3;
    struct IA *ia;
    struct IA_ADDRESS *iaaddr;
    struct lease_details *p = partial_lease, *q, *r;
    
    while (p)
    {
	opt_ptr = get_options_ptr (request_message, OPTION_CLIENTID);
	d = (struct DUID *) opt_ptr -> opt_data;
	
	switch (d -> u_duid_type.duid_type)
	{
	    case 1 :
		if (p -> client_duid -> u_duid_type.duid_type != 1)
		{
		    p = p -> next;
		    continue;
		}
		
		d1 = (struct DUID1 *) d -> duid_type;
		if (!check_duid1 (d1, (struct DUID1 *) p -> client_duid -> duid_type))
		{
		    p = p -> next;
		    continue;
		}
		break;
		
	    case 2 :
		if (p -> client_duid -> u_duid_type.duid_type != 2)
		{
		    p = p -> next;
		    continue;
		}
		
		d2 = (struct DUID2 *) d -> duid_type;
		if (!check_duid2 (d2, (struct DUID2 *) p -> client_duid -> duid_type))
		{
		    p = p -> next;
		    continue;
		}
		break;

	    case 3 :
		if (p -> client_duid -> u_duid_type.duid_type != 3)
		{
		    p = p -> next;
		    continue;
		}
		
		d3 = (struct DUID3 *) d -> duid_type;
		if (!check_duid3 (d3, (struct DUID3 *) p -> client_duid -> duid_type))
		{
		    p = p -> next;
		    continue;
		}
		break;
	}
		
	opt_ptr = get_options_ptr (request_message, OPTION_IA);
	ia = (struct IA *) opt_ptr -> opt_data;
	
	if (p -> u_iaid.iaid == ia -> u_iaid.iaid)
	    if (p -> u_t1.t1 == ia -> u_t1.t1)
		if (p -> u_t2.t2 == ia -> u_t2.t2)
		{
		    opt_ptr = get_options_ptr (request_message, OPTION_IAADDR);
		    iaaddr = (struct IA_ADDRESS *) opt_ptr -> opt_data;
		    
		    if (p -> u_pref_lifetime.pref_lifetime == iaaddr -> u_pref_lifetime.pref_lifetime)
			if (p -> u_valid_lifetime.valid_lifetime == iaaddr -> u_valid_lifetime.valid_lifetime)
			    if (p -> prefix_length == iaaddr -> prefix_length)
				if (!memcmp (&p -> assigned_ipv6_addr.s6_addr, iaaddr -> addr, 16))
				{
				    if (p != partial_lease)
				    {
					r = partial_lease;
					while (r != p)
					{
					    q = r;
					    r = r -> next;
					}
				    	q -> next = p -> next;
				    }
				    else
					partial_lease = partial_lease -> next;
					
				    if (lease)
				    {
					r = lease;
					while (r)
					{
					    q = r;
					    r = r -> next;
					}
					q -> next = p;
				    }
				    else
					lease = p;
				    
				    p -> next = 0;
				    // Copy trans id from request message
				    p -> u_trans_id.trans_id = request_message -> u_trans_id.trans_id;
				    p -> renew_count = 1;
				    // Set start time
				    time (&p -> start_time);
				    return 1;
				}
		}
	
	p = p -> next;
    }
    
    return 0;
}

int move_node_from_lease_to_partial (struct DHCP_MESSAGE *message)
{
    extern struct lease_details *partial_lease, *lease;
    struct OPTIONS *opt_ptr;
    struct DUID *d;
    struct DUID1 *d1;
    struct DUID2 *d2;
    struct DUID3 *d3;
    struct IA *ia;
    struct IA_ADDRESS *iaaddr;
    struct lease_details *p = lease, *q, *r;
    
    while (p)
    {
	opt_ptr = get_options_ptr (message, OPTION_CLIENTID);
	d = (struct DUID *) opt_ptr -> opt_data;
	
	switch (d -> u_duid_type.duid_type)
	{
	    case 1 :
		if (p -> client_duid -> u_duid_type.duid_type != 1)
		{
		    p = p -> next;
		    continue;
		}
		
		d1 = (struct DUID1 *) d -> duid_type;
		if (!check_duid1 (d1, (struct DUID1 *) p -> client_duid -> duid_type))
		{
		    p = p -> next;
		    continue;
		}
		break;
		
	    case 2 :
		if (p -> client_duid -> u_duid_type.duid_type != 2)
		{
		    p = p -> next;
		    continue;
		}
		
		d2 = (struct DUID2 *) d -> duid_type;
		if (!check_duid2 (d2, (struct DUID2 *) p -> client_duid -> duid_type))
		{
		    p = p -> next;
		    continue;
		}
		break;

	    case 3 :
		if (p -> client_duid -> u_duid_type.duid_type != 3)
		{
		    p = p -> next;
		    continue;
		}
		
		d3 = (struct DUID3 *) d -> duid_type;
		if (!check_duid3 (d3, (struct DUID3 *) p -> client_duid -> duid_type))
		{
		    p = p -> next;
		    continue;
		}
		break;
	}
		
	opt_ptr = get_options_ptr (message, OPTION_IA);
	ia = (struct IA *) opt_ptr -> opt_data;
	
	if (p -> u_iaid.iaid == ia -> u_iaid.iaid)
	    if (p -> u_t1.t1 == ia -> u_t1.t1)
		if (p -> u_t2.t2 == ia -> u_t2.t2)
		{
		    opt_ptr = get_options_ptr (message, OPTION_IAADDR);
		    iaaddr = (struct IA_ADDRESS *) opt_ptr -> opt_data;
		    
		    if (p -> u_pref_lifetime.pref_lifetime == iaaddr -> u_pref_lifetime.pref_lifetime)
			if (p -> u_valid_lifetime.valid_lifetime == iaaddr -> u_valid_lifetime.valid_lifetime)
			    if (p -> prefix_length == iaaddr -> prefix_length)
				if (!memcmp (&p -> assigned_ipv6_addr.s6_addr, iaaddr -> addr, 16))
				{
				    if (p != lease)
				    {
					r = lease;
					while (r != p)
					{
					    q = r;
					    r = r -> next;
					}
				    	q -> next = p -> next;
				    }
				    else
					lease = lease -> next;
					
				    if (partial_lease)
				    {
					r = partial_lease;
					while (r)
					{
					    q = r;
					    r = r -> next;
					}
					q -> next = p;
				    }
				    else
					partial_lease = p;
				    
				    p -> next = 0;
				    // Copy trans id from message
				    p -> u_trans_id.trans_id = message -> u_trans_id.trans_id;
				    
				    // Set start time
				    time (&p -> start_time);
				    return 1;
				}
		}
	
	p = p -> next;
    }
    
    return 0;
}

void remove_expired_nodes_from_partial_lease (void)
{
   extern struct lease_details *partial_lease;
   struct lease_details *p = partial_lease;
   time_t curr_time = time (&curr_time);
   
   while (p)
   {
	if ((p -> start_time + FIVE_MINUTES) < curr_time)
	    p = delete_lease_node (p, partial_lease); 
	else
	    p = p -> next;
   }
}

void del_node_from_lease (struct DHCP_MESSAGE *message, struct lease_details *lease_struct)
{
    struct OPTIONS *opt_ptr = get_options_ptr (message, OPTION_IAADDR);
    struct lease_details *p = lease_struct;
    struct in6_addr ipv6_addr;
    
    memcpy (&ipv6_addr.s6_addr, ((struct IA_ADDRESS *) opt_ptr -> opt_data) -> addr, 16);
    while (p)
    {
	if (!memcmp (&ipv6_addr, &p -> assigned_ipv6_addr, sizeof (struct in6_addr)))
	   p = delete_lease_node (p, lease_struct);
	else
	    p = p -> next;
    }
}

int check_renewal_policy (struct DHCP_MESSAGE *message, struct lease_details *lease_struct)
{
    struct lease_details *p = lease_struct;
    struct OPTIONS *opt_ptr = get_options_ptr (message, OPTION_IAADDR);
    struct IA_ADDRESS *iaaddr_ptr = (struct IA_ADDRESS *) opt_ptr -> opt_data;
        
    while (p)
    {
	if (!memcmp (p -> assigned_ipv6_addr.s6_addr, iaaddr_ptr -> addr, 16))
	{
	    if (p -> renew_count == p -> max_renew_count)
		return 1;
	    else
	    {
		p -> renew_count++;
		time (&p -> start_time);
		return 0;
	    }
	}
	
	p = p -> next;
    }
    
    return 1;
}
