]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/client_db.cc
3 * $Id: client_db.cc,v 1.72 2007/12/14 23:11:46 amosjeffries Exp $
5 * DEBUG: section 0 Client Database
6 * AUTHOR: Duane Wessels
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
38 #include "CacheManager.h"
39 #include "SquidTime.h"
43 static hash_table
*client_table
= NULL
;
45 static ClientInfo
*clientdbAdd(const IPAddress
&addr
);
46 static FREE clientdbFreeItem
;
47 static void clientdbStartGC(void);
48 static void clientdbScheduledGC(void *);
50 static int max_clients
= 32;
51 static int cleanup_running
= 0;
52 static int cleanup_scheduled
= 0;
53 static int cleanup_removed
;
55 #define CLIENT_DB_HASH_SIZE 467
59 clientdbAdd(const IPAddress
&addr
)
62 char *buf
= new char[MAX_IPSTRLEN
];
63 c
= (ClientInfo
*)memAllocate(MEM_CLIENT_INFO
);
64 c
->hash
.key
= addr
.NtoA(buf
,MAX_IPSTRLEN
);
66 hash_join(client_table
, &c
->hash
);
67 statCounter
.client_http
.clients
++;
69 if ((statCounter
.client_http
.clients
> max_clients
) && !cleanup_running
&& cleanup_scheduled
< 2)
72 eventAdd("client_db garbage collector", clientdbScheduledGC
, NULL
, 90, 0);
79 clientdbRegisterWithCacheManager(void)
81 CacheManager::GetInstance()->
82 registerAction("client_list", "Cache Client List", clientdbDump
, 0, 1);
88 clientdbRegisterWithCacheManager();
93 client_table
= hash_create((HASHCMP
*) strcmp
, CLIENT_DB_HASH_SIZE
, hash_string
);
98 clientdbUpdate(const IPAddress
&addr
, log_type ltype
, protocol_t p
, size_t size
)
100 char key
[MAX_IPSTRLEN
];
103 if (!Config
.onoff
.client_db
)
106 addr
.NtoA(key
,MAX_IPSTRLEN
);
108 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
111 c
= clientdbAdd(addr
);
114 debug_trap("clientdbUpdate: Failed to add entry");
118 c
->Http
.n_requests
++;
119 c
->Http
.result_hist
[ltype
]++;
120 kb_incr(&c
->Http
.kbytes_out
, size
);
122 if (logTypeIsATcpHit(ltype
))
123 kb_incr(&c
->Http
.hit_kbytes_out
, size
);
124 } else if (p
== PROTO_ICP
)
127 c
->Icp
.result_hist
[ltype
]++;
128 kb_incr(&c
->Icp
.kbytes_out
, size
);
130 if (LOG_UDP_HIT
== ltype
)
131 kb_incr(&c
->Icp
.hit_kbytes_out
, size
);
134 c
->last_seen
= squid_curtime
;
138 * This function tracks the number of currently established connections
139 * for a client IP address. When a connection is accepted, call this
140 * with delta = 1. When the connection is closed, call with delta =
141 * -1. To get the current value, simply call with delta = 0.
144 clientdbEstablished(const IPAddress
&addr
, int delta
)
146 char key
[MAX_IPSTRLEN
];
149 if (!Config
.onoff
.client_db
)
152 addr
.NtoA(key
,MAX_IPSTRLEN
);
154 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
157 c
= clientdbAdd(addr
);
161 debug_trap("clientdbUpdate: Failed to add entry");
163 c
->n_established
+= delta
;
165 return c
->n_established
;
168 #define CUTOFF_SECONDS 3600
171 clientdbCutoffDenied(const IPAddress
&addr
)
173 char key
[MAX_IPSTRLEN
];
179 if (!Config
.onoff
.client_db
)
182 addr
.NtoA(key
,MAX_IPSTRLEN
);
184 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
190 * If we are in a cutoff window, we don't send a reply
192 if (squid_curtime
- c
->cutoff
.time
< CUTOFF_SECONDS
)
196 * Calculate the percent of DENIED replies since the last
199 NR
= c
->Icp
.n_requests
- c
->cutoff
.n_req
;
204 ND
= c
->Icp
.result_hist
[LOG_UDP_DENIED
] - c
->cutoff
.n_denied
;
211 debugs(1, 0, "WARNING: Probable misconfigured neighbor at " << key
);
213 debugs(1, 0, "WARNING: " << ND
<< " of the last " << NR
<<
214 " ICP replies are DENIED");
216 debugs(1, 0, "WARNING: No replies will be sent for the next " <<
217 CUTOFF_SECONDS
<< " seconds");
219 c
->cutoff
.time
= squid_curtime
;
221 c
->cutoff
.n_req
= c
->Icp
.n_requests
;
223 c
->cutoff
.n_denied
= c
->Icp
.result_hist
[LOG_UDP_DENIED
];
228 log_type
&operator++ (log_type
&aLogType
)
230 int tmp
= (int)aLogType
;
231 aLogType
= (log_type
)(++tmp
);
237 clientdbDump(StoreEntry
* sentry
)
245 storeAppendPrintf(sentry
, "Cache Clients:\n");
246 hash_first(client_table
);
248 while ((c
= (ClientInfo
*) hash_next(client_table
))) {
249 storeAppendPrintf(sentry
, "Address: %s\n", hashKeyStr(&c
->hash
));
250 storeAppendPrintf(sentry
, "Name: %s\n", fqdnFromAddr(c
->addr
));
251 storeAppendPrintf(sentry
, "Currently established connections: %d\n",
253 storeAppendPrintf(sentry
, " ICP Requests %d\n",
256 for (l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
257 if (c
->Icp
.result_hist
[l
] == 0)
260 icp_total
+= c
->Icp
.result_hist
[l
];
262 if (LOG_UDP_HIT
== l
)
263 icp_hits
+= c
->Icp
.result_hist
[l
];
265 storeAppendPrintf(sentry
,
266 " %-20.20s %7d %3d%%\n",
268 c
->Icp
.result_hist
[l
],
269 percent(c
->Icp
.result_hist
[l
], c
->Icp
.n_requests
));
272 storeAppendPrintf(sentry
, " HTTP Requests %d\n",
275 for (l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
276 if (c
->Http
.result_hist
[l
] == 0)
279 http_total
+= c
->Http
.result_hist
[l
];
281 if (logTypeIsATcpHit(l
))
282 http_hits
+= c
->Http
.result_hist
[l
];
284 storeAppendPrintf(sentry
,
285 " %-20.20s %7d %3d%%\n",
287 c
->Http
.result_hist
[l
],
288 percent(c
->Http
.result_hist
[l
], c
->Http
.n_requests
));
291 storeAppendPrintf(sentry
, "\n");
294 storeAppendPrintf(sentry
, "TOTALS\n");
295 storeAppendPrintf(sentry
, "ICP : %d Queries, %d Hits (%3d%%)\n",
296 icp_total
, icp_hits
, percent(icp_hits
, icp_total
));
297 storeAppendPrintf(sentry
, "HTTP: %d Requests, %d Hits (%3d%%)\n",
298 http_total
, http_hits
, percent(http_hits
, http_total
));
302 clientdbFreeItem(void *data
)
304 ClientInfo
*c
= (ClientInfo
*)data
;
305 safe_free(c
->hash
.key
);
306 memFree(c
, MEM_CLIENT_INFO
);
310 clientdbFreeMemory(void)
312 hashFreeItems(client_table
, clientdbFreeItem
);
313 hashFreeMemory(client_table
);
318 clientdbScheduledGC(void *unused
)
320 cleanup_scheduled
= 0;
325 clientdbGC(void *unused
)
327 static int bucket
= 0;
328 hash_link
*link_next
;
330 link_next
= hash_get_bucket(client_table
, bucket
++);
332 while (link_next
!= NULL
) {
333 ClientInfo
*c
= (ClientInfo
*)link_next
;
334 int age
= squid_curtime
- c
->last_seen
;
335 link_next
= link_next
->next
;
337 if (c
->n_established
)
340 if (age
< 24 * 3600 && c
->Http
.n_requests
> 100)
343 if (age
< 4 * 3600 && (c
->Http
.n_requests
> 10 || c
->Icp
.n_requests
> 10))
346 if (age
< 5 * 60 && (c
->Http
.n_requests
> 1 || c
->Icp
.n_requests
> 1))
352 hash_remove_link(client_table
, &c
->hash
);
356 statCounter
.client_http
.clients
--;
361 if (bucket
< CLIENT_DB_HASH_SIZE
)
362 eventAdd("client_db garbage collector", clientdbGC
, NULL
, 0.15, 0);
366 max_clients
= statCounter
.client_http
.clients
* 3 / 2;
368 if (!cleanup_scheduled
) {
369 cleanup_scheduled
= 1;
370 eventAdd("client_db garbage collector", clientdbScheduledGC
, NULL
, 6 * 3600, 0);
373 debugs(49, 2, "clientdbGC: Removed " << cleanup_removed
<< " entries");
378 clientdbStartGC(void)
380 max_clients
= statCounter
.client_http
.clients
;
389 client_entry(IPAddress
*current
)
391 ClientInfo
*c
= NULL
;
392 char key
[MAX_IPSTRLEN
];
396 current
->NtoA(key
,MAX_IPSTRLEN
);
397 hash_first(client_table
);
398 while ((c
= (ClientInfo
*) hash_next(client_table
))) {
399 if (!strcmp(key
, hashKeyStr(&c
->hash
)))
403 c
= (ClientInfo
*) hash_next(client_table
);
406 hash_first(client_table
);
407 c
= (ClientInfo
*) hash_next(client_table
);
410 hash_last(client_table
);
420 snmp_meshCtblFn(variable_list
* Var
, snint
* ErrP
)
422 variable_list
*Answer
= NULL
;
424 ClientInfo
*c
= NULL
;
428 *ErrP
= SNMP_ERR_NOERROR
;
429 debugs(49, 6, "snmp_meshCtblFn: Current : ");
430 snmpDebugOid(6, Var
->name
, Var
->name_length
);
431 /* FIXME INET6 : This must implement the key for IPv6 address */
432 snprintf(key
, sizeof(key
), "%d.%d.%d.%d", Var
->name
[LEN_SQ_NET
+ 3], Var
->name
[LEN_SQ_NET
+ 4],
433 Var
->name
[LEN_SQ_NET
+ 5], Var
->name
[LEN_SQ_NET
+ 6]);
434 debugs(49, 5, "snmp_meshCtblFn: [" << key
<< "] requested!");
435 c
= (ClientInfo
*) hash_lookup(client_table
, key
);
438 debugs(49, 5, "snmp_meshCtblFn: not found.");
439 *ErrP
= SNMP_ERR_NOSUCHNAME
;
443 switch (Var
->name
[LEN_SQ_NET
+ 2]) {
445 case MESH_CTBL_ADDR_TYPE
:
448 ival
= c
->addr
.IsIPv4() ? INETADDRESSTYPE_IPV4
: INETADDRESSTYPE_IPV6
;
449 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
456 Answer
= snmp_var_new(Var
->name
, Var
->name_length
);
457 // InetAddress doesn't have its own ASN.1 type,
458 // like IpAddr does (SMI_IPADDRESS)
460 Answer
->type
= ASN_OCTET_STR
;
461 char client
[MAX_IPSTRLEN
];
462 c
->addr
.NtoA(client
,MAX_IPSTRLEN
);
463 Answer
->val_len
= strlen(client
);
464 Answer
->val
.string
= (u_char
*) xstrdup(client
);
467 case MESH_CTBL_HTBYTES
:
468 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
469 (snint
) c
->Http
.kbytes_out
.kb
,
473 case MESH_CTBL_HTREQ
:
474 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
475 (snint
) c
->Http
.n_requests
,
479 case MESH_CTBL_HTHITS
:
482 for (l
= LOG_TAG_NONE
; l
< LOG_TYPE_MAX
; ++l
) {
483 if (logTypeIsATcpHit(l
))
484 aggr
+= c
->Http
.result_hist
[l
];
487 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
492 case MESH_CTBL_HTHITBYTES
:
493 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
494 (snint
) c
->Http
.hit_kbytes_out
.kb
,
498 case MESH_CTBL_ICPBYTES
:
499 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
500 (snint
) c
->Icp
.kbytes_out
.kb
,
504 case MESH_CTBL_ICPREQ
:
505 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
506 (snint
) c
->Icp
.n_requests
,
510 case MESH_CTBL_ICPHITS
:
511 aggr
= c
->Icp
.result_hist
[LOG_UDP_HIT
];
512 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
517 case MESH_CTBL_ICPHITBYTES
:
518 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
519 (snint
) c
->Icp
.hit_kbytes_out
.kb
,
524 *ErrP
= SNMP_ERR_NOSUCHNAME
;
525 debugs(49, 5, "snmp_meshCtblFn: illegal column.");
532 #endif /*SQUID_SNMP */