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