Yes, it is broken.
I have seen a patch floating around somewhere.. lets see.. ah there it
is. Message and patch from Damien Miller attached.
-- Henrik Nordstrom Squid hacker Petrov Sergei wrote: > > Who already had such problem? > If yes, how are you to solve a it? > > Petrov Sergei
attached mail follows:
Hi,
I just noticed that ARP acls were failing with squid-2.2-STABLE4
running on Linux 2.2.12. A bit of investigation revealed why:
It seems that 2.2.x uses a per-interface arp/neighbour cache, whereas
2.0.x uses a unified cache. Under 2.2.x you are required to specify a
interface name when looking up ARP table entries with SIOCGARP.
Squid's ARP acl code did not do this.
Attached is a patch which Works For Me. It retains the existing
behaviour under 2.0.x kernels by doing the ARP lookup with a blank
interface name. 
If this fails, it will then try the ARP lookup for each interface that
the kernel knows about (up to a max of 32) excluding loopback and
alias interfaces.
Please CC me any followups as I am not on any of the squid lists.
Regards,
Damien Miller
--- squid-2.2.STABLE4-orig/src/acl.c	Wed Jul  7 12:12:48 1999
+++ squid-2.2.STABLE4/src/acl.c	Thu Sep 23 13:00:45 1999
@@ -2285,27 +2283,122 @@
 {
     struct arpreq arpReq;
     struct sockaddr_in ipAddr;
+    unsigned char ifbuffer[sizeof(struct ifreq) * 64];
+    struct ifconf ifc;
+    struct ifreq *ifr;
+    int offset;
     splayNode **Top = dataptr;
+
+    /* The linux kernel 2.2 maintains per interface ARP caches and thus */
+    /* requires an interface name when doing ARP queries. */
+
+    /* The older 2.0 kernels appear to use a unified ARP cache, and require */
+    /* an empty interface name */
+    
+    /* To support both, we attempt the lookup with a blank interface name */
+    /* first. If that does not succeed, the try each interface in turn */
+        
+    /* Set up structures for ARP lookup with blank interface name */
     ipAddr.sin_family = AF_INET;
     ipAddr.sin_port = 0;
     ipAddr.sin_addr = c;
+    memset(&arpReq, '\0', sizeof(arpReq));
     memcpy(&arpReq.arp_pa, &ipAddr, sizeof(struct sockaddr_in));
-    arpReq.arp_dev[0] = '\0';
-    arpReq.arp_flags = 0;
-    /* any AF_INET socket will do... gives back hardware type, device, etc */
-    if (ioctl(HttpSockets[0], SIOCGARP, &arpReq) == -1) {
-	debug(28, 1) ("ARP query failed - %d", errno);
-	return 0;
-    } else if (arpReq.arp_ha.sa_family != ARPHRD_ETHER) {
-	debug(28, 1) ("Non-ethernet interface returned from ARP query - %d",
-	    arpReq.arp_ha.sa_family);
-	/* update here and MAC address parsing to handle non-ethernet */
-	return 0;
-    } else
-	*Top = splay_splay(&arpReq.arp_ha.sa_data, *Top, aclArpCompare);
-    debug(28, 3) ("aclMatchArp: '%s' %s\n",
-	inet_ntoa(c), splayLastResult ? "NOT found" : "found");
-    return !splayLastResult;
+
+    /* Query ARP table */
+    if (ioctl(HttpSockets[0], SIOCGARP, &arpReq) != -1) {
+        /* Skip non-ethernet interfaces */
+        if (arpReq.arp_ha.sa_family != ARPHRD_ETHER) {
+            return(0);
+        }
+
+        debug(28, 4) ("Got address %02x:%02x:%02x:%02x:%02x:%02x\n", 
+        arpReq.arp_ha.sa_data[0] & 0xff, arpReq.arp_ha.sa_data[1] & 0xff, 
+        arpReq.arp_ha.sa_data[2] & 0xff, arpReq.arp_ha.sa_data[3] & 0xff, 
+        arpReq.arp_ha.sa_data[4] & 0xff, arpReq.arp_ha.sa_data[5] & 0xff);
+
+        /* Do lookup */
+        *Top = splay_splay(&arpReq.arp_ha.sa_data, *Top, aclArpCompare);
+
+        debug(28, 3) ("aclMatchArp: '%s' %s\n",
+        inet_ntoa(c), splayLastResult ? "NOT found" : "found");
+        
+        return(splayLastResult == 0);
+    }
+
+    /* lookup list of interface names */
+    ifc.ifc_len = sizeof(ifbuffer);
+    ifc.ifc_buf = ifbuffer;
+    if (ioctl(HttpSockets[0], SIOCGIFCONF, &ifc) < 0) {
+        debug(28, 1) ("Attempt to retrieve interface list failed - %s[%d]\n", strerror(errno), errno);
+        return(0);
+    }
+
+    if (ifc.ifc_len > sizeof(ifbuffer)) {
+        debug(28, 1) ("Interface list too long - %d\n", ifc.ifc_len);
+        return(0);
+    }
+    
+    /* Attempt ARP lookup on each interface */
+    offset = 0;
+    while (offset < ifc.ifc_len) {
+        ifr = (struct ifreq*) (ifbuffer + offset);
+        offset += sizeof(*ifr);
+        
+        /* Skip loopback and aliased interfaces */
+        if ((strncmp(ifr->ifr_name, "lo", 2) == 0) || 
+            (strchr(ifr->ifr_name, ':') != NULL)) {
+            continue;
+        }
+                    
+        debug(28, 4) ("Looking up ARP address for %s on %s\n", inet_ntoa(c), ifr->ifr_name);
+
+        /* Set up structures for ARP lookup */
+        ipAddr.sin_family = AF_INET;
+        ipAddr.sin_port = 0;
+        ipAddr.sin_addr = c;
+        memset(&arpReq, '\0', sizeof(arpReq));
+        memcpy(&arpReq.arp_pa, &ipAddr, sizeof(struct sockaddr_in));
+        strncpy(arpReq.arp_dev, ifr->ifr_name, sizeof(arpReq.arp_dev) - 1);
+        arpReq.arp_dev[sizeof(arpReq.arp_dev) - 1] = '\0';
+
+        /* Query ARP table */
+        if (ioctl(HttpSockets[0], SIOCGARP, &arpReq) != -1) {
+            /* Skip non-ethernet interfaces */
+            if (arpReq.arp_ha.sa_family != ARPHRD_ETHER) {
+                continue;
+            }
+            
+            debug(28, 4) ("Got address %02x:%02x:%02x:%02x:%02x:%02x on %s\n", 
+            arpReq.arp_ha.sa_data[0] & 0xff, arpReq.arp_ha.sa_data[1] & 0xff, 
+            arpReq.arp_ha.sa_data[2] & 0xff, arpReq.arp_ha.sa_data[3] & 0xff, 
+            arpReq.arp_ha.sa_data[4] & 0xff, arpReq.arp_ha.sa_data[5] & 0xff, 
+            ifr->ifr_name);
+
+            /* Do lookup */
+            *Top = splay_splay(&arpReq.arp_ha.sa_data, *Top, aclArpCompare);
+
+            /* Return if match, otherwise continue to other interfaces */
+            if (splayLastResult == 0) {
+                debug(28, 3) ("aclMatchArp: %s found on %s\n", inet_ntoa(c), ifr->ifr_name);
+                return(1);
+            }
+            
+            /* Should we stop looking here? Can the same IP address exist */
+            /* on multiple interfaces? */
+            
+        } else {
+            /* Query failed */
+            /* Do not log failed lookups or "device not supported" */
+            if ((errno != ENXIO) && (errno != ENODEV)) {
+                debug(28, 1) ("ARP query failed - %s %s[%d]\n", ifr->ifr_name, strerror(errno), errno);
+            }
+        }
+    }
+
+    /* Address was not found on any interface */
+    debug(28, 3) ("aclMatchArp: %s NOT found\n", inet_ntoa(c));
+    return 0;
 }
 
 static int
Received on Sun Oct 17 1999 - 15:23:26 MDT
This archive was generated by hypermail pre-2.1.9 : Tue Dec 09 2003 - 16:48:56 MST