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

 

Introduction

Documentation

Support

Miscellaneous