/*
 * Center for Information Technology Integration
 *           The University of Michigan
 *                    Ann Arbor
 *
 * Dedicated to the public domain.
 * Send questions to info@citi.umich.edu
 * BOOTP is documented in RFC 951 and RFC 1048
 *
 * Delinted, ANSIfied and reformatted - 5/30/91 P. Karn
 */



/* Dynamic Ip Assignment for a Bootp Server
 * Called when a client request is received and the bootp server doesnt' have a
 * record for it.
 *
 * Design goals:
 *   Assign an IP address
 *   Separation/Identification of IP addresses assigned and not assigned
 *   Time out mechanism to reclaim IP address
 *	Timer, and arp on address with little activity
 *   Reassignment to same machine if possible.
 */     
#include <stdio.h>
#include <time.h>	

#include "global.h"
#include "arp.h"
#include "iface.h"
#include "mbuf.h"
#include "netuser.h"
#include "pktdrvr.h"
#include "timer.h"
#include "bootpd.h"


#define E_NOMEM 3101
#define ERR_NOIPADDRESS	3103		/* No IP address available. */

#define THRESH_ON            20    /* (%) When to turn on reclaimation of IP addresses. */
#define THRESH_CRITICAL       2    /* (#) */
#define THRESH_OFF           50    /* (%) */

#define R_OFF                   0x01    /* Reclaimation is off. */
#define R_RECLAIM               0x02    /* Reclaimation is on. */
#define R_CRITICAL              0x04    /* Reclaimation is operating in critical state. */
#define R_DONE                  0x08    /* Reclaimation is finishing up. */
#define V_SWAIT                 0x10    /* Reclaimation is wait to start verif cycle. */
#define V_VERIFY                0x20    /* Reclaimation is in verification cycle. */

#define TIME_RWAIT		(5) 	/* Time between running reclm da_task */
#define TIME_SWAIT              (30)    /* Time between cycles of starting address rec */
#define TIME_VWAIT              (10)    /* Time to wait between sending ARPs to verify add
resses. */
#define TIME_ADDRRETRY          (4 * 600)       /* Time to wait before trying to reclaim a
n address. */
#define TIME_ADDRRECLAIM        (900)   /* Time for which an address must be in the reclai
mation */
                                        /* queue before being moved to the free list. */

#define RECLAIM_QUEUE_MAX       15      /* Maximum number of addresses in reclaimation queue. */



/*      dynamic_ip.c
 *
 * This file contains code to manage a range of dynamic IP addresses on a network.
 */
	


/* Queue structures  */

typedef  struct  q_elt {
        struct  q_elt *next;
};

#define NULLQ_ELT (struct q_elt *) 0


struct  q {
        char *head;
        char *tail;
};

#define NULLQ_P (struct q *) 0


/* Dynamic IP structures */

struct daddr {
        struct daddr	*da_next;       /* Queue link. */
        int32		da_addr;        /* IP address. */
        time_t		da_time;        /* last time this address was answered for. */
	char		da_hwaddr[1];   /* Hardware address, variable length. */
};

#define NULLDADDR (struct daddr *) 0

struct drange_desc {
        struct drange_desc *dr_next;    /* Queue link. */
        struct iface    *dr_iface;      /* Pointer to network information. */
	struct timer	timer;		/* Timer for reclaiming */
        int32    	dr_start;       /* First IP address in range. */
        int32    	dr_end;         /* Last IP address in range. */
        int16           dr_acount;      /* Number of IP addresses in range. */
        int16           dr_fcount;      /* Number of IP addresses in free. */
        int16           dr_rcount;      /* Number of IP addresses on reclmation queue  */
        int16           dr_thon;        /* Threshold for turning on reclaimation. */
        int16           dr_thcritical;  /* Threshold for critical reclaimation. */
        int16           dr_thoff;       /* Threshold for turning off reclaimation. */
        int32           dr_time_addrretry;      /* Time to wait before retrying addresses.
						   Varies with state. */
        int16           dr_hwaddrlen;   /* Length of hardware address. */
        unsigned char   dr_rstate;      /* Reclaimation state. */
        unsigned char   dr_vstate;      /* Verification state. */
        time_t          dr_rtime;       /* Time stamp for reclaimation. */
        struct daddr    *dr_raddr;      /* Address being verified. */
        struct daddr    *dr_table;      /* Pointer to table of addresses. */
        struct q        dr_usedq;       /* Pointer to list of used addresses. */
        struct q        dr_reclaimq;    /* Pointer to list of addrs being reclaimed.  */
        struct q        dr_freeq;       /* Pointer to list of free addresses. */
};

#define NULLDRANGE (struct drange_desc *) 0





#define da_structlen(dr)        (sizeof (struct daddr) + dr->dr_hwaddrlen)
#define da_getnext(dr,da)       ((struct daddr *) ((unsigned char *)da + da_structlen(dr)))


/*
 * Globals.
 */

int ifaceToArpMap[] = {
        0, 		                        /* CL_NONE */
        ARP_ETHER, 	                        /* CL_ETHERNET */
        ARP_PRONET, 	                        /* CL_PRONET_10 */
        ARP_IEEE802,	                        /* CL_IEEE8025 */
        0, 		                        /* CL_OMNINET */
        ARP_APPLETALK,                          /* CL_APPLETALK */
        0, 	          	                /* CL_SERIAL_LINE */
        0, 		                        /* CL_STARLAN */
        ARP_ARCNET, 	                        /* CL_ARCNET */
        ARP_AX25,                               /* CL_AX25 */
        0,                                      /* CL_KISS */
        0,                                      /* CL_IEEE8023 */
        0,                                      /* CL_FDDI */
        0,                                      /* CL_INTERNET_X25 */
        0,                                      /* CL_LANSTAR */
        0,                                      /* CL_SLFP */
        ARP_NETROM,                             /* CL_NETROM */
        0                                       /* NCLASS */
};



static struct q                 rtabq;
struct timer			da_timer;
char				bp_ascii[128];

static void da_runtask __ARGS((void *arg));
struct q_elt *q_dequeue __ARGS((struct q *queue));
static void da_closeup __ARGS((struct drange_desc *dr));
static void dprint_addresses __ARGS((struct drange_desc *dr));
static int q_remove __ARGS((struct q *source_queue,struct q_elt *qel));
static void iptoa __ARGS((int32 ipaddr,char ipstr[16]));
static void da_task __ARGS((void));
static int da_fill_reclaim __ARGS((struct drange_desc *dr));
static void da_do_verify __ARGS((struct drange_desc *dr,int pendtime));
static void da_enter_reclaim __ARGS((struct drange_desc *dr));
static void da_enter_done __ARGS((struct drange_desc *dr));
static void da_enter_off __ARGS((struct drange_desc *dr));
static void q_enqueue __ARGS((struct q 	*queue,struct q_elt *elem));
static int da_get_old_addr __ARGS((struct drange_desc *dr,char *hwaddr,struct daddr **dap));
static int da_get_free_addr __ARGS((struct drange_desc *dr,struct daddr **dap));
static void da_enter_critical __ARGS((struct drange_desc *dr));
static void q_init __ARGS((struct q *queue));

extern int bp_ReadingCMDFile;



/*
 * Shutdown routines.
 */

/*
 * Done serving a network.
 */
da_done_net(iface)
struct iface *iface;
{
        struct drange_desc *dr;

        /* Find the network table */
        for(dr = (struct drange_desc *) rtabq.head; dr != NULLDRANGE; dr = dr->dr_next){
                if(iface == dr->dr_iface)
                        break;
        }

        if(dr == NULLDRANGE){
		bp_log("Range for interface '%s' not found.\n", iface->name);
		return -1;
	}

        da_closeup(dr);
	bp_log("Range removed for iface %s\n", iface->name);
        return 0;
}




/*
 * Print the status of the da structures.
 */
void
da_status(iface)
struct iface *iface;
{
        struct drange_desc *dr;

	/* If no interface was specified, print all the range information */
	if(iface == NULLIF){
        	for(dr = (struct drange_desc *) rtabq.head; dr != NULLDRANGE; 
		 dr = dr->dr_next) 
			dprint_addresses(dr);
	
	} else {
		/* Print the specified range's information */
		/* Find the specified interface */
        	for(dr = (struct drange_desc *) rtabq.head; 
		 (dr != NULLDRANGE) && (dr->dr_iface != iface); 
		 dr = dr->dr_next)
			; 
		
		/* If network not found, return */
		if(dr == NULLDRANGE){
			tprintf("Range for interface '%s' not found.\n", iface->name);
			return;	
		}
		/* The range has been found.  Print it. */
		dprint_addresses(dr);
	}
}



/*
 * Finish up service.  Close up on each of the address ranges.
 */
void
da_shut()
{
        struct drange_desc *dr;

	stop_timer(&da_timer);
        while((dr = (struct drange_desc *)q_dequeue (&rtabq)) != NULLDRANGE)
                da_closeup(dr);
}


/*
 * Release resource for a network.
 */
static void
da_closeup(dr)
struct drange_desc *dr;
{
        free(dr->dr_table);			/* Free the address table. */
        q_remove(&rtabq, (struct q_elt *)dr);	/* Dequeue the range descriptor. */
        free(dr);				/* Free the range descriptor. */
}



/* This is only called from a command */
static void
dprint_addresses(dr)
struct drange_desc *dr;
{
        struct daddr *da;
        char ipa[16];
	char ipb[16];
	struct arp_type	*at;
	
	at = &Arp_type[dr->dr_iface->type];

	iptoa(dr->dr_start, ipa);
	iptoa(dr->dr_end, ipb);
	tprintf("Interface %s range: %s - %s\n", dr->dr_iface->name, ipa, ipb);

        da = (struct daddr *) dr->dr_freeq.head;
	tprintf("Free address queue\n");
        while(da){
                iptoa(da->da_addr, ipa);
                tprintf("    %s  last used by %s\n", ipa,(*at->format)(bp_ascii, da->da_hwaddr));
                da = da->da_next;
        }

        da = (struct daddr *) dr->dr_usedq.head;
        tprintf("\nUsed address queue\n");
        while(da){
                iptoa(da->da_addr, ipa);
                tprintf("    %s  in use by %s\n", ipa, (*at->format)(bp_ascii, da->da_hwaddr));
                da = da->da_next;
        }

        da =(struct daddr *) dr->dr_reclaimq.head;
        tprintf("\nReclaimation address queue\n");
        while(da){
                iptoa(da->da_addr, ipa);
                tprintf("    %s  in use by %s?\n", ipa, (*at->format)(bp_ascii, da->da_hwaddr));
                da = da->da_next;
        }
        tprintf("\n");
}



/*
 * Reclaimation routines.
 */
static void
da_runtask(p)
void *p;
{
	stop_timer(&da_timer);
	da_task();
	set_timer(&da_timer,TIME_RWAIT*1000L);
	start_timer(&da_timer);
}

/*
 * Called periodically to run reclaimation.
 */
static void
da_task()
{
	struct drange_desc *dr;
	time_t now;
	int arpHardware, arpPendtime;

	now = time(NULL);

	for(dr = (struct drange_desc *)rtabq.head; dr != NULLDRANGE; dr = dr->dr_next){

		arpHardware = ifaceToArpMap [dr->dr_iface->type];
		arpPendtime = Arp_type[arpHardware].pendtime;

		if(!(dr->dr_rstate & R_OFF)){	/* If doing reclaimation on this range. */
			if(dr->dr_vstate == V_SWAIT){	/* If in wait sub-state. */
				/* Doing reclaimation on this range and am waiting to 
				 * start a cycle of address
				 * verification.  Check if it is time to start the 
				 * cycle. */

				if(now - dr->dr_rtime > TIME_SWAIT){
					/* Start the cycle.  */
					if(!(dr->dr_rstate & R_DONE))
						da_fill_reclaim(dr);

					dr->dr_vstate = V_VERIFY; /* verify sub-state. */
					dr->dr_raddr = NULLDADDR; /* start at beginning */
				}
			}
			/* If in the verify state (may have just been changed above), and 
			 * enough time has passed since last lookup, check it and start
			 * the next lookup. */

			if(dr->dr_vstate == V_VERIFY){
				if(now - dr->dr_rtime > arpPendtime){
					da_do_verify(dr, arpPendtime); /* Verify address. */
					dr->dr_rtime = time(NULL); /* Set time stamp. */
					if(dr->dr_raddr == NULLDADDR){ /* If at end... */
						dr->dr_vstate = V_SWAIT; /* Q empty; enter wait sub-state. */
					}
				}
			}
			
			/* 
			 * State transitions.  May have moved some addresses to free list.
			 * If so, I may be able to move to a "lower" state.
			 */
			switch(dr->dr_rstate){
			/* case R_OFF: Not handled. */
			case R_CRITICAL:
				/* Have conditions droped below critical threshhold? */
				if(dr->dr_fcount > dr->dr_thcritical)	
					da_enter_reclaim(dr);
				/* Fall through. */
			case R_RECLAIM:
				/* Have I reclaimed enough addresses? */
				if(dr->dr_fcount > dr->dr_thoff)	
					da_enter_done(dr);
				/* Fall through. */
			case R_DONE:
				/* Am I in the done state and have exausted the reclaimation queue? */
				if((dr->dr_rstate & R_DONE) && dr->dr_reclaimq.head == NULLCHAR) 
					da_enter_off(dr);
				break;
			}
		}
	}
}




/* 
 * Enter the DONE state.  Can't get to the done state from the off state.
 */
static void
da_enter_done(dr)
struct drange_desc *dr;
{
	char ipa[16], ipb[16];
	
	iptoa(dr->dr_start, ipa);
	iptoa(dr->dr_end, ipb);

	if((dr->dr_rstate & R_OFF) == 0){ 
		dr->dr_rstate = R_DONE;
		dr->dr_time_addrretry = TIME_ADDRRETRY;		/* Wait a while before retrying addresses. */
	}
}


/* 
 * Enter the OFF state.
 */
static void
da_enter_off(dr)
struct drange_desc *dr;
{
	char ipa[16], ipb[16];
	
	iptoa(dr->dr_start, ipa);
	iptoa(dr->dr_end, ipb);

	dr->dr_rstate = R_OFF;
}


/*
 * Verify addresses.
 * To avoid flodding the network and our address resolution queue I only send
 * out one ARP at a time.  This routine is called periodically to step through
 * the reclaimation queue.  The first step is to check for a responce to the
 * ARP that was sent out previously.  If there is a responce I move the address
 * to the used queue. The next step is to send out an ARP for the next address
 * on the recliamation queue. After a suitable intervel (TIME_VTIME) I'll be
 * called again.
 */
static void
da_do_verify(dr, pendtime)
struct drange_desc *dr;
int pendtime;
{
	struct daddr *da, *dn;
	struct iface *iface;
	long now;
	struct arp_tab *ap;
	int16 arpType;
	
 	now = time(NULL);
 	iface = dr->dr_iface;
	arpType = ifaceToArpMap[iface->type];

	/*
 	 * If I sent an ARP for an address, check if that ARP has been responded to.
	 * If dr_raddr points to an address record, I have previously sent an
	 * ARP for that address.  Check the ARP cache for a responce.
	 * If dr_raddr is NULL then I am to start at the head of the reclaim queue.
 	 */

	if(dr->dr_raddr != NULLDADDR){
		/* ARP has been sent for dr_raddr.  Check the ARP cache for a responce. */
		da = dr->dr_raddr;
		dn = da->da_next;

		ap = arp_lookup(arpType, da->da_addr);

		if((ap != NULLARP) && (ap->state == ARP_VALID)){
			/* Host responded to arp.  Place address on used queue.
			 * Copy in physical address of host using address to
			 * make sure our info is up to date.  
			 * I could verify that physical address of host
			 * responding to  ARP matches the physical address of
			 * the host I think owns the address.  If don't match
			 * someone is probably using an incorrect address.
			 */

			q_remove(&dr->dr_reclaimq, (struct q_elt *)da);
			--dr->dr_rcount;
			da->da_time = now;		/* Time tested. */
			memcpy(da->da_hwaddr, ap->hw_addr, Arp_type[ap->hardware].hwalen);
			q_enqueue(&dr->dr_usedq, (struct q_elt *)da);

		} else {
			/* Host did not respond to ARP.  If addr on reclaim
			 * queue long enough, move it to the free queue.
			 */
			if(now - da->da_time >= pendtime){
				q_remove(&dr->dr_reclaimq, (struct q_elt *)da);
				--dr->dr_rcount;
				q_enqueue(&dr->dr_freeq,(struct q_elt *)da);
				++dr->dr_fcount;
				bp_log("Reclaimed address %s on net %s.\n", 
					inet_ntoa(da->da_addr), dr->dr_iface->name);
			}
		}
	} else {
		/* Use first addr in reclaimq. */
		dn = (struct daddr *) dr->dr_reclaimq.head;
	}
	/*
 	 * Now move to the next entry in the queue and ARP for it.
 	 */
 	da = dn;
	if(da != NULLDADDR){
		ap = arp_lookup(arpType, da->da_addr);
		if(ap != NULLARP) arp_drop(ap);
		res_arp(iface, arpType, da->da_addr, NULLBUF);
	}
	dr->dr_raddr = da;	/* Verify this address next time around. */
	dr->dr_rtime = time(NULL);
}


/*
 * Fill the reclaimation list from the used list.  Take addresses off the head
 * of the used queue until the reclaim queue is full, the used queue is empty,
 * or the address at the head of the used queue has been verified (responded
 * to an ARP) within dr_time_addrretry tocks.
 */
static int
da_fill_reclaim(dr)
struct drange_desc *dr;
{
        struct daddr *da;
        long now;

        now = time(NULL);

        while(dr->dr_rcount < RECLAIM_QUEUE_MAX){
		/* Look at first address on used queue. */
                da = (struct daddr *) dr->dr_usedq.head;
                if(da == NULLDADDR)
			return 0;    /* If used queue is empty, done filling. */
                if(now - da->da_time < dr->dr_time_addrretry)
                        return 0;

		/* If the first element has responded to in ARP recently.
		 * I am done filling.
		 */
		/* Get first address on used queue. */
                da = (struct daddr *) q_dequeue(&dr->dr_usedq);
		/* Mark time addr put in reclaim queue. */
                da->da_time = now;
		/* Put it at end of reclaim queue. */
                q_enqueue(&dr->dr_reclaimq,(struct q_elt *)da);
                ++dr->dr_rcount;
        }
        return 0;
}


/*
 * Address assignment routines.
 */

/*
 * Assign an address.
 */
int
da_assign(iface, hwaddr, ipaddr)
struct iface *iface;	/* -> Pointer to lnet struct of net on which to assign addr. */
char	*hwaddr;	/* -> Pointer to hardware address of hosts. */
int32	*ipaddr;	/* <- Address assigned to host. */
{
	struct drange_desc *dr;	
	struct daddr *da;	
	int status;
	struct arp_type	*at;

	/* Find the network table */
	for(dr = (struct drange_desc *) rtabq.head; dr != NULLDRANGE; dr = dr->dr_next){
		if(iface == dr->dr_iface)
			break;	
	}

	if(dr == NULLDRANGE){
		*ipaddr = 0;
		return ERR_NOIPADDRESS;
	}

	/* If this host had an address assigned previously, try to reassign
	 * that. If no previous address, assign a new one.
	 */
	status = da_get_old_addr(dr, hwaddr, &da);
	if(status != 0) 
		status = da_get_free_addr(dr, &da);
	
	/* If I got an address, assign it and link it in to the use list. */
	if(status == 0){
		memcpy(da->da_hwaddr, hwaddr, dr->dr_hwaddrlen);
		*ipaddr = da->da_addr;
		da->da_time = time(NULL);	/* Time assigned */
		q_enqueue(&dr->dr_usedq,(struct q_elt *)da);
		at = &Arp_type[dr->dr_iface->type];
		bp_log("IP addr %s assigned to %s on network %s\n",
		 inet_ntoa(*ipaddr),
		 (*at->format)(bp_ascii, hwaddr), dr->dr_iface->name);
	}

        switch(dr->dr_rstate){
        case R_OFF:
        case R_DONE:
                if(dr->dr_fcount <= dr->dr_thon) 
			da_enter_reclaim(dr);
                /* Fall through. */
        case R_RECLAIM:
                if(dr->dr_fcount <= dr->dr_thcritical) 
			da_enter_critical(dr);
                break;
        /* case R_CRITICAL: is not handled. */
        }
        return status;
}


/*
 * Enter the reclaimation state.
 */
static void
da_enter_reclaim(dr)
struct drange_desc *dr;
{
        char ipa[16], ipb[16];

        iptoa(dr->dr_start, ipa);
        iptoa(dr->dr_end, ipb);

        if(dr->dr_rstate & R_OFF){
                dr->dr_vstate = V_SWAIT;  /* da_enter_reclaim: R_OFF */
                dr->dr_rtime = 0;
        }
        dr->dr_rstate = R_RECLAIM;
        dr->dr_time_addrretry = TIME_ADDRRETRY;         /* Wait a while before retrying addresses. */
}

/*
 * Search for hwaddr on the used list, the reclaimation list, and the free list.
 */
static int
da_get_free_addr(dr, dap)
struct drange_desc *dr;
struct daddr **dap;
{
        *dap = (struct daddr *) q_dequeue(&(dr->dr_freeq));
        if(*dap == NULLDADDR) 
		return ERR_NOIPADDRESS;
        --dr->dr_fcount;

        return 0;
}

/*
 * Search for hwaddr on the used list, the reclaimation list, and the free list.
 */
static int
da_get_old_addr(dr, hwaddr, dap)
struct drange_desc *dr;
char   	*hwaddr;
struct daddr **dap;
{
        struct daddr *da;

	/* Search the used queue */
        for(da = (struct daddr *) dr->dr_usedq.head; da != NULLDADDR; da = da->da_next){
                if(memcmp(da->da_hwaddr, hwaddr, dr->dr_hwaddrlen) == 0){
                        q_remove(&dr->dr_usedq,(struct q_elt *)da);
                        *dap = da;
                        return 0;
                }
        }

	/* Search the relaimq queue */
        for(da = (struct daddr *) dr->dr_reclaimq.head; da != NULLDADDR; 
		da = da->da_next){
                if(memcmp(da->da_hwaddr, hwaddr, dr->dr_hwaddrlen) == 0){
                        /* Here is the address.  I have to be carefull in removing it from
			 * reclaim queue, I may be verifying this address.
                         * If I am, I have to fix up the pointers before removing this
                         * element.
                         */
                        if((dr->dr_rstate & R_OFF) == 0 && dr->dr_vstate == V_VERIFY 
				&& dr->dr_raddr == da){ 
				/* I am verifying this very address.  */
                                /* Start over.  
				 * This should happen very infrequently at most. */
                                dr->dr_vstate = V_SWAIT;  /* get_old_addr */
                        }
                        q_remove(&dr->dr_reclaimq,(struct q_elt *)da);
                        *dap = da;
                        return 0;
                }
        }

	/* Search the free queue */
        for(da = (struct daddr *) dr->dr_freeq.head; da != NULLDADDR; da = da->da_next){
                if(memcmp(da->da_hwaddr, hwaddr, dr->dr_hwaddrlen) == 0){
                        q_remove(&dr->dr_freeq,(struct q_elt *)da);
                        --dr->dr_fcount;
                        *dap = da;
                        return 0;
                }
        }
        return ERR_NOIPADDRESS;
}

#ifdef	notdef
static void
dprint_dr_record(dr)
struct drange_desc *dr;
{
        bp_log("Queue link   x%lx\n", dr->dr_next);
        bp_log("Pointer to network information         x%lx\n", dr->dr_iface);
        bp_log("First IP address in range              x%lx\n", dr->dr_start);
        bp_log("Last IP address in range               x%lx\n", dr->dr_end);
        bp_log("Number of IP addresses in range        %d\n", dr->dr_acount);
        bp_log("Number of IP addresses in free         %d\n", dr->dr_fcount);
        bp_log("Number of IP addresses on reclaimation queue %d\n", 
		dr->dr_rcount);
        bp_log("Threshold for turning on reclaimation  %d\n", dr->dr_thon);
        bp_log("Threshold for critical reclaimation    %d\n", dr->dr_thcritical);
        bp_log("Threshold for turning off reclaimation %d\n", dr->dr_thoff);
        bp_log("Time to wait before retrying addresses %ld\n", 
		dr->dr_time_addrretry);
        bp_log("Length of hardware address             %d\n", dr->dr_hwaddrlen);
        bp_log("Reclaimation state                     %d\n",(int)dr->dr_rstate);
        bp_log("Verification state                     %d\n",(int)dr->dr_vstate);
        bp_log("Time stamp for reclaimation            %ld\n", dr->dr_rtime);
        bp_log("Address being verified                 x%lx\n", dr->dr_raddr);
        bp_log("Pointer to table of addresses          x%lx\n", dr->dr_table);
        bp_log("uesdq x%lx  reclaimq                   x%lx  freeq x%lx\n", dr->dr_usedq, 
		dr->dr_reclaimq, dr->dr_freeq);
}
#endif


/*
 * Enter the critical reclaimation state.
 */
static void
da_enter_critical(dr)
struct drange_desc *dr;
{
        char ipa[16], ipb[16];
	char *ipc;

	ipc = inet_ntoa(dr->dr_start);
	strcpy(ipa, ipc);
	ipc = inet_ntoa(dr->dr_end);
	strcpy(ipb, ipc);

        if((dr->dr_rstate & R_OFF) == 0){
                dr->dr_vstate = V_SWAIT;	/* Enter critical, & R_OFF */
                dr->dr_rtime = 0;
        }
        dr->dr_rstate = R_CRITICAL;
        dr->dr_time_addrretry = 0;      /* Retry addresses as fast as possible. */
}

/*
 * Initialization	
 */
/*
 * Initialize the Dynamic address assignment module.
 */
int
da_init()
{
        q_init(&rtabq);
        return 0;
}

/*
 * Begin dynamic address service for a network.
 */
int
da_serve_net(iface, rstart, rend)
struct iface *iface;		/* Pointer to lnet record. */
int32 rstart;			/* First address in range. */
int32 rend;			/* Last address in range. */
{
	struct drange_desc *dr;	/* Pointer to the range descriptor. */
	struct daddr *da;	/* Pointer to an address structure. */
	int32 rcount;		/* Number of addresses range. */
	time_t now;		/* Current time. */
	int16 i;
	char ipc[16], ipd[16];

        /* Find the network table */
        for(dr = (struct drange_desc *) rtabq.head; dr != NULLDRANGE;
	 dr = dr->dr_next){
                if(iface == dr->dr_iface)
                        break;
        }

	
	if(dr == NULLDRANGE){
		/* If there is no network table, allocate a new one
		 *
 		 * Allocate the memory I need.
 		 */
		dr = (struct drange_desc *) calloc(1, sizeof(*dr));
		if(dr == NULLDRANGE) 
			return E_NOMEM;
	} else if((dr->dr_start != rstart) || (dr->dr_end != rend)) 
		/* If the range is different, create a new range */
		free(dr->dr_table);
	else
		return 0; /* There is no change, return */


	rcount = (rend - rstart) + 1;
	da = (struct daddr *) calloc(1,(sizeof (*da) + iface->iftype->hwalen) * rcount);
	if(da == NULLDADDR) 
		return E_NOMEM;

	/* 
	 * Got the memory, fill in the structures.
	 */
	dr->dr_iface = iface;
	dr->dr_start = rstart;
	dr->dr_end = rend;
	dr->dr_acount = rcount;
	dr->dr_fcount = 0;
	dr->dr_rcount = 0;
	dr->dr_thon = (rcount * THRESH_ON) / 100;
	dr->dr_thcritical = THRESH_CRITICAL;
	dr->dr_thoff = (rcount * THRESH_OFF) / 100;
	dr->dr_time_addrretry = 0;
        dr->dr_hwaddrlen = iface->iftype->hwalen;
	dr->dr_rstate = R_OFF;
	dr->dr_vstate = V_SWAIT;			/* Initialize */
	dr->dr_rtime = 0;
	dr->dr_raddr = NULLDADDR;
	dr->dr_table = da;

	/* 
	 * Fill in the table and link them all onto the used list.
	 */
	time(&now);
	for(i = 0, da = dr->dr_table; i < dr->dr_acount; ++i, da = da_getnext(dr, da)){
		da->da_addr = rstart++;
		da->da_time = 0;		/* Initiallize at 0, only here */
		q_enqueue(&dr->dr_usedq,(struct q_elt *)da);
	}
	/* and set up the timer stuff */
	if(rtabq.head == NULLCHAR){
		set_timer(&da_timer,TIME_RWAIT*1000L);
       		da_timer.func = da_runtask;
	       	da_timer.arg = (void *) 0;
		start_timer(&da_timer);
	}
	q_enqueue(&rtabq,(struct q_elt *)dr);
	da_enter_critical(dr);	/* Start reclaiming some of these addresses. */

	iptoa(dr->dr_start, ipc);
	iptoa(dr->dr_end, ipd);
	bp_log("DynamicIP range: %s - %s\n", ipc, ipd);
	return 0;
}


/*
 * Routines to implement a simple forward linked queue.
 */

/*
 *      q_init()
 *      Initialize simple Q descriptor
 */
static void
q_init(queue)
struct q *queue;
{
        queue->head = 0;
        queue->tail = 0;
}


/*
 *      q_enqueue()
 *              Enqueue an element in a simple Q.
 */
void
q_enqueue(queue, elem)
struct q *queue;
struct q_elt *elem;
{
        struct q_elt *last;

        if(queue->tail != NULLCHAR){  /* If not empty Q... */
                last = (struct q_elt *) queue->tail;
                last->next = elem;
        }
        else
		queue->head = (char *) elem;

        queue->tail = (char *) elem;
        elem->next = NULLQ_ELT;
}


/*
 *      q_dequeue       ()
 *      Pull an element off of the head of a Q.
 */
struct q_elt *
q_dequeue(queue)
struct q *queue;
{
        struct q_elt *elem;

        if(queue->head == NULLCHAR)
		return NULLQ_ELT; /* return NULL when empty Q */
        elem = (struct q_elt *) queue->head;
        queue->head = (char *) elem->next;
        elem->next = NULLQ_ELT;
        if(queue->head == NULLCHAR)
		queue->tail = NULLCHAR;
        return elem;
}


/*
 *      Remove an element from the middle of a queue.  Note that
 *      there is no mutex here, so this shouldn't be used on
 *      critical Qs
 */

static int
q_remove(source_queue, qel)
struct q *source_queue;
struct q_elt *qel;
{
        struct q_elt *prev, *e;

        /*   Case : removing first in Q */

        if(qel == (struct q_elt *) source_queue->head){
                source_queue->head = (char *)qel->next;  /* trying to remove first in queue... */
                if(source_queue->head == NULLCHAR)  	/* nothing left... */
                        source_queue->tail = NULLCHAR; 	/* blank out the Q */
                else if(source_queue->head == source_queue->tail){ /* One thing left */
                        e = (struct q_elt *) source_queue->head; /* As insurance, set it's next to NULL. */
                        e->next = NULLQ_ELT;
                }
                return 0;
        }

        /* find Q element before qel, so that we can link around qel */
        for(prev = (struct q_elt *) source_queue->head; prev->next != qel; prev = prev->next)
                if(prev == NULLQ_ELT)
                        return 1;

        /* Case : Removing last in Q */

        if(qel == (struct q_elt *) source_queue->tail){     /* trying to remove last one in queue... */
                prev->next = NULLQ_ELT;   /* there is a prev elt, since we return on first */
                source_queue->tail = (char *) prev;
                return 0;
        }

        /*  else, removing a queue element in the middle...  */
        prev->next = qel->next;
        return 0;
}

/*
 * Support Routines
 */

static void
iptoa(ipaddr, ipstr)
int32 ipaddr;
char ipstr[16];
{
	char *tmpStr;

	tmpStr = inet_ntoa(ipaddr);
	strcpy(ipstr, tmpStr);
}


#ifdef	notdef
static  void
build_hex_string(fromstr, len, tostr)
char *fromstr; int  len;
char *tostr;
{
	int i;

	for(i=0; i < len; i++){
		sprintf(tostr, "%02x", fromstr[i]);
		tostr++;
		tostr++;
	}
	fromstr[len] = 0;
}
#endif
