]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/client_db.cc
2 * DEBUG: section 00 Client Database
3 * AUTHOR: Duane Wessels
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
34 #include "client_db.h"
36 #include "format/Token.h"
37 #include "ClientInfo.h"
38 #include "fqdncache.h"
39 #include "ip/Address.h"
40 #include "log/access_log.h"
42 #include "mgr/Registration.h"
43 #include "SquidConfig.h"
44 #include "SquidMath.h"
45 #include "SquidTime.h"
46 #include "StatCounters.h"
51 #include "snmp_core.h"
54 static hash_table
*client_table
= NULL
;
56 static ClientInfo
*clientdbAdd(const Ip::Address
&addr
);
57 static FREE clientdbFreeItem
;
58 static void clientdbStartGC(void);
59 static void clientdbScheduledGC(void *);
62 static int max_clients
= 32768;
64 static int max_clients
= 32;
67 static int cleanup_running
= 0;
68 static int cleanup_scheduled
= 0;
69 static int cleanup_removed
;
72 #define CLIENT_DB_HASH_SIZE 65357
74 #define CLIENT_DB_HASH_SIZE 467
79 clientdbAdd(const Ip::Address
&addr
)
82 char *buf
= new char[MAX_IPSTRLEN
];
83 c
= (ClientInfo
*)memAllocate(MEM_CLIENT_INFO
);
84 c
->hash
.key
= addr
.NtoA(buf
,MAX_IPSTRLEN
);
87 /* setup default values for client write limiter */
88 c
->writeLimitingActive
=false;
91 c
->firstTimeConnection
=true;
95 c
->selectWaiting
= false;
96 c
->eventWaiting
= false;
98 /* get current time */
100 c
->prevTime
=current_dtime
;/* put current time to have something sensible here */
102 hash_join(client_table
, &c
->hash
);
103 ++statCounter
.client_http
.clients
;
105 if ((statCounter
.client_http
.clients
> max_clients
) && !cleanup_running
&& cleanup_scheduled
< 2) {
107 eventAdd("client_db garbage collector", clientdbScheduledGC
, NULL
, 90, 0);
114 clientdbRegisterWithCacheManager(void)
116 Mgr::RegisterAction("client_list", "Cache Client List", clientdbDump
, 0, 1);
122 clientdbRegisterWithCacheManager();
127 client_table
= hash_create((HASHCMP
*) strcmp
, CLIENT_DB_HASH_SIZE
, hash_string
);
131 /* returns ClientInfo for given IP addr
132 Returns NULL if no such client (or clientdb turned off)
133 (it is assumed that clientdbEstablished will be called before and create client record if needed)
135 ClientInfo
* clientdbGetInfo(const Ip::Address
&addr
)
137 char key
[MAX_IPSTRLEN
];
140 if (!Config
.onoff
.client_db
)
143 addr
.NtoA(key
,MAX_IPSTRLEN
);
145 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
147 debugs(77, DBG_IMPORTANT
,"Client db does not contain information for given IP address "<<(const char*)key
);
154 clientdbUpdate(const Ip::Address
&addr
, log_type ltype
, AnyP::ProtocolType p
, size_t size
)
156 char key
[MAX_IPSTRLEN
];
159 if (!Config
.onoff
.client_db
)
162 addr
.NtoA(key
,MAX_IPSTRLEN
);
164 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
167 c
= clientdbAdd(addr
);
170 debug_trap("clientdbUpdate: Failed to add entry");
172 if (p
== AnyP::PROTO_HTTP
) {
173 ++ c
->Http
.n_requests
;
174 ++ c
->Http
.result_hist
[ltype
];
175 kb_incr(&c
->Http
.kbytes_out
, size
);
177 if (logTypeIsATcpHit(ltype
))
178 kb_incr(&c
->Http
.hit_kbytes_out
, size
);
179 } else if (p
== AnyP::PROTO_ICP
) {
180 ++ c
->Icp
.n_requests
;
181 ++ c
->Icp
.result_hist
[ltype
];
182 kb_incr(&c
->Icp
.kbytes_out
, size
);
184 if (LOG_UDP_HIT
== ltype
)
185 kb_incr(&c
->Icp
.hit_kbytes_out
, size
);
188 c
->last_seen
= squid_curtime
;
192 * This function tracks the number of currently established connections
193 * for a client IP address. When a connection is accepted, call this
194 * with delta = 1. When the connection is closed, call with delta =
195 * -1. To get the current value, simply call with delta = 0.
198 clientdbEstablished(const Ip::Address
&addr
, int delta
)
200 char key
[MAX_IPSTRLEN
];
203 if (!Config
.onoff
.client_db
)
206 addr
.NtoA(key
,MAX_IPSTRLEN
);
208 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
211 c
= clientdbAdd(addr
);
215 debug_trap("clientdbUpdate: Failed to add entry");
217 c
->n_established
+= delta
;
219 return c
->n_established
;
222 #define CUTOFF_SECONDS 3600
225 clientdbCutoffDenied(const Ip::Address
&addr
)
227 char key
[MAX_IPSTRLEN
];
233 if (!Config
.onoff
.client_db
)
236 addr
.NtoA(key
,MAX_IPSTRLEN
);
238 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
244 * If we are in a cutoff window, we don't send a reply
246 if (squid_curtime
- c
->cutoff
.time
< CUTOFF_SECONDS
)
250 * Calculate the percent of DENIED replies since the last
253 NR
= c
->Icp
.n_requests
- c
->cutoff
.n_req
;
258 ND
= c
->Icp
.result_hist
[LOG_UDP_DENIED
] - c
->cutoff
.n_denied
;
265 debugs(1, DBG_CRITICAL
, "WARNING: Probable misconfigured neighbor at " << key
);
267 debugs(1, DBG_CRITICAL
, "WARNING: " << ND
<< " of the last " << NR
<<
268 " ICP replies are DENIED");
270 debugs(1, DBG_CRITICAL
, "WARNING: No replies will be sent for the next " <<
271 CUTOFF_SECONDS
<< " seconds");
273 c
->cutoff
.time
= squid_curtime
;
275 c
->cutoff
.n_req
= c
->Icp
.n_requests
;
277 c
->cutoff
.n_denied
= c
->Icp
.result_hist
[LOG_UDP_DENIED
];
282 log_type
&operator++ (log_type
&aLogType
)
284 int tmp
= (int)aLogType
;
285 aLogType
= (log_type
)(++tmp
);
290 clientdbDump(StoreEntry
* sentry
)
299 storeAppendPrintf(sentry
, "Cache Clients:\n");
300 hash_first(client_table
);
302 while ((c
= (ClientInfo
*) hash_next(client_table
))) {
303 storeAppendPrintf(sentry
, "Address: %s\n", hashKeyStr(&c
->hash
));
304 if ( (name
= fqdncache_gethostbyaddr(c
->addr
, 0)) ) {
305 storeAppendPrintf(sentry
, "Name: %s\n", name
);
307 storeAppendPrintf(sentry
, "Currently established connections: %d\n",
309 storeAppendPrintf(sentry
, " ICP Requests %d\n",
312 for (l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
313 if (c
->Icp
.result_hist
[l
] == 0)
316 icp_total
+= c
->Icp
.result_hist
[l
];
318 if (LOG_UDP_HIT
== l
)
319 icp_hits
+= c
->Icp
.result_hist
[l
];
321 storeAppendPrintf(sentry
, " %-20.20s %7d %3d%%\n",Format::log_tags
[l
], c
->Icp
.result_hist
[l
], Math::intPercent(c
->Icp
.result_hist
[l
], c
->Icp
.n_requests
));
324 storeAppendPrintf(sentry
, " HTTP Requests %d\n", c
->Http
.n_requests
);
326 for (l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
327 if (c
->Http
.result_hist
[l
] == 0)
330 http_total
+= c
->Http
.result_hist
[l
];
332 if (logTypeIsATcpHit(l
))
333 http_hits
+= c
->Http
.result_hist
[l
];
335 storeAppendPrintf(sentry
,
336 " %-20.20s %7d %3d%%\n",
338 c
->Http
.result_hist
[l
],
339 Math::intPercent(c
->Http
.result_hist
[l
], c
->Http
.n_requests
));
342 storeAppendPrintf(sentry
, "\n");
345 storeAppendPrintf(sentry
, "TOTALS\n");
346 storeAppendPrintf(sentry
, "ICP : %d Queries, %d Hits (%3d%%)\n",
347 icp_total
, icp_hits
, Math::intPercent(icp_hits
, icp_total
));
348 storeAppendPrintf(sentry
, "HTTP: %d Requests, %d Hits (%3d%%)\n",
349 http_total
, http_hits
, Math::intPercent(http_hits
, http_total
));
353 clientdbFreeItem(void *data
)
355 ClientInfo
*c
= (ClientInfo
*)data
;
356 safe_free(c
->hash
.key
);
359 if (CommQuotaQueue
*q
= c
->quotaQueue
) {
360 q
->clientInfo
= NULL
;
361 delete q
; // invalidates cbdata, cancelling any pending kicks
365 memFree(c
, MEM_CLIENT_INFO
);
369 clientdbFreeMemory(void)
371 hashFreeItems(client_table
, clientdbFreeItem
);
372 hashFreeMemory(client_table
);
377 clientdbScheduledGC(void *unused
)
379 cleanup_scheduled
= 0;
384 clientdbGC(void *unused
)
386 static int bucket
= 0;
387 hash_link
*link_next
;
389 link_next
= hash_get_bucket(client_table
, bucket
++);
391 while (link_next
!= NULL
) {
392 ClientInfo
*c
= (ClientInfo
*)link_next
;
393 int age
= squid_curtime
- c
->last_seen
;
394 link_next
= link_next
->next
;
396 if (c
->n_established
)
399 if (age
< 24 * 3600 && c
->Http
.n_requests
> 100)
402 if (age
< 4 * 3600 && (c
->Http
.n_requests
> 10 || c
->Icp
.n_requests
> 10))
405 if (age
< 5 * 60 && (c
->Http
.n_requests
> 1 || c
->Icp
.n_requests
> 1))
411 hash_remove_link(client_table
, &c
->hash
);
415 --statCounter
.client_http
.clients
;
420 if (bucket
< CLIENT_DB_HASH_SIZE
)
421 eventAdd("client_db garbage collector", clientdbGC
, NULL
, 0.15, 0);
425 max_clients
= statCounter
.client_http
.clients
* 3 / 2;
427 if (!cleanup_scheduled
) {
428 cleanup_scheduled
= 1;
429 eventAdd("client_db garbage collector", clientdbScheduledGC
, NULL
, 6 * 3600, 0);
432 debugs(49, 2, "clientdbGC: Removed " << cleanup_removed
<< " entries");
437 clientdbStartGC(void)
439 max_clients
= statCounter
.client_http
.clients
;
448 client_entry(Ip::Address
*current
)
450 ClientInfo
*c
= NULL
;
451 char key
[MAX_IPSTRLEN
];
454 current
->NtoA(key
,MAX_IPSTRLEN
);
455 hash_first(client_table
);
456 while ((c
= (ClientInfo
*) hash_next(client_table
))) {
457 if (!strcmp(key
, hashKeyStr(&c
->hash
)))
461 c
= (ClientInfo
*) hash_next(client_table
);
463 hash_first(client_table
);
464 c
= (ClientInfo
*) hash_next(client_table
);
467 hash_last(client_table
);
477 snmp_meshCtblFn(variable_list
* Var
, snint
* ErrP
)
479 char key
[MAX_IPSTRLEN
];
480 ClientInfo
*c
= NULL
;
483 *ErrP
= SNMP_ERR_NOERROR
;
485 debugs(49, 6, HERE
<< "Current : length=" << Var
->name_length
<< ": " << snmpDebugOid(Var
->name
, Var
->name_length
, tmp
));
486 if (Var
->name_length
== 16) {
487 oid2addr(&(Var
->name
[12]), keyIp
, 4);
488 } else if (Var
->name_length
== 28) {
489 oid2addr(&(Var
->name
[12]), keyIp
, 16);
491 *ErrP
= SNMP_ERR_NOSUCHNAME
;
495 keyIp
.NtoA(key
, sizeof(key
));
496 debugs(49, 5, HERE
<< "[" << key
<< "] requested!");
497 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
500 debugs(49, 5, HERE
<< "not found.");
501 *ErrP
= SNMP_ERR_NOSUCHNAME
;
505 variable_list
*Answer
= NULL
;
509 switch (Var
->name
[LEN_SQ_NET
+ 2]) {
511 case MESH_CTBL_ADDR_TYPE
: {
513 ival
= c
->addr
.IsIPv4() ? INETADDRESSTYPE_IPV4
: INETADDRESSTYPE_IPV6
;
514 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
519 case MESH_CTBL_ADDR
: {
520 Answer
= snmp_var_new(Var
->name
, Var
->name_length
);
521 // InetAddress doesn't have its own ASN.1 type,
522 // like IpAddr does (SMI_IPADDRESS)
524 Answer
->type
= ASN_OCTET_STR
;
525 char client
[MAX_IPSTRLEN
];
526 c
->addr
.NtoA(client
,MAX_IPSTRLEN
);
527 Answer
->val_len
= strlen(client
);
528 Answer
->val
.string
= (u_char
*) xstrdup(client
);
531 case MESH_CTBL_HTBYTES
:
532 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
533 (snint
) c
->Http
.kbytes_out
.kb
,
537 case MESH_CTBL_HTREQ
:
538 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
539 (snint
) c
->Http
.n_requests
,
543 case MESH_CTBL_HTHITS
:
546 for (l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
547 if (logTypeIsATcpHit(l
))
548 aggr
+= c
->Http
.result_hist
[l
];
551 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
556 case MESH_CTBL_HTHITBYTES
:
557 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
558 (snint
) c
->Http
.hit_kbytes_out
.kb
,
562 case MESH_CTBL_ICPBYTES
:
563 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
564 (snint
) c
->Icp
.kbytes_out
.kb
,
568 case MESH_CTBL_ICPREQ
:
569 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
570 (snint
) c
->Icp
.n_requests
,
574 case MESH_CTBL_ICPHITS
:
575 aggr
= c
->Icp
.result_hist
[LOG_UDP_HIT
];
576 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
581 case MESH_CTBL_ICPHITBYTES
:
582 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
583 (snint
) c
->Icp
.hit_kbytes_out
.kb
,
588 *ErrP
= SNMP_ERR_NOSUCHNAME
;
589 debugs(49, 5, "snmp_meshCtblFn: illegal column.");
596 #endif /*SQUID_SNMP */