2 * DEBUG: section 89 EUI-48 Lookup
3 * AUTHOR: Duane Wessels
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
32 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
40 #include "eui/Eui48.h"
42 #include "ip/Address.h"
46 /* START Legacy includes pattern */
47 /* TODO: clean this up so we dont have per-OS requirements.
48 The files are checked for existence individually
55 Ip::Address arp_pa
; /* protocol address */
57 struct sockaddr arp_ha
; /* hardware address */
58 int arp_flags
; /* flags */
66 #include <sys/param.h>
69 /* required by Solaris */
70 #include <sys/sockio.h>
73 #include <sys/sysctl.h>
76 #include <net/route.h>
82 #include <net/if_arp.h>
85 #include <net/if_dl.h>
87 #if HAVE_NETINET_IF_ETHER_H
88 #include <netinet/if_ether.h>
91 #include <sys/ioctl.h>
94 /* ==== BEGIN EUI LOOKUP SUPPORT ============================================= */
97 * From: dale@server.ctam.bitmcnit.bryansk.su (Dale)
98 * To: wessels@nlanr.net
99 * Subject: Another Squid patch... :)
100 * Date: Thu, 04 Dec 1997 19:55:01 +0300
101 * ============================================================================
103 * Working on setting up a proper firewall for a network containing some
104 * Win'95 computers at our Univ, I've discovered that some smart students
105 * avoid the restrictions easily just changing their IP addresses in Win'95
106 * Contol Panel... It has been getting boring, so I took Squid-1.1.18
107 * sources and added a new acl type for hard-wired access control:
109 * acl <name> arp <Ethernet address> ...
113 * acl students arp 00:00:21:55:ed:22 00:00:21:ff:55:38
115 * NOTE: Linux code by David Luyer <luyer@ucs.uwa.edu.au>.
116 * Original (BSD-specific) code no longer works.
117 * Solaris code by R. Gancarz <radekg@solaris.elektrownia-lagisza.com.pl>
121 Eui::Eui48::decode(const char *asc
)
123 int a1
= 0, a2
= 0, a3
= 0, a4
= 0, a5
= 0, a6
= 0;
125 if (sscanf(asc
, "%x:%x:%x:%x:%x:%x", &a1
, &a2
, &a3
, &a4
, &a5
, &a6
) != 6) {
126 debugs(28, DBG_CRITICAL
, "Decode EUI-48: Invalid ethernet address '" << asc
<< "'");
128 return false; /* This is not valid address */
131 eui
[0] = (u_char
) a1
;
132 eui
[1] = (u_char
) a2
;
133 eui
[2] = (u_char
) a3
;
134 eui
[3] = (u_char
) a4
;
135 eui
[4] = (u_char
) a5
;
136 eui
[5] = (u_char
) a6
;
138 debugs(28, 4, "id=" << (void*)this << " decoded " << asc
);
143 Eui::Eui48::encode(char *buf
, const int len
)
145 if (len
< SZ_EUI48_BUF
)
148 snprintf(buf
, len
, "%02x:%02x:%02x:%02x:%02x:%02x",
149 eui
[0] & 0xff, eui
[1] & 0xff,
150 eui
[2] & 0xff, eui
[3] & 0xff,
151 eui
[4] & 0xff, eui
[5] & 0xff);
153 debugs(28, 4, "id=" << (void*)this << " encoded " << buf
);
157 // return binary representation of the EUI
159 Eui::Eui48::lookup(const Ip::Address
&c
)
161 Ip::Address ipAddr
= c
;
166 unsigned char ifbuffer
[sizeof(struct ifreq
) * 64];
172 /* IPv6 builds do not provide the first http_port as an IPv4 socket for ARP */
173 int tmpSocket
= socket(AF_INET
,SOCK_STREAM
,0);
175 debugs(28, DBG_IMPORTANT
, "Attempt to open socket for EUI retrieval failed: " << xstrerror());
181 * The linux kernel 2.2 maintains per interface ARP caches and
182 * thus requires an interface name when doing ARP queries.
184 * The older 2.0 kernels appear to use a unified ARP cache,
185 * and require an empty interface name
187 * To support both, we attempt the lookup with a blank interface
188 * name first. If that does not succeed, the try each interface
193 * Set up structures for ARP lookup with blank interface name
195 struct arpreq arpReq
;
196 memset(&arpReq
, '\0', sizeof(arpReq
));
198 struct sockaddr_in
*sa
= (struct sockaddr_in
*)&arpReq
.arp_pa
;
199 ipAddr
.getSockAddr(*sa
);
201 /* Query ARP table */
202 debugs(28, 4, "id=" << (void*)this << " query ARP table");
203 if (ioctl(tmpSocket
, SIOCGARP
, &arpReq
) != -1) {
204 /* Skip non-ethernet interfaces */
207 if (arpReq
.arp_ha
.sa_family
!= ARPHRD_ETHER
) {
212 debugs(28, 4, "id=" << (void*)this << " got address "<< std::setfill('0') << std::hex
<<
213 std::setw(2) << (arpReq
.arp_ha
.sa_data
[0] & 0xff) << ":" <<
214 std::setw(2) << (arpReq
.arp_ha
.sa_data
[1] & 0xff) << ":" <<
215 std::setw(2) << (arpReq
.arp_ha
.sa_data
[2] & 0xff) << ":" <<
216 std::setw(2) << (arpReq
.arp_ha
.sa_data
[3] & 0xff) << ":" <<
217 std::setw(2) << (arpReq
.arp_ha
.sa_data
[4] & 0xff) << ":" <<
218 std::setw(2) << (arpReq
.arp_ha
.sa_data
[5] & 0xff));
220 set(arpReq
.arp_ha
.sa_data
, 6);
224 /* lookup list of interface names */
225 ifc
.ifc_len
= sizeof(ifbuffer
);
227 ifc
.ifc_buf
= (char *)ifbuffer
;
229 if (ioctl(tmpSocket
, SIOCGIFCONF
, &ifc
) < 0) {
230 debugs(28, DBG_IMPORTANT
, "Attempt to retrieve interface list failed: " << xstrerror());
236 if (ifc
.ifc_len
> (int)sizeof(ifbuffer
)) {
237 debugs(28, DBG_IMPORTANT
, "Interface list too long - " << ifc
.ifc_len
);
243 /* Attempt ARP lookup on each interface */
245 debugs(28, 4, "id=" << (void*)this << " query ARP on each interface (" << ifc
.ifc_len
<< " found)");
246 while (offset
< ifc
.ifc_len
) {
248 ifr
= (struct ifreq
*) (ifbuffer
+ offset
);
249 offset
+= sizeof(*ifr
);
251 debugs(28, 4, "id=" << (void*)this << " found interface " << ifr
->ifr_name
);
253 /* Skip loopback and aliased interfaces */
254 if (!strncmp(ifr
->ifr_name
, "lo", 2))
257 if (strchr(ifr
->ifr_name
, ':'))
260 debugs(28, 4, "id=" << (void*)this << " looking up ARP address for " << ipAddr
<< " on " << ifr
->ifr_name
);
262 /* Set up structures for ARP lookup */
264 memset(&arpReq
, '\0', sizeof(arpReq
));
266 sa
= (sockaddr_in
*)&arpReq
.arp_pa
;
267 ipAddr
.getSockAddr(*sa
);
269 strncpy(arpReq
.arp_dev
, ifr
->ifr_name
, sizeof(arpReq
.arp_dev
) - 1);
271 arpReq
.arp_dev
[sizeof(arpReq
.arp_dev
) - 1] = '\0';
273 /* Query ARP table */
274 if (-1 == ioctl(tmpSocket
, SIOCGARP
, &arpReq
)) {
276 * Query failed. Do not log failed lookups or "device
282 else if (ENODEV
== errno
)
285 debugs(28, DBG_IMPORTANT
, "ARP query " << ipAddr
<< " failed: " << ifr
->ifr_name
<< ": " << xstrerror());
290 /* Skip non-ethernet interfaces */
291 if (arpReq
.arp_ha
.sa_family
!= ARPHRD_ETHER
) {
292 debugs(28, 4, "id=" << (void*)this << "... not an Ethernet interface");
296 debugs(28, 4, "id=" << (void*)this << " got address "<< std::setfill('0') << std::hex
<<
297 std::setw(2) << (arpReq
.arp_ha
.sa_data
[0] & 0xff) << ":" <<
298 std::setw(2) << (arpReq
.arp_ha
.sa_data
[1] & 0xff) << ":" <<
299 std::setw(2) << (arpReq
.arp_ha
.sa_data
[2] & 0xff) << ":" <<
300 std::setw(2) << (arpReq
.arp_ha
.sa_data
[3] & 0xff) << ":" <<
301 std::setw(2) << (arpReq
.arp_ha
.sa_data
[4] & 0xff) << ":" <<
302 std::setw(2) << (arpReq
.arp_ha
.sa_data
[5] & 0xff) << " on "<<
303 std::setfill(' ') << ifr
->ifr_name
);
305 set(arpReq
.arp_ha
.sa_data
, 6);
308 * Should we stop looking here? Can the same IP address
309 * exist on multiple interfaces?
312 /* AYJ: 2009-10-06: for now we have to. We can only store one EUI at a time. */
319 #elif _SQUID_SOLARIS_
321 /* IPv6 builds do not provide the first http_port as an IPv4 socket for ARP */
322 int tmpSocket
= socket(AF_INET
,SOCK_STREAM
,0);
324 debugs(28, DBG_IMPORTANT
, "Attempt to open socket for EUI retrieval failed: " << xstrerror());
329 /* Set up structures for ARP lookup with blank interface name */
330 struct arpreq arpReq
;
331 memset(&arpReq
, '\0', sizeof(arpReq
));
333 struct sockaddr_in
*sa
= (struct sockaddr_in
*)&arpReq
.arp_pa
;
334 ipAddr
.getSockAddr(*sa
);
336 /* Query ARP table */
337 if (ioctl(tmpSocket
, SIOCGARP
, &arpReq
) != -1) {
339 * Solaris (at least 2.6/x86) does not use arp_ha.sa_family -
340 * it returns 00:00:00:00:00:00 for non-ethernet media
344 if (arpReq
.arp_ha
.sa_data
[0] == 0 &&
345 arpReq
.arp_ha
.sa_data
[1] == 0 &&
346 arpReq
.arp_ha
.sa_data
[2] == 0 &&
347 arpReq
.arp_ha
.sa_data
[3] == 0 &&
348 arpReq
.arp_ha
.sa_data
[4] == 0 && arpReq
.arp_ha
.sa_data
[5] == 0) {
353 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex
<<
354 std::setw(2) << (arpReq
.arp_ha
.sa_data
[0] & 0xff) << ":" <<
355 std::setw(2) << (arpReq
.arp_ha
.sa_data
[1] & 0xff) << ":" <<
356 std::setw(2) << (arpReq
.arp_ha
.sa_data
[2] & 0xff) << ":" <<
357 std::setw(2) << (arpReq
.arp_ha
.sa_data
[3] & 0xff) << ":" <<
358 std::setw(2) << (arpReq
.arp_ha
.sa_data
[4] & 0xff) << ":" <<
359 std::setw(2) << (arpReq
.arp_ha
.sa_data
[5] & 0xff));
361 set(arpReq
.arp_ha
.sa_data
, 6);
367 #elif _SQUID_FREEBSD_ || _SQUID_NETBSD_ || _SQUID_OPENBSD_ || _SQUID_DRAGONFLY_ || _SQUID_KFREEBSD_
373 char *lim
, *buf
, *next
;
375 struct rt_msghdr
*rtm
;
377 struct sockaddr_inarp
*sin
;
379 struct sockaddr_dl
*sdl
;
382 * Set up structures for ARP lookup with blank interface name
384 struct arpreq arpReq
;
385 memset(&arpReq
, '\0', sizeof(arpReq
));
387 struct sockaddr_in
*sa
= (struct sockaddr_in
*)&arpReq
.arp_pa
;
388 ipAddr
.getSockAddr(*sa
);
390 /* Query ARP table */
399 mib
[4] = NET_RT_FLAGS
;
403 if (sysctl(mib
, 6, NULL
, &needed
, NULL
, 0) < 0) {
404 debugs(28, DBG_CRITICAL
, "Can't estimate ARP table size!");
409 if ((buf
= (char *)xmalloc(needed
)) == NULL
) {
410 debugs(28, DBG_CRITICAL
, "Can't allocate temporary ARP table!");
415 if (sysctl(mib
, 6, buf
, &needed
, NULL
, 0) < 0) {
416 debugs(28, DBG_CRITICAL
, "Can't retrieve ARP table!");
424 for (next
= buf
; next
< lim
; next
+= rtm
->rtm_msglen
) {
426 rtm
= (struct rt_msghdr
*) next
;
428 sin
= (struct sockaddr_inarp
*) (rtm
+ 1);
429 /*sdl = (struct sockaddr_dl *) (sin + 1); */
432 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
434 sdl
= (struct sockaddr_dl
*)((char *) sin
+ ROUNDUP(sin
->sin_len
));
436 if (ipAddr
== sin
->sin_addr
) {
439 arpReq
.arp_ha
.sa_len
= sizeof(struct sockaddr
);
440 arpReq
.arp_ha
.sa_family
= AF_UNSPEC
;
441 memcpy(arpReq
.arp_ha
.sa_data
, LLADDR(sdl
), sdl
->sdl_alen
);
448 if (arpReq
.arp_ha
.sa_data
[0] == 0 && arpReq
.arp_ha
.sa_data
[1] == 0 &&
449 arpReq
.arp_ha
.sa_data
[2] == 0 && arpReq
.arp_ha
.sa_data
[3] == 0 &&
450 arpReq
.arp_ha
.sa_data
[4] == 0 && arpReq
.arp_ha
.sa_data
[5] == 0) {
455 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex
<<
456 std::setw(2) << (arpReq
.arp_ha
.sa_data
[0] & 0xff) << ":" <<
457 std::setw(2) << (arpReq
.arp_ha
.sa_data
[1] & 0xff) << ":" <<
458 std::setw(2) << (arpReq
.arp_ha
.sa_data
[2] & 0xff) << ":" <<
459 std::setw(2) << (arpReq
.arp_ha
.sa_data
[3] & 0xff) << ":" <<
460 std::setw(2) << (arpReq
.arp_ha
.sa_data
[4] & 0xff) << ":" <<
461 std::setw(2) << (arpReq
.arp_ha
.sa_data
[5] & 0xff));
463 set(arpReq
.arp_ha
.sa_data
, 6);
466 #elif _SQUID_WINDOWS_
468 DWORD dwNetTable
= 0;
470 DWORD ipNetTableLen
= 0;
472 PMIB_IPNETTABLE NetTable
= NULL
;
476 struct arpreq arpReq
;
477 memset(&arpReq
, '\0', sizeof(arpReq
));
479 /* Get size of Windows ARP table */
480 if (GetIpNetTable(NetTable
, &ipNetTableLen
, FALSE
) != ERROR_INSUFFICIENT_BUFFER
) {
481 debugs(28, DBG_CRITICAL
, "Can't estimate ARP table size!");
486 /* Allocate space for ARP table and assign pointers */
487 if ((NetTable
= (PMIB_IPNETTABLE
)xmalloc(ipNetTableLen
)) == NULL
) {
488 debugs(28, DBG_CRITICAL
, "Can't allocate temporary ARP table!");
493 /* Get actual ARP table */
494 if ((dwNetTable
= GetIpNetTable(NetTable
, &ipNetTableLen
, FALSE
)) != NO_ERROR
) {
495 debugs(28, DBG_CRITICAL
, "Can't retrieve ARP table!");
501 /* Find MAC address from net table */
502 for (i
= 0 ; i
< NetTable
->dwNumEntries
; ++i
) {
504 a
.s_addr
= NetTable
->table
[i
].dwAddr
;
505 if (c
== a
&& (NetTable
->table
[i
].dwType
> 2)) {
506 arpReq
.arp_ha
.sa_family
= AF_UNSPEC
;
507 memcpy(arpReq
.arp_ha
.sa_data
, NetTable
->table
[i
].bPhysAddr
, NetTable
->table
[i
].dwPhysAddrLen
);
513 if (arpReq
.arp_ha
.sa_data
[0] == 0 && arpReq
.arp_ha
.sa_data
[1] == 0 &&
514 arpReq
.arp_ha
.sa_data
[2] == 0 && arpReq
.arp_ha
.sa_data
[3] == 0 &&
515 arpReq
.arp_ha
.sa_data
[4] == 0 && arpReq
.arp_ha
.sa_data
[5] == 0) {
520 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex
<<
521 std::setw(2) << (arpReq
.arp_ha
.sa_data
[0] & 0xff) << ":" <<
522 std::setw(2) << (arpReq
.arp_ha
.sa_data
[1] & 0xff) << ":" <<
523 std::setw(2) << (arpReq
.arp_ha
.sa_data
[2] & 0xff) << ":" <<
524 std::setw(2) << (arpReq
.arp_ha
.sa_data
[3] & 0xff) << ":" <<
525 std::setw(2) << (arpReq
.arp_ha
.sa_data
[4] & 0xff) << ":" <<
526 std::setw(2) << (arpReq
.arp_ha
.sa_data
[5] & 0xff));
528 set(arpReq
.arp_ha
.sa_data
, 6);
533 debugs(28, DBG_CRITICAL
, "ERROR: ARP / MAC / EUI-* operations not supported on this operating system.");
537 * Address was not found on any interface
539 debugs(28, 3, "id=" << (void*)this << ' ' << ipAddr
<< " NOT found");
545 /* ==== END EUI LOOKUP SUPPORT =============================================== */
547 #endif /* USE_SQUID_EUI */