]> git.ipfire.org Git - thirdparty/squid.git/blob - src/eui/Eui48.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / eui / Eui48.cc
1 /*
2 * Copyright (C) 1996-2016 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 "Debug.h"
16 #include "eui/Eui48.h"
17 #include "globals.h"
18 #include "ip/Address.h"
19
20 #include <cerrno>
21
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
25 and can be wrapped
26 */
27
28 #if _SQUID_WINDOWS_
29 struct arpreq {
30
31 Ip::Address arp_pa; /* protocol address */
32
33 struct sockaddr arp_ha; /* hardware address */
34 int arp_flags; /* flags */
35 };
36 #if HAVE_IPHLPAPI_H
37 #include <iphlpapi.h>
38 #endif
39 #endif
40
41 #if HAVE_SYS_PARAM_H
42 #include <sys/param.h>
43 #endif
44 #if HAVE_SYS_SOCKIO_H
45 /* required by Solaris */
46 #include <sys/sockio.h>
47 #endif
48 #if HAVE_SYS_SYSCTL_H
49 #include <sys/sysctl.h>
50 #endif
51 #if HAVE_NET_ROUTE_H
52 #include <net/route.h>
53 #endif
54 #if HAVE_NET_IF_H
55 #include <net/if.h>
56 #endif
57 #if HAVE_NET_IF_ARP_H
58 #include <net/if_arp.h>
59 #endif
60 #if HAVE_NET_IF_DL_H
61 #include <net/if_dl.h>
62 #endif
63 #if HAVE_NETINET_IF_ETHER_H
64 #include <netinet/if_ether.h>
65 #endif
66 #if HAVE_SYS_IOCTL_H
67 #include <sys/ioctl.h>
68 #endif
69
70 /* ==== BEGIN EUI LOOKUP SUPPORT ============================================= */
71
72 /*
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 * ============================================================================
78 *
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:
84 *
85 * acl <name> arp <Ethernet address> ...
86 *
87 * For example,
88 *
89 * acl students arp 00:00:21:55:ed:22 00:00:21:ff:55:38
90 *
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>
94 */
95
96 bool
97 Eui::Eui48::decode(const char *asc)
98 {
99 int a1 = 0, a2 = 0, a3 = 0, a4 = 0, a5 = 0, a6 = 0;
100
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 << "'");
103 clear();
104 return false; /* This is not valid address */
105 }
106
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;
113
114 debugs(28, 4, "id=" << (void*)this << " decoded " << asc);
115 return true;
116 }
117
118 bool
119 Eui::Eui48::encode(char *buf, const int len) const
120 {
121 if (len < SZ_EUI48_BUF)
122 return false;
123
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);
128
129 debugs(28, 4, "id=" << (void*)this << " encoded " << buf);
130 return true;
131 }
132
133 // return binary representation of the EUI
134 bool
135 Eui::Eui48::lookup(const Ip::Address &c)
136 {
137 Ip::Address ipAddr = c;
138 ipAddr.port(0);
139
140 #if _SQUID_LINUX_
141
142 unsigned char ifbuffer[sizeof(struct ifreq) * 64];
143 struct ifconf ifc;
144
145 struct ifreq *ifr;
146 int offset;
147
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);
150 if (tmpSocket < 0) {
151 debugs(28, DBG_IMPORTANT, "Attempt to open socket for EUI retrieval failed: " << xstrerror());
152 clear();
153 return false;
154 }
155
156 /*
157 * The linux kernel 2.2 maintains per interface ARP caches and
158 * thus requires an interface name when doing ARP queries.
159 *
160 * The older 2.0 kernels appear to use a unified ARP cache,
161 * and require an empty interface name
162 *
163 * To support both, we attempt the lookup with a blank interface
164 * name first. If that does not succeed, the try each interface
165 * in turn
166 */
167
168 /*
169 * Set up structures for ARP lookup with blank interface name
170 */
171 struct arpreq arpReq;
172 memset(&arpReq, '\0', sizeof(arpReq));
173
174 struct sockaddr_in *sa = (struct sockaddr_in*)&arpReq.arp_pa;
175 ipAddr.getSockAddr(*sa);
176
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 */
181 close(tmpSocket);
182
183 if (arpReq.arp_ha.sa_family != ARPHRD_ETHER) {
184 clear();
185 return false;
186 }
187
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));
195
196 set(arpReq.arp_ha.sa_data, 6);
197 return true;
198 }
199
200 /* lookup list of interface names */
201 ifc.ifc_len = sizeof(ifbuffer);
202
203 ifc.ifc_buf = (char *)ifbuffer;
204
205 if (ioctl(tmpSocket, SIOCGIFCONF, &ifc) < 0) {
206 debugs(28, DBG_IMPORTANT, "Attempt to retrieve interface list failed: " << xstrerror());
207 clear();
208 close(tmpSocket);
209 return false;
210 }
211
212 if (ifc.ifc_len > (int)sizeof(ifbuffer)) {
213 debugs(28, DBG_IMPORTANT, "Interface list too long - " << ifc.ifc_len);
214 clear();
215 close(tmpSocket);
216 return false;
217 }
218
219 /* Attempt ARP lookup on each interface */
220 offset = 0;
221 debugs(28, 4, "id=" << (void*)this << " query ARP on each interface (" << ifc.ifc_len << " found)");
222 while (offset < ifc.ifc_len) {
223
224 ifr = (struct ifreq *) (ifbuffer + offset);
225 offset += sizeof(*ifr);
226
227 debugs(28, 4, "id=" << (void*)this << " found interface " << ifr->ifr_name);
228
229 /* Skip loopback and aliased interfaces */
230 if (!strncmp(ifr->ifr_name, "lo", 2))
231 continue;
232
233 if (strchr(ifr->ifr_name, ':'))
234 continue;
235
236 debugs(28, 4, "id=" << (void*)this << " looking up ARP address for " << ipAddr << " on " << ifr->ifr_name);
237
238 /* Set up structures for ARP lookup */
239
240 memset(&arpReq, '\0', sizeof(arpReq));
241
242 sa = (sockaddr_in*)&arpReq.arp_pa;
243 ipAddr.getSockAddr(*sa);
244
245 strncpy(arpReq.arp_dev, ifr->ifr_name, sizeof(arpReq.arp_dev) - 1);
246
247 arpReq.arp_dev[sizeof(arpReq.arp_dev) - 1] = '\0';
248
249 /* Query ARP table */
250 if (-1 == ioctl(tmpSocket, SIOCGARP, &arpReq)) {
251 /*
252 * Query failed. Do not log failed lookups or "device
253 * not supported"
254 */
255
256 if (ENXIO == errno)
257 (void) 0;
258 else if (ENODEV == errno)
259 (void) 0;
260 else
261 debugs(28, DBG_IMPORTANT, "ARP query " << ipAddr << " failed: " << ifr->ifr_name << ": " << xstrerror());
262
263 continue;
264 }
265
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");
269 continue;
270 }
271
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);
280
281 set(arpReq.arp_ha.sa_data, 6);
282
283 /*
284 * Should we stop looking here? Can the same IP address
285 * exist on multiple interfaces?
286 */
287
288 /* AYJ: 2009-10-06: for now we have to. We can only store one EUI at a time. */
289 close(tmpSocket);
290 return true;
291 }
292
293 close(tmpSocket);
294
295 #elif _SQUID_SOLARIS_
296
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);
299 if (tmpSocket < 0) {
300 debugs(28, DBG_IMPORTANT, "Attempt to open socket for EUI retrieval failed: " << xstrerror());
301 clear();
302 return false;
303 }
304
305 /* Set up structures for ARP lookup with blank interface name */
306 struct arpreq arpReq;
307 memset(&arpReq, '\0', sizeof(arpReq));
308
309 struct sockaddr_in *sa = (struct sockaddr_in*)&arpReq.arp_pa;
310 ipAddr.getSockAddr(*sa);
311
312 /* Query ARP table */
313 if (ioctl(tmpSocket, SIOCGARP, &arpReq) != -1) {
314 /*
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
317 */
318 close(tmpSocket);
319
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) {
325 clear();
326 return false;
327 }
328
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));
336
337 set(arpReq.arp_ha.sa_data, 6);
338 return true;
339 } else {
340 close(tmpSocket);
341 }
342
343 #elif _SQUID_FREEBSD_ || _SQUID_NETBSD_ || _SQUID_OPENBSD_ || _SQUID_DRAGONFLY_ || _SQUID_KFREEBSD_
344
345 int mib[6];
346
347 size_t needed;
348
349 char *lim, *buf, *next;
350
351 struct rt_msghdr *rtm;
352
353 struct sockaddr_inarp *sin;
354
355 struct sockaddr_dl *sdl;
356
357 /*
358 * Set up structures for ARP lookup with blank interface name
359 */
360 struct arpreq arpReq;
361 memset(&arpReq, '\0', sizeof(arpReq));
362
363 struct sockaddr_in *sa = (struct sockaddr_in*)&arpReq.arp_pa;
364 ipAddr.getSockAddr(*sa);
365
366 /* Query ARP table */
367 mib[0] = CTL_NET;
368
369 mib[1] = PF_ROUTE;
370
371 mib[2] = 0;
372
373 mib[3] = AF_INET;
374
375 mib[4] = NET_RT_FLAGS;
376
377 mib[5] = RTF_LLINFO;
378
379 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
380 debugs(28, DBG_CRITICAL, "Can't estimate ARP table size!");
381 clear();
382 return false;
383 }
384
385 if ((buf = (char *)xmalloc(needed)) == NULL) {
386 debugs(28, DBG_CRITICAL, "Can't allocate temporary ARP table!");
387 clear();
388 return false;
389 }
390
391 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
392 debugs(28, DBG_CRITICAL, "Can't retrieve ARP table!");
393 xfree(buf);
394 clear();
395 return false;
396 }
397
398 lim = buf + needed;
399
400 for (next = buf; next < lim; next += rtm->rtm_msglen) {
401
402 rtm = (struct rt_msghdr *) next;
403
404 sin = (struct sockaddr_inarp *) (rtm + 1);
405 /*sdl = (struct sockaddr_dl *) (sin + 1); */
406
407 #define ROUNDUP(a) \
408 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
409
410 sdl = (struct sockaddr_dl *)((char *) sin + ROUNDUP(sin->sin_len));
411
412 if (ipAddr == sin->sin_addr) {
413 if (sdl->sdl_alen) {
414
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);
418 }
419 }
420 }
421
422 xfree(buf);
423
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) {
427 clear();
428 return false;
429 }
430
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));
438
439 set(arpReq.arp_ha.sa_data, 6);
440 return true;
441
442 #elif _SQUID_WINDOWS_
443
444 DWORD dwNetTable = 0;
445
446 DWORD ipNetTableLen = 0;
447
448 PMIB_IPNETTABLE NetTable = NULL;
449
450 DWORD i;
451
452 struct arpreq arpReq;
453 memset(&arpReq, '\0', sizeof(arpReq));
454
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!");
458 clear();
459 return false;
460 }
461
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!");
465 clear();
466 return false;
467 }
468
469 /* Get actual ARP table */
470 if ((dwNetTable = GetIpNetTable(NetTable, &ipNetTableLen, FALSE)) != NO_ERROR) {
471 debugs(28, DBG_CRITICAL, "Can't retrieve ARP table!");
472 xfree(NetTable);
473 clear();
474 return false;
475 }
476
477 /* Find MAC address from net table */
478 for (i = 0 ; i < NetTable->dwNumEntries ; ++i) {
479 in_addr a;
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);
484 }
485 }
486
487 xfree(NetTable);
488
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) {
492 clear();
493 return false;
494 }
495
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));
503
504 set(arpReq.arp_ha.sa_data, 6);
505 return true;
506
507 #else
508
509 debugs(28, DBG_CRITICAL, "ERROR: ARP / MAC / EUI-* operations not supported on this operating system.");
510
511 #endif
512 /*
513 * Address was not found on any interface
514 */
515 debugs(28, 3, "id=" << (void*)this << ' ' << ipAddr << " NOT found");
516
517 clear();
518 return false;
519 }
520
521 /* ==== END EUI LOOKUP SUPPORT =============================================== */
522
523 #endif /* USE_SQUID_EUI */
524