client_db.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 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
55#if USE_DELAY_POOLS
56 BandwidthBucket(0, 0, 0),
57#endif
58 addr(ip),
59 n_established(0),
60 last_seen(0)
61#if USE_DELAY_POOLS
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
411{
413 cleanup_running = 1;
414 cleanup_removed = 0;
415 clientdbGC(nullptr);
416}
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:37
@ LOG_UDP_DENIED
Definition: LogTags.h:59
@ LOG_UDP_HIT
Definition: LogTags.h:57
@ LOG_TAG_NONE
Definition: LogTags.h:38
@ LOG_TYPE_MAX
Definition: LogTags.h:63
int size
Definition: ModDevPoll.cc:75
time_t squid_curtime
Definition: stub_libtime.cc:20
class SquidConfig Config
Definition: SquidConfig.cc:12
StatCounters statCounter
Definition: StatCounters.cc:12
#define ASN_OCTET_STR
Definition: asn1.h:54
@ 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
#define LEN_SQ_NET
Definition: cache_snmp.h:49
int64_t snint
Definition: cache_snmp.h:14
Base class for Squid-to-client bandwidth limiting.
size_t kb
Definition: ByteCounter.h:25
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:792
bool isIPv4() const
Definition: Address.cc:158
LogTags_ot oldType
a set of client protocol, cache use, and other transaction outcome tags
Definition: LogTags.h:93
bool isTcpHit() const
determine if the log tag code indicates a cache HIT
Definition: LogTags.cc:101
Definition: MemBuf.h:24
struct SquidConfig::@106 onoff
struct StatCounters::@122 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
DefineRunnerRegistrator(ClientDbRr)
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:194
#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
SQUIDCEXTERN void hash_last(hash_table *)
Definition: hash.cc:204
SQUIDCEXTERN hash_table * hash_create(HASHCMP *, int, HASHHASH *)
Definition: hash.cc:108
SQUIDCEXTERN void hash_join(hash_table *, hash_link *)
Definition: hash.cc:131
SQUIDCEXTERN void hash_first(hash_table *)
Definition: hash.cc:172
int HASHCMP(const void *, const void *)
Definition: hash.h:13
SQUIDCEXTERN HASHHASH hash_string
Definition: hash.h:45
SQUIDCEXTERN hash_link * hash_next(hash_table *)
Definition: hash.cc:188
SQUIDCEXTERN hash_link * hash_get_bucket(hash_table *, unsigned int)
Definition: hash.cc:244
SQUIDCEXTERN void hash_remove_link(hash_table *, hash_link *)
Definition: hash.cc:220
SQUIDCEXTERN const char * hashKeyStr(const hash_link *)
Definition: hash.cc:313
SQUIDCEXTERN hash_link * hash_lookup(hash_table *, const void *)
Definition: hash.cc:146
#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
ProtocolType
Definition: ProtocolType.h:23
@ PROTO_HTTP
Definition: ProtocolType.h:25
@ PROTO_ICP
Definition: ProtocolType.h:31
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, int pw_req_flag, int atomic)
Definition: Registration.cc:16
#define xstrdup
#define xmalloc
const char * snmpDebugOid(oid *Name, snint Len, MemBuf &outbuf)
Definition: snmp_core.cc:1056
void oid2addr(oid *id, Ip::Address &addr, u_int size)
Definition: snmp_core.cc:1115
#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:841
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
int name_length
Definition: snmp_vars.h:47
oid * name
Definition: snmp_vars.h:46
u_char type
Definition: snmp_vars.h:48
u_char * string
Definition: snmp_vars.h:51
union variable_list::@19 val
static hash_table * hash
Definition: text_backend.cc:41
void debug_trap(const char *message)
Definition: tools.cc:458
#define safe_free(x)
Definition: xalloc.h:73

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors