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