Squid Web Cache master
Loading...
Searching...
No Matches
Ip.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9/* DEBUG: section 28 Access Control */
10
11#include "squid.h"
12#include "acl/Checklist.h"
13#include "acl/Ip.h"
14#include "acl/SplayInserter.h"
15#include "cache_cf.h"
16#include "ConfigParser.h"
17#include "debug/Stream.h"
18#include "ip/tools.h"
19#include "MemBuf.h"
20#include "wordlist.h"
21
22#include <algorithm>
23
24void *
25ACLIP::operator new (size_t)
26{
27 fatal ("ACLIP::operator new: unused");
28 return (void *)1;
29}
30
31void
32ACLIP::operator delete (void *)
33{
34 fatal ("ACLIP::operator delete: unused");
35}
36
43void
44acl_ip_data::toStr(char *buf, int len) const
45{
46 char *b1 = buf;
47 char *b2 = nullptr;
48 char *b3 = nullptr;
49 int rlen = 0;
50
51 addr1.toStr(b1, len - rlen );
52 rlen = strlen(buf);
53 b2 = buf + rlen;
54
55 if (!addr2.isAnyAddr()) {
56 b2[0] = '-';
57 ++rlen;
58 addr2.toStr(&(b2[1]), len - rlen );
59 rlen = strlen(buf);
60 } else
61 b2[0] = '\0';
62
63 b3 = buf + rlen;
64
65 if (!mask.isNoAddr()) {
66 b3[0] = '/';
67 ++rlen;
68 int cidr = mask.cidr() - (addr1.isIPv4()?96:0);
69 snprintf(&(b3[1]), (len-rlen), "%u", (unsigned int)(cidr<0?0:cidr) );
70 } else
71 b3[0] = '\0';
72}
73
74SBuf
76{
77 const int bufsz = MAX_IPSTRLEN*2+6;
78 static char tmpbuf[ bufsz ];
79 toStr(tmpbuf,bufsz);
80 return SBuf(tmpbuf);
81}
82
85{
86 auto ip = addr1;
87 if (!mask.isNoAddr())
88 ip.applyMask(mask);
89 return ip;
90}
91
94{
95 auto ip = addr2.isAnyAddr() ? addr1 : addr2;
96 if (!mask.isNoAddr())
98 return ip;
99}
100
101template <>
102int
104{
105 if (a->lastAddress() < b->firstAddress())
106 return -1; // the entire range a is to the left of range b
107
108 if (a->firstAddress() > b->lastAddress())
109 return +1; // the entire range a is to the right of range b
110
111 return 0; // equal or partially overlapping ranges
112}
113
114template <>
115bool
117{
118 return b->firstAddress() <= a->firstAddress() && a->lastAddress() <= b->lastAddress();
119}
120
121template <>
124{
125 const auto minLeft = std::min(a->firstAddress(), b->firstAddress());
126 const auto maxRight = std::max(a->lastAddress(), b->lastAddress());
127 return new acl_ip_data(minLeft, maxRight, Ip::Address::NoAddr(), nullptr);
128}
129
131static std::ostream &
132operator <<(std::ostream &os, acl_ip_data *value)
133{
134 if (value)
135 os << value->toSBuf();
136 return os;
137}
138
139/*
140 * aclIpAddrNetworkCompare - The guts of the comparison for IP ACLs
141 * matching checks. The first argument (p) is a "host" address,
142 * i.e. the IP address of a cache client. The second argument (q)
143 * is an entry in some address-based access control element. This
144 * function is called via ACLIP::match() and the splay library.
145 */
146static int
148{
149 Ip::Address A = p->addr1;
150
151 /* apply netmask */
152 A.applyMask(q->mask);
153
154 debugs(28,9, "aclIpAddrNetworkCompare: compare: " << p->addr1 << "/" << q->mask << " (" << A << ") vs " <<
155 q->addr1 << "-" << q->addr2 << "/" << q->mask);
156
157 if (q->addr2.isAnyAddr()) { /* single address check */
158
159 return A.matchIPAddr( q->addr1 );
160
161 } else { /* range address check */
162
163 if ( (A >= q->addr1) && (A <= q->addr2) )
164 return 0; /* valid. inside range. */
165 else
166 return A.matchIPAddr( q->addr1 ); /* outside of range, 'less than' */
167 }
168}
169
175bool
176acl_ip_data::DecodeMask(const char *asc, Ip::Address &mask, int ctype)
177{
178 char junk;
179 int a1 = 0;
180
181 /* default is a mask that doesn't change any IP */
182 mask.setNoAddr();
183
184 if (!asc || !*asc) {
185 return true;
186 }
187
188 /* An int mask 128, 32 */
189 if ((sscanf(asc, "%d%c", &a1, &junk)==1) &&
190 (a1 <= 128) && (a1 >= 0)
191 ) {
192 return mask.applyMask(a1, ctype);
193 }
194
195 /* dotted notation */
196 /* assignment returns true if asc contained an IP address as text */
197 if ((mask = asc)) {
198 /* HACK: IPv4 netmasks don't cleanly map to IPv6 masks. */
199 debugs(28, DBG_CRITICAL, "WARNING: Netmasks are deprecated. Please use CIDR masks instead.");
200 if (mask.isIPv4()) {
201 /* locate what CIDR mask was _probably_ meant to be in its native protocol format. */
202 /* this will completely crap out with a security fail-open if the admin is playing mask tricks */
203 /* however, that's their fault, and we do warn. see bug 2601 for the effects if we don't do this. */
204 unsigned int m = mask.cidr();
205 debugs(28, DBG_CRITICAL, "WARNING: IPv4 netmasks are particularly nasty when used to compare IPv6 to IPv4 ranges.");
206 debugs(28, DBG_CRITICAL, "WARNING: For now we will assume you meant to write /" << m);
207 /* reset the mask completely, and crop to the CIDR boundary back properly. */
208 mask.setNoAddr();
209 return mask.applyMask(m,AF_INET);
210 }
211 return true;
212 }
213
214 return false;
215}
216
218bool
220{
221 // XXX: FactoryParse() adds an item and only then checks its validity. This
222 // loop excludes the last (i.e. being vetted) item. TODO: Refactor parsing
223 // to use a temporary container of vetted items instead of the current
224 // acl_ip_data::next hack.
225 for (const auto *i = this; i && i->next; i = i->next) {
226 if (i->addr1 == needle)
227 return true;
228 }
229 return false;
230}
231
232/* Handle either type of address, IPv6 will be discarded with a warning if disabled */
233#define SCAN_ACL1_6 "%[0123456789ABCDEFabcdef:]-%[0123456789ABCDEFabcdef:]/%[0123456789]"
234#define SCAN_ACL2_6 "%[0123456789ABCDEFabcdef:]-%[0123456789ABCDEFabcdef:]%c"
235#define SCAN_ACL3_6 "%[0123456789ABCDEFabcdef:]/%[0123456789]"
236#define SCAN_ACL4_6 "%[0123456789ABCDEFabcdef:]/%c"
237/* We DO need to know which is which though, for proper CIDR masking. */
238#define SCAN_ACL1_4 "%[0123456789.]-%[0123456789.]/%[0123456789.]"
239#define SCAN_ACL2_4 "%[0123456789.]-%[0123456789.]%c"
240#define SCAN_ACL3_4 "%[0123456789.]/%[0123456789.]"
241#define SCAN_ACL4_4 "%[0123456789.]/%c"
242
245{
246 LOCAL_ARRAY(char, addr1, 256);
247 LOCAL_ARRAY(char, addr2, 256);
248 LOCAL_ARRAY(char, mask, 256);
249 acl_ip_data *r = nullptr;
250 acl_ip_data **Q = nullptr;
251 Ip::Address temp;
252 char c;
253 unsigned int changed;
254 acl_ip_data *q = new acl_ip_data;
255 int iptype = AF_UNSPEC;
256
257 debugs(28, 5, "aclIpParseIpData: " << t);
258
259// IPv4
260 if (sscanf(t, SCAN_ACL1_4, addr1, addr2, mask) == 3) {
261 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN1-v4: " << SCAN_ACL1_4);
262 iptype=AF_INET;
263 } else if (sscanf(t, SCAN_ACL2_4, addr1, addr2, &c) >= 2) {
264 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN2-v4: " << SCAN_ACL2_4);
265 mask[0] = '\0';
266 iptype=AF_INET;
267 } else if (sscanf(t, SCAN_ACL3_4, addr1, mask) == 2) {
268 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN3-v4: " << SCAN_ACL3_4);
269 addr2[0] = '\0';
270 iptype=AF_INET;
271 } else if (sscanf(t, SCAN_ACL4_4, addr1,&c) == 2) {
272 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN4-v4: " << SCAN_ACL4_4);
273 addr2[0] = '\0';
274 mask[0] = '\0';
275 iptype=AF_INET;
276
277// IPv6
278 } else if (sscanf(t, SCAN_ACL1_6, addr1, addr2, mask) == 3) {
279 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN1-v6: " << SCAN_ACL1_6);
280 iptype=AF_INET6;
281 } else if (sscanf(t, SCAN_ACL2_6, addr1, addr2, &c) >= 2) {
282 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN2-v6: " << SCAN_ACL2_6);
283 mask[0] = '\0';
284 iptype=AF_INET6;
285 } else if (sscanf(t, SCAN_ACL3_6, addr1, mask) == 2) {
286 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN3-v6: " << SCAN_ACL3_6);
287 addr2[0] = '\0';
288 iptype=AF_INET6;
289 } else if (sscanf(t, SCAN_ACL4_6, addr1, mask) == 2) {
290 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN4-v6: " << SCAN_ACL4_6);
291 addr2[0] = '\0';
292 iptype=AF_INET6;
293
294// Neither
295 } else if (sscanf(t, "%[^/]/%s", addr1, mask) == 2) {
296 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: non-IP pattern: %[^/]/%s");
297 addr2[0] = '\0';
298 } else if (sscanf(t, "%s", addr1) == 1) {
299 /*
300 * Note, must use plain getaddrinfo() here because at startup
301 * ipcache hasn't been initialized
302 * TODO: offload this to one of the Ip::Address lookups.
303 */
304
305 debugs(28, 5, "aclIpParseIpData: Lookup Host/IP " << addr1);
306 struct addrinfo *hp = nullptr, *x = nullptr;
307 struct addrinfo hints;
308
309 memset(&hints, 0, sizeof(struct addrinfo));
310
311 int errcode = getaddrinfo(addr1,nullptr,&hints,&hp);
312 if (hp == nullptr) {
313 delete q;
314 if (strcmp(addr1, "::1") == 0) {
315 debugs(28, DBG_IMPORTANT, "aclIpParseIpData: IPv6 has not been enabled in host DNS resolver.");
316 } else {
317 debugs(28, DBG_CRITICAL, "ERROR: aclIpParseIpData: Bad host/IP: '" << addr1 <<
318 "' in '" << t << "', flags=" << hints.ai_flags <<
319 " : (" << errcode << ") " << gai_strerror(errcode) );
321 }
322 return nullptr;
323 }
324
325 Q = &q;
326
327 for (x = hp; x != nullptr;) {
328 if ((r = *Q) == nullptr)
329 r = *Q = new acl_ip_data;
330
331 r->addr1 = *x;
332 x = x->ai_next;
333 if (q->containsVetted(r->addr1)) {
334 // getaddrinfo() returned duplicate ai_addr values; we have already added one of them
335 debugs(28, 3, "aclIpParseIpData: Duplicate host/IP: '" << r->addr1 << "' dropped.");
336 delete r;
337 *Q = nullptr;
338 continue;
339 }
340
341 debugs(28, 3, "aclIpParseIpData: Located host/IP: '" << r->addr1 << "'");
342
343 r->addr2.setAnyAddr();
344 r->mask.setNoAddr();
345
346 Q = &r->next;
347
348 debugs(28, 3, "" << addr1 << " --> " << r->addr1 );
349 }
350
351 freeaddrinfo(hp);
352
353 if (*Q != nullptr) {
354 debugs(28, DBG_CRITICAL, "ERROR: aclIpParseIpData: Bad host/IP: '" << t << "'");
356 return nullptr;
357 }
358
359 return q;
360 }
361
362 /* ignore IPv6 addresses when built with IPv4-only */
363 if ( iptype == AF_INET6 && !Ip::EnableIpv6) {
364 debugs(28, DBG_IMPORTANT, "aclIpParseIpData: IPv6 has not been enabled.");
365 delete q;
366 return nullptr;
367 }
368
369 /* Decode addr1 */
370 if (!*addr1 || !(q->addr1 = addr1)) {
371 debugs(28, DBG_CRITICAL, "ERROR: aclIpParseIpData: unknown first address in '" << t << "'");
372 delete q;
374 return nullptr;
375 }
376
377 /* Decode addr2 */
378 if (!*addr2)
379 q->addr2.setAnyAddr();
380 else if (!(q->addr2=addr2) ) {
381 debugs(28, DBG_CRITICAL, "ERROR: aclIpParseIpData: unknown second address in '" << t << "'");
382 delete q;
384 return nullptr;
385 }
386
387 /* Decode mask (NULL or empty means a exact host mask) */
388 if (!DecodeMask(mask, q->mask, iptype)) {
389 debugs(28, DBG_CRITICAL, "ERROR: aclParseIpData: unknown netmask '" << mask << "' in '" << t << "'");
390 delete q;
392 return nullptr;
393 }
394
395 changed = 0;
396 changed += q->addr1.applyMask(q->mask);
397 changed += q->addr2.applyMask(q->mask);
398
399 if (changed)
400 debugs(28, DBG_CRITICAL, "WARNING: aclIpParseIpData: Netmask masks away part of the specified IP in '" << t << "'");
401
402 // TODO: Either switch match() to Acl::SplayInserter<acl_ip_data*>::Compare()
403 // range logic (that does not have these problems) OR warn that some (or
404 // even all) addresses will never match this configured ACL value when
405 // `q->addr1.applyMask()` above is positive:
406 //
407 // * A single configured IP value will never match:
408 // A.matchIPAddr(q->addr1) in aclIpAddrNetworkCompare() will not return 0.
409 // For example, `acl x src 127.0.0.1/24` does not match any address.
410 //
411 // * A configured IP range will not match any q->addr1/mask IPs:
412 // (A >= q->addr1) in aclIpAddrNetworkCompare() is false and
413 // A.matchIPAddr(q->addr1) will not return 0.
414 // For example, `acl y src 10.0.0.1-10.0.0.255/24` does not match 10.0.0.1.
415
416 debugs(28,9, "Parsed: " << q->addr1 << "-" << q->addr2 << "/" << q->mask << "(/" << q->mask.cidr() <<")");
417
418 /* 1.2.3.4/255.255.255.0 --> 1.2.3.0 */
419 /* Same as IPv6 (not so trivial to depict) */
420 return q;
421}
422
425bool
426ACLIP::parseGlobal(const char * const token)
427{
428 // "all" matches entire Internet
429 if (strcmp(token, "all") == 0) {
430 debugs(28, 8, "found " << token);
431 matchAnyIpv4 = true;
432 matchAnyIpv6 = true;
433 // TODO: Ignore all other ACL data parameters, with a once/ACL warning.
434 return true;
435 }
436
437 // "ipv4" matches IPv4 Internet
438 if (strcmp(token, "ipv4") == 0) {
439 debugs(28, 8, "found " << token);
440 matchAnyIpv4 = true;
441 // TODO: Ignore all IPv4 data parameters, with a once/ACL warning.
442 return true;
443 }
444
445 // "ipv4" matches IPv6 Internet
446 if (strcmp(token, "ipv6") == 0) {
447 debugs(28, 8, "found " << token);
448 matchAnyIpv6 = true;
449 // TODO: Ignore all IPv6 data parameters, with a once/ACL warning.
450 return true;
451 }
452
453 /* Detect some old broken strings equivalent to 'all'.
454 * treat them nicely. But be loud until its fixed. */
455 if (strcmp(token, "0/0") == 0 ||
456 strcmp(token, "0.0.0.0/0") == 0 ||
457 strcmp(token, "0.0.0.0/0.0.0.0") == 0 ||
458 strcmp(token, "0.0.0.0-255.255.255.255") == 0 ||
459 strcmp(token, "0.0.0.0-0.0.0.0/0") == 0) {
460
461 debugs(28,DBG_CRITICAL, "ERROR: '" << token << "' needs to be replaced by the term 'all'.");
462 debugs(28,DBG_CRITICAL, "SECURITY NOTICE: Overriding config setting. Using 'all' instead.");
463 matchAnyIpv4 = true;
464 matchAnyIpv6 = true;
465 return true;
466 }
467
468 return false;
469}
470
471void
473{
474 if (data == nullptr)
475 data = new IPSplay();
476
477 while (char *t = ConfigParser::strtokFile()) {
478 if (parseGlobal(t))
479 continue;
480
482
483 while (q != nullptr) {
484 /* pop each result off the list and add it to the data tree individually */
485 acl_ip_data *next_node = q->next;
486 q->next = nullptr;
488 q = next_node;
489 }
490 }
491}
492
494{
495 if (data) {
496 data->destroy();
497 delete data;
498 }
499}
500
503 void operator() (acl_ip_data * const & ip) {
504 contents.push_back(ip->toSBuf());
505 }
506};
507
510{
511 IpAclDumpVisitor visitor;
512
514 visitor.contents.push_back(SBuf("all"));
515 else if (matchAnyIpv4)
516 visitor.contents.push_back(SBuf("ipv4"));
517 else if (matchAnyIpv6)
518 visitor.contents.push_back(SBuf("ipv6"));
519
520 data->visit(visitor);
521 return visitor.contents;
522}
523
524bool
526{
527 return data->empty() && !matchAnyIpv4 && !matchAnyIpv6;
528}
529
530int
531ACLIP::match(const Ip::Address &clientip)
532{
533 if (matchAnyIpv4) {
534 if (matchAnyIpv6) {
535 debugs(28, 3, clientip << " found, matched 'all'");
536 return true;
537 }
538 if (clientip.isIPv4()) {
539 debugs(28, 3, clientip << " found, matched 'ipv4'");
540 return true;
541 }
542 // fall through to look for an IPv6 match among IP parameters
543 } else if (matchAnyIpv6) {
544 if (clientip.isIPv6()) {
545 debugs(28, 3, clientip << " found, matched 'ipv6'");
546 return true;
547 }
548 // fall through to look for an IPv4 match among IP parameters
549 }
550
551 static acl_ip_data ClientAddress;
552 /*
553 * aclIpAddrNetworkCompare() takes two acl_ip_data pointers as
554 * arguments, so we must create a fake one for the client's IP
555 * address. Since we are scanning for a single IP mask and addr2
556 * MUST be set to empty.
557 */
558 ClientAddress.addr1 = clientip;
559 ClientAddress.addr2.setEmpty();
560 ClientAddress.mask.setEmpty();
561
562 const acl_ip_data * const * result = data->find(&ClientAddress, aclIpAddrNetworkCompare);
563 debugs(28, 3, "aclIpMatchIp: '" << clientip << "' " << (result ? "found" : "NOT found"));
564 return (result != nullptr);
565}
566
567acl_ip_data::acl_ip_data() :addr1(), addr2(), mask(), next (nullptr) {}
568
569acl_ip_data::acl_ip_data(Ip::Address const &anAddress1, Ip::Address const &anAddress2, Ip::Address const &aMask, acl_ip_data *aNext) : addr1(anAddress1), addr2(anAddress2), mask(aMask), next(aNext) {}
570
static std::ostream & operator<<(std::ostream &os, acl_ip_data *value)
reports acl_ip_data using squid.conf ACL value format
Definition Ip.cc:132
#define SCAN_ACL2_6
Definition Ip.cc:234
#define SCAN_ACL1_4
Definition Ip.cc:238
#define SCAN_ACL2_4
Definition Ip.cc:239
#define SCAN_ACL4_6
Definition Ip.cc:236
#define SCAN_ACL3_4
Definition Ip.cc:240
#define SCAN_ACL1_6
Definition Ip.cc:233
#define SCAN_ACL4_4
Definition Ip.cc:241
static int aclIpAddrNetworkCompare(acl_ip_data *const &p, acl_ip_data *const &q)
Definition Ip.cc:147
#define SCAN_ACL3_6
Definition Ip.cc:235
void self_destruct(void)
Definition cache_cf.cc:275
bool matchAnyIpv4
whether match() should return 1 for any IPv4 parameter
Definition Ip.h:78
SBufList dump() const override
Definition Ip.cc:509
Splay< acl_ip_data * > IPSplay
Definition Ip.h:60
bool parseGlobal(const char *)
Definition Ip.cc:426
void parse() override
parses node representation in squid.conf; dies on failures
Definition Ip.cc:472
~ACLIP() override
Definition Ip.cc:493
bool matchAnyIpv6
whether match() should return 1 for any IPv6 parameter
Definition Ip.h:81
bool empty() const override
Definition Ip.cc:525
IPSplay * data
Definition Ip.h:72
int match(ACLChecklist *checklist) override=0
Matches the actual data in checklist against this Acl::Node.
static Value MakeCombinedValue(const Value &a, const Value &b)
static int Compare(const Value &a, const Value &b)
static bool IsSubset(const Value &a, const Value &b)
static void Merge(Splay< Value > &, Value &&)
static char * strtokFile()
char * toStr(char *buf, const unsigned int blen, int force=AF_UNSPEC) const
Definition Address.cc:804
void setEmpty()
Fast reset of the stored content to what would be after default constructor.
Definition Address.cc:204
bool isIPv4() const
Definition Address.cc:178
static const Address & NoAddr()
Definition Address.h:321
int matchIPAddr(const Address &rhs) const
Definition Address.cc:715
void turnMaskedBitsOn(const Address &mask)
Definition Address.cc:115
bool isNoAddr() const
Definition Address.cc:304
bool isAnyAddr() const
Definition Address.cc:190
bool isIPv6() const
Definition Address.cc:184
void setNoAddr()
Definition Address.cc:312
int applyMask(const Address &mask)
Definition Address.cc:97
int cidr() const
Definition Address.cc:54
void setAnyAddr()
NOTE: Does NOT clear the Port stored. Only the Address and Type.
Definition Address.cc:197
Definition SBuf.h:94
Value const * find(FindValue const &, int(*compare)(FindValue const &a, Value const &b)) const
bool empty() const
Definition splay.h:78
void visit(ValueVisitor &) const
left-to-right visit of all stored Values
void destroy(SPLAYFREE *=DefaultFree)
Definition splay.h:369
Ip::Address mask
Definition Ip.h:40
acl_ip_data()
Definition Ip.cc:567
Ip::Address lastAddress() const
maximum (masked) address that matches this configured ACL value
Definition Ip.cc:93
Ip::Address firstAddress() const
minimum (masked) address that matches this configured ACL value
Definition Ip.cc:84
SBuf toSBuf() const
Definition Ip.cc:75
static bool DecodeMask(const char *asc, Ip::Address &mask, int string_format_type)
Definition Ip.cc:176
bool containsVetted(const Ip::Address &needle) const
whether we have parsed and vetted an item with an addr1 field that matches the needle
Definition Ip.cc:219
Ip::Address addr1
Definition Ip.h:36
static acl_ip_data * FactoryParse(char const *)
Definition Ip.cc:244
void toStr(char *buf, int len) const
Definition Ip.cc:44
acl_ip_data * next
Definition Ip.h:42
Ip::Address addr2
Definition Ip.h:38
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
void fatal(const char *message)
Definition fatal.cc:28
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition forward.h:25
std::list< SBuf > SBufList
Definition forward.h:23
#define LOCAL_ARRAY(type, name, size)
Definition squid.h:62
SBufList contents
Definition Ip.cc:502
void operator()(acl_ip_data *const &ip)
Definition Ip.cc:503
int const char size_t