]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/client_db.cc
2 * Copyright (C) 1996-2015 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
57 clientdbAdd(const Ip::Address
&addr
)
60 char *buf
= static_cast<char*>(xmalloc(MAX_IPSTRLEN
)); // becomes hash.key
61 c
= (ClientInfo
*)memAllocate(MEM_CLIENT_INFO
);
62 debugs(77, 9, "ClientInfo constructed, this=" << c
);
63 c
->hash
.key
= addr
.toStr(buf
,MAX_IPSTRLEN
);
66 /* setup default values for client write limiter */
67 c
->writeLimitingActive
=false;
70 c
->firstTimeConnection
=true;
74 c
->selectWaiting
= false;
75 c
->eventWaiting
= false;
77 /* get current time */
79 c
->prevTime
=current_dtime
;/* put current time to have something sensible here */
81 hash_join(client_table
, &c
->hash
);
82 ++statCounter
.client_http
.clients
;
84 if ((statCounter
.client_http
.clients
> max_clients
) && !cleanup_running
&& cleanup_scheduled
< 2) {
86 eventAdd("client_db garbage collector", clientdbScheduledGC
, NULL
, 90, 0);
98 client_table
= hash_create((HASHCMP
*) strcmp
, CLIENT_DB_HASH_SIZE
, hash_string
);
101 class ClientDbRr
: public RegisteredRunner
104 /* RegisteredRunner API */
105 virtual void useConfig();
107 RunnerRegistrationEntry(ClientDbRr
);
110 ClientDbRr::useConfig()
113 Mgr::RegisterAction("client_list", "Cache Client List", clientdbDump
, 0, 1);
117 /* returns ClientInfo for given IP addr
118 Returns NULL if no such client (or clientdb turned off)
119 (it is assumed that clientdbEstablished will be called before and create client record if needed)
121 ClientInfo
* clientdbGetInfo(const Ip::Address
&addr
)
123 char key
[MAX_IPSTRLEN
];
126 if (!Config
.onoff
.client_db
)
129 addr
.toStr(key
,MAX_IPSTRLEN
);
131 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
133 debugs(77, DBG_IMPORTANT
,"Client db does not contain information for given IP address "<<(const char*)key
);
140 clientdbUpdate(const Ip::Address
&addr
, LogTags ltype
, AnyP::ProtocolType p
, size_t size
)
142 char key
[MAX_IPSTRLEN
];
145 if (!Config
.onoff
.client_db
)
148 addr
.toStr(key
,MAX_IPSTRLEN
);
150 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
153 c
= clientdbAdd(addr
);
156 debug_trap("clientdbUpdate: Failed to add entry");
158 if (p
== AnyP::PROTO_HTTP
) {
159 ++ c
->Http
.n_requests
;
160 ++ c
->Http
.result_hist
[ltype
];
161 kb_incr(&c
->Http
.kbytes_out
, size
);
163 if (logTypeIsATcpHit(ltype
))
164 kb_incr(&c
->Http
.hit_kbytes_out
, size
);
165 } else if (p
== AnyP::PROTO_ICP
) {
166 ++ c
->Icp
.n_requests
;
167 ++ c
->Icp
.result_hist
[ltype
];
168 kb_incr(&c
->Icp
.kbytes_out
, size
);
170 if (LOG_UDP_HIT
== ltype
)
171 kb_incr(&c
->Icp
.hit_kbytes_out
, size
);
174 c
->last_seen
= squid_curtime
;
178 * This function tracks the number of currently established connections
179 * for a client IP address. When a connection is accepted, call this
180 * with delta = 1. When the connection is closed, call with delta =
181 * -1. To get the current value, simply call with delta = 0.
184 clientdbEstablished(const Ip::Address
&addr
, int delta
)
186 char key
[MAX_IPSTRLEN
];
189 if (!Config
.onoff
.client_db
)
192 addr
.toStr(key
,MAX_IPSTRLEN
);
194 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
197 c
= clientdbAdd(addr
);
201 debug_trap("clientdbUpdate: Failed to add entry");
203 c
->n_established
+= delta
;
205 return c
->n_established
;
208 #define CUTOFF_SECONDS 3600
211 clientdbCutoffDenied(const Ip::Address
&addr
)
213 char key
[MAX_IPSTRLEN
];
219 if (!Config
.onoff
.client_db
)
222 addr
.toStr(key
,MAX_IPSTRLEN
);
224 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
230 * If we are in a cutoff window, we don't send a reply
232 if (squid_curtime
- c
->cutoff
.time
< CUTOFF_SECONDS
)
236 * Calculate the percent of DENIED replies since the last
239 NR
= c
->Icp
.n_requests
- c
->cutoff
.n_req
;
244 ND
= c
->Icp
.result_hist
[LOG_UDP_DENIED
] - c
->cutoff
.n_denied
;
251 debugs(1, DBG_CRITICAL
, "WARNING: Probable misconfigured neighbor at " << key
);
253 debugs(1, DBG_CRITICAL
, "WARNING: " << ND
<< " of the last " << NR
<<
254 " ICP replies are DENIED");
256 debugs(1, DBG_CRITICAL
, "WARNING: No replies will be sent for the next " <<
257 CUTOFF_SECONDS
<< " seconds");
259 c
->cutoff
.time
= squid_curtime
;
261 c
->cutoff
.n_req
= c
->Icp
.n_requests
;
263 c
->cutoff
.n_denied
= c
->Icp
.result_hist
[LOG_UDP_DENIED
];
269 clientdbDump(StoreEntry
* sentry
)
277 storeAppendPrintf(sentry
, "Cache Clients:\n");
278 hash_first(client_table
);
280 while ((c
= (ClientInfo
*) hash_next(client_table
))) {
281 storeAppendPrintf(sentry
, "Address: %s\n", hashKeyStr(&c
->hash
));
282 if ( (name
= fqdncache_gethostbyaddr(c
->addr
, 0)) ) {
283 storeAppendPrintf(sentry
, "Name: %s\n", name
);
285 storeAppendPrintf(sentry
, "Currently established connections: %d\n",
287 storeAppendPrintf(sentry
, " ICP Requests %d\n",
290 for (LogTags l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
291 if (c
->Icp
.result_hist
[l
] == 0)
294 icp_total
+= c
->Icp
.result_hist
[l
];
296 if (LOG_UDP_HIT
== l
)
297 icp_hits
+= c
->Icp
.result_hist
[l
];
299 storeAppendPrintf(sentry
, " %-20.20s %7d %3d%%\n",LogTags_str
[l
], c
->Icp
.result_hist
[l
], Math::intPercent(c
->Icp
.result_hist
[l
], c
->Icp
.n_requests
));
302 storeAppendPrintf(sentry
, " HTTP Requests %d\n", c
->Http
.n_requests
);
304 for (LogTags l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
305 if (c
->Http
.result_hist
[l
] == 0)
308 http_total
+= c
->Http
.result_hist
[l
];
310 if (logTypeIsATcpHit(l
))
311 http_hits
+= c
->Http
.result_hist
[l
];
313 storeAppendPrintf(sentry
,
314 " %-20.20s %7d %3d%%\n",
316 c
->Http
.result_hist
[l
],
317 Math::intPercent(c
->Http
.result_hist
[l
], c
->Http
.n_requests
));
320 storeAppendPrintf(sentry
, "\n");
323 storeAppendPrintf(sentry
, "TOTALS\n");
324 storeAppendPrintf(sentry
, "ICP : %d Queries, %d Hits (%3d%%)\n",
325 icp_total
, icp_hits
, Math::intPercent(icp_hits
, icp_total
));
326 storeAppendPrintf(sentry
, "HTTP: %d Requests, %d Hits (%3d%%)\n",
327 http_total
, http_hits
, Math::intPercent(http_hits
, http_total
));
331 clientdbFreeItem(void *data
)
333 ClientInfo
*c
= (ClientInfo
*)data
;
334 safe_free(c
->hash
.key
);
337 if (CommQuotaQueue
*q
= c
->quotaQueue
) {
338 q
->clientInfo
= NULL
;
339 delete q
; // invalidates cbdata, cancelling any pending kicks
343 debugs(77, 9, "ClientInfo destructed, this=" << c
);
344 memFree(c
, MEM_CLIENT_INFO
);
348 clientdbFreeMemory(void)
350 hashFreeItems(client_table
, clientdbFreeItem
);
351 hashFreeMemory(client_table
);
356 clientdbScheduledGC(void *)
358 cleanup_scheduled
= 0;
365 static int bucket
= 0;
366 hash_link
*link_next
;
368 link_next
= hash_get_bucket(client_table
, bucket
++);
370 while (link_next
!= NULL
) {
371 ClientInfo
*c
= (ClientInfo
*)link_next
;
372 int age
= squid_curtime
- c
->last_seen
;
373 link_next
= link_next
->next
;
375 if (c
->n_established
)
378 if (age
< 24 * 3600 && c
->Http
.n_requests
> 100)
381 if (age
< 4 * 3600 && (c
->Http
.n_requests
> 10 || c
->Icp
.n_requests
> 10))
384 if (age
< 5 * 60 && (c
->Http
.n_requests
> 1 || c
->Icp
.n_requests
> 1))
390 hash_remove_link(client_table
, &c
->hash
);
394 --statCounter
.client_http
.clients
;
399 if (bucket
< CLIENT_DB_HASH_SIZE
)
400 eventAdd("client_db garbage collector", clientdbGC
, NULL
, 0.15, 0);
404 max_clients
= statCounter
.client_http
.clients
* 3 / 2;
406 if (!cleanup_scheduled
) {
407 cleanup_scheduled
= 1;
408 eventAdd("client_db garbage collector", clientdbScheduledGC
, NULL
, 6 * 3600, 0);
411 debugs(49, 2, "clientdbGC: Removed " << cleanup_removed
<< " entries");
416 clientdbStartGC(void)
418 max_clients
= statCounter
.client_http
.clients
;
427 client_entry(Ip::Address
*current
)
429 ClientInfo
*c
= NULL
;
430 char key
[MAX_IPSTRLEN
];
433 current
->toStr(key
,MAX_IPSTRLEN
);
434 hash_first(client_table
);
435 while ((c
= (ClientInfo
*) hash_next(client_table
))) {
436 if (!strcmp(key
, hashKeyStr(&c
->hash
)))
440 c
= (ClientInfo
*) hash_next(client_table
);
442 hash_first(client_table
);
443 c
= (ClientInfo
*) hash_next(client_table
);
446 hash_last(client_table
);
456 snmp_meshCtblFn(variable_list
* Var
, snint
* ErrP
)
458 char key
[MAX_IPSTRLEN
];
459 ClientInfo
*c
= NULL
;
462 *ErrP
= SNMP_ERR_NOERROR
;
464 debugs(49, 6, HERE
<< "Current : length=" << Var
->name_length
<< ": " << snmpDebugOid(Var
->name
, Var
->name_length
, tmp
));
465 if (Var
->name_length
== 16) {
466 oid2addr(&(Var
->name
[12]), keyIp
, 4);
467 } else if (Var
->name_length
== 28) {
468 oid2addr(&(Var
->name
[12]), keyIp
, 16);
470 *ErrP
= SNMP_ERR_NOSUCHNAME
;
474 keyIp
.toStr(key
, sizeof(key
));
475 debugs(49, 5, HERE
<< "[" << key
<< "] requested!");
476 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
479 debugs(49, 5, HERE
<< "not found.");
480 *ErrP
= SNMP_ERR_NOSUCHNAME
;
484 variable_list
*Answer
= NULL
;
487 switch (Var
->name
[LEN_SQ_NET
+ 2]) {
489 case MESH_CTBL_ADDR_TYPE
: {
491 ival
= c
->addr
.isIPv4() ? INETADDRESSTYPE_IPV4
: INETADDRESSTYPE_IPV6
;
492 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
497 case MESH_CTBL_ADDR
: {
498 Answer
= snmp_var_new(Var
->name
, Var
->name_length
);
499 // InetAddress doesn't have its own ASN.1 type,
500 // like IpAddr does (SMI_IPADDRESS)
502 Answer
->type
= ASN_OCTET_STR
;
503 char client
[MAX_IPSTRLEN
];
504 c
->addr
.toStr(client
,MAX_IPSTRLEN
);
505 Answer
->val_len
= strlen(client
);
506 Answer
->val
.string
= (u_char
*) xstrdup(client
);
509 case MESH_CTBL_HTBYTES
:
510 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
511 (snint
) c
->Http
.kbytes_out
.kb
,
515 case MESH_CTBL_HTREQ
:
516 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
517 (snint
) c
->Http
.n_requests
,
521 case MESH_CTBL_HTHITS
:
524 for (LogTags l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
525 if (logTypeIsATcpHit(l
))
526 aggr
+= c
->Http
.result_hist
[l
];
529 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
534 case MESH_CTBL_HTHITBYTES
:
535 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
536 (snint
) c
->Http
.hit_kbytes_out
.kb
,
540 case MESH_CTBL_ICPBYTES
:
541 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
542 (snint
) c
->Icp
.kbytes_out
.kb
,
546 case MESH_CTBL_ICPREQ
:
547 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
548 (snint
) c
->Icp
.n_requests
,
552 case MESH_CTBL_ICPHITS
:
553 aggr
= c
->Icp
.result_hist
[LOG_UDP_HIT
];
554 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
559 case MESH_CTBL_ICPHITBYTES
:
560 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
561 (snint
) c
->Icp
.hit_kbytes_out
.kb
,
566 *ErrP
= SNMP_ERR_NOSUCHNAME
;
567 debugs(49, 5, "snmp_meshCtblFn: illegal column.");
574 #endif /*SQUID_SNMP */