]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/client_db.cc
2 * Copyright (C) 1996-2017 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
) :
64 writeLimitingActive(false),
65 firstTimeConnection(true),
73 debugs(77, 9, "ClientInfo constructed, this=" << static_cast<void*>(this));
77 /* put current time to have something sensible here */
78 prevTime
= current_dtime
;
81 char *buf
= static_cast<char*>(xmalloc(MAX_IPSTRLEN
)); // becomes hash.key
82 hash
.key
= addr
.toStr(buf
,MAX_IPSTRLEN
);
86 clientdbAdd(const Ip::Address
&addr
)
88 ClientInfo
*c
= new ClientInfo(addr
);
89 hash_join(client_table
, &c
->hash
);
90 ++statCounter
.client_http
.clients
;
92 if ((statCounter
.client_http
.clients
> max_clients
) && !cleanup_running
&& cleanup_scheduled
< 2) {
94 eventAdd("client_db garbage collector", clientdbScheduledGC
, NULL
, 90, 0);
106 client_table
= hash_create((HASHCMP
*) strcmp
, CLIENT_DB_HASH_SIZE
, hash_string
);
109 class ClientDbRr
: public RegisteredRunner
112 /* RegisteredRunner API */
113 virtual void useConfig();
115 RunnerRegistrationEntry(ClientDbRr
);
118 ClientDbRr::useConfig()
121 Mgr::RegisterAction("client_list", "Cache Client List", clientdbDump
, 0, 1);
125 /* returns ClientInfo for given IP addr
126 Returns NULL if no such client (or clientdb turned off)
127 (it is assumed that clientdbEstablished will be called before and create client record if needed)
129 ClientInfo
* clientdbGetInfo(const Ip::Address
&addr
)
131 char key
[MAX_IPSTRLEN
];
134 if (!Config
.onoff
.client_db
)
137 addr
.toStr(key
,MAX_IPSTRLEN
);
139 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
141 debugs(77, DBG_IMPORTANT
,"Client db does not contain information for given IP address "<<(const char*)key
);
148 clientdbUpdate(const Ip::Address
&addr
, const LogTags
<ype
, AnyP::ProtocolType p
, size_t size
)
150 char key
[MAX_IPSTRLEN
];
153 if (!Config
.onoff
.client_db
)
156 addr
.toStr(key
,MAX_IPSTRLEN
);
158 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
161 c
= clientdbAdd(addr
);
164 debug_trap("clientdbUpdate: Failed to add entry");
166 if (p
== AnyP::PROTO_HTTP
) {
167 ++ c
->Http
.n_requests
;
168 ++ c
->Http
.result_hist
[ltype
.oldType
];
169 c
->Http
.kbytes_out
+= size
;
171 if (ltype
.isTcpHit())
172 c
->Http
.hit_kbytes_out
+= size
;
173 } else if (p
== AnyP::PROTO_ICP
) {
174 ++ c
->Icp
.n_requests
;
175 ++ c
->Icp
.result_hist
[ltype
.oldType
];
176 c
->Icp
.kbytes_out
+= size
;
178 if (LOG_UDP_HIT
== ltype
.oldType
)
179 c
->Icp
.hit_kbytes_out
+= size
;
182 c
->last_seen
= squid_curtime
;
186 * This function tracks the number of currently established connections
187 * for a client IP address. When a connection is accepted, call this
188 * with delta = 1. When the connection is closed, call with delta =
189 * -1. To get the current value, simply call with delta = 0.
192 clientdbEstablished(const Ip::Address
&addr
, int delta
)
194 char key
[MAX_IPSTRLEN
];
197 if (!Config
.onoff
.client_db
)
200 addr
.toStr(key
,MAX_IPSTRLEN
);
202 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
205 c
= clientdbAdd(addr
);
209 debug_trap("clientdbUpdate: Failed to add entry");
211 c
->n_established
+= delta
;
213 return c
->n_established
;
216 #define CUTOFF_SECONDS 3600
219 clientdbCutoffDenied(const Ip::Address
&addr
)
221 char key
[MAX_IPSTRLEN
];
227 if (!Config
.onoff
.client_db
)
230 addr
.toStr(key
,MAX_IPSTRLEN
);
232 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
238 * If we are in a cutoff window, we don't send a reply
240 if (squid_curtime
- c
->cutoff
.time
< CUTOFF_SECONDS
)
244 * Calculate the percent of DENIED replies since the last
247 NR
= c
->Icp
.n_requests
- c
->cutoff
.n_req
;
252 ND
= c
->Icp
.result_hist
[LOG_UDP_DENIED
] - c
->cutoff
.n_denied
;
259 debugs(1, DBG_CRITICAL
, "WARNING: Probable misconfigured neighbor at " << key
);
261 debugs(1, DBG_CRITICAL
, "WARNING: " << ND
<< " of the last " << NR
<<
262 " ICP replies are DENIED");
264 debugs(1, DBG_CRITICAL
, "WARNING: No replies will be sent for the next " <<
265 CUTOFF_SECONDS
<< " seconds");
267 c
->cutoff
.time
= squid_curtime
;
269 c
->cutoff
.n_req
= c
->Icp
.n_requests
;
271 c
->cutoff
.n_denied
= c
->Icp
.result_hist
[LOG_UDP_DENIED
];
277 clientdbDump(StoreEntry
* sentry
)
285 storeAppendPrintf(sentry
, "Cache Clients:\n");
286 hash_first(client_table
);
288 while ((c
= (ClientInfo
*) hash_next(client_table
))) {
289 storeAppendPrintf(sentry
, "Address: %s\n", hashKeyStr(&c
->hash
));
290 if ( (name
= fqdncache_gethostbyaddr(c
->addr
, 0)) ) {
291 storeAppendPrintf(sentry
, "Name: %s\n", name
);
293 storeAppendPrintf(sentry
, "Currently established connections: %d\n",
295 storeAppendPrintf(sentry
, " ICP Requests %d\n",
298 for (LogTags_ot l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
299 if (c
->Icp
.result_hist
[l
] == 0)
302 icp_total
+= c
->Icp
.result_hist
[l
];
304 if (LOG_UDP_HIT
== l
)
305 icp_hits
+= c
->Icp
.result_hist
[l
];
307 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
));
310 storeAppendPrintf(sentry
, " HTTP Requests %d\n", c
->Http
.n_requests
);
312 for (LogTags_ot l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
313 if (c
->Http
.result_hist
[l
] == 0)
316 http_total
+= c
->Http
.result_hist
[l
];
318 if (LogTags(l
).isTcpHit())
319 http_hits
+= c
->Http
.result_hist
[l
];
321 storeAppendPrintf(sentry
,
322 " %-20.20s %7d %3d%%\n",
324 c
->Http
.result_hist
[l
],
325 Math::intPercent(c
->Http
.result_hist
[l
], c
->Http
.n_requests
));
328 storeAppendPrintf(sentry
, "\n");
331 storeAppendPrintf(sentry
, "TOTALS\n");
332 storeAppendPrintf(sentry
, "ICP : %d Queries, %d Hits (%3d%%)\n",
333 icp_total
, icp_hits
, Math::intPercent(icp_hits
, icp_total
));
334 storeAppendPrintf(sentry
, "HTTP: %d Requests, %d Hits (%3d%%)\n",
335 http_total
, http_hits
, Math::intPercent(http_hits
, http_total
));
339 clientdbFreeItem(void *data
)
341 ClientInfo
*c
= (ClientInfo
*)data
;
345 ClientInfo::~ClientInfo()
350 if (CommQuotaQueue
*q
= quotaQueue
) {
351 q
->clientInfo
= NULL
;
352 delete q
; // invalidates cbdata, cancelling any pending kicks
356 debugs(77, 9, "ClientInfo destructed, this=" << static_cast<void*>(this));
360 clientdbFreeMemory(void)
362 hashFreeItems(client_table
, clientdbFreeItem
);
363 hashFreeMemory(client_table
);
368 clientdbScheduledGC(void *)
370 cleanup_scheduled
= 0;
377 static int bucket
= 0;
378 hash_link
*link_next
;
380 link_next
= hash_get_bucket(client_table
, bucket
++);
382 while (link_next
!= NULL
) {
383 ClientInfo
*c
= (ClientInfo
*)link_next
;
384 int age
= squid_curtime
- c
->last_seen
;
385 link_next
= link_next
->next
;
387 if (c
->n_established
)
390 if (age
< 24 * 3600 && c
->Http
.n_requests
> 100)
393 if (age
< 4 * 3600 && (c
->Http
.n_requests
> 10 || c
->Icp
.n_requests
> 10))
396 if (age
< 5 * 60 && (c
->Http
.n_requests
> 1 || c
->Icp
.n_requests
> 1))
402 hash_remove_link(client_table
, &c
->hash
);
406 --statCounter
.client_http
.clients
;
411 if (bucket
< CLIENT_DB_HASH_SIZE
)
412 eventAdd("client_db garbage collector", clientdbGC
, NULL
, 0.15, 0);
416 max_clients
= statCounter
.client_http
.clients
* 3 / 2;
418 if (!cleanup_scheduled
) {
419 cleanup_scheduled
= 1;
420 eventAdd("client_db garbage collector", clientdbScheduledGC
, NULL
, 6 * 3600, 0);
423 debugs(49, 2, "clientdbGC: Removed " << cleanup_removed
<< " entries");
428 clientdbStartGC(void)
430 max_clients
= statCounter
.client_http
.clients
;
439 client_entry(Ip::Address
*current
)
441 ClientInfo
*c
= NULL
;
442 char key
[MAX_IPSTRLEN
];
445 current
->toStr(key
,MAX_IPSTRLEN
);
446 hash_first(client_table
);
447 while ((c
= (ClientInfo
*) hash_next(client_table
))) {
448 if (!strcmp(key
, hashKeyStr(&c
->hash
)))
452 c
= (ClientInfo
*) hash_next(client_table
);
454 hash_first(client_table
);
455 c
= (ClientInfo
*) hash_next(client_table
);
458 hash_last(client_table
);
468 snmp_meshCtblFn(variable_list
* Var
, snint
* ErrP
)
470 char key
[MAX_IPSTRLEN
];
471 ClientInfo
*c
= NULL
;
474 *ErrP
= SNMP_ERR_NOERROR
;
476 debugs(49, 6, HERE
<< "Current : length=" << Var
->name_length
<< ": " << snmpDebugOid(Var
->name
, Var
->name_length
, tmp
));
477 if (Var
->name_length
== 16) {
478 oid2addr(&(Var
->name
[12]), keyIp
, 4);
479 } else if (Var
->name_length
== 28) {
480 oid2addr(&(Var
->name
[12]), keyIp
, 16);
482 *ErrP
= SNMP_ERR_NOSUCHNAME
;
486 keyIp
.toStr(key
, sizeof(key
));
487 debugs(49, 5, HERE
<< "[" << key
<< "] requested!");
488 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
491 debugs(49, 5, HERE
<< "not found.");
492 *ErrP
= SNMP_ERR_NOSUCHNAME
;
496 variable_list
*Answer
= NULL
;
499 switch (Var
->name
[LEN_SQ_NET
+ 2]) {
501 case MESH_CTBL_ADDR_TYPE
: {
503 ival
= c
->addr
.isIPv4() ? INETADDRESSTYPE_IPV4
: INETADDRESSTYPE_IPV6
;
504 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
509 case MESH_CTBL_ADDR
: {
510 Answer
= snmp_var_new(Var
->name
, Var
->name_length
);
511 // InetAddress doesn't have its own ASN.1 type,
512 // like IpAddr does (SMI_IPADDRESS)
514 Answer
->type
= ASN_OCTET_STR
;
515 char client
[MAX_IPSTRLEN
];
516 c
->addr
.toStr(client
,MAX_IPSTRLEN
);
517 Answer
->val_len
= strlen(client
);
518 Answer
->val
.string
= (u_char
*) xstrdup(client
);
521 case MESH_CTBL_HTBYTES
:
522 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
523 (snint
) c
->Http
.kbytes_out
.kb
,
527 case MESH_CTBL_HTREQ
:
528 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
529 (snint
) c
->Http
.n_requests
,
533 case MESH_CTBL_HTHITS
:
536 for (LogTags_ot l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
537 if (LogTags(l
).isTcpHit())
538 aggr
+= c
->Http
.result_hist
[l
];
541 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
546 case MESH_CTBL_HTHITBYTES
:
547 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
548 (snint
) c
->Http
.hit_kbytes_out
.kb
,
552 case MESH_CTBL_ICPBYTES
:
553 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
554 (snint
) c
->Icp
.kbytes_out
.kb
,
558 case MESH_CTBL_ICPREQ
:
559 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
560 (snint
) c
->Icp
.n_requests
,
564 case MESH_CTBL_ICPHITS
:
565 aggr
= c
->Icp
.result_hist
[LOG_UDP_HIT
];
566 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
571 case MESH_CTBL_ICPHITBYTES
:
572 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
573 (snint
) c
->Icp
.hit_kbytes_out
.kb
,
578 *ErrP
= SNMP_ERR_NOSUCHNAME
;
579 debugs(49, 5, "snmp_meshCtblFn: illegal column.");
586 #endif /*SQUID_SNMP */