]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/client_db.cc
4 * DEBUG: section 00 Client Database
5 * AUTHOR: Duane Wessels
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
35 #include "squid-old.h"
37 #include "format/Token.h"
38 #include "ClientInfo.h"
39 #include "ip/Address.h"
40 #include "mgr/Registration.h"
41 #include "SquidMath.h"
42 #include "SquidTime.h"
43 #include "StatCounters.h"
47 static hash_table
*client_table
= NULL
;
49 static ClientInfo
*clientdbAdd(const Ip::Address
&addr
);
50 static FREE clientdbFreeItem
;
51 static void clientdbStartGC(void);
52 static void clientdbScheduledGC(void *);
55 static int max_clients
= 32768;
57 static int max_clients
= 32;
60 static int cleanup_running
= 0;
61 static int cleanup_scheduled
= 0;
62 static int cleanup_removed
;
65 #define CLIENT_DB_HASH_SIZE 65357
67 #define CLIENT_DB_HASH_SIZE 467
72 clientdbAdd(const Ip::Address
&addr
)
75 char *buf
= new char[MAX_IPSTRLEN
];
76 c
= (ClientInfo
*)memAllocate(MEM_CLIENT_INFO
);
77 c
->hash
.key
= addr
.NtoA(buf
,MAX_IPSTRLEN
);
80 /* setup default values for client write limiter */
81 c
->writeLimitingActive
=false;
84 c
->firstTimeConnection
=true;
88 c
->selectWaiting
= false;
89 c
->eventWaiting
= false;
91 /* get current time */
93 c
->prevTime
=current_dtime
;/* put current time to have something sensible here */
95 hash_join(client_table
, &c
->hash
);
96 ++statCounter
.client_http
.clients
;
98 if ((statCounter
.client_http
.clients
> max_clients
) && !cleanup_running
&& cleanup_scheduled
< 2) {
100 eventAdd("client_db garbage collector", clientdbScheduledGC
, NULL
, 90, 0);
107 clientdbRegisterWithCacheManager(void)
109 Mgr::RegisterAction("client_list", "Cache Client List", clientdbDump
, 0, 1);
115 clientdbRegisterWithCacheManager();
120 client_table
= hash_create((HASHCMP
*) strcmp
, CLIENT_DB_HASH_SIZE
, hash_string
);
124 /* returns ClientInfo for given IP addr
125 Returns NULL if no such client (or clientdb turned off)
126 (it is assumed that clientdbEstablished will be called before and create client record if needed)
128 ClientInfo
* clientdbGetInfo(const Ip::Address
&addr
)
130 char key
[MAX_IPSTRLEN
];
133 if (!Config
.onoff
.client_db
)
136 addr
.NtoA(key
,MAX_IPSTRLEN
);
138 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
140 debugs(77, DBG_IMPORTANT
,"Client db does not contain information for given IP address "<<(const char*)key
);
147 clientdbUpdate(const Ip::Address
&addr
, log_type ltype
, AnyP::ProtocolType p
, size_t size
)
149 char key
[MAX_IPSTRLEN
];
152 if (!Config
.onoff
.client_db
)
155 addr
.NtoA(key
,MAX_IPSTRLEN
);
157 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
160 c
= clientdbAdd(addr
);
163 debug_trap("clientdbUpdate: Failed to add entry");
165 if (p
== AnyP::PROTO_HTTP
) {
166 ++ c
->Http
.n_requests
;
167 ++ c
->Http
.result_hist
[ltype
];
168 kb_incr(&c
->Http
.kbytes_out
, size
);
170 if (logTypeIsATcpHit(ltype
))
171 kb_incr(&c
->Http
.hit_kbytes_out
, size
);
172 } else if (p
== AnyP::PROTO_ICP
) {
173 ++ c
->Icp
.n_requests
;
174 ++ c
->Icp
.result_hist
[ltype
];
175 kb_incr(&c
->Icp
.kbytes_out
, size
);
177 if (LOG_UDP_HIT
== ltype
)
178 kb_incr(&c
->Icp
.hit_kbytes_out
, size
);
181 c
->last_seen
= squid_curtime
;
185 * This function tracks the number of currently established connections
186 * for a client IP address. When a connection is accepted, call this
187 * with delta = 1. When the connection is closed, call with delta =
188 * -1. To get the current value, simply call with delta = 0.
191 clientdbEstablished(const Ip::Address
&addr
, int delta
)
193 char key
[MAX_IPSTRLEN
];
196 if (!Config
.onoff
.client_db
)
199 addr
.NtoA(key
,MAX_IPSTRLEN
);
201 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
204 c
= clientdbAdd(addr
);
208 debug_trap("clientdbUpdate: Failed to add entry");
210 c
->n_established
+= delta
;
212 return c
->n_established
;
215 #define CUTOFF_SECONDS 3600
218 clientdbCutoffDenied(const Ip::Address
&addr
)
220 char key
[MAX_IPSTRLEN
];
226 if (!Config
.onoff
.client_db
)
229 addr
.NtoA(key
,MAX_IPSTRLEN
);
231 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
237 * If we are in a cutoff window, we don't send a reply
239 if (squid_curtime
- c
->cutoff
.time
< CUTOFF_SECONDS
)
243 * Calculate the percent of DENIED replies since the last
246 NR
= c
->Icp
.n_requests
- c
->cutoff
.n_req
;
251 ND
= c
->Icp
.result_hist
[LOG_UDP_DENIED
] - c
->cutoff
.n_denied
;
258 debugs(1, 0, "WARNING: Probable misconfigured neighbor at " << key
);
260 debugs(1, 0, "WARNING: " << ND
<< " of the last " << NR
<<
261 " ICP replies are DENIED");
263 debugs(1, 0, "WARNING: No replies will be sent for the next " <<
264 CUTOFF_SECONDS
<< " seconds");
266 c
->cutoff
.time
= squid_curtime
;
268 c
->cutoff
.n_req
= c
->Icp
.n_requests
;
270 c
->cutoff
.n_denied
= c
->Icp
.result_hist
[LOG_UDP_DENIED
];
275 log_type
&operator++ (log_type
&aLogType
)
277 int tmp
= (int)aLogType
;
278 aLogType
= (log_type
)(++tmp
);
283 clientdbDump(StoreEntry
* sentry
)
292 storeAppendPrintf(sentry
, "Cache Clients:\n");
293 hash_first(client_table
);
295 while ((c
= (ClientInfo
*) hash_next(client_table
))) {
296 storeAppendPrintf(sentry
, "Address: %s\n", hashKeyStr(&c
->hash
));
297 if ( (name
= fqdncache_gethostbyaddr(c
->addr
, 0)) ) {
298 storeAppendPrintf(sentry
, "Name: %s\n", name
);
300 storeAppendPrintf(sentry
, "Currently established connections: %d\n",
302 storeAppendPrintf(sentry
, " ICP Requests %d\n",
305 for (l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
306 if (c
->Icp
.result_hist
[l
] == 0)
309 icp_total
+= c
->Icp
.result_hist
[l
];
311 if (LOG_UDP_HIT
== l
)
312 icp_hits
+= c
->Icp
.result_hist
[l
];
314 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
));
317 storeAppendPrintf(sentry
, " HTTP Requests %d\n", c
->Http
.n_requests
);
319 for (l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
320 if (c
->Http
.result_hist
[l
] == 0)
323 http_total
+= c
->Http
.result_hist
[l
];
325 if (logTypeIsATcpHit(l
))
326 http_hits
+= c
->Http
.result_hist
[l
];
328 storeAppendPrintf(sentry
,
329 " %-20.20s %7d %3d%%\n",
331 c
->Http
.result_hist
[l
],
332 Math::intPercent(c
->Http
.result_hist
[l
], c
->Http
.n_requests
));
335 storeAppendPrintf(sentry
, "\n");
338 storeAppendPrintf(sentry
, "TOTALS\n");
339 storeAppendPrintf(sentry
, "ICP : %d Queries, %d Hits (%3d%%)\n",
340 icp_total
, icp_hits
, Math::intPercent(icp_hits
, icp_total
));
341 storeAppendPrintf(sentry
, "HTTP: %d Requests, %d Hits (%3d%%)\n",
342 http_total
, http_hits
, Math::intPercent(http_hits
, http_total
));
346 clientdbFreeItem(void *data
)
348 ClientInfo
*c
= (ClientInfo
*)data
;
349 safe_free(c
->hash
.key
);
352 if (CommQuotaQueue
*q
= c
->quotaQueue
) {
353 q
->clientInfo
= NULL
;
354 delete q
; // invalidates cbdata, cancelling any pending kicks
358 memFree(c
, MEM_CLIENT_INFO
);
362 clientdbFreeMemory(void)
364 hashFreeItems(client_table
, clientdbFreeItem
);
365 hashFreeMemory(client_table
);
370 clientdbScheduledGC(void *unused
)
372 cleanup_scheduled
= 0;
377 clientdbGC(void *unused
)
379 static int bucket
= 0;
380 hash_link
*link_next
;
382 link_next
= hash_get_bucket(client_table
, bucket
++);
384 while (link_next
!= NULL
) {
385 ClientInfo
*c
= (ClientInfo
*)link_next
;
386 int age
= squid_curtime
- c
->last_seen
;
387 link_next
= link_next
->next
;
389 if (c
->n_established
)
392 if (age
< 24 * 3600 && c
->Http
.n_requests
> 100)
395 if (age
< 4 * 3600 && (c
->Http
.n_requests
> 10 || c
->Icp
.n_requests
> 10))
398 if (age
< 5 * 60 && (c
->Http
.n_requests
> 1 || c
->Icp
.n_requests
> 1))
404 hash_remove_link(client_table
, &c
->hash
);
408 --statCounter
.client_http
.clients
;
413 if (bucket
< CLIENT_DB_HASH_SIZE
)
414 eventAdd("client_db garbage collector", clientdbGC
, NULL
, 0.15, 0);
418 max_clients
= statCounter
.client_http
.clients
* 3 / 2;
420 if (!cleanup_scheduled
) {
421 cleanup_scheduled
= 1;
422 eventAdd("client_db garbage collector", clientdbScheduledGC
, NULL
, 6 * 3600, 0);
425 debugs(49, 2, "clientdbGC: Removed " << cleanup_removed
<< " entries");
430 clientdbStartGC(void)
432 max_clients
= statCounter
.client_http
.clients
;
441 client_entry(Ip::Address
*current
)
443 ClientInfo
*c
= NULL
;
444 char key
[MAX_IPSTRLEN
];
447 current
->NtoA(key
,MAX_IPSTRLEN
);
448 hash_first(client_table
);
449 while ((c
= (ClientInfo
*) hash_next(client_table
))) {
450 if (!strcmp(key
, hashKeyStr(&c
->hash
)))
454 c
= (ClientInfo
*) hash_next(client_table
);
456 hash_first(client_table
);
457 c
= (ClientInfo
*) hash_next(client_table
);
460 hash_last(client_table
);
470 snmp_meshCtblFn(variable_list
* Var
, snint
* ErrP
)
472 char key
[MAX_IPSTRLEN
];
473 ClientInfo
*c
= NULL
;
476 *ErrP
= SNMP_ERR_NOERROR
;
478 debugs(49, 6, HERE
<< "Current : length=" << Var
->name_length
<< ": " << snmpDebugOid(Var
->name
, Var
->name_length
, tmp
));
479 if (Var
->name_length
== 16) {
480 oid2addr(&(Var
->name
[12]), keyIp
, 4);
481 } else if (Var
->name_length
== 28) {
482 oid2addr(&(Var
->name
[12]), keyIp
, 16);
484 *ErrP
= SNMP_ERR_NOSUCHNAME
;
488 keyIp
.NtoA(key
, sizeof(key
));
489 debugs(49, 5, HERE
<< "[" << key
<< "] requested!");
490 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
493 debugs(49, 5, HERE
<< "not found.");
494 *ErrP
= SNMP_ERR_NOSUCHNAME
;
498 variable_list
*Answer
= NULL
;
502 switch (Var
->name
[LEN_SQ_NET
+ 2]) {
504 case MESH_CTBL_ADDR_TYPE
: {
506 ival
= c
->addr
.IsIPv4() ? INETADDRESSTYPE_IPV4
: INETADDRESSTYPE_IPV6
;
507 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
512 case MESH_CTBL_ADDR
: {
513 Answer
= snmp_var_new(Var
->name
, Var
->name_length
);
514 // InetAddress doesn't have its own ASN.1 type,
515 // like IpAddr does (SMI_IPADDRESS)
517 Answer
->type
= ASN_OCTET_STR
;
518 char client
[MAX_IPSTRLEN
];
519 c
->addr
.NtoA(client
,MAX_IPSTRLEN
);
520 Answer
->val_len
= strlen(client
);
521 Answer
->val
.string
= (u_char
*) xstrdup(client
);
524 case MESH_CTBL_HTBYTES
:
525 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
526 (snint
) c
->Http
.kbytes_out
.kb
,
530 case MESH_CTBL_HTREQ
:
531 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
532 (snint
) c
->Http
.n_requests
,
536 case MESH_CTBL_HTHITS
:
539 for (l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
540 if (logTypeIsATcpHit(l
))
541 aggr
+= c
->Http
.result_hist
[l
];
544 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
549 case MESH_CTBL_HTHITBYTES
:
550 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
551 (snint
) c
->Http
.hit_kbytes_out
.kb
,
555 case MESH_CTBL_ICPBYTES
:
556 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
557 (snint
) c
->Icp
.kbytes_out
.kb
,
561 case MESH_CTBL_ICPREQ
:
562 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
563 (snint
) c
->Icp
.n_requests
,
567 case MESH_CTBL_ICPHITS
:
568 aggr
= c
->Icp
.result_hist
[LOG_UDP_HIT
];
569 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
574 case MESH_CTBL_ICPHITBYTES
:
575 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
576 (snint
) c
->Icp
.hit_kbytes_out
.kb
,
581 *ErrP
= SNMP_ERR_NOSUCHNAME
;
582 debugs(49, 5, "snmp_meshCtblFn: illegal column.");
589 #endif /*SQUID_SNMP */