]> git.ipfire.org Git - thirdparty/squid.git/blob - src/eui/Eui48.cc
Merged from trunk (r13515).
[thirdparty/squid.git] / src / eui / Eui48.cc
1 /*
2 * DEBUG: section 89 EUI-48 Lookup
3 * AUTHOR: Duane Wessels
4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 *
32 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
33 */
34
35 #include "squid.h"
36
37 #if USE_SQUID_EUI
38
39 #include "Debug.h"
40 #include "eui/Eui48.h"
41 #include "globals.h"
42 #include "ip/Address.h"
43
44 #include <cerrno>
45
46 /* START Legacy includes pattern */
47 /* TODO: clean this up so we dont have per-OS requirements.
48 The files are checked for existence individually
49 and can be wrapped
50 */
51
52 #if _SQUID_WINDOWS_
53 struct arpreq {
54
55 Ip::Address arp_pa; /* protocol address */
56
57 struct sockaddr arp_ha; /* hardware address */
58 int arp_flags; /* flags */
59 };
60 #if HAVE_IPHLPAPI_H
61 #include <iphlpapi.h>
62 #endif
63 #endif
64
65 #if HAVE_SYS_PARAM_H
66 #include <sys/param.h>
67 #endif
68 #if HAVE_SYS_SOCKIO_H
69 /* required by Solaris */
70 #include <sys/sockio.h>
71 #endif
72 #if HAVE_SYS_SYSCTL_H
73 #include <sys/sysctl.h>
74 #endif
75 #if HAVE_NET_ROUTE_H
76 #include <net/route.h>
77 #endif
78 #if HAVE_NET_IF_H
79 #include <net/if.h>
80 #endif
81 #if HAVE_NET_IF_ARP_H
82 #include <net/if_arp.h>
83 #endif
84 #if HAVE_NET_IF_DL_H
85 #include <net/if_dl.h>
86 #endif
87 #if HAVE_NETINET_IF_ETHER_H
88 #include <netinet/if_ether.h>
89 #endif
90 #if HAVE_SYS_IOCTL_H
91 #include <sys/ioctl.h>
92 #endif
93
94 /* ==== BEGIN EUI LOOKUP SUPPORT ============================================= */
95
96 /*
97 * From: dale@server.ctam.bitmcnit.bryansk.su (Dale)
98 * To: wessels@nlanr.net
99 * Subject: Another Squid patch... :)
100 * Date: Thu, 04 Dec 1997 19:55:01 +0300
101 * ============================================================================
102 *
103 * Working on setting up a proper firewall for a network containing some
104 * Win'95 computers at our Univ, I've discovered that some smart students
105 * avoid the restrictions easily just changing their IP addresses in Win'95
106 * Contol Panel... It has been getting boring, so I took Squid-1.1.18
107 * sources and added a new acl type for hard-wired access control:
108 *
109 * acl <name> arp <Ethernet address> ...
110 *
111 * For example,
112 *
113 * acl students arp 00:00:21:55:ed:22 00:00:21:ff:55:38
114 *
115 * NOTE: Linux code by David Luyer <luyer@ucs.uwa.edu.au>.
116 * Original (BSD-specific) code no longer works.
117 * Solaris code by R. Gancarz <radekg@solaris.elektrownia-lagisza.com.pl>
118 */
119
120 bool
121 Eui::Eui48::decode(const char *asc)
122 {
123 int a1 = 0, a2 = 0, a3 = 0, a4 = 0, a5 = 0, a6 = 0;
124
125 if (sscanf(asc, "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6) != 6) {
126 debugs(28, DBG_CRITICAL, "Decode EUI-48: Invalid ethernet address '" << asc << "'");
127 clear();
128 return false; /* This is not valid address */
129 }
130
131 eui[0] = (u_char) a1;
132 eui[1] = (u_char) a2;
133 eui[2] = (u_char) a3;
134 eui[3] = (u_char) a4;
135 eui[4] = (u_char) a5;
136 eui[5] = (u_char) a6;
137
138 debugs(28, 4, "id=" << (void*)this << " decoded " << asc);
139 return true;
140 }
141
142 bool
143 Eui::Eui48::encode(char *buf, const int len)
144 {
145 if (len < SZ_EUI48_BUF)
146 return false;
147
148 snprintf(buf, len, "%02x:%02x:%02x:%02x:%02x:%02x",
149 eui[0] & 0xff, eui[1] & 0xff,
150 eui[2] & 0xff, eui[3] & 0xff,
151 eui[4] & 0xff, eui[5] & 0xff);
152
153 debugs(28, 4, "id=" << (void*)this << " encoded " << buf);
154 return true;
155 }
156
157 // return binary representation of the EUI
158 bool
159 Eui::Eui48::lookup(const Ip::Address &c)
160 {
161 Ip::Address ipAddr = c;
162 ipAddr.port(0);
163
164 #if _SQUID_LINUX_
165
166 unsigned char ifbuffer[sizeof(struct ifreq) * 64];
167 struct ifconf ifc;
168
169 struct ifreq *ifr;
170 int offset;
171
172 /* IPv6 builds do not provide the first http_port as an IPv4 socket for ARP */
173 int tmpSocket = socket(AF_INET,SOCK_STREAM,0);
174 if (tmpSocket < 0) {
175 debugs(28, DBG_IMPORTANT, "Attempt to open socket for EUI retrieval failed: " << xstrerror());
176 clear();
177 return false;
178 }
179
180 /*
181 * The linux kernel 2.2 maintains per interface ARP caches and
182 * thus requires an interface name when doing ARP queries.
183 *
184 * The older 2.0 kernels appear to use a unified ARP cache,
185 * and require an empty interface name
186 *
187 * To support both, we attempt the lookup with a blank interface
188 * name first. If that does not succeed, the try each interface
189 * in turn
190 */
191
192 /*
193 * Set up structures for ARP lookup with blank interface name
194 */
195 struct arpreq arpReq;
196 memset(&arpReq, '\0', sizeof(arpReq));
197
198 struct sockaddr_in *sa = (struct sockaddr_in*)&arpReq.arp_pa;
199 ipAddr.getSockAddr(*sa);
200
201 /* Query ARP table */
202 debugs(28, 4, "id=" << (void*)this << " query ARP table");
203 if (ioctl(tmpSocket, SIOCGARP, &arpReq) != -1) {
204 /* Skip non-ethernet interfaces */
205 close(tmpSocket);
206
207 if (arpReq.arp_ha.sa_family != ARPHRD_ETHER) {
208 clear();
209 return false;
210 }
211
212 debugs(28, 4, "id=" << (void*)this << " got address "<< std::setfill('0') << std::hex <<
213 std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff) << ":" <<
214 std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff) << ":" <<
215 std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff) << ":" <<
216 std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff) << ":" <<
217 std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff) << ":" <<
218 std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff));
219
220 set(arpReq.arp_ha.sa_data, 6);
221 return true;
222 }
223
224 /* lookup list of interface names */
225 ifc.ifc_len = sizeof(ifbuffer);
226
227 ifc.ifc_buf = (char *)ifbuffer;
228
229 if (ioctl(tmpSocket, SIOCGIFCONF, &ifc) < 0) {
230 debugs(28, DBG_IMPORTANT, "Attempt to retrieve interface list failed: " << xstrerror());
231 clear();
232 close(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 close(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 /*
276 * Query failed. Do not log failed lookups or "device
277 * not supported"
278 */
279
280 if (ENXIO == errno)
281 (void) 0;
282 else if (ENODEV == errno)
283 (void) 0;
284 else
285 debugs(28, DBG_IMPORTANT, "ARP query " << ipAddr << " failed: " << ifr->ifr_name << ": " << xstrerror());
286
287 continue;
288 }
289
290 /* Skip non-ethernet interfaces */
291 if (arpReq.arp_ha.sa_family != ARPHRD_ETHER) {
292 debugs(28, 4, "id=" << (void*)this << "... not an Ethernet interface");
293 continue;
294 }
295
296 debugs(28, 4, "id=" << (void*)this << " got address "<< std::setfill('0') << std::hex <<
297 std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff) << ":" <<
298 std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff) << ":" <<
299 std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff) << ":" <<
300 std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff) << ":" <<
301 std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff) << ":" <<
302 std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff) << " on "<<
303 std::setfill(' ') << ifr->ifr_name);
304
305 set(arpReq.arp_ha.sa_data, 6);
306
307 /*
308 * Should we stop looking here? Can the same IP address
309 * exist on multiple interfaces?
310 */
311
312 /* AYJ: 2009-10-06: for now we have to. We can only store one EUI at a time. */
313 close(tmpSocket);
314 return true;
315 }
316
317 close(tmpSocket);
318
319 #elif _SQUID_SOLARIS_
320
321 /* IPv6 builds do not provide the first http_port as an IPv4 socket for ARP */
322 int tmpSocket = socket(AF_INET,SOCK_STREAM,0);
323 if (tmpSocket < 0) {
324 debugs(28, DBG_IMPORTANT, "Attempt to open socket for EUI retrieval failed: " << xstrerror());
325 clear();
326 return false;
327 }
328
329 /* Set up structures for ARP lookup with blank interface name */
330 struct arpreq arpReq;
331 memset(&arpReq, '\0', sizeof(arpReq));
332
333 struct sockaddr_in *sa = (struct sockaddr_in*)&arpReq.arp_pa;
334 ipAddr.getSockAddr(*sa);
335
336 /* Query ARP table */
337 if (ioctl(tmpSocket, SIOCGARP, &arpReq) != -1) {
338 /*
339 * Solaris (at least 2.6/x86) does not use arp_ha.sa_family -
340 * it returns 00:00:00:00:00:00 for non-ethernet media
341 */
342 close(tmpSocket);
343
344 if (arpReq.arp_ha.sa_data[0] == 0 &&
345 arpReq.arp_ha.sa_data[1] == 0 &&
346 arpReq.arp_ha.sa_data[2] == 0 &&
347 arpReq.arp_ha.sa_data[3] == 0 &&
348 arpReq.arp_ha.sa_data[4] == 0 && arpReq.arp_ha.sa_data[5] == 0) {
349 clear();
350 return false;
351 }
352
353 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex <<
354 std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff) << ":" <<
355 std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff) << ":" <<
356 std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff) << ":" <<
357 std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff) << ":" <<
358 std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff) << ":" <<
359 std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff));
360
361 set(arpReq.arp_ha.sa_data, 6);
362 return true;
363 } else {
364 close(tmpSocket);
365 }
366
367 #elif _SQUID_FREEBSD_ || _SQUID_NETBSD_ || _SQUID_OPENBSD_ || _SQUID_DRAGONFLY_ || _SQUID_KFREEBSD_
368
369 int mib[6];
370
371 size_t needed;
372
373 char *lim, *buf, *next;
374
375 struct rt_msghdr *rtm;
376
377 struct sockaddr_inarp *sin;
378
379 struct sockaddr_dl *sdl;
380
381 /*
382 * Set up structures for ARP lookup with blank interface name
383 */
384 struct arpreq arpReq;
385 memset(&arpReq, '\0', sizeof(arpReq));
386
387 struct sockaddr_in *sa = (struct sockaddr_in*)&arpReq.arp_pa;
388 ipAddr.getSockAddr(*sa);
389
390 /* Query ARP table */
391 mib[0] = CTL_NET;
392
393 mib[1] = PF_ROUTE;
394
395 mib[2] = 0;
396
397 mib[3] = AF_INET;
398
399 mib[4] = NET_RT_FLAGS;
400
401 mib[5] = RTF_LLINFO;
402
403 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
404 debugs(28, DBG_CRITICAL, "Can't estimate ARP table size!");
405 clear();
406 return false;
407 }
408
409 if ((buf = (char *)xmalloc(needed)) == NULL) {
410 debugs(28, DBG_CRITICAL, "Can't allocate temporary ARP table!");
411 clear();
412 return false;
413 }
414
415 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
416 debugs(28, DBG_CRITICAL, "Can't retrieve ARP table!");
417 xfree(buf);
418 clear();
419 return false;
420 }
421
422 lim = buf + needed;
423
424 for (next = buf; next < lim; next += rtm->rtm_msglen) {
425
426 rtm = (struct rt_msghdr *) next;
427
428 sin = (struct sockaddr_inarp *) (rtm + 1);
429 /*sdl = (struct sockaddr_dl *) (sin + 1); */
430
431 #define ROUNDUP(a) \
432 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
433
434 sdl = (struct sockaddr_dl *)((char *) sin + ROUNDUP(sin->sin_len));
435
436 if (ipAddr == sin->sin_addr) {
437 if (sdl->sdl_alen) {
438
439 arpReq.arp_ha.sa_len = sizeof(struct sockaddr);
440 arpReq.arp_ha.sa_family = AF_UNSPEC;
441 memcpy(arpReq.arp_ha.sa_data, LLADDR(sdl), sdl->sdl_alen);
442 }
443 }
444 }
445
446 xfree(buf);
447
448 if (arpReq.arp_ha.sa_data[0] == 0 && arpReq.arp_ha.sa_data[1] == 0 &&
449 arpReq.arp_ha.sa_data[2] == 0 && arpReq.arp_ha.sa_data[3] == 0 &&
450 arpReq.arp_ha.sa_data[4] == 0 && arpReq.arp_ha.sa_data[5] == 0) {
451 clear();
452 return false;
453 }
454
455 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex <<
456 std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff) << ":" <<
457 std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff) << ":" <<
458 std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff) << ":" <<
459 std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff) << ":" <<
460 std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff) << ":" <<
461 std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff));
462
463 set(arpReq.arp_ha.sa_data, 6);
464 return true;
465
466 #elif _SQUID_WINDOWS_
467
468 DWORD dwNetTable = 0;
469
470 DWORD ipNetTableLen = 0;
471
472 PMIB_IPNETTABLE NetTable = NULL;
473
474 DWORD i;
475
476 struct arpreq arpReq;
477 memset(&arpReq, '\0', sizeof(arpReq));
478
479 /* Get size of Windows ARP table */
480 if (GetIpNetTable(NetTable, &ipNetTableLen, FALSE) != ERROR_INSUFFICIENT_BUFFER) {
481 debugs(28, DBG_CRITICAL, "Can't estimate ARP table size!");
482 clear();
483 return false;
484 }
485
486 /* Allocate space for ARP table and assign pointers */
487 if ((NetTable = (PMIB_IPNETTABLE)xmalloc(ipNetTableLen)) == NULL) {
488 debugs(28, DBG_CRITICAL, "Can't allocate temporary ARP table!");
489 clear();
490 return false;
491 }
492
493 /* Get actual ARP table */
494 if ((dwNetTable = GetIpNetTable(NetTable, &ipNetTableLen, FALSE)) != NO_ERROR) {
495 debugs(28, DBG_CRITICAL, "Can't retrieve ARP table!");
496 xfree(NetTable);
497 clear();
498 return false;
499 }
500
501 /* Find MAC address from net table */
502 for (i = 0 ; i < NetTable->dwNumEntries ; ++i) {
503 in_addr a;
504 a.s_addr = NetTable->table[i].dwAddr;
505 if (c == a && (NetTable->table[i].dwType > 2)) {
506 arpReq.arp_ha.sa_family = AF_UNSPEC;
507 memcpy(arpReq.arp_ha.sa_data, NetTable->table[i].bPhysAddr, NetTable->table[i].dwPhysAddrLen);
508 }
509 }
510
511 xfree(NetTable);
512
513 if (arpReq.arp_ha.sa_data[0] == 0 && arpReq.arp_ha.sa_data[1] == 0 &&
514 arpReq.arp_ha.sa_data[2] == 0 && arpReq.arp_ha.sa_data[3] == 0 &&
515 arpReq.arp_ha.sa_data[4] == 0 && arpReq.arp_ha.sa_data[5] == 0) {
516 clear();
517 return false;
518 }
519
520 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex <<
521 std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff) << ":" <<
522 std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff) << ":" <<
523 std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff) << ":" <<
524 std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff) << ":" <<
525 std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff) << ":" <<
526 std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff));
527
528 set(arpReq.arp_ha.sa_data, 6);
529 return true;
530
531 #else
532
533 debugs(28, DBG_CRITICAL, "ERROR: ARP / MAC / EUI-* operations not supported on this operating system.");
534
535 #endif
536 /*
537 * Address was not found on any interface
538 */
539 debugs(28, 3, "id=" << (void*)this << ' ' << ipAddr << " NOT found");
540
541 clear();
542 return false;
543 }
544
545 /* ==== END EUI LOOKUP SUPPORT =============================================== */
546
547 #endif /* USE_SQUID_EUI */