]> git.ipfire.org Git - thirdparty/squid.git/blob - src/eui/Eui48.cc
Merged from trunk
[thirdparty/squid.git] / src / eui / Eui48.cc
1 /*
2 * DEBUG: section ?? EUI 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 "config.h"
36
37 #if USE_SQUID_EUI
38
39 #include "Debug.h"
40 #include "eui/Eui48.h"
41 #include "globals.h"
42 #include "ip/IpAddress.h"
43
44
45 #if defined(_SQUID_CYGWIN_)
46 #include <squid_windows.h>
47 #endif
48
49 /* START Legacy includes pattern */
50 /* TODO: clean this up so we dont have per-OS requirements.
51 The files are checked for existence individually
52 and can be wrapped
53 */
54
55 #if defined(_SQUID_WIN32_)
56
57 struct arpreq {
58
59 IpAddress arp_pa; /* protocol address */
60
61 struct sockaddr arp_ha; /* hardware address */
62 int arp_flags; /* flags */
63 };
64
65 #include <Iphlpapi.h>
66 #endif
67
68
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 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, 0, "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 return true;
139 }
140
141 bool
142 Eui48::encode(char *buf, const int len)
143 {
144 if (len < SZ_EUI48_BUF) return false;
145
146 snprintf(buf, len, "%02x:%02x:%02x:%02x:%02x:%02x",
147 eui[0] & 0xff, eui[1] & 0xff,
148 eui[2] & 0xff, eui[3] & 0xff,
149 eui[4] & 0xff, eui[5] & 0xff);
150 return true;
151 }
152
153 // return binary representation of the EUI
154 bool
155 Eui48::lookup(IpAddress &c)
156 {
157 struct arpreq arpReq;
158 #if !defined(_SQUID_WIN32_)
159 struct sockaddr_in *sa = NULL;
160 #endif
161
162 IpAddress ipAddr = c;
163 ipAddr.SetPort(0);
164
165 #if defined(_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 /*
174 * The linux kernel 2.2 maintains per interface ARP caches and
175 * thus requires an interface name when doing ARP queries.
176 *
177 * The older 2.0 kernels appear to use a unified ARP cache,
178 * and require an empty interface name
179 *
180 * To support both, we attempt the lookup with a blank interface
181 * name first. If that does not succeed, the try each interface
182 * in turn
183 */
184
185 /*
186 * Set up structures for ARP lookup with blank interface name
187 */
188 memset(&arpReq, '\0', sizeof(arpReq));
189
190 sa = (sockaddr_in*)&arpReq.arp_pa;
191 ipAddr.GetSockAddr(*sa);
192
193 /* Query ARP table */
194 if (ioctl(HttpSockets[0], SIOCGARP, &arpReq) != -1) {
195 /* Skip non-ethernet interfaces */
196
197 if (arpReq.arp_ha.sa_family != ARPHRD_ETHER) {
198 clear();
199 return false;
200 }
201
202 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex <<
203 std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff) << ":" <<
204 std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff) << ":" <<
205 std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff) << ":" <<
206 std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff) << ":" <<
207 std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff) << ":" <<
208 std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff));
209
210 set(arpReq.arp_ha.sa_data, 6);
211 return true;
212 }
213
214 /* lookup list of interface names */
215 ifc.ifc_len = sizeof(ifbuffer);
216
217 ifc.ifc_buf = (char *)ifbuffer;
218
219 if (ioctl(HttpSockets[0], SIOCGIFCONF, &ifc) < 0) {
220 debugs(28, 1, "Attempt to retrieve interface list failed: " << xstrerror());
221 clear();
222 return false;
223 }
224
225 if (ifc.ifc_len > (int)sizeof(ifbuffer)) {
226 debugs(28, 1, "Interface list too long - " << ifc.ifc_len);
227 clear();
228 return false;
229 }
230
231 /* Attempt ARP lookup on each interface */
232 offset = 0;
233
234 while (offset < ifc.ifc_len) {
235
236 ifr = (struct ifreq *) (ifbuffer + offset);
237 offset += sizeof(*ifr);
238 /* Skip loopback and aliased interfaces */
239
240 if (0 == strncmp(ifr->ifr_name, "lo", 2))
241 continue;
242
243 if (NULL != strchr(ifr->ifr_name, ':'))
244 continue;
245
246 debugs(28, 4, "Looking up ARP address for " << ipAddr << " on " << ifr->ifr_name);
247
248 /* Set up structures for ARP lookup */
249
250 memset(&arpReq, '\0', sizeof(arpReq));
251
252 sa = (sockaddr_in*)&arpReq.arp_pa;
253 ipAddr.GetSockAddr(*sa);
254
255 strncpy(arpReq.arp_dev, ifr->ifr_name, sizeof(arpReq.arp_dev) - 1);
256
257 arpReq.arp_dev[sizeof(arpReq.arp_dev) - 1] = '\0';
258
259 /* Query ARP table */
260 if (-1 == ioctl(HttpSockets[0], SIOCGARP, &arpReq)) {
261 /*
262 * Query failed. Do not log failed lookups or "device
263 * not supported"
264 */
265
266 if (ENXIO == errno)
267 (void) 0;
268 else if (ENODEV == errno)
269 (void) 0;
270 else
271 debugs(28, 1, "ARP query failed: " << ifr->ifr_name << ": " << xstrerror());
272
273 continue;
274 }
275
276 /* Skip non-ethernet interfaces */
277 if (arpReq.arp_ha.sa_family != ARPHRD_ETHER)
278 continue;
279
280 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex <<
281 std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff) << ":" <<
282 std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff) << ":" <<
283 std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff) << ":" <<
284 std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff) << ":" <<
285 std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff) << ":" <<
286 std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff) << " on "<<
287 std::setfill(' ') << ifr->ifr_name);
288
289 set(arpReq.arp_ha.sa_data, 6);
290
291 /*
292 * Should we stop looking here? Can the same IP address
293 * exist on multiple interfaces?
294 */
295
296 /* AYJ: 2009-10-06: for now we have to. We can only store one EUI at a time. */
297 return true;
298 }
299
300 #elif defined(_SQUID_SOLARIS_)
301
302 /* Set up structures for ARP lookup with blank interface name */
303
304 memset(&arpReq, '\0', sizeof(arpReq));
305
306 sa = (sockaddr_in*)&arpReq.arp_pa;
307 ipAddr.GetSockAddr(*sa);
308
309 /* Query ARP table */
310 if (ioctl(HttpSockets[0], SIOCGARP, &arpReq) != -1) {
311 /*
312 * Solaris (at least 2.6/x86) does not use arp_ha.sa_family -
313 * it returns 00:00:00:00:00:00 for non-ethernet media
314 */
315
316 if (arpReq.arp_ha.sa_data[0] == 0 &&
317 arpReq.arp_ha.sa_data[1] == 0 &&
318 arpReq.arp_ha.sa_data[2] == 0 &&
319 arpReq.arp_ha.sa_data[3] == 0 &&
320 arpReq.arp_ha.sa_data[4] == 0 && arpReq.arp_ha.sa_data[5] == 0) {
321 clear();
322 return false;
323 }
324
325 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex <<
326 std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff) << ":" <<
327 std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff) << ":" <<
328 std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff) << ":" <<
329 std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff) << ":" <<
330 std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff) << ":" <<
331 std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff));
332
333 set(arpReq.arp_ha.sa_data, 6);
334 return true;
335 }
336
337 #elif defined(_SQUID_FREEBSD_) || defined(_SQUID_NETBSD_) || defined(_SQUID_OPENBSD_) || defined(_SQUID_DRAGONFLY_) || defined(_SQUID_KFREEBSD_)
338
339 int mib[6];
340
341 size_t needed;
342
343 char *lim, *buf, *next;
344
345 struct rt_msghdr *rtm;
346
347 struct sockaddr_inarp *sin;
348
349 struct sockaddr_dl *sdl;
350
351 /*
352 * Set up structures for ARP lookup with blank interface name
353 */
354
355 memset(&arpReq, '\0', sizeof(arpReq));
356
357 sa = (struct sockaddr_in*) &arpReq.arp_pa;
358 ipAddr.GetSockAddr(*sa);
359
360 /* Query ARP table */
361 mib[0] = CTL_NET;
362
363 mib[1] = PF_ROUTE;
364
365 mib[2] = 0;
366
367 mib[3] = AF_INET;
368
369 mib[4] = NET_RT_FLAGS;
370
371 mib[5] = RTF_LLINFO;
372
373 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
374 debugs(28, 0, "Can't estimate ARP table size!");
375 clear();
376 return false;
377 }
378
379 if ((buf = (char *)xmalloc(needed)) == NULL) {
380 debugs(28, 0, "Can't allocate temporary ARP table!");
381 clear();
382 return false;
383 }
384
385 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
386 debugs(28, 0, "Can't retrieve ARP table!");
387 xfree(buf);
388 clear();
389 return false;
390 }
391
392 lim = buf + needed;
393
394 for (next = buf; next < lim; next += rtm->rtm_msglen) {
395
396 rtm = (struct rt_msghdr *) next;
397
398 sin = (struct sockaddr_inarp *) (rtm + 1);
399 /*sdl = (struct sockaddr_dl *) (sin + 1); */
400
401 #define ROUNDUP(a) \
402 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
403
404 sdl = (struct sockaddr_dl *)((char *) sin + ROUNDUP(sin->sin_len));
405
406 if (ipAddr == sin->sin_addr) {
407 if (sdl->sdl_alen) {
408
409 arpReq.arp_ha.sa_len = sizeof(struct sockaddr);
410 arpReq.arp_ha.sa_family = AF_UNSPEC;
411 memcpy(arpReq.arp_ha.sa_data, LLADDR(sdl), sdl->sdl_alen);
412 }
413 }
414 }
415
416 xfree(buf);
417
418 if (arpReq.arp_ha.sa_data[0] == 0 && arpReq.arp_ha.sa_data[1] == 0 &&
419 arpReq.arp_ha.sa_data[2] == 0 && arpReq.arp_ha.sa_data[3] == 0 &&
420 arpReq.arp_ha.sa_data[4] == 0 && arpReq.arp_ha.sa_data[5] == 0) {
421 clear();
422 return false;
423 }
424
425 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex <<
426 std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff) << ":" <<
427 std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff) << ":" <<
428 std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff) << ":" <<
429 std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff) << ":" <<
430 std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff) << ":" <<
431 std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff));
432
433 set(arpReq.arp_ha.sa_data, 6);
434 return true;
435
436 #elif defined(_SQUID_WIN32_)
437
438 DWORD dwNetTable = 0;
439
440 DWORD ipNetTableLen = 0;
441
442 PMIB_IPNETTABLE NetTable = NULL;
443
444 DWORD i;
445
446 memset(&arpReq, '\0', sizeof(arpReq));
447
448 /* Get size of Windows ARP table */
449 if (GetIpNetTable(NetTable, &ipNetTableLen, FALSE) != ERROR_INSUFFICIENT_BUFFER) {
450 debugs(28, 0, "Can't estimate ARP table size!");
451 clear();
452 return false;
453 }
454
455 /* Allocate space for ARP table and assign pointers */
456 if ((NetTable = (PMIB_IPNETTABLE)xmalloc(ipNetTableLen)) == NULL) {
457 debugs(28, 0, "Can't allocate temporary ARP table!");
458 clear();
459 return false;
460 }
461
462 /* Get actual ARP table */
463 if ((dwNetTable = GetIpNetTable(NetTable, &ipNetTableLen, FALSE)) != NO_ERROR) {
464 debugs(28, 0, "Can't retrieve ARP table!");
465 xfree(NetTable);
466 clear();
467 return false;
468 }
469
470 /* Find MAC address from net table */
471 for (i = 0 ; i < NetTable->dwNumEntries ; i++) {
472 in_addr a;
473 a.s_addr = NetTable->table[i].dwAddr;
474 if (c == a && (NetTable->table[i].dwType > 2)) {
475 arpReq.arp_ha.sa_family = AF_UNSPEC;
476 memcpy(arpReq.arp_ha.sa_data, NetTable->table[i].bPhysAddr, NetTable->table[i].dwPhysAddrLen);
477 }
478 }
479
480 xfree(NetTable);
481
482 if (arpReq.arp_ha.sa_data[0] == 0 && arpReq.arp_ha.sa_data[1] == 0 &&
483 arpReq.arp_ha.sa_data[2] == 0 && arpReq.arp_ha.sa_data[3] == 0 &&
484 arpReq.arp_ha.sa_data[4] == 0 && arpReq.arp_ha.sa_data[5] == 0) {
485 clear();
486 return false;
487 }
488
489 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex <<
490 std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff) << ":" <<
491 std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff) << ":" <<
492 std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff) << ":" <<
493 std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff) << ":" <<
494 std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff) << ":" <<
495 std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff));
496
497 set(arpReq.arp_ha.sa_data, 6);
498 return true;
499
500 #else
501
502 debugs(28, 0, "ERROR: ARP / MAC / EUI-* operations not supported on this operating system.");
503
504 #endif
505 /*
506 * Address was not found on any interface
507 */
508 debugs(28, 3, HERE << ipAddr << " NOT found");
509
510 clear();
511 return false;
512 }
513
514 /* ==== END EUI LOOKUP SUPPORT =============================================== */
515
516 #endif /* USE_SQUID_EUI */