]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_db.cc
Prep for 3.3.12 and 3.4.4
[thirdparty/squid.git] / src / client_db.cc
CommitLineData
516350ca 1/*
b510f3a1 2 * DEBUG: section 00 Client Database
516350ca 3 * AUTHOR: Duane Wessels
4 *
2b6662ba 5 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 6 * ----------------------------------------------------------
516350ca 7 *
2b6662ba 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.
516350ca 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.
26ac0430 21 *
516350ca 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.
26ac0430 26 *
516350ca 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
cbdec147 29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 30 *
516350ca 31 */
32
582c2af2 33#include "squid.h"
38e50b3a 34#include "base/RunnersRegistry.h"
95e6d864 35#include "client_db.h"
602d9612 36#include "ClientInfo.h"
a553a5a3 37#include "event.h"
31971e6a 38#include "format/Token.h"
95e6d864 39#include "fqdncache.h"
96d89ea0 40#include "ip/Address.h"
1c7ae5ff 41#include "log/access_log.h"
8a89c28f 42#include "Mem.h"
8822ebee 43#include "mgr/Registration.h"
4d5904f7 44#include "SquidConfig.h"
a98bcbee 45#include "SquidMath.h"
985c86bc 46#include "SquidTime.h"
e4f1fdae 47#include "StatCounters.h"
e6ccf245 48#include "Store.h"
4e540555 49#include "tools.h"
c41d4b6d 50
9c0a2256
FC
51#if SQUID_SNMP
52#include "snmp_core.h"
53#endif
54
365e5b34 55static hash_table *client_table = NULL;
62e76326 56
b7ac5457 57static ClientInfo *clientdbAdd(const Ip::Address &addr);
ec878047 58static FREE clientdbFreeItem;
a0eba6bc 59static void clientdbStartGC(void);
60static void clientdbScheduledGC(void *);
61
9a0a18de 62#if USE_DELAY_POOLS
b4cd430a
CT
63static int max_clients = 32768;
64#else
a0eba6bc 65static int max_clients = 32;
b4cd430a
CT
66#endif
67
a0eba6bc 68static int cleanup_running = 0;
69static int cleanup_scheduled = 0;
70static int cleanup_removed;
71
9a0a18de 72#if USE_DELAY_POOLS
b4cd430a
CT
73#define CLIENT_DB_HASH_SIZE 65357
74#else
a0eba6bc 75#define CLIENT_DB_HASH_SIZE 467
b4cd430a 76#endif
c41d4b6d 77
78static ClientInfo *
62e76326 79
b7ac5457 80clientdbAdd(const Ip::Address &addr)
c41d4b6d 81{
82 ClientInfo *c;
fdb29403 83 char *buf = static_cast<char*>(xmalloc(MAX_IPSTRLEN)); // becomes hash.key
e6ccf245 84 c = (ClientInfo *)memAllocate(MEM_CLIENT_INFO);
fdb29403 85 debugs(77, 9, "ClientInfo constructed, this=" << c);
4dd643d5 86 c->hash.key = addr.toStr(buf,MAX_IPSTRLEN);
c41d4b6d 87 c->addr = addr;
9a0a18de 88#if USE_DELAY_POOLS
b4cd430a
CT
89 /* setup default values for client write limiter */
90 c->writeLimitingActive=false;
91 c->writeSpeedLimit=0;
92 c->bucketSize = 0;
93 c->firstTimeConnection=true;
94 c->quotaQueue = NULL;
95 c->rationedQuota = 0;
96 c->rationedCount = 0;
97 c->selectWaiting = false;
98 c->eventWaiting = false;
99
100 /* get current time */
101 getCurrentTime();
102 c->prevTime=current_dtime;/* put current time to have something sensible here */
103#endif
186477c1 104 hash_join(client_table, &c->hash);
e4f1fdae 105 ++statCounter.client_http.clients;
a0eba6bc 106
26ac0430 107 if ((statCounter.client_http.clients > max_clients) && !cleanup_running && cleanup_scheduled < 2) {
5086523e 108 ++cleanup_scheduled;
a0eba6bc 109 eventAdd("client_db garbage collector", clientdbScheduledGC, NULL, 90, 0);
110 }
111
c41d4b6d 112 return c;
113}
114
5f5e883f 115static void
c41d4b6d 116clientdbInit(void)
117{
19054954 118 if (client_table)
62e76326 119 return;
120
30abd221 121 client_table = hash_create((HASHCMP *) strcmp, CLIENT_DB_HASH_SIZE, hash_string);
62ee09ca 122}
62e76326 123
38e50b3a
NH
124class ClientDbRr: public RegisteredRunner
125{
86c63190 126public:
21b7990f
AR
127 /* RegisteredRunner API */
128 virtual void useConfig();
38e50b3a 129};
21b7990f 130RunnerRegistrationEntry(ClientDbRr);
38e50b3a
NH
131
132void
21b7990f 133ClientDbRr::useConfig()
38e50b3a
NH
134{
135 clientdbInit();
136 Mgr::RegisterAction("client_list", "Cache Client List", clientdbDump, 0, 1);
137}
138
9a0a18de 139#if USE_DELAY_POOLS
b4cd430a
CT
140/* returns ClientInfo for given IP addr
141 Returns NULL if no such client (or clientdb turned off)
142 (it is assumed that clientdbEstablished will be called before and create client record if needed)
143*/
144ClientInfo * clientdbGetInfo(const Ip::Address &addr)
145{
146 char key[MAX_IPSTRLEN];
147 ClientInfo *c;
148
149 if (!Config.onoff.client_db)
150 return NULL;
151
4dd643d5 152 addr.toStr(key,MAX_IPSTRLEN);
b4cd430a
CT
153
154 c = (ClientInfo *) hash_lookup(client_table, key);
f33d34a8 155 if (c==NULL) {
e0236918 156 debugs(77, DBG_IMPORTANT,"Client db does not contain information for given IP address "<<(const char*)key);
b4cd430a
CT
157 return NULL;
158 }
159 return c;
160}
161#endif
62ee09ca 162void
02c8dde5 163clientdbUpdate(const Ip::Address &addr, LogTags ltype, AnyP::ProtocolType p, size_t size)
c41d4b6d 164{
cc192b50 165 char key[MAX_IPSTRLEN];
429fdbec 166 ClientInfo *c;
62e76326 167
17a0a4ee 168 if (!Config.onoff.client_db)
62e76326 169 return;
170
4dd643d5 171 addr.toStr(key,MAX_IPSTRLEN);
62e76326 172
429fdbec 173 c = (ClientInfo *) hash_lookup(client_table, key);
62e76326 174
c41d4b6d 175 if (c == NULL)
62e76326 176 c = clientdbAdd(addr);
177
c41d4b6d 178 if (c == NULL)
62e76326 179 debug_trap("clientdbUpdate: Failed to add entry");
180
0c3d3f65 181 if (p == AnyP::PROTO_HTTP) {
5086523e
FC
182 ++ c->Http.n_requests;
183 ++ c->Http.result_hist[ltype];
62e76326 184 kb_incr(&c->Http.kbytes_out, size);
185
186 if (logTypeIsATcpHit(ltype))
187 kb_incr(&c->Http.hit_kbytes_out, size);
0c3d3f65 188 } else if (p == AnyP::PROTO_ICP) {
5086523e
FC
189 ++ c->Icp.n_requests;
190 ++ c->Icp.result_hist[ltype];
62e76326 191 kb_incr(&c->Icp.kbytes_out, size);
192
193 if (LOG_UDP_HIT == ltype)
194 kb_incr(&c->Icp.hit_kbytes_out, size);
81d0c856 195 }
a0eba6bc 196
197 c->last_seen = squid_curtime;
c41d4b6d 198}
199
077fe581 200/**
9bc73deb 201 * This function tracks the number of currently established connections
202 * for a client IP address. When a connection is accepted, call this
203 * with delta = 1. When the connection is closed, call with delta =
204 * -1. To get the current value, simply call with delta = 0.
205 */
206int
b7ac5457 207clientdbEstablished(const Ip::Address &addr, int delta)
9bc73deb 208{
cc192b50 209 char key[MAX_IPSTRLEN];
9bc73deb 210 ClientInfo *c;
62e76326 211
9bc73deb 212 if (!Config.onoff.client_db)
62e76326 213 return 0;
214
4dd643d5 215 addr.toStr(key,MAX_IPSTRLEN);
62e76326 216
9bc73deb 217 c = (ClientInfo *) hash_lookup(client_table, key);
62e76326 218
cc192b50 219 if (c == NULL) {
62e76326 220 c = clientdbAdd(addr);
cc192b50 221 }
62e76326 222
9bc73deb 223 if (c == NULL)
62e76326 224 debug_trap("clientdbUpdate: Failed to add entry");
225
9bc73deb 226 c->n_established += delta;
62e76326 227
9bc73deb 228 return c->n_established;
229}
230
e711a2ff 231#define CUTOFF_SECONDS 3600
c41d4b6d 232int
62e76326 233
b7ac5457 234clientdbCutoffDenied(const Ip::Address &addr)
c41d4b6d 235{
cc192b50 236 char key[MAX_IPSTRLEN];
e711a2ff 237 int NR;
238 int ND;
239 double p;
429fdbec 240 ClientInfo *c;
62e76326 241
59c4d35b 242 if (!Config.onoff.client_db)
62e76326 243 return 0;
244
4dd643d5 245 addr.toStr(key,MAX_IPSTRLEN);
62e76326 246
429fdbec 247 c = (ClientInfo *) hash_lookup(client_table, key);
62e76326 248
c41d4b6d 249 if (c == NULL)
62e76326 250 return 0;
251
e711a2ff 252 /*
253 * If we are in a cutoff window, we don't send a reply
254 */
255 if (squid_curtime - c->cutoff.time < CUTOFF_SECONDS)
62e76326 256 return 1;
257
e711a2ff 258 /*
259 * Calculate the percent of DENIED replies since the last
260 * cutoff time.
261 */
262 NR = c->Icp.n_requests - c->cutoff.n_req;
62e76326 263
e711a2ff 264 if (NR < 150)
62e76326 265 NR = 150;
266
e711a2ff 267 ND = c->Icp.result_hist[LOG_UDP_DENIED] - c->cutoff.n_denied;
62e76326 268
e711a2ff 269 p = 100.0 * ND / NR;
62e76326 270
e711a2ff 271 if (p < 95.0)
62e76326 272 return 0;
273
fa84c01d 274 debugs(1, DBG_CRITICAL, "WARNING: Probable misconfigured neighbor at " << key);
62e76326 275
fa84c01d 276 debugs(1, DBG_CRITICAL, "WARNING: " << ND << " of the last " << NR <<
bf8fe701 277 " ICP replies are DENIED");
62e76326 278
fa84c01d 279 debugs(1, DBG_CRITICAL, "WARNING: No replies will be sent for the next " <<
bf8fe701 280 CUTOFF_SECONDS << " seconds");
62e76326 281
e711a2ff 282 c->cutoff.time = squid_curtime;
62e76326 283
e711a2ff 284 c->cutoff.n_req = c->Icp.n_requests;
62e76326 285
e711a2ff 286 c->cutoff.n_denied = c->Icp.result_hist[LOG_UDP_DENIED];
62e76326 287
e711a2ff 288 return 1;
c41d4b6d 289}
290
8eb58c9c 291void
c41d4b6d 292clientdbDump(StoreEntry * sentry)
293{
c63e5f01 294 const char *name;
c41d4b6d 295 ClientInfo *c;
ac0963ac 296 int icp_total = 0;
297 int icp_hits = 0;
298 int http_total = 0;
299 int http_hits = 0;
15576b6a 300 storeAppendPrintf(sentry, "Cache Clients:\n");
0f6bebac 301 hash_first(client_table);
62e76326 302
0f6bebac 303 while ((c = (ClientInfo *) hash_next(client_table))) {
62e76326 304 storeAppendPrintf(sentry, "Address: %s\n", hashKeyStr(&c->hash));
af6a12ee 305 if ( (name = fqdncache_gethostbyaddr(c->addr, 0)) ) {
c63e5f01
AJ
306 storeAppendPrintf(sentry, "Name: %s\n", name);
307 }
62e76326 308 storeAppendPrintf(sentry, "Currently established connections: %d\n",
309 c->n_established);
077fe581 310 storeAppendPrintf(sentry, " ICP Requests %d\n",
62e76326 311 c->Icp.n_requests);
312
02c8dde5 313 for (LogTags l = LOG_TAG_NONE; l < LOG_TYPE_MAX; ++l) {
62e76326 314 if (c->Icp.result_hist[l] == 0)
315 continue;
316
317 icp_total += c->Icp.result_hist[l];
318
319 if (LOG_UDP_HIT == l)
320 icp_hits += c->Icp.result_hist[l];
321
02c8dde5 322 storeAppendPrintf(sentry, " %-20.20s %7d %3d%%\n",LogTags_str[l], c->Icp.result_hist[l], Math::intPercent(c->Icp.result_hist[l], c->Icp.n_requests));
62e76326 323 }
324
a98bcbee 325 storeAppendPrintf(sentry, " HTTP Requests %d\n", c->Http.n_requests);
62e76326 326
02c8dde5 327 for (LogTags l = LOG_TAG_NONE; l < LOG_TYPE_MAX; ++l) {
62e76326 328 if (c->Http.result_hist[l] == 0)
329 continue;
330
331 http_total += c->Http.result_hist[l];
332
333 if (logTypeIsATcpHit(l))
334 http_hits += c->Http.result_hist[l];
335
336 storeAppendPrintf(sentry,
337 " %-20.20s %7d %3d%%\n",
02c8dde5 338 LogTags_str[l],
62e76326 339 c->Http.result_hist[l],
a98bcbee 340 Math::intPercent(c->Http.result_hist[l], c->Http.n_requests));
62e76326 341 }
342
343 storeAppendPrintf(sentry, "\n");
c41d4b6d 344 }
62e76326 345
ac0963ac 346 storeAppendPrintf(sentry, "TOTALS\n");
347 storeAppendPrintf(sentry, "ICP : %d Queries, %d Hits (%3d%%)\n",
a98bcbee 348 icp_total, icp_hits, Math::intPercent(icp_hits, icp_total));
ac0963ac 349 storeAppendPrintf(sentry, "HTTP: %d Requests, %d Hits (%3d%%)\n",
a98bcbee 350 http_total, http_hits, Math::intPercent(http_hits, http_total));
c41d4b6d 351}
83394596 352
ec878047 353static void
354clientdbFreeItem(void *data)
355{
e6ccf245 356 ClientInfo *c = (ClientInfo *)data;
186477c1 357 safe_free(c->hash.key);
b4cd430a 358
9a0a18de 359#if USE_DELAY_POOLS
b4cd430a
CT
360 if (CommQuotaQueue *q = c->quotaQueue) {
361 q->clientInfo = NULL;
362 delete q; // invalidates cbdata, cancelling any pending kicks
f33d34a8 363 }
b4cd430a
CT
364#endif
365
fdb29403 366 debugs(77, 9, "ClientInfo destructed, this=" << c);
db1cd23c 367 memFree(c, MEM_CLIENT_INFO);
ec878047 368}
369
83394596 370void
371clientdbFreeMemory(void)
372{
ec878047 373 hashFreeItems(client_table, clientdbFreeItem);
83394596 374 hashFreeMemory(client_table);
375 client_table = NULL;
376}
81d0c856 377
a0eba6bc 378static void
379clientdbScheduledGC(void *unused)
380{
381 cleanup_scheduled = 0;
382 clientdbStartGC();
383}
384
385static void
386clientdbGC(void *unused)
387{
388 static int bucket = 0;
389 hash_link *link_next;
390
391 link_next = hash_get_bucket(client_table, bucket++);
392
393 while (link_next != NULL) {
394 ClientInfo *c = (ClientInfo *)link_next;
395 int age = squid_curtime - c->last_seen;
396 link_next = link_next->next;
397
398 if (c->n_established)
399 continue;
400
401 if (age < 24 * 3600 && c->Http.n_requests > 100)
402 continue;
403
404 if (age < 4 * 3600 && (c->Http.n_requests > 10 || c->Icp.n_requests > 10))
405 continue;
406
407 if (age < 5 * 60 && (c->Http.n_requests > 1 || c->Icp.n_requests > 1))
408 continue;
409
410 if (age < 60)
411 continue;
412
413 hash_remove_link(client_table, &c->hash);
414
415 clientdbFreeItem(c);
416
e4f1fdae 417 --statCounter.client_http.clients;
a0eba6bc 418
5086523e 419 ++cleanup_removed;
a0eba6bc 420 }
421
422 if (bucket < CLIENT_DB_HASH_SIZE)
423 eventAdd("client_db garbage collector", clientdbGC, NULL, 0.15, 0);
424 else {
425 bucket = 0;
426 cleanup_running = 0;
427 max_clients = statCounter.client_http.clients * 3 / 2;
428
429 if (!cleanup_scheduled) {
430 cleanup_scheduled = 1;
431 eventAdd("client_db garbage collector", clientdbScheduledGC, NULL, 6 * 3600, 0);
432 }
433
bf8fe701 434 debugs(49, 2, "clientdbGC: Removed " << cleanup_removed << " entries");
a0eba6bc 435 }
436}
437
438static void
439clientdbStartGC(void)
440{
441 max_clients = statCounter.client_http.clients;
442 cleanup_running = 1;
443 cleanup_removed = 0;
444 clientdbGC(NULL);
445}
446
81d0c856 447#if SQUID_SNMP
62e76326 448
b7ac5457
AJ
449Ip::Address *
450client_entry(Ip::Address *current)
81d0c856 451{
6f47fbc7 452 ClientInfo *c = NULL;
cc192b50 453 char key[MAX_IPSTRLEN];
62e76326 454
26ac0430 455 if (current) {
4dd643d5 456 current->toStr(key,MAX_IPSTRLEN);
62e76326 457 hash_first(client_table);
62e76326 458 while ((c = (ClientInfo *) hash_next(client_table))) {
26ac0430 459 if (!strcmp(key, hashKeyStr(&c->hash)))
62e76326 460 break;
461 }
26ac0430
AJ
462
463 c = (ClientInfo *) hash_next(client_table);
464 } else {
465 hash_first(client_table);
62e76326 466 c = (ClientInfo *) hash_next(client_table);
6f47fbc7 467 }
62e76326 468
b6a2f15e 469 hash_last(client_table);
62e76326 470
b6a2f15e 471 if (c)
26ac0430 472 return (&c->addr);
b6a2f15e 473 else
26ac0430
AJ
474 return (NULL);
475
b6a2f15e 476}
81d0c856 477
478variable_list *
6f47fbc7 479snmp_meshCtblFn(variable_list * Var, snint * ErrP)
81d0c856 480{
6a644e75 481 char key[MAX_IPSTRLEN];
81d0c856 482 ClientInfo *c = NULL;
b7ac5457 483 Ip::Address keyIp;
cc192b50 484
81d0c856 485 *ErrP = SNMP_ERR_NOERROR;
6a644e75
AJ
486 MemBuf tmp;
487 debugs(49, 6, HERE << "Current : length=" << Var->name_length << ": " << snmpDebugOid(Var->name, Var->name_length, tmp));
055421ee 488 if (Var->name_length == 16) {
6a644e75 489 oid2addr(&(Var->name[12]), keyIp, 4);
055421ee 490 } else if (Var->name_length == 28) {
6a644e75 491 oid2addr(&(Var->name[12]), keyIp, 16);
6a644e75
AJ
492 } else {
493 *ErrP = SNMP_ERR_NOSUCHNAME;
494 return NULL;
495 }
496
4dd643d5 497 keyIp.toStr(key, sizeof(key));
6a644e75 498 debugs(49, 5, HERE << "[" << key << "] requested!");
81d0c856 499 c = (ClientInfo *) hash_lookup(client_table, key);
62e76326 500
81d0c856 501 if (c == NULL) {
6a644e75 502 debugs(49, 5, HERE << "not found.");
62e76326 503 *ErrP = SNMP_ERR_NOSUCHNAME;
504 return NULL;
81d0c856 505 }
62e76326 506
6a644e75
AJ
507 variable_list *Answer = NULL;
508 int aggr = 0;
6a644e75 509
135171fe 510 switch (Var->name[LEN_SQ_NET + 2]) {
62e76326 511
26ac0430
AJ
512 case MESH_CTBL_ADDR_TYPE: {
513 int ival;
4dd643d5 514 ival = c->addr.isIPv4() ? INETADDRESSTYPE_IPV4 : INETADDRESSTYPE_IPV6 ;
26ac0430
AJ
515 Answer = snmp_var_new_integer(Var->name, Var->name_length,
516 ival, SMI_INTEGER);
517 }
518 break;
519
520 case MESH_CTBL_ADDR: {
521 Answer = snmp_var_new(Var->name, Var->name_length);
522 // InetAddress doesn't have its own ASN.1 type,
523 // like IpAddr does (SMI_IPADDRESS)
524 // See: rfc4001.txt
525 Answer->type = ASN_OCTET_STR;
526 char client[MAX_IPSTRLEN];
4dd643d5 527 c->addr.toStr(client,MAX_IPSTRLEN);
26ac0430
AJ
528 Answer->val_len = strlen(client);
529 Answer->val.string = (u_char *) xstrdup(client);
530 }
531 break;
81d0c856 532 case MESH_CTBL_HTBYTES:
62e76326 533 Answer = snmp_var_new_integer(Var->name, Var->name_length,
534 (snint) c->Http.kbytes_out.kb,
535 SMI_COUNTER32);
536 break;
537
81d0c856 538 case MESH_CTBL_HTREQ:
62e76326 539 Answer = snmp_var_new_integer(Var->name, Var->name_length,
540 (snint) c->Http.n_requests,
541 SMI_COUNTER32);
542 break;
543
81d0c856 544 case MESH_CTBL_HTHITS:
62e76326 545 aggr = 0;
546
02c8dde5 547 for (LogTags l = LOG_TAG_NONE; l < LOG_TYPE_MAX; ++l) {
62e76326 548 if (logTypeIsATcpHit(l))
549 aggr += c->Http.result_hist[l];
550 }
551
552 Answer = snmp_var_new_integer(Var->name, Var->name_length,
553 (snint) aggr,
554 SMI_COUNTER32);
555 break;
556
81d0c856 557 case MESH_CTBL_HTHITBYTES:
62e76326 558 Answer = snmp_var_new_integer(Var->name, Var->name_length,
559 (snint) c->Http.hit_kbytes_out.kb,
560 SMI_COUNTER32);
561 break;
562
81d0c856 563 case MESH_CTBL_ICPBYTES:
62e76326 564 Answer = snmp_var_new_integer(Var->name, Var->name_length,
565 (snint) c->Icp.kbytes_out.kb,
566 SMI_COUNTER32);
567 break;
568
81d0c856 569 case MESH_CTBL_ICPREQ:
62e76326 570 Answer = snmp_var_new_integer(Var->name, Var->name_length,
571 (snint) c->Icp.n_requests,
572 SMI_COUNTER32);
573 break;
574
81d0c856 575 case MESH_CTBL_ICPHITS:
62e76326 576 aggr = c->Icp.result_hist[LOG_UDP_HIT];
577 Answer = snmp_var_new_integer(Var->name, Var->name_length,
578 (snint) aggr,
579 SMI_COUNTER32);
580 break;
581
81d0c856 582 case MESH_CTBL_ICPHITBYTES:
62e76326 583 Answer = snmp_var_new_integer(Var->name, Var->name_length,
584 (snint) c->Icp.hit_kbytes_out.kb,
585 SMI_COUNTER32);
586 break;
587
81d0c856 588 default:
62e76326 589 *ErrP = SNMP_ERR_NOSUCHNAME;
bf8fe701 590 debugs(49, 5, "snmp_meshCtblFn: illegal column.");
62e76326 591 break;
81d0c856 592 }
62e76326 593
81d0c856 594 return Answer;
595}
596
135171fe 597#endif /*SQUID_SNMP */