]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_db.cc
Cleanup: zap CVS Id tags
[thirdparty/squid.git] / src / client_db.cc
CommitLineData
8eb58c9c 1
516350ca 2/*
262a0e14 3 * $Id$
516350ca 4 *
f43e2ec2 5 * DEBUG: section 0 Client Database
516350ca 6 * AUTHOR: Duane Wessels
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
516350ca 10 *
2b6662ba 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.
516350ca 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.
26ac0430 24 *
516350ca 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.
26ac0430 29 *
516350ca 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
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
516350ca 34 */
35
c41d4b6d 36#include "squid.h"
a553a5a3 37#include "event.h"
62ee09ca 38#include "CacheManager.h"
985c86bc 39#include "SquidTime.h"
e6ccf245 40#include "Store.h"
41
c41d4b6d 42
365e5b34 43static hash_table *client_table = NULL;
62e76326 44
ad61a2b4 45static ClientInfo *clientdbAdd(const IpAddress &addr);
ec878047 46static FREE clientdbFreeItem;
a0eba6bc 47static void clientdbStartGC(void);
48static void clientdbScheduledGC(void *);
49
50static int max_clients = 32;
51static int cleanup_running = 0;
52static int cleanup_scheduled = 0;
53static int cleanup_removed;
54
55#define CLIENT_DB_HASH_SIZE 467
c41d4b6d 56
57static ClientInfo *
62e76326 58
ad61a2b4 59clientdbAdd(const IpAddress &addr)
c41d4b6d 60{
61 ClientInfo *c;
cc192b50 62 char *buf = new char[MAX_IPSTRLEN];
e6ccf245 63 c = (ClientInfo *)memAllocate(MEM_CLIENT_INFO);
cc192b50 64 c->hash.key = addr.NtoA(buf,MAX_IPSTRLEN);
c41d4b6d 65 c->addr = addr;
186477c1 66 hash_join(client_table, &c->hash);
83704487 67 statCounter.client_http.clients++;
a0eba6bc 68
26ac0430 69 if ((statCounter.client_http.clients > max_clients) && !cleanup_running && cleanup_scheduled < 2) {
a0eba6bc 70 cleanup_scheduled++;
71 eventAdd("client_db garbage collector", clientdbScheduledGC, NULL, 90, 0);
72 }
73
c41d4b6d 74 return c;
75}
76
5f5e883f
FC
77static void
78clientdbRegisterWithCacheManager(void)
79{
80 CacheManager::GetInstance()->
26ac0430 81 registerAction("client_list", "Cache Client List", clientdbDump, 0, 1);
5f5e883f
FC
82}
83
c41d4b6d 84void
85clientdbInit(void)
86{
cfaf75a7
FC
87 clientdbRegisterWithCacheManager();
88
19054954 89 if (client_table)
62e76326 90 return;
91
30abd221 92 client_table = hash_create((HASHCMP *) strcmp, CLIENT_DB_HASH_SIZE, hash_string);
cfaf75a7 93
62ee09ca 94}
62e76326 95
62ee09ca 96void
ad61a2b4 97clientdbUpdate(const IpAddress &addr, log_type ltype, protocol_t p, size_t size)
c41d4b6d 98{
cc192b50 99 char key[MAX_IPSTRLEN];
429fdbec 100 ClientInfo *c;
62e76326 101
17a0a4ee 102 if (!Config.onoff.client_db)
62e76326 103 return;
104
cc192b50 105 addr.NtoA(key,MAX_IPSTRLEN);
62e76326 106
429fdbec 107 c = (ClientInfo *) hash_lookup(client_table, key);
62e76326 108
c41d4b6d 109 if (c == NULL)
62e76326 110 c = clientdbAdd(addr);
111
c41d4b6d 112 if (c == NULL)
62e76326 113 debug_trap("clientdbUpdate: Failed to add entry");
114
26ac0430 115 if (p == PROTO_HTTP) {
62e76326 116 c->Http.n_requests++;
117 c->Http.result_hist[ltype]++;
118 kb_incr(&c->Http.kbytes_out, size);
119
120 if (logTypeIsATcpHit(ltype))
121 kb_incr(&c->Http.hit_kbytes_out, size);
26ac0430 122 } else if (p == PROTO_ICP) {
62e76326 123 c->Icp.n_requests++;
124 c->Icp.result_hist[ltype]++;
125 kb_incr(&c->Icp.kbytes_out, size);
126
127 if (LOG_UDP_HIT == ltype)
128 kb_incr(&c->Icp.hit_kbytes_out, size);
81d0c856 129 }
a0eba6bc 130
131 c->last_seen = squid_curtime;
c41d4b6d 132}
133
077fe581 134/**
9bc73deb 135 * This function tracks the number of currently established connections
136 * for a client IP address. When a connection is accepted, call this
137 * with delta = 1. When the connection is closed, call with delta =
138 * -1. To get the current value, simply call with delta = 0.
139 */
140int
ad61a2b4 141clientdbEstablished(const IpAddress &addr, int delta)
9bc73deb 142{
cc192b50 143 char key[MAX_IPSTRLEN];
9bc73deb 144 ClientInfo *c;
62e76326 145
9bc73deb 146 if (!Config.onoff.client_db)
62e76326 147 return 0;
148
cc192b50 149 addr.NtoA(key,MAX_IPSTRLEN);
62e76326 150
9bc73deb 151 c = (ClientInfo *) hash_lookup(client_table, key);
62e76326 152
cc192b50 153 if (c == NULL) {
62e76326 154 c = clientdbAdd(addr);
cc192b50 155 }
62e76326 156
9bc73deb 157 if (c == NULL)
62e76326 158 debug_trap("clientdbUpdate: Failed to add entry");
159
9bc73deb 160 c->n_established += delta;
62e76326 161
9bc73deb 162 return c->n_established;
163}
164
e711a2ff 165#define CUTOFF_SECONDS 3600
c41d4b6d 166int
62e76326 167
ad61a2b4 168clientdbCutoffDenied(const IpAddress &addr)
c41d4b6d 169{
cc192b50 170 char key[MAX_IPSTRLEN];
e711a2ff 171 int NR;
172 int ND;
173 double p;
429fdbec 174 ClientInfo *c;
62e76326 175
59c4d35b 176 if (!Config.onoff.client_db)
62e76326 177 return 0;
178
cc192b50 179 addr.NtoA(key,MAX_IPSTRLEN);
62e76326 180
429fdbec 181 c = (ClientInfo *) hash_lookup(client_table, key);
62e76326 182
c41d4b6d 183 if (c == NULL)
62e76326 184 return 0;
185
e711a2ff 186 /*
187 * If we are in a cutoff window, we don't send a reply
188 */
189 if (squid_curtime - c->cutoff.time < CUTOFF_SECONDS)
62e76326 190 return 1;
191
e711a2ff 192 /*
193 * Calculate the percent of DENIED replies since the last
194 * cutoff time.
195 */
196 NR = c->Icp.n_requests - c->cutoff.n_req;
62e76326 197
e711a2ff 198 if (NR < 150)
62e76326 199 NR = 150;
200
e711a2ff 201 ND = c->Icp.result_hist[LOG_UDP_DENIED] - c->cutoff.n_denied;
62e76326 202
e711a2ff 203 p = 100.0 * ND / NR;
62e76326 204
e711a2ff 205 if (p < 95.0)
62e76326 206 return 0;
207
bf8fe701 208 debugs(1, 0, "WARNING: Probable misconfigured neighbor at " << key);
62e76326 209
bf8fe701 210 debugs(1, 0, "WARNING: " << ND << " of the last " << NR <<
211 " ICP replies are DENIED");
62e76326 212
bf8fe701 213 debugs(1, 0, "WARNING: No replies will be sent for the next " <<
214 CUTOFF_SECONDS << " seconds");
62e76326 215
e711a2ff 216 c->cutoff.time = squid_curtime;
62e76326 217
e711a2ff 218 c->cutoff.n_req = c->Icp.n_requests;
62e76326 219
e711a2ff 220 c->cutoff.n_denied = c->Icp.result_hist[LOG_UDP_DENIED];
62e76326 221
e711a2ff 222 return 1;
c41d4b6d 223}
224
e6ccf245 225log_type &operator++ (log_type &aLogType)
226{
1f1ae50a 227 int tmp = (int)aLogType;
228 aLogType = (log_type)(++tmp);
e6ccf245 229 return aLogType;
230}
231
8eb58c9c 232void
c41d4b6d 233clientdbDump(StoreEntry * sentry)
234{
c63e5f01 235 const char *name;
c41d4b6d 236 ClientInfo *c;
237 log_type l;
ac0963ac 238 int icp_total = 0;
239 int icp_hits = 0;
240 int http_total = 0;
241 int http_hits = 0;
15576b6a 242 storeAppendPrintf(sentry, "Cache Clients:\n");
0f6bebac 243 hash_first(client_table);
62e76326 244
0f6bebac 245 while ((c = (ClientInfo *) hash_next(client_table))) {
62e76326 246 storeAppendPrintf(sentry, "Address: %s\n", hashKeyStr(&c->hash));
c63e5f01
AJ
247 if( (name = fqdncache_gethostbyaddr(c->addr, 0)) ) {
248 storeAppendPrintf(sentry, "Name: %s\n", name);
249 }
62e76326 250 storeAppendPrintf(sentry, "Currently established connections: %d\n",
251 c->n_established);
077fe581 252 storeAppendPrintf(sentry, " ICP Requests %d\n",
62e76326 253 c->Icp.n_requests);
254
255 for (l = LOG_TAG_NONE; l < LOG_TYPE_MAX; ++l) {
256 if (c->Icp.result_hist[l] == 0)
257 continue;
258
259 icp_total += c->Icp.result_hist[l];
260
261 if (LOG_UDP_HIT == l)
262 icp_hits += c->Icp.result_hist[l];
263
264 storeAppendPrintf(sentry,
265 " %-20.20s %7d %3d%%\n",
266 log_tags[l],
267 c->Icp.result_hist[l],
268 percent(c->Icp.result_hist[l], c->Icp.n_requests));
269 }
270
271 storeAppendPrintf(sentry, " HTTP Requests %d\n",
272 c->Http.n_requests);
273
274 for (l = LOG_TAG_NONE; l < LOG_TYPE_MAX; ++l) {
275 if (c->Http.result_hist[l] == 0)
276 continue;
277
278 http_total += c->Http.result_hist[l];
279
280 if (logTypeIsATcpHit(l))
281 http_hits += c->Http.result_hist[l];
282
283 storeAppendPrintf(sentry,
284 " %-20.20s %7d %3d%%\n",
285 log_tags[l],
286 c->Http.result_hist[l],
287 percent(c->Http.result_hist[l], c->Http.n_requests));
288 }
289
290 storeAppendPrintf(sentry, "\n");
c41d4b6d 291 }
62e76326 292
ac0963ac 293 storeAppendPrintf(sentry, "TOTALS\n");
294 storeAppendPrintf(sentry, "ICP : %d Queries, %d Hits (%3d%%)\n",
62e76326 295 icp_total, icp_hits, percent(icp_hits, icp_total));
ac0963ac 296 storeAppendPrintf(sentry, "HTTP: %d Requests, %d Hits (%3d%%)\n",
62e76326 297 http_total, http_hits, percent(http_hits, http_total));
c41d4b6d 298}
83394596 299
ec878047 300static void
301clientdbFreeItem(void *data)
302{
e6ccf245 303 ClientInfo *c = (ClientInfo *)data;
186477c1 304 safe_free(c->hash.key);
db1cd23c 305 memFree(c, MEM_CLIENT_INFO);
ec878047 306}
307
83394596 308void
309clientdbFreeMemory(void)
310{
ec878047 311 hashFreeItems(client_table, clientdbFreeItem);
83394596 312 hashFreeMemory(client_table);
313 client_table = NULL;
314}
81d0c856 315
a0eba6bc 316static void
317clientdbScheduledGC(void *unused)
318{
319 cleanup_scheduled = 0;
320 clientdbStartGC();
321}
322
323static void
324clientdbGC(void *unused)
325{
326 static int bucket = 0;
327 hash_link *link_next;
328
329 link_next = hash_get_bucket(client_table, bucket++);
330
331 while (link_next != NULL) {
332 ClientInfo *c = (ClientInfo *)link_next;
333 int age = squid_curtime - c->last_seen;
334 link_next = link_next->next;
335
336 if (c->n_established)
337 continue;
338
339 if (age < 24 * 3600 && c->Http.n_requests > 100)
340 continue;
341
342 if (age < 4 * 3600 && (c->Http.n_requests > 10 || c->Icp.n_requests > 10))
343 continue;
344
345 if (age < 5 * 60 && (c->Http.n_requests > 1 || c->Icp.n_requests > 1))
346 continue;
347
348 if (age < 60)
349 continue;
350
351 hash_remove_link(client_table, &c->hash);
352
353 clientdbFreeItem(c);
354
355 statCounter.client_http.clients--;
356
357 cleanup_removed++;
358 }
359
360 if (bucket < CLIENT_DB_HASH_SIZE)
361 eventAdd("client_db garbage collector", clientdbGC, NULL, 0.15, 0);
362 else {
363 bucket = 0;
364 cleanup_running = 0;
365 max_clients = statCounter.client_http.clients * 3 / 2;
366
367 if (!cleanup_scheduled) {
368 cleanup_scheduled = 1;
369 eventAdd("client_db garbage collector", clientdbScheduledGC, NULL, 6 * 3600, 0);
370 }
371
bf8fe701 372 debugs(49, 2, "clientdbGC: Removed " << cleanup_removed << " entries");
a0eba6bc 373 }
374}
375
376static void
377clientdbStartGC(void)
378{
379 max_clients = statCounter.client_http.clients;
380 cleanup_running = 1;
381 cleanup_removed = 0;
382 clientdbGC(NULL);
383}
384
81d0c856 385#if SQUID_SNMP
62e76326 386
ad61a2b4
AJ
387IpAddress *
388client_entry(IpAddress *current)
81d0c856 389{
6f47fbc7 390 ClientInfo *c = NULL;
cc192b50 391 char key[MAX_IPSTRLEN];
62e76326 392
26ac0430 393 if (current) {
cc192b50 394 current->NtoA(key,MAX_IPSTRLEN);
62e76326 395 hash_first(client_table);
62e76326 396 while ((c = (ClientInfo *) hash_next(client_table))) {
26ac0430 397 if (!strcmp(key, hashKeyStr(&c->hash)))
62e76326 398 break;
399 }
26ac0430
AJ
400
401 c = (ClientInfo *) hash_next(client_table);
402 } else {
403 hash_first(client_table);
62e76326 404 c = (ClientInfo *) hash_next(client_table);
6f47fbc7 405 }
62e76326 406
b6a2f15e 407 hash_last(client_table);
62e76326 408
b6a2f15e 409 if (c)
26ac0430 410 return (&c->addr);
b6a2f15e 411 else
26ac0430
AJ
412 return (NULL);
413
b6a2f15e 414}
81d0c856 415
416variable_list *
6f47fbc7 417snmp_meshCtblFn(variable_list * Var, snint * ErrP)
81d0c856 418{
736eb6ad 419 variable_list *Answer = NULL;
6a702e8d 420 static char key[16];
81d0c856 421 ClientInfo *c = NULL;
6f47fbc7 422 int aggr = 0;
cc192b50 423
cc4621e5 424 log_type l;
81d0c856 425 *ErrP = SNMP_ERR_NOERROR;
bf8fe701 426 debugs(49, 6, "snmp_meshCtblFn: Current : ");
b6a2f15e 427 snmpDebugOid(6, Var->name, Var->name_length);
cc192b50 428 /* FIXME INET6 : This must implement the key for IPv6 address */
1388e4a7 429 snprintf(key, sizeof(key), "%d.%d.%d.%d", Var->name[LEN_SQ_NET + 3], Var->name[LEN_SQ_NET + 4],
62e76326 430 Var->name[LEN_SQ_NET + 5], Var->name[LEN_SQ_NET + 6]);
bf8fe701 431 debugs(49, 5, "snmp_meshCtblFn: [" << key << "] requested!");
81d0c856 432 c = (ClientInfo *) hash_lookup(client_table, key);
62e76326 433
81d0c856 434 if (c == NULL) {
bf8fe701 435 debugs(49, 5, "snmp_meshCtblFn: not found.");
62e76326 436 *ErrP = SNMP_ERR_NOSUCHNAME;
437 return NULL;
81d0c856 438 }
62e76326 439
135171fe 440 switch (Var->name[LEN_SQ_NET + 2]) {
62e76326 441
26ac0430
AJ
442 case MESH_CTBL_ADDR_TYPE: {
443 int ival;
444 ival = c->addr.IsIPv4() ? INETADDRESSTYPE_IPV4 : INETADDRESSTYPE_IPV6 ;
445 Answer = snmp_var_new_integer(Var->name, Var->name_length,
446 ival, SMI_INTEGER);
447 }
448 break;
449
450 case MESH_CTBL_ADDR: {
451 Answer = snmp_var_new(Var->name, Var->name_length);
452 // InetAddress doesn't have its own ASN.1 type,
453 // like IpAddr does (SMI_IPADDRESS)
454 // See: rfc4001.txt
455 Answer->type = ASN_OCTET_STR;
456 char client[MAX_IPSTRLEN];
457 c->addr.NtoA(client,MAX_IPSTRLEN);
458 Answer->val_len = strlen(client);
459 Answer->val.string = (u_char *) xstrdup(client);
460 }
461 break;
81d0c856 462 case MESH_CTBL_HTBYTES:
62e76326 463 Answer = snmp_var_new_integer(Var->name, Var->name_length,
464 (snint) c->Http.kbytes_out.kb,
465 SMI_COUNTER32);
466 break;
467
81d0c856 468 case MESH_CTBL_HTREQ:
62e76326 469 Answer = snmp_var_new_integer(Var->name, Var->name_length,
470 (snint) c->Http.n_requests,
471 SMI_COUNTER32);
472 break;
473
81d0c856 474 case MESH_CTBL_HTHITS:
62e76326 475 aggr = 0;
476
477 for (l = LOG_TAG_NONE; l < LOG_TYPE_MAX; ++l) {
478 if (logTypeIsATcpHit(l))
479 aggr += c->Http.result_hist[l];
480 }
481
482 Answer = snmp_var_new_integer(Var->name, Var->name_length,
483 (snint) aggr,
484 SMI_COUNTER32);
485 break;
486
81d0c856 487 case MESH_CTBL_HTHITBYTES:
62e76326 488 Answer = snmp_var_new_integer(Var->name, Var->name_length,
489 (snint) c->Http.hit_kbytes_out.kb,
490 SMI_COUNTER32);
491 break;
492
81d0c856 493 case MESH_CTBL_ICPBYTES:
62e76326 494 Answer = snmp_var_new_integer(Var->name, Var->name_length,
495 (snint) c->Icp.kbytes_out.kb,
496 SMI_COUNTER32);
497 break;
498
81d0c856 499 case MESH_CTBL_ICPREQ:
62e76326 500 Answer = snmp_var_new_integer(Var->name, Var->name_length,
501 (snint) c->Icp.n_requests,
502 SMI_COUNTER32);
503 break;
504
81d0c856 505 case MESH_CTBL_ICPHITS:
62e76326 506 aggr = c->Icp.result_hist[LOG_UDP_HIT];
507 Answer = snmp_var_new_integer(Var->name, Var->name_length,
508 (snint) aggr,
509 SMI_COUNTER32);
510 break;
511
81d0c856 512 case MESH_CTBL_ICPHITBYTES:
62e76326 513 Answer = snmp_var_new_integer(Var->name, Var->name_length,
514 (snint) c->Icp.hit_kbytes_out.kb,
515 SMI_COUNTER32);
516 break;
517
81d0c856 518 default:
62e76326 519 *ErrP = SNMP_ERR_NOSUCHNAME;
bf8fe701 520 debugs(49, 5, "snmp_meshCtblFn: illegal column.");
62e76326 521 break;
81d0c856 522 }
62e76326 523
81d0c856 524 return Answer;
525}
526
135171fe 527#endif /*SQUID_SNMP */