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