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