Here's a cleaned up version of your patch which works against FreeBSD-CURRENT.
I have mixed feelings about committing this; it raises the bar somewhat
but it does not completely close the hole.

Let's say Alice and Bob are talking to each other with IPv4 over Ethernet.

Making the arp cache entry permanent is probably a bad idea for Alice. She
should be allowed to expire out the entry at some point, which admittedly
provides a window for the cache poisoning attack, but the potential for
abuse is increased if the attacker is able to race Bob in creating the
original entry in Alice's cache.

I've attempted to mitigate this by using a simple linearly computed threshold,
but this still doesn't really solve the problem.

Might suggest we review the following papers on the subject:
        http://www.cs.utexas.edu/users/chuang/comnet0103.pdf
        http://www.acsac.org/1999/papers/fri-b-0830-dutta.pdf
        http://alor.antifork.org/projects/s-arp/article.pdf
And code:
        http://alor.antifork.org/projects/s-arp/sarpd-0.0.9-devel.tar.gz

The daemon above could probably be adapted for *BSD use by setting
IFF_NOARP on an Ethernet network interface, removing the subnet route
and re-adding it with RTF_XRESOLVE|RTX_CLONING, and adding bpf/rtsock
support to the userland code.

Also, please clarify your license regarding this code, I am assuming that
it was submitted under the BSD license.

Regards,
BMS
Index: src/sys/conf/options
===================================================================
RCS file: /home/ncvs/src/sys/conf/options,v
retrieving revision 1.457
diff -u -p -r1.457 options
--- src/sys/conf/options        29 Jun 2004 02:30:12 -0000      1.457
+++ src/sys/conf/options        3 Jul 2004 13:16:11 -0000
@@ -305,6 +305,7 @@ ALTQ_CDNR           opt_altq.h
 ALTQ_PRIQ              opt_altq.h
 ALTQ_NOPCC             opt_altq.h
 ALTQ_DEBUG             opt_altq.h
+ARP_VERIFY_REPLY       opt_inet.h
 BOOTP                  opt_bootp.h
 BOOTP_COMPAT           opt_bootp.h
 BOOTP_NFSROOT          opt_bootp.h
Index: src/sys/netinet/if_ether.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/if_ether.c,v
retrieving revision 1.128
diff -u -p -r1.128 if_ether.c
--- src/sys/netinet/if_ether.c  13 Jun 2004 10:54:36 -0000      1.128
+++ src/sys/netinet/if_ether.c  3 Jul 2004 15:50:50 -0000
@@ -57,8 +57,8 @@
 #include <net/route.h>
 #include <net/netisr.h>
 #include <net/if_llc.h>
-#ifdef BRIDGE
 #include <net/ethernet.h>
+#ifdef BRIDGE
 #include <net/bridge.h>
 #endif
 
@@ -95,8 +95,16 @@ struct llinfo_arp {
        struct  mbuf *la_hold;  /* last packet until resolved/timeout */
        u_short la_preempt;     /* countdown for pre-expiry arps */
        u_short la_asked;       /* #times we QUERIED following expiration */
-#define la_timer la_rt->rt_rmx.rmx_expire /* deletion time in seconds */
+#ifdef ARP_VERIFY_REPLY
+       struct  in_addr la_opaddr;      /* XXX original protocol level
+                                        * address for verification */
+       u_short la_ack;         /* #times original MAC acknowledged an
+                                * ARP reply which we then RE-QUERIED */
+       u_char  la_olladdr[ETHER_ADDR_LEN];     /* XXX original MAC for
+                                                * verification */
+#endif
 };
+#define la_timer la_rt->rt_rmx.rmx_expire /* deletion time in seconds */
 
 static LIST_HEAD(, llinfo_arp) llinfo_arp;
 
@@ -106,6 +114,9 @@ static int  arp_allocated;
 static int     arp_maxtries = 5;
 static int     useloopback = 1; /* use loopback interface for local traffic */
 static int     arp_proxyall = 0;
+#ifdef ARP_VERIFY_REPLY
+static int     arp_verifyrep = 0;
+#endif
 static struct callout arp_callout;
 
 SYSCTL_INT(_net_link_ether_inet, OID_AUTO, maxtries, CTLFLAG_RW,
@@ -114,6 +125,10 @@ SYSCTL_INT(_net_link_ether_inet, OID_AUT
           &useloopback, 0, "");
 SYSCTL_INT(_net_link_ether_inet, OID_AUTO, proxyall, CTLFLAG_RW,
           &arp_proxyall, 0, "");
+#ifdef ARP_VERIFY_REPLY
+SYSCTL_INT(_net_link_ether_inet, OID_AUTO, verify_reply, CTLFLAG_RW,
+          &arp_verifyrep, 0, "Verify untrusted ARP traffic");
+#endif
 
 static void    arp_init(void);
 static void    arp_rtrequest(int, struct rtentry *, struct rt_addrinfo *);
@@ -605,18 +620,104 @@ match:
                                    ifp->if_xname);
                        goto reply;
                }
+#ifdef ARP_VERIFY_REPLY
+               if (arp_verifyrep && rt->rt_expire &&
+                   sdl->sdl_alen == ETHER_ADDR_LEN && /* IPv4 check ok */
+                   la->la_ack == 1 &&
+                   !bcmp(ar_sha(ah), LLADDR(sdl), ETHER_ADDR_LEN)) {
+                       /*
+                        * We have seen gratuitous ARP from this MAC for this
+                        * protocol level address on the wire in the past.
+                        * Check if the learned address was verified by a
+                        * further unicast ARP reply/request sequence.
+                        */
+                       if (!bcmp(ar_sha(ah), la->la_olladdr, ETHER_ADDR_LEN)) {
+                               /*
+                                * The source MAC address of this reply
+                                * matches the previously learned entry.
+                                */
+                               if (bootverbose && log_arp_movements)
+                                       log(LOG_INFO,
+"arp: reply for %s from %*D on %s verified ok\n",
+                                           inet_ntoa(isaddr),
+                                           ETHER_ADDR_LEN,
+                                           (u_char *)ar_sha(ah), ":",
+                                           ifp->if_xname);
+                               la->la_ack = 0;
+                       } else {
+                               /*
+                                * Someone is now attempting to spoof a
+                                * learned protocol level address in the
+                                * current gratuitous reply being handled.
+                                *
+                                * Clamp expunge period to a threshold
+                                * derived from the following formula.
+                                */
+                               int expire = (((uint32_t)arpt_keep +
+                                   time_second) * la->la_ack) / 2;
+                               rt->rt_expire = MAX(rt->rt_expire, expire);
+
+                               /*
+                                * Log an appropriate message, and
+                                * discard the incoming reply.
+                                */
+                               log(LOG_ERR,
+"arp: untrusted reply for %s from %*D on %s (learned MAC is %*D)\n",
+                                   inet_ntoa(la->la_opaddr),
+                                   ETHER_ADDR_LEN,
+                                   (u_char *)&la->la_olladdr, ":",
+                                   ifp->if_xname,
+                                   ETHER_ADDR_LEN,
+                                   (u_char *)ar_sha(ah), ":");
+                               bzero(&la->la_olladdr, sizeof(la->la_olladdr));
+                               la->la_ack = 0;
+                               m_free(m);
+                               return;
+                       }
+               }
+#endif /* ARP_VERIFY_REPLY */
                if (sdl->sdl_alen &&
                    bcmp(ar_sha(ah), LLADDR(sdl), sdl->sdl_alen)) {
                        if (rt->rt_expire) {
-                           if (log_arp_movements)
-                               log(LOG_INFO, "arp: %s moved from %*D to %*D on %s\n",
-                                   inet_ntoa(isaddr),
+#ifdef ARP_VERIFY_REPLY
+                       if (arp_verifyrep) {
+                               /*
+                                * If this protocol-level and link-level
+                                * address was not previously verified,
+                                * verify it now by sending another
+                                * unicast ARP request back to the
+                                * originator.
+                                */
+                               if (la->la_ack == 0) {
+                                       if (bootverbose && log_arp_movements)
+                                               log(LOG_INFO,
+"arp: verifying reply for %s from %*D on %s\n", inet_ntoa(isaddr),
+                                                   ETHER_ADDR_LEN,
+                                                   (u_char *)ar_sha(ah), ":",
+                                                   ifp->if_xname);
+
+                                       arprequest(ifp, &myaddr, &isaddr,
+                                           IF_LLADDR(ifp));
+                                       bcopy(ar_sha(ah), &la->la_olladdr,
+                                           ETHER_ADDR_LEN);
+                                       bcopy(&isaddr, &la->la_opaddr,
+                                           ETHER_ADDR_LEN);
+                                       la->la_ack++;
+                                       la->la_asked++;
+                                       m_free(m);
+                                       return;
+                               }
+                       } else
+#endif /* ARP_VERIFY_REPLY */
+                       if (log_arp_movements)
+                               log(LOG_INFO,
+"arp: %s moved from %*D to %*D on %s\n", inet_ntoa(isaddr),
                                    ifp->if_addrlen, (u_char *)LLADDR(sdl), ":",
                                    ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
                                    ifp->if_xname);
                        } else {
                            log(LOG_ERR,
-                               "arp: %*D attempts to modify permanent entry for %s on 
%s\n",
+"arp: %*D attempts to modify permanent entry for %s on %s\n",
                                ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
                                inet_ntoa(isaddr), ifp->if_xname);
                            goto reply;
_______________________________________________
[EMAIL PROTECTED] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-net
To unsubscribe, send any mail to "[EMAIL PROTECTED]"

Reply via email to