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"
12 #include "base/RunnersRegistry.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 
31 static hash_table *client_table = nullptr;
32 
33 static ClientInfo *clientdbAdd(const Ip::Address &addr);
35 static void clientdbStartGC(void);
36 static void clientdbScheduledGC(void *);
37 
38 #if USE_DELAY_POOLS
39 static int max_clients = 32768;
40 #else
41 static int max_clients = 32;
42 #endif
43 
44 static int cleanup_running = 0;
45 static int cleanup_scheduled = 0;
46 static 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
72  key = addr.toStr(buf,MAX_IPSTRLEN);
73 }
74 
75 static 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 
90 static void
92 {
93  if (client_table)
94  return;
95 
97 }
98 
100 {
101 public:
102  /* RegisteredRunner API */
103  void useConfig() override;
104 };
106 
107 void
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 
124  if (!Config.onoff.client_db)
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
137 void
138 clientdbUpdate(const Ip::Address &addr, const LogTags &ltype, AnyP::ProtocolType p, size_t size)
139 {
140  char key[MAX_IPSTRLEN];
141  ClientInfo *c;
142 
143  if (!Config.onoff.client_db)
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())
162  c->Http.hit_kbytes_out += size;
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 
181 int
182 clientdbEstablished(const Ip::Address &addr, int delta)
183 {
184  char key[MAX_IPSTRLEN];
185  ClientInfo *c;
186 
187  if (!Config.onoff.client_db)
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
207 int
208 
210 {
211  char key[MAX_IPSTRLEN];
212  int NR;
213  int ND;
214  double p;
215  ClientInfo *c;
216 
217  if (!Config.onoff.client_db)
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 
266 void
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 
277  while (hash_link *hash = hash_next(client_table)) {
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 
328 static void
329 clientdbFreeItem(void *data)
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 
349 static void
351 {
352  cleanup_scheduled = 0;
353  clientdbStartGC();
354 }
355 
356 static void
357 clientdbGC(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 
386  clientdbFreeItem(c);
387 
389 
390  ++cleanup_removed;
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) {
401  cleanup_scheduled = 1;
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 
409 static void
411 {
413  cleanup_running = 1;
414  cleanup_removed = 0;
415  clientdbGC(nullptr);
416 }
417 
418 #if SQUID_SNMP
419 
420 Ip::Address *
422 {
423  char key[MAX_IPSTRLEN];
425 
426  if (current) {
427  current->toStr(key,MAX_IPSTRLEN);
428  while (hash_link *hash = hash_next(client_table)) {
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;
495  case MESH_CTBL_HTBYTES:
496  Answer = snmp_var_new_integer(Var->name, Var->name_length,
497  (snint) c->Http.kbytes_out.kb,
498  SMI_COUNTER32);
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,
504  SMI_COUNTER32);
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,
517  SMI_COUNTER32);
518  break;
519 
521  Answer = snmp_var_new_integer(Var->name, Var->name_length,
523  SMI_COUNTER32);
524  break;
525 
526  case MESH_CTBL_ICPBYTES:
527  Answer = snmp_var_new_integer(Var->name, Var->name_length,
528  (snint) c->Icp.kbytes_out.kb,
529  SMI_COUNTER32);
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,
535  SMI_COUNTER32);
536  break;
537 
538  case MESH_CTBL_ICPHITS:
539  aggr = c->Icp.result_hist[LOG_UDP_HIT];
540  Answer = snmp_var_new_integer(Var->name, Var->name_length,
541  (snint) aggr,
542  SMI_COUNTER32);
543  break;
544 
546  Answer = snmp_var_new_integer(Var->name, Var->name_length,
547  (snint) c->Icp.hit_kbytes_out.kb,
548  SMI_COUNTER32);
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 
void useConfig() override
Definition: client_db.cc:108
void oid2addr(oid *id, Ip::Address &addr, u_int size)
Definition: snmp_core.cc:1115
ByteCounter hit_kbytes_out
Definition: ClientInfo.h:56
const char * snmpDebugOid(oid *Name, snint Len, MemBuf &outbuf)
Definition: snmp_core.cc:1056
#define DBG_CRITICAL
Definition: Stream.h:37
static int cleanup_scheduled
Definition: client_db.cc:45
struct ClientInfo::Protocol Icp
#define xmalloc
int intPercent(const int a, const int b)
Definition: SquidMath.cc:13
void hash_last(hash_table *)
Definition: hash.cc:204
@ MESH_CTBL_ICPHITS
Definition: cache_snmp.h:265
union variable_list::@19 val
void clientdbDump(StoreEntry *sentry)
Definition: client_db.cc:267
void FREE(void *)
Definition: forward.h:37
@ MESH_CTBL_HTHITS
Definition: cache_snmp.h:261
bool isTcpHit() const
determine if the log tag code indicates a cache HIT
Definition: LogTags.cc:110
@ MESH_CTBL_ICPREQ
Definition: cache_snmp.h:263
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:855
HASHHASH hash_string
Definition: hash.h:45
void hash_remove_link(hash_table *, hash_link *)
Definition: hash.cc:220
#define ASN_OCTET_STR
Definition: asn1.h:54
DefineRunnerRegistrator(ClientDbRr)
Ip::Address addr
Definition: ClientInfo.h:45
#define xstrdup
#define CUTOFF_SECONDS
Definition: client_db.cc:206
void clientdbUpdate(const Ip::Address &addr, const LogTags &ltype, AnyP::ProtocolType p, size_t size)
Definition: client_db.cc:138
int clientdbEstablished(const Ip::Address &addr, int delta)
Definition: client_db.cc:182
Ip::Address * client_entry(Ip::Address *current)
Definition: client_db.cc:421
struct SquidConfig::@97 onoff
ClientInfo(const Ip::Address &)
Definition: client_db.cc:54
hash_link * hash_lookup(hash_table *, const void *)
Definition: hash.cc:146
#define INETADDRESSTYPE_IPV4
Definition: snmp_vars.h:93
int HASHCMP(const void *, const void *)
Definition: hash.h:13
bool isIPv4() const
Definition: Address.cc:178
Definition: forward.h:17
static hash_table * client_table
Definition: client_db.cc:31
struct StatCounters::@111 client_http
@ LOG_TYPE_MAX
Definition: LogTags.h:66
const char * hashKeyStr(const hash_link *)
Definition: hash.cc:313
int clientdbCutoffDenied(const Ip::Address &addr)
Definition: client_db.cc:209
char * toStr(char *buf, const unsigned int blen, int force=AF_UNSPEC) const
Definition: Address.cc:812
size_t kb
Definition: ByteCounter.h:25
int name_length
Definition: snmp_vars.h:71
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition: forward.h:25
oid * name
Definition: snmp_vars.h:70
~ClientInfo() override
Definition: client_db.cc:335
ProtocolType
Definition: ProtocolType.h:23
#define CLIENT_DB_HASH_SIZE
Definition: client_db.cc:49
static void clientdbInit(void)
Definition: client_db.cc:91
int64_t snint
Definition: cache_snmp.h:14
CommQuotaQueue * quotaQueue
clients waiting for more write quota
Definition: ClientInfo.h:72
struct ClientInfo::Protocol Http
int size
Definition: ModDevPoll.cc:69
const char * fqdncache_gethostbyaddr(const Ip::Address &addr, int flags)
Definition: fqdncache.cc:481
struct variable_list * snmp_var_new(oid *, int)
Definition: snmp_vars.c:109
@ LOG_UDP_HIT
Definition: LogTags.h:60
#define SMI_INTEGER
Definition: snmp_vars.h:71
static ClientInfo * clientdbAdd(const Ip::Address &addr)
Definition: client_db.cc:76
static void clientdbStartGC(void)
Definition: client_db.cc:410
@ LOG_TAG_NONE
Definition: LogTags.h:41
u_char * string
Definition: snmp_vars.h:75
Definition: MemBuf.h:23
static FREE clientdbFreeItem
Definition: client_db.cc:34
struct variable_list * snmp_var_new_integer(oid *, int, int, unsigned char)
Definition: snmp_vars.c:151
void debug_trap(const char *message)
Definition: tools.cc:458
time_t last_seen
Definition: ClientInfo.h:67
#define safe_free(x)
Definition: xalloc.h:73
@ MESH_CTBL_ADDR
Definition: cache_snmp.h:258
Base class for Squid-to-client bandwidth limiting.
u_char type
Definition: snmp_vars.h:72
static int cleanup_removed
Definition: client_db.cc:46
int n_established
Definition: ClientInfo.h:66
static hash_table * hash
Definition: text_backend.cc:41
time_t squid_curtime
Definition: stub_libtime.cc:20
ClientInfo * clientdbGetInfo(const Ip::Address &addr)
Definition: client_db.cc:119
void hash_first(hash_table *)
Definition: hash.cc:172
static int cleanup_running
Definition: client_db.cc:44
@ MESH_CTBL_ICPHITBYTES
Definition: cache_snmp.h:266
@ PROTO_HTTP
Definition: ProtocolType.h:25
#define LEN_SQ_NET
Definition: cache_snmp.h:49
static void clientdbScheduledGC(void *)
Definition: client_db.cc:350
variable_list * snmp_meshCtblFn(variable_list *Var, snint *ErrP)
Definition: client_db.cc:442
@ MESH_CTBL_HTREQ
Definition: cache_snmp.h:259
ByteCounter kbytes_out
Definition: ClientInfo.h:55
hash_table * hash_create(HASHCMP *, int, HASHHASH *)
Definition: hash.cc:108
void RegisterAction(char const *action, char const *desc, OBJH *handler, Protected, Atomic, Format)
Definition: Registration.cc:54
LogTags_ot
Definition: LogTags.h:40
#define DBG_IMPORTANT
Definition: Stream.h:38
LogTags_ot oldType
a set of client protocol, cache use, and other transaction outcome tags
Definition: LogTags.h:96
static int max_clients
Definition: client_db.cc:39
#define INETADDRESSTYPE_IPV6
Definition: snmp_vars.h:94
int result_hist[LOG_TYPE_MAX]
Definition: ClientInfo.h:52
#define SNMP_ERR_NOSUCHNAME
Definition: snmp_error.h:44
@ MESH_CTBL_ADDR_TYPE
Definition: cache_snmp.h:257
@ MESH_CTBL_HTBYTES
Definition: cache_snmp.h:260
struct ClientInfo::Cutoff cutoff
@ LOG_UDP_DENIED
Definition: LogTags.h:62
@ MESH_CTBL_HTHITBYTES
Definition: cache_snmp.h:262
static void clientdbGC(void *)
Definition: client_db.cc:357
hash_link * hash_next(hash_table *)
Definition: hash.cc:188
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
void hash_join(hash_table *, hash_link *)
Definition: hash.cc:131
@ MESH_CTBL_ICPBYTES
Definition: cache_snmp.h:264
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition: event.cc:107
@ PROTO_ICP
Definition: ProtocolType.h:31
#define SNMP_ERR_NOERROR
Definition: snmp_error.h:42
hash_link * hash_get_bucket(hash_table *, unsigned int)
Definition: hash.cc:244
class SquidConfig Config
Definition: SquidConfig.cc:12
#define SMI_COUNTER32
Definition: snmp_vars.h:76
StatCounters statCounter
Definition: StatCounters.cc:12

 

Introduction

Documentation

Support

Miscellaneous