]> git.ipfire.org Git - thirdparty/squid.git/blob - src/client_db.cc
Merged from trunk.
[thirdparty/squid.git] / src / client_db.cc
1
2 /*
3 * $Id: client_db.cc,v 1.72 2007/12/14 23:11:46 amosjeffries Exp $
4 *
5 * DEBUG: section 0 Client Database
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
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.
19 *
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.
24 *
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.
29 *
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.
33 *
34 */
35
36 #include "squid.h"
37 #include "event.h"
38 #include "CacheManager.h"
39 #include "SquidTime.h"
40 #include "Store.h"
41
42
43 static hash_table *client_table = NULL;
44
45 static ClientInfo *clientdbAdd(const IPAddress &addr);
46 static FREE clientdbFreeItem;
47 static void clientdbStartGC(void);
48 static void clientdbScheduledGC(void *);
49
50 static int max_clients = 32;
51 static int cleanup_running = 0;
52 static int cleanup_scheduled = 0;
53 static int cleanup_removed;
54
55 #define CLIENT_DB_HASH_SIZE 467
56
57 static ClientInfo *
58
59 clientdbAdd(const IPAddress &addr)
60 {
61 ClientInfo *c;
62 char *buf = new char[MAX_IPSTRLEN];
63 c = (ClientInfo *)memAllocate(MEM_CLIENT_INFO);
64 c->hash.key = addr.NtoA(buf,MAX_IPSTRLEN);
65 c->addr = addr;
66 hash_join(client_table, &c->hash);
67 statCounter.client_http.clients++;
68
69 if ((statCounter.client_http.clients > max_clients) && !cleanup_running && cleanup_scheduled < 2)
70 {
71 cleanup_scheduled++;
72 eventAdd("client_db garbage collector", clientdbScheduledGC, NULL, 90, 0);
73 }
74
75 return c;
76 }
77
78 static void
79 clientdbRegisterWithCacheManager(void)
80 {
81 CacheManager::GetInstance()->
82 registerAction("client_list", "Cache Client List", clientdbDump, 0, 1);
83 }
84
85 void
86 clientdbInit(void)
87 {
88 clientdbRegisterWithCacheManager();
89
90 if (client_table)
91 return;
92
93 client_table = hash_create((HASHCMP *) strcmp, CLIENT_DB_HASH_SIZE, hash_string);
94
95 }
96
97 void
98 clientdbUpdate(const IPAddress &addr, log_type ltype, protocol_t p, size_t size)
99 {
100 char key[MAX_IPSTRLEN];
101 ClientInfo *c;
102
103 if (!Config.onoff.client_db)
104 return;
105
106 addr.NtoA(key,MAX_IPSTRLEN);
107
108 c = (ClientInfo *) hash_lookup(client_table, key);
109
110 if (c == NULL)
111 c = clientdbAdd(addr);
112
113 if (c == NULL)
114 debug_trap("clientdbUpdate: Failed to add entry");
115
116 if (p == PROTO_HTTP)
117 {
118 c->Http.n_requests++;
119 c->Http.result_hist[ltype]++;
120 kb_incr(&c->Http.kbytes_out, size);
121
122 if (logTypeIsATcpHit(ltype))
123 kb_incr(&c->Http.hit_kbytes_out, size);
124 } else if (p == PROTO_ICP)
125 {
126 c->Icp.n_requests++;
127 c->Icp.result_hist[ltype]++;
128 kb_incr(&c->Icp.kbytes_out, size);
129
130 if (LOG_UDP_HIT == ltype)
131 kb_incr(&c->Icp.hit_kbytes_out, size);
132 }
133
134 c->last_seen = squid_curtime;
135 }
136
137 /**
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.
142 */
143 int
144 clientdbEstablished(const IPAddress &addr, int delta)
145 {
146 char key[MAX_IPSTRLEN];
147 ClientInfo *c;
148
149 if (!Config.onoff.client_db)
150 return 0;
151
152 addr.NtoA(key,MAX_IPSTRLEN);
153
154 c = (ClientInfo *) hash_lookup(client_table, key);
155
156 if (c == NULL) {
157 c = clientdbAdd(addr);
158 }
159
160 if (c == NULL)
161 debug_trap("clientdbUpdate: Failed to add entry");
162
163 c->n_established += delta;
164
165 return c->n_established;
166 }
167
168 #define CUTOFF_SECONDS 3600
169 int
170
171 clientdbCutoffDenied(const IPAddress &addr)
172 {
173 char key[MAX_IPSTRLEN];
174 int NR;
175 int ND;
176 double p;
177 ClientInfo *c;
178
179 if (!Config.onoff.client_db)
180 return 0;
181
182 addr.NtoA(key,MAX_IPSTRLEN);
183
184 c = (ClientInfo *) hash_lookup(client_table, key);
185
186 if (c == NULL)
187 return 0;
188
189 /*
190 * If we are in a cutoff window, we don't send a reply
191 */
192 if (squid_curtime - c->cutoff.time < CUTOFF_SECONDS)
193 return 1;
194
195 /*
196 * Calculate the percent of DENIED replies since the last
197 * cutoff time.
198 */
199 NR = c->Icp.n_requests - c->cutoff.n_req;
200
201 if (NR < 150)
202 NR = 150;
203
204 ND = c->Icp.result_hist[LOG_UDP_DENIED] - c->cutoff.n_denied;
205
206 p = 100.0 * ND / NR;
207
208 if (p < 95.0)
209 return 0;
210
211 debugs(1, 0, "WARNING: Probable misconfigured neighbor at " << key);
212
213 debugs(1, 0, "WARNING: " << ND << " of the last " << NR <<
214 " ICP replies are DENIED");
215
216 debugs(1, 0, "WARNING: No replies will be sent for the next " <<
217 CUTOFF_SECONDS << " seconds");
218
219 c->cutoff.time = squid_curtime;
220
221 c->cutoff.n_req = c->Icp.n_requests;
222
223 c->cutoff.n_denied = c->Icp.result_hist[LOG_UDP_DENIED];
224
225 return 1;
226 }
227
228 log_type &operator++ (log_type &aLogType)
229 {
230 int tmp = (int)aLogType;
231 aLogType = (log_type)(++tmp);
232 return aLogType;
233 }
234
235
236 void
237 clientdbDump(StoreEntry * sentry)
238 {
239 ClientInfo *c;
240 log_type l;
241 int icp_total = 0;
242 int icp_hits = 0;
243 int http_total = 0;
244 int http_hits = 0;
245 storeAppendPrintf(sentry, "Cache Clients:\n");
246 hash_first(client_table);
247
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",
252 c->n_established);
253 storeAppendPrintf(sentry, " ICP Requests %d\n",
254 c->Icp.n_requests);
255
256 for (l = LOG_TAG_NONE; l < LOG_TYPE_MAX; ++l) {
257 if (c->Icp.result_hist[l] == 0)
258 continue;
259
260 icp_total += c->Icp.result_hist[l];
261
262 if (LOG_UDP_HIT == l)
263 icp_hits += c->Icp.result_hist[l];
264
265 storeAppendPrintf(sentry,
266 " %-20.20s %7d %3d%%\n",
267 log_tags[l],
268 c->Icp.result_hist[l],
269 percent(c->Icp.result_hist[l], c->Icp.n_requests));
270 }
271
272 storeAppendPrintf(sentry, " HTTP Requests %d\n",
273 c->Http.n_requests);
274
275 for (l = LOG_TAG_NONE; l < LOG_TYPE_MAX; ++l) {
276 if (c->Http.result_hist[l] == 0)
277 continue;
278
279 http_total += c->Http.result_hist[l];
280
281 if (logTypeIsATcpHit(l))
282 http_hits += c->Http.result_hist[l];
283
284 storeAppendPrintf(sentry,
285 " %-20.20s %7d %3d%%\n",
286 log_tags[l],
287 c->Http.result_hist[l],
288 percent(c->Http.result_hist[l], c->Http.n_requests));
289 }
290
291 storeAppendPrintf(sentry, "\n");
292 }
293
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));
299 }
300
301 static void
302 clientdbFreeItem(void *data)
303 {
304 ClientInfo *c = (ClientInfo *)data;
305 safe_free(c->hash.key);
306 memFree(c, MEM_CLIENT_INFO);
307 }
308
309 void
310 clientdbFreeMemory(void)
311 {
312 hashFreeItems(client_table, clientdbFreeItem);
313 hashFreeMemory(client_table);
314 client_table = NULL;
315 }
316
317 static void
318 clientdbScheduledGC(void *unused)
319 {
320 cleanup_scheduled = 0;
321 clientdbStartGC();
322 }
323
324 static void
325 clientdbGC(void *unused)
326 {
327 static int bucket = 0;
328 hash_link *link_next;
329
330 link_next = hash_get_bucket(client_table, bucket++);
331
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;
336
337 if (c->n_established)
338 continue;
339
340 if (age < 24 * 3600 && c->Http.n_requests > 100)
341 continue;
342
343 if (age < 4 * 3600 && (c->Http.n_requests > 10 || c->Icp.n_requests > 10))
344 continue;
345
346 if (age < 5 * 60 && (c->Http.n_requests > 1 || c->Icp.n_requests > 1))
347 continue;
348
349 if (age < 60)
350 continue;
351
352 hash_remove_link(client_table, &c->hash);
353
354 clientdbFreeItem(c);
355
356 statCounter.client_http.clients--;
357
358 cleanup_removed++;
359 }
360
361 if (bucket < CLIENT_DB_HASH_SIZE)
362 eventAdd("client_db garbage collector", clientdbGC, NULL, 0.15, 0);
363 else {
364 bucket = 0;
365 cleanup_running = 0;
366 max_clients = statCounter.client_http.clients * 3 / 2;
367
368 if (!cleanup_scheduled) {
369 cleanup_scheduled = 1;
370 eventAdd("client_db garbage collector", clientdbScheduledGC, NULL, 6 * 3600, 0);
371 }
372
373 debugs(49, 2, "clientdbGC: Removed " << cleanup_removed << " entries");
374 }
375 }
376
377 static void
378 clientdbStartGC(void)
379 {
380 max_clients = statCounter.client_http.clients;
381 cleanup_running = 1;
382 cleanup_removed = 0;
383 clientdbGC(NULL);
384 }
385
386 #if SQUID_SNMP
387
388 IPAddress *
389 client_entry(IPAddress *current)
390 {
391 ClientInfo *c = NULL;
392 char key[MAX_IPSTRLEN];
393
394 if (current)
395 {
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)))
400 break;
401 }
402
403 c = (ClientInfo *) hash_next(client_table);
404 } else
405 {
406 hash_first(client_table);
407 c = (ClientInfo *) hash_next(client_table);
408 }
409
410 hash_last(client_table);
411
412 if (c)
413 return (&c->addr);
414 else
415 return (NULL);
416
417 }
418
419 variable_list *
420 snmp_meshCtblFn(variable_list * Var, snint * ErrP)
421 {
422 variable_list *Answer = NULL;
423 static char key[16];
424 ClientInfo *c = NULL;
425 int aggr = 0;
426
427 log_type l;
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);
436
437 if (c == NULL) {
438 debugs(49, 5, "snmp_meshCtblFn: not found.");
439 *ErrP = SNMP_ERR_NOSUCHNAME;
440 return NULL;
441 }
442
443 switch (Var->name[LEN_SQ_NET + 2]) {
444
445 case MESH_CTBL_ADDR_TYPE:
446 {
447 int ival;
448 ival = c->addr.IsIPv4() ? INETADDRESSTYPE_IPV4 : INETADDRESSTYPE_IPV6 ;
449 Answer = snmp_var_new_integer(Var->name, Var->name_length,
450 ival, SMI_INTEGER);
451 }
452 break;
453
454 case MESH_CTBL_ADDR:
455 {
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)
459 // See: rfc4001.txt
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);
465 }
466 break;
467 case MESH_CTBL_HTBYTES:
468 Answer = snmp_var_new_integer(Var->name, Var->name_length,
469 (snint) c->Http.kbytes_out.kb,
470 SMI_COUNTER32);
471 break;
472
473 case MESH_CTBL_HTREQ:
474 Answer = snmp_var_new_integer(Var->name, Var->name_length,
475 (snint) c->Http.n_requests,
476 SMI_COUNTER32);
477 break;
478
479 case MESH_CTBL_HTHITS:
480 aggr = 0;
481
482 for (l = LOG_TAG_NONE; l < LOG_TYPE_MAX; ++l) {
483 if (logTypeIsATcpHit(l))
484 aggr += c->Http.result_hist[l];
485 }
486
487 Answer = snmp_var_new_integer(Var->name, Var->name_length,
488 (snint) aggr,
489 SMI_COUNTER32);
490 break;
491
492 case MESH_CTBL_HTHITBYTES:
493 Answer = snmp_var_new_integer(Var->name, Var->name_length,
494 (snint) c->Http.hit_kbytes_out.kb,
495 SMI_COUNTER32);
496 break;
497
498 case MESH_CTBL_ICPBYTES:
499 Answer = snmp_var_new_integer(Var->name, Var->name_length,
500 (snint) c->Icp.kbytes_out.kb,
501 SMI_COUNTER32);
502 break;
503
504 case MESH_CTBL_ICPREQ:
505 Answer = snmp_var_new_integer(Var->name, Var->name_length,
506 (snint) c->Icp.n_requests,
507 SMI_COUNTER32);
508 break;
509
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,
513 (snint) aggr,
514 SMI_COUNTER32);
515 break;
516
517 case MESH_CTBL_ICPHITBYTES:
518 Answer = snmp_var_new_integer(Var->name, Var->name_length,
519 (snint) c->Icp.hit_kbytes_out.kb,
520 SMI_COUNTER32);
521 break;
522
523 default:
524 *ErrP = SNMP_ERR_NOSUCHNAME;
525 debugs(49, 5, "snmp_meshCtblFn: illegal column.");
526 break;
527 }
528
529 return Answer;
530 }
531
532 #endif /*SQUID_SNMP */