]>
Commit | Line | Data |
---|---|---|
062e2281 | 1 | /* |
b8ae064d | 2 | * Copyright (C) 1996-2023 The Squid Software Foundation and contributors |
e25c139f | 3 | * |
bbc27441 AJ |
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. | |
062e2281 | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 44 Peer Selection Algorithm */ |
10 | ||
582c2af2 FC |
11 | #include "squid.h" |
12 | #include "acl/FilledChecklist.h" | |
9865de72 | 13 | #include "base/AsyncCbdataCalls.h" |
6043e368 | 14 | #include "base/InstanceId.h" |
8b082ed9 | 15 | #include "base/TypeTraits.h" |
a011edee | 16 | #include "CachePeer.h" |
2e24d0bf | 17 | #include "CachePeers.h" |
21c22f04 | 18 | #include "carp.h" |
582c2af2 | 19 | #include "client_side.h" |
4a3b98d7 | 20 | #include "dns/LookupDetails.h" |
a37fdd8a | 21 | #include "errorpage.h" |
a553a5a3 | 22 | #include "event.h" |
eb13c21e | 23 | #include "FwdState.h" |
af69c635 | 24 | #include "globals.h" |
bbaf2685 | 25 | #include "hier_code.h" |
924f73bc | 26 | #include "htcp.h" |
d3dddfb5 | 27 | #include "http/Stream.h" |
582c2af2 | 28 | #include "HttpRequest.h" |
9b5c4a9a | 29 | #include "icmp/net_db.h" |
582c2af2 | 30 | #include "ICP.h" |
c7a52e6d | 31 | #include "ip/tools.h" |
602d9612 | 32 | #include "ipcache.h" |
f0ba2534 | 33 | #include "neighbors.h" |
f795b373 | 34 | #include "peer_sourcehash.h" |
37236ba1 | 35 | #include "peer_userhash.h" |
582c2af2 | 36 | #include "PeerSelectState.h" |
4d5904f7 | 37 | #include "SquidConfig.h" |
582c2af2 | 38 | #include "Store.h" |
98cacedb | 39 | #include "time/gadgets.h" |
062e2281 | 40 | |
6043e368 AR |
41 | /** |
42 | * A CachePeer which has been selected as a possible destination. | |
43 | * Listed as pointers here so as to prevent duplicates being added but will | |
44 | * be converted to a set of IP address path options before handing back out | |
45 | * to the caller. | |
46 | * | |
47 | * Certain connection flags and outgoing settings will also be looked up and | |
48 | * set based on the received request and CachePeer settings before handing back. | |
49 | */ | |
50 | class FwdServer | |
51 | { | |
52 | MEMPROXY_CLASS(FwdServer); | |
53 | ||
54 | public: | |
55 | FwdServer(CachePeer *p, hier_code c) : | |
56 | _peer(p), | |
57 | code(c), | |
58 | next(nullptr) | |
59 | {} | |
60 | ||
61 | CbcPointer<CachePeer> _peer; /* NULL --> origin server */ | |
62 | hier_code code; | |
63 | FwdServer *next; | |
64 | }; | |
65 | ||
26ac0430 | 66 | static struct { |
75e88d56 | 67 | int timeouts; |
2fadd50d | 68 | } PeerStats; |
062e2281 | 69 | |
26ac0430 AJ |
70 | static const char *DirectStr[] = { |
71 | "DIRECT_UNKNOWN", | |
72 | "DIRECT_NO", | |
73 | "DIRECT_MAYBE", | |
74 | "DIRECT_YES" | |
75 | }; | |
75e88d56 | 76 | |
e7b08eec AR |
77 | /// a helper class to report a selected destination (for debugging) |
78 | class PeerSelectionDumper | |
79 | { | |
80 | public: | |
cb835136 AR |
81 | PeerSelectionDumper(const PeerSelector * const aSelector, const CachePeer * const aPeer, const hier_code aCode): |
82 | selector(aSelector), peer(aPeer), code(aCode) {} | |
e7b08eec | 83 | |
cb835136 | 84 | const PeerSelector * const selector; ///< selection parameters |
e7b08eec AR |
85 | const CachePeer * const peer; ///< successful selection info |
86 | const hier_code code; ///< selection algorithm | |
87 | }; | |
88 | ||
cb835136 | 89 | CBDATA_CLASS_INIT(PeerSelector); |
aa839030 | 90 | |
e7b08eec AR |
91 | /// prints PeerSelectionDumper (for debugging) |
92 | static std::ostream & | |
93 | operator <<(std::ostream &os, const PeerSelectionDumper &fsd) | |
94 | { | |
95 | os << hier_code_str[fsd.code]; | |
96 | ||
97 | if (fsd.peer) | |
a555a85b | 98 | os << '/' << *fsd.peer; |
cb835136 AR |
99 | else if (fsd.selector) // useful for DIRECT and gone PINNED destinations |
100 | os << '#' << fsd.selector->request->url.host(); | |
e7b08eec AR |
101 | |
102 | return os; | |
103 | } | |
104 | ||
9865de72 EB |
105 | /// An ICP ping timeout service. |
106 | /// Protects event.cc (which is designed to handle a few unrelated timeouts) | |
107 | /// from exposure to thousands of ping-related timeouts on busy proxies. | |
108 | class PeerSelectorPingMonitor | |
109 | { | |
110 | public: | |
111 | /// registers the given selector to be notified about the IPC ping timeout | |
112 | void monitor(PeerSelector *); | |
113 | ||
114 | /// removes a PeerSelector from the waiting list | |
115 | void forget(PeerSelector *); | |
116 | ||
117 | /// \returns a (nil) registration of a non-waiting peer selector | |
118 | WaitingPeerSelectorPosition npos() { return selectors.end(); } | |
119 | ||
120 | private: | |
121 | static void NoteWaitOver(void *monitor); | |
122 | ||
123 | void startWaiting(); | |
124 | void abortWaiting(); | |
125 | void noteWaitOver(); | |
126 | ||
127 | WaitingPeerSelectors selectors; ///< \see WaitingPeerSelectors | |
128 | }; | |
129 | ||
130 | /// monitors all PeerSelector ICP ping timeouts | |
131 | static PeerSelectorPingMonitor & | |
132 | PingMonitor() | |
133 | { | |
134 | static const auto Instance = new PeerSelectorPingMonitor(); | |
135 | return *Instance; | |
136 | } | |
137 | ||
138 | /* PeerSelectorPingMonitor */ | |
139 | ||
140 | /// PeerSelectorPingMonitor::noteWaitOver() wrapper | |
141 | void | |
142 | PeerSelectorPingMonitor::NoteWaitOver(void *raw) | |
143 | { | |
144 | assert(raw); | |
145 | static_cast<PeerSelectorPingMonitor*>(raw)->noteWaitOver(); | |
146 | } | |
147 | ||
148 | /// schedules a single event to represent all waiting selectors | |
149 | void | |
150 | PeerSelectorPingMonitor::startWaiting() | |
151 | { | |
152 | assert(!selectors.empty()); | |
153 | const auto interval = tvSubDsec(current_time, selectors.begin()->first); | |
154 | eventAdd("PeerSelectorPingMonitor::NoteWaitOver", &PeerSelectorPingMonitor::NoteWaitOver, this, interval, 0, false); | |
155 | } | |
156 | ||
157 | /// undoes an earlier startWaiting() call | |
158 | void | |
159 | PeerSelectorPingMonitor::abortWaiting() | |
160 | { | |
161 | // our event may be already in the AsyncCallQueue but that is OK: | |
162 | // such queued calls cannot accumulate, and we ignore any stale ones | |
163 | eventDelete(&PeerSelectorPingMonitor::NoteWaitOver, nullptr); | |
164 | } | |
165 | ||
166 | /// calls back all ready PeerSelectors and continues to wait for others | |
167 | void | |
168 | PeerSelectorPingMonitor::noteWaitOver() | |
169 | { | |
170 | while (!selectors.empty() && current_time >= selectors.begin()->first) { | |
171 | const auto selector = selectors.begin()->second; | |
172 | CallBack(selector->al, [selector,this] { | |
173 | selector->ping.monitorRegistration = npos(); | |
174 | AsyncCall::Pointer callback = asyncCall(44, 4, "PeerSelector::HandlePingTimeout", | |
b6388dfd | 175 | cbdataDialer(PeerSelector::HandlePingTimeout, selector)); |
9865de72 EB |
176 | ScheduleCallHere(callback); |
177 | }); | |
178 | selectors.erase(selectors.begin()); | |
179 | } | |
180 | ||
181 | if (!selectors.empty()) { | |
182 | // Since abortWaiting() is unreliable, we may have been awakened by a | |
183 | // stale event A after event B has been scheduled. Now we are going to | |
184 | // schedule event C. Prevent event accumulation by deleting B (if any). | |
185 | abortWaiting(); | |
186 | ||
187 | startWaiting(); | |
188 | } | |
189 | } | |
190 | ||
191 | void | |
192 | PeerSelectorPingMonitor::monitor(PeerSelector *selector) | |
193 | { | |
194 | assert(selector); | |
195 | ||
196 | const auto deadline = selector->ping.deadline(); | |
197 | const auto position = selectors.emplace(deadline, selector); | |
198 | selector->ping.monitorRegistration = position; | |
199 | ||
200 | if (position == selectors.begin()) { | |
201 | if (selectors.size() > 1) | |
202 | abortWaiting(); // remove the previously scheduled earlier event | |
203 | startWaiting(); | |
204 | } // else the already scheduled event is still the earliest one | |
205 | } | |
206 | ||
207 | void | |
208 | PeerSelectorPingMonitor::forget(PeerSelector *selector) | |
209 | { | |
210 | assert(selector); | |
211 | ||
212 | if (selector->ping.monitorRegistration == npos()) | |
213 | return; // already forgotten | |
214 | ||
215 | const auto wasFirst = selector->ping.monitorRegistration == selectors.begin(); | |
216 | selectors.erase(selector->ping.monitorRegistration); | |
217 | selector->ping.monitorRegistration = npos(); | |
218 | ||
219 | if (wasFirst) { | |
220 | // do not reschedule if there are still elements with the same deadline | |
221 | if (!selectors.empty() && selectors.begin()->first == selector->ping.deadline()) | |
222 | return; | |
223 | abortWaiting(); | |
224 | if (!selectors.empty()) | |
225 | startWaiting(); | |
226 | } // else do nothing since the old scheduled event is still the earliest one | |
227 | } | |
228 | ||
229 | /* PeerSelector */ | |
230 | ||
cb835136 | 231 | PeerSelector::~PeerSelector() |
348b2031 | 232 | { |
36339742 AJ |
233 | while (servers) { |
234 | FwdServer *next = servers->next; | |
3c670b50 | 235 | delete servers; |
36339742 AJ |
236 | servers = next; |
237 | } | |
238 | ||
9865de72 EB |
239 | cancelPingTimeoutMonitoring(); |
240 | ||
029c8349 AJ |
241 | if (entry) { |
242 | debugs(44, 3, entry->url()); | |
029c8349 | 243 | entry->ping_status = PING_DONE; |
5ae21d99 AJ |
244 | } |
245 | ||
029c8349 | 246 | if (acl_checklist) { |
d816f28d | 247 | debugs(44, DBG_IMPORTANT, "ERROR: Squid BUG: peer selector gone while waiting for a slow ACL"); |
029c8349 | 248 | delete acl_checklist; |
348b2031 | 249 | } |
62e76326 | 250 | |
029c8349 | 251 | HTTPMSGUNLOCK(request); |
62e76326 | 252 | |
029c8349 AJ |
253 | if (entry) { |
254 | assert(entry->ping_status != PING_WAITING); | |
1f90fd4a | 255 | entry->unlock("peerSelect"); |
aee3523a | 256 | entry = nullptr; |
73a201f8 | 257 | } |
62e76326 | 258 | |
029c8349 | 259 | delete lastError; |
348b2031 | 260 | } |
062e2281 | 261 | |
9865de72 EB |
262 | void |
263 | PeerSelector::startPingWaiting() | |
264 | { | |
265 | assert(entry); | |
266 | assert(entry->ping_status != PING_WAITING); | |
267 | PingMonitor().monitor(this); | |
268 | entry->ping_status = PING_WAITING; | |
269 | } | |
270 | ||
271 | void | |
272 | PeerSelector::cancelPingTimeoutMonitoring() | |
273 | { | |
274 | PingMonitor().forget(this); | |
275 | } | |
276 | ||
2d72d4fd | 277 | static int |
cb365059 | 278 | peerSelectIcpPing(PeerSelector *ps, int direct, StoreEntry * entry) |
062e2281 | 279 | { |
cb365059 EB |
280 | assert(ps); |
281 | HttpRequest *request = ps->request; | |
282 | ||
7b665aeb | 283 | int n; |
db1cd23c | 284 | assert(entry); |
285 | assert(entry->ping_status == PING_NONE); | |
9bd6a36d | 286 | assert(direct != DIRECT_YES); |
cb835136 | 287 | debugs(44, 3, entry->url()); |
62e76326 | 288 | |
45e5102d | 289 | if (!request->flags.hierarchical && direct != DIRECT_NO) |
62e76326 | 290 | return 0; |
291 | ||
d46a87a8 | 292 | if (EBIT_TEST(entry->flags, KEY_PRIVATE) && !neighbors_do_private_keys) |
62e76326 | 293 | if (direct != DIRECT_NO) |
294 | return 0; | |
295 | ||
cb365059 | 296 | n = neighborsCount(ps); |
62e76326 | 297 | |
cb835136 | 298 | debugs(44, 3, "counted " << n << " neighbors"); |
62e76326 | 299 | |
7b665aeb | 300 | return n; |
062e2281 | 301 | } |
302 | ||
6043e368 AR |
303 | static void |
304 | peerSelect(PeerSelectionInitiator *initiator, | |
cfd66529 | 305 | HttpRequest * request, |
d4806c91 | 306 | AccessLogEntry::Pointer const &al, |
6043e368 | 307 | StoreEntry * entry) |
75e88d56 | 308 | { |
86b389fc | 309 | if (entry) |
7f06a3d8 | 310 | debugs(44, 3, *entry << ' ' << entry->url()); |
86b389fc | 311 | else |
7f06a3d8 | 312 | debugs(44, 3, request->method); |
62e76326 | 313 | |
cb835136 | 314 | const auto selector = new PeerSelector(initiator); |
62e76326 | 315 | |
cb835136 AR |
316 | selector->request = request; |
317 | HTTPMSGLOCK(selector->request); | |
318 | selector->al = al; | |
62e76326 | 319 | |
cb835136 | 320 | selector->entry = entry; |
62e76326 | 321 | |
6cfa8966 | 322 | #if USE_CACHE_DIGESTS |
62e76326 | 323 | |
39edba21 | 324 | request->hier.peer_select_start = current_time; |
62e76326 | 325 | |
39edba21 | 326 | #endif |
62e76326 | 327 | |
cb835136 AR |
328 | if (selector->entry) |
329 | selector->entry->lock("peerSelect"); | |
62e76326 | 330 | |
cb835136 | 331 | selector->selectMore(); |
75e88d56 | 332 | } |
333 | ||
6043e368 AR |
334 | void |
335 | PeerSelectionInitiator::startSelectingDestinations(HttpRequest *request, const AccessLogEntry::Pointer &ale, StoreEntry *entry) | |
336 | { | |
337 | subscribed = true; | |
338 | peerSelect(this, request, ale, entry); | |
339 | // and wait for noteDestination() and/or noteDestinationsEnd() calls | |
340 | } | |
341 | ||
cb835136 | 342 | void |
329c128c | 343 | PeerSelector::checkNeverDirectDone(const Acl::Answer answer) |
75e88d56 | 344 | { |
cb835136 AR |
345 | acl_checklist = nullptr; |
346 | debugs(44, 3, answer); | |
347 | never_direct = answer; | |
3985b3f8 | 348 | switch (answer) { |
ae026ec6 | 349 | case ACCESS_ALLOWED: |
f365d297 | 350 | /** if never_direct says YES, do that. */ |
cb835136 AR |
351 | direct = DIRECT_NO; |
352 | debugs(44, 3, "direct = " << DirectStr[direct] << " (never_direct allow)"); | |
ae026ec6 AJ |
353 | break; |
354 | case ACCESS_DENIED: // not relevant. | |
1f585949 | 355 | case ACCESS_DUNNO: // not relevant. |
ae026ec6 | 356 | break; |
1f585949 | 357 | case ACCESS_AUTH_REQUIRED: |
ae026ec6 | 358 | debugs(44, DBG_IMPORTANT, "WARNING: never_direct resulted in " << answer << ". Username ACLs are not reliable here."); |
1f585949 | 359 | break; |
ae026ec6 | 360 | } |
cb835136 | 361 | selectMore(); |
75e88d56 | 362 | } |
363 | ||
cb835136 | 364 | void |
329c128c | 365 | PeerSelector::CheckNeverDirectDone(Acl::Answer answer, void *data) |
75e88d56 | 366 | { |
cb835136 AR |
367 | static_cast<PeerSelector*>(data)->checkNeverDirectDone(answer); |
368 | } | |
369 | ||
370 | void | |
329c128c | 371 | PeerSelector::checkAlwaysDirectDone(const Acl::Answer answer) |
cb835136 AR |
372 | { |
373 | acl_checklist = nullptr; | |
374 | debugs(44, 3, answer); | |
375 | always_direct = answer; | |
3985b3f8 | 376 | switch (answer) { |
ae026ec6 AJ |
377 | case ACCESS_ALLOWED: |
378 | /** if always_direct says YES, do that. */ | |
cb835136 AR |
379 | direct = DIRECT_YES; |
380 | debugs(44, 3, "direct = " << DirectStr[direct] << " (always_direct allow)"); | |
ae026ec6 AJ |
381 | break; |
382 | case ACCESS_DENIED: // not relevant. | |
1f585949 | 383 | case ACCESS_DUNNO: // not relevant. |
ae026ec6 | 384 | break; |
1f585949 | 385 | case ACCESS_AUTH_REQUIRED: |
ae026ec6 | 386 | debugs(44, DBG_IMPORTANT, "WARNING: always_direct resulted in " << answer << ". Username ACLs are not reliable here."); |
1f585949 | 387 | break; |
ae026ec6 | 388 | } |
cb835136 AR |
389 | selectMore(); |
390 | } | |
391 | ||
392 | void | |
329c128c | 393 | PeerSelector::CheckAlwaysDirectDone(Acl::Answer answer, void *data) |
cb835136 AR |
394 | { |
395 | static_cast<PeerSelector*>(data)->checkAlwaysDirectDone(answer); | |
75e88d56 | 396 | } |
397 | ||
cb835136 | 398 | /// \returns true (after destroying "this") if the peer initiator is gone |
6043e368 | 399 | /// \returns false (without side effects) otherwise |
cb835136 AR |
400 | bool |
401 | PeerSelector::selectionAborted() | |
6043e368 | 402 | { |
cb835136 | 403 | if (interestedInitiator()) |
6043e368 AR |
404 | return false; |
405 | ||
406 | debugs(44, 3, "Aborting peer selection: Initiator gone or lost interest."); | |
cb835136 | 407 | delete this; |
6043e368 AR |
408 | return true; |
409 | } | |
410 | ||
cb835136 | 411 | /// A single DNS resolution loop iteration: Converts selected FwdServer to IPs. |
cfd66529 | 412 | void |
cb835136 | 413 | PeerSelector::resolveSelected() |
cfd66529 | 414 | { |
cb835136 | 415 | if (selectionAborted()) |
36339742 | 416 | return; |
6043e368 | 417 | |
cb835136 | 418 | FwdServer *fs = servers; |
36339742 | 419 | |
7177edfb AJ |
420 | // Bug 3243: CVE 2009-0801 |
421 | // Bypass of browser same-origin access control in intercepted communication | |
422 | // To resolve this we must use only the original client destination when going DIRECT | |
423 | // on intercepted traffic which failed Host verification | |
cb835136 | 424 | const HttpRequest *req = request; |
45e5102d | 425 | const bool isIntercepted = !req->flags.redirected && |
0d901ef4 | 426 | (req->flags.intercepted || req->flags.interceptTproxy); |
45e5102d | 427 | const bool useOriginalDst = Config.onoff.client_dst_passthru || !req->flags.hostVerified; |
7177edfb AJ |
428 | const bool choseDirect = fs && fs->code == HIER_DIRECT; |
429 | if (isIntercepted && useOriginalDst && choseDirect) { | |
d9371d7b AJ |
430 | // check the client is still around before using any of its details |
431 | if (req->clientConnectionManager.valid()) { | |
432 | // construct a "result" adding the ORIGINAL_DST to the set instead of DIRECT | |
433 | Comm::ConnectionPointer p = new Comm::Connection(); | |
434 | p->remote = req->clientConnectionManager->clientConnection->local; | |
6043e368 | 435 | fs->code = ORIGINAL_DST; // fs->code is DIRECT. This fixes the display. |
cb835136 | 436 | handlePath(p, *fs); |
d9371d7b | 437 | } |
7177edfb AJ |
438 | |
439 | // clear the used fs and continue | |
cb835136 | 440 | servers = fs->next; |
3c670b50 | 441 | delete fs; |
cb835136 | 442 | resolveSelected(); |
7177edfb AJ |
443 | return; |
444 | } | |
445 | ||
3dde9e52 CT |
446 | if (fs && fs->code == PINNED) { |
447 | // Nil path signals a PINNED destination selection. Our initiator should | |
448 | // borrow and use clientConnectionManager's pinned connection object | |
449 | // (regardless of that connection destination). | |
450 | handlePath(nullptr, *fs); | |
451 | servers = fs->next; | |
452 | delete fs; | |
453 | resolveSelected(); | |
454 | return; | |
455 | } | |
456 | ||
cfd66529 | 457 | // convert the list of FwdServer destinations into destinations IP addresses |
cb835136 | 458 | if (fs && wantsMoreDestinations()) { |
cfd66529 | 459 | // send the next one off for DNS lookup. |
cb835136 AR |
460 | const char *host = fs->_peer.valid() ? fs->_peer->host : request->url.host(); |
461 | debugs(44, 2, "Find IP destination for: " << url() << "' via " << host); | |
462 | Dns::nbgethostbyname(host, this); | |
cfd66529 AJ |
463 | return; |
464 | } | |
465 | ||
c2f4e9cc AJ |
466 | // Bug 3605: clear any extra listed FwdServer destinations, when the options exceeds max_foward_tries. |
467 | // due to the allocation method of fs, we must deallocate each manually. | |
468 | // TODO: use a std::list so we can get the size and abort adding whenever the selection loops reach Config.forward_max_tries | |
6043e368 | 469 | if (fs) { |
cb835136 | 470 | assert(fs == servers); |
9bc68187 | 471 | while (fs) { |
cb835136 | 472 | servers = fs->next; |
3c670b50 | 473 | delete fs; |
cb835136 | 474 | fs = servers; |
c2f4e9cc AJ |
475 | } |
476 | } | |
477 | ||
cfd66529 | 478 | // done with DNS lookups. pass back to caller |
62e76326 | 479 | |
cb835136 AR |
480 | debugs(44, 2, id << " found all " << foundPaths << " destinations for " << url()); |
481 | debugs(44, 2, " always_direct = " << always_direct); | |
482 | debugs(44, 2, " never_direct = " << never_direct); | |
483 | debugs(44, 2, " timedout = " << ping.timedout); | |
5ae21d99 | 484 | |
cb835136 AR |
485 | ping.stop = current_time; |
486 | request->hier.ping = ping; // final result | |
3dfe8371 | 487 | |
cb835136 | 488 | if (lastError && foundPaths) { |
6043e368 AR |
489 | // nobody cares about errors if we found destinations despite them |
490 | debugs(44, 3, "forgetting the last error"); | |
cb835136 AR |
491 | delete lastError; |
492 | lastError = nullptr; | |
db1cd23c | 493 | } |
62e76326 | 494 | |
cb835136 AR |
495 | if (const auto initiator = interestedInitiator()) |
496 | initiator->noteDestinationsEnd(lastError); | |
497 | lastError = nullptr; // initiator owns the ErrorState object now | |
498 | delete this; | |
93775f90 | 499 | } |
500 | ||
fd9c47d1 | 501 | void |
cb835136 | 502 | PeerSelector::noteLookup(const Dns::LookupDetails &details) |
cfd66529 | 503 | { |
fd9c47d1 | 504 | /* ignore lookup delays that occurred after the initiator moved on */ |
cfd66529 | 505 | |
cb835136 | 506 | if (selectionAborted()) |
fd9c47d1 | 507 | return; |
cfd66529 | 508 | |
fd9c47d1 AR |
509 | if (!wantsMoreDestinations()) |
510 | return; | |
cfd66529 | 511 | |
fd9c47d1 AR |
512 | request->recordLookup(details); |
513 | } | |
5229395c | 514 | |
fd9c47d1 | 515 | void |
cb835136 | 516 | PeerSelector::noteIp(const Ip::Address &ip) |
fd9c47d1 | 517 | { |
cb835136 | 518 | if (selectionAborted()) |
fd9c47d1 | 519 | return; |
cfd66529 | 520 | |
fd9c47d1 AR |
521 | if (!wantsMoreDestinations()) |
522 | return; | |
27d1f0a0 | 523 | |
fd9c47d1 | 524 | const auto peer = servers->_peer.valid(); |
cfd66529 | 525 | |
fd9c47d1 AR |
526 | // for TPROXY spoofing, we must skip unusable addresses |
527 | if (request->flags.spoofClientIp && !(peer && peer->options.no_tproxy) ) { | |
528 | if (ip.isIPv4() != request->client_addr.isIPv4()) | |
529 | return; // cannot spoof the client address on this link | |
530 | } | |
c7a52e6d | 531 | |
fd9c47d1 AR |
532 | Comm::ConnectionPointer p = new Comm::Connection(); |
533 | p->remote = ip; | |
380b09ae AR |
534 | // XXX: We return a (non-peer) destination with a zero port if the selection |
535 | // initiator supplied a request target without a port. If there are no valid | |
536 | // use cases for this behavior, stop _selecting_ such destinations. | |
537 | p->remote.port(peer ? peer->http_port : request->url.port().value_or(0)); | |
fd9c47d1 AR |
538 | handlePath(p, *servers); |
539 | } | |
c7a52e6d | 540 | |
fd9c47d1 | 541 | void |
cb835136 | 542 | PeerSelector::noteIps(const Dns::CachedIps *ia, const Dns::LookupDetails &details) |
fd9c47d1 | 543 | { |
cb835136 | 544 | if (selectionAborted()) |
fd9c47d1 | 545 | return; |
cfd66529 | 546 | |
fd9c47d1 AR |
547 | FwdServer *fs = servers; |
548 | if (!ia) { | |
549 | debugs(44, 3, "Unknown host: " << (fs->_peer.valid() ? fs->_peer->host : request->url.host())); | |
a37fdd8a | 550 | // discard any previous error. |
fd9c47d1 | 551 | delete lastError; |
aee3523a | 552 | lastError = nullptr; |
a37fdd8a | 553 | if (fs->code == HIER_DIRECT) { |
7e6eabbc | 554 | lastError = new ErrorState(ERR_DNS_FAIL, Http::scServiceUnavailable, request, al); |
fd9c47d1 | 555 | lastError->dnsError = details.error; |
a37fdd8a | 556 | } |
cfd66529 | 557 | } |
fd9c47d1 | 558 | // else noteIp() calls have already processed all IPs in *ia |
cfd66529 | 559 | |
fd9c47d1 | 560 | servers = fs->next; |
3c670b50 | 561 | delete fs; |
cfd66529 | 562 | |
cb835136 AR |
563 | // continue resolving selected peers |
564 | resolveSelected(); | |
cfd66529 AJ |
565 | } |
566 | ||
cb835136 AR |
567 | int |
568 | PeerSelector::checkNetdbDirect() | |
b3264694 | 569 | { |
9b5c4a9a | 570 | #if USE_ICMP |
a3c6762c | 571 | CachePeer *p; |
b3264694 | 572 | int myrtt; |
573 | int myhops; | |
62e76326 | 574 | |
cb835136 | 575 | if (direct == DIRECT_NO) |
62e76326 | 576 | return 0; |
577 | ||
9b5c4a9a AJ |
578 | /* base lookup on RTT and Hops if ICMP NetDB is enabled. */ |
579 | ||
cb835136 | 580 | myrtt = netdbHostRtt(request->url.host()); |
5c51bffb AJ |
581 | debugs(44, 3, "MY RTT = " << myrtt << " msec"); |
582 | debugs(44, 3, "minimum_direct_rtt = " << Config.minDirectRtt << " msec"); | |
62e76326 | 583 | |
5f84d830 | 584 | if (myrtt && myrtt <= Config.minDirectRtt) |
62e76326 | 585 | return 1; |
586 | ||
cb835136 | 587 | myhops = netdbHostHops(request->url.host()); |
62e76326 | 588 | |
cb835136 AR |
589 | debugs(44, 3, "MY hops = " << myhops); |
590 | debugs(44, 3, "minimum_direct_hops = " << Config.minDirectHops); | |
62e76326 | 591 | |
b3264694 | 592 | if (myhops && myhops <= Config.minDirectHops) |
62e76326 | 593 | return 1; |
594 | ||
cb835136 | 595 | p = whichPeer(closest_parent_miss); |
62e76326 | 596 | |
aee3523a | 597 | if (p == nullptr) |
62e76326 | 598 | return 0; |
599 | ||
cb835136 | 600 | debugs(44, 3, "closest_parent_miss RTT = " << ping.p_rtt << " msec"); |
62e76326 | 601 | |
cb835136 | 602 | if (myrtt && myrtt <= ping.p_rtt) |
62e76326 | 603 | return 1; |
604 | ||
9b5c4a9a AJ |
605 | #endif /* USE_ICMP */ |
606 | ||
b3264694 | 607 | return 0; |
608 | } | |
609 | ||
cb835136 AR |
610 | void |
611 | PeerSelector::selectMore() | |
062e2281 | 612 | { |
cb835136 | 613 | if (selectionAborted()) |
36339742 | 614 | return; |
36339742 | 615 | |
5c51bffb | 616 | debugs(44, 3, request->method << ' ' << request->url.host()); |
62e76326 | 617 | |
45e5102d | 618 | /** If we don't know whether DIRECT is permitted ... */ |
cb835136 AR |
619 | if (direct == DIRECT_UNKNOWN) { |
620 | if (always_direct == ACCESS_DUNNO) { | |
621 | debugs(44, 3, "direct = " << DirectStr[direct] << " (always_direct to be checked)"); | |
45e5102d | 622 | /** check always_direct; */ |
aee3523a | 623 | ACLFilledChecklist *ch = new ACLFilledChecklist(Config.accessList.AlwaysDirect, request, nullptr); |
cb835136 AR |
624 | ch->al = al; |
625 | acl_checklist = ch; | |
cb365059 | 626 | acl_checklist->syncAle(request, nullptr); |
cb835136 | 627 | acl_checklist->nonBlockingCheck(CheckAlwaysDirectDone, this); |
62e76326 | 628 | return; |
cb835136 AR |
629 | } else if (never_direct == ACCESS_DUNNO) { |
630 | debugs(44, 3, "direct = " << DirectStr[direct] << " (never_direct to be checked)"); | |
45e5102d | 631 | /** check never_direct; */ |
aee3523a | 632 | ACLFilledChecklist *ch = new ACLFilledChecklist(Config.accessList.NeverDirect, request, nullptr); |
cb835136 AR |
633 | ch->al = al; |
634 | acl_checklist = ch; | |
cb365059 | 635 | acl_checklist->syncAle(request, nullptr); |
cb835136 | 636 | acl_checklist->nonBlockingCheck(CheckNeverDirectDone, this); |
62e76326 | 637 | return; |
450fe1cb | 638 | } else if (request->flags.noDirect) { |
45e5102d | 639 | /** if we are accelerating, direct is not an option. */ |
cb835136 AR |
640 | direct = DIRECT_NO; |
641 | debugs(44, 3, "direct = " << DirectStr[direct] << " (forced non-direct)"); | |
450fe1cb | 642 | } else if (request->flags.loopDetected) { |
45e5102d | 643 | /** if we are in a forwarding-loop, direct is not an option. */ |
cb835136 AR |
644 | direct = DIRECT_YES; |
645 | debugs(44, 3, "direct = " << DirectStr[direct] << " (forwarding loop detected)"); | |
646 | } else if (checkNetdbDirect()) { | |
647 | direct = DIRECT_YES; | |
648 | debugs(44, 3, "direct = " << DirectStr[direct] << " (checkNetdbDirect)"); | |
62e76326 | 649 | } else { |
cb835136 AR |
650 | direct = DIRECT_MAYBE; |
651 | debugs(44, 3, "direct = " << DirectStr[direct] << " (default)"); | |
62e76326 | 652 | } |
653 | ||
cb835136 | 654 | debugs(44, 3, "direct = " << DirectStr[direct]); |
db1cd23c | 655 | } |
62e76326 | 656 | |
d67acb4e | 657 | if (!entry || entry->ping_status == PING_NONE) |
cb835136 | 658 | selectPinned(); |
aee3523a | 659 | if (entry == nullptr) { |
62e76326 | 660 | (void) 0; |
df503d6c | 661 | } else if (entry->ping_status == PING_NONE) { |
cb835136 | 662 | selectSomeNeighbor(); |
62e76326 | 663 | |
664 | if (entry->ping_status == PING_WAITING) | |
665 | return; | |
db1cd23c | 666 | } else if (entry->ping_status == PING_WAITING) { |
cb835136 | 667 | selectSomeNeighborReplies(); |
9865de72 | 668 | cancelPingTimeoutMonitoring(); |
62e76326 | 669 | entry->ping_status = PING_DONE; |
db1cd23c | 670 | } |
62e76326 | 671 | |
cb835136 | 672 | switch (direct) { |
62e76326 | 673 | |
168dfda9 | 674 | case DIRECT_YES: |
cb835136 | 675 | selectSomeDirect(); |
62e76326 | 676 | break; |
677 | ||
168dfda9 | 678 | case DIRECT_NO: |
cb835136 AR |
679 | selectSomeParent(); |
680 | selectAllParents(); | |
62e76326 | 681 | break; |
682 | ||
168dfda9 | 683 | default: |
62e76326 | 684 | |
685 | if (Config.onoff.prefer_direct) | |
cb835136 | 686 | selectSomeDirect(); |
62e76326 | 687 | |
45e5102d | 688 | if (request->flags.hierarchical || !Config.onoff.nonhierarchical_direct) { |
cb835136 AR |
689 | selectSomeParent(); |
690 | selectAllParents(); | |
8b9a89c9 | 691 | } |
62e76326 | 692 | |
693 | if (!Config.onoff.prefer_direct) | |
cb835136 | 694 | selectSomeDirect(); |
62e76326 | 695 | |
696 | break; | |
168dfda9 | 697 | } |
62e76326 | 698 | |
cb835136 AR |
699 | // end peer selection; start resolving selected peers |
700 | resolveSelected(); | |
db1cd23c | 701 | } |
702 | ||
cb365059 | 703 | bool peerAllowedToUse(const CachePeer *, PeerSelector*); |
cfd66529 | 704 | |
cb835136 AR |
705 | /// Selects a pinned connection if it exists, is valid, and is allowed. |
706 | void | |
707 | PeerSelector::selectPinned() | |
d67acb4e | 708 | { |
cb835136 | 709 | // TODO: Avoid all repeated calls. Relying on PING_DONE is not enough. |
d67acb4e AJ |
710 | if (!request->pinnedConnection()) |
711 | return; | |
3dde9e52 | 712 | |
daf80700 CT |
713 | const auto peer = request->pinnedConnection()->pinnedPeer(); |
714 | const auto usePinned = peer ? peerAllowedToUse(peer, this) : (direct != DIRECT_NO); | |
715 | // If the pinned connection is prohibited (for this request) then | |
cb835136 | 716 | // the initiator must decide whether it is OK to open a new one instead. |
daf80700 CT |
717 | request->pinnedConnection()->pinning.peerAccessDenied = !usePinned; |
718 | ||
719 | addSelection(peer, PINNED); | |
720 | if (entry) | |
721 | entry->ping_status = PING_DONE; // skip ICP | |
d67acb4e AJ |
722 | } |
723 | ||
27774cee | 724 | /** |
db1cd23c | 725 | * Selects a neighbor (parent or sibling) based on one of the |
726 | * following methods: | |
727 | * Cache Digests | |
728 | * CARP | |
9b5c4a9a | 729 | * ICMP Netdb RTT estimates |
db1cd23c | 730 | * ICP/HTCP queries |
731 | */ | |
cb835136 AR |
732 | void |
733 | PeerSelector::selectSomeNeighbor() | |
db1cd23c | 734 | { |
a3c6762c | 735 | CachePeer *p; |
db1cd23c | 736 | hier_code code = HIER_NONE; |
737 | assert(entry->ping_status == PING_NONE); | |
62e76326 | 738 | |
cb835136 | 739 | if (direct == DIRECT_YES) { |
62e76326 | 740 | entry->ping_status = PING_DONE; |
741 | return; | |
124511e5 | 742 | } |
62e76326 | 743 | |
6cfa8966 | 744 | #if USE_CACHE_DIGESTS |
cb365059 | 745 | if ((p = neighborsDigestSelect(this))) { |
5c51bffb | 746 | if (neighborType(p, request->url) == PEER_PARENT) |
62e76326 | 747 | code = CD_PARENT_HIT; |
748 | else | |
749 | code = CD_SIBLING_HIT; | |
db1cd23c | 750 | } else |
c127134a | 751 | #endif |
cb365059 | 752 | if ((p = netdbClosestParent(this))) { |
62e76326 | 753 | code = CLOSEST_PARENT; |
cb365059 | 754 | } else if (peerSelectIcpPing(this, direct, entry)) { |
cb835136 AR |
755 | debugs(44, 3, "Doing ICP pings"); |
756 | ping.start = current_time; | |
757 | ping.n_sent = neighborsUdpPing(request, | |
758 | entry, | |
759 | HandlePingReply, | |
760 | this, | |
761 | &ping.n_replies_expected, | |
762 | &ping.timeout); | |
9865de72 EB |
763 | // TODO: Refactor neighborsUdpPing() to guarantee positive timeouts. |
764 | if (ping.timeout < 0) | |
765 | ping.timeout = 0; | |
cb835136 AR |
766 | |
767 | if (ping.n_sent == 0) | |
fa84c01d | 768 | debugs(44, DBG_CRITICAL, "WARNING: neighborsUdpPing returned 0"); |
cb835136 AR |
769 | debugs(44, 3, ping.n_replies_expected << |
770 | " ICP replies expected, RTT " << ping.timeout << | |
26ac0430 | 771 | " msec"); |
62e76326 | 772 | |
cb835136 | 773 | if (ping.n_replies_expected > 0) { |
9865de72 | 774 | startPingWaiting(); |
62e76326 | 775 | return; |
776 | } | |
777 | } | |
778 | ||
db1cd23c | 779 | if (code != HIER_NONE) { |
62e76326 | 780 | assert(p); |
cb835136 | 781 | addSelection(p, code); |
db1cd23c | 782 | } |
62e76326 | 783 | |
db1cd23c | 784 | entry->ping_status = PING_DONE; |
785 | } | |
786 | ||
cb835136 AR |
787 | /// Selects a neighbor (parent or sibling) based on ICP/HTCP replies. |
788 | void | |
789 | PeerSelector::selectSomeNeighborReplies() | |
db1cd23c | 790 | { |
aee3523a | 791 | CachePeer *p = nullptr; |
db1cd23c | 792 | hier_code code = HIER_NONE; |
cb835136 AR |
793 | assert(entry->ping_status == PING_WAITING); |
794 | assert(direct != DIRECT_YES); | |
62e76326 | 795 | |
cb835136 | 796 | if (checkNetdbDirect()) { |
62e76326 | 797 | code = CLOSEST_DIRECT; |
cb835136 | 798 | addSelection(nullptr, code); |
62e76326 | 799 | return; |
db1cd23c | 800 | } |
62e76326 | 801 | |
cb835136 AR |
802 | if ((p = hit)) { |
803 | code = hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT; | |
26ac0430 | 804 | } else { |
cb835136 AR |
805 | if (!closest_parent_miss.isAnyAddr()) { |
806 | p = whichPeer(closest_parent_miss); | |
cc192b50 | 807 | code = CLOSEST_PARENT_MISS; |
cb835136 AR |
808 | } else if (!first_parent_miss.isAnyAddr()) { |
809 | p = whichPeer(first_parent_miss); | |
cc192b50 | 810 | code = FIRST_PARENT_MISS; |
811 | } | |
812 | } | |
db1cd23c | 813 | if (p && code != HIER_NONE) { |
cb835136 | 814 | addSelection(p, code); |
db1cd23c | 815 | } |
816 | } | |
817 | ||
cb835136 AR |
818 | /// Adds a "direct" entry if the request can be forwarded to the origin server. |
819 | void | |
820 | PeerSelector::selectSomeDirect() | |
db1cd23c | 821 | { |
cb835136 | 822 | if (direct == DIRECT_NO) |
62e76326 | 823 | return; |
824 | ||
db80e881 | 825 | /* WAIS is not implemented natively */ |
cb835136 | 826 | if (request->url.getScheme() == AnyP::PROTO_WAIS) |
26ac0430 | 827 | return; |
db80e881 | 828 | |
cb835136 | 829 | addSelection(nullptr, HIER_DIRECT); |
db1cd23c | 830 | } |
831 | ||
cb835136 AR |
832 | void |
833 | PeerSelector::selectSomeParent() | |
db1cd23c | 834 | { |
a3c6762c | 835 | CachePeer *p; |
db1cd23c | 836 | hier_code code = HIER_NONE; |
5c51bffb | 837 | debugs(44, 3, request->method << ' ' << request->url.host()); |
62e76326 | 838 | |
cb835136 | 839 | if (direct == DIRECT_YES) |
62e76326 | 840 | return; |
841 | ||
cb365059 | 842 | if ((p = peerSourceHashSelectParent(this))) { |
b4ab8666 | 843 | code = SOURCEHASH_PARENT; |
2f1431ea | 844 | #if USE_AUTH |
cb365059 | 845 | } else if ((p = peerUserHashSelectParent(this))) { |
f7e1d9ce | 846 | code = USERHASH_PARENT; |
2f1431ea | 847 | #endif |
cb365059 | 848 | } else if ((p = carpSelectParent(this))) { |
62e76326 | 849 | code = CARP; |
cb365059 | 850 | } else if ((p = getRoundRobinParent(this))) { |
62e76326 | 851 | code = ROUNDROBIN_PARENT; |
cb365059 | 852 | } else if ((p = getWeightedRoundRobinParent(this))) { |
62e76326 | 853 | code = ROUNDROBIN_PARENT; |
cb365059 | 854 | } else if ((p = getFirstUpParent(this))) { |
62e76326 | 855 | code = FIRSTUP_PARENT; |
cb365059 | 856 | } else if ((p = getDefaultParent(this))) { |
b4ab8666 | 857 | code = DEFAULT_PARENT; |
db1cd23c | 858 | } |
62e76326 | 859 | |
db1cd23c | 860 | if (code != HIER_NONE) { |
cb835136 | 861 | addSelection(p, code); |
062e2281 | 862 | } |
863 | } | |
864 | ||
cb835136 AR |
865 | /// Adds alive parents. Used as a last resort for never_direct. |
866 | void | |
867 | PeerSelector::selectAllParents() | |
168dfda9 | 868 | { |
168dfda9 | 869 | /* Add all alive parents */ |
62e76326 | 870 | |
2e24d0bf EB |
871 | for (const auto &peer: CurrentCachePeers()) { |
872 | const auto p = peer.get(); | |
62e76326 | 873 | /* XXX: neighbors.c lacks a public interface for enumerating |
874 | * parents to a request so we have to dig some here.. | |
875 | */ | |
876 | ||
5c51bffb | 877 | if (neighborType(p, request->url) != PEER_PARENT) |
62e76326 | 878 | continue; |
879 | ||
cb365059 | 880 | if (!peerHTTPOkay(p, this)) |
62e76326 | 881 | continue; |
882 | ||
cb835136 | 883 | addSelection(p, ANY_OLD_PARENT); |
168dfda9 | 884 | } |
62e76326 | 885 | |
168dfda9 | 886 | /* XXX: should add dead parents here, but it is currently |
887 | * not possible to find out which parents are dead or which | |
888 | * simply are not configured to handle the request. | |
889 | */ | |
890 | /* Add default parent as a last resort */ | |
2e24d0bf | 891 | if (const auto p = getDefaultParent(this)) { |
cb835136 | 892 | addSelection(p, DEFAULT_PARENT); |
168dfda9 | 893 | } |
894 | } | |
895 | ||
cb835136 AR |
896 | void |
897 | PeerSelector::handlePingTimeout() | |
062e2281 | 898 | { |
cb835136 | 899 | debugs(44, 3, url()); |
62e76326 | 900 | |
9865de72 EB |
901 | // do nothing if ping reply came while handlePingTimeout() was queued |
902 | if (!entry || entry->ping_status != PING_WAITING) | |
903 | return; | |
904 | ||
905 | entry->ping_status = PING_DONE; | |
6043e368 | 906 | |
cb835136 | 907 | if (selectionAborted()) |
62e76326 | 908 | return; |
62e76326 | 909 | |
5db6bf73 | 910 | ++PeerStats.timeouts; |
cb835136 AR |
911 | ping.timedout = 1; |
912 | selectMore(); | |
913 | } | |
914 | ||
915 | void | |
9865de72 | 916 | PeerSelector::HandlePingTimeout(PeerSelector *selector) |
cb835136 | 917 | { |
9865de72 | 918 | selector->handlePingTimeout(); |
85034133 | 919 | } |
920 | ||
921 | void | |
922 | peerSelectInit(void) | |
923 | { | |
75e88d56 | 924 | memset(&PeerStats, '\0', sizeof(PeerStats)); |
062e2281 | 925 | } |
93775f90 | 926 | |
cb835136 AR |
927 | void |
928 | PeerSelector::handleIcpParentMiss(CachePeer *p, icp_common_t *header) | |
b3264694 | 929 | { |
930 | int rtt; | |
62e76326 | 931 | |
9b5c4a9a | 932 | #if USE_ICMP |
b3264694 | 933 | if (Config.onoff.query_icmp) { |
62e76326 | 934 | if (header->flags & ICP_FLAG_SRC_RTT) { |
935 | rtt = header->pad & 0xFFFF; | |
9b5c4a9a | 936 | int hops = (header->pad >> 16) & 0xFFFF; |
62e76326 | 937 | |
938 | if (rtt > 0 && rtt < 0xFFFF) | |
cb835136 | 939 | netdbUpdatePeer(request->url, p, rtt, hops); |
62e76326 | 940 | |
cb835136 AR |
941 | if (rtt && (ping.p_rtt == 0 || rtt < ping.p_rtt)) { |
942 | closest_parent_miss = p->in_addr; | |
943 | ping.p_rtt = rtt; | |
62e76326 | 944 | } |
945 | } | |
b3264694 | 946 | } |
8b082ed9 FC |
947 | #else |
948 | (void)header; | |
9b5c4a9a | 949 | #endif /* USE_ICMP */ |
62e76326 | 950 | |
18ec72b2 | 951 | /* if closest-only is set, then don't allow FIRST_PARENT_MISS */ |
cd196bc8 | 952 | if (p->options.closest_only) |
62e76326 | 953 | return; |
954 | ||
85223cd7 | 955 | /* set FIRST_MISS if there is no CLOSEST parent */ |
cb835136 | 956 | if (!closest_parent_miss.isAnyAddr()) |
62e76326 | 957 | return; |
958 | ||
cb835136 | 959 | rtt = (tvSubMsec(ping.start, current_time) - p->basetime) / p->weight; |
62e76326 | 960 | |
d1b63fc8 | 961 | if (rtt < 1) |
62e76326 | 962 | rtt = 1; |
963 | ||
cb835136 AR |
964 | if (first_parent_miss.isAnyAddr() || rtt < ping.w_rtt) { |
965 | first_parent_miss = p->in_addr; | |
966 | ping.w_rtt = rtt; | |
b3264694 | 967 | } |
968 | } | |
93775f90 | 969 | |
cb835136 AR |
970 | void |
971 | PeerSelector::handleIcpReply(CachePeer *p, const peer_t type, icp_common_t *header) | |
93775f90 | 972 | { |
e6ccf245 | 973 | icp_opcode op = header->getOpCode(); |
cb835136 | 974 | debugs(44, 3, icp_opcode_str[op] << ' ' << url()); |
69c95dd3 | 975 | #if USE_CACHE_DIGESTS && 0 |
26b164ac | 976 | /* do cd lookup to count false misses */ |
62e76326 | 977 | |
3ab66981 | 978 | if (p && request) |
62e76326 | 979 | peerNoteDigestLookup(request, p, |
cb365059 | 980 | peerDigestLookup(p, this)); |
62e76326 | 981 | |
26b164ac | 982 | #endif |
62e76326 | 983 | |
cb835136 | 984 | ++ping.n_recv; |
62e76326 | 985 | |
27cd7235 | 986 | if (op == ICP_MISS || op == ICP_DECHO) { |
62e76326 | 987 | if (type == PEER_PARENT) |
cb835136 | 988 | handleIcpParentMiss(p, header); |
a7c05555 | 989 | } else if (op == ICP_HIT) { |
cb835136 AR |
990 | hit = p; |
991 | hit_type = type; | |
992 | selectMore(); | |
62e76326 | 993 | return; |
db1cd23c | 994 | } |
62e76326 | 995 | |
cb835136 | 996 | if (ping.n_recv < ping.n_replies_expected) |
62e76326 | 997 | return; |
998 | ||
cb835136 | 999 | selectMore(); |
93775f90 | 1000 | } |
86aebcda | 1001 | |
1002 | #if USE_HTCP | |
cb835136 AR |
1003 | void |
1004 | PeerSelector::handleHtcpReply(CachePeer *p, const peer_t type, HtcpReplyData *htcp) | |
86aebcda | 1005 | { |
cb835136 AR |
1006 | debugs(44, 3, (htcp->hit ? "HIT" : "MISS") << ' ' << url()); |
1007 | ++ping.n_recv; | |
62e76326 | 1008 | |
44e237d0 | 1009 | if (htcp->hit) { |
cb835136 AR |
1010 | hit = p; |
1011 | hit_type = type; | |
1012 | selectMore(); | |
62e76326 | 1013 | return; |
44e237d0 | 1014 | } |
62e76326 | 1015 | |
44e237d0 | 1016 | if (type == PEER_PARENT) |
cb835136 | 1017 | handleHtcpParentMiss(p, htcp); |
62e76326 | 1018 | |
cb835136 | 1019 | if (ping.n_recv < ping.n_replies_expected) |
62e76326 | 1020 | return; |
1021 | ||
cb835136 | 1022 | selectMore(); |
44e237d0 | 1023 | } |
1024 | ||
cb835136 AR |
1025 | void |
1026 | PeerSelector::handleHtcpParentMiss(CachePeer *p, HtcpReplyData *htcp) | |
44e237d0 | 1027 | { |
1028 | int rtt; | |
62e76326 | 1029 | |
9b5c4a9a | 1030 | #if USE_ICMP |
44e237d0 | 1031 | if (Config.onoff.query_icmp) { |
62e76326 | 1032 | if (htcp->cto.rtt > 0) { |
1033 | rtt = (int) htcp->cto.rtt * 1000; | |
9b5c4a9a | 1034 | int hops = (int) htcp->cto.hops * 1000; |
cb835136 | 1035 | netdbUpdatePeer(request->url, p, rtt, hops); |
62e76326 | 1036 | |
cb835136 AR |
1037 | if (rtt && (ping.p_rtt == 0 || rtt < ping.p_rtt)) { |
1038 | closest_parent_miss = p->in_addr; | |
1039 | ping.p_rtt = rtt; | |
62e76326 | 1040 | } |
1041 | } | |
44e237d0 | 1042 | } |
8b082ed9 FC |
1043 | #else |
1044 | (void)htcp; | |
9b5c4a9a | 1045 | #endif /* USE_ICMP */ |
62e76326 | 1046 | |
44e237d0 | 1047 | /* if closest-only is set, then don't allow FIRST_PARENT_MISS */ |
cd196bc8 | 1048 | if (p->options.closest_only) |
62e76326 | 1049 | return; |
1050 | ||
44e237d0 | 1051 | /* set FIRST_MISS if there is no CLOSEST parent */ |
cb835136 | 1052 | if (!closest_parent_miss.isAnyAddr()) |
62e76326 | 1053 | return; |
1054 | ||
cb835136 | 1055 | rtt = (tvSubMsec(ping.start, current_time) - p->basetime) / p->weight; |
62e76326 | 1056 | |
d1b63fc8 | 1057 | if (rtt < 1) |
62e76326 | 1058 | rtt = 1; |
1059 | ||
cb835136 AR |
1060 | if (first_parent_miss.isAnyAddr() || rtt < ping.w_rtt) { |
1061 | first_parent_miss = p->in_addr; | |
1062 | ping.w_rtt = rtt; | |
44e237d0 | 1063 | } |
86aebcda | 1064 | } |
62e76326 | 1065 | |
86aebcda | 1066 | #endif |
1067 | ||
cb835136 AR |
1068 | void |
1069 | PeerSelector::HandlePingReply(CachePeer * p, peer_t type, AnyP::ProtocolType proto, void *pingdata, void *data) | |
86aebcda | 1070 | { |
0c3d3f65 | 1071 | if (proto == AnyP::PROTO_ICP) |
cb835136 | 1072 | static_cast<PeerSelector*>(data)->handleIcpReply(p, type, static_cast<icp_common_t*>(pingdata)); |
62e76326 | 1073 | |
86aebcda | 1074 | #if USE_HTCP |
62e76326 | 1075 | |
0c3d3f65 | 1076 | else if (proto == AnyP::PROTO_HTCP) |
cb835136 | 1077 | static_cast<PeerSelector*>(data)->handleHtcpReply(p, type, static_cast<HtcpReplyData*>(pingdata)); |
62e76326 | 1078 | |
86aebcda | 1079 | #endif |
62e76326 | 1080 | |
86aebcda | 1081 | else |
cb835136 | 1082 | debugs(44, DBG_IMPORTANT, "ERROR: ignoring an ICP reply with unknown protocol " << proto); |
86aebcda | 1083 | } |
db1cd23c | 1084 | |
cb835136 AR |
1085 | void |
1086 | PeerSelector::addSelection(CachePeer *peer, const hier_code code) | |
db1cd23c | 1087 | { |
e7b08eec | 1088 | // Find the end of the servers list. Bail on a duplicate destination. |
cb835136 AR |
1089 | auto **serversTail = &servers; |
1090 | while (const auto server = *serversTail) { | |
e7b08eec AR |
1091 | // There can be at most one PINNED destination. |
1092 | // Non-PINNED destinations are uniquely identified by their CachePeer | |
1093 | // (even though a DIRECT destination might match a cache_peer address). | |
9b7992d9 EB |
1094 | // TODO: We may still add duplicates because the same peer could have |
1095 | // been removed from `servers` already (and given to the requestor). | |
e7b08eec | 1096 | const bool duplicate = (server->code == PINNED) ? |
cb835136 | 1097 | (code == PINNED) : (server->_peer == peer); |
e7b08eec | 1098 | if (duplicate) { |
cb835136 AR |
1099 | debugs(44, 3, "skipping " << PeerSelectionDumper(this, peer, code) << |
1100 | "; have " << PeerSelectionDumper(this, server->_peer.get(), server->code)); | |
e7b08eec AR |
1101 | return; |
1102 | } | |
cb835136 | 1103 | serversTail = &server->next; |
e7b08eec | 1104 | } |
62e76326 | 1105 | |
cb835136 AR |
1106 | debugs(44, 3, "adding " << PeerSelectionDumper(this, peer, code)); |
1107 | *serversTail = new FwdServer(peer, code); | |
db1cd23c | 1108 | } |
b24880fe | 1109 | |
cb835136 | 1110 | PeerSelector::PeerSelector(PeerSelectionInitiator *initiator): |
6043e368 | 1111 | request(nullptr), |
aee3523a | 1112 | entry (nullptr), |
f53969cc SM |
1113 | always_direct(Config.accessList.AlwaysDirect?ACCESS_DUNNO:ACCESS_DENIED), |
1114 | never_direct(Config.accessList.NeverDirect?ACCESS_DUNNO:ACCESS_DENIED), | |
1115 | direct(DIRECT_UNKNOWN), | |
aee3523a AR |
1116 | lastError(nullptr), |
1117 | servers (nullptr), | |
f53969cc SM |
1118 | first_parent_miss(), |
1119 | closest_parent_miss(), | |
aee3523a | 1120 | hit(nullptr), |
f53969cc | 1121 | hit_type(PEER_NONE), |
aee3523a | 1122 | acl_checklist (nullptr), |
6043e368 | 1123 | initiator_(initiator) |
b24880fe | 1124 | { |
cc192b50 | 1125 | ; // no local defaults. |
b24880fe | 1126 | } |
1127 | ||
851feda6 | 1128 | const SBuf |
cb835136 | 1129 | PeerSelector::url() const |
769c64cc AJ |
1130 | { |
1131 | if (entry) | |
851feda6 | 1132 | return SBuf(entry->url()); |
769c64cc AJ |
1133 | |
1134 | if (request) | |
851feda6 | 1135 | return request->effectiveRequestUri(); |
769c64cc | 1136 | |
851feda6 AJ |
1137 | static const SBuf noUrl("[no URL]"); |
1138 | return noUrl; | |
769c64cc AJ |
1139 | } |
1140 | ||
6043e368 AR |
1141 | /// \returns valid/interested peer initiator or nil |
1142 | PeerSelectionInitiator * | |
cb835136 | 1143 | PeerSelector::interestedInitiator() |
6043e368 AR |
1144 | { |
1145 | const auto initiator = initiator_.valid(); | |
1146 | ||
1147 | if (!initiator) { | |
1148 | debugs(44, 3, id << " initiator gone"); | |
1149 | return nullptr; | |
1150 | } | |
1151 | ||
1152 | if (!initiator->subscribed) { | |
1153 | debugs(44, 3, id << " initiator lost interest"); | |
1154 | return nullptr; | |
1155 | } | |
1156 | ||
fd9c47d1 | 1157 | debugs(44, 7, id); |
6043e368 AR |
1158 | return initiator; |
1159 | } | |
1160 | ||
1161 | bool | |
cb835136 | 1162 | PeerSelector::wantsMoreDestinations() const { |
6043e368 | 1163 | const auto maxCount = Config.forward_max_tries; |
8b082ed9 | 1164 | return maxCount >= 0 && foundPaths < static_cast<size_t>(maxCount); |
6043e368 AR |
1165 | } |
1166 | ||
1167 | void | |
3dde9e52 | 1168 | PeerSelector::handlePath(const Comm::ConnectionPointer &path, FwdServer &fs) |
6043e368 AR |
1169 | { |
1170 | ++foundPaths; | |
1171 | ||
3dde9e52 CT |
1172 | if (path) { |
1173 | path->peerType = fs.code; | |
1174 | path->setPeer(fs._peer.get()); | |
6043e368 | 1175 | |
3dde9e52 CT |
1176 | // check for a configured outgoing address for this destination... |
1177 | getOutgoingAddress(request, path); | |
1178 | debugs(44, 2, id << " found " << path << ", destination #" << foundPaths << " for " << url()); | |
1179 | } else | |
1180 | debugs(44, 2, id << " found pinned, destination #" << foundPaths << " for " << url()); | |
6043e368 AR |
1181 | |
1182 | request->hier.ping = ping; // may be updated later | |
1183 | ||
6043e368 AR |
1184 | debugs(44, 2, " always_direct = " << always_direct); |
1185 | debugs(44, 2, " never_direct = " << never_direct); | |
1186 | debugs(44, 2, " timedout = " << ping.timedout); | |
1187 | ||
1188 | if (const auto initiator = interestedInitiator()) | |
1189 | initiator->noteDestination(path); | |
1190 | } | |
1191 | ||
cb835136 | 1192 | InstanceIdDefinitions(PeerSelector, "PeerSelector"); |
6043e368 | 1193 | |
b24880fe | 1194 | ping_data::ping_data() : |
f53969cc SM |
1195 | n_sent(0), |
1196 | n_recv(0), | |
1197 | n_replies_expected(0), | |
1198 | timeout(0), | |
1199 | timedout(0), | |
1200 | w_rtt(0), | |
9865de72 EB |
1201 | p_rtt(0), |
1202 | monitorRegistration(PingMonitor().npos()) | |
b24880fe | 1203 | { |
1204 | start.tv_sec = 0; | |
1205 | start.tv_usec = 0; | |
1206 | stop.tv_sec = 0; | |
1207 | stop.tv_usec = 0; | |
1208 | } | |
f53969cc | 1209 | |
9865de72 EB |
1210 | timeval |
1211 | ping_data::deadline() const | |
1212 | { | |
1213 | timeval timeInterval; | |
1214 | timeInterval.tv_sec = timeout / 1000; | |
1215 | timeInterval.tv_usec = (timeout % 1000) * 1000; | |
1216 | ||
1217 | timeval result; | |
1218 | tvAdd(result, start, timeInterval); | |
1219 | return result; | |
1220 | } | |
1221 |