/*
 * client for Dynamic Host Configuration Protocol IPv6
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * Copyright (c) International Business Machines Corp., 2002
 *
 * Author: Suresh Kodati <skodati@in.ibm.com>
 */


#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <linux/types.h>
#include <linux/sockios.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <math.h>
#include <unistd.h>
#include <net/if.h>
#include <time.h>
#include <errno.h>
#include <err.h>
#include <netdb.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>

#include "dhcp6.h"
#include "dhcp6common.h"

/* Value of 2^32 */
#define POW32 4294967296

/* Prefix lengths */
#define LINK_LOCAL_PLEN 10

/* Configuration files */
#define DHCP_CONF "dhcp6client.conf"

struct IAlist{
	struct dhc6_iaaddr_option IAaddr;
	struct IAlist *next;
};

struct  dhc6ext_head{
	uint16_t option_code;
	uint16_t option_len;
};

struct client_state {
	int STATE;
	time_t t1;
	time_t t2;
}CLIENT;

struct configured_addr{
	struct in6_addr address;
	uint8_t prefix_len;
	struct configured_addr *next;
};


void client_bound(void);
int  client_process_option(char  *);

#define SOLICITING 1
#define REQUESTING 2
#define BOUND 	3
#define RENEWING 4
#define REBINDING 5
#define RELEASING 6
#define CONFIRMING 7
#define DECLINING 8
#define INF_REQUESTING 9

int NUM_REQ_ADDR = 1;
int CREATE_CONF  = 0;
int DELET_STATUS = 1;
int PREF_VAL = 0;
int DEC_STATUS = 1;
int IAID = 100;
int DBUG = 1;

//temporary for RELEASE
struct in6_addr serveraddr;

struct configured_addr *Confaddr=NULL;
struct duid_type_1 duid_cur;
struct in6_addr ADDRESS;

int Insock;	/* for incoming traffic bound to 546 */
int Outsock; 	/* for outgoing traffic */
char *device=NULL;

char* version="DHCPv6 Client version 0.01 based on draft 23";


/* sendto send the message in buf to the address specified in the addres
 * specified in char to the port. It limits the hops to lim  
 */
 
int send_to(int sock, char *addr, char *port, int lim, char *buf, int message_len)
{
	struct addrinfo hints, *res;
	int error;
	
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = PF_INET6;
	hints.ai_socktype = SOCK_DGRAM;
	if((error = getaddrinfo(addr, port, &hints, &res) < 0)) {
		dPrint(DBUG,"Error in transmitting");
		exit(0);
	}

	if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &lim, sizeof(lim)) < 0) {
		dPrint(DBUG,"Error setting up socket");
		exit(0);
	}
 
	error = sendto(sock, buf, message_len, 0, res->ai_addr, res->ai_addrlen);
	freeaddrinfo(res);
	return (error != message_len) ? -1 : 0; 
}


/* Actual DAD mechanism has to go here
 * 	right now it returns 1 default
 * 	1 means no duplicate is found
 * 	-1 returned duplicate is foun
 */

int  client_verify_uniq(struct in6_addr address)
{
	return 1;
}


/* sets the client DUID option to type1, which is generated from
 *	genereate_duid
 */

void dhc6_set_client_id( struct dhc6_duid_opt *dhc6duid)
{

	dhc6duid->option_code = OPTION_CLIENTID;
	memcpy(&dhc6duid->duid, &duid_cur, sizeof(duid_cur));
	dhc6duid->option_len  = sizeof (struct dhc6_duid_opt)-4;
	
}


/* sets the ia option  the IAID is equal to the IAID value + if_nametoindex(device)
 * this ensures selection of IAID should be unique per interface as specified in 
 * draft
 */

void client_set_ia_option(struct dhc6_ia_option *dhc6Iaid )
{
	dhc6Iaid->option_code = OPTION_IA;
	dhc6Iaid->iaid = IAID + if_nametoindex(device);
	dhc6Iaid->option_len = sizeof(struct dhc6_ia_option)-4;
}


/* Copies the status code to DELET_STATUS, global variable */

void client_process_status_code_option(char *option, int num)
{
	struct dhc6_status_code_option statuscode;
	memcpy(&statuscode, option+num, sizeof(struct dhc6_status_code_option));
	DELET_STATUS = statuscode.status;
}


/* Processes the preference option (copies it to  prefopt */

void  client_process_pref_option(char *option, int num)
{
	struct dhc6_pref_option prefopt;
	memcpy(&prefopt, option+num, sizeof(struct dhc6_pref_option));
	PREF_VAL = prefopt.pref;
	dPrint(DBUG, "Preference = %d", prefopt.pref);
}


/* Procesing the ia option, copies the t1 and t2 values from the
 *	ia and uses for further purpose
 */	

void client_process_ia_option(char *option,  int num)
{
	struct dhc6_ia_option ia;

	memcpy(&ia, option+num, sizeof(struct dhc6_ia_option));

	CLIENT.t1 = ia.T1;
	CLIENT.t2 = ia.T2;
	dPrint(DBUG, "option t1 = %d', option t2 = %d, IAID=%d", (int)ia.T1, (int)ia.T2, ia.iaid);
}


/* Send a decline message, called when the address obtained is
 * not unique
 */

void client_dec_send(struct in6_addr servaddr, uint32_t trans_id)
{
	char buf[BUFSIZ];
	struct dhc6_msg *dh6msg;
	int len = 0;
	struct IAlist *IA, *IAref= NULL;
	struct dhc6_duid_opt duid;
	struct dhc6_ia_option iaopt;
	struct configured_addr *temp_conf=NULL;
	int numaddr;
 
	dh6msg = (struct dhc6_msg *)buf;
	memset(dh6msg, 0, sizeof(*dh6msg));
	dh6msg->msg_type = DHC6_DECLINE;
	dh6msg->trans_id = htons(trans_id);

/* Client must include the client id option 19.1.9*/

	dhc6_set_client_id(&duid);
	memcpy((dh6msg->dhc6_ext+len), &duid, sizeof(struct dhc6_duid_opt));
	len += sizeof(struct dhc6_duid_opt);

/* Includes the ia options for the addresses */
	client_set_ia_option(&iaopt);
	memcpy((dh6msg->dhc6_ext+len), &iaopt, sizeof(struct dhc6_ia_option)); 
	len += sizeof(struct dhc6_ia_option);


/* It should include all the failed addressesi */

	 temp_conf = Confaddr;
	if(temp_conf == NULL){
		dPrint(DBUG, "No address available to request");
		exit(0);
	}

	for ( numaddr = 0; numaddr < NUM_REQ_ADDR ; numaddr++){
		if(!IA) {
			IA = (struct IAlist *)malloc( sizeof(struct IAlist));
			if(IA == NULL){
				dPrint(DBUG,"Error: Allocating memory");
				exit(0);
			}
		}
		else{
			IA->next = (struct IAlist *)malloc( sizeof(struct IAlist));
			if(IA->next == NULL){
				dPrint(DBUG,"Error: Allocating memory");
				exit(0);
			}
			IA = IA->next;
		}

		IA->IAaddr.option_code = OPTION_IAADDR;
		IA->IAaddr.option_len = sizeof(struct dhc6_iaaddr_option)-4;

		IA->IAaddr.addr = temp_conf->address;
		IA->IAaddr.prefix_len =temp_conf->prefix_len;
		temp_conf = temp_conf->next;
		IA->next = NULL;

		if( IAref == NULL){
			IAref = IA;
		}
	}
	IA = IAref;
	free(Confaddr);
	Confaddr = NULL;


	while( IA != NULL){
		memcpy((dh6msg->dhc6_ext)+len, &IA->IAaddr, 
			sizeof(struct dhc6_iaaddr_option));
		len += sizeof(struct dhc6_iaaddr_option);
	       IA= IA->next;
	}

/* server unicast option is not yet available so sending it to DHC6_ALLAGENT_ADDR */
 
	if (send_to(Outsock, DHC6_ALLAGENT_ADDR, DHC6_AGENT_PORT, 1, buf, len+3) != 0) {
		dPrint(DBUG," Error in transmission");
		exit(0);
	}
	dPrint(DBUG, "decline sent");
	return;
}



void client_dec_bound(uint32_t trans_id)
{
	ssize_t message_len;
	char buf[BUFSIZ];
	struct sockaddr_storage from;
	socklen_t fromlen = sizeof(from);
	struct dhc6_msg *dh6msg;
	struct timeval;



	if( (message_len = recvfrom( Insock, buf, sizeof(buf), 0,  
			(struct sockaddr *)&from,&fromlen)) < 0){
		dPrint(DBUG,"\n Error receving packet");
		exit(0);
	}
	if (message_len < 3){
		dPrint(DBUG,"\n Error size less then struct");
		return;
	}
 
	dh6msg = (struct dhc6_msg *)buf;

	if(dh6msg->msg_type == DHC6_REPLY){ 
		dPrint(DBUG, "Reply received");
	}else{
		return;
	}

/* client discards the message if the transaction id doesn't match */
	if(dh6msg->trans_id != htons(trans_id)){
		dPrint(DBUG, "Error in transaction ID in client_ren_bound");
		return;
	}

	if(dh6msg->dhc6_ext == NULL ) {
		dPrint(DBUG, "Extension null here");
	}

/* sends for processing the option */

	client_process_option(dh6msg->dhc6_ext);

	dPrint(DBUG, "Returning from dec_bound");
	return;
}


/* Sends decline message to the the server provided in servaddr
 * This is initiated if the uniqueness of the address fails
 */

int client_dec(struct in6_addr servaddr)
{
	int noAttempts=0;
	int return_value;
	uint32_t trans_id;
	fd_set r;
	struct timeval RT, ORT;

	CLIENT.STATE = DECLINING;
 
	trans_id = (uint32_t)random();

	ORT.tv_sec = DEC_TIMEOUT;
	ORT.tv_usec = 0;

	client_dec_send(servaddr, trans_id);

	while(1){
		RT.tv_sec = ORT.tv_sec*2 + random_min()*(float)ORT.tv_sec;
		RT.tv_usec= ORT.tv_usec*2 + random_min()*(float)ORT.tv_usec;
		fix_time_val(&RT);

		if(RT.tv_sec >= DEC_MAX_RT ){
			RT.tv_sec = DEC_MAX_RT + random_min()*(float)DEC_MAX_RT;
			RT.tv_usec = 0;
		}

		ORT = RT;

		FD_ZERO(&r);
		FD_SET(Insock, &r);
		return_value= select(Insock+1, &r, NULL, NULL, &RT);
		switch(return_value){
		case -1:
			dPrint(DBUG,"Error selecting: in renew");
			exit(0);
		case 0:
			if(noAttempts > REL_MAX_RC){
				 dPrint(DBUG,"Max attempts reached");
/* Actually this should not exit, this has to go ahead with what ever it wants 
 * If it doesnt want to use the address(es)
 */
				exit(0);
			}
			++noAttempts;
			client_dec_send(servaddr, trans_id);
			break;
		default:
			if(FD_ISSET(Insock, &r)){
				client_dec_bound(trans_id);
				return 1;
			}
			else
			{
/* Then something is wrong */
				dPrint(DBUG, "SEE HERE");
				exit(0);
			}
			break;
 
		}
 
	}
}


/* Processes the ia addr option, depending on the state of the client, here
 * it processes only the solicit and request, in case of solicit, it copies
 * the addresses received from the advertisement, and returns. In case of
 * REQUESTING phase it configures the address based on the status of ia  
 */

int client_process_iaaddr_option(char *option, int num)
{
	struct dhc6_iaaddr_option p;
	char addr_char[50];
	char *envp[5];
	char *argv[2];
	int pid = 0;
	char pfix[4];
	struct configured_addr *temp_conf;

	memcpy(&p, option+num, sizeof( struct dhc6_iaaddr_option));
	dPrint(DBUG, "Processing IA Addr option");

	if( (CLIENT.STATE != REQUESTING) && (CLIENT.STATE != SOLICITING)){
		dPrint(DBUG, "returning\n");
		return 1;
	}

	/* Fills up the addresses in Confaddr */

	if(Confaddr == NULL){
		Confaddr = (struct configured_addr *)malloc(sizeof
				(struct configured_addr));
		if(Confaddr == NULL){
			dPrint(DBUG,"Error: Allocating memory");
			exit(0);
		}

		memcpy(&(Confaddr->address), &p.addr, sizeof(p.addr));
		Confaddr->prefix_len = p.prefix_len;
		Confaddr->next = NULL;
	}
	else{
		dPrint(DBUG, "Are you configuring more than one address Came upto here\n");
		temp_conf = Confaddr;
		while(Confaddr->next != NULL)
			Confaddr = Confaddr->next;
		Confaddr->next = (struct configured_addr *)malloc(
					sizeof(struct configured_addr));
		if(Confaddr->next == NULL){
			dPrint(DBUG,"Error: Allocating memory");
			exit(0);
		}
		Confaddr = Confaddr->next;
		memcpy(&(Confaddr->address), &p.addr, sizeof(p.addr));
		Confaddr->prefix_len = p.prefix_len;
		Confaddr->next = NULL;
		Confaddr = temp_conf;
	}

/* If it is SOLICIT phase just copies the address and returns */
	if(CLIENT.STATE == SOLICITING)
		return 1;

	memcpy(&ADDRESS, &p.addr, sizeof(ADDRESS));

/* Goes ahead only if the status us SUCCESS */	

	if(p.status_ia == DHC6_SUCCESS){
		dPrint(DBUG, "Address Assignement succeeded");
	}else{
	       dPrint(DBUG, "Error: Address Assignment falied");
		return -1;
	}

/* Checks for the uniqueness if the address */
		if( client_verify_uniq(ADDRESS) == -1){
			client_dec(serveraddr );
			CLIENT.STATE = REQUESTING;			
			return -1;
		}

	inet_ntop(AF_INET6, &p.addr, addr_char, 50);
	printf("\n Address is %s\n", addr_char);

	argv[0] = (char *)malloc(9);
	if(argv[0] == NULL){
		dPrint(DBUG,"Error: Allocating memory");
		exit(0);
	}

	argv[0] = "./script";
	argv[1] = (char *)0;
	envp[0] = (char * )malloc(15);
	if(envp[0] == NULL){
		dPrint(DBUG,"Error: Allocating memory");
		exit(0);
	}
	strcpy(envp[0], "interface=");
	strcat(envp[0], device);

	envp[1] = (char * )malloc(11);
	if(envp[1] == NULL){
		dPrint(DBUG,"Error: Allocating memory");
		exit(0);
	}

	envp[1]="reason=SET";
	envp[2]= (char *)malloc(strlen(addr_char)+9);

	if(envp[2] == NULL){
		dPrint(DBUG,"Error: Allocating memory");
		exit(0);
	}
	strcpy(envp[2],"address=");
	strcat(envp[2], addr_char);
	
	envp[3]= (char *)malloc(10);
	if(envp[3] == NULL){
		dPrint(DBUG,"Error: Allocating memory");
		exit(0);
	}
	strcpy(envp[3],"prefix=");
	memset(pfix, '\0', 4);
	sprintf(pfix, "%d", p.prefix_len);
	
	strcat(envp[3], pfix);

	envp[4] = (char *)malloc(2);
	envp[4] = (char *)0;
	
	pid = fork();
	if(pid == 0){
		execve("script", argv, envp);
		perror("Error is");
		system("/sbin/ifconfig >>log");
		exit(0);
	}
return 1;
}


/* This deletes the configured adresses available from the Confaddr
 * structure, based on the status of the IA
 */

void client_delete_address()
{
	
	char addr_char[50];
	char *envp[5];
	char *argv[2];
	int pid = 0;
	char pfix[4];
	int prefix_len;
	struct configured_addr *temp_conf;

	temp_conf = Confaddr;

	while(temp_conf != NULL){
		
		inet_ntop(AF_INET6, &(temp_conf->address), addr_char, 50);
		prefix_len = temp_conf->prefix_len;
		temp_conf = temp_conf->next;
		dPrint(DBUG, "\n Address is %s\n", addr_char);

		argv[0] = (char *)malloc(sizeof(15));
		if(argv[0] == NULL){
			dPrint(DBUG,"Error: Allocating memory");
			exit(0);
		}
		argv[0] = "./script";
		argv[1] = (char *)0;
		envp[0] = (char * )malloc(15);
		if(envp[0] == NULL){
			dPrint(DBUG,"Error: Allocating memory");
			exit(0);
		}

		strcpy(envp[0],"interface=");
		envp[0]= strcat(envp[0], device);
		envp[1] = (char * )malloc(15);
		if(envp[1] == NULL){
			dPrint(DBUG,"Error: Allocating memory");
			exit(0);
		}
		strcpy(envp[1], "reason=RELEASE");
		envp[2]= (char *)malloc(strlen(addr_char)+9);
		if(envp[2] == NULL){
			dPrint(DBUG,"Error: Allocating memory");
			exit(0);
		}
		strcpy(envp[2],"address=");
		strcat(envp[2], addr_char);
		envp[3]= (char *)malloc(10);
		if(envp[3] == NULL){
			dPrint(DBUG,"Error: Allocating memory");
			exit(0);
		}
		strcpy(envp[3],"prefix=");
		
		memset(pfix, '\0', 4);
		sprintf(pfix, "%d", prefix_len);
		strcat(envp[3], pfix);
	
		envp[4] = (char *)malloc(2);
		envp[4] = (char *)0;

		pid = fork();
		if(pid == 0){
			execve("script", argv, envp);
			perror("error is");
			system("/sbin/ifconfig >>log");
			exit(0);
		}
	}



}


/* This proceses the options available from the dhc6_msg->dhc6_ext
 * Initially it reads the structure and calls respective processing
 * function.
 */

int client_process_option(char *option)
{
	struct dhc6ext_head extension;
	int len=0;
	int num;

	while(1){
		num = len;
		memcpy(&extension, option+len, sizeof(extension));
		if(extension.option_len==0){
			return 1;
		}

		len += extension.option_len+4;
		
		switch(extension.option_code ){
			case  OPTION_SERVERID:
			dPrint(DBUG, "\nServer DUID OPTION RECEIVED");
			break;
			
			case   OPTION_IA:
			dPrint(DBUG, "\nIA Option received");
			client_process_ia_option(option, num);
			break;
			
			case OPTION_PREF:
			dPrint(DBUG, "\nPREF Option received");
			client_process_pref_option(option, num);
			break;
	
			case OPTION_STATUSCODE:
			dPrint(DBUG, "\nSTATUS CODE option received");
			client_process_status_code_option(option, num);

/* If the client is declinig nothing to do, return 1 */

			if(CLIENT.STATE == DECLINING){
				return 1;
			}
			break;

			case OPTION_IAADDR:
			dPrint(DBUG, "\nIAID option recived ");

/* If the client is declinig nothing to do return 1 */

			if(CLIENT.STATE == DECLINING){
					return 1;
			}

			if(CLIENT.STATE != RELEASING){
				if((client_process_iaaddr_option(option, num)) == -1){
					dPrint(DBUG, "Error error processing IADr option");
					return -1;
				}
			}
			break;
			
			default:
			dPrint(DBUG,"Cannot process the option no = %d", extension.option_code);
			return -1;
		}
	}
}
 
void client_infreq_bound(uint32_t trans_id)
{
	ssize_t message_len;
	char buf[BUFSIZ];
	struct sockaddr_storage from;
	socklen_t fromlen = sizeof(from);
	struct dhc6_msg *dh6msg;
	struct timeval;

	if( (message_len = recvfrom( Insock, buf, sizeof(buf), 0, 
			 (struct sockaddr *)&from,&fromlen)) < 0){
		dPrint(DBUG,"\n Error receving packet");
		exit(0);
	}
	if (message_len < 3){
		dPrint(DBUG,"\n Error size less then struct");
		return;
	}
 
	dh6msg = (struct dhc6_msg *)buf;
	if(dh6msg->msg_type == DHC6_REPLY){ 
		dPrint(DBUG, "Reply received");
		}
		else
		{
		return;
	}
	
	if(dh6msg->trans_id != htons(trans_id)){
		dPrint(DBUG,"\n ID's are diffSol ID=%d, AdvtID=%d", trans_id, dh6msg->trans_id);
		dPrint(DBUG, "Error in transaction ID in client_ren_bound");
		return;
	}


	if(dh6msg->dhc6_ext == NULL ) {
		dPrint(DBUG, "Extension null here");
	}
	client_process_option(dh6msg->dhc6_ext);
	return;
}

void client_infreq_send(struct in6_addr servaddr, uint32_t trans_id)
{
	char buf[BUFSIZ];
	struct dhc6_msg *dh6msg;
	int len = 0;
	struct dhc6_duid_opt duid;
 
	dh6msg = (struct dhc6_msg *)buf;
	memset(dh6msg, 0, sizeof(*dh6msg));
	dh6msg->msg_type = DHC6_INFORMATIONREQEST;
	dh6msg->trans_id = htons(trans_id);

	dhc6_set_client_id(&duid);
	memcpy((dh6msg->dhc6_ext+len), &duid, sizeof(struct dhc6_duid_opt));
	len += sizeof(struct dhc6_duid_opt);
 
	if (send_to(Outsock, DHC6_ALLAGENT_ADDR, DHC6_AGENT_PORT, 1, buf, len+3) != 0) {
		dPrint(DBUG," Error in transmission");
		exit(0);
	}
	dPrint(DBUG, "renew sent");
	return;
}
 
int client_infreq(struct in6_addr servaddr)
{
	int return_value;
	uint32_t trans_id;
	fd_set r;
	struct timeval RT, ORT, start_time, time_now;

	CLIENT.STATE = INF_REQUESTING;
 
	trans_id = (uint32_t)random();

	ORT.tv_sec = CNF_TIMEOUT;
	ORT.tv_usec = 0;

	gettimeofday(&start_time, NULL);

	client_infreq_send(servaddr, trans_id);

	while(1){
		RT.tv_sec = ORT.tv_sec*2 + random_min()*(float)ORT.tv_sec;
		RT.tv_usec= ORT.tv_usec*2 + random_min()*(float)ORT.tv_usec;
		fix_time_val(&RT);


		gettimeofday(&time_now, NULL);

		if((start_time.tv_sec - time_now.tv_sec) > CNF_MAX_RD){
			return -1;
		}

		if(RT.tv_sec >= CNF_MAX_RT ){
			RT.tv_sec = CNF_MAX_RT + random_min()*(float)CNF_MAX_RT;
			RT.tv_usec = 0;
		}

		ORT = RT;

		FD_ZERO(&r);
		FD_SET(Insock, &r);
		return_value= select(Insock+1, &r, NULL, NULL, &RT);
		switch(return_value){
		case -1:
			dPrint(DBUG,"Error selecting: in renew");
			exit(0);
		case 0:
			client_infreq_send(servaddr, trans_id);
			break;
		default:
			if(FD_ISSET(Insock, &r)){
				client_infreq_bound(trans_id);
				return 1;
			}
			else
			{
				dPrint(DBUG, "SEE HERE");
				exit(0);
			}
			break;
 
		}
 
	}
}

void client_conf_send(struct in6_addr servaddr, uint32_t trans_id)
{
	char buf[BUFSIZ];
	struct dhc6_msg *dh6msg;
	int len = 0;
 
	struct dhc6_duid_opt duid;

	dh6msg = (struct dhc6_msg *)buf;
	memset(dh6msg, 0, sizeof(*dh6msg));
	dh6msg->msg_type =  DHC6_CONFIRM;
	dh6msg->trans_id = htons(trans_id);

	dhc6_set_client_id(&duid);
	memcpy((dh6msg->dhc6_ext+len), &duid, sizeof(struct dhc6_duid_opt));
	len += sizeof(struct dhc6_duid_opt);
 
	if (send_to(Outsock, DHC6_ALLAGENT_ADDR, DHC6_AGENT_PORT, 1, buf, len+3) != 0) {
		dPrint(DBUG," Error in transmission");
		exit(0);
	}
	dPrint(DBUG, "renew sent");
	return;
}

void client_cnf_bound(uint32_t trans_id)
{
	ssize_t message_len;
	char buf[BUFSIZ];
	struct sockaddr_storage from;
	socklen_t fromlen = sizeof(from);
	struct dhc6_msg *dh6msg;
	struct timeval;



	if( (message_len = recvfrom( Insock, buf, sizeof(buf), 0,  
				(struct sockaddr *)&from,&fromlen)) < 0){
		dPrint(DBUG,"\n Error receving packet");
		exit(0);
	}
	if (message_len < 3){
		dPrint(DBUG,"\n Error size less then struct");
		return;
	}
 
	dh6msg = (struct dhc6_msg *)buf;
	if(dh6msg->msg_type == DHC6_REPLY){ 
		dPrint(DBUG, "Reply received");
		}
		else
		{
		return;
	}
	
	if(dh6msg->trans_id != htons(trans_id)){
		dPrint(DBUG, "Error in transaction ID in client_ren_bound");
		return;
	}

	if(dh6msg->dhc6_ext == NULL ) {
		dPrint(DBUG, "Extension null here");
	}
	client_process_option(dh6msg->dhc6_ext);
	return;
}
 
int client_conf(struct in6_addr servaddr)
{
	int return_value;
	uint32_t trans_id;
	fd_set r;
	struct timeval RT, ORT, start_time, time_now;

	CLIENT.STATE = CONFIRMING;
 
	trans_id = (uint32_t)random();

	ORT.tv_sec = CNF_TIMEOUT;
	ORT.tv_usec = 0;

	gettimeofday(&start_time, NULL);

	client_conf_send(servaddr, trans_id);

	while(1){
		RT.tv_sec = ORT.tv_sec*2 + random_min()*(float)ORT.tv_sec;
		RT.tv_usec= ORT.tv_usec*2 + random_min()*(float)ORT.tv_usec;
		fix_time_val(&RT);


		gettimeofday(&time_now, NULL);

		if((start_time.tv_sec - time_now.tv_sec) > CNF_MAX_RD){
			return -1;
		}

		if(RT.tv_sec >= CNF_MAX_RT ){
			RT.tv_sec = CNF_MAX_RT + random_min()*(float)CNF_MAX_RT;
			RT.tv_usec = 0;
		}

		ORT = RT;

		FD_ZERO(&r);
		FD_SET(Insock, &r);
		return_value= select(Insock+1, &r, NULL, NULL, &RT);
		switch(return_value){
		case -1:
			dPrint(DBUG,"Error selecting: in renew");
			exit(0);
		case 0:
			client_conf_send(servaddr, trans_id);
			break;
		default:
			if(FD_ISSET(Insock, &r)){
				client_cnf_bound(trans_id);
				return 1;
			}
			else
			{
				dPrint(DBUG, "SEE HERE");
				exit(0);
			}
			break;
 
		}
 
	}
}


/* Deletes the address based on the success of the status */

void client_rel_bound(uint32_t trans_id)
{
	ssize_t message_len;
	char buf[BUFSIZ];
	struct sockaddr_storage from;
	socklen_t fromlen = sizeof(from);
	struct dhc6_msg *dh6msg;



	if( (message_len = recvfrom( Insock, buf, sizeof(buf), 0, 
				(struct sockaddr *)&from,&fromlen)) < 0){
		dPrint(DBUG,"\n Error receving packet");
		exit(0);
	}
	if (message_len < 3){
		dPrint(DBUG,"\n Error size less then struct");
		return;
	}
 
	dh6msg = (struct dhc6_msg *)buf;
	if(dh6msg->msg_type == DHC6_REPLY){ 
		dPrint(DBUG, "Reply received");
		}
		else
		{
		return;
	}
	
	if(dh6msg->trans_id != htons(trans_id)){
		dPrint(DBUG,"\n ID's are diffSol ID=%d, AdvtID=%d", trans_id, dh6msg->trans_id);
		dPrint(DBUG, "Error in transaction ID in client_rel_bound");
		return;
	}


	if(dh6msg->dhc6_ext == NULL ) {
		dPrint(DBUG, "Extension null here");
	}
	client_process_option(dh6msg->dhc6_ext);

/* Since the assumption is to delete all the addresses client_delete_address
 * is called here based on the receipt of DHC6_SUCCESS			    
 */

	if(DELET_STATUS == DHC6_SUCCESS){
		printf("\n Delete status success");
		client_delete_address();
	}
		
	printf("\n Address released exiting\n");
	exit(0);
}
 

/* Sends the release message from here */

void client_rel_send(struct in6_addr servaddr, uint32_t trans_id)
{
	char buf[BUFSIZ];
	struct dhc6_msg *dh6msg;
	int len = 0;
	struct IAlist *IA, *IAref= NULL;
	struct dhc6_duid_opt duid;
	struct dhc6_ia_option iaopt;
	int numaddr;
	struct configured_addr *temp_conf;
	
	dh6msg = (struct dhc6_msg *)buf;
	memset(dh6msg, 0, sizeof(*dh6msg));
	dh6msg->msg_type =  DHC6_RELEASE;
	dh6msg->trans_id = htons(trans_id);

	dhc6_set_client_id(&duid);
	memcpy((dh6msg->dhc6_ext+len), &duid, sizeof(struct dhc6_duid_opt));
	len += sizeof(struct  dhc6_duid_opt);

	client_set_ia_option(&iaopt);
	memcpy((dh6msg->dhc6_ext+len), &iaopt, sizeof(struct dhc6_ia_option)); 
	len += sizeof(struct dhc6_ia_option);

	temp_conf =  Confaddr;
	
	for ( numaddr = 0; numaddr < NUM_REQ_ADDR ; numaddr++){
		if(!IA)
			IA = (struct IAlist *)malloc( sizeof(struct IAlist));
			if(IA == NULL){
				dPrint(DBUG,"Error: Allocating memory");
				exit(0);
			}
		else{
			IA->next = (struct IAlist *)malloc( sizeof(struct IAlist));
			if(IA->next == NULL){
				dPrint(DBUG,"Error: Allocating memory");
				exit(0);
			}
			IA = IA->next;
		}

		IA->IAaddr.option_code = OPTION_IAADDR;
		IA->IAaddr.option_len = sizeof(struct dhc6_iaaddr_option)-4;
		IA->IAaddr.addr = temp_conf->address;
		IA->IAaddr.prefix_len =temp_conf->prefix_len;
		IA->next = NULL;
		temp_conf = temp_conf->next;

		if( IAref == NULL){
			IAref = IA;
		}
	}
	IA = IAref;

	while( IA != NULL){
		memcpy((dh6msg->dhc6_ext)+len, &IA->IAaddr, 
				sizeof(struct dhc6_iaaddr_option));
		len += sizeof(struct dhc6_iaaddr_option);
		IA= IA->next;
	}

	if (send_to(Outsock, DHC6_ALLAGENT_ADDR, DHC6_AGENT_PORT, 1, buf, len+3) != 0) {
		dPrint(DBUG," Error in transmission");
		exit(0);
	}
	dPrint(DBUG, "\nrelease sent");
	return;
}


/* here the procedure of seding the calling release message and waiting
 * for reply							
 */

void client_rel(struct in6_addr servaddr)
{
	int noAttempts=0;
	int return_value;
	uint32_t trans_id;
	fd_set r;
	struct timeval RT, ORT;

	CLIENT.STATE = RELEASING;
 
	trans_id = (uint32_t)random();
	
	ORT.tv_sec  = 0;
	ORT.tv_usec = REL_TIMEOUT * 1000;
	
	client_rel_send(servaddr, trans_id);
 
	while(1){
		RT.tv_sec = ORT.tv_sec*2 + random_min()*(float)ORT.tv_sec;
		RT.tv_usec= ORT.tv_usec*2 + random_min()*(float)ORT.tv_usec;

		fix_time_val(&RT);
		ORT = RT;
		FD_ZERO(&r);
		FD_SET(Insock, &r);
		return_value= select(Insock+1, &r, NULL, NULL, &RT);
		switch(return_value){
		case -1:
				dPrint(DBUG,"Error selecting: in request");
				exit(0);
		case 0:
			if(noAttempts > REL_MAX_RC){
				 dPrint(DBUG,"Max attempts reached");
				exit(0);
			}
			++noAttempts;
			client_rel_send(servaddr, trans_id);
			break;
		default:
			if(FD_ISSET(Insock, &r)){
				client_rel_bound(trans_id);
				break;
			}
			else
			{
				dPrint(DBUG, "SEE HERE");
				exit(0);
			}
			break;
 
		}/* switch */
 
	}/*while*/
}


/* Signal handler calls the client release function this is temporary */

void signal_handler(int i)
{
	dPrint(DBUG, "Received signal for release address : Releasing address");
	client_rel(serveraddr);
	return;
}


/* here is the procedure for receiving the response for the renew
 * and processing the options accrodingly
 */

void client_ren_bound(uint32_t trans_id)
{
	ssize_t message_len;
	char buf[BUFSIZ];
	struct sockaddr_storage from;
	socklen_t fromlen = sizeof(from);
	struct dhc6_msg *dh6msg;
	struct timeval;



	if( (message_len = recvfrom( Insock, buf, sizeof(buf), 0, 
				(struct sockaddr *)&from,&fromlen)) < 0){
		dPrint(DBUG,"\n Error receving packet");
		exit(0);
	}
	if (message_len < 3){
		dPrint(DBUG,"\n Error size less then struct in ren bound");
		return;
	}
 
	dh6msg = (struct dhc6_msg *)buf;
	if(dh6msg->msg_type == DHC6_REPLY){ 
		dPrint(DBUG, "Reply received");
		}
		else
		{
		return;
	}
	
	if(dh6msg->trans_id != htons(trans_id)){
		dPrint(DBUG,"\n ID's are diffSol ID=%d, AdvtID=%d", trans_id, dh6msg->trans_id);
		dPrint(DBUG, "Error in transaction ID in client_ren_bound");
		return;
	}


	if(dh6msg->dhc6_ext == NULL ) {
		dPrint(DBUG, "Estension null here");
	}
	client_process_option(dh6msg->dhc6_ext);
	return;
}
 

/* Send the renew message */

void client_ren_send(struct in6_addr servaddr, uint32_t trans_id)
{
	char buf[BUFSIZ];
	struct dhc6_msg *dh6msg;
	int len = 0;
	struct IAlist *IA, *IAref= NULL;
	struct dhc6_duid_opt duid;
	struct dhc6_ia_option iaopt;
	int numaddr;
	struct configured_addr *temp_conf;
 
	dh6msg = (struct dhc6_msg *)buf;
	memset(dh6msg, 0, sizeof(*dh6msg));
	dh6msg->msg_type =  DHC6_RENEW;
	dh6msg->trans_id = htons(trans_id);
 
	dhc6_set_client_id(&duid);
	memcpy((dh6msg->dhc6_ext+len), &duid, sizeof(struct dhc6_duid_opt));
	len += sizeof(struct dhc6_duid_opt);

	client_set_ia_option(&iaopt);
	memcpy((dh6msg->dhc6_ext+len), &iaopt, sizeof(struct dhc6_ia_option));
	len += sizeof(struct dhc6_ia_option);

	temp_conf =  Confaddr;

	for ( numaddr = 0; numaddr < NUM_REQ_ADDR ; numaddr++){
		if(!IA)
			IA = (struct IAlist *)malloc( sizeof(struct IAlist));
			if(IA == NULL){
				dPrint(DBUG,"Error: Allocating memory");
				exit(0);
			}
		else{
			IA->next = (struct IAlist *)malloc( sizeof(struct IAlist));
			if(IA->next == NULL){
				dPrint(DBUG,"Error: Allocating memory");
				exit(0);
			}
			IA = IA->next;
		}

		IA->IAaddr.option_code = OPTION_IAADDR;
		IA->IAaddr.option_len = sizeof(struct dhc6_iaaddr_option)-4;
		IA->IAaddr.addr = temp_conf->address;
		IA->IAaddr.prefix_len =temp_conf->prefix_len;
		IA->next = NULL;
		temp_conf = temp_conf->next;

		if( IAref == NULL){
			IAref = IA;
		}
	}
	IA = IAref;

	while( IA != NULL){
		memcpy((dh6msg->dhc6_ext)+len, &IA->IAaddr, 
				sizeof(struct dhc6_iaaddr_option));
		len += sizeof(struct dhc6_iaaddr_option);
		IA= IA->next;
	}


	if (send_to(Outsock, DHC6_ALLAGENT_ADDR, DHC6_AGENT_PORT, 1, buf, len+3) != 0) {
		dPrint(DBUG," Error in transmission");
		exit(0);
	}
	dPrint(DBUG, "renew sent");
	return;
}
 

/* Procedure to call functions to send renew message and waiting
 * for replies and timeouts				
 */

int client_ren(struct in6_addr servaddr, time_t t2)
{
	int return_value;
	uint32_t trans_id;
	fd_set r;
	struct timeval RT, ORT;
	time_t present;

	CLIENT.STATE = RENEWING;
 
	trans_id = (uint32_t)random();

	ORT.tv_sec = REN_TIMEOUT;
	ORT.tv_usec = 0;

	client_ren_send(servaddr, trans_id);

	while(1){
		RT.tv_sec = ORT.tv_sec*2 + random_min()*(float)ORT.tv_sec;
		RT.tv_usec= ORT.tv_usec*2 + random_min()*(float)ORT.tv_usec;
		fix_time_val(&RT);

		if(RT.tv_sec >= REN_MAX_RT ){
			RT.tv_sec = REN_MAX_RT + random_min()*(float)REN_MAX_RT;
			RT.tv_usec = 0;
		}

		ORT = RT;

		FD_ZERO(&r);
		FD_SET(Insock, &r);
		return_value= select(Insock+1, &r, NULL, NULL, &RT);
		switch(return_value){
		case -1:
			dPrint(DBUG,"Error selecting: in renew");
		case 0:
			time(&present);
			if(present > t2 && t2!= 0){
				dPrint(DBUG, "\n Present time > t2");
				return -1;
			}

			client_ren_send(servaddr, trans_id);
			break;
		default:
			if(FD_ISSET(Insock, &r)){
					client_ren_bound(trans_id);
					return 1;
			}
			else
			{
				dPrint(DBUG, "SEE HERE");
				exit(0);
			}
			break;
 
		}/* switch */
 
	}/*while*/
}
			

void client_ren_Reconfig(void)
{
	ssize_t message_len;
	char buf[BUFSIZ];
	struct sockaddr_storage from;
	socklen_t fromlen = sizeof(from);
	struct dhc6_msg *dh6msg;
	struct timeval;



	if( (message_len = recvfrom( Insock, buf, sizeof(buf), 0, 
				(struct sockaddr *)&from,&fromlen)) < 0){
		dPrint(DBUG,"\n Error receving packet");
		exit(0);
	}
	if (message_len < 3){
		dPrint(DBUG,"\n Error size less then struct in ren bound");
		return;
	}
 
	dh6msg = (struct dhc6_msg *)buf;
	if(dh6msg->msg_type == DHC6_RECONFIGINIT){ 
		dPrint(DBUG, "Reconfigure init message");
		}
		else
		{
		dPrint(DBUG, "Unwanted message format %d", dh6msg->msg_type);
		return;
	}
/*
	
	if(dh6msg->dhc6_ext == NULL ) {
		printf("\n NULL HERE");
	}
	else{
	client_process_option(dh6msg->dhc6_ext);
	}
*/
	client_ren(serveraddr, 0);
	return;
}


/* Receiving the replies for rebind message and process options
 * accordingly						
 */

void client_reb_bound(uint32_t trans_id)
{
	ssize_t message_len;
	char buf[BUFSIZ];
	struct sockaddr_storage from;
	socklen_t fromlen = sizeof(from);
	struct dhc6_msg *dh6msg;



	if( (message_len = recvfrom( Insock, buf, sizeof(buf), 0, 
				(struct sockaddr *)&from,&fromlen)) < 0){
		dPrint(DBUG,"\n Error receving packet");
		exit(0);
	}
	if (message_len < 3){
		dPrint(DBUG,"\n Error size less then struct");
		return;
	}
 
	dh6msg = (struct dhc6_msg *)buf;
	if(dh6msg->msg_type == DHC6_REPLY){ 
		dPrint(DBUG, "Reply received");
		}
		else
		{
		return;
	}
	
	if(dh6msg->trans_id != htons(trans_id)){
		dPrint(DBUG,"\n ID's are diffSol ID=%d, AdvtID=%d", trans_id, dh6msg->trans_id);
		dPrint(DBUG, "Error in transaction ID in client_reb_bound");
		return;
	}


	if(dh6msg->dhc6_ext == NULL ) {
		dPrint(DBUG, "Extension null here");
	}
	client_process_option(dh6msg->dhc6_ext);
	return;
	client_bound();
}


/* Send the rebind message */

void client_reb_send(uint32_t trans_id)
{
 
 
	char buf[BUFSIZ];
	struct dhc6_msg *dh6msg;
	int numaddr, len = 0;
	struct dhc6_duid_opt duid;
	struct IAlist *IA, *IAref= NULL;
	struct dhc6_ia_option iaopt;
	struct configured_addr *temp_conf=NULL;


	dh6msg = (struct dhc6_msg *)buf;
	memset(dh6msg, 0, sizeof(*dh6msg));
	dh6msg->msg_type =  DHC6_REBIND;
	dh6msg->trans_id = htons(trans_id);
 
 
	/* Add the duid option */
	dhc6_set_client_id(&duid);
	memcpy((dh6msg->dhc6_ext+len), &duid, sizeof(struct dhc6_duid_opt));
	len += sizeof(struct dhc6_duid_opt);

	client_set_ia_option(&iaopt);
	memcpy((dh6msg->dhc6_ext+len), &iaopt, sizeof(struct dhc6_ia_option));
	len += sizeof(struct dhc6_ia_option);

	temp_conf = Confaddr;
	if(temp_conf == NULL){
		dPrint(DBUG, "No address available to request");
		exit(0);
	}
	
		for ( numaddr = 0; numaddr < NUM_REQ_ADDR ; numaddr++){
		if(!IA)
			IA = (struct IAlist *)malloc( sizeof(struct IAlist));
			if(IA == NULL){
				dPrint(DBUG,"Error: Allocating memory");
				exit(0);
			}
		else{
			IA->next = (struct IAlist *)malloc( sizeof(struct IAlist));
			if(IA->next == NULL){
				dPrint(DBUG,"Error: Allocating memory");
				exit(0);
			}
			IA = IA->next;
		}

		IA->IAaddr.option_code = OPTION_IAADDR;
		IA->IAaddr.option_len = sizeof(struct dhc6_iaaddr_option)-4;

		IA->IAaddr.addr = temp_conf->address;
		IA->IAaddr.prefix_len =temp_conf->prefix_len;

		temp_conf = temp_conf->next;
		IA->next = NULL;

		if( IAref == NULL){
			IAref = IA;
		}
	}
	IA = IAref;
	free(Confaddr);
	Confaddr = NULL;


	while( IA != NULL){
		memcpy((dh6msg->dhc6_ext)+len, &IA->IAaddr, 
			sizeof(struct dhc6_iaaddr_option));
		len += sizeof(struct dhc6_iaaddr_option);
		IA= IA->next;
	}

/* Rebind message is sent to DHC6_ALLAGENT_ADDR only */

	if (send_to(Outsock, DHC6_ALLAGENT_ADDR, DHC6_AGENT_PORT, 1, buf, len+3) != 0) {
		dPrint(DBUG," Error in transmission");
		exit(0);
	}
 
	dPrint(DBUG,"Returning from send REBIND");
	return;
}


/* calling client_reb_send and waiting for the replies
 * and timeouts 			
 */

void client_reb()
{
	int return_value;
	uint32_t trans_id;
	fd_set r;
	struct timeval RT, ORT;

	CLIENT.STATE = REBINDING;
 
	trans_id = (uint32_t)random();
	
	client_reb_send(trans_id);
	ORT.tv_sec = REB_TIMEOUT;
	ORT.tv_usec = 0;
 
	while(1){
		RT.tv_sec = ORT.tv_sec*2 + random_min()*(float)ORT.tv_sec;
		RT.tv_usec= ORT.tv_usec*2 + random_min()*(float)ORT.tv_usec;
		fix_time_val(&RT);
		ORT = RT;

		FD_ZERO(&r);
		FD_SET(Insock, &r);
		return_value= select(Insock+1, &r, NULL, NULL, &RT);
		switch(return_value){
			case -1:
				dPrint(DBUG,"Error selecting: in rebind");
				exit(0);
			case 0:
				client_reb_send(trans_id);
				break;
			default:
				if(FD_ISSET(Insock, &r)){
					client_reb_bound(trans_id);
					return;
					}
				else{
					dPrint(DBUG, "SEE HERE");
					exit(0);
				}
				break;
 
		}/* switch */
 
	}/*while*/
 
}


/* Here is the place where the client lies while utilising the
 * the addresses and waits for reconfgure-init messages and waits
 * till the existing life time of the addresses expires
 * assumes all the addreeses expires at a time	  
 */
	
void client_bound(void)
{
	fd_set r;
	struct timeval wait;	
	int return_value;
	time_t ti;

	signal(SIGUSR1, signal_handler);
	while(1){
	
		FD_ZERO(&r);
		FD_SET(Insock, &r);
		time(&ti);

		wait.tv_sec = CLIENT.t2 - ti;
		if(wait.tv_sec < 0 )wait.tv_sec = 0;
		wait.tv_usec = 0;
	
		dPrint(DBUG, "\n Wait time = %d seconds\n", (int)(wait.tv_sec));
		return_value = select(Insock+1, &r, NULL, NULL, &wait);
		switch( return_value){
		case 0:
			if(	client_ren(serveraddr, CLIENT.t2) == -1){
				dPrint(DBUG, "\nRebinding......");
				client_reb();
			}
			else{
				dPrint(DBUG, "\n Renewed Successfully");
				break;
			}
				
		case 1:
				if( FD_ISSET(Insock,&r)){
					client_ren_Reconfig();
				}
				break;
		case -1:
			dPrint(DBUG, "\nin BOUND error in return value of select");
			break;
		};
	}
}
	

// Good if it return -1 to take care of dec and sending the 
// request again

/* waits for replies after sending request message and sends options
 * for processing	   					    
 */

void client_req_bound(uint32_t trans_id)
{
	ssize_t message_len;
	char buf[BUFSIZ];
	struct sockaddr_storage from;
	socklen_t fromlen = sizeof(from);
	struct dhc6_msg *dh6msg;
	struct timeval;



	if( (message_len = recvfrom( Insock, buf, sizeof(buf), 0, 
				(struct sockaddr *)&from,&fromlen)) < 0){
		dPrint(DBUG,"\n Error receving packet");
		exit(0);
	}
	if (message_len < 3){
		dPrint(DBUG,"\n Error size less then struct");
		return;
	}
 
	dh6msg = (struct dhc6_msg *)buf;
	if(dh6msg->msg_type == DHC6_REPLY){ 
		dPrint(DBUG, "Reply received");
	}else{
		return;
	}
	
	if(dh6msg->trans_id != htons(trans_id)){
		dPrint(DBUG, "Error in transaction ID client_req_bound ");
		return;
	}

	if(dh6msg->dhc6_ext == NULL ) {
		dPrint(DBUG, " Extension null here");
		return;
	}

	if(client_process_option(dh6msg->dhc6_ext) == -1){

/* client_process_option retruns -1 if something goes wrong
 * and it send decline for the address		 	   
 */
		client_dec(serveraddr );
		return;
	}
	client_bound();	
}


/*  send REQUEST message along with appropriate options */

void client_req_send(struct in6_addr servaddr, uint32_t trans_id)
{
	char buf[BUFSIZ];
	struct dhc6_msg *dh6msg;
	int len=0;
	int numaddr=0;
	struct IAlist *IA, *IAref= NULL;
	struct  dhc6_duid_opt duid;
	struct dhc6_ia_option iaopt;
	struct configured_addr *temp_conf=NULL;
	char addr_char[INET6_ADDRSTRLEN];

	dh6msg = (struct dhc6_msg *)buf;
	memset(dh6msg, '\0', sizeof(*dh6msg));
	dh6msg->msg_type =  DHC6_REQUEST;
	dh6msg->trans_id = htons(trans_id);
/* should it add server identifier option??? */

/* adds the client identifier option	*/

	dhc6_set_client_id(&duid);
	memcpy((dh6msg->dhc6_ext+len), &duid, sizeof(struct dhc6_duid_opt));
	len += sizeof(struct  dhc6_duid_opt);

/* Adds the ia options	*/

	client_set_ia_option(&iaopt);
	memcpy((dh6msg->dhc6_ext+len), &iaopt, sizeof(struct dhc6_ia_option)); 
	len += sizeof(struct dhc6_ia_option);

/* Adds all the addresses obtained from the advertisement */

	temp_conf = Confaddr;
	if(temp_conf == NULL){
		dPrint(DBUG, "No address available to request");
		exit(0);
	}	

	for ( numaddr = 0; numaddr < NUM_REQ_ADDR ; numaddr++){
		if(!IA)
			IA = (struct IAlist *)malloc( sizeof(struct IAlist));
			if(IA == NULL){
				dPrint(DBUG,"Error: Allocating memory");
				exit(0);
			}
		else{
			IA->next = (struct IAlist *)malloc( sizeof(struct IAlist));
			if(IA->next == NULL){
				dPrint(DBUG,"Error: Allocating memory");
				exit(0);
			}
			IA = IA->next;
		}
		
		IA->IAaddr.option_code = OPTION_IAADDR;
		IA->IAaddr.option_len = sizeof(struct dhc6_iaaddr_option)-4;
		
		IA->IAaddr.addr = temp_conf->address;
		IA->IAaddr.prefix_len =temp_conf->prefix_len;

		 inet_ntop(AF_INET6, &temp_conf->address,addr_char, sizeof(addr_char));
		dPrint(DBUG, "Requested for address %s\n", addr_char);
		temp_conf = temp_conf->next;
		IA->next = NULL;

		if( IAref == NULL){
			IAref = IA;
		}
	}
	IA = IAref;
	free(Confaddr);
	Confaddr = NULL;
	

	while( IA != NULL){
		 inet_ntop(AF_INET6, &IA->IAaddr.addr,addr_char, sizeof(addr_char));
		dPrint(DBUG, "Adding Requested for address %s\n", addr_char);
		dPrint(DBUG, "\n Adding addresses prefix %d\n", IA->IAaddr.prefix_len);
		memcpy((dh6msg->dhc6_ext)+len, &IA->IAaddr, 
			sizeof(struct dhc6_iaaddr_option));
		len += sizeof(struct dhc6_iaaddr_option); 
		IA= IA->next;
	}

	/* No ORO as of now */
	if(send_to(Outsock, DHC6_ALLAGENT_ADDR, DHC6_AGENT_PORT, 1, buf, len+3) != 0) {
		dPrint(DBUG," Error in transmission");
		exit(0);
	}
	dPrint(DBUG, "request sent");

	free(IA);
	return;
}


/* initiates sending REQUEST message ( timeouts etc) */

void client_req(struct in6_addr servaddr)
{
	int noAttempts=0;
	int return_value;
	uint32_t trans_id;
	fd_set r;
	struct timeval RT, ORT;
 
	trans_id = (uint32_t)random();

	CLIENT.STATE = REQUESTING;

	ORT.tv_sec = 0;
	ORT.tv_usec = REQ_TIMEOUT*1000;
	
	client_req_send(servaddr, trans_id);
	while(1){
		RT.tv_sec = ORT.tv_sec*2 + random_min()*(float)ORT.tv_sec;
		RT.tv_usec= ORT.tv_usec*2 + random_min()*(float)ORT.tv_usec;
		fix_time_val(&RT);
		ORT = RT;

		if( RT.tv_sec > REQ_MAX_RT ){
			RT.tv_sec = REQ_MAX_RT + random_min()*(float)REQ_MAX_RT;
			RT.tv_usec = 0;
		}

		FD_ZERO(&r);
		FD_SET(Insock, &r);
		return_value= select(Insock+1, &r, NULL, NULL, &RT);
	
		switch(return_value){
		case -1:
			dPrint(DBUG,"Error selecting: in request");
			exit(0);
		case 0:
			if(++noAttempts > REQ_MAX_RC){
				dPrint(DBUG,"Max attempts reached");
				exit(0);
			}
			client_req_send(servaddr, trans_id);
			break;
		default:
			if(FD_ISSET(Insock, &r)){
				client_req_bound(trans_id);
				break;
			}
			else
			{
				dPrint(DBUG, "SEE HERE");
				exit(0);
			}
			break;

		}/* switch */

	}/*while*/
}


/* Processes the received ADVERTISEMENT message from the server and
 *initiates sending  REQUEST message 				   
 */

void client_process_advt(uint32_t trans_id)
{
	ssize_t message_len;
	char buf[BUFSIZ];
	struct sockaddr_storage delet;
	socklen_t deletlen = sizeof(delet);
	struct dhc6_msg *dh6msg;
	struct sockaddr_in6 six_sock;
	struct in6_addr six_add;
	char dele[INET6_ADDRSTRLEN];

	if( (message_len = recvfrom( Insock, buf, sizeof(buf), 0,
				(struct sockaddr *)&delet,&deletlen)) < 0){
		dPrint(DBUG,"\n Error receving packet");
		exit(0);
	}

	memcpy(&six_sock, &delet , sizeof(struct sockaddr_in6));
	six_add = six_sock.sin6_addr;

	inet_ntop(AF_INET6, &six_sock.sin6_addr, dele, sizeof(dele));
	dPrint(DBUG, "\nAdvertisement received with source address %s", dele);

	if (message_len < 3){
		dPrint(DBUG,"\n Error size less then struct");
		return;
	}
	
	dh6msg = (struct dhc6_msg *)buf;

	if(dh6msg->trans_id != htons(trans_id)){
		dPrint(DBUG,"\n Sol ID=%d, AdvtID=%d", trans_id, dh6msg->trans_id);
		dPrint(DBUG, "Error in transaction ID in process Advt");
		exit(0);
	}
	
	switch(dh6msg->msg_type){ 
		case DHC6_ADVERTISE:
			dPrint(DBUG, "Advertisement received");	
			CLIENT.STATE = SOLICITING;	
			client_process_option(dh6msg->dhc6_ext);
/* todo change the following line to suit the preference value */

			serveraddr = six_sock.sin6_addr;

/* suresh Again, processAdvt should not do this job ProcessAdvt
 * should be limited to collecting the information of server and
 * it can  choose to do this if it receives any server pref value
 * with 255							
 */

			client_req(serveraddr);
			break;
		default:
			dPrint(DBUG,"\nUn-expected message format");
			return;
	}
}


/* Initiates sending SOLICIT message timouts etc */

void client_sol_send(uint32_t trans_id)
{
	char buf[BUFSIZ];
	struct dhc6_msg *dh6msg;
	int message_len;
	int len = 0;
	struct dhc6_duid_opt duid;
	struct dhc6_ia_option iaopt;
	struct IAlist *IA=NULL, *IAref= NULL;
	int numaddr=0;

	dh6msg = (struct dhc6_msg *)buf;
	message_len = sizeof(*dh6msg);
	memset(dh6msg, '\0', sizeof(*dh6msg));
	memset(buf, '\0', BUFSIZ);

	dh6msg->msg_type =  DHC6_SOLICIT;
	dh6msg->trans_id = htons(trans_id);

	
/*  Add the duid option  MUST */

	dhc6_set_client_id(&duid);
	memcpy((dh6msg->dhc6_ext+len), &duid, sizeof(struct dhc6_duid_opt));
	len += sizeof( struct dhc6_duid_opt);

/* Adds the ia options	*/	
	client_set_ia_option(&iaopt);
	memcpy((dh6msg->dhc6_ext+len), &iaopt, sizeof(struct dhc6_ia_option)); 
	len += sizeof(struct dhc6_ia_option);

/* Adds the iaaddr options for he number of requested addresses	*/
	
	for ( numaddr = 0; numaddr < NUM_REQ_ADDR ; numaddr++){
		if(!IA)
			IA = (struct IAlist *)malloc( sizeof(struct IAlist));
			if(IA == NULL){
				dPrint(DBUG,"Error: Allocating memory");
				exit(0);
			}
		else{
			IA->next = (struct IAlist *)malloc( sizeof(struct IAlist));
			if(IA->next == NULL){
				dPrint(DBUG,"Error: Allocating memory");
				exit(0);
			}

			IA = IA->next;
		}

		IA->IAaddr.option_code = OPTION_IAADDR;
		IA->IAaddr.option_len = sizeof(struct dhc6_iaaddr_option)-4;
		
		IA->next = NULL;

		if( IAref == NULL){
			IAref = IA;
		}
	}
	IA = IAref;

	while( IA != NULL){
		dPrint(DBUG, "\n Adding addresses prefix %d\n", IA->IAaddr.prefix_len);
		memcpy((dh6msg->dhc6_ext)+len, &IA->IAaddr, 
			sizeof(struct dhc6_iaaddr_option));
		len += sizeof(struct dhc6_iaaddr_option);
		IA= IA->next;
	}

/* Sends it to the DHC6_ALLAGENT_ADDR */
	if (send_to(Outsock, DHC6_ALLAGENT_ADDR, DHC6_AGENT_PORT, 1, buf, len+3) != 0) {
		dPrint(DBUG," Error in transmission");
		exit(0);
	}
	
	dPrint(DBUG,"SOLICIT SENT");
	return;

}

/* sends solicit message and watches to inbound port for any in coming data */

void client_sol()
{
	int return_value;
	uint32_t trans_id;
	fd_set r;
	float rand_mul;
	struct timeval RT, ORT;
	time_t randomdelay;

	CLIENT.STATE = SOLICITING;
	Confaddr = NULL;
	
	trans_id = (uint32_t)random();
	randomdelay= random_max_min(MIN_SOL_DELAY, MAX_SOL_DELAY);
	
	rand_mul = random_min();

/* put random delay timer here */
	client_sol_send(trans_id);

/* initialise delay timer */

	ORT.tv_sec = 0;
	ORT.tv_usec = SOL_TIMEOUT * 1000;

	while(1){
		RT.tv_sec = ORT.tv_sec*2 + random_min()*(float)ORT.tv_sec; 
		RT.tv_usec= ORT.tv_usec*2 + random_min()*(float)ORT.tv_usec;

		fix_time_val(&RT);
		ORT = RT;
		if( RT.tv_sec > SOL_MAX_RT ){
			RT.tv_sec = SOL_MAX_RT + random_min()*(float)SOL_MAX_RT;
			RT.tv_usec = 0;
		}
	
		FD_ZERO(&r);
		FD_SET(Insock, &r);
		return_value= select(Insock+1, &r, NULL, NULL, &RT);
		switch(return_value){
			case -1:
				dPrint(DBUG,"Error selecting:");
				exit(0);
			case 0:
				client_sol_send(trans_id);
				break;
			default:

/* suresh ProcessAdvt should not start the request It has to
 * indentify the preference value of the server, and save the
 * server information,  After the completion of the timeout the
 * client has to process each of the server's information and
 * should choose one among them 			
 * since the scenario expected in this version is a single server
 * this is ok as of now.
 */

		client_process_advt(trans_id);
	
		}/* switch */
	}	/* while */
}


/* Generates DUID  Type 1 as specified in draft-23 and fills it
 * in duid_cur						
 */

void client_generate_duid()
{
	time_t t2,t3;
	struct tm *brk;
	uint32_t remi, i;
	struct ifreq ifhw;
	struct in6_addr linkLocalAddr;
	struct in6_addr linkLocalPrefix; 
	FILE *fp;

	char addr_char[INET6_ADDRSTRLEN];
	int s;
	uint16_t hwtype;

	if(!CREATE_CONF){
		fp = fopen(DHCP_CONF, "r");
		if(fp != NULL){
			dPrint(DBUG, "Configuration file exists already");
			fread(&duid_cur, sizeof(duid_cur), 1, fp);
			return;
		} 
	}

	duid_cur.identifier = 1;
	
	brk=(struct tm *)malloc( sizeof(struct tm));
	if(brk == NULL){
		dPrint(DBUG,"Error: Allocating memory");
		exit(0);
	}

	brk->tm_sec=0;
	brk->tm_min=0;
	brk->tm_hour=0;
	brk->tm_mday=0;
	brk->tm_mon=0;
	brk->tm_year=100;
	t2=mktime(brk);
 
	t3=time(NULL);
	i=difftime(t3,t2);
	remi=(uint32_t)drem(i,POW32);
	duid_cur.time_since_2000= remi;

	dPrint(DBUG, "\n Time since 2000 is %d", duid_cur.time_since_2000);
	if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
		dPrint(DBUG,"Error opening socket:");
		exit(0);
		return;
	}
	strncpy(ifhw.ifr_name,device, sizeof(ifhw.ifr_name));

/* Use ioctl to get the HW address of the device being used
 * SIOCGIFHWADDR returns the hardware details of the interface.
 */

	ioctl(s, SIOCGIFHWADDR, &ifhw);
	hwtype=ifhw.ifr_hwaddr.sa_family;
	duid_cur.hw_type= hwtype;

	if (inet_pton(AF_INET6, "fe80::", &linkLocalPrefix) != 1) {
		dPrint(DBUG,"inet_pton failed for link local prefix::");
		exit(0);
	}
	if (getifaddr(&linkLocalAddr, device, &linkLocalPrefix,LINK_LOCAL_PLEN) != 0) {
		dPrint(1,"Getifaddr failed in generate duid");
		exit(0);
		return;
	}
	duid_cur.link_local_addr = linkLocalAddr;

	inet_ntop(AF_INET6, &linkLocalAddr, addr_char, sizeof(addr_char));
	if(DBUG)
	dPrint(DBUG, "\n DUID Link Localo Addr is %s", addr_char);
	
	fp = fopen(DHCP_CONF, "w+");
	if(fp == NULL){
		dPrint(1, "\nError cannot create configuration file DHCP_CONF");
		exit(0);
	}
	
	dPrint(DBUG,"Configuration file DHCP_CONF created");
	fwrite(&duid_cur, sizeof(duid_cur), 1, fp);
	fclose(fp);
	free(brk);
	dPrint(DBUG,"DUID Succeessfilly generated with address");
}


/* It performs the basic initiation of client,  such as sockets etc, */

void client_init()
{
	
	int interface_index;
	int error;
	int on=1;
	struct addrinfo hints, *res;
	
/* Time to open sockets for in bound and outbound traffic */	
	if( (interface_index = if_nametoindex(device)) < 0){
		dPrint(DBUG,"Error in finding interface index");
		exit(0);
	}

	memset(&hints, 0, sizeof(hints));
	hints.ai_family = PF_INET6;
	hints.ai_socktype = SOCK_DGRAM;
	hints.ai_protocol = IPPROTO_UDP;
	hints.ai_flags = AI_PASSIVE;
	if ( (error = getaddrinfo(NULL,DHC6_CLIENT_PORT, &hints, &res)) < 0){
		dPrint(DBUG,"Downstream address not found");
		exit(0);
	}	

/* Open a socket with for inbound  traffic */
	if( (Insock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0){
		dPrint(DBUG,"Error opening inbound socket:");
		exit(0);
	}
	
/* Set the options */
	if (setsockopt(Insock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
		dPrint(DBUG,"Error setting socket options ");
		exit(0);
	}

/* Binding the socket */
	if (bind(Insock, res->ai_addr, res->ai_addrlen) < 0) {
		dPrint(DBUG,"Error binding inbound sockets:");
		exit(0);
	}
	
	freeaddrinfo(res);
 
	hints.ai_flags = 0;

	if (( error = getaddrinfo(NULL, DHC6_AGENT_PORT, &hints, &res)) < 0){
		dPrint(DBUG,"Upstream info could not found:");
		exit(0);
	}

/* open a socket for outbound traffic */
	if ((Outsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0){
		dPrint(DBUG,"Error opening outbound socket ");
		exit(0);
	}
	
/* Set the socket options to send multicast messages */
	if (setsockopt(Outsock, IPPROTO_IPV6, IPV6_MULTICAST_IF,
			&interface_index, sizeof(interface_index)) < 0) {
		dPrint(DBUG,"Error setting outbound socket options\nVerify the interface name");
		exit(0);
		
	}

	if (setsockopt(Outsock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on)) < 0) {
		dPrint(DBUG,"Error setting outbound socket options MCAST LOOP");
		exit(0);
	}

	freeaddrinfo(res);
	
/* Generate the duid: this is onetime aplication and should be unique
 * through out life-time of the client 				
 */
	client_generate_duid();
}


/* prints usage */

void usage(void)
{
	printf("\nError in parameter parsing");
	printf("\nUse client [-I interface] [-n number of address] [-d][-c]
		-d for debugging\n-c to create new configuration file(Not recommended)");
	
}
	

int main(int argc, char **argv)
{

int caseval;

/* Parameter parsing goes here */

	if(argc <= 1){
		usage();
		exit(0);
	}

	while((caseval = getopt(argc, argv, "vcdI:n:")) != EOF) {
		switch(caseval){
			
			case 'd':
				dPrint(0,"\nDebug level set ");
				DBUG = 0;
				break;
			case 'I':
				dPrint(0, "\nInterface = %s", optarg);
				device = optarg;
				break;
			case 'c':
				dPrint(0,"Create new configuration file");
				CREATE_CONF = 1;
				break;
			case 'n':
				NUM_REQ_ADDR = atoi(optarg);
				if(NUM_REQ_ADDR <= 0){
					dPrint(0, "Invalid number of addresses");
					exit(0);
				}
				dPrint(0, "Number of addresses = %d", NUM_REQ_ADDR);
				break;
			case 'v':
				dPrint(0,"%s", version);
				exit(0);
			default :
				usage();
				exit(0);
		}
	}

	if (optind < argc) {
		usage();
		exit(0);
	}
		
				
	

/* Enter into the init state */
	client_init();

/* Enter the SOLICITIING phase */
	client_sol();
	dPrint(DBUG, "Exiting client program");
	return 1;
}	
