]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ACLARP.cc
Merged from trunk.
[thirdparty/squid.git] / src / ACLARP.cc
1
2 /*
3 * $Id: ACLARP.cc,v 1.29 2008/02/05 22:38:24 amosjeffries Exp $
4 *
5 * DEBUG: section 28 Access Control
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 *
35 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
36 */
37
38 #include "config.h"
39 #ifdef _SQUID_CYGWIN_
40 #include <squid_windows.h>
41 #endif
42 #include "squid.h"
43 #include "IPAddress.h"
44
45 #ifdef _SQUID_WIN32_
46
47 struct arpreq
48 {
49
50 IPAddress arp_pa; /* protocol address */
51
52 struct sockaddr arp_ha; /* hardware address */
53 int arp_flags; /* flags */
54 };
55
56 #include <Iphlpapi.h>
57 #else
58
59 #ifdef _SQUID_SOLARIS_
60 #include <sys/sockio.h>
61 #else
62 #include <sys/sysctl.h>
63 #endif
64 #ifdef _SQUID_LINUX_
65 #include <net/if_arp.h>
66 #include <sys/ioctl.h>
67 #else
68 #include <net/if_dl.h>
69 #endif
70 #include <net/route.h>
71 #include <net/if.h>
72 #if defined(_SQUID_FREEBSD_) || defined(_SQUID_NETBSD_) || defined(_SQUID_OPENBSD_) || defined(_SQUID_DRAGONFLY_)
73 #include <net/if_arp.h>
74 #endif
75 #if HAVE_NETINET_IF_ETHER_H
76 #include <netinet/if_ether.h>
77 #endif
78 #endif
79
80 #include "ACLARP.h"
81 #include "wordlist.h"
82
83 #if !USE_ARP_ACL
84 #error USE_ARP_ACL Not defined
85 #endif
86 static void aclParseArpList(SplayNode<acl_arp_data *> **curlist);
87 static int decode_eth(const char *asc, char *eth);
88 static int aclMatchArp(SplayNode<acl_arp_data *> **dataptr, IPAddress &c);
89 static SplayNode<acl_arp_data *>::SPLAYCMP aclArpCompare;
90 static SplayNode<acl_arp_data *>::SPLAYWALKEE aclDumpArpListWalkee;
91
92 ACL::Prototype ACLARP::RegistryProtoype(&ACLARP::RegistryEntry_, "arp");
93
94 ACLARP ACLARP::RegistryEntry_("arp");
95
96 ACL *
97 ACLARP::clone() const
98 {
99 return new ACLARP(*this);
100 }
101
102 ACLARP::ACLARP (char const *theClass) : data (NULL), class_ (theClass)
103 {}
104
105 ACLARP::ACLARP (ACLARP const & old) : data (NULL), class_ (old.class_)
106 {
107 /* we don't have copy constructors for the data yet */
108 assert (!old.data);
109 }
110
111 ACLARP::~ACLARP()
112 {
113 if (data)
114 data->destroy(SplayNode<acl_arp_data*>::DefaultFree);
115 }
116
117 char const *
118 ACLARP::typeString() const
119 {
120 return class_;
121 }
122
123 bool
124 ACLARP::empty () const
125 {
126 return data->empty();
127 }
128
129 /* ==== BEGIN ARP ACL SUPPORT ============================================= */
130
131 /*
132 * From: dale@server.ctam.bitmcnit.bryansk.su (Dale)
133 * To: wessels@nlanr.net
134 * Subject: Another Squid patch... :)
135 * Date: Thu, 04 Dec 1997 19:55:01 +0300
136 * ============================================================================
137 *
138 * Working on setting up a proper firewall for a network containing some
139 * Win'95 computers at our Univ, I've discovered that some smart students
140 * avoid the restrictions easily just changing their IP addresses in Win'95
141 * Contol Panel... It has been getting boring, so I took Squid-1.1.18
142 * sources and added a new acl type for hard-wired access control:
143 *
144 * acl <name> arp <Ethernet address> ...
145 *
146 * For example,
147 *
148 * acl students arp 00:00:21:55:ed:22 00:00:21:ff:55:38
149 *
150 * NOTE: Linux code by David Luyer <luyer@ucs.uwa.edu.au>.
151 * Original (BSD-specific) code no longer works.
152 * Solaris code by R. Gancarz <radekg@solaris.elektrownia-lagisza.com.pl>
153 */
154
155 /**
156 * Decode an ascii representation (asc) of an ethernet adress.
157 *
158 \param asc[in] ASCII representation of an ethernet (MAC) address
159 \param eth[out] Binary representation of the ethernet address
160 \retval 0 Conversion to binary failed. Invalid address
161 \retval 1 Conversion completed successfully
162 */
163 static int
164 decode_eth(const char *asc, char *eth)
165 {
166 int a1 = 0, a2 = 0, a3 = 0, a4 = 0, a5 = 0, a6 = 0;
167
168 if (sscanf(asc, "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6) != 6) {
169 debugs(28, 0, "decode_eth: Invalid ethernet address '" << asc << "'");
170 return 0; /* This is not valid address */
171 }
172
173 eth[0] = (u_char) a1;
174 eth[1] = (u_char) a2;
175 eth[2] = (u_char) a3;
176 eth[3] = (u_char) a4;
177 eth[4] = (u_char) a5;
178 eth[5] = (u_char) a6;
179 return 1;
180 }
181
182 acl_arp_data *
183 aclParseArpData(const char *t)
184 {
185 LOCAL_ARRAY(char, eth, 256);
186 acl_arp_data *q = new acl_arp_data;
187 debugs(28, 5, "aclParseArpData: " << t);
188
189 if (sscanf(t, "%[0-9a-fA-F:]", eth) != 1) {
190 debugs(28, 0, "aclParseArpData: Bad ethernet address: '" << t << "'");
191 safe_free(q);
192 return NULL;
193 }
194
195 if (!decode_eth(eth, q->eth)) {
196 debugs(28, 0, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line);
197 debugs(28, 0, "aclParseArpData: Ignoring invalid ARP acl entry: can't parse '" << eth << "'");
198 safe_free(q);
199 return NULL;
200 }
201
202 return q;
203 }
204
205
206 /*******************/
207 /* aclParseArpList */
208 /*******************/
209 void
210 ACLARP::parse()
211 {
212 aclParseArpList (&data);
213 }
214
215 void
216 aclParseArpList(SplayNode<acl_arp_data *> **curlist)
217 {
218 char *t = NULL;
219 SplayNode<acl_arp_data *> **Top = curlist;
220 acl_arp_data *q = NULL;
221
222 while ((t = strtokFile())) {
223 if ((q = aclParseArpData(t)) == NULL)
224 continue;
225
226 *Top = (*Top)->insert(q, aclArpCompare);
227 }
228 }
229
230 int
231 ACLARP::match(ACLChecklist *checklist)
232 {
233 /* IPv6 does not do ARP */
234 if(!checklist->src_addr.IsIPv4())
235 {
236 debugs(14, 3, "ACLARP::match: IPv4 Required for ARP Lookups. Skipping " << checklist->src_addr );
237 return 0;
238 }
239
240 return aclMatchArp(&data, checklist->src_addr);
241 }
242
243 /***************/
244 /* aclMatchArp */
245 /***************/
246 int
247 aclMatchArp(SplayNode<acl_arp_data *> **dataptr, IPAddress &c)
248 {
249 struct arpreq arpReq;
250 struct sockaddr_in *sa = NULL;
251
252 IPAddress ipAddr = c;
253
254 #if defined(_SQUID_LINUX_)
255
256 unsigned char ifbuffer[sizeof(struct ifreq) * 64];
257 struct ifconf ifc;
258
259 struct ifreq *ifr;
260 int offset;
261
262 SplayNode<acl_arp_data*> **Top = dataptr;
263 /*
264 * The linux kernel 2.2 maintains per interface ARP caches and
265 * thus requires an interface name when doing ARP queries.
266 *
267 * The older 2.0 kernels appear to use a unified ARP cache,
268 * and require an empty interface name
269 *
270 * To support both, we attempt the lookup with a blank interface
271 * name first. If that does not succeed, the try each interface
272 * in turn
273 */
274
275 /*
276 * Set up structures for ARP lookup with blank interface name
277 */
278 memset(&arpReq, '\0', sizeof(arpReq));
279
280 sa = (sockaddr_in*)&arpReq.arp_pa;
281 ipAddr.GetSockAddr(*sa);
282 /* Query ARP table */
283
284 if (ioctl(HttpSockets[0], SIOCGARP, &arpReq) != -1) {
285 /* Skip non-ethernet interfaces */
286
287 if (arpReq.arp_ha.sa_family != ARPHRD_ETHER) {
288 return 0;
289 }
290
291 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex <<
292 std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff) << ":" <<
293 std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff) << ":" <<
294 std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff) << ":" <<
295 std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff) << ":" <<
296 std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff) << ":" <<
297 std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff));
298
299 /* Do lookup */
300 acl_arp_data X;
301 memcpy (X.eth, arpReq.arp_ha.sa_data, 6);
302 *Top = (*Top)->splay(&X, aclArpCompare);
303 debugs(28, 3, "aclMatchArp: '" << c << "' " << (splayLastResult ? "NOT found" : "found"));
304 return (0 == splayLastResult);
305 }
306
307 /* lookup list of interface names */
308 ifc.ifc_len = sizeof(ifbuffer);
309
310 ifc.ifc_buf = (char *)ifbuffer;
311
312 if (ioctl(HttpSockets[0], SIOCGIFCONF, &ifc) < 0) {
313 debugs(28, 1, "Attempt to retrieve interface list failed: " << xstrerror());
314 return 0;
315 }
316
317 if (ifc.ifc_len > (int)sizeof(ifbuffer)) {
318 debugs(28, 1, "Interface list too long - " << ifc.ifc_len);
319 return 0;
320 }
321
322 /* Attempt ARP lookup on each interface */
323 offset = 0;
324
325 while (offset < ifc.ifc_len) {
326
327 ifr = (struct ifreq *) (ifbuffer + offset);
328 offset += sizeof(*ifr);
329 /* Skip loopback and aliased interfaces */
330
331 if (0 == strncmp(ifr->ifr_name, "lo", 2))
332 continue;
333
334 if (NULL != strchr(ifr->ifr_name, ':'))
335 continue;
336
337 debugs(28, 4, "Looking up ARP address for " << c << " on " << ifr->ifr_name);
338
339 /* Set up structures for ARP lookup */
340
341 memset(&arpReq, '\0', sizeof(arpReq));
342
343 sa = (sockaddr_in*)&arpReq.arp_pa;
344 ipAddr.GetSockAddr(*sa);
345
346 strncpy(arpReq.arp_dev, ifr->ifr_name, sizeof(arpReq.arp_dev) - 1);
347
348 arpReq.arp_dev[sizeof(arpReq.arp_dev) - 1] = '\0';
349
350 /* Query ARP table */
351 if (-1 == ioctl(HttpSockets[0], SIOCGARP, &arpReq)) {
352 /*
353 * Query failed. Do not log failed lookups or "device
354 * not supported"
355 */
356
357 if (ENXIO == errno)
358 (void) 0;
359 else if (ENODEV == errno)
360 (void) 0;
361 else
362 debugs(28, 1, "ARP query failed: " << ifr->ifr_name << ": " << xstrerror());
363
364 continue;
365 }
366
367 /* Skip non-ethernet interfaces */
368 if (arpReq.arp_ha.sa_family != ARPHRD_ETHER)
369 continue;
370
371 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex <<
372 std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff) << ":" <<
373 std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff) << ":" <<
374 std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff) << ":" <<
375 std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff) << ":" <<
376 std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff) << ":" <<
377 std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff) << " on "<<
378 std::setfill(' ') << ifr->ifr_name);
379
380 /* Do lookup */
381 acl_arp_data X;
382
383 memcpy (X.eth, arpReq.arp_ha.sa_data, 6);
384
385 *Top = (*Top)->splay(&X, aclArpCompare);
386
387 /* Return if match, otherwise continue to other interfaces */
388 if (0 == splayLastResult) {
389 debugs(28, 3, "aclMatchArp: " << c << " found on " << ifr->ifr_name);
390 return 1;
391 }
392
393 /*
394 * Should we stop looking here? Can the same IP address
395 * exist on multiple interfaces?
396 */
397 }
398
399 #elif defined(_SQUID_SOLARIS_)
400
401 SplayNode<acl_arp_data *> **Top = dataptr;
402
403 /*
404 * Set up structures for ARP lookup with blank interface name
405 */
406
407 memset(&arpReq, '\0', sizeof(arpReq));
408
409 sa = (sockaddr_in*)&arpReq.arp_pa;
410 ipAddr.GetSockAddr(*sa);
411
412 /* Query ARP table */
413 if (ioctl(HttpSockets[0], SIOCGARP, &arpReq) != -1) {
414 /*
415 * Solaris (at least 2.6/x86) does not use arp_ha.sa_family -
416 * it returns 00:00:00:00:00:00 for non-ethernet media
417 */
418
419 if (arpReq.arp_ha.sa_data[0] == 0 &&
420 arpReq.arp_ha.sa_data[1] == 0 &&
421 arpReq.arp_ha.sa_data[2] == 0 &&
422 arpReq.arp_ha.sa_data[3] == 0 &&
423 arpReq.arp_ha.sa_data[4] == 0 && arpReq.arp_ha.sa_data[5] == 0)
424 return 0;
425
426 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex <<
427 std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff) << ":" <<
428 std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff) << ":" <<
429 std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff) << ":" <<
430 std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff) << ":" <<
431 std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff) << ":" <<
432 std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff));
433
434 /* Do lookup */
435 *Top = (*Top)->splay((acl_arp_data *)&arpReq.arp_ha.sa_data, aclArpCompare);
436
437 debugs(28, 3, "aclMatchArp: '" << c << "' " << (splayLastResult ? "NOT found" : "found"));
438
439 return (0 == splayLastResult);
440 }
441
442 #elif defined(_SQUID_FREEBSD_) || defined(_SQUID_NETBSD_) || defined(_SQUID_OPENBSD_) || defined(_SQUID_DRAGONFLY_)
443
444 SplayNode<acl_arp_data *> **Top = dataptr;
445
446 int mib[6];
447
448 size_t needed;
449
450 char *lim, *buf, *next;
451
452 struct rt_msghdr *rtm;
453
454 struct sockaddr_inarp *sin;
455
456 struct sockaddr_dl *sdl;
457
458 /*
459 * Set up structures for ARP lookup with blank interface name
460 */
461
462 memset(&arpReq, '\0', sizeof(arpReq));
463
464 ipAddr.GetSockAddr(arpReq.arp_pa);
465
466 /* Query ARP table */
467 mib[0] = CTL_NET;
468
469 mib[1] = PF_ROUTE;
470
471 mib[2] = 0;
472
473 mib[3] = AF_INET;
474
475 mib[4] = NET_RT_FLAGS;
476
477 mib[5] = RTF_LLINFO;
478
479 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
480 debugs(28, 0, "Can't estimate ARP table size!");
481 return 0;
482 }
483
484 if ((buf = (char *)xmalloc(needed)) == NULL) {
485 debugs(28, 0, "Can't allocate temporary ARP table!");
486 return 0;
487 }
488
489 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
490 debugs(28, 0, "Can't retrieve ARP table!");
491 xfree(buf);
492 return 0;
493 }
494
495 lim = buf + needed;
496
497 for (next = buf; next < lim; next += rtm->rtm_msglen) {
498
499 rtm = (struct rt_msghdr *) next;
500
501 sin = (struct sockaddr_inarp *) (rtm + 1);
502 /*sdl = (struct sockaddr_dl *) (sin + 1); */
503
504 #define ROUNDUP(a) \
505 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
506
507 sdl = (struct sockaddr_dl *)((char *) sin + ROUNDUP(sin->sin_len));
508
509 if (c == sin->sin_addr) {
510 if (sdl->sdl_alen) {
511
512 arpReq.arp_ha.sa_len = sizeof(struct sockaddr);
513 arpReq.arp_ha.sa_family = AF_UNSPEC;
514 memcpy(arpReq.arp_ha.sa_data, LLADDR(sdl), sdl->sdl_alen);
515 }
516 }
517 }
518
519 xfree(buf);
520
521 if (arpReq.arp_ha.sa_data[0] == 0 && arpReq.arp_ha.sa_data[1] == 0 &&
522 arpReq.arp_ha.sa_data[2] == 0 && arpReq.arp_ha.sa_data[3] == 0 &&
523 arpReq.arp_ha.sa_data[4] == 0 && arpReq.arp_ha.sa_data[5] == 0)
524 return 0;
525
526 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex <<
527 std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff) << ":" <<
528 std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff) << ":" <<
529 std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff) << ":" <<
530 std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff) << ":" <<
531 std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff) << ":" <<
532 std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff));
533
534 /* Do lookup */
535 *Top = (*Top)->splay((acl_arp_data *)&arpReq.arp_ha.sa_data, aclArpCompare);
536
537 debugs(28, 3, "aclMatchArp: '" << c << "' " << (splayLastResult ? "NOT found" : "found"));
538
539 return (0 == splayLastResult);
540
541 #elif defined(_SQUID_WIN32_)
542
543 DWORD dwNetTable = 0;
544
545 DWORD ipNetTableLen = 0;
546
547 PMIB_IPNETTABLE NetTable = NULL;
548
549 DWORD i;
550
551 SplayNode<acl_arp_data *> **Top = dataptr;
552
553 memset(&arpReq, '\0', sizeof(arpReq));
554
555 /* Get size of Windows ARP table */
556 if (GetIpNetTable(NetTable, &ipNetTableLen, FALSE) != ERROR_INSUFFICIENT_BUFFER) {
557 debugs(28, 0, "Can't estimate ARP table size!");
558 return 0;
559 }
560
561 /* Allocate space for ARP table and assign pointers */
562 if ((NetTable = (PMIB_IPNETTABLE)xmalloc(ipNetTableLen)) == NULL) {
563 debugs(28, 0, "Can't allocate temporary ARP table!");
564 return 0;
565 }
566
567 /* Get actual ARP table */
568 if ((dwNetTable = GetIpNetTable(NetTable, &ipNetTableLen, FALSE)) != NO_ERROR) {
569 debugs(28, 0, "Can't retrieve ARP table!");
570 xfree(NetTable);
571 return 0;
572 }
573
574 /* Find MAC address from net table */
575 for (i = 0 ; i < NetTable->dwNumEntries ; i++) {
576 in_addr a;
577 a.s_addr = NetTable->table[i].dwAddr;
578 if (c == a && (NetTable->table[i].dwType > 2)) {
579 arpReq.arp_ha.sa_family = AF_UNSPEC;
580 memcpy(arpReq.arp_ha.sa_data, NetTable->table[i].bPhysAddr, NetTable->table[i].dwPhysAddrLen);
581 }
582 }
583
584 xfree(NetTable);
585
586 if (arpReq.arp_ha.sa_data[0] == 0 && arpReq.arp_ha.sa_data[1] == 0 &&
587 arpReq.arp_ha.sa_data[2] == 0 && arpReq.arp_ha.sa_data[3] == 0 &&
588 arpReq.arp_ha.sa_data[4] == 0 && arpReq.arp_ha.sa_data[5] == 0)
589 return 0;
590
591 debugs(28, 4, "Got address "<< std::setfill('0') << std::hex <<
592 std::setw(2) << (arpReq.arp_ha.sa_data[0] & 0xff) << ":" <<
593 std::setw(2) << (arpReq.arp_ha.sa_data[1] & 0xff) << ":" <<
594 std::setw(2) << (arpReq.arp_ha.sa_data[2] & 0xff) << ":" <<
595 std::setw(2) << (arpReq.arp_ha.sa_data[3] & 0xff) << ":" <<
596 std::setw(2) << (arpReq.arp_ha.sa_data[4] & 0xff) << ":" <<
597 std::setw(2) << (arpReq.arp_ha.sa_data[5] & 0xff));
598
599 /* Do lookup */
600 *Top = (*Top)->splay((acl_arp_data *)&arpReq.arp_ha.sa_data, aclArpCompare);
601
602 debugs(28, 3, "aclMatchArp: '" << c << "' " << (splayLastResult ? "NOT found" : "found"));
603
604 return (0 == splayLastResult);
605
606 #else
607
608 #error "ARP type ACL not supported on this operating system."
609
610 #endif
611 /*
612 * Address was not found on any interface
613 */
614 debugs(28, 3, "aclMatchArp: " << c << " NOT found");
615
616 return 0;
617 }
618
619 static int
620 aclArpCompare(acl_arp_data * const &a, acl_arp_data * const &b)
621 {
622 return memcmp(a->eth, b->eth, 6);
623 }
624
625 static void
626 aclDumpArpListWalkee(acl_arp_data * const &node, void *state)
627 {
628 acl_arp_data *arp = node;
629 static char buf[24];
630 snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
631 arp->eth[0] & 0xff, arp->eth[1] & 0xff,
632 arp->eth[2] & 0xff, arp->eth[3] & 0xff,
633 arp->eth[4] & 0xff, arp->eth[5] & 0xff);
634 wordlistAdd((wordlist **)state, buf);
635 }
636
637 wordlist *
638 ACLARP::dump() const
639 {
640 wordlist *w = NULL;
641 data->walk(aclDumpArpListWalkee, &w);
642 return w;
643 }
644
645 /* ==== END ARP ACL SUPPORT =============================================== */