]> git.ipfire.org Git - thirdparty/squid.git/blob - src/peer_select.cc
20b3688542221946919bbddb1b1f1d3705842236
[thirdparty/squid.git] / src / peer_select.cc
1 /*
2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /* DEBUG: section 44 Peer Selection Algorithm */
10
11 #include "squid.h"
12 #include "acl/FilledChecklist.h"
13 #include "CachePeer.h"
14 #include "carp.h"
15 #include "client_side.h"
16 #include "dns/LookupDetails.h"
17 #include "errorpage.h"
18 #include "event.h"
19 #include "FwdState.h"
20 #include "globals.h"
21 #include "hier_code.h"
22 #include "htcp.h"
23 #include "HttpRequest.h"
24 #include "icmp/net_db.h"
25 #include "ICP.h"
26 #include "ip/tools.h"
27 #include "ipcache.h"
28 #include "neighbors.h"
29 #include "peer_sourcehash.h"
30 #include "peer_userhash.h"
31 #include "PeerSelectState.h"
32 #include "SquidConfig.h"
33 #include "SquidTime.h"
34 #include "Store.h"
35 #include "URL.h"
36
37 static struct {
38 int timeouts;
39 } PeerStats;
40
41 static const char *DirectStr[] = {
42 "DIRECT_UNKNOWN",
43 "DIRECT_NO",
44 "DIRECT_MAYBE",
45 "DIRECT_YES"
46 };
47
48 static void peerSelectFoo(ps_state *);
49 static void peerPingTimeout(void *data);
50 static IRCB peerHandlePingReply;
51 static void peerIcpParentMiss(CachePeer *, icp_common_t *, ps_state *);
52 #if USE_HTCP
53 static void peerHtcpParentMiss(CachePeer *, HtcpReplyData *, ps_state *);
54 static void peerHandleHtcpReply(CachePeer *, peer_t, HtcpReplyData *, void *);
55 #endif
56 static int peerCheckNetdbDirect(ps_state * psstate);
57 static void peerGetSomeNeighbor(ps_state *);
58 static void peerGetSomeNeighborReplies(ps_state *);
59 static void peerGetSomeDirect(ps_state *);
60 static void peerGetSomeParent(ps_state *);
61 static void peerGetAllParents(ps_state *);
62 static void peerAddFwdServer(FwdServer **, CachePeer *, hier_code);
63 static void peerSelectPinned(ps_state * ps);
64 static void peerSelectDnsResults(const ipcache_addrs *ia, const Dns::LookupDetails &details, void *data);
65
66 CBDATA_CLASS_INIT(ps_state);
67
68 ps_state::~ps_state()
69 {
70 while (servers) {
71 FwdServer *next = servers->next;
72 cbdataReferenceDone(servers->_peer);
73 memFree(servers, MEM_FWD_SERVER);
74 servers = next;
75 }
76
77 if (entry) {
78 debugs(44, 3, entry->url());
79
80 if (entry->ping_status == PING_WAITING)
81 eventDelete(peerPingTimeout, this);
82
83 entry->ping_status = PING_DONE;
84 }
85
86 if (acl_checklist) {
87 debugs(44, DBG_IMPORTANT, "calling aclChecklistFree() from ps_state destructor");
88 delete acl_checklist;
89 }
90
91 HTTPMSGUNLOCK(request);
92
93 if (entry) {
94 assert(entry->ping_status != PING_WAITING);
95 entry->unlock("peerSelect");
96 entry = NULL;
97 }
98
99 delete lastError;
100 }
101
102 static int
103 peerSelectIcpPing(HttpRequest * request, int direct, StoreEntry * entry)
104 {
105 int n;
106 assert(entry);
107 assert(entry->ping_status == PING_NONE);
108 assert(direct != DIRECT_YES);
109 debugs(44, 3, "peerSelectIcpPing: " << entry->url());
110
111 if (!request->flags.hierarchical && direct != DIRECT_NO)
112 return 0;
113
114 if (EBIT_TEST(entry->flags, KEY_PRIVATE) && !neighbors_do_private_keys)
115 if (direct != DIRECT_NO)
116 return 0;
117
118 n = neighborsCount(request);
119
120 debugs(44, 3, "peerSelectIcpPing: counted " << n << " neighbors");
121
122 return n;
123 }
124
125 void
126 peerSelect(Comm::ConnectionList * paths,
127 HttpRequest * request,
128 AccessLogEntry::Pointer const &al,
129 StoreEntry * entry,
130 PSC * callback,
131 void *callback_data)
132 {
133 ps_state *psstate;
134
135 if (entry)
136 debugs(44, 3, *entry << ' ' << entry->url());
137 else
138 debugs(44, 3, request->method);
139
140 psstate = new ps_state;
141
142 psstate->request = request;
143 HTTPMSGLOCK(psstate->request);
144 psstate->al = al;
145
146 psstate->entry = entry;
147 psstate->paths = paths;
148
149 psstate->callback = callback;
150
151 psstate->callback_data = cbdataReference(callback_data);
152
153 #if USE_CACHE_DIGESTS
154
155 request->hier.peer_select_start = current_time;
156
157 #endif
158
159 if (psstate->entry)
160 psstate->entry->lock("peerSelect");
161
162 peerSelectFoo(psstate);
163 }
164
165 static void
166 peerCheckNeverDirectDone(allow_t answer, void *data)
167 {
168 ps_state *psstate = (ps_state *) data;
169 psstate->acl_checklist = NULL;
170 debugs(44, 3, "peerCheckNeverDirectDone: " << answer);
171 psstate->never_direct = answer;
172 switch (answer) {
173 case ACCESS_ALLOWED:
174 /** if never_direct says YES, do that. */
175 psstate->direct = DIRECT_NO;
176 debugs(44, 3, HERE << "direct = " << DirectStr[psstate->direct] << " (never_direct allow)");
177 break;
178 case ACCESS_DENIED: // not relevant.
179 case ACCESS_DUNNO: // not relevant.
180 break;
181 case ACCESS_AUTH_REQUIRED:
182 debugs(44, DBG_IMPORTANT, "WARNING: never_direct resulted in " << answer << ". Username ACLs are not reliable here.");
183 break;
184 }
185 peerSelectFoo(psstate);
186 }
187
188 static void
189 peerCheckAlwaysDirectDone(allow_t answer, void *data)
190 {
191 ps_state *psstate = (ps_state *)data;
192 psstate->acl_checklist = NULL;
193 debugs(44, 3, "peerCheckAlwaysDirectDone: " << answer);
194 psstate->always_direct = answer;
195 switch (answer) {
196 case ACCESS_ALLOWED:
197 /** if always_direct says YES, do that. */
198 psstate->direct = DIRECT_YES;
199 debugs(44, 3, HERE << "direct = " << DirectStr[psstate->direct] << " (always_direct allow)");
200 break;
201 case ACCESS_DENIED: // not relevant.
202 case ACCESS_DUNNO: // not relevant.
203 break;
204 case ACCESS_AUTH_REQUIRED:
205 debugs(44, DBG_IMPORTANT, "WARNING: always_direct resulted in " << answer << ". Username ACLs are not reliable here.");
206 break;
207 }
208 peerSelectFoo(psstate);
209 }
210
211 void
212 peerSelectDnsPaths(ps_state *psstate)
213 {
214 FwdServer *fs = psstate->servers;
215
216 if (!cbdataReferenceValid(psstate->callback_data)) {
217 debugs(44, 3, "Aborting peer selection. Parent Job went away.");
218 delete psstate;
219 return;
220 }
221
222 // Bug 3243: CVE 2009-0801
223 // Bypass of browser same-origin access control in intercepted communication
224 // To resolve this we must use only the original client destination when going DIRECT
225 // on intercepted traffic which failed Host verification
226 const HttpRequest *req = psstate->request;
227 const bool isIntercepted = !req->flags.redirected &&
228 (req->flags.intercepted || req->flags.interceptTproxy);
229 const bool useOriginalDst = Config.onoff.client_dst_passthru || !req->flags.hostVerified;
230 const bool choseDirect = fs && fs->code == HIER_DIRECT;
231 if (isIntercepted && useOriginalDst && choseDirect) {
232 // check the client is still around before using any of its details
233 if (req->clientConnectionManager.valid()) {
234 // construct a "result" adding the ORIGINAL_DST to the set instead of DIRECT
235 Comm::ConnectionPointer p = new Comm::Connection();
236 p->remote = req->clientConnectionManager->clientConnection->local;
237 p->peerType = ORIGINAL_DST; // fs->code is DIRECT. This fixes the display.
238 p->setPeer(fs->_peer);
239
240 // check for a configured outgoing address for this destination...
241 getOutgoingAddress(psstate->request, p);
242 psstate->paths->push_back(p);
243 }
244
245 // clear the used fs and continue
246 psstate->servers = fs->next;
247 cbdataReferenceDone(fs->_peer);
248 memFree(fs, MEM_FWD_SERVER);
249 peerSelectDnsPaths(psstate);
250 return;
251 }
252
253 // convert the list of FwdServer destinations into destinations IP addresses
254 if (fs && psstate->paths->size() < (unsigned int)Config.forward_max_tries) {
255 // send the next one off for DNS lookup.
256 const char *host = fs->_peer ? fs->_peer->host : psstate->request->url.host();
257 debugs(44, 2, "Find IP destination for: " << psstate->url() << "' via " << host);
258 ipcache_nbgethostbyname(host, peerSelectDnsResults, psstate);
259 return;
260 }
261
262 // Bug 3605: clear any extra listed FwdServer destinations, when the options exceeds max_foward_tries.
263 // due to the allocation method of fs, we must deallocate each manually.
264 // TODO: use a std::list so we can get the size and abort adding whenever the selection loops reach Config.forward_max_tries
265 if (fs && psstate->paths->size() >= (unsigned int)Config.forward_max_tries) {
266 assert(fs == psstate->servers);
267 while (fs) {
268 psstate->servers = fs->next;
269 cbdataReferenceDone(fs->_peer);
270 memFree(fs, MEM_FWD_SERVER);
271 fs = psstate->servers;
272 }
273 }
274
275 // done with DNS lookups. pass back to caller
276 PSC *callback = psstate->callback;
277 psstate->callback = NULL;
278
279 debugs(44, 2, (psstate->paths->size()<1?"Failed to select source":"Found sources") << " for '" << psstate->url() << "'");
280 debugs(44, 2, " always_direct = " << psstate->always_direct);
281 debugs(44, 2, " never_direct = " << psstate->never_direct);
282 if (psstate->paths) {
283 for (size_t i = 0; i < psstate->paths->size(); ++i) {
284 if ((*psstate->paths)[i]->peerType == HIER_DIRECT)
285 debugs(44, 2, " DIRECT = " << (*psstate->paths)[i]);
286 else if ((*psstate->paths)[i]->peerType == ORIGINAL_DST)
287 debugs(44, 2, " ORIGINAL_DST = " << (*psstate->paths)[i]);
288 else if ((*psstate->paths)[i]->peerType == PINNED)
289 debugs(44, 2, " PINNED = " << (*psstate->paths)[i]);
290 else
291 debugs(44, 2, " cache_peer = " << (*psstate->paths)[i]);
292 }
293 }
294 debugs(44, 2, " timedout = " << psstate->ping.timedout);
295
296 psstate->ping.stop = current_time;
297 psstate->request->hier.ping = psstate->ping;
298
299 void *cbdata;
300 if (cbdataReferenceValidDone(psstate->callback_data, &cbdata)) {
301 callback(psstate->paths, psstate->lastError, cbdata);
302 psstate->lastError = NULL; // FwdState has taken control over the ErrorState object.
303 }
304
305 delete psstate;
306 }
307
308 static void
309 peerSelectDnsResults(const ipcache_addrs *ia, const Dns::LookupDetails &details, void *data)
310 {
311 ps_state *psstate = (ps_state *)data;
312
313 if (!cbdataReferenceValid(psstate->callback_data)) {
314 debugs(44, 3, "Aborting peer selection. Parent Job went away.");
315 delete psstate;
316 return;
317 }
318
319 psstate->request->recordLookup(details);
320
321 FwdServer *fs = psstate->servers;
322 if (ia != NULL) {
323
324 assert(ia->cur < ia->count);
325
326 // loop over each result address, adding to the possible destinations.
327 int ip = ia->cur;
328 for (int n = 0; n < ia->count; ++n, ++ip) {
329 Comm::ConnectionPointer p;
330
331 if (ip >= ia->count) ip = 0; // looped back to zero.
332
333 // Enforce forward_max_tries configuration.
334 if (psstate->paths->size() >= (unsigned int)Config.forward_max_tries)
335 break;
336
337 // for TPROXY spoofing we must skip unusable addresses.
338 if (psstate->request->flags.spoofClientIp && !(fs->_peer && fs->_peer->options.no_tproxy) ) {
339 if (ia->in_addrs[ip].isIPv4() != psstate->request->client_addr.isIPv4()) {
340 // we CAN'T spoof the address on this link. find another.
341 continue;
342 }
343 }
344
345 p = new Comm::Connection();
346 p->remote = ia->in_addrs[ip];
347
348 // when IPv6 is disabled we cannot use it
349 if (!Ip::EnableIpv6 && p->remote.isIPv6()) {
350 const char *host = (fs->_peer ? fs->_peer->host : psstate->request->url.host());
351 ipcacheMarkBadAddr(host, p->remote);
352 continue;
353 }
354
355 p->remote.port(fs->_peer ? fs->_peer->http_port : psstate->request->url.port());
356 p->peerType = fs->code;
357 p->setPeer(fs->_peer);
358
359 // check for a configured outgoing address for this destination...
360 getOutgoingAddress(psstate->request, p);
361 psstate->paths->push_back(p);
362 }
363 } else {
364 debugs(44, 3, "Unknown host: " << (fs->_peer ? fs->_peer->host : psstate->request->url.host()));
365 // discard any previous error.
366 delete psstate->lastError;
367 psstate->lastError = NULL;
368 if (fs->code == HIER_DIRECT) {
369 psstate->lastError = new ErrorState(ERR_DNS_FAIL, Http::scServiceUnavailable, psstate->request);
370 psstate->lastError->dnsError = details.error;
371 }
372 }
373
374 psstate->servers = fs->next;
375 cbdataReferenceDone(fs->_peer);
376 memFree(fs, MEM_FWD_SERVER);
377
378 // see if more paths can be found
379 peerSelectDnsPaths(psstate);
380 }
381
382 static int
383 peerCheckNetdbDirect(ps_state * psstate)
384 {
385 #if USE_ICMP
386 CachePeer *p;
387 int myrtt;
388 int myhops;
389
390 if (psstate->direct == DIRECT_NO)
391 return 0;
392
393 /* base lookup on RTT and Hops if ICMP NetDB is enabled. */
394
395 myrtt = netdbHostRtt(psstate->request->url.host());
396 debugs(44, 3, "MY RTT = " << myrtt << " msec");
397 debugs(44, 3, "minimum_direct_rtt = " << Config.minDirectRtt << " msec");
398
399 if (myrtt && myrtt <= Config.minDirectRtt)
400 return 1;
401
402 myhops = netdbHostHops(psstate->request->url.host());
403
404 debugs(44, 3, "peerCheckNetdbDirect: MY hops = " << myhops);
405 debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_hops = " << Config.minDirectHops);
406
407 if (myhops && myhops <= Config.minDirectHops)
408 return 1;
409
410 p = whichPeer(psstate->closest_parent_miss);
411
412 if (p == NULL)
413 return 0;
414
415 debugs(44, 3, "peerCheckNetdbDirect: closest_parent_miss RTT = " << psstate->ping.p_rtt << " msec");
416
417 if (myrtt && myrtt <= psstate->ping.p_rtt)
418 return 1;
419
420 #endif /* USE_ICMP */
421
422 return 0;
423 }
424
425 static void
426 peerSelectFoo(ps_state * ps)
427 {
428 if (!cbdataReferenceValid(ps->callback_data)) {
429 debugs(44, 3, "Aborting peer selection. Parent Job went away.");
430 delete ps;
431 return;
432 }
433
434 StoreEntry *entry = ps->entry;
435 HttpRequest *request = ps->request;
436 debugs(44, 3, request->method << ' ' << request->url.host());
437
438 /** If we don't know whether DIRECT is permitted ... */
439 if (ps->direct == DIRECT_UNKNOWN) {
440 if (ps->always_direct == ACCESS_DUNNO) {
441 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (always_direct to be checked)");
442 /** check always_direct; */
443 ACLFilledChecklist *ch = new ACLFilledChecklist(Config.accessList.AlwaysDirect, request, NULL);
444 ch->al = ps->al;
445 ps->acl_checklist = ch;
446 ps->acl_checklist->nonBlockingCheck(peerCheckAlwaysDirectDone, ps);
447 return;
448 } else if (ps->never_direct == ACCESS_DUNNO) {
449 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (never_direct to be checked)");
450 /** check never_direct; */
451 ACLFilledChecklist *ch = new ACLFilledChecklist(Config.accessList.NeverDirect, request, NULL);
452 ch->al = ps->al;
453 ps->acl_checklist = ch;
454 ps->acl_checklist->nonBlockingCheck(peerCheckNeverDirectDone, ps);
455 return;
456 } else if (request->flags.noDirect) {
457 /** if we are accelerating, direct is not an option. */
458 ps->direct = DIRECT_NO;
459 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (forced non-direct)");
460 } else if (request->flags.loopDetected) {
461 /** if we are in a forwarding-loop, direct is not an option. */
462 ps->direct = DIRECT_YES;
463 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (forwarding loop detected)");
464 } else if (peerCheckNetdbDirect(ps)) {
465 ps->direct = DIRECT_YES;
466 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (checkNetdbDirect)");
467 } else {
468 ps->direct = DIRECT_MAYBE;
469 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (default)");
470 }
471
472 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct]);
473 }
474
475 if (!entry || entry->ping_status == PING_NONE)
476 peerSelectPinned(ps);
477 if (entry == NULL) {
478 (void) 0;
479 } else if (entry->ping_status == PING_NONE) {
480 peerGetSomeNeighbor(ps);
481
482 if (entry->ping_status == PING_WAITING)
483 return;
484 } else if (entry->ping_status == PING_WAITING) {
485 peerGetSomeNeighborReplies(ps);
486 entry->ping_status = PING_DONE;
487 }
488
489 switch (ps->direct) {
490
491 case DIRECT_YES:
492 peerGetSomeDirect(ps);
493 break;
494
495 case DIRECT_NO:
496 peerGetSomeParent(ps);
497 peerGetAllParents(ps);
498 break;
499
500 default:
501
502 if (Config.onoff.prefer_direct)
503 peerGetSomeDirect(ps);
504
505 if (request->flags.hierarchical || !Config.onoff.nonhierarchical_direct) {
506 peerGetSomeParent(ps);
507 peerGetAllParents(ps);
508 }
509
510 if (!Config.onoff.prefer_direct)
511 peerGetSomeDirect(ps);
512
513 break;
514 }
515
516 // resolve the possible peers
517 peerSelectDnsPaths(ps);
518 }
519
520 bool peerAllowedToUse(const CachePeer * p, HttpRequest * request);
521
522 /**
523 * peerSelectPinned
524 *
525 * Selects a pinned connection.
526 */
527 static void
528 peerSelectPinned(ps_state * ps)
529 {
530 HttpRequest *request = ps->request;
531 if (!request->pinnedConnection())
532 return;
533 CachePeer *pear = request->pinnedConnection()->pinnedPeer();
534 if (Comm::IsConnOpen(request->pinnedConnection()->validatePinnedConnection(request, pear))) {
535 if (pear && peerAllowedToUse(pear, request)) {
536 peerAddFwdServer(&ps->servers, pear, PINNED);
537 if (ps->entry)
538 ps->entry->ping_status = PING_DONE; /* Skip ICP */
539 } else if (!pear && ps->direct != DIRECT_NO) {
540 peerAddFwdServer(&ps->servers, NULL, PINNED);
541 if (ps->entry)
542 ps->entry->ping_status = PING_DONE; /* Skip ICP */
543 }
544 }
545 }
546
547 /**
548 * peerGetSomeNeighbor
549 *
550 * Selects a neighbor (parent or sibling) based on one of the
551 * following methods:
552 * Cache Digests
553 * CARP
554 * ICMP Netdb RTT estimates
555 * ICP/HTCP queries
556 */
557 static void
558 peerGetSomeNeighbor(ps_state * ps)
559 {
560 StoreEntry *entry = ps->entry;
561 HttpRequest *request = ps->request;
562 CachePeer *p;
563 hier_code code = HIER_NONE;
564 assert(entry->ping_status == PING_NONE);
565
566 if (ps->direct == DIRECT_YES) {
567 entry->ping_status = PING_DONE;
568 return;
569 }
570
571 #if USE_CACHE_DIGESTS
572 if ((p = neighborsDigestSelect(request))) {
573 if (neighborType(p, request->url) == PEER_PARENT)
574 code = CD_PARENT_HIT;
575 else
576 code = CD_SIBLING_HIT;
577 } else
578 #endif
579 if ((p = netdbClosestParent(request))) {
580 code = CLOSEST_PARENT;
581 } else if (peerSelectIcpPing(request, ps->direct, entry)) {
582 debugs(44, 3, "peerSelect: Doing ICP pings");
583 ps->ping.start = current_time;
584 ps->ping.n_sent = neighborsUdpPing(request,
585 entry,
586 peerHandlePingReply,
587 ps,
588 &ps->ping.n_replies_expected,
589 &ps->ping.timeout);
590
591 if (ps->ping.n_sent == 0)
592 debugs(44, DBG_CRITICAL, "WARNING: neighborsUdpPing returned 0");
593 debugs(44, 3, "peerSelect: " << ps->ping.n_replies_expected <<
594 " ICP replies expected, RTT " << ps->ping.timeout <<
595 " msec");
596
597 if (ps->ping.n_replies_expected > 0) {
598 entry->ping_status = PING_WAITING;
599 eventAdd("peerPingTimeout",
600 peerPingTimeout,
601 ps,
602 0.001 * ps->ping.timeout,
603 0);
604 return;
605 }
606 }
607
608 if (code != HIER_NONE) {
609 assert(p);
610 debugs(44, 3, "peerSelect: " << hier_code_str[code] << "/" << p->host);
611 peerAddFwdServer(&ps->servers, p, code);
612 }
613
614 entry->ping_status = PING_DONE;
615 }
616
617 /*
618 * peerGetSomeNeighborReplies
619 *
620 * Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
621 */
622 static void
623 peerGetSomeNeighborReplies(ps_state * ps)
624 {
625 HttpRequest *request = ps->request;
626 CachePeer *p = NULL;
627 hier_code code = HIER_NONE;
628 assert(ps->entry->ping_status == PING_WAITING);
629 assert(ps->direct != DIRECT_YES);
630
631 if (peerCheckNetdbDirect(ps)) {
632 code = CLOSEST_DIRECT;
633 debugs(44, 3, hier_code_str[code] << "/" << request->url.host());
634 peerAddFwdServer(&ps->servers, NULL, code);
635 return;
636 }
637
638 if ((p = ps->hit)) {
639 code = ps->hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT;
640 } else {
641 if (!ps->closest_parent_miss.isAnyAddr()) {
642 p = whichPeer(ps->closest_parent_miss);
643 code = CLOSEST_PARENT_MISS;
644 } else if (!ps->first_parent_miss.isAnyAddr()) {
645 p = whichPeer(ps->first_parent_miss);
646 code = FIRST_PARENT_MISS;
647 }
648 }
649 if (p && code != HIER_NONE) {
650 debugs(44, 3, hier_code_str[code] << "/" << p->host);
651 peerAddFwdServer(&ps->servers, p, code);
652 }
653 }
654
655 /*
656 * peerGetSomeDirect
657 *
658 * Simply adds a 'direct' entry to the FwdServers list if this
659 * request can be forwarded directly to the origin server
660 */
661 static void
662 peerGetSomeDirect(ps_state * ps)
663 {
664 if (ps->direct == DIRECT_NO)
665 return;
666
667 /* WAIS is not implemented natively */
668 if (ps->request->url.getScheme() == AnyP::PROTO_WAIS)
669 return;
670
671 peerAddFwdServer(&ps->servers, NULL, HIER_DIRECT);
672 }
673
674 static void
675 peerGetSomeParent(ps_state * ps)
676 {
677 CachePeer *p;
678 HttpRequest *request = ps->request;
679 hier_code code = HIER_NONE;
680 debugs(44, 3, request->method << ' ' << request->url.host());
681
682 if (ps->direct == DIRECT_YES)
683 return;
684
685 if ((p = peerSourceHashSelectParent(request))) {
686 code = SOURCEHASH_PARENT;
687 #if USE_AUTH
688 } else if ((p = peerUserHashSelectParent(request))) {
689 code = USERHASH_PARENT;
690 #endif
691 } else if ((p = carpSelectParent(request))) {
692 code = CARP;
693 } else if ((p = getRoundRobinParent(request))) {
694 code = ROUNDROBIN_PARENT;
695 } else if ((p = getWeightedRoundRobinParent(request))) {
696 code = ROUNDROBIN_PARENT;
697 } else if ((p = getFirstUpParent(request))) {
698 code = FIRSTUP_PARENT;
699 } else if ((p = getDefaultParent(request))) {
700 code = DEFAULT_PARENT;
701 }
702
703 if (code != HIER_NONE) {
704 debugs(44, 3, "peerSelect: " << hier_code_str[code] << "/" << p->host);
705 peerAddFwdServer(&ps->servers, p, code);
706 }
707 }
708
709 /* Adds alive parents. Used as a last resort for never_direct.
710 */
711 static void
712 peerGetAllParents(ps_state * ps)
713 {
714 CachePeer *p;
715 HttpRequest *request = ps->request;
716 /* Add all alive parents */
717
718 for (p = Config.peers; p; p = p->next) {
719 /* XXX: neighbors.c lacks a public interface for enumerating
720 * parents to a request so we have to dig some here..
721 */
722
723 if (neighborType(p, request->url) != PEER_PARENT)
724 continue;
725
726 if (!peerHTTPOkay(p, request))
727 continue;
728
729 debugs(15, 3, "peerGetAllParents: adding alive parent " << p->host);
730
731 peerAddFwdServer(&ps->servers, p, ANY_OLD_PARENT);
732 }
733
734 /* XXX: should add dead parents here, but it is currently
735 * not possible to find out which parents are dead or which
736 * simply are not configured to handle the request.
737 */
738 /* Add default parent as a last resort */
739 if ((p = getDefaultParent(request))) {
740 peerAddFwdServer(&ps->servers, p, DEFAULT_PARENT);
741 }
742 }
743
744 static void
745 peerPingTimeout(void *data)
746 {
747 ps_state *psstate = (ps_state *)data;
748 StoreEntry *entry = psstate->entry;
749
750 if (entry)
751 debugs(44, 3, psstate->url());
752
753 if (!cbdataReferenceValid(psstate->callback_data)) {
754 /* request aborted */
755 if (entry)
756 entry->ping_status = PING_DONE;
757 cbdataReferenceDone(psstate->callback_data);
758 delete psstate;
759 return;
760 }
761
762 ++PeerStats.timeouts;
763 psstate->ping.timedout = 1;
764 peerSelectFoo(psstate);
765 }
766
767 void
768 peerSelectInit(void)
769 {
770 memset(&PeerStats, '\0', sizeof(PeerStats));
771 memDataInit(MEM_FWD_SERVER, "FwdServer", sizeof(FwdServer), 0);
772 }
773
774 static void
775 peerIcpParentMiss(CachePeer * p, icp_common_t * header, ps_state * ps)
776 {
777 int rtt;
778
779 #if USE_ICMP
780 if (Config.onoff.query_icmp) {
781 if (header->flags & ICP_FLAG_SRC_RTT) {
782 rtt = header->pad & 0xFFFF;
783 int hops = (header->pad >> 16) & 0xFFFF;
784
785 if (rtt > 0 && rtt < 0xFFFF)
786 netdbUpdatePeer(ps->request->url, p, rtt, hops);
787
788 if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
789 ps->closest_parent_miss = p->in_addr;
790 ps->ping.p_rtt = rtt;
791 }
792 }
793 }
794 #endif /* USE_ICMP */
795
796 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
797 if (p->options.closest_only)
798 return;
799
800 /* set FIRST_MISS if there is no CLOSEST parent */
801 if (!ps->closest_parent_miss.isAnyAddr())
802 return;
803
804 rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
805
806 if (rtt < 1)
807 rtt = 1;
808
809 if (ps->first_parent_miss.isAnyAddr() || rtt < ps->ping.w_rtt) {
810 ps->first_parent_miss = p->in_addr;
811 ps->ping.w_rtt = rtt;
812 }
813 }
814
815 static void
816 peerHandleIcpReply(CachePeer * p, peer_t type, icp_common_t * header, void *data)
817 {
818 ps_state *psstate = (ps_state *)data;
819 icp_opcode op = header->getOpCode();
820 debugs(44, 3, "peerHandleIcpReply: " << icp_opcode_str[op] << " " << psstate->url() );
821 #if USE_CACHE_DIGESTS && 0
822 /* do cd lookup to count false misses */
823
824 if (p && request)
825 peerNoteDigestLookup(request, p,
826 peerDigestLookup(p, request, psstate->entry));
827
828 #endif
829
830 ++ psstate->ping.n_recv;
831
832 if (op == ICP_MISS || op == ICP_DECHO) {
833 if (type == PEER_PARENT)
834 peerIcpParentMiss(p, header, psstate);
835 } else if (op == ICP_HIT) {
836 psstate->hit = p;
837 psstate->hit_type = type;
838 peerSelectFoo(psstate);
839 return;
840 }
841
842 if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
843 return;
844
845 peerSelectFoo(psstate);
846 }
847
848 #if USE_HTCP
849 static void
850 peerHandleHtcpReply(CachePeer * p, peer_t type, HtcpReplyData * htcp, void *data)
851 {
852 ps_state *psstate = (ps_state *)data;
853 debugs(44, 3, "" << (htcp->hit ? "HIT" : "MISS") << " " << psstate->url());
854 ++ psstate->ping.n_recv;
855
856 if (htcp->hit) {
857 psstate->hit = p;
858 psstate->hit_type = type;
859 peerSelectFoo(psstate);
860 return;
861 }
862
863 if (type == PEER_PARENT)
864 peerHtcpParentMiss(p, htcp, psstate);
865
866 if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
867 return;
868
869 peerSelectFoo(psstate);
870 }
871
872 static void
873 peerHtcpParentMiss(CachePeer * p, HtcpReplyData * htcp, ps_state * ps)
874 {
875 int rtt;
876
877 #if USE_ICMP
878 if (Config.onoff.query_icmp) {
879 if (htcp->cto.rtt > 0) {
880 rtt = (int) htcp->cto.rtt * 1000;
881 int hops = (int) htcp->cto.hops * 1000;
882 netdbUpdatePeer(ps->request->url, p, rtt, hops);
883
884 if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
885 ps->closest_parent_miss = p->in_addr;
886 ps->ping.p_rtt = rtt;
887 }
888 }
889 }
890 #endif /* USE_ICMP */
891
892 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
893 if (p->options.closest_only)
894 return;
895
896 /* set FIRST_MISS if there is no CLOSEST parent */
897 if (!ps->closest_parent_miss.isAnyAddr())
898 return;
899
900 rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
901
902 if (rtt < 1)
903 rtt = 1;
904
905 if (ps->first_parent_miss.isAnyAddr() || rtt < ps->ping.w_rtt) {
906 ps->first_parent_miss = p->in_addr;
907 ps->ping.w_rtt = rtt;
908 }
909 }
910
911 #endif
912
913 static void
914 peerHandlePingReply(CachePeer * p, peer_t type, AnyP::ProtocolType proto, void *pingdata, void *data)
915 {
916 if (proto == AnyP::PROTO_ICP)
917 peerHandleIcpReply(p, type, (icp_common_t *)pingdata, data);
918
919 #if USE_HTCP
920
921 else if (proto == AnyP::PROTO_HTCP)
922 peerHandleHtcpReply(p, type, (HtcpReplyData *)pingdata, data);
923
924 #endif
925
926 else
927 debugs(44, DBG_IMPORTANT, "peerHandlePingReply: unknown protocol " << proto);
928 }
929
930 static void
931 peerAddFwdServer(FwdServer ** FSVR, CachePeer * p, hier_code code)
932 {
933 FwdServer *fs = (FwdServer *)memAllocate(MEM_FWD_SERVER);
934 debugs(44, 5, "peerAddFwdServer: adding " <<
935 (p ? p->host : "DIRECT") << " " <<
936 hier_code_str[code] );
937 fs->_peer = cbdataReference(p);
938 fs->code = code;
939
940 while (*FSVR)
941 FSVR = &(*FSVR)->next;
942
943 *FSVR = fs;
944 }
945
946 ps_state::ps_state() : request (NULL),
947 entry (NULL),
948 always_direct(Config.accessList.AlwaysDirect?ACCESS_DUNNO:ACCESS_DENIED),
949 never_direct(Config.accessList.NeverDirect?ACCESS_DUNNO:ACCESS_DENIED),
950 direct(DIRECT_UNKNOWN),
951 callback (NULL),
952 callback_data (NULL),
953 lastError(NULL),
954 paths(NULL),
955 servers (NULL),
956 first_parent_miss(),
957 closest_parent_miss(),
958 hit(NULL),
959 hit_type(PEER_NONE),
960 acl_checklist (NULL)
961 {
962 ; // no local defaults.
963 }
964
965 const SBuf
966 ps_state::url() const
967 {
968 if (entry)
969 return SBuf(entry->url());
970
971 if (request)
972 return request->effectiveRequestUri();
973
974 static const SBuf noUrl("[no URL]");
975 return noUrl;
976 }
977
978 ping_data::ping_data() :
979 n_sent(0),
980 n_recv(0),
981 n_replies_expected(0),
982 timeout(0),
983 timedout(0),
984 w_rtt(0),
985 p_rtt(0)
986 {
987 start.tv_sec = 0;
988 start.tv_usec = 0;
989 stop.tv_sec = 0;
990 stop.tv_usec = 0;
991 }
992