Squid Web Cache master
Loading...
Searching...
No Matches
Eui48.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9/* DEBUG: section 89 EUI-48 Lookup */
10
11#include "squid.h"
12
13#if USE_SQUID_EUI
14
15#include "base/IoManip.h"
16#include "compat/socket.h"
17#include "compat/unistd.h"
18#include "debug/Stream.h"
19#include "eui/Eui48.h"
20#include "globals.h"
21#include "ip/Address.h"
22
23#include <cerrno>
24
25/* START Legacy includes pattern */
26/* TODO: clean this up so we do not have per-OS requirements.
27 The files are checked for existence individually
28 and can be wrapped
29 */
30
31#if _SQUID_WINDOWS_
32struct arpreq {
33
34 Ip::Address arp_pa; /* protocol address */
35
36 struct sockaddr arp_ha; /* hardware address */
37 int arp_flags; /* flags */
38};
39#if HAVE_IPHLPAPI_H
40#include <iphlpapi.h>
41#endif
42#endif
43
44#if HAVE_SYS_PARAM_H
45#include <sys/param.h>
46#endif
47#if HAVE_SYS_SOCKIO_H
48/* required by Solaris */
49#include <sys/sockio.h>
50#endif
51#if HAVE_SYS_SYSCTL_H
52#include <sys/sysctl.h>
53#endif
54#if HAVE_NET_ROUTE_H
55#include <net/route.h>
56#endif
57#if HAVE_NET_IF_H
58#include <net/if.h>
59#endif
60#if HAVE_NET_IF_ARP_H
61#include <net/if_arp.h>
62#endif
63#if HAVE_NET_IF_DL_H
64#include <net/if_dl.h>
65#endif
66#if HAVE_NETINET_IF_ETHER_H
67#include <netinet/if_ether.h>
68#endif
69#if HAVE_SYS_IOCTL_H
70#include <sys/ioctl.h>
71#endif
72
73/* ==== BEGIN EUI LOOKUP SUPPORT ============================================= */
74
75/*
76 * From: dale@server.ctam.bitmcnit.bryansk.su (Dale)
77 * To: wessels@nlanr.net
78 * Subject: Another Squid patch... :)
79 * Date: Thu, 04 Dec 1997 19:55:01 +0300
80 * ============================================================================
81 *
82 * Working on setting up a proper firewall for a network containing some
83 * Win'95 computers at our Univ, I've discovered that some smart students
84 * avoid the restrictions easily just changing their IP addresses in Win'95
85 * Control Panel... It has been getting boring, so I took Squid-1.1.18
86 * sources and added a new acl type for hard-wired access control:
87 *
88 * acl <name> arp <Ethernet address> ...
89 *
90 * For example,
91 *
92 * acl students arp 00:00:21:55:ed:22 00:00:21:ff:55:38
93 *
94 * NOTE: Linux code by David Luyer <luyer@ucs.uwa.edu.au>.
95 * Original (BSD-specific) code no longer works.
96 * Solaris code by R. Gancarz <radekg@solaris.elektrownia-lagisza.com.pl>
97 */
98
100template <typename HardwareAddress>
101class AsEui48 {
102public:
104 explicit AsEui48(const HardwareAddress * const a): hardwareAddress(a) {}
105 const HardwareAddress * const hardwareAddress;
106};
107
108template <typename HardwareAddress>
109static std::ostream &
110operator <<(std::ostream &os, const AsEui48<HardwareAddress> &manipulator)
111{
112 const auto &ha = *manipulator.hardwareAddress;
113 os <<
114 asHex(ha.sa_data[0] & 0xff).minDigits(2) << ':' <<
115 asHex(ha.sa_data[1] & 0xff).minDigits(2) << ':' <<
116 asHex(ha.sa_data[2] & 0xff).minDigits(2) << ':' <<
117 asHex(ha.sa_data[3] & 0xff).minDigits(2) << ':' <<
118 asHex(ha.sa_data[4] & 0xff).minDigits(2) << ':' <<
119 asHex(ha.sa_data[5] & 0xff).minDigits(2);
120 return os;
121}
122
123bool
124Eui::Eui48::decode(const char *asc)
125{
126 int a1 = 0, a2 = 0, a3 = 0, a4 = 0, a5 = 0, a6 = 0;
127
128 if (sscanf(asc, "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6) != 6) {
129 debugs(28, DBG_CRITICAL, "ERROR: Decode EUI-48: Invalid ethernet address '" << asc << "'");
130 clear();
131 return false; /* This is not valid address */
132 }
133
134 eui[0] = (u_char) a1;
135 eui[1] = (u_char) a2;
136 eui[2] = (u_char) a3;
137 eui[3] = (u_char) a4;
138 eui[4] = (u_char) a5;
139 eui[5] = (u_char) a6;
140
141 debugs(28, 4, "id=" << (void*)this << " decoded " << asc);
142 return true;
143}
144
145bool
146Eui::Eui48::encode(char *buf, const int len) const
147{
148 if (len < SZ_EUI48_BUF)
149 return false;
150
151 snprintf(buf, len, "%02x:%02x:%02x:%02x:%02x:%02x",
152 eui[0] & 0xff, eui[1] & 0xff,
153 eui[2] & 0xff, eui[3] & 0xff,
154 eui[4] & 0xff, eui[5] & 0xff);
155
156 debugs(28, 4, "id=" << (void*)this << " encoded " << buf);
157 return true;
158}
159
160// return binary representation of the EUI
161bool
163{
164 Ip::Address ipAddr = c;
165 ipAddr.port(0);
166
167#if _SQUID_LINUX_
168
169 unsigned char ifbuffer[sizeof(struct ifreq) * 64];
170 struct ifconf ifc;
171
172 struct ifreq *ifr;
173 int offset;
174
175 /* IPv6 builds do not provide the first http_port as an IPv4 socket for ARP */
176 auto tmpSocket = xsocket(AF_INET,SOCK_STREAM,0);
177 if (tmpSocket < 0) {
178 int xerrno = errno;
179 debugs(28, DBG_IMPORTANT, "ERROR: Attempt to open socket for EUI retrieval failed: " << xstrerr(xerrno));
180 clear();
181 return false;
182 }
183
184 /*
185 * The linux kernel 2.2 maintains per interface ARP caches and
186 * thus requires an interface name when doing ARP queries.
187 *
188 * The older 2.0 kernels appear to use a unified ARP cache,
189 * and require an empty interface name
190 *
191 * To support both, we attempt the lookup with a blank interface
192 * name first. If that does not succeed, the try each interface
193 * in turn
194 */
195
196 /*
197 * Set up structures for ARP lookup with blank interface name
198 */
199 struct arpreq arpReq;
200 memset(&arpReq, '\0', sizeof(arpReq));
201
202 struct sockaddr_in *sa = (struct sockaddr_in*)&arpReq.arp_pa;
203 ipAddr.getSockAddr(*sa);
204
205 /* Query ARP table */
206 debugs(28, 4, "id=" << (void*)this << " query ARP table");
207 if (ioctl(tmpSocket, SIOCGARP, &arpReq) != -1) {
208 /* Skip non-ethernet interfaces */
209 xclose(tmpSocket);
210
211 if (arpReq.arp_ha.sa_family != ARPHRD_ETHER) {
212 debugs(28, 4, "id=" << (void*)this << " ... not an Ethernet interface, sa_family=" << arpReq.arp_ha.sa_family);
213 clear();
214 return false;
215 }
216
217 debugs(28, 4, "id=" << static_cast<void*>(this) << " got address " << AsEui48(&arpReq.arp_ha));
218
219 set(arpReq.arp_ha.sa_data, 6);
220 return true;
221 }
222
223 /* lookup list of interface names */
224 ifc.ifc_len = sizeof(ifbuffer);
225
226 ifc.ifc_buf = (char *)ifbuffer;
227
228 if (ioctl(tmpSocket, SIOCGIFCONF, &ifc) < 0) {
229 int xerrno = errno;
230 debugs(28, DBG_IMPORTANT, "ERROR: Attempt to retrieve interface list failed: " << xstrerr(xerrno));
231 clear();
232 xclose(tmpSocket);
233 return false;
234 }
235
236 if (ifc.ifc_len > (int)sizeof(ifbuffer)) {
237 debugs(28, DBG_IMPORTANT, "Interface list too long - " << ifc.ifc_len);
238 clear();
239 xclose(tmpSocket);
240 return false;
241 }
242
243 /* Attempt ARP lookup on each interface */
244 offset = 0;
245 debugs(28, 4, "id=" << (void*)this << " query ARP on each interface (" << ifc.ifc_len << " found)");
246 while (offset < ifc.ifc_len) {
247
248 ifr = (struct ifreq *) (ifbuffer + offset);
249 offset += sizeof(*ifr);
250
251 debugs(28, 4, "id=" << (void*)this << " found interface " << ifr->ifr_name);
252
253 /* Skip loopback and aliased interfaces */
254 if (!strncmp(ifr->ifr_name, "lo", 2))
255 continue;
256
257 if (strchr(ifr->ifr_name, ':'))
258 continue;
259
260 debugs(28, 4, "id=" << (void*)this << " looking up ARP address for " << ipAddr << " on " << ifr->ifr_name);
261
262 /* Set up structures for ARP lookup */
263
264 memset(&arpReq, '\0', sizeof(arpReq));
265
266 sa = (sockaddr_in*)&arpReq.arp_pa;
267 ipAddr.getSockAddr(*sa);
268
269 strncpy(arpReq.arp_dev, ifr->ifr_name, sizeof(arpReq.arp_dev) - 1);
270
271 arpReq.arp_dev[sizeof(arpReq.arp_dev) - 1] = '\0';
272
273 /* Query ARP table */
274 if (-1 == ioctl(tmpSocket, SIOCGARP, &arpReq)) {
275 int xerrno = errno;
276 // Query failed. Do not log failed lookups or "device not supported"
277 if (ENXIO != xerrno && ENODEV != xerrno)
278 debugs(28, DBG_IMPORTANT, "ERROR: ARP query " << ipAddr << " failed: " << ifr->ifr_name << ": " << xstrerr(xerrno));
279
280 continue;
281 }
282
283 /* Skip non-ethernet interfaces */
284 if (arpReq.arp_ha.sa_family != ARPHRD_ETHER) {
285 debugs(28, 4, "id=" << (void*)this << "... not an Ethernet interface");
286 continue;
287 }
288
289 debugs(28, 4, "id=" << static_cast<void*>(this) << " got address " << AsEui48(&arpReq.arp_ha) <<
290 " on " << ifr->ifr_name);
291
292 set(arpReq.arp_ha.sa_data, 6);
293
294 /*
295 * Should we stop looking here? Can the same IP address
296 * exist on multiple interfaces?
297 */
298
299 /* AYJ: 2009-10-06: for now we have to. We can only store one EUI at a time. */
300 xclose(tmpSocket);
301 return true;
302 }
303
304 xclose(tmpSocket);
305
306#elif _SQUID_SOLARIS_
307
308 /* IPv6 builds do not provide the first http_port as an IPv4 socket for ARP */
309 auto tmpSocket = xsocket(AF_INET,SOCK_STREAM,0);
310 if (tmpSocket < 0) {
311 int xerrno = errno;
312 debugs(28, DBG_IMPORTANT, "ERROR: Attempt to open socket for EUI retrieval failed: " << xstrerr(xerrno));
313 clear();
314 return false;
315 }
316
317 /* Set up structures for ARP lookup with blank interface name */
318 struct arpreq arpReq;
319 memset(&arpReq, '\0', sizeof(arpReq));
320
321 struct sockaddr_in *sa = (struct sockaddr_in*)&arpReq.arp_pa;
322 ipAddr.getSockAddr(*sa);
323
324 /* Query ARP table */
325 if (ioctl(tmpSocket, SIOCGARP, &arpReq) != -1) {
326 /*
327 * Solaris (at least 2.6/x86) does not use arp_ha.sa_family -
328 * it returns 00:00:00:00:00:00 for non-ethernet media
329 */
330 xclose(tmpSocket);
331
332 if (arpReq.arp_ha.sa_data[0] == 0 &&
333 arpReq.arp_ha.sa_data[1] == 0 &&
334 arpReq.arp_ha.sa_data[2] == 0 &&
335 arpReq.arp_ha.sa_data[3] == 0 &&
336 arpReq.arp_ha.sa_data[4] == 0 && arpReq.arp_ha.sa_data[5] == 0) {
337 clear();
338 return false;
339 }
340
341 debugs(28, 4, "Got address " << AsEui48(&arpReq.arp_ha));
342
343 set(arpReq.arp_ha.sa_data, 6);
344 return true;
345 } else {
346 xclose(tmpSocket);
347 }
348
349#elif _SQUID_FREEBSD_ || _SQUID_NETBSD_ || _SQUID_OPENBSD_ || _SQUID_DRAGONFLY_ || _SQUID_KFREEBSD_
350
351 int mib[6];
352
353 size_t needed;
354
355 char *lim, *buf, *next;
356
357 struct rt_msghdr *rtm;
358
359 struct sockaddr_inarp *sin;
360
361 struct sockaddr_dl *sdl;
362
363 /*
364 * Set up structures for ARP lookup with blank interface name
365 */
366 struct arpreq arpReq;
367 memset(&arpReq, '\0', sizeof(arpReq));
368
369 struct sockaddr_in *sa = (struct sockaddr_in*)&arpReq.arp_pa;
370 ipAddr.getSockAddr(*sa);
371
372 /* Query ARP table */
373 mib[0] = CTL_NET;
374
375 mib[1] = PF_ROUTE;
376
377 mib[2] = 0;
378
379 mib[3] = AF_INET;
380
381 mib[4] = NET_RT_FLAGS;
382
383#if defined(RTF_LLDATA)
384 mib[5] = RTF_LLDATA;
385#else
386 mib[5] = RTF_LLINFO;
387#endif
388
389 if (sysctl(mib, 6, nullptr, &needed, nullptr, 0) < 0) {
390 debugs(28, DBG_CRITICAL, "ERROR: Cannot estimate ARP table size!");
391 clear();
392 return false;
393 }
394
395 if ((buf = (char *)xmalloc(needed)) == NULL) {
396 debugs(28, DBG_CRITICAL, "ERROR: Cannot allocate temporary ARP table!");
397 clear();
398 return false;
399 }
400
401 if (sysctl(mib, 6, buf, &needed, nullptr, 0) < 0) {
402 debugs(28, DBG_CRITICAL, "ERROR: Cannot retrieve ARP table!");
403 xfree(buf);
404 clear();
405 return false;
406 }
407
408 lim = buf + needed;
409
410 for (next = buf; next < lim; next += rtm->rtm_msglen) {
411
412 rtm = (struct rt_msghdr *) next;
413
414 sin = (struct sockaddr_inarp *) (rtm + 1);
415 /*sdl = (struct sockaddr_dl *) (sin + 1); */
416
417#define ROUNDUP(a) \
418 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
419
420 sdl = (struct sockaddr_dl *)((char *) sin + ROUNDUP(sin->sin_len));
421
422 if (ipAddr == sin->sin_addr) {
423 if (sdl->sdl_alen) {
424
425 arpReq.arp_ha.sa_len = sizeof(struct sockaddr);
426 arpReq.arp_ha.sa_family = AF_UNSPEC;
427 memcpy(arpReq.arp_ha.sa_data, LLADDR(sdl), sdl->sdl_alen);
428 }
429 }
430 }
431
432 xfree(buf);
433
434 if (arpReq.arp_ha.sa_data[0] == 0 && arpReq.arp_ha.sa_data[1] == 0 &&
435 arpReq.arp_ha.sa_data[2] == 0 && arpReq.arp_ha.sa_data[3] == 0 &&
436 arpReq.arp_ha.sa_data[4] == 0 && arpReq.arp_ha.sa_data[5] == 0) {
437 clear();
438 return false;
439 }
440
441 debugs(28, 4, "Got address " << AsEui48(&arpReq.arp_ha));
442
443 set(arpReq.arp_ha.sa_data, 6);
444 return true;
445
446#elif _SQUID_WINDOWS_
447
448 DWORD dwNetTable = 0;
449
450 DWORD ipNetTableLen = 0;
451
452 PMIB_IPNETTABLE NetTable = nullptr;
453
454 DWORD i;
455
456 struct arpreq arpReq;
457 memset(&arpReq, '\0', sizeof(arpReq));
458
459 /* Get size of Windows ARP table */
460 if (GetIpNetTable(NetTable, &ipNetTableLen, FALSE) != ERROR_INSUFFICIENT_BUFFER) {
461 debugs(28, DBG_CRITICAL, "ERROR: Cannot estimate ARP table size!");
462 clear();
463 return false;
464 }
465
466 /* Allocate space for ARP table and assign pointers */
467 if ((NetTable = (PMIB_IPNETTABLE)xmalloc(ipNetTableLen)) == NULL) {
468 debugs(28, DBG_CRITICAL, "ERROR: Cannot allocate temporary ARP table!");
469 clear();
470 return false;
471 }
472
473 /* Get actual ARP table */
474 if ((dwNetTable = GetIpNetTable(NetTable, &ipNetTableLen, FALSE)) != NO_ERROR) {
475 debugs(28, DBG_CRITICAL, "ERROR: Cannot retrieve ARP table!");
476 xfree(NetTable);
477 clear();
478 return false;
479 }
480
481 /* Find MAC address from net table */
482 for (i = 0 ; i < NetTable->dwNumEntries ; ++i) {
483 in_addr a;
484 a.s_addr = NetTable->table[i].dwAddr;
485 if (c == a && (NetTable->table[i].dwType > 2)) {
486 arpReq.arp_ha.sa_family = AF_UNSPEC;
487 memcpy(arpReq.arp_ha.sa_data, NetTable->table[i].bPhysAddr, NetTable->table[i].dwPhysAddrLen);
488 }
489 }
490
491 xfree(NetTable);
492
493 if (arpReq.arp_ha.sa_data[0] == 0 && arpReq.arp_ha.sa_data[1] == 0 &&
494 arpReq.arp_ha.sa_data[2] == 0 && arpReq.arp_ha.sa_data[3] == 0 &&
495 arpReq.arp_ha.sa_data[4] == 0 && arpReq.arp_ha.sa_data[5] == 0) {
496 clear();
497 return false;
498 }
499
500 debugs(28, 4, "Got address " << AsEui48(&arpReq.arp_ha));
501
502 set(arpReq.arp_ha.sa_data, 6);
503 return true;
504
505#else
506
507 debugs(28, DBG_CRITICAL, "ERROR: ARP / MAC / EUI-* operations not supported on this operating system.");
508
509#endif
510 /*
511 * Address was not found on any interface
512 */
513 debugs(28, 3, "id=" << (void*)this << ' ' << ipAddr << " NOT found");
514
515 clear();
516 return false;
517}
518
519/* ==== END EUI LOOKUP SUPPORT =============================================== */
520
521#endif /* USE_SQUID_EUI */
522
static std::ostream & operator<<(std::ostream &os, const AsEui48< HardwareAddress > &manipulator)
Definition Eui48.cc:110
#define SZ_EUI48_BUF
Definition Eui48.h:15
AsHex< Integer > asHex(const Integer n)
a helper to ease AsHex object creation
Definition IoManip.h:169
I/O manipulator to print EUI48 addresses.
Definition Eui48.cc:101
const HardwareAddress *const hardwareAddress
Definition Eui48.cc:105
AsEui48(const HardwareAddress *const a)
caller is responsible for the passed address storage lifetime
Definition Eui48.cc:104
unsigned char eui[SZ_EUI48_BUF]
Definition Eui48.h:71
void clear()
Definition Eui48.h:44
bool lookup(const Ip::Address &c)
Definition Eui48.cc:162
bool encode(char *buf, const int len) const
Definition Eui48.cc:146
bool decode(const char *asc)
Definition Eui48.cc:124
void getSockAddr(struct sockaddr_storage &addr, const int family) const
Definition Address.cc:936
unsigned short port() const
Definition Address.cc:790
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
#define FALSE
Definition defines.h:16
#define xfree
#define xmalloc
int xsocket(int domain, int type, int protocol)
POSIX socket(2) equivalent.
Definition socket.h:128
#define NULL
Definition types.h:145
int xclose(int fd)
POSIX close(2) equivalent.
Definition unistd.h:43
const char * xstrerr(int error)
Definition xstrerror.cc:83