--- db.c.orig	Sat Sep 30 19:39:58 2000
+++ db.c	Tue Apr 13 14:39:50 2004
@@ -41,6 +41,7 @@
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>
+#include <pthread.h>

 #include "gnuc.h"
 #ifdef HAVE_OS_PROTO_H
@@ -54,18 +55,9 @@
 #include "report.h"
 #include "util.h"

-#define HASHSIZE (2 << 15)
-
 #define NEWACTIVITY_DELTA (6*30*24*60*60)	/* 6 months in seconds */
 #define FLIPFLIP_DELTA (24*60*60)		/* 24 hours in seconds */

-/* Ethernet info */
-struct einfo {
-	u_char e[6];		/* ether address */
-	char h[34];		/* simple hostname */
-	time_t t;		/* timestamp */
-};
-
 /* Address info */
 struct ainfo {
 	u_int32_t a;		/* ip address */
@@ -78,22 +70,69 @@
 /* Address hash table */
 static struct ainfo ainfo_table[HASHSIZE];

+
+/* Ethernet hash table */
+struct einfo einfo_table[HASHSIZE];
+int et_cnt = 0;
+
 static void alist_alloc(struct ainfo *);
 int cmpeinfo(const void *, const void *);
-static struct einfo *elist_alloc(u_int32_t, u_char *, time_t, char *);
+static struct einfo *elist_alloc(u_int32_t, u_char *, time_t, char *, char *);
 static struct ainfo *ainfo_find(u_int32_t);
+static struct einfo *einfo_find(u_char *);
 static void check_hname(struct ainfo *);
 struct ainfo *newainfo(void);

+pthread_mutex_t mtx_einfo, mtx_ainfo;
+
 int
-ent_add(register u_int32_t a, register u_char *e, time_t t, register char *h)
+ent_add(register u_int32_t a, register u_char *e, time_t t, register char *h, register char *interface)
 {
 	register struct ainfo *ap;
-	register struct einfo *ep;
+	struct einfo *ep;
 	register int i;
 	register u_int len;
 	u_char *e2;
 	time_t t2;
+	register evt_type event = 0;
+	char *if2 = NULL;
+
+	pthread_mutex_lock(&mtx_einfo);
+
+	/* Lookup ethernet address */
+	ep = einfo_find(e);
+
+	/* New einfo? (elist_alloc makes 16 at a time -- no thanks) */
+	if (ep == NULL && ! initializing) {
+		if (et_cnt >= HASHSIZE) {
+			syslog(LOG_ERR, "ERROR: einfo_table too big");
+		} else {
+		  ep = &einfo_table[et_cnt++];
+		  BCOPY(e, ep->e, sizeof(ep->e));
+		  if (h == NULL)
+		    h = getsname(a);
+		  if (h != NULL)
+		    strncpy(ep->h, h, sizeof(ep->h));
+		  ep->t = t;
+		  strncpy(ep->iface, interface, sizeof(ep->iface));
+		  event |= ETHER_NEW;
+		  e2 = NULL;
+		  t2 = 0;
+		}
+	} else if (! initializing) {
+		if (strncmp(ep->iface, interface, sizeof(ep->iface)) != 0) {
+			event |= ETHER_IFCHG;
+			asprintf(&if2, "%s", ep->iface);
+			memset((char *)ep->iface, 0, sizeof(ep->iface));
+			BCOPY(interface, ep->iface, sizeof(ep->iface));
+			e2 = NULL;
+			t2 = ep->t;
+			ep->t = t;
+		}
+	}
+
+	pthread_mutex_unlock(&mtx_einfo);
+	pthread_mutex_lock(&mtx_ainfo);

 	/* Lookup ip address */
 	ap = ainfo_find(a);
@@ -101,28 +140,30 @@
 	/* Check for the usual case first */
 	if (ap->ecount > 0) {
 		ep = ap->elist[0];
-		if (MEMCMP(e, ep->e, 6) == 0) {
+		if (MEMCMP(e, ep->e, sizeof(ep->e)) == 0) {
 			if (t - ep->t > NEWACTIVITY_DELTA) {
-				report("new activity", a, e, NULL, &t, &ep->t);
+			        event |= ACTIVITY_NEW;
+				e2 = NULL;
+				t2 = ep->t;
 				check_hname(ap);
 			}
 			ep->t = t;
-			return (1);
 		}
 	}

 	/* Check for a virgin ainfo record */
 	if (ap->ecount == 0) {
 		ap->ecount = 1;
-		ap->elist[0] = elist_alloc(a, e, t, h);
-		report("new station", a, e, NULL, &t, NULL);
-		return (1);
+		ap->elist[0] = elist_alloc(a, e, t, h, interface);
+		event |= IP_NEW;
+		e2 = NULL;
+		t2 = 0;
 	}

 	/* Check for a flip-flop */
 	if (ap->ecount > 1) {
 		ep = ap->elist[1];
-		if (MEMCMP(e, ep->e, 6) == 0) {
+		if (MEMCMP(e, ep->e, sizeof(ep->e)) == 0) {
 			/*
 			 * Suppress report when less than
 			 * FLIPFLOP_DELTA and one of the two ethernet
@@ -131,48 +172,76 @@
 			t2 = ap->elist[0]->t;
 			e2 = ap->elist[0]->e;
 			if (t - t2 < FLIPFLIP_DELTA &&
-			    (isdecnet(e) || isdecnet(e2)))
+			    (isdecnet(e) || isdecnet(e2))) {
 				dosyslog(LOG_INFO,
 				    "suppressed DECnet flip flop", a, e, e2);
-			else
-				report("flip flop", a, e, e2, &t, &t2);
+				event |= FLIPFLOP_DECNET;
+			} else {
+				event |= FLIPFLOP;
+			}
+
 			ap->elist[1] = ap->elist[0];
 			ap->elist[0] = ep;
 			ep->t = t;
 			check_hname(ap);
-			return (1);
 		}
 	}

 	for (i = 2; i < ap->ecount; ++i) {
 		ep = ap->elist[i];
-		if (MEMCMP(e, ep->e, 6) == 0) {
+		if (MEMCMP(e, ep->e, sizeof(ep->e)) == 0) {
 			/* An old entry comes to life */
 			e2 = ap->elist[0]->e;
 			t2 = ap->elist[0]->t;
 			dosyslog(LOG_NOTICE, "reused old ethernet address",
 			    a, e, e2);
+			event |= IP_ETHER_REUSE;
 			/* Shift entries down */
 			len = i * sizeof(ap->elist[0]);
 			BCOPY(&ap->elist[0], &ap->elist[1], len);
 			ap->elist[0] = ep;
 			ep->t = t;
 			check_hname(ap);
-			return (1);
 		}
 	}

-	/* New ether address */
-	e2 = ap->elist[0]->e;
-	t2 = ap->elist[0]->t;
-	report("changed ethernet address", a, e, e2, &t, &t2);
-	/* Make room at head of list */
-	alist_alloc(ap);
-	len = ap->ecount * sizeof(ap->elist[0]);
-	BCOPY(&ap->elist[0], &ap->elist[1], len);
-	ap->elist[0] = elist_alloc(a, e, t, h);
-	++ap->ecount;
-	return (1);
+	/* as originally written, any of these conditions would cause this
+	 * block never to be reached. ETHER_NEW and ETHER_IFCHG have been added to that list.
+	 */
+	if (event & ~(ACTIVITY_NEW | IP_NEW | FLIPFLOP | FLIPFLOP_DECNET | IP_ETHER_REUSE | ETHER_NEW | ETHER_IFCHG)) {
+	  /* New ether address */
+	  e2 = ap->elist[0]->e;
+	  t2 = ap->elist[0]->t;
+	  event |= IP_ETHERCHG;
+	  /* Make room at head of list */
+	  alist_alloc(ap);
+	  len = ap->ecount * sizeof(ap->elist[0]);
+	  BCOPY(&ap->elist[0], &ap->elist[1], len);
+	  ap->elist[0] = elist_alloc(a, e, t, h, interface);
+	  ++ap->ecount;
+	}
+
+	pthread_mutex_unlock(&mtx_ainfo);
+
+	report(event, a, e, e2, &t, &t2, interface, if2);
+
+	if (if2 != NULL)
+	  free(if2);
+
+	return(1);
+}
+
+static struct einfo *
+einfo_find(register u_char *e)
+{
+	register int i;
+
+	for (i=0; i < et_cnt; i++) {
+		if (MEMCMP(einfo_table[i].e, e, sizeof(einfo_table[i].e)) == 0)
+			return(&einfo_table[i]);
+	}
+
+	return(NULL);
 }

 static struct ainfo *
@@ -259,7 +328,7 @@
 /* Allocate and initialize a elist struct */
 static struct einfo *
 elist_alloc(register u_int32_t a, register u_char *e, register time_t t,
-    register char *h)
+    register char *h, register char *interface)
 {
 	register struct einfo *ep;
 	register u_int size;
@@ -280,12 +349,16 @@

 	ep = elist++;
 	--eleft;
-	BCOPY(e, ep->e, 6);
+	BCOPY(e, ep->e, sizeof(ep->e));
 	if (h == NULL && !initializing)
 		h = getsname(a);
-	if (h != NULL && !isdigit((int)*h))
-		strcpy(ep->h, h);
+	if (h != NULL)
+		strncpy(ep->h, h, sizeof(ep->h));
 	ep->t = t;
+
+	if (interface != NULL)
+	  strncpy(ep->iface, interface, sizeof(ep->iface));
+
 	return (ep);
 }

@@ -301,10 +374,10 @@
 		return;
 	ep = ap->elist[0];
 	h = getsname(ap->a);
-	if (!isdigit((int)*h) && strcmp(h, ep->h) != 0) {
+	if (h != NULL && strcmp(h, ep->h) != 0) {
 		syslog(LOG_INFO, "hostname changed %s %s %s -> %s",
 		    intoa(ap->a), e2str(ep->e), ep->h, h);
-		strcpy(ep->h, h);
+		strncpy(ep->h, h, sizeof(ep->h));
 	}
 }

