Index: FLI-CHANGES
===================================================================
RCS file: FLI-CHANGES
diff -N FLI-CHANGES
--- /dev/null	Sat Mar 24 05:37:44 2001
+++ FLI-CHANGES	Mon Feb 25 17:19:25 2002
@@ -0,0 +1,95 @@
+Changes made to dhcrelay 3.0
+
+Overview:
+The dhcrelay have been modified to instead of sending a
+recieved dhcp message to one or more servers it now
+sends the dhcp message to one server based on the mac
+addrress of the requesting client.
+
+There is also a web interface for a client to bind it's mac address
+to the provider of his/her choice.
+
+Structure:
+The dhcrelay now uses a database with the binding of mac address
+to dhcpserver. The database is a Postgesql and is accessed trough
+libpq.
+
+The database has the following definition:
+
+-- SQL definition --
+create table T_provider (
+	provider_id SERIAL,
+	provider_name text,
+	provider_dhcp inet,  
+	provider_auth_url text,
+	provider_net  inet,
+	provider_netmask inet,
+	primary key (provider_id));
+
+create table T_macbind (
+	mac_addr macaddr,
+	provider_id bigint,
+	mac_reg_date timestamp,
+	primary key (mac_addr));
+
+create view V_mapping as select M.mac_addr, P.provider_dhcp, P.provider_net, P.provider_netmask, P.provider_auth_url from T_macbind as M,T_provider as P where M.provider_id = P.provider_id;
+
+grant all on T_provider to macbind;
+grant all on T_macbind to macbind;
+grant all on V_mapping to macbind;
+
+-- SQL definition --
+
+For the database there should also be a user called 'macbind' with the password 'macbind' (or whatever you
+choose).
+
+In the table T_provider:
+provider_id is just a serialnumber identifying the provider and is used to bind mac addresses to it.
+provider_name is the name of the provider and is also displayed to the users when choosing provider.
+provider_dhcp is the ip address of the dhcpserver of the provider.
+provider_auth_url is the URL to the providers authentication page.
+provider_net is the ip address of the network that the dhcpserver should hand to a client
+provider_netmask is the netmask for provider_net.
+
+In T_provider there has to exist one entry with provider_name='default' to be used
+as default when a (mac address, provider) binding is not available.
+
+In the table T_macbind:
+mac_addr is the macaddress of the client.
+provider_id referes to the table T_provider.
+
+The view V_mapping is just a way to ease up the querys to the database so that
+the client only has to make one query to get the mac address, provider binding.
+
+
+Installation:
+
+Requiremets:
+Postgresql server.
+Apache server.
+Perl with modules CGI, DBI and DBD-Pg.
+ISC dhcp-3.0.
+
+Database:
+Get a postgresql database and install it.
+
+Install the perl modules DBI and DBD-Pg.
+
+Create a database called 'macbind' (pgsql/bin/createdb macbind).
+Create a user called 'macbind' with password 'macbind' (pgsql/bin/createuser).
+
+Use the psql interface to create the tables etc. as specified above.
+
+Web interface:
+Copy the mac-bind_web/bind_*.pl files to a cgi-bin directory of your choise.
+
+DHCP (relay):
+Apply the patch in the main directory (patch -p1 < db-dhcrelay.patch)
+Configure and make and install.
+
+
+
+
+
+
+
Index: Makefile.conf
===================================================================
RCS file: /var/lib/cvs/open/src/dhcp-3.0/Makefile.conf,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- Makefile.conf	2001/11/09 17:08:46	1.1.1.1
+++ Makefile.conf	2001/12/03 10:57:14	1.3
@@ -46,6 +46,8 @@
 RANLIB = ranlib
 MKDEP = mkdep
 CLIENT_PATH = '"PATH=/usr/ucb:/usr/bin:/usr/sbin:/bin:/sbin"'
+PGLIB=/usr/lib
+PGINCLUDE=/usr/include/postgresql
 
 BINDLIB = ../minires/libres.a
 BINDINC =
Index: Makefile.dist
===================================================================
RCS file: /var/lib/cvs/open/src/dhcp-3.0/Makefile.dist,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- Makefile.dist	2001/11/09 17:08:46	1.1.1.1
+++ Makefile.dist	2001/11/27 09:57:55	1.2
@@ -17,7 +17,8 @@
 # http://www.isc.org for more information.
 #
 
-SUBDIRS=	common $(MINIRES) dst omapip server client relay dhcpctl
+#SUBDIRS=	common $(MINIRES) dst omapip server client relay dhcpctl
+SUBDIRS=	common $(MINIRES) dst omapip relay
 
 all:
 	@for dir in ${SUBDIRS}; do \
Index: relay/Makefile.dist
===================================================================
RCS file: /var/lib/cvs/open/src/dhcp-3.0/relay/Makefile.dist,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- relay/Makefile.dist	2001/11/09 17:08:46	1.1.1.1
+++ relay/Makefile.dist	2001/11/19 16:01:11	1.2
@@ -24,9 +24,11 @@
 PROG   = dhcrelay
 MAN    = dhcrelay.8
 
-INCLUDES = -I$(TOP) $(BINDINC) -I$(TOP)/includes
+INCLUDES = -I$(TOP) $(BINDINC) -I$(TOP)/includes -I$(PGINCLUDE)
 DHCPLIB = ../common/libdhcp.a $(BINDLIB) ../omapip/libomapi.a ../dst/libdst.a
 CFLAGS = $(DEBUG) $(PREDEFINES) $(INCLUDES) $(COPTS)
+LFLAGS = -L $(PGLIB)
+LIBS = -lpq
 
 all:	$(PROG) $(CATMANPAGES)
 
Index: relay/dhcrelay.c
===================================================================
RCS file: /var/lib/cvs/open/src/dhcp-3.0/relay/dhcrelay.c,v
retrieving revision 1.1.1.1
retrieving revision 1.15
diff -u -r1.1.1.1 -r1.15
--- relay/dhcrelay.c	2001/11/09 17:08:46	1.1.1.1
+++ relay/dhcrelay.c	2002/02/25 15:53:46	1.15
@@ -43,11 +43,14 @@
 
 #ifndef lint
 static char ocopyright[] =
-"$Id: dhcrelay.c,v 1.52 2001/04/19 16:48:53 mellon Exp $ Copyright (c) 1997-2000 Internet Software Consortium.  All rights reserved.\n";
+"$Id: dhcrelay.c,v 1.15 2002/02/25 15:53:46 mhe Exp $ Copyright (c) 1997-2000 Internet Software Consortium.  All rights reserved.\n";
 #endif /* not lint */
 
+#include <sys/ioctl.h>
+#include <netinet/in.h>
 #include "dhcpd.h"
 #include "version.h"
+#include <libpq-fe.h>
 
 static void usage PROTO ((void));
 
@@ -107,11 +110,418 @@
 	struct sockaddr_in to;
 } *servers;
 
+struct localinterface {
+  struct in_addr lif;
+  struct localinterface *next;
+} *localinterfaces;
+
 static char copyright [] = "Copyright 1997-2000 Internet Software Consortium.";
 static char arr [] = "All rights reserved.";
 static char message [] = "Internet Software Consortium DHCP Relay Agent";
 static char url [] = "For info, please visit http://www.isc.org/products/DHCP";
 
+PGconn *conn; /* --fli-- The database connection */
+
+/* --mhe-- Create a queue of the received requests. When a request is
+   received, it is added to the queue and a timer is set. When the
+   timer expires, the request is removed from the queue and the MAC
+   address is removed from the MAC database, so that following
+   requests from that MAC are relayed to the default server. The timer
+   is reset when a new request is received (ie, the dhcp client is
+   still trying to contact the server) or when a reply is received.
+ */
+#define HALEN 6
+typedef struct req_t req_t;
+struct req_t {
+	unsigned char haddr[HALEN]; /* hardware address of client */
+	int nreq;                   /* number of BOOTREQUESTs w/o a BOOTREPLY */
+	TIME tm;                    /* time of last request */
+	req_t *next;
+};
+req_t *req_head = 0;            /* start of the queue */
+req_t *req_tail = 0;            /* end of the queue */
+
+/* timeout in seconds before a client that has not received a reply
+ * from a server is removed from the database
+ */
+#define TIMEOUT 30
+
+void req_timeout(req_t *req);
+
+/* --mhe-- Add or update the request (with hardware address HADDR) to
+ * the request queue and set a timer for it
+ */
+req_t *req_add(unsigned char *haddr)
+{
+	req_t *req = 0, *i;
+
+	/* first see if this request has already been added */
+	for(i = req_head; i; i = i->next) {
+		if(memcmp(haddr, i->haddr, HALEN) == 0) {
+			req = i;
+			break;
+		}
+	}
+	if(req) {
+		/* this is an old request, update it */
+		log_debug("updating request from %02x:%02x:%02x:%02x:%02x:%02x",
+				 req->haddr[0], req->haddr[1], req->haddr[2],
+				 req->haddr[3], req->haddr[4], req->haddr[5]);
+		req->tm = time(0);
+		req->nreq++;
+	} else {
+		/* this is a new request */
+		req = (req_t *)malloc(sizeof(req_t));
+		memcpy(&req->haddr, haddr, HALEN);
+		req->nreq = 1;
+		req->tm = time(0);
+		req->next = 0;
+
+		log_debug("adding request from %02x:%02x:%02x:%02x:%02x:%02x",
+				 req->haddr[0], req->haddr[1], req->haddr[2],
+				 req->haddr[3], req->haddr[4], req->haddr[5]);
+
+		/* insert it at the end of the list */
+		if(req_tail) {
+			req_tail->next = req;
+			req_tail = req;
+		} else {
+			/* list not initialized */
+			req_head = req_tail = req;
+		}
+	}
+	/* Set the timer. add_timeout() will update the timer if if is
+	 * already added (otherwise it will add a new). It also sorts the
+	 * timeouts for us.
+	 */
+	add_timeout(req->tm + TIMEOUT, /* client will fail after TIMEOUT seconds */
+				(void (*)(void *))req_timeout,
+				req /* what */,
+				0 /* ref */,
+				0 /* unref */);
+}
+
+/* --mhe-- Delete a request (with hardware address HADDR) from the
+ * request queue and cancel any timer set
+ */
+void req_del(unsigned char *haddr)
+{
+	req_t *i, *p = 0;
+
+	i = req_head;
+	if(i == 0)
+		return;
+
+	while(1) {
+		if(memcmp(haddr, i->haddr, HALEN) == 0) {
+			log_debug("deleting request from %02x:%02x:%02x:%02x:%02x:%02x",
+					 i->haddr[0], i->haddr[1], i->haddr[2],
+					 i->haddr[3], i->haddr[4], i->haddr[5]);
+			cancel_timeout((void (*)(void *))req_timeout, i);
+			if(p == 0)
+				req_head = req_head->next;
+			else
+				p->next = i->next;
+			if(req_tail == i && i->next == 0)
+				req_tail = p;
+			free(i);
+			break;
+		}
+		p = i;
+		i = i->next;
+		if(i == 0)
+			break;
+	}
+}
+
+/* --mhe-- This function is the callback function installed with the
+ * add_timeout() and is called whenever a timer expires
+ */
+void req_timeout(req_t *req)
+{
+	char qstr[256];
+	PGresult *res;
+
+	log_debug("request timed out from %02x:%02x:%02x:%02x:%02x:%02x",
+			  req->haddr[0], req->haddr[1], req->haddr[2],
+			  req->haddr[3], req->haddr[4], req->haddr[5]);
+
+	/* remove the entry from the MAC database */
+
+	if(test_database_connection() < 0)
+		log_fatal("Can't access database\n");
+
+	sprintf(qstr,"DELETE FROM T_macbind WHERE mac_addr"
+			" = \'%02x:%02x:%02x:%02x:%02x:%02x\'",
+			req->haddr[0], req->haddr[1], req->haddr[2],
+			req->haddr[3], req->haddr[4], req->haddr[5]);
+
+	res = PQexec(conn, qstr);
+
+    if(!res || PQresultStatus(res) != PGRES_COMMAND_OK)
+		log_error("Can't delete MAC entry: %s\n",PQresultErrorMessage(res));
+	PQclear(res);
+
+	/* remove the request from the queue */
+	req_del(req->haddr);
+}
+
+/* --fli--
+   get_localinterfaces()
+
+   Fills in the list of local interfaces.
+   returns number of interfaces allocated.
+   on error -1.
+*/
+int
+get_localinterfaces(){
+  struct ifreq  *interfacep;
+  struct sockaddr *sockaddrp; 
+  struct sockaddr_in *addrp;
+  struct ifconf ifc; 
+  char buf[1000];
+  int n;
+  int fd;
+  int count;
+  struct localinterface *tmplif;
+
+  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+    return -1;
+  
+  /* Get interface configuration */
+  ifc.ifc_len = sizeof (buf);   
+  ifc.ifc_buf = buf;
+  if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) < 0)
+    /* Error in ioctl */
+    return -1 ;
+
+  interfacep = ifc.ifc_req;
+  
+  count = 0;
+  /* Parse config. for all interfaces */
+  for (n = ifc.ifc_len / sizeof (struct ifreq); --n >= 0; interfacep++)
+    { 
+      if (interfacep->ifr_addr.sa_family != AF_INET)
+        continue;
+      
+      if ((tmplif = malloc(sizeof(struct localinterface))) == NULL){
+	log_fatal("Can't get memory for interface\n");
+	return -1;
+      }
+      
+      sockaddrp = &(interfacep->ifr_addr);
+      addrp = (struct sockaddr_in*)sockaddrp;
+      memcpy(&tmplif->lif,&addrp->sin_addr,sizeof(struct in_addr));
+      
+      tmplif->next = localinterfaces;
+      localinterfaces = tmplif;
+      count++;
+    }
+  
+  return count;
+}
+
+/* --fli--
+   address_in_network(struct in_addr *interface, struct in_addr *network, struct in_addr *netmask){
+   
+   Checks if the interface is in the network given.
+   The netmask is on network bitmask format.
+*/
+
+int 
+address_in_network(interface, network, netmask)
+struct in_addr *interface;
+struct in_addr *network;
+struct in_addr *netmask;
+{
+  if ((interface->s_addr & netmask->s_addr) == network->s_addr)
+    return 1;
+
+  return 0;
+}
+
+/* --fli--
+   test_database_connection()
+   
+   Test if the database connection is open
+   if not opens it.
+
+   returns 
+   0 if the database was open
+   1 if the database was opened
+   -1 if it failed to open the datbase
+*/
+test_database_connection()
+{
+  const char *conninfo = "dbname=macbind user=macbind password=macbind_qwerty";
+  
+  if (PQstatus(conn) == CONNECTION_OK)
+    return 0;
+  
+  /* if the database connections isn't started */
+  if (conn == NULL){
+    conn = PQconnectdb(conninfo);
+  }
+  
+  /* If bad try to reset*/
+  if (PQstatus(conn) == CONNECTION_BAD){
+    PQreset(conn);
+    
+    /* If still bad try to close and reconnet */
+    if (PQstatus(conn) == CONNECTION_BAD){
+      PQfinish(conn);
+      conn = PQconnectdb(conninfo);
+    }
+    
+    /* If still bad were in trouble */
+    if (PQstatus(conn) == CONNECTION_BAD){
+      log_fatal ("Can't open database.\n");
+      return -1;
+    }
+  }
+  else if (PQstatus(conn) == CONNECTION_OK)
+    return 1;
+  
+  return -1;
+}
+
+
+/* --fli--
+   get_dhcpserver_by_mac(macaddr,interface,dhcpserver)
+   
+   Looks up in the database which dhcpserver and interface
+   that a macaddr is registered with.
+
+   The database returns which network a dhcp servers serves
+   so the interface to use has to be found among the local 
+   interfaces (even aliases).
+*/
+
+int
+get_dhcpserver_by_mac(macaddr,interface,dhcpserver)
+     unsigned char *macaddr;
+     struct sockaddr_in *interface;
+     struct sockaddr_in *dhcpserver;
+{
+  int i;
+  struct localinterface *out;  
+  struct in_addr netmask;
+  struct in_addr network;
+  PGresult *res;
+  PGresult *res2;
+  char qstr[255];
+  char provider_dhcp[16];
+  char provider_net[16];
+  char provider_netmask[16];
+  char *result;
+  int if_found;
+
+  if (test_database_connection() < 0){
+    log_fatal("Can't access database\n");
+    return -1;
+  }
+
+  sprintf(qstr,"SELECT * from V_mapping where mac_addr = \'%02x:%02x:%02x:%02x:%02x:%02x\'"
+	  ,macaddr[0]
+	  ,macaddr[1]
+	  ,macaddr[2]
+	  ,macaddr[3]
+	  ,macaddr[4]
+	  ,macaddr[5]);
+
+  res = PQexec(conn,qstr);
+    
+  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) {
+    log_error("Can't access database %s\n",PQresultErrorMessage(res));
+    PQclear(res);
+    return -1;
+  }
+  
+  /* Did we get any data from the database */
+  if (PQntuples(res) == 0){
+    /* If not then get the default entry */
+    PQclear(res);
+
+    sprintf(qstr,"SELECT * from T_provider where provider_name = 'default'");
+    res = PQexec(conn,qstr);
+    
+    if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) {
+      log_fatal("Can't access database %s\n",PQresultErrorMessage(res));
+      PQclear(res);
+      return -1;
+    }
+    
+    if (PQntuples(res) == 0){
+      /* No default error error */
+      log_fatal("Can't access database %s\n",PQresultErrorMessage(res));
+      PQclear(res);
+      return -1;
+    }
+  }
+  else {
+    /* --fli-- If we are here then we do have an entry in the database.
+       So lets update the timestamp for when it was used last. 
+       We use res2 here since res holds the info from the previous request*/
+    
+
+    sprintf(qstr,"UPDATE T_macbind set mac_reg_date=NOW() where mac_addr = \'%02x:%02x:%02x:%02x:%02x:%02x\'"
+	  ,macaddr[0]
+	  ,macaddr[1]
+	  ,macaddr[2]
+	  ,macaddr[3]
+	  ,macaddr[4]
+	  ,macaddr[5]);
+    
+    res2 = PQexec(conn,qstr);
+
+    if (!res2 || PQresultStatus(res2) != PGRES_COMMAND_OK) {
+      log_error("Can't update timestamp %s\n",PQresultErrorMessage(res2));
+      PQclear(res2);
+    }
+  }
+
+  /* If all is ok now we should have an entry */
+
+  result = PQgetvalue(res,0,PQfnumber(res, "provider_dhcp"));
+  strncpy(provider_dhcp,result,strlen(result)+1);
+
+  result = PQgetvalue(res,0,PQfnumber(res, "provider_net"));
+  strncpy(provider_net,result,strlen(result)+1);
+
+  result = PQgetvalue(res,0,PQfnumber(res, "provider_netmask"));
+  strncpy(provider_netmask,result,strlen(result)+1);
+
+  inet_aton(provider_net,&network);
+  inet_aton(provider_netmask,&netmask);
+  inet_aton(provider_dhcp,&dhcpserver->sin_addr);
+
+  /* Loop all the interfaces and check for the right one */
+  if_found = 0;
+  for (out = localinterfaces; out; out = out -> next) {
+    if (address_in_network(&out->lif, &network, &netmask)){
+      memcpy(&interface->sin_addr,&out->lif,sizeof(struct in_addr));
+      if_found = 1;
+      break;
+    }
+  }
+
+  if (if_found == 0){ 
+    log_error("Can't find matching local interface\n");
+    PQclear(res);
+    return -1;
+  }
+
+  dhcpserver->sin_port = local_port;
+  dhcpserver->sin_family = AF_INET;
+  interface->sin_port = local_port;
+  interface->sin_family = AF_INET;
+    
+
+  PQclear(res);
+  return 0;
+}
+
+
 int main (argc, argv, envp)
 	int argc;
 	char **argv, **envp;
@@ -138,7 +548,7 @@
 	openlog ("dhcrelay", LOG_NDELAY);
 	log_priority = LOG_DAEMON;
 #else
-	openlog ("dhcrelay", LOG_NDELAY, LOG_DAEMON);
+	openlog ("dhcrelay", LOG_NDELAY, LOG_LOCAL5);
 #endif
 
 #if !(defined (DEBUG) || defined (SYSLOG_4_2))
@@ -207,30 +617,35 @@
 			log_info ("isc-dhcrelay-%s", DHCP_VERSION);
 			exit (0);
  		} else {
+		  /*--fli--
+		    We don't need the servers anymore
+		    so no need to create the serverlist
+		    
 			struct hostent *he;
 			struct in_addr ia, *iap = (struct in_addr *)0;
-			if (inet_aton (argv [i], &ia)) {
-				iap = &ia;
-			} else {
-				he = gethostbyname (argv [i]);
+ 			if (inet_aton (argv [i], &ia)) {
+	 			iap = &ia;
+		 	} else {
+			 	he = gethostbyname (argv [i]);
 				if (!he) {
-					log_error ("%s: host unknown",
-						   argv [i]);
-				} else {
-					iap = ((struct in_addr *)
-					       he -> h_addr_list [0]);
-				}
-			}
-			if (iap) {
-				sp = ((struct server_list *)
-				      dmalloc (sizeof *sp, MDL));
-				if (!sp)
-					log_fatal ("no memory for server.\n");
-				sp -> next = servers;
-				servers = sp;
-				memcpy (&sp -> to.sin_addr,
-					iap, sizeof *iap);
+ 					log_error ("%s: host unknown",
+ 						   argv [i]);
+ 				} else {
+ 					iap = ((struct in_addr *)
+ 					       he -> h_addr_list [0]);
+ 				}
+ 			}
+ 			if (iap) {
+ 				sp = ((struct server_list *)
+ 				      dmalloc (sizeof *sp, MDL)); 
+	 			if (!sp)
+ 					log_fatal ("no memory for server.\n");
+	 			sp -> next = servers;
+ 				servers = sp;
+	 			memcpy (&sp -> to.sin_addr,
+ 					iap, sizeof *iap);
 			}
+		  */
  		}
 	}
 
@@ -259,10 +674,14 @@
 	}
 	remote_port = htons (ntohs (local_port) + 1);
   
+	
 	/* We need at least one server. */
+	/* --fli-- 
+	   No we don't 
 	if (!sp) {
 		usage ();
 	}
+	*/
 
 	/* Set up the server sockaddrs. */
 	for (sp = servers; sp; sp = sp -> next) {
@@ -279,6 +698,9 @@
 	/* Discover all the network interfaces. */
 	discover_interfaces (DISCOVER_RELAY);
 
+	/* --fli-- */
+	get_localinterfaces();
+
 	/* Set up the bootp packet handler... */
 	bootp_packet_handler = relay;
 
@@ -324,6 +746,37 @@
 	return 0;
 }
 
+/* Parse options and return the DHCP message type
+ */
+int dhcp_message_type(struct dhcp_packet *packet)
+{
+	int mt = -1; /* message type */
+	unsigned char *data = packet->options;
+
+	if(data[0] != 0x63 && data[1] != 0x82 && data[2] != 0x53 && data[3] != 0x63) {
+		/* wrong magic cookie */
+		log_debug("wrong magic cookie");
+		return -1;
+	}
+	data += 4;
+
+	while(1) {
+		int ot = *data++; /* option type */
+		if(ot == 255) /* end option, length 0 */
+			break;
+		else if(ot == 0) /* pad option, length 0 */
+			continue;
+		else if(ot == 0x35) { /* DHCP Message Type */
+			mt = data[1];
+			break;
+		} else {
+			/* jump to next option */
+			data += 1 + *data;
+		}
+	}
+	return mt;
+}
+
 void relay (ip, packet, length, from_port, from, hfrom)
 	struct interface_info *ip;
 	struct dhcp_packet *packet;
@@ -337,6 +790,10 @@
 	struct interface_info *out;
 	struct hardware hto;
 
+	struct sockaddr_in interface; /* --fli-- Interface to send from */
+	struct sockaddr_in dhcpserver; /* --fli-- DHCP server to send to */
+	struct localinterface *outif;
+
 	if (packet -> hlen > sizeof packet -> chaddr) {
 		log_info ("Discarding packet with invalid hlen.");
 		return;
@@ -351,6 +808,16 @@
 				     sizeof packet -> giaddr))
 				break;
 		}
+		
+		/* --fli--
+		   Check if it is one of our local interfaces
+		*/
+		for (outif = localinterfaces; outif; outif = outif -> next){
+		  if (!memcmp (&outif -> lif,
+			       &packet -> giaddr,
+			       sizeof packet -> giaddr))
+		    out = interfaces;
+                }
 	} else {
 		out = (struct interface_info *)0;
 	}
@@ -389,6 +856,11 @@
 			return;
 		}
 
+		/* --mhe-- Got a reply, remove this request from the queue
+		 * and cancel any timer set
+		 */
+		req_del(packet->chaddr);
+
 		if (send_packet (out,
 				 (struct packet *)0,
 				 packet, length, out -> primary_address,
@@ -416,16 +888,45 @@
 						ip -> primary_address)))
 		return;
 
+
+	/* --fli--
+	   Get the mapping from mac addr to output interface and dhcpserver
+	*/
+	if (get_dhcpserver_by_mac(packet->chaddr,&interface,&dhcpserver) < 0){
+	  log_error("Can't get mapping for macaddr '%02x:%02x:%02x:%02x:%02x:%02x"
+		    ,packet->chaddr[0]
+		    ,packet->chaddr[1]
+		    ,packet->chaddr[2]
+		    ,packet->chaddr[3]
+		    ,packet->chaddr[4]
+		    ,packet->chaddr[5]);
+	  return;
+	}
+
 	/* If giaddr is not already set, Set it so the server can
 	   figure out what net it's from and so that we can later
 	   forward the response to the correct net.    If it's already
 	   set, the response will be sent directly to the relay agent
 	   that set giaddr, so we won't see it. */
+	/* --fli--
+	   Modified to set it to our looked up interface address
+	*/
 	if (!packet -> giaddr.s_addr)
-		packet -> giaddr = ip -> primary_address;
+	  packet -> giaddr = interface.sin_addr;
 
 	/* Otherwise, it's a BOOTREQUEST, so forward it to all the
 	   servers. */
+
+	/* --fli-- 
+	   In the sequence below the dhcp packet is sent to the server
+	   idenified by 
+	   sp->to (a sockaddr_in) 
+	   and is sent from an interface with the address 
+	   ip->primary_address 
+
+	   These are the values that need to be looked up in a database 
+	*/
+	/*
 	for (sp = servers; sp; sp = sp -> next) {
 		if (send_packet ((fallback_interface
 				  ? fallback_interface : interfaces),
@@ -440,8 +941,32 @@
 			       inet_ntoa (sp -> to.sin_addr));
 			++client_packets_relayed;
 		}
+	}
+	*/
+	
+	/* --fli--
+	   packet->chaddr contains the macaddr of the host that is asking for the
+	   dhcp server.
+	*/
+	if (send_packet ((fallback_interface
+			  ? fallback_interface : interfaces),
+			 (struct packet *)0,
+			 packet, length, interface.sin_addr,
+			 &dhcpserver, (struct hardware *)0) < 0) {
+	  ++client_packet_errors;
+	} else {
+	  log_debug ("forwarded BOOTREQUEST for %s to %s",
+		     print_hw_addr (packet -> htype, packet -> hlen,
+				    packet -> chaddr),
+		     inet_ntoa (dhcpserver.sin_addr));
+	    ++client_packets_relayed;
+		/* --mhe-- We sent a request to the server, now add the
+		 * request to the queue, and set an alarm for it. We only
+		 * queue the request if this is a DISCOVER message.
+		 */
+		if(dhcp_message_type(packet) == 1)
+			req_add(packet->chaddr);
 	}
-				 
 }
 
 static void usage ()
