2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 89 EUI-48 Lookup */
16 #include "eui/Eui48.h"
18 #include "ip/Address.h"
22 /* START Legacy includes pattern */
23 /* TODO: clean this up so we dont have per-OS requirements.
24 The files are checked for existence individually
31 Ip::Address arp_pa
; /* protocol address */
33 struct sockaddr arp_ha
; /* hardware address */
34 int arp_flags
; /* flags */
42 #include <sys/param.h>
45 /* required by Solaris */
46 #include <sys/sockio.h>
49 #include <sys/sysctl.h>
52 #include <net/route.h>
58 #include <net/if_arp.h>
61 #include <net/if_dl.h>
63 #if HAVE_NETINET_IF_ETHER_H
64 #include <netinet/if_ether.h>
67 #include <sys/ioctl.h>
70 /* ==== BEGIN EUI LOOKUP SUPPORT ============================================= */
73 * From: dale@server.ctam.bitmcnit.bryansk.su (Dale)
74 * To: wessels@nlanr.net
75 * Subject: Another Squid patch... :)
76 * Date: Thu, 04 Dec 1997 19:55:01 +0300
77 * ============================================================================
79 * Working on setting up a proper firewall for a network containing some
80 * Win'95 computers at our Univ, I've discovered that some smart students
81 * avoid the restrictions easily just changing their IP addresses in Win'95
82 * Contol Panel... It has been getting boring, so I took Squid-1.1.18
83 * sources and added a new acl type for hard-wired access control:
85 * acl <name> arp <Ethernet address> ...
89 * acl students arp 00:00:21:55:ed:22 00:00:21:ff:55:38
91 * NOTE: Linux code by David Luyer <luyer@ucs.uwa.edu.au>.
92 * Original (BSD-specific) code no longer works.
93 * Solaris code by R. Gancarz <radekg@solaris.elektrownia-lagisza.com.pl>
97 Eui::Eui48::decode(const char *asc
)
99 int a1
= 0, a2
= 0, a3
= 0, a4
= 0, a5
= 0, a6
= 0;
101 if (sscanf(asc
, "%x:%x:%x:%x:%x:%x", &a1
, &a2
, &a3
, &a4
, &a5
, &a6
) != 6) {
102 debugs(28, DBG_CRITICAL
, "Decode EUI-48: Invalid ethernet address '" << asc
<< "'");
104 return false; /* This is not valid address */
107 eui
[0] = (u_char
) a1
;
108 eui
[1] = (u_char
) a2
;
109 eui
[2] = (u_char
) a3
;
110 eui
[3] = (u_char
) a4
;
111 eui
[4] = (u_char
) a5
;
112 eui
[5] = (u_char
) a6
;
114 debugs(28, 4, "id=" << (void*)this << " decoded " << asc
);
119 Eui::Eui48::encode(char *buf
, const int len
)
121 if (len
< SZ_EUI48_BUF
)
124 snprintf(buf
, len
, "%02x:%02x:%02x:%02x:%02x:%02x",
125 eui
[0] & 0xff, eui
[1] & 0xff,
126 eui
[2] & 0xff, eui
[3] & 0xff,
127 eui
[4] & 0xff, eui
[5] & 0xff);
129 debugs(28, 4, "id=" << (void*)this << " encoded " << buf
);
133 // return binary representation of the EUI
135 Eui::Eui48::lookup(const Ip::Address
&c
)
137 Ip::Address ipAddr
= c
;
142 unsigned char ifbuffer
[sizeof(struct ifreq
) * 64];
148 /* IPv6 builds do not provide the first http_port as an IPv4 socket for ARP */
149 int tmpSocket
= socket(AF_INET
,SOCK_STREAM
,0);
151 debugs(28, DBG_IMPORTANT
, "Attempt to open socket for EUI retrieval failed: " << xstrerror());
157 * The linux kernel 2.2 maintains per interface ARP caches and
158 * thus requires an interface name when doing ARP queries.
160 * The older 2.0 kernels appear to use a unified ARP cache,
161 * and require an empty interface name
163 * To support both, we attempt the lookup with a blank interface
164 * name first. If that does not succeed, the try each interface
169 * Set up structures for ARP lookup with blank interface name
171 struct arpreq arpReq
;
172 memset(&arpReq
, '\0', sizeof(arpReq
));
174 struct sockaddr_in
*sa
= (struct sockaddr_in
*)&arpReq
.arp_pa
;
175 ipAddr
.getSockAddr(*sa
);
177 /* Query ARP table */
178 debugs(28, 4, "id=" << (void*)this << " query ARP table");
179 if (ioctl(tmpSocket
, SIOCGARP
, &arpReq
) != -1) {
180 /* Skip non-ethernet interfaces */
183 if (arpReq
.arp_ha
.sa_family
!= ARPHRD_ETHER
) {
188 debugs(28, 4, "id=" << (void*)this << " got address "<< std::setfill('0') << std::hex
<<
189 std::setw(2) << (arpReq
.arp_ha
.sa_data
[0] & 0xff) << ":" <<
190 std::setw(2) << (arpReq
.arp_ha
.sa_data
[1] & 0xff) << ":" <<
191 std::setw(2) << (arpReq
.arp_ha
.sa_data
[2] & 0xff) << ":" <<
192 std::setw(2) << (arpReq
.arp_ha
.sa_data
[3] & 0xff) << ":" <<
193 std::setw(2) << (arpReq
.arp_ha
.sa_data
[4] & 0xff) << ":" <<
194 std::setw(2) << (arpReq
.arp_ha
.sa_data
[5] & 0xff));
196 set(arpReq
.arp_ha
.sa_data
, 6);
200 /* lookup list of interface names */
201 ifc
.ifc_len
= sizeof(ifbuffer
);
203 ifc
.ifc_buf
= (char *)ifbuffer
;
205 if (ioctl(tmpSocket
, SIOCGIFCONF
, &ifc
) < 0) {
206 debugs(28, DBG_IMPORTANT
, "Attempt to retrieve interface list failed: " << xstrerror());
212 if (ifc
.ifc_len
> (int)sizeof(ifbuffer
)) {
213 debugs(28, DBG_IMPORTANT
, "Interface list too long - " << ifc
.ifc_len
);
219 /* Attempt ARP lookup on each interface */
221 debugs(28, 4, "id=" << (void*)this << " query ARP on each interface (" << ifc
.ifc_len
<< " found)");
222 while (offset
< ifc
.ifc_len
) {
224 ifr
= (struct ifreq
*) (ifbuffer
+ offset
);
225 offset
+= sizeof(*ifr
);
227 debugs(28, 4, "id=" << (void*)this << " found interface " << ifr
->ifr_name
);
229 /* Skip loopback and aliased interfaces */
230 if (!strncmp(ifr
->ifr_name
, "lo", 2))
233 if (strchr(ifr
->ifr_name
, ':'))
236 debugs(28, 4, "id=" << (void*)this << " looking up ARP address for " << ipAddr
<< " on " << ifr
->ifr_name
);
238 /* Set up structures for ARP lookup */
240 memset(&arpReq
, '\0', sizeof(arpReq
));
242 sa
= (sockaddr_in
*)&arpReq
.arp_pa
;
243 ipAddr
.getSockAddr(*sa
);
245 strncpy(arpReq
.arp_dev
, ifr
->ifr_name
, sizeof(arpReq
.arp_dev
) - 1);
247 arpReq
.arp_dev
[sizeof(arpReq
.arp_dev
) - 1] = '\0';
249 /* Query ARP table */
250 if (-1 == ioctl(tmpSocket
, SIOCGARP
, &arpReq
)) {
252 * Query failed. Do not log failed lookups or "device
258 else if (ENODEV
== errno
)
261 debugs(28, DBG_IMPORTANT
, "ARP query " << ipAddr
<< " failed: " << ifr
->ifr_name
<< ": " << xstrerror());
266 /* Skip non-ethernet interfaces */
267 if (arpReq
.arp_ha
.sa_family
!= ARPHRD_ETHER
) {
268 debugs(28, 4, "id=" << (void*)this << "... not an Ethernet interface");
272 debugs(28, 4, "id=" << (void*)this << " got address "<< std::setfill('0') << std::hex
<<
273 std::setw(2) << (arpReq
.arp_ha
.sa_data
[0] & 0xff) << ":" <<
274 std::setw(2) << (arpReq
.arp_ha
.sa_data
[1] & 0xff) << ":" <<
275 std::setw(2) << (arpReq
.arp_ha
.sa_data
[2] & 0xff) << ":" <<
276 std::setw(2) << (arpReq
.arp_ha
.sa_data
[3] & 0xff) << ":" <<
277 std::setw(2) << (arpReq
.arp_ha
.sa_data
[4] & 0xff) << ":" <<
278 std::setw(2) << (arpReq
.arp_ha
.sa_data
[5] & 0xff) << " on "<<
279 std::setfill(' ') << ifr
->ifr_name
);
281 set(arpReq
.arp_ha
.sa_data
, 6);
284 * Should we stop looking here? Can the same IP address
285 * exist on multiple interfaces?
288 /* AYJ: 2009-10-06: for now we have to. We can only store one EUI at a time. */
295 #elif _SQUID_SOLARIS_
297 /* IPv6 builds do not provide the first http_port as an IPv4 socket for ARP */
298 int tmpSocket
= socket(AF_INET
,SOCK_STREAM
,0);
300 debugs(28, DBG_IMPORTANT
, "Attempt to open socket for EUI retrieval failed: " << xstrerror());
305 /* Set up structures for ARP lookup with blank interface name */
306 struct arpreq arpReq
;
307 memset(&arpReq
, '\0', sizeof(arpReq
));
309 struct sockaddr_in
*sa
= (struct sockaddr_in
*)&arpReq
.arp_pa
;
310 ipAddr
.getSockAddr(*sa
);
312 /* Query ARP table */
313 if (ioctl(tmpSocket
, SIOCGARP
, &arpReq
) != -1) {
315 * Solaris (at least 2.6/x86) does not use arp_ha.sa_family -
316 * it returns 00:00:00:00:00:00 for non-ethernet media
320 if (arpReq
.arp_ha
.sa_data
[0] == 0 &&
321 arpReq
.arp_ha
.sa_data
[1] == 0 &&
322 arpReq
.arp_ha
.sa_data
[2] == 0 &&
323 arpReq
.arp_ha
.sa_data
[3] == 0 &&
324 arpReq
.arp_ha
.sa_data
[4] == 0 && arpReq
.arp_ha
.sa_data
[5] == 0) {
329 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex
<<
330 std::setw(2) << (arpReq
.arp_ha
.sa_data
[0] & 0xff) << ":" <<
331 std::setw(2) << (arpReq
.arp_ha
.sa_data
[1] & 0xff) << ":" <<
332 std::setw(2) << (arpReq
.arp_ha
.sa_data
[2] & 0xff) << ":" <<
333 std::setw(2) << (arpReq
.arp_ha
.sa_data
[3] & 0xff) << ":" <<
334 std::setw(2) << (arpReq
.arp_ha
.sa_data
[4] & 0xff) << ":" <<
335 std::setw(2) << (arpReq
.arp_ha
.sa_data
[5] & 0xff));
337 set(arpReq
.arp_ha
.sa_data
, 6);
343 #elif _SQUID_FREEBSD_ || _SQUID_NETBSD_ || _SQUID_OPENBSD_ || _SQUID_DRAGONFLY_ || _SQUID_KFREEBSD_
349 char *lim
, *buf
, *next
;
351 struct rt_msghdr
*rtm
;
353 struct sockaddr_inarp
*sin
;
355 struct sockaddr_dl
*sdl
;
358 * Set up structures for ARP lookup with blank interface name
360 struct arpreq arpReq
;
361 memset(&arpReq
, '\0', sizeof(arpReq
));
363 struct sockaddr_in
*sa
= (struct sockaddr_in
*)&arpReq
.arp_pa
;
364 ipAddr
.getSockAddr(*sa
);
366 /* Query ARP table */
375 mib
[4] = NET_RT_FLAGS
;
379 if (sysctl(mib
, 6, NULL
, &needed
, NULL
, 0) < 0) {
380 debugs(28, DBG_CRITICAL
, "Can't estimate ARP table size!");
385 if ((buf
= (char *)xmalloc(needed
)) == NULL
) {
386 debugs(28, DBG_CRITICAL
, "Can't allocate temporary ARP table!");
391 if (sysctl(mib
, 6, buf
, &needed
, NULL
, 0) < 0) {
392 debugs(28, DBG_CRITICAL
, "Can't retrieve ARP table!");
400 for (next
= buf
; next
< lim
; next
+= rtm
->rtm_msglen
) {
402 rtm
= (struct rt_msghdr
*) next
;
404 sin
= (struct sockaddr_inarp
*) (rtm
+ 1);
405 /*sdl = (struct sockaddr_dl *) (sin + 1); */
408 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
410 sdl
= (struct sockaddr_dl
*)((char *) sin
+ ROUNDUP(sin
->sin_len
));
412 if (ipAddr
== sin
->sin_addr
) {
415 arpReq
.arp_ha
.sa_len
= sizeof(struct sockaddr
);
416 arpReq
.arp_ha
.sa_family
= AF_UNSPEC
;
417 memcpy(arpReq
.arp_ha
.sa_data
, LLADDR(sdl
), sdl
->sdl_alen
);
424 if (arpReq
.arp_ha
.sa_data
[0] == 0 && arpReq
.arp_ha
.sa_data
[1] == 0 &&
425 arpReq
.arp_ha
.sa_data
[2] == 0 && arpReq
.arp_ha
.sa_data
[3] == 0 &&
426 arpReq
.arp_ha
.sa_data
[4] == 0 && arpReq
.arp_ha
.sa_data
[5] == 0) {
431 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex
<<
432 std::setw(2) << (arpReq
.arp_ha
.sa_data
[0] & 0xff) << ":" <<
433 std::setw(2) << (arpReq
.arp_ha
.sa_data
[1] & 0xff) << ":" <<
434 std::setw(2) << (arpReq
.arp_ha
.sa_data
[2] & 0xff) << ":" <<
435 std::setw(2) << (arpReq
.arp_ha
.sa_data
[3] & 0xff) << ":" <<
436 std::setw(2) << (arpReq
.arp_ha
.sa_data
[4] & 0xff) << ":" <<
437 std::setw(2) << (arpReq
.arp_ha
.sa_data
[5] & 0xff));
439 set(arpReq
.arp_ha
.sa_data
, 6);
442 #elif _SQUID_WINDOWS_
444 DWORD dwNetTable
= 0;
446 DWORD ipNetTableLen
= 0;
448 PMIB_IPNETTABLE NetTable
= NULL
;
452 struct arpreq arpReq
;
453 memset(&arpReq
, '\0', sizeof(arpReq
));
455 /* Get size of Windows ARP table */
456 if (GetIpNetTable(NetTable
, &ipNetTableLen
, FALSE
) != ERROR_INSUFFICIENT_BUFFER
) {
457 debugs(28, DBG_CRITICAL
, "Can't estimate ARP table size!");
462 /* Allocate space for ARP table and assign pointers */
463 if ((NetTable
= (PMIB_IPNETTABLE
)xmalloc(ipNetTableLen
)) == NULL
) {
464 debugs(28, DBG_CRITICAL
, "Can't allocate temporary ARP table!");
469 /* Get actual ARP table */
470 if ((dwNetTable
= GetIpNetTable(NetTable
, &ipNetTableLen
, FALSE
)) != NO_ERROR
) {
471 debugs(28, DBG_CRITICAL
, "Can't retrieve ARP table!");
477 /* Find MAC address from net table */
478 for (i
= 0 ; i
< NetTable
->dwNumEntries
; ++i
) {
480 a
.s_addr
= NetTable
->table
[i
].dwAddr
;
481 if (c
== a
&& (NetTable
->table
[i
].dwType
> 2)) {
482 arpReq
.arp_ha
.sa_family
= AF_UNSPEC
;
483 memcpy(arpReq
.arp_ha
.sa_data
, NetTable
->table
[i
].bPhysAddr
, NetTable
->table
[i
].dwPhysAddrLen
);
489 if (arpReq
.arp_ha
.sa_data
[0] == 0 && arpReq
.arp_ha
.sa_data
[1] == 0 &&
490 arpReq
.arp_ha
.sa_data
[2] == 0 && arpReq
.arp_ha
.sa_data
[3] == 0 &&
491 arpReq
.arp_ha
.sa_data
[4] == 0 && arpReq
.arp_ha
.sa_data
[5] == 0) {
496 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex
<<
497 std::setw(2) << (arpReq
.arp_ha
.sa_data
[0] & 0xff) << ":" <<
498 std::setw(2) << (arpReq
.arp_ha
.sa_data
[1] & 0xff) << ":" <<
499 std::setw(2) << (arpReq
.arp_ha
.sa_data
[2] & 0xff) << ":" <<
500 std::setw(2) << (arpReq
.arp_ha
.sa_data
[3] & 0xff) << ":" <<
501 std::setw(2) << (arpReq
.arp_ha
.sa_data
[4] & 0xff) << ":" <<
502 std::setw(2) << (arpReq
.arp_ha
.sa_data
[5] & 0xff));
504 set(arpReq
.arp_ha
.sa_data
, 6);
509 debugs(28, DBG_CRITICAL
, "ERROR: ARP / MAC / EUI-* operations not supported on this operating system.");
513 * Address was not found on any interface
515 debugs(28, 3, "id=" << (void*)this << ' ' << ipAddr
<< " NOT found");
521 /* ==== END EUI LOOKUP SUPPORT =============================================== */
523 #endif /* USE_SQUID_EUI */