--- if_ether.c.old	Tue Dec  2 19:57:08 2003
+++ if_ether.c	Wed Dec  3 10:41:38 2003
@@ -40,6 +40,10 @@
  *	add "inuse/lock" bit (or ref. count) along with valid bit
  */
 
+/*
+ * ARP-posion Antidote code (c) 2003 by Bert <bert@furry.ru>
+ */
+
 #include "opt_inet.h"
 #include "opt_bdg.h"
 
@@ -82,6 +86,10 @@
 static int arpt_keep = (20*60); /* once resolved, good for 20 more minutes */
 static int arpt_down = 20;	/* once declared down, don't send for 20 sec */
 
+static int arp_secure = 1;
+static int arpt_debug = 0;
+
+
 SYSCTL_INT(_net_link_ether_inet, OID_AUTO, prune_intvl, CTLFLAG_RW,
 	   &arpt_prune, 0, "");
 SYSCTL_INT(_net_link_ether_inet, OID_AUTO, max_age, CTLFLAG_RW, 
@@ -96,6 +104,9 @@
 	struct	rtentry *la_rt;
 	struct	mbuf *la_hold;		/* last packet until resolved/timeout */
 	long	la_asked;		/* last time we QUERIED for this addr */
+	long	la_ack;			/* Arp reply acknowledgement flag */	
+        u_char  check_eth[ETHER_ADDR_LEN]; /* MAC address, before it changing */
+	struct in_addr	check_ip;
 #define la_timer la_rt->rt_rmx.rmx_expire /* deletion time in seconds */
 };
 
@@ -115,6 +126,14 @@
 SYSCTL_INT(_net_link_ether_inet, OID_AUTO, proxyall, CTLFLAG_RW,
 	   &arp_proxyall, 0, "");
 
+/* sysctl arp antidote variable. Default is "on" */
+SYSCTL_INT(_net_link_ether_inet, OID_AUTO, arp_antidote, CTLFLAG_RW,
+	   &arp_secure, 0, "");
+	   
+/* sysctl arp debug variable. Default is "off" */
+SYSCTL_INT(_net_link_ether_inet, OID_AUTO, arp_debug, CTLFLAG_RW,
+	   &arpt_debug, 0, "");
+
 static void	arp_rtrequest __P((int, struct rtentry *, struct rt_addrinfo *));
 static void	arprequest __P((struct ifnet *,
 			struct in_addr *, struct in_addr *, u_char *));
@@ -663,14 +682,73 @@
 			    ifp->if_name, ifp->if_unit);
 		    goto reply;
 		}
+		
+		/* Print announced packet if debug is enabled */
+		if (sdl->sdl_alen && arpt_debug) {
+                  log(LOG_INFO, "arp: Got MAC address %*D from %s via %s%d\n", 
+		      ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
+		      inet_ntoa(isaddr),
+		      ifp->if_name, ifp->if_unit);
+		}
+		
+		/* Check possible reply with old MAC-address*/
+		if (arp_secure && rt->rt_expire && 
+		    sdl->sdl_alen && la->la_ack == 1 && 
+		    !bcmp(ar_sha(ah), LLADDR(sdl), sdl->sdl_alen)) 
+		{
+		  /* Got old MAC address. Check it with saved */
+		  if (!bcmp(ar_sha(ah), la->check_eth, sdl->sdl_alen)) {
+		    /* Addresses are equal. MAC address verified */
+		    if (arpt_debug)
+                      log(LOG_INFO, "arp: MAC address %*D from %s verified.\n",
+		          ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
+		          inet_ntoa(isaddr));
+		    la->la_ack = 0;
+		  } else {
+		    /* Make an arp entry "static" */
+		    rt->rt_expire = 0;
+		    /* Reply with another MAC */
+                    log(LOG_ERR, "arp: ARP poison detected!, attacker from %*D trying to infect %*D (%s) in my ARP table!\n",
+		      sdl->sdl_alen, (u_char *)&la->check_eth, ":",
+		      sdl->sdl_alen, (u_char *)ar_sha(ah), ":",
+		      inet_ntoa(la->check_ip));
+		      
+		    /* Clear cached MAC */
+		    bzero(&la->check_eth, sizeof(la->check_eth));
+		    la->la_ack = 0;
+                    m_free(m);
+                    return;
+		  }
+		}
+		
 		if (sdl->sdl_alen &&
 		    bcmp(ar_sha(ah), LLADDR(sdl), sdl->sdl_alen)) {
-			if (rt->rt_expire)
-			    log(LOG_INFO, "arp: %s moved from %*D to %*D on %s%d\n",
-				inet_ntoa(isaddr),
-				ifp->if_addrlen, (u_char *)LLADDR(sdl), ":",
-				ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
-				ifp->if_name, ifp->if_unit);
+			if (rt->rt_expire) {
+			    if (arp_secure) {
+                              if (la->la_ack == 0) {
+			        if (arpt_debug)
+                                  log(LOG_INFO, "arp: Got new MAC address %*D from %s (%s%d). Now verifying.\n",
+				      ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
+				      inet_ntoa(isaddr),
+				      ifp->if_name, ifp->if_unit);
+				/* Check old MAC address for alive */
+                                arprequest(ifp, &myaddr, &isaddr, IF_LLADDR(ifp));
+				(void)memcpy((u_char *)&la->check_eth, (u_char *)ar_sha(ah), sdl->sdl_alen);
+				la->check_ip.s_addr = isaddr.s_addr;
+				
+                                la->la_ack++;
+				la->la_asked++;
+                                m_free(m);
+                                return;
+			      }
+			    } else {
+			       log(LOG_INFO, "arp: %s moved from %*D to %*D on %s%d\n",
+			          inet_ntoa(isaddr),
+			  	  ifp->if_addrlen, (u_char *)LLADDR(sdl), ":",
+				  ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
+				  ifp->if_name, ifp->if_unit);
+			    }
+			}
 			else {
 			    log(LOG_ERR,
 				"arp: %*D attempts to modify permanent entry for %s on %s%d\n",

