]>
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.
36 #include "client_db.h"
38 #include "format/Token.h"
39 #include "ClientInfo.h"
40 #include "fqdncache.h"
41 #include "ip/Address.h"
43 #include "mgr/Registration.h"
45 #include "SquidMath.h"
46 #include "SquidTime.h"
47 #include "StatCounters.h"
51 static hash_table
*client_table
= NULL
;
53 static ClientInfo
*clientdbAdd(const Ip::Address
&addr
);
54 static FREE clientdbFreeItem
;
55 static void clientdbStartGC(void);
56 static void clientdbScheduledGC(void *);
59 static int max_clients
= 32768;
61 static int max_clients
= 32;
64 static int cleanup_running
= 0;
65 static int cleanup_scheduled
= 0;
66 static int cleanup_removed
;
69 #define CLIENT_DB_HASH_SIZE 65357
71 #define CLIENT_DB_HASH_SIZE 467
76 clientdbAdd(const Ip::Address
&addr
)
79 char *buf
= new char[MAX_IPSTRLEN
];
80 c
= (ClientInfo
*)memAllocate(MEM_CLIENT_INFO
);
81 c
->hash
.key
= addr
.NtoA(buf
,MAX_IPSTRLEN
);
84 /* setup default values for client write limiter */
85 c
->writeLimitingActive
=false;
88 c
->firstTimeConnection
=true;
92 c
->selectWaiting
= false;
93 c
->eventWaiting
= false;
95 /* get current time */
97 c
->prevTime
=current_dtime
;/* put current time to have something sensible here */
99 hash_join(client_table
, &c
->hash
);
100 ++statCounter
.client_http
.clients
;
102 if ((statCounter
.client_http
.clients
> max_clients
) && !cleanup_running
&& cleanup_scheduled
< 2) {
104 eventAdd("client_db garbage collector", clientdbScheduledGC
, NULL
, 90, 0);
111 clientdbRegisterWithCacheManager(void)
113 Mgr::RegisterAction("client_list", "Cache Client List", clientdbDump
, 0, 1);
119 clientdbRegisterWithCacheManager();
124 client_table
= hash_create((HASHCMP
*) strcmp
, CLIENT_DB_HASH_SIZE
, hash_string
);
128 /* returns ClientInfo for given IP addr
129 Returns NULL if no such client (or clientdb turned off)
130 (it is assumed that clientdbEstablished will be called before and create client record if needed)
132 ClientInfo
* clientdbGetInfo(const Ip::Address
&addr
)
134 char key
[MAX_IPSTRLEN
];
137 if (!Config
.onoff
.client_db
)
140 addr
.NtoA(key
,MAX_IPSTRLEN
);
142 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
144 debugs(77, DBG_IMPORTANT
,"Client db does not contain information for given IP address "<<(const char*)key
);
151 clientdbUpdate(const Ip::Address
&addr
, log_type ltype
, AnyP::ProtocolType p
, size_t size
)
153 char key
[MAX_IPSTRLEN
];
156 if (!Config
.onoff
.client_db
)
159 addr
.NtoA(key
,MAX_IPSTRLEN
);
161 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
164 c
= clientdbAdd(addr
);
167 debug_trap("clientdbUpdate: Failed to add entry");
169 if (p
== AnyP::PROTO_HTTP
) {
170 ++ c
->Http
.n_requests
;
171 ++ c
->Http
.result_hist
[ltype
];
172 kb_incr(&c
->Http
.kbytes_out
, size
);
174 if (logTypeIsATcpHit(ltype
))
175 kb_incr(&c
->Http
.hit_kbytes_out
, size
);
176 } else if (p
== AnyP::PROTO_ICP
) {
177 ++ c
->Icp
.n_requests
;
178 ++ c
->Icp
.result_hist
[ltype
];
179 kb_incr(&c
->Icp
.kbytes_out
, size
);
181 if (LOG_UDP_HIT
== ltype
)
182 kb_incr(&c
->Icp
.hit_kbytes_out
, size
);
185 c
->last_seen
= squid_curtime
;
189 * This function tracks the number of currently established connections
190 * for a client IP address. When a connection is accepted, call this
191 * with delta = 1. When the connection is closed, call with delta =
192 * -1. To get the current value, simply call with delta = 0.
195 clientdbEstablished(const Ip::Address
&addr
, int delta
)
197 char key
[MAX_IPSTRLEN
];
200 if (!Config
.onoff
.client_db
)
203 addr
.NtoA(key
,MAX_IPSTRLEN
);
205 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
208 c
= clientdbAdd(addr
);
212 debug_trap("clientdbUpdate: Failed to add entry");
214 c
->n_established
+= delta
;
216 return c
->n_established
;
219 #define CUTOFF_SECONDS 3600
222 clientdbCutoffDenied(const Ip::Address
&addr
)
224 char key
[MAX_IPSTRLEN
];
230 if (!Config
.onoff
.client_db
)
233 addr
.NtoA(key
,MAX_IPSTRLEN
);
235 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
241 * If we are in a cutoff window, we don't send a reply
243 if (squid_curtime
- c
->cutoff
.time
< CUTOFF_SECONDS
)
247 * Calculate the percent of DENIED replies since the last
250 NR
= c
->Icp
.n_requests
- c
->cutoff
.n_req
;
255 ND
= c
->Icp
.result_hist
[LOG_UDP_DENIED
] - c
->cutoff
.n_denied
;
262 debugs(1, DBG_CRITICAL
, "WARNING: Probable misconfigured neighbor at " << key
);
264 debugs(1, DBG_CRITICAL
, "WARNING: " << ND
<< " of the last " << NR
<<
265 " ICP replies are DENIED");
267 debugs(1, DBG_CRITICAL
, "WARNING: No replies will be sent for the next " <<
268 CUTOFF_SECONDS
<< " seconds");
270 c
->cutoff
.time
= squid_curtime
;
272 c
->cutoff
.n_req
= c
->Icp
.n_requests
;
274 c
->cutoff
.n_denied
= c
->Icp
.result_hist
[LOG_UDP_DENIED
];
279 log_type
&operator++ (log_type
&aLogType
)
281 int tmp
= (int)aLogType
;
282 aLogType
= (log_type
)(++tmp
);
287 clientdbDump(StoreEntry
* sentry
)
296 storeAppendPrintf(sentry
, "Cache Clients:\n");
297 hash_first(client_table
);
299 while ((c
= (ClientInfo
*) hash_next(client_table
))) {
300 storeAppendPrintf(sentry
, "Address: %s\n", hashKeyStr(&c
->hash
));
301 if ( (name
= fqdncache_gethostbyaddr(c
->addr
, 0)) ) {
302 storeAppendPrintf(sentry
, "Name: %s\n", name
);
304 storeAppendPrintf(sentry
, "Currently established connections: %d\n",
306 storeAppendPrintf(sentry
, " ICP Requests %d\n",
309 for (l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
310 if (c
->Icp
.result_hist
[l
] == 0)
313 icp_total
+= c
->Icp
.result_hist
[l
];
315 if (LOG_UDP_HIT
== l
)
316 icp_hits
+= c
->Icp
.result_hist
[l
];
318 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
));
321 storeAppendPrintf(sentry
, " HTTP Requests %d\n", c
->Http
.n_requests
);
323 for (l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
324 if (c
->Http
.result_hist
[l
] == 0)
327 http_total
+= c
->Http
.result_hist
[l
];
329 if (logTypeIsATcpHit(l
))
330 http_hits
+= c
->Http
.result_hist
[l
];
332 storeAppendPrintf(sentry
,
333 " %-20.20s %7d %3d%%\n",
335 c
->Http
.result_hist
[l
],
336 Math::intPercent(c
->Http
.result_hist
[l
], c
->Http
.n_requests
));
339 storeAppendPrintf(sentry
, "\n");
342 storeAppendPrintf(sentry
, "TOTALS\n");
343 storeAppendPrintf(sentry
, "ICP : %d Queries, %d Hits (%3d%%)\n",
344 icp_total
, icp_hits
, Math::intPercent(icp_hits
, icp_total
));
345 storeAppendPrintf(sentry
, "HTTP: %d Requests, %d Hits (%3d%%)\n",
346 http_total
, http_hits
, Math::intPercent(http_hits
, http_total
));
350 clientdbFreeItem(void *data
)
352 ClientInfo
*c
= (ClientInfo
*)data
;
353 safe_free(c
->hash
.key
);
356 if (CommQuotaQueue
*q
= c
->quotaQueue
) {
357 q
->clientInfo
= NULL
;
358 delete q
; // invalidates cbdata, cancelling any pending kicks
362 memFree(c
, MEM_CLIENT_INFO
);
366 clientdbFreeMemory(void)
368 hashFreeItems(client_table
, clientdbFreeItem
);
369 hashFreeMemory(client_table
);
374 clientdbScheduledGC(void *unused
)
376 cleanup_scheduled
= 0;
381 clientdbGC(void *unused
)
383 static int bucket
= 0;
384 hash_link
*link_next
;
386 link_next
= hash_get_bucket(client_table
, bucket
++);
388 while (link_next
!= NULL
) {
389 ClientInfo
*c
= (ClientInfo
*)link_next
;
390 int age
= squid_curtime
- c
->last_seen
;
391 link_next
= link_next
->next
;
393 if (c
->n_established
)
396 if (age
< 24 * 3600 && c
->Http
.n_requests
> 100)
399 if (age
< 4 * 3600 && (c
->Http
.n_requests
> 10 || c
->Icp
.n_requests
> 10))
402 if (age
< 5 * 60 && (c
->Http
.n_requests
> 1 || c
->Icp
.n_requests
> 1))
408 hash_remove_link(client_table
, &c
->hash
);
412 --statCounter
.client_http
.clients
;
417 if (bucket
< CLIENT_DB_HASH_SIZE
)
418 eventAdd("client_db garbage collector", clientdbGC
, NULL
, 0.15, 0);
422 max_clients
= statCounter
.client_http
.clients
* 3 / 2;
424 if (!cleanup_scheduled
) {
425 cleanup_scheduled
= 1;
426 eventAdd("client_db garbage collector", clientdbScheduledGC
, NULL
, 6 * 3600, 0);
429 debugs(49, 2, "clientdbGC: Removed " << cleanup_removed
<< " entries");
434 clientdbStartGC(void)
436 max_clients
= statCounter
.client_http
.clients
;
445 client_entry(Ip::Address
*current
)
447 ClientInfo
*c
= NULL
;
448 char key
[MAX_IPSTRLEN
];
451 current
->NtoA(key
,MAX_IPSTRLEN
);
452 hash_first(client_table
);
453 while ((c
= (ClientInfo
*) hash_next(client_table
))) {
454 if (!strcmp(key
, hashKeyStr(&c
->hash
)))
458 c
= (ClientInfo
*) hash_next(client_table
);
460 hash_first(client_table
);
461 c
= (ClientInfo
*) hash_next(client_table
);
464 hash_last(client_table
);
474 snmp_meshCtblFn(variable_list
* Var
, snint
* ErrP
)
476 char key
[MAX_IPSTRLEN
];
477 ClientInfo
*c
= NULL
;
480 *ErrP
= SNMP_ERR_NOERROR
;
482 debugs(49, 6, HERE
<< "Current : length=" << Var
->name_length
<< ": " << snmpDebugOid(Var
->name
, Var
->name_length
, tmp
));
483 if (Var
->name_length
== 16) {
484 oid2addr(&(Var
->name
[12]), keyIp
, 4);
485 } else if (Var
->name_length
== 28) {
486 oid2addr(&(Var
->name
[12]), keyIp
, 16);
488 *ErrP
= SNMP_ERR_NOSUCHNAME
;
492 keyIp
.NtoA(key
, sizeof(key
));
493 debugs(49, 5, HERE
<< "[" << key
<< "] requested!");
494 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
497 debugs(49, 5, HERE
<< "not found.");
498 *ErrP
= SNMP_ERR_NOSUCHNAME
;
502 variable_list
*Answer
= NULL
;
506 switch (Var
->name
[LEN_SQ_NET
+ 2]) {
508 case MESH_CTBL_ADDR_TYPE
: {
510 ival
= c
->addr
.IsIPv4() ? INETADDRESSTYPE_IPV4
: INETADDRESSTYPE_IPV6
;
511 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
516 case MESH_CTBL_ADDR
: {
517 Answer
= snmp_var_new(Var
->name
, Var
->name_length
);
518 // InetAddress doesn't have its own ASN.1 type,
519 // like IpAddr does (SMI_IPADDRESS)
521 Answer
->type
= ASN_OCTET_STR
;
522 char client
[MAX_IPSTRLEN
];
523 c
->addr
.NtoA(client
,MAX_IPSTRLEN
);
524 Answer
->val_len
= strlen(client
);
525 Answer
->val
.string
= (u_char
*) xstrdup(client
);
528 case MESH_CTBL_HTBYTES
:
529 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
530 (snint
) c
->Http
.kbytes_out
.kb
,
534 case MESH_CTBL_HTREQ
:
535 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
536 (snint
) c
->Http
.n_requests
,
540 case MESH_CTBL_HTHITS
:
543 for (l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
544 if (logTypeIsATcpHit(l
))
545 aggr
+= c
->Http
.result_hist
[l
];
548 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
553 case MESH_CTBL_HTHITBYTES
:
554 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
555 (snint
) c
->Http
.hit_kbytes_out
.kb
,
559 case MESH_CTBL_ICPBYTES
:
560 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
561 (snint
) c
->Icp
.kbytes_out
.kb
,
565 case MESH_CTBL_ICPREQ
:
566 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
567 (snint
) c
->Icp
.n_requests
,
571 case MESH_CTBL_ICPHITS
:
572 aggr
= c
->Icp
.result_hist
[LOG_UDP_HIT
];
573 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
578 case MESH_CTBL_ICPHITBYTES
:
579 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
580 (snint
) c
->Icp
.hit_kbytes_out
.kb
,
585 *ErrP
= SNMP_ERR_NOSUCHNAME
;
586 debugs(49, 5, "snmp_meshCtblFn: illegal column.");
593 #endif /*SQUID_SNMP */