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_
32 struct 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 
100 template <typename HardwareAddress>
101 class AsEui48 {
102 public:
104  explicit AsEui48(const HardwareAddress * const a): hardwareAddress(a) {}
105  const HardwareAddress * const hardwareAddress;
106 };
107 
108 template <typename HardwareAddress>
109 static std::ostream &
110 operator <<(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 
123 bool
124 Eui::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 
145 bool
146 Eui::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
161 bool
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: " << arpReq.arp_ha.sa_data);
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 
#define FALSE
Definition: defines.h:16
void clear()
Definition: Eui48.h:44
const char * xstrerr(int error)
Definition: xstrerror.cc:83
#define DBG_CRITICAL
Definition: Stream.h:37
I/O manipulator to print EUI48 addresses.
Definition: Eui48.cc:101
#define xmalloc
static std::ostream & operator<<(std::ostream &os, const AsEui48< HardwareAddress > &manipulator)
Definition: Eui48.cc:110
#define SZ_EUI48_BUF
Definition: Eui48.h:15
bool encode(char *buf, const int len) const
Definition: Eui48.cc:146
bool decode(const char *asc)
Definition: Eui48.cc:124
bool lookup(const Ip::Address &c)
Definition: Eui48.cc:162
unsigned char eui[SZ_EUI48_BUF]
Definition: Eui48.h:71
#define NULL
Definition: types.h:145
unsigned short port() const
Definition: Address.cc:790
AsHex< Integer > asHex(const Integer n)
a helper to ease AsHex object creation
Definition: IoManip.h:169
AsEui48(const HardwareAddress *const a)
caller is responsible for the passed address storage lifetime
Definition: Eui48.cc:104
#define xfree
int xsocket(int domain, int type, int protocol)
POSIX socket(2) equivalent.
Definition: socket.h:128
#define DBG_IMPORTANT
Definition: Stream.h:38
const HardwareAddress *const hardwareAddress
Definition: Eui48.cc:105
int xclose(int fd)
POSIX close(2) equivalent.
Definition: unistd.h:43
void getSockAddr(struct sockaddr_storage &addr, const int family) const
Definition: Address.cc:936
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192

 

Introduction

Documentation

Support

Miscellaneous