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