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