]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/client_db.cc
2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 00 Client Database */
12 #include "base/RunnersRegistry.h"
13 #include "client_db.h"
14 #include "ClientInfo.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 "SquidTime.h"
24 #include "StatCounters.h"
29 #include "snmp_core.h"
32 static hash_table
*client_table
= NULL
;
34 static ClientInfo
*clientdbAdd(const Ip::Address
&addr
);
35 static FREE clientdbFreeItem
;
36 static void clientdbStartGC(void);
37 static void clientdbScheduledGC(void *);
40 static int max_clients
= 32768;
42 static int max_clients
= 32;
45 static int cleanup_running
= 0;
46 static int cleanup_scheduled
= 0;
47 static int cleanup_removed
;
50 #define CLIENT_DB_HASH_SIZE 65357
52 #define CLIENT_DB_HASH_SIZE 467
55 ClientInfo::ClientInfo(const Ip::Address
&ip
) :
57 BandwidthBucket(0, 0, 0),
63 , writeLimitingActive(false),
64 firstTimeConnection(true),
71 debugs(77, 9, "ClientInfo constructed, this=" << static_cast<void*>(this));
72 char *buf
= static_cast<char*>(xmalloc(MAX_IPSTRLEN
)); // becomes hash.key
73 key
= addr
.toStr(buf
,MAX_IPSTRLEN
);
77 clientdbAdd(const Ip::Address
&addr
)
79 ClientInfo
*c
= new ClientInfo(addr
);
80 hash_join(client_table
, static_cast<hash_link
*>(c
));
81 ++statCounter
.client_http
.clients
;
83 if ((statCounter
.client_http
.clients
> max_clients
) && !cleanup_running
&& cleanup_scheduled
< 2) {
85 eventAdd("client_db garbage collector", clientdbScheduledGC
, NULL
, 90, 0);
97 client_table
= hash_create((HASHCMP
*) strcmp
, CLIENT_DB_HASH_SIZE
, hash_string
);
100 class ClientDbRr
: public RegisteredRunner
103 /* RegisteredRunner API */
104 virtual void useConfig();
106 RunnerRegistrationEntry(ClientDbRr
);
109 ClientDbRr::useConfig()
112 Mgr::RegisterAction("client_list", "Cache Client List", clientdbDump
, 0, 1);
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)
120 ClientInfo
* clientdbGetInfo(const Ip::Address
&addr
)
122 char key
[MAX_IPSTRLEN
];
125 if (!Config
.onoff
.client_db
)
128 addr
.toStr(key
,MAX_IPSTRLEN
);
130 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
132 debugs(77, DBG_IMPORTANT
,"Client db does not contain information for given IP address "<<(const char*)key
);
139 clientdbUpdate(const Ip::Address
&addr
, const LogTags
<ype
, AnyP::ProtocolType p
, size_t size
)
141 char key
[MAX_IPSTRLEN
];
144 if (!Config
.onoff
.client_db
)
147 addr
.toStr(key
,MAX_IPSTRLEN
);
149 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
152 c
= clientdbAdd(addr
);
155 debug_trap("clientdbUpdate: Failed to add entry");
157 if (p
== AnyP::PROTO_HTTP
) {
158 ++ c
->Http
.n_requests
;
159 ++ c
->Http
.result_hist
[ltype
.oldType
];
160 c
->Http
.kbytes_out
+= size
;
162 if (ltype
.isTcpHit())
163 c
->Http
.hit_kbytes_out
+= size
;
164 } else if (p
== AnyP::PROTO_ICP
) {
165 ++ c
->Icp
.n_requests
;
166 ++ c
->Icp
.result_hist
[ltype
.oldType
];
167 c
->Icp
.kbytes_out
+= size
;
169 if (LOG_UDP_HIT
== ltype
.oldType
)
170 c
->Icp
.hit_kbytes_out
+= size
;
173 c
->last_seen
= squid_curtime
;
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.
183 clientdbEstablished(const Ip::Address
&addr
, int delta
)
185 char key
[MAX_IPSTRLEN
];
188 if (!Config
.onoff
.client_db
)
191 addr
.toStr(key
,MAX_IPSTRLEN
);
193 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
196 c
= clientdbAdd(addr
);
200 debug_trap("clientdbUpdate: Failed to add entry");
202 c
->n_established
+= delta
;
204 return c
->n_established
;
207 #define CUTOFF_SECONDS 3600
210 clientdbCutoffDenied(const Ip::Address
&addr
)
212 char key
[MAX_IPSTRLEN
];
218 if (!Config
.onoff
.client_db
)
221 addr
.toStr(key
,MAX_IPSTRLEN
);
223 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
229 * If we are in a cutoff window, we don't send a reply
231 if (squid_curtime
- c
->cutoff
.time
< CUTOFF_SECONDS
)
235 * Calculate the percent of DENIED replies since the last
238 NR
= c
->Icp
.n_requests
- c
->cutoff
.n_req
;
243 ND
= c
->Icp
.result_hist
[LOG_UDP_DENIED
] - c
->cutoff
.n_denied
;
250 debugs(1, DBG_CRITICAL
, "WARNING: Probable misconfigured neighbor at " << key
);
252 debugs(1, DBG_CRITICAL
, "WARNING: " << ND
<< " of the last " << NR
<<
253 " ICP replies are DENIED");
255 debugs(1, DBG_CRITICAL
, "WARNING: No replies will be sent for the next " <<
256 CUTOFF_SECONDS
<< " seconds");
258 c
->cutoff
.time
= squid_curtime
;
260 c
->cutoff
.n_req
= c
->Icp
.n_requests
;
262 c
->cutoff
.n_denied
= c
->Icp
.result_hist
[LOG_UDP_DENIED
];
268 clientdbDump(StoreEntry
* sentry
)
275 storeAppendPrintf(sentry
, "Cache Clients:\n");
276 hash_first(client_table
);
278 while (hash_link
*hash
= hash_next(client_table
)) {
279 const ClientInfo
*c
= static_cast<const ClientInfo
*>(hash
);
280 storeAppendPrintf(sentry
, "Address: %s\n", hashKeyStr(hash
));
281 if ( (name
= fqdncache_gethostbyaddr(c
->addr
, 0)) ) {
282 storeAppendPrintf(sentry
, "Name: %s\n", name
);
284 storeAppendPrintf(sentry
, "Currently established connections: %d\n",
286 storeAppendPrintf(sentry
, " ICP Requests %d\n",
289 for (LogTags_ot l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
290 if (c
->Icp
.result_hist
[l
] == 0)
293 icp_total
+= c
->Icp
.result_hist
[l
];
295 if (LOG_UDP_HIT
== l
)
296 icp_hits
+= c
->Icp
.result_hist
[l
];
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
));
301 storeAppendPrintf(sentry
, " HTTP Requests %d\n", c
->Http
.n_requests
);
303 for (LogTags_ot l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
304 if (c
->Http
.result_hist
[l
] == 0)
307 http_total
+= c
->Http
.result_hist
[l
];
309 if (LogTags(l
).isTcpHit())
310 http_hits
+= c
->Http
.result_hist
[l
];
312 storeAppendPrintf(sentry
,
313 " %-20.20s %7d %3d%%\n",
315 c
->Http
.result_hist
[l
],
316 Math::intPercent(c
->Http
.result_hist
[l
], c
->Http
.n_requests
));
319 storeAppendPrintf(sentry
, "\n");
322 storeAppendPrintf(sentry
, "TOTALS\n");
323 storeAppendPrintf(sentry
, "ICP : %d Queries, %d Hits (%3d%%)\n",
324 icp_total
, icp_hits
, Math::intPercent(icp_hits
, icp_total
));
325 storeAppendPrintf(sentry
, "HTTP: %d Requests, %d Hits (%3d%%)\n",
326 http_total
, http_hits
, Math::intPercent(http_hits
, http_total
));
330 clientdbFreeItem(void *data
)
332 ClientInfo
*c
= (ClientInfo
*)data
;
336 ClientInfo::~ClientInfo()
341 if (CommQuotaQueue
*q
= quotaQueue
) {
342 q
->clientInfo
= NULL
;
343 delete q
; // invalidates cbdata, cancelling any pending kicks
347 debugs(77, 9, "ClientInfo destructed, this=" << static_cast<void*>(this));
351 clientdbFreeMemory(void)
353 hashFreeItems(client_table
, clientdbFreeItem
);
354 hashFreeMemory(client_table
);
359 clientdbScheduledGC(void *)
361 cleanup_scheduled
= 0;
368 static int bucket
= 0;
369 hash_link
*link_next
;
371 link_next
= hash_get_bucket(client_table
, bucket
++);
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
;
378 if (c
->n_established
)
381 if (age
< 24 * 3600 && c
->Http
.n_requests
> 100)
384 if (age
< 4 * 3600 && (c
->Http
.n_requests
> 10 || c
->Icp
.n_requests
> 10))
387 if (age
< 5 * 60 && (c
->Http
.n_requests
> 1 || c
->Icp
.n_requests
> 1))
393 hash_remove_link(client_table
, static_cast<hash_link
*>(c
));
397 --statCounter
.client_http
.clients
;
402 if (bucket
< CLIENT_DB_HASH_SIZE
)
403 eventAdd("client_db garbage collector", clientdbGC
, NULL
, 0.15, 0);
407 max_clients
= statCounter
.client_http
.clients
* 3 / 2;
409 if (!cleanup_scheduled
) {
410 cleanup_scheduled
= 1;
411 eventAdd("client_db garbage collector", clientdbScheduledGC
, NULL
, 6 * 3600, 0);
414 debugs(49, 2, "clientdbGC: Removed " << cleanup_removed
<< " entries");
419 clientdbStartGC(void)
421 max_clients
= statCounter
.client_http
.clients
;
430 client_entry(Ip::Address
*current
)
432 char key
[MAX_IPSTRLEN
];
433 hash_first(client_table
);
436 current
->toStr(key
,MAX_IPSTRLEN
);
437 while (hash_link
*hash
= hash_next(client_table
)) {
438 if (!strcmp(key
, hashKeyStr(hash
)))
443 ClientInfo
*c
= static_cast<ClientInfo
*>(hash_next(client_table
));
445 hash_last(client_table
);
447 return c
? &c
->addr
: nullptr;
451 snmp_meshCtblFn(variable_list
* Var
, snint
* ErrP
)
453 char key
[MAX_IPSTRLEN
];
454 ClientInfo
*c
= NULL
;
457 *ErrP
= SNMP_ERR_NOERROR
;
459 debugs(49, 6, HERE
<< "Current : length=" << Var
->name_length
<< ": " << snmpDebugOid(Var
->name
, Var
->name_length
, tmp
));
460 if (Var
->name_length
== 16) {
461 oid2addr(&(Var
->name
[12]), keyIp
, 4);
462 } else if (Var
->name_length
== 28) {
463 oid2addr(&(Var
->name
[12]), keyIp
, 16);
465 *ErrP
= SNMP_ERR_NOSUCHNAME
;
469 keyIp
.toStr(key
, sizeof(key
));
470 debugs(49, 5, HERE
<< "[" << key
<< "] requested!");
471 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
474 debugs(49, 5, HERE
<< "not found.");
475 *ErrP
= SNMP_ERR_NOSUCHNAME
;
479 variable_list
*Answer
= NULL
;
482 switch (Var
->name
[LEN_SQ_NET
+ 2]) {
484 case MESH_CTBL_ADDR_TYPE
: {
486 ival
= c
->addr
.isIPv4() ? INETADDRESSTYPE_IPV4
: INETADDRESSTYPE_IPV6
;
487 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
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)
497 Answer
->type
= ASN_OCTET_STR
;
498 char client
[MAX_IPSTRLEN
];
499 c
->addr
.toStr(client
,MAX_IPSTRLEN
);
500 Answer
->val_len
= strlen(client
);
501 Answer
->val
.string
= (u_char
*) xstrdup(client
);
504 case MESH_CTBL_HTBYTES
:
505 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
506 (snint
) c
->Http
.kbytes_out
.kb
,
510 case MESH_CTBL_HTREQ
:
511 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
512 (snint
) c
->Http
.n_requests
,
516 case MESH_CTBL_HTHITS
:
519 for (LogTags_ot l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
520 if (LogTags(l
).isTcpHit())
521 aggr
+= c
->Http
.result_hist
[l
];
524 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
529 case MESH_CTBL_HTHITBYTES
:
530 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
531 (snint
) c
->Http
.hit_kbytes_out
.kb
,
535 case MESH_CTBL_ICPBYTES
:
536 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
537 (snint
) c
->Icp
.kbytes_out
.kb
,
541 case MESH_CTBL_ICPREQ
:
542 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
543 (snint
) c
->Icp
.n_requests
,
547 case MESH_CTBL_ICPHITS
:
548 aggr
= c
->Icp
.result_hist
[LOG_UDP_HIT
];
549 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
554 case MESH_CTBL_ICPHITBYTES
:
555 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
556 (snint
) c
->Icp
.hit_kbytes_out
.kb
,
561 *ErrP
= SNMP_ERR_NOSUCHNAME
;
562 debugs(49, 5, "snmp_meshCtblFn: illegal column.");
569 #endif /*SQUID_SNMP */