Re: arp-acl + Linux kernel 2.2.x = broken?

From: Henrik Nordstrom <hno@dont-contact.us>
Date: Sun, 17 Oct 1999 21:10:25 +0200

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