Squid Web Cache master
Loading...
Searching...
No Matches
client_db.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9/* DEBUG: section 00 Client Database */
10
11#include "squid.h"
13#include "client_db.h"
14#include "ClientInfo.h"
15#include "event.h"
16#include "format/Token.h"
17#include "fqdncache.h"
18#include "ip/Address.h"
19#include "log/access_log.h"
20#include "mgr/Registration.h"
21#include "SquidConfig.h"
22#include "SquidMath.h"
23#include "StatCounters.h"
24#include "Store.h"
25#include "tools.h"
26
27#if SQUID_SNMP
28#include "snmp_core.h"
29#endif
30
31static hash_table *client_table = nullptr;
32
33static ClientInfo *clientdbAdd(const Ip::Address &addr);
35static void clientdbStartGC(void);
36static void clientdbScheduledGC(void *);
37
38#if USE_DELAY_POOLS
39static int max_clients = 32768;
40#else
41static int max_clients = 32;
42#endif
43
44static int cleanup_running = 0;
45static int cleanup_scheduled = 0;
46static int cleanup_removed;
47
48#if USE_DELAY_POOLS
49#define CLIENT_DB_HASH_SIZE 65357
50#else
51#define CLIENT_DB_HASH_SIZE 467
52#endif
53
56 BandwidthBucket(0, 0, 0),
57#endif
58 addr(ip),
59 n_established(0),
60 last_seen(0)
62 , writeLimitingActive(false),
63 firstTimeConnection(true),
64 quotaQueue(nullptr),
65 rationedQuota(0),
66 rationedCount(0),
67 eventWaiting(false)
68#endif
69{
70 debugs(77, 9, "ClientInfo constructed, this=" << static_cast<void*>(this));
71 char *buf = static_cast<char*>(xmalloc(MAX_IPSTRLEN)); // becomes hash.key
73}
74
75static ClientInfo *
77{
78 ClientInfo *c = new ClientInfo(addr);
79 hash_join(client_table, static_cast<hash_link*>(c));
81
84 eventAdd("client_db garbage collector", clientdbScheduledGC, nullptr, 90, 0);
85 }
86
87 return c;
88}
89
90static void
92{
93 if (client_table)
94 return;
95
97}
98
100{
101public:
102 /* RegisteredRunner API */
103 void useConfig() override;
104};
106
107void
109{
110 clientdbInit();
111 Mgr::RegisterAction("client_list", "Cache Client List", clientdbDump, 0, 1);
112}
113
114#if USE_DELAY_POOLS
115/* returns ClientInfo for given IP addr
116 Returns NULL if no such client (or clientdb turned off)
117 (it is assumed that clientdbEstablished will be called before and create client record if needed)
118*/
120{
121 char key[MAX_IPSTRLEN];
122 ClientInfo *c;
123
125 return nullptr;
126
127 addr.toStr(key,MAX_IPSTRLEN);
128
129 c = (ClientInfo *) hash_lookup(client_table, key);
130 if (c==nullptr) {
131 debugs(77, DBG_IMPORTANT,"Client db does not contain information for given IP address "<<(const char*)key);
132 return nullptr;
133 }
134 return c;
135}
136#endif
137void
138clientdbUpdate(const Ip::Address &addr, const LogTags &ltype, AnyP::ProtocolType p, size_t size)
139{
140 char key[MAX_IPSTRLEN];
141 ClientInfo *c;
142
144 return;
145
146 addr.toStr(key,MAX_IPSTRLEN);
147
148 c = (ClientInfo *) hash_lookup(client_table, key);
149
150 if (c == nullptr)
151 c = clientdbAdd(addr);
152
153 if (c == nullptr)
154 debug_trap("clientdbUpdate: Failed to add entry");
155
156 if (p == AnyP::PROTO_HTTP) {
157 ++ c->Http.n_requests;
158 ++ c->Http.result_hist[ltype.oldType];
159 c->Http.kbytes_out += size;
160
161 if (ltype.isTcpHit())
163 } else if (p == AnyP::PROTO_ICP) {
164 ++ c->Icp.n_requests;
165 ++ c->Icp.result_hist[ltype.oldType];
166 c->Icp.kbytes_out += size;
167
168 if (LOG_UDP_HIT == ltype.oldType)
169 c->Icp.hit_kbytes_out += size;
170 }
171
173}
174
181int
182clientdbEstablished(const Ip::Address &addr, int delta)
183{
184 char key[MAX_IPSTRLEN];
185 ClientInfo *c;
186
188 return 0;
189
190 addr.toStr(key,MAX_IPSTRLEN);
191
192 c = (ClientInfo *) hash_lookup(client_table, key);
193
194 if (c == nullptr) {
195 c = clientdbAdd(addr);
196 }
197
198 if (c == nullptr)
199 debug_trap("clientdbUpdate: Failed to add entry");
200
201 c->n_established += delta;
202
203 return c->n_established;
204}
205
206#define CUTOFF_SECONDS 3600
207int
208
210{
211 char key[MAX_IPSTRLEN];
212 int NR;
213 int ND;
214 double p;
215 ClientInfo *c;
216
218 return 0;
219
220 addr.toStr(key,MAX_IPSTRLEN);
221
222 c = (ClientInfo *) hash_lookup(client_table, key);
223
224 if (c == nullptr)
225 return 0;
226
227 /*
228 * If we are in a cutoff window, we don't send a reply
229 */
231 return 1;
232
233 /*
234 * Calculate the percent of DENIED replies since the last
235 * cutoff time.
236 */
237 NR = c->Icp.n_requests - c->cutoff.n_req;
238
239 if (NR < 150)
240 NR = 150;
241
243
244 p = 100.0 * ND / NR;
245
246 if (p < 95.0)
247 return 0;
248
249 debugs(1, DBG_CRITICAL, "WARNING: Probable misconfigured neighbor at " << key);
250
251 debugs(1, DBG_CRITICAL, "WARNING: " << ND << " of the last " << NR <<
252 " ICP replies are DENIED");
253
254 debugs(1, DBG_CRITICAL, "WARNING: No replies will be sent for the next " <<
255 CUTOFF_SECONDS << " seconds");
256
258
259 c->cutoff.n_req = c->Icp.n_requests;
260
262
263 return 1;
264}
265
266void
268{
269 const char *name;
270 int icp_total = 0;
271 int icp_hits = 0;
272 int http_total = 0;
273 int http_hits = 0;
274 storeAppendPrintf(sentry, "Cache Clients:\n");
276
278 const ClientInfo *c = static_cast<const ClientInfo *>(hash);
279 storeAppendPrintf(sentry, "Address: %s\n", hashKeyStr(hash));
280 if ( (name = fqdncache_gethostbyaddr(c->addr, 0)) ) {
281 storeAppendPrintf(sentry, "Name: %s\n", name);
282 }
283 storeAppendPrintf(sentry, "Currently established connections: %d\n",
284 c->n_established);
285 storeAppendPrintf(sentry, " ICP Requests %d\n",
286 c->Icp.n_requests);
287
288 for (LogTags_ot l = LOG_TAG_NONE; l < LOG_TYPE_MAX; ++l) {
289 if (c->Icp.result_hist[l] == 0)
290 continue;
291
292 icp_total += c->Icp.result_hist[l];
293
294 if (LOG_UDP_HIT == l)
295 icp_hits += c->Icp.result_hist[l];
296
297 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));
298 }
299
300 storeAppendPrintf(sentry, " HTTP Requests %d\n", c->Http.n_requests);
301
302 for (LogTags_ot l = LOG_TAG_NONE; l < LOG_TYPE_MAX; ++l) {
303 if (c->Http.result_hist[l] == 0)
304 continue;
305
306 http_total += c->Http.result_hist[l];
307
308 if (LogTags(l).isTcpHit())
309 http_hits += c->Http.result_hist[l];
310
311 storeAppendPrintf(sentry,
312 " %-20.20s %7d %3d%%\n",
313 LogTags(l).c_str(),
314 c->Http.result_hist[l],
316 }
317
318 storeAppendPrintf(sentry, "\n");
319 }
320
321 storeAppendPrintf(sentry, "TOTALS\n");
322 storeAppendPrintf(sentry, "ICP : %d Queries, %d Hits (%3d%%)\n",
323 icp_total, icp_hits, Math::intPercent(icp_hits, icp_total));
324 storeAppendPrintf(sentry, "HTTP: %d Requests, %d Hits (%3d%%)\n",
325 http_total, http_hits, Math::intPercent(http_hits, http_total));
326}
327
328static void
330{
331 ClientInfo *c = (ClientInfo *)data;
332 delete c;
333}
334
336{
337 safe_free(key);
338
339#if USE_DELAY_POOLS
340 if (CommQuotaQueue *q = quotaQueue) {
341 q->clientInfo = nullptr;
342 delete q; // invalidates cbdata, cancelling any pending kicks
343 }
344#endif
345
346 debugs(77, 9, "ClientInfo destructed, this=" << static_cast<void*>(this));
347}
348
349static void
351{
354}
355
356static void
358{
359 static int bucket = 0;
360 hash_link *link_next;
361
362 link_next = hash_get_bucket(client_table, bucket++);
363
364 while (link_next != nullptr) {
365 ClientInfo *c = (ClientInfo *)link_next;
366 int age = squid_curtime - c->last_seen;
367 link_next = link_next->next;
368
369 if (c->n_established)
370 continue;
371
372 if (age < 24 * 3600 && c->Http.n_requests > 100)
373 continue;
374
375 if (age < 4 * 3600 && (c->Http.n_requests > 10 || c->Icp.n_requests > 10))
376 continue;
377
378 if (age < 5 * 60 && (c->Http.n_requests > 1 || c->Icp.n_requests > 1))
379 continue;
380
381 if (age < 60)
382 continue;
383
384 hash_remove_link(client_table, static_cast<hash_link*>(c));
385
387
389
391 }
392
393 if (bucket < CLIENT_DB_HASH_SIZE)
394 eventAdd("client_db garbage collector", clientdbGC, nullptr, 0.15, 0);
395 else {
396 bucket = 0;
397 cleanup_running = 0;
399
400 if (!cleanup_scheduled) {
402 eventAdd("client_db garbage collector", clientdbScheduledGC, nullptr, 6 * 3600, 0);
403 }
404
405 debugs(49, 2, "clientdbGC: Removed " << cleanup_removed << " entries");
406 }
407}
408
409static void
417
418#if SQUID_SNMP
419
422{
423 char key[MAX_IPSTRLEN];
425
426 if (current) {
427 current->toStr(key,MAX_IPSTRLEN);
429 if (!strcmp(key, hashKeyStr(hash)))
430 break;
431 }
432 }
433
434 ClientInfo *c = static_cast<ClientInfo *>(hash_next(client_table));
435
437
438 return c ? &c->addr : nullptr;
439}
440
443{
444 char key[MAX_IPSTRLEN];
445 ClientInfo *c = nullptr;
446 Ip::Address keyIp;
447
448 *ErrP = SNMP_ERR_NOERROR;
449 MemBuf tmp;
450 debugs(49, 6, "Current : length=" << Var->name_length << ": " << snmpDebugOid(Var->name, Var->name_length, tmp));
451 if (Var->name_length == 16) {
452 oid2addr(&(Var->name[12]), keyIp, 4);
453 } else if (Var->name_length == 28) {
454 oid2addr(&(Var->name[12]), keyIp, 16);
455 } else {
456 *ErrP = SNMP_ERR_NOSUCHNAME;
457 return nullptr;
458 }
459
460 keyIp.toStr(key, sizeof(key));
461 debugs(49, 5, "[" << key << "] requested!");
462 c = (ClientInfo *) hash_lookup(client_table, key);
463
464 if (c == nullptr) {
465 debugs(49, 5, "not found.");
466 *ErrP = SNMP_ERR_NOSUCHNAME;
467 return nullptr;
468 }
469
470 variable_list *Answer = nullptr;
471 int aggr = 0;
472
473 switch (Var->name[LEN_SQ_NET + 2]) {
474
475 case MESH_CTBL_ADDR_TYPE: {
476 int ival;
478 Answer = snmp_var_new_integer(Var->name, Var->name_length,
479 ival, SMI_INTEGER);
480 }
481 break;
482
483 case MESH_CTBL_ADDR: {
484 Answer = snmp_var_new(Var->name, Var->name_length);
485 // InetAddress doesn't have its own ASN.1 type,
486 // like IpAddr does (SMI_IPADDRESS)
487 // See: rfc4001.txt
488 Answer->type = ASN_OCTET_STR;
489 char client[MAX_IPSTRLEN];
490 c->addr.toStr(client,MAX_IPSTRLEN);
491 Answer->val_len = strlen(client);
492 Answer->val.string = (u_char *) xstrdup(client);
493 }
494 break;
496 Answer = snmp_var_new_integer(Var->name, Var->name_length,
497 (snint) c->Http.kbytes_out.kb,
499 break;
500
501 case MESH_CTBL_HTREQ:
502 Answer = snmp_var_new_integer(Var->name, Var->name_length,
503 (snint) c->Http.n_requests,
505 break;
506
507 case MESH_CTBL_HTHITS:
508 aggr = 0;
509
510 for (LogTags_ot l = LOG_TAG_NONE; l < LOG_TYPE_MAX; ++l) {
511 if (LogTags(l).isTcpHit())
512 aggr += c->Http.result_hist[l];
513 }
514
515 Answer = snmp_var_new_integer(Var->name, Var->name_length,
516 (snint) aggr,
518 break;
519
521 Answer = snmp_var_new_integer(Var->name, Var->name_length,
524 break;
525
527 Answer = snmp_var_new_integer(Var->name, Var->name_length,
528 (snint) c->Icp.kbytes_out.kb,
530 break;
531
532 case MESH_CTBL_ICPREQ:
533 Answer = snmp_var_new_integer(Var->name, Var->name_length,
534 (snint) c->Icp.n_requests,
536 break;
537
539 aggr = c->Icp.result_hist[LOG_UDP_HIT];
540 Answer = snmp_var_new_integer(Var->name, Var->name_length,
541 (snint) aggr,
543 break;
544
546 Answer = snmp_var_new_integer(Var->name, Var->name_length,
549 break;
550
551 default:
552 *ErrP = SNMP_ERR_NOSUCHNAME;
553 debugs(49, 5, "snmp_meshCtblFn: illegal column.");
554 break;
555 }
556
557 return Answer;
558}
559
560#endif /*SQUID_SNMP */
561
LogTags_ot
Definition LogTags.h:40
@ LOG_UDP_DENIED
Definition LogTags.h:62
@ LOG_UDP_HIT
Definition LogTags.h:60
@ LOG_TAG_NONE
Definition LogTags.h:41
@ LOG_TYPE_MAX
Definition LogTags.h:66
int size
Definition ModDevPoll.cc:70
time_t squid_curtime
#define DefineRunnerRegistrator(ClassName)
class SquidConfig Config
StatCounters statCounter
#define ASN_OCTET_STR
Definition asn1.h:54
#define USE_DELAY_POOLS
Definition autoconf.h:1511
#define LEN_SQ_NET
Definition cache_snmp.h:49
int64_t snint
Definition cache_snmp.h:14
@ MESH_CTBL_HTHITBYTES
Definition cache_snmp.h:262
@ MESH_CTBL_ICPREQ
Definition cache_snmp.h:263
@ MESH_CTBL_ADDR_TYPE
Definition cache_snmp.h:257
@ MESH_CTBL_HTBYTES
Definition cache_snmp.h:260
@ MESH_CTBL_ICPBYTES
Definition cache_snmp.h:264
@ MESH_CTBL_HTHITS
Definition cache_snmp.h:261
@ MESH_CTBL_ICPHITS
Definition cache_snmp.h:265
@ MESH_CTBL_ADDR
Definition cache_snmp.h:258
@ MESH_CTBL_ICPHITBYTES
Definition cache_snmp.h:266
@ MESH_CTBL_HTREQ
Definition cache_snmp.h:259
Base class for Squid-to-client bandwidth limiting.
void useConfig() override
Definition client_db.cc:108
struct ClientInfo::Protocol Icp
Ip::Address addr
Definition ClientInfo.h:45
int n_established
Definition ClientInfo.h:66
CommQuotaQueue * quotaQueue
clients waiting for more write quota
Definition ClientInfo.h:72
time_t last_seen
Definition ClientInfo.h:67
ClientInfo(const Ip::Address &)
Definition client_db.cc:54
struct ClientInfo::Protocol Http
~ClientInfo() override
Definition client_db.cc:335
struct ClientInfo::Cutoff cutoff
char * toStr(char *buf, const unsigned int blen, int force=AF_UNSPEC) const
Definition Address.cc:804
bool isIPv4() const
Definition Address.cc:178
LogTags_ot oldType
a set of client protocol, cache use, and other transaction outcome tags
Definition LogTags.h:96
bool isTcpHit() const
determine if the log tag code indicates a cache HIT
Definition LogTags.cc:110
struct SquidConfig::@90 onoff
struct StatCounters::@104 client_http
static void clientdbGC(void *)
Definition client_db.cc:357
static int cleanup_running
Definition client_db.cc:44
static int max_clients
Definition client_db.cc:39
static void clientdbStartGC(void)
Definition client_db.cc:410
Ip::Address * client_entry(Ip::Address *current)
Definition client_db.cc:421
int clientdbEstablished(const Ip::Address &addr, int delta)
Definition client_db.cc:182
void clientdbUpdate(const Ip::Address &addr, const LogTags &ltype, AnyP::ProtocolType p, size_t size)
Definition client_db.cc:138
static void clientdbScheduledGC(void *)
Definition client_db.cc:350
static int cleanup_removed
Definition client_db.cc:46
variable_list * snmp_meshCtblFn(variable_list *Var, snint *ErrP)
Definition client_db.cc:442
static ClientInfo * clientdbAdd(const Ip::Address &addr)
Definition client_db.cc:76
static void clientdbInit(void)
Definition client_db.cc:91
static int cleanup_scheduled
Definition client_db.cc:45
#define CUTOFF_SECONDS
Definition client_db.cc:206
static hash_table * client_table
Definition client_db.cc:31
static FREE clientdbFreeItem
Definition client_db.cc:34
int clientdbCutoffDenied(const Ip::Address &addr)
Definition client_db.cc:209
ClientInfo * clientdbGetInfo(const Ip::Address &addr)
Definition client_db.cc:119
#define CLIENT_DB_HASH_SIZE
Definition client_db.cc:49
void clientdbDump(StoreEntry *sentry)
Definition client_db.cc:267
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition event.cc:107
const char * fqdncache_gethostbyaddr(const Ip::Address &addr, int flags)
Definition fqdncache.cc:481
HASHHASH hash_string
Definition hash.h:45
hash_link * hash_lookup(hash_table *, const void *)
Definition hash.cc:146
hash_table * hash_create(HASHCMP *, int, HASHHASH *)
Definition hash.cc:108
int HASHCMP(const void *, const void *)
Definition hash.h:13
void hash_last(hash_table *)
Definition hash.cc:204
hash_link * hash_next(hash_table *)
Definition hash.cc:188
hash_link * hash_get_bucket(hash_table *, unsigned int)
Definition hash.cc:244
void hash_first(hash_table *)
Definition hash.cc:172
void hash_join(hash_table *, hash_link *)
Definition hash.cc:131
void hash_remove_link(hash_table *, hash_link *)
Definition hash.cc:220
const char * hashKeyStr(const hash_link *)
Definition hash.cc:313
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition forward.h:25
void FREE(void *)
Definition forward.h:37
@ PROTO_HTTP
@ PROTO_ICP
Definition forward.h:18
int intPercent(const int a, const int b)
Definition SquidMath.cc:13
void RegisterAction(char const *action, char const *desc, OBJH *handler, Protected, Atomic, Format)
#define xstrdup
#define xmalloc
const char * snmpDebugOid(oid *Name, snint Len, MemBuf &outbuf)
void oid2addr(oid *id, Ip::Address &addr, u_int size)
#define SNMP_ERR_NOERROR
Definition snmp_error.h:42
#define SNMP_ERR_NOSUCHNAME
Definition snmp_error.h:44
#define INETADDRESSTYPE_IPV4
Definition snmp_vars.h:93
#define SMI_COUNTER32
Definition snmp_vars.h:76
struct variable_list * snmp_var_new(oid *, int)
Definition snmp_vars.c:109
#define SMI_INTEGER
Definition snmp_vars.h:71
#define INETADDRESSTYPE_IPV6
Definition snmp_vars.h:94
struct variable_list * snmp_var_new_integer(oid *, int, int, unsigned char)
Definition snmp_vars.c:151
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition store.cc:855
ByteCounter hit_kbytes_out
Definition ClientInfo.h:56
int result_hist[LOG_TYPE_MAX]
Definition ClientInfo.h:52
ByteCounter kbytes_out
Definition ClientInfo.h:55
union variable_list::@0 val
u_char type
Definition snmp_vars.h:48
u_char * string
Definition snmp_vars.h:51
static hash_table * hash
void debug_trap(const char *message)
Definition tools.cc:459
#define safe_free(x)
Definition xalloc.h:73