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