]> git.ipfire.org Git - thirdparty/squid.git/blame - src/neighbors.cc
Fix parsing of malformed quoted squid.conf strings (#2239)
[thirdparty/squid.git] / src / neighbors.cc
CommitLineData
30a4f2a8 1/*
1f7b830e 2 * Copyright (C) 1996-2025 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.
019dd986 7 */
090089c4 8
bbc27441
AJ
9/* DEBUG: section 15 Neighbor Routines */
10
582c2af2 11#include "squid.h"
c0941a6a 12#include "acl/FilledChecklist.h"
65d448bc 13#include "anyp/PortCfg.h"
2fefc43c 14#include "base/EnumIterator.h"
a555a85b 15#include "base/IoManip.h"
90153ed6 16#include "base/PackableStream.h"
16cafa1f 17#include "base/PrecomputedCodeContext.h"
b814e8d4 18#include "CacheDigest.h"
a011edee 19#include "CachePeer.h"
2e24d0bf 20#include "CachePeers.h"
55cbb02b 21#include "comm/Connection.h"
aed188fd 22#include "comm/ConnOpener.h"
5be401dc 23#include "compat/netdb.h"
675b8408 24#include "debug/Messages.h"
a553a5a3 25#include "event.h"
eb13c21e 26#include "FwdState.h"
582c2af2 27#include "globals.h"
aa839030 28#include "htcp.h"
528b2c61 29#include "HttpRequest.h"
582c2af2 30#include "icmp/net_db.h"
aa839030 31#include "ICP.h"
f9b6ff6e 32#include "int.h"
582c2af2 33#include "ip/Address.h"
b283ea3f 34#include "ip/tools.h"
714e68b7 35#include "ipcache.h"
528b2c61 36#include "MemObject.h"
582c2af2 37#include "mgr/Registration.h"
afabcc13 38#include "multicast.h"
f0ba2534 39#include "neighbors.h"
602d9612 40#include "NeighborTypeDomainList.h"
e8dca475 41#include "pconn.h"
aa839030 42#include "PeerDigest.h"
e8dca475 43#include "PeerPoolMgr.h"
b24880fe 44#include "PeerSelectState.h"
f206b652 45#include "RequestFlags.h"
4d5904f7 46#include "SquidConfig.h"
a98bcbee 47#include "SquidMath.h"
6ea5959e 48#include "stat.h"
aa839030 49#include "Store.h"
fb548aaf 50#include "store_key_md5.h"
8d03bdb4 51#include "tools.h"
090089c4 52
429fdbec 53/* count mcast group peers every 15 minutes */
54#define MCAST_COUNT_RATE 900
55
cb365059
EB
56bool peerAllowedToUse(const CachePeer *, PeerSelector *);
57static int peerWouldBePinged(const CachePeer *, PeerSelector *);
a3c6762c 58static void neighborAlive(CachePeer *, const MemObject *, const icp_common_t *);
399cabec 59#if USE_HTCP
fad2588a 60static void neighborAliveHtcp(CachePeer *, const MemObject *, const HtcpReplyData *);
399cabec 61#endif
a3c6762c 62static void neighborCountIgnored(CachePeer *);
dd3292c9
EB
63static void peerDnsRefreshCheck(void *);
64static void peerDnsRefreshStart();
b69f7771 65static IPH peerDNSConfigure;
883b9a4d 66static void peerProbeConnect(CachePeer *, const bool reprobeIfBusy = false);
eb406bb7 67static CNCB peerProbeConnectDone;
f5b8bbc4 68static void peerCountMcastPeersDone(void *data);
69static void peerCountMcastPeersStart(void *data);
a3c6762c 70static void peerCountMcastPeersSchedule(CachePeer * p, time_t when);
3a8df7e9
EB
71static void peerCountMcastPeersAbort(PeerSelector *);
72static void peerCountMcastPeersCreateAndSend(CachePeer *p);
b3264694 73static IRCB peerCountHandleIcpReply;
62e76326 74
b7ac5457 75static void neighborIgnoreNonPeer(const Ip::Address &, icp_opcode);
ed7f5615 76static OBJH neighborDumpPeers;
2e24d0bf 77static void dump_peers(StoreEntry *, CachePeers *);
090089c4 78
f45dd259 79static unsigned short echo_port;
30a4f2a8 80
c7a3724d 81static int NLateReplies = 0;
28070024 82
a2c963ae 83const char *
a3c6762c 84neighborTypeStr(const CachePeer * p)
69a71bd7 85{
e102ebda 86 if (p->type == PEER_NONE)
62e76326 87 return "Non-Peer";
88
b69f7771 89 if (p->type == PEER_SIBLING)
62e76326 90 return "Sibling";
91
b69f7771 92 if (p->type == PEER_MULTICAST)
62e76326 93 return "Multicast Group";
94
69a71bd7 95 return "Parent";
96}
97
a3c6762c 98CachePeer *
b7ac5457 99whichPeer(const Ip::Address &from)
090089c4 100{
22e4fa85 101 int j;
62e76326 102
cc192b50 103 debugs(15, 3, "whichPeer: from " << from);
62e76326 104
2e24d0bf 105 for (const auto &p: CurrentCachePeers()) {
5db6bf73 106 for (j = 0; j < p->n_addresses; ++j) {
4dd643d5 107 if (from == p->addresses[j] && from.port() == p->icp.port) {
2e24d0bf 108 return p.get();
62e76326 109 }
110 }
090089c4 111 }
62e76326 112
aee3523a 113 return nullptr;
090089c4 114}
115
701a8572 116peer_t
c8ab5ec6 117neighborType(const CachePeer * p, const AnyP::Uri &url)
24a1003d 118{
62e76326 119
aee3523a 120 const NeighborTypeDomainList *d = nullptr;
62e76326 121
b69f7771 122 for (d = p->typelist; d; d = d->next) {
5c51bffb 123 if (0 == matchDomainName(url.host(), d->domain))
62e76326 124 if (d->type != PEER_NONE)
125 return d->type;
24a1003d 126 }
8e4a911a
AJ
127
128 if (p->type == PEER_MULTICAST && p->options.mcast_siblings)
129 return PEER_SIBLING;
62e76326 130
b69f7771 131 return p->type;
24a1003d 132}
133
2efeb0b7
AJ
134/**
135 * \return Whether it is appropriate to fetch REQUEST from PEER.
7b3bd12c 136 */
2efeb0b7 137bool
cb365059 138peerAllowedToUse(const CachePeer * p, PeerSelector * ps)
090089c4 139{
cb365059
EB
140 assert(ps);
141 HttpRequest *request = ps->request;
aee3523a 142 assert(request != nullptr);
62e76326 143
5c51bffb 144 if (neighborType(p, request->url) == PEER_SIBLING) {
8a368316 145 if (p->type == PEER_MULTICAST && p->options.mcast_siblings &&
450fe1cb 146 (request->flags.noCache || request->flags.refresh || request->flags.loopDetected || request->flags.needValidation))
a555a85b 147 debugs(15, 2, "multicast-siblings optimization match for " << *p << ", " << request->url.authority());
8e4a911a 148
450fe1cb 149 if (request->flags.noCache)
2efeb0b7 150 return false;
62e76326 151
45e5102d 152 if (request->flags.refresh)
2efeb0b7 153 return false;
62e76326 154
450fe1cb 155 if (request->flags.loopDetected)
2efeb0b7 156 return false;
62e76326 157
450fe1cb 158 if (request->flags.needValidation)
2efeb0b7 159 return false;
9689d97c 160 }
62e76326 161
ad46590b 162 // CONNECT requests are proxy requests. Not to be forwarded to origin servers.
a3c6762c 163 // Unless the destination port matches, in which case we MAY perform a 'DIRECT' to this CachePeer.
d9a7bc71 164 if (p->options.originserver && request->method == Http::METHOD_CONNECT && request->url.port() != p->http_port)
2efeb0b7 165 return false;
ad46590b 166
aee3523a 167 if (p->access == nullptr)
f1a5d071 168 return true;
62e76326 169
e94ff527 170 ACLFilledChecklist checklist(p->access, request);
b1c2ea7a 171 checklist.updateAle(ps->al);
cb365059 172 checklist.syncAle(request, nullptr);
06bf5384 173 return checklist.fastCheck().allowed();
090089c4 174}
175
a3c6762c 176/* Return TRUE if it is okay to send an ICP request to this CachePeer. */
62fd6124 177static int
cb365059 178peerWouldBePinged(const CachePeer * p, PeerSelector * ps)
62fd6124 179{
cb365059
EB
180 assert(ps);
181 HttpRequest *request = ps->request;
182
6315602c 183 if (p->icp.port == 0)
62e76326 184 return 0;
185
cd196bc8 186 if (p->options.no_query)
62e76326 187 return 0;
188
cd196bc8 189 if (p->options.mcast_responder)
62e76326 190 return 0;
191
83b381d5 192 if (p->n_addresses == 0)
62e76326 193 return 0;
194
6315602c 195 if (p->options.background_ping && (squid_curtime - p->stats.last_query < Config.backgroundPingRate))
62e76326 196 return 0;
197
62fd6124 198 /* the case below seems strange, but can happen if the
199 * URL host is on the other side of a firewall */
b69f7771 200 if (p->type == PEER_SIBLING)
45e5102d 201 if (!request->flags.hierarchical)
62e76326 202 return 0;
203
cb365059 204 if (!peerAllowedToUse(p, ps))
6315602c
JGD
205 return 0;
206
83b381d5 207 /* Ping dead peers every timeout interval */
208 if (squid_curtime - p->stats.last_query > Config.Timeout.deadPeer)
62e76326 209 return 1;
210
5537c535 211 if (!neighborUp(p))
212 return 0;
62e76326 213
62fd6124 214 return 1;
215}
216
e8dca475
CT
217bool
218peerCanOpenMore(const CachePeer *p)
219{
220 const int effectiveLimit = p->max_conn <= 0 ? Squid_MaxFD : p->max_conn;
221 const int remaining = effectiveLimit - p->stats.conn_open;
222 debugs(15, 7, remaining << '=' << effectiveLimit << '-' << p->stats.conn_open);
223 return remaining > 0;
224}
225
226bool
227peerHasConnAvailable(const CachePeer *p)
228{
229 // Standby connections can be used without opening new connections.
230 const int standbys = p->standby.pool ? p->standby.pool->count() : 0;
231
232 // XXX: Some idle pconns can be used without opening new connections.
233 // Complication: Idle pconns cannot be reused for some requests.
234 const int usableIdles = 0;
235
236 const int available = standbys + usableIdles;
237 debugs(15, 7, available << '=' << standbys << '+' << usableIdles);
238 return available > 0;
239}
240
241void
242peerConnClosed(CachePeer *p)
243{
244 --p->stats.conn_open;
245 if (p->standby.waitingForClose && peerCanOpenMore(p)) {
246 p->standby.waitingForClose = false;
247 PeerPoolMgr::Checkpoint(p->standby.mgr, "conn closed");
248 }
249}
250
a3c6762c 251/* Return TRUE if it is okay to send an HTTP request to this CachePeer. */
8ff4505b 252int
cb365059 253peerHTTPOkay(const CachePeer * p, PeerSelector * ps)
62fd6124 254{
e8dca475
CT
255 if (!peerCanOpenMore(p) && !peerHasConnAvailable(p))
256 return 0;
6315602c 257
cb365059 258 if (!peerAllowedToUse(p, ps))
62e76326 259 return 0;
260
b69f7771 261 if (!neighborUp(p))
62e76326 262 return 0;
263
62fd6124 264 return 1;
265}
266
3c6e634f 267int
cb365059 268neighborsCount(PeerSelector *ps)
090089c4 269{
090089c4 270 int count = 0;
62e76326 271
2e24d0bf
EB
272 for (const auto &p: CurrentCachePeers())
273 if (peerWouldBePinged(p.get(), ps))
5db6bf73 274 ++count;
62e76326 275
bf8fe701 276 debugs(15, 3, "neighborsCount: " << count);
62e76326 277
3c6e634f 278 return count;
279}
090089c4 280
a3c6762c 281CachePeer *
cb365059 282getFirstUpParent(PeerSelector *ps)
090089c4 283{
cb365059
EB
284 assert(ps);
285 HttpRequest *request = ps->request;
286
2e24d0bf 287 for (const auto &peer: CurrentCachePeers()) {
e9eb5900 288 const auto p = peer.get();
2e24d0bf 289
62e76326 290 if (!neighborUp(p))
291 continue;
292
5c51bffb 293 if (neighborType(p, request->url) != PEER_PARENT)
62e76326 294 continue;
295
cb365059 296 if (!peerHTTPOkay(p, ps))
62e76326 297 continue;
298
e9eb5900
EB
299 debugs(15, 3, "returning " << *p);
300 return p;
090089c4 301 }
62e76326 302
e9eb5900
EB
303 debugs(15, 3, "none found");
304 return nullptr;
090089c4 305}
306
a3c6762c 307CachePeer *
cb365059 308getRoundRobinParent(PeerSelector *ps)
48b38d01 309{
cb365059
EB
310 assert(ps);
311 HttpRequest *request = ps->request;
312
aee3523a 313 CachePeer *q = nullptr;
62e76326 314
2e24d0bf
EB
315 for (const auto &peer: CurrentCachePeers()) {
316 const auto p = peer.get();
62e76326 317 if (!p->options.roundrobin)
318 continue;
319
5c51bffb 320 if (neighborType(p, request->url) != PEER_PARENT)
62e76326 321 continue;
322
cb365059 323 if (!peerHTTPOkay(p, ps))
62e76326 324 continue;
325
1d0bef67 326 if (p->weight == 0)
0d5a2006 327 continue;
1d0bef67
AJ
328
329 if (q) {
330 if (p->weight == q->weight) {
331 if (q->rr_count < p->rr_count)
332 continue;
2d3d0b4e 333 } else if ( ((double) q->rr_count / q->weight) < ((double) p->rr_count / p->weight)) {
1d0bef67
AJ
334 continue;
335 }
0d5a2006 336 }
62e76326 337
338 q = p;
48b38d01 339 }
62e76326 340
b69f7771 341 if (q)
5db6bf73 342 ++ q->rr_count;
62e76326 343
a555a85b 344 debugs(15, 3, "returning " << RawPointer(q).orNil());
62e76326 345
b69f7771 346 return q;
48b38d01 347}
348
a3c6762c 349CachePeer *
cb365059 350getWeightedRoundRobinParent(PeerSelector *ps)
d1b63fc8 351{
cb365059
EB
352 assert(ps);
353 HttpRequest *request = ps->request;
354
aee3523a 355 CachePeer *q = nullptr;
d1b63fc8 356 int weighted_rtt;
62e76326 357
2e24d0bf
EB
358 for (const auto &peer: CurrentCachePeers()) {
359 const auto p = peer.get();
360
62e76326 361 if (!p->options.weighted_roundrobin)
362 continue;
363
5c51bffb 364 if (neighborType(p, request->url) != PEER_PARENT)
62e76326 365 continue;
366
cb365059 367 if (!peerHTTPOkay(p, ps))
62e76326 368 continue;
369
370 if (q && q->rr_count < p->rr_count)
371 continue;
372
373 q = p;
d1b63fc8 374 }
62e76326 375
d1b63fc8 376 if (q && q->rr_count > 1000000)
2e24d0bf 377 for (const auto &p: CurrentCachePeers()) {
62e76326 378 if (!p->options.weighted_roundrobin)
379 continue;
380
2e24d0bf 381 if (neighborType(p.get(), request->url) != PEER_PARENT)
62e76326 382 continue;
383
384 p->rr_count = 0;
385 }
386
d1b63fc8 387 if (q) {
62e76326 388 weighted_rtt = (q->stats.rtt - q->basetime) / q->weight;
d1b63fc8 389
62e76326 390 if (weighted_rtt < 1)
391 weighted_rtt = 1;
392
393 q->rr_count += weighted_rtt;
394
4a7a3d56 395 debugs(15, 3, "getWeightedRoundRobinParent: weighted_rtt " << weighted_rtt);
d1b63fc8 396 }
62e76326 397
a555a85b 398 debugs(15, 3, "returning " << RawPointer(q).orNil());
d1b63fc8 399 return q;
400}
401
32a47e3e
AJ
402/**
403 * This gets called every 5 minutes to clear the round-robin counter.
404 * The exact timing is an arbitrary default, set on estimate timing of a
405 * large number of requests in a high-performance environment during the
406 * period. The larger the number of requests between cycled resets the
407 * more balanced the operations.
408 *
9837567d
AJ
409 * \param data unused
410 *
411 * TODO: Make the reset timing a selectable parameter in squid.conf
32a47e3e 412 */
26ac0430 413static void
32a47e3e
AJ
414peerClearRRLoop(void *data)
415{
416 peerClearRR();
417 eventAdd("peerClearRR", peerClearRRLoop, data, 5 * 60.0, 0);
418}
419
420/**
a3c6762c 421 * This gets called on startup and restart to kick off the CachePeer round-robin
32a47e3e
AJ
422 * maintenance event. It ensures that no matter how many times its called
423 * no more than one event is scheduled.
424 */
82056f1e 425void
32a47e3e 426peerClearRRStart(void)
82056f1e 427{
7a3f0061 428 static bool event_added = false;
32a47e3e 429 if (!event_added) {
aee3523a 430 peerClearRRLoop(nullptr);
7a3f0061 431 event_added=true;
32a47e3e
AJ
432 }
433}
62e76326 434
32a47e3e
AJ
435/**
436 * Called whenever the round-robin counters need to be reset to a sane state.
437 * So far those times are:
d85b8894 438 * - On startup and reconfigure - to set the counters to sane initial settings.
a3c6762c 439 * - When a CachePeer has revived from dead, to prevent the revived CachePeer being
32a47e3e
AJ
440 * flooded with requests which it has 'missed' during the down period.
441 */
442void
443peerClearRR()
444{
2e24d0bf 445 for (const auto &p: CurrentCachePeers())
2da06da0 446 p->rr_count = 1;
32a47e3e 447}
62e76326 448
022dbabd 449void
a3c6762c 450peerAlive(CachePeer *p)
32a47e3e
AJ
451{
452 if (p->stats.logged_state == PEER_DEAD && p->tcp_up) {
a555a85b 453 debugs(15, DBG_IMPORTANT, "Detected REVIVED " << neighborTypeStr(p) << ": " << *p);
32a47e3e
AJ
454 p->stats.logged_state = PEER_ALIVE;
455 peerClearRR();
e8dca475
CT
456 if (p->standby.mgr.valid())
457 PeerPoolMgr::Checkpoint(p->standby.mgr, "revived peer");
32a47e3e 458 }
62e76326 459
32a47e3e
AJ
460 p->stats.last_reply = squid_curtime;
461 p->stats.probe_start = 0;
022dbabd
EB
462
463 // TODO: Remove or explain how we could detect an alive peer without IP addresses
464 if (!p->n_addresses)
465 ipcache_nbgethostbyname(p->host, peerDNSConfigure, p);
82056f1e 466}
467
a3c6762c 468CachePeer *
cb365059 469getDefaultParent(PeerSelector *ps)
5269d0bd 470{
cb365059
EB
471 assert(ps);
472 HttpRequest *request = ps->request;
473
2e24d0bf
EB
474 for (const auto &peer: CurrentCachePeers()) {
475 const auto p = peer.get();
62e76326 476
5c51bffb 477 if (neighborType(p, request->url) != PEER_PARENT)
62e76326 478 continue;
479
480 if (!p->options.default_parent)
481 continue;
482
cb365059 483 if (!peerHTTPOkay(p, ps))
62e76326 484 continue;
485
a555a85b 486 debugs(15, 3, "returning " << *p);
62e76326 487
488 return p;
5269d0bd 489 }
62e76326 490
a555a85b
AR
491 // TODO: Refactor similar get*() functions to use our return/reporting style
492 debugs(15, 3, "none found");
aee3523a 493 return nullptr;
5269d0bd 494}
495
5f5e883f
FC
496static void
497neighborsRegisterWithCacheManager()
498{
8822ebee 499 Mgr::RegisterAction("server_list",
d9fc6862
A
500 "Peer Cache Statistics",
501 neighborDumpPeers, 0, 1);
5f5e883f
FC
502}
503
b8d8561b 504void
a86029b9 505neighbors_init(void)
090089c4 506{
b263bd3f 507 const char *me = getMyHostname();
62e76326 508
6fdc2d18
FC
509 neighborsRegisterWithCacheManager();
510
e0d28505 511 if (Comm::IsConnOpen(icpIncomingConn)) {
2e24d0bf 512 RawCachePeers peersToRemove;
62e76326 513
2e24d0bf 514 for (const auto &thisPeer: CurrentCachePeers()) {
62e76326 515 if (0 != strcmp(thisPeer->host, me))
516 continue;
517
aee3523a 518 for (AnyP::PortCfgPointer s = HttpPortList; s != nullptr; s = s->next) {
4dd643d5 519 if (thisPeer->http_port != s->s.port())
62e76326 520 continue;
521
ccfbe8f4 522 debugs(15, DBG_IMPORTANT, "WARNING: Peer looks like this host." <<
a555a85b 523 Debug::Extra << "Ignoring cache_peer " << *thisPeer);
62e76326 524
2e24d0bf
EB
525 peersToRemove.push_back(thisPeer.get());
526 break; // avoid warning about (and removing) the same CachePeer twice
62e76326 527 }
528 }
2e24d0bf
EB
529
530 while (peersToRemove.size()) {
531 const auto p = peersToRemove.back();
532 peersToRemove.pop_back();
533 DeleteConfigured(p);
534 }
b263bd3f 535 }
62e76326 536
dd3292c9 537 peerDnsRefreshStart();
62e76326 538
5be401dc 539 const auto sep = xgetservbyname("echo", "udp");
f45dd259 540 echo_port = sep ? ntohs((unsigned short) sep->s_port) : 7;
62ee09ca 541}
542
b8d8561b 543int
190154cf 544neighborsUdpPing(HttpRequest * request,
62e76326 545 StoreEntry * entry,
546 IRCB * callback,
cb365059 547 PeerSelector *ps,
62e76326 548 int *exprep,
549 int *timeout)
090089c4 550{
3900307b 551 const char *url = entry->url();
b6c0e933 552 MemObject *mem = entry->mem_obj;
1061b406 553 int reqnum = 0;
6d2296d4 554 int flags;
0a0bf5db 555 int peers_pinged = 0;
a8c926ff 556 int parent_timeout = 0, parent_exprep = 0;
557 int sibling_timeout = 0, sibling_exprep = 0;
1bf2cd49 558 int mcast_timeout = 0, mcast_exprep = 0;
090089c4 559
aee3523a 560 if (Config.peers == nullptr)
62e76326 561 return 0;
562
4310f8b0 563 assert(!entry->hasDisk());
62e76326 564
b6c0e933 565 mem->start_ping = current_time;
62e76326 566
b4e7f82d 567 mem->ping_reply_callback = callback;
62e76326 568
cb365059 569 mem->ircb_data = ps;
62e76326 570
332dafa2 571 reqnum = icpSetCacheKey((const cache_key *)entry->key);
62e76326 572
16cafa1f 573 const auto savedContext = CodeContext::Current();
2e24d0bf
EB
574 for (size_t i = 0; i < Config.peers->size(); ++i) {
575 const auto p = &Config.peers->nextPeerToPing(i);
62e76326 576
16cafa1f
EB
577 CodeContext::Reset(p->probeCodeContext);
578
a555a85b 579 debugs(15, 5, "candidate: " << *p);
62e76326 580
cb365059 581 if (!peerWouldBePinged(p, ps))
f53969cc 582 continue; /* next CachePeer */
62e76326 583
5db6bf73 584 ++peers_pinged;
62e76326 585
a555a85b 586 debugs(15, 4, "pinging cache_peer " << *p << " for '" << url << "'");
62e76326 587
bf8fe701 588 debugs(15, 3, "neighborsUdpPing: key = '" << entry->getMD5Text() << "'");
62e76326 589
bf8fe701 590 debugs(15, 3, "neighborsUdpPing: reqnum = " << reqnum);
090089c4 591
dc9d133b 592#if USE_HTCP
4f4fa815 593 if (p->options.htcp && !p->options.htcp_only_clr) {
bebf08ff 594 if (Config.Port.htcp <= 0) {
d816f28d 595 debugs(15, DBG_CRITICAL, "ERROR: HTCP is disabled! Cannot send HTCP request to peer.");
bebf08ff
AJ
596 continue;
597 }
598
bf8fe701 599 debugs(15, 3, "neighborsUdpPing: sending HTCP query");
65d448bc
AJ
600 if (htcpQuery(entry, request, p) <= 0)
601 continue; // unable to send.
62e76326 602 } else
dc9d133b 603#endif
bebf08ff 604 {
e0d28505 605 if (Config.Port.icp <= 0 || !Comm::IsConnOpen(icpOutgoingConn)) {
d816f28d 606 debugs(15, DBG_CRITICAL, "ERROR: ICP is disabled! Cannot send ICP request to peer.");
bebf08ff 607 continue;
62e76326 608 } else {
62e76326 609
bebf08ff 610 if (p->type == PEER_MULTICAST)
e0d28505 611 mcastSetTtl(icpOutgoingConn->fd, p->mcast.ttl);
bebf08ff
AJ
612
613 if (p->icp.port == echo_port) {
614 debugs(15, 4, "neighborsUdpPing: Looks like a dumb cache, send DECHO ping");
d2a6dcba
EB
615 // TODO: Get ALE from callback_data if possible.
616 icpCreateAndSend(ICP_DECHO, 0, url, reqnum, 0,
617 icpOutgoingConn->fd, p->in_addr, nullptr);
bebf08ff
AJ
618 } else {
619 flags = 0;
62e76326 620
bebf08ff
AJ
621 if (Config.onoff.query_icmp)
622 if (p->icp.version == ICP_VERSION_2)
623 flags |= ICP_FLAG_SRC_RTT;
62e76326 624
d2a6dcba
EB
625 // TODO: Get ALE from callback_data if possible.
626 icpCreateAndSend(ICP_QUERY, flags, url, reqnum, 0,
627 icpOutgoingConn->fd, p->in_addr, nullptr);
bebf08ff 628 }
62e76326 629 }
bebf08ff 630 }
62e76326 631
5db6bf73 632 ++ p->stats.pings_sent;
62e76326 633
634 if (p->type == PEER_MULTICAST) {
1bf2cd49 635 mcast_exprep += p->mcast.n_replies_expected;
636 mcast_timeout += (p->stats.rtt * p->mcast.n_replies_expected);
62e76326 637 } else if (neighborUp(p)) {
638 /* its alive, expect a reply from it */
639
5c51bffb 640 if (neighborType(p, request->url) == PEER_PARENT) {
5db6bf73 641 ++parent_exprep;
62e76326 642 parent_timeout += p->stats.rtt;
643 } else {
5db6bf73 644 ++sibling_exprep;
62e76326 645 sibling_timeout += p->stats.rtt;
646 }
647 } else {
648 /* Neighbor is dead; ping it anyway, but don't expect a reply */
649 /* log it once at the threshold */
650
651 if (p->stats.logged_state == PEER_ALIVE) {
a555a85b 652 debugs(15, DBG_IMPORTANT, "Detected DEAD " << neighborTypeStr(p) << ": " << *p);
62e76326 653 p->stats.logged_state = PEER_DEAD;
654 }
655 }
656
657 p->stats.last_query = squid_curtime;
658
67c8308c 659 /*
a3c6762c
FC
660 * keep probe_start == 0 for a multicast CachePeer,
661 * so neighborUp() never says this CachePeer is dead.
67c8308c 662 */
663
664 if ((p->type != PEER_MULTICAST) && (p->stats.probe_start == 0))
62e76326 665 p->stats.probe_start = squid_curtime;
090089c4 666 }
16cafa1f 667 CodeContext::Reset(savedContext);
62e76326 668
a8c926ff 669 /*
670 * How many replies to expect?
671 */
1bf2cd49 672 *exprep = parent_exprep + sibling_exprep + mcast_exprep;
a8c926ff 673
52040193 674 /*
465dc415 675 * If there is a configured timeout, use it
52040193 676 */
465dc415 677 if (Config.Timeout.icp_query)
62e76326 678 *timeout = Config.Timeout.icp_query;
28993292 679 else {
62e76326 680 if (*exprep > 0) {
681 if (parent_exprep)
682 *timeout = 2 * parent_timeout / parent_exprep;
1bf2cd49 683 else if (mcast_exprep)
684 *timeout = 2 * mcast_timeout / mcast_exprep;
62e76326 685 else
686 *timeout = 2 * sibling_timeout / sibling_exprep;
687 } else
f53969cc 688 *timeout = 2000; /* 2 seconds */
62e76326 689
690 if (Config.Timeout.icp_query_max)
691 if (*timeout > Config.Timeout.icp_query_max)
692 *timeout = Config.Timeout.icp_query_max;
693
694 if (*timeout < Config.Timeout.icp_query_min)
695 *timeout = Config.Timeout.icp_query_min;
28993292 696 }
62e76326 697
0a0bf5db 698 return peers_pinged;
090089c4 699}
700
a3c6762c 701/* lookup the digest of a given CachePeer */
26b164ac 702lookup_t
cb365059 703peerDigestLookup(CachePeer * p, PeerSelector * ps)
26b164ac 704{
6cfa8966 705#if USE_CACHE_DIGESTS
cb365059
EB
706 assert(ps);
707 HttpRequest *request = ps->request;
0c511722 708 assert(request);
877e36d4
A
709
710 assert(p);
a555a85b 711 debugs(15, 5, "cache_peer " << *p);
4b4cd312 712 /* does the peeer have a valid digest? */
62e76326 713
e13ee7ad 714 if (!p->digest) {
bf8fe701 715 debugs(15, 5, "peerDigestLookup: gone!");
62e76326 716 return LOOKUP_NONE;
cb365059 717 } else if (!peerHTTPOkay(p, ps)) {
bf8fe701 718 debugs(15, 5, "peerDigestLookup: !peerHTTPOkay");
6755df2e 719 return LOOKUP_NONE;
e13ee7ad 720 } else if (!p->digest->flags.needed) {
bf8fe701 721 debugs(15, 5, "peerDigestLookup: note need");
62e76326 722 peerDigestNeeded(p->digest);
723 return LOOKUP_NONE;
4ed0e075 724 } else if (!p->digest->flags.usable) {
bf8fe701 725 debugs(15, 5, "peerDigestLookup: !ready && " << (p->digest->flags.requested ? "" : "!") << "requested");
62e76326 726 return LOOKUP_NONE;
26b164ac 727 }
62e76326 728
a555a85b 729 debugs(15, 5, "OK to lookup cache_peer " << *p);
e13ee7ad 730 assert(p->digest->cd);
26b164ac 731 /* does digest predict a hit? */
62e76326 732
877e36d4 733 if (!p->digest->cd->contains(storeKeyPublicByRequest(request)))
62e76326 734 return LOOKUP_MISS;
735
a555a85b 736 debugs(15, 5, "HIT for cache_peer " << *p);
62e76326 737
26b164ac 738 return LOOKUP_HIT;
8b082ed9
FC
739#else
740 (void)p;
741 (void)ps;
26b164ac 742#endif
62e76326 743
26b164ac 744 return LOOKUP_NONE;
745}
746
a3c6762c
FC
747/* select best CachePeer based on cache digests */
748CachePeer *
cb365059 749neighborsDigestSelect(PeerSelector *ps)
a54d3f8e 750{
aee3523a 751 CachePeer *best_p = nullptr;
6cfa8966 752#if USE_CACHE_DIGESTS
cb365059
EB
753 assert(ps);
754 HttpRequest *request = ps->request;
62e76326 755
a54d3f8e 756 int best_rtt = 0;
757 int choice_count = 0;
758 int ichoice_count = 0;
a54d3f8e 759 int p_rtt;
62e76326 760
70eaaee3
EB
761 if (!Config.peers)
762 return nullptr;
763
45e5102d 764 if (!request->flags.hierarchical)
aee3523a 765 return nullptr;
62e76326 766
e91e2a72 767 storeKeyPublicByRequest(request);
62e76326 768
2e24d0bf
EB
769 for (size_t i = 0; i < Config.peers->size(); ++i) {
770 const auto p = &Config.peers->nextPeerToPing(i);
62e76326 771
2e24d0bf 772 const auto lookup = peerDigestLookup(p, ps);
62e76326 773
774 if (lookup == LOOKUP_NONE)
775 continue;
776
5db6bf73 777 ++choice_count;
62e76326 778
779 if (lookup == LOOKUP_MISS)
780 continue;
781
782 p_rtt = netdbHostRtt(p->host);
783
a555a85b 784 debugs(15, 5, "cache_peer " << *p << " rtt: " << p_rtt);
62e76326 785
a3c6762c 786 /* is this CachePeer better than others in terms of rtt ? */
62e76326 787 if (!best_p || (p_rtt && p_rtt < best_rtt)) {
788 best_p = p;
789 best_rtt = p_rtt;
790
f53969cc 791 if (p_rtt) /* informative choice (aka educated guess) */
5db6bf73 792 ++ichoice_count;
62e76326 793
a555a85b 794 debugs(15, 4, "cache_peer " << *p << " leads with rtt " << best_rtt);
62e76326 795 }
a54d3f8e 796 }
62e76326 797
bf8fe701 798 debugs(15, 4, "neighborsDigestSelect: choices: " << choice_count << " (" << ichoice_count << ")");
26b164ac 799 peerNoteDigestLookup(request, best_p,
62e76326 800 best_p ? LOOKUP_HIT : (choice_count ? LOOKUP_MISS : LOOKUP_NONE));
a54d3f8e 801 request->hier.n_choices = choice_count;
802 request->hier.n_ichoices = ichoice_count;
8b082ed9
FC
803#else
804 (void)ps;
a54d3f8e 805#endif
62e76326 806
a54d3f8e 807 return best_p;
808}
809
26b164ac 810void
a3c6762c 811peerNoteDigestLookup(HttpRequest * request, CachePeer * p, lookup_t lookup)
26b164ac 812{
6cfa8966 813#if USE_CACHE_DIGESTS
26b164ac 814 if (p)
0a84e4fb 815 strncpy(request->hier.cd_host, p->host, sizeof(request->hier.cd_host)-1);
26b164ac 816 else
62e76326 817 *request->hier.cd_host = '\0';
818
26b164ac 819 request->hier.cd_lookup = lookup;
a555a85b 820 debugs(15, 4, "cache_peer " << RawPointer(p).orNil() << ", lookup: " << lookup_t_str[lookup]);
8b082ed9
FC
821#else
822 (void)request;
823 (void)p;
824 (void)lookup;
26b164ac 825#endif
826}
827
d73844ca 828static void
ced8def3 829neighborAlive(CachePeer * p, const MemObject *, const icp_common_t * header)
4eda6afe 830{
32a47e3e 831 peerAlive(p);
5db6bf73 832 ++ p->stats.pings_acked;
62e76326 833
27cd7235 834 if ((icp_opcode) header->opcode <= ICP_END)
5db6bf73 835 ++ p->icp.counts[header->opcode];
62e76326 836
44f2f2e4 837 p->icp.version = (int) header->version;
838}
839
840static void
a3c6762c 841neighborUpdateRtt(CachePeer * p, MemObject * mem)
44f2f2e4 842{
d1b63fc8 843 int rtt, rtt_av_factor;
62e76326 844
d2cd6935 845 if (!mem)
62e76326 846 return;
847
44f2f2e4 848 if (!mem->start_ping.tv_sec)
62e76326 849 return;
850
44f2f2e4 851 rtt = tvSubMsec(mem->start_ping, current_time);
62e76326 852
1b6ef2d2 853 if (rtt < 1 || rtt > 10000)
62e76326 854 return;
855
d1b63fc8 856 rtt_av_factor = RTT_AV_FACTOR;
62e76326 857
d1b63fc8 858 if (p->options.weighted_roundrobin)
62e76326 859 rtt_av_factor = RTT_BACKGROUND_AV_FACTOR;
860
a98bcbee 861 p->stats.rtt = Math::intAverage(p->stats.rtt, rtt, p->stats.pings_acked, rtt_av_factor);
4eda6afe 862}
090089c4 863
399cabec 864#if USE_HTCP
865static void
ced8def3 866neighborAliveHtcp(CachePeer * p, const MemObject *, const HtcpReplyData * htcp)
399cabec 867{
32a47e3e 868 peerAlive(p);
5db6bf73
FC
869 ++ p->stats.pings_acked;
870 ++ p->htcp.counts[htcp->hit ? 1 : 0];
44f2f2e4 871 p->htcp.version = htcp->version;
399cabec 872}
62e76326 873
399cabec 874#endif
875
38792624 876static void
a3c6762c 877neighborCountIgnored(CachePeer * p)
a7e59001 878{
aee3523a 879 if (p == nullptr)
62e76326 880 return;
881
5db6bf73 882 ++ p->stats.ignored_replies;
62e76326 883
5db6bf73 884 ++NLateReplies;
a7e59001 885}
886
e102ebda 887static void
b7ac5457 888neighborIgnoreNonPeer(const Ip::Address &from, icp_opcode opcode)
e102ebda 889{
e3732982
EB
890 static uint64_t ignoredReplies = 0;
891 if (isPowTen(++ignoredReplies)) {
892 debugs(15, DBG_IMPORTANT, "WARNING: Ignored " << ignoredReplies << " ICP replies from non-peers" <<
893 Debug::Extra << "last seen non-peer source address: " << from <<
894 Debug::Extra << "last seen ICP reply opcode: " << icp_opcode_str[opcode]);
e102ebda 895 }
e102ebda 896}
897
429fdbec 898/* ignoreMulticastReply
26ac0430 899 *
d6827718 900 * * We want to ignore replies from multicast peers if the
a3c6762c 901 * * cache_host_domain rules would normally prevent the CachePeer
d6827718 902 * * from being used
429fdbec 903 */
904static int
cb365059 905ignoreMulticastReply(CachePeer * p, PeerSelector * ps)
429fdbec 906{
aee3523a 907 if (p == nullptr)
62e76326 908 return 0;
909
cd196bc8 910 if (!p->options.mcast_responder)
62e76326 911 return 0;
912
cb365059 913 if (peerHTTPOkay(p, ps))
62e76326 914 return 0;
915
429fdbec 916 return 1;
917}
918
4b981814
AJ
919/**
920 * I should attach these records to the entry. We take the first
429fdbec 921 * hit we get our wait until everyone misses. The timeout handler
922 * call needs to nip this shopping list or call one of the misses.
26ac0430 923 *
429fdbec 924 * If a hit process is already started, then sobeit
925 */
b8d8561b 926void
b7ac5457 927neighborsUdpAck(const cache_key * key, icp_common_t * header, const Ip::Address &from)
090089c4 928{
aee3523a 929 CachePeer *p = nullptr;
5ad33356 930 StoreEntry *entry;
aee3523a 931 MemObject *mem = nullptr;
b6c0e933 932 peer_t ntype = PEER_NONE;
a7e59001 933 icp_opcode opcode = (icp_opcode) header->opcode;
090089c4 934
4a7a3d56 935 debugs(15, 6, "neighborsUdpAck: opcode " << opcode << " '" << storeKeyText(key) << "'");
62e76326 936
80d0fe08 937 if ((entry = Store::Root().findCallbackXXX(key)))
62e76326 938 mem = entry->mem_obj;
939
b69f7771 940 if ((p = whichPeer(from)))
62e76326 941 neighborAlive(p, mem, header);
942
27cd7235 943 if (opcode > ICP_END)
62e76326 944 return;
945
4b981814 946 const char *opcode_d = icp_opcode_str[opcode];
62e76326 947
41587298 948 if (p)
62e76326 949 neighborUpdateRtt(p, mem);
950
5ad33356 951 /* Does the entry exist? */
aee3523a 952 if (nullptr == entry) {
bf8fe701 953 debugs(12, 3, "neighborsUdpAck: Cache key '" << storeKeyText(key) << "' not found");
62e76326 954 neighborCountIgnored(p);
955 return;
5ad33356 956 }
62e76326 957
2d1c6a4f 958 /* check if someone is already fetching it */
26ac0430 959 if (EBIT_TEST(entry->flags, ENTRY_DISPATCHED)) {
bf8fe701 960 debugs(15, 3, "neighborsUdpAck: '" << storeKeyText(key) << "' already being fetched.");
62e76326 961 neighborCountIgnored(p);
962 return;
d2af9477 963 }
62e76326 964
aee3523a 965 if (mem == nullptr) {
bf8fe701 966 debugs(15, 2, "Ignoring " << opcode_d << " for missing mem_obj: " << storeKeyText(key));
62e76326 967 neighborCountIgnored(p);
968 return;
8de2f7ad 969 }
62e76326 970
26ac0430 971 if (entry->ping_status != PING_WAITING) {
bf8fe701 972 debugs(15, 2, "neighborsUdpAck: Late " << opcode_d << " for " << storeKeyText(key));
62e76326 973 neighborCountIgnored(p);
974 return;
8de2f7ad 975 }
62e76326 976
1bfe9ade 977 if (!entry->locked()) {
5bd484b5 978 // TODO: many entries are unlocked; why is this reported at level 1?
e0236918 979 debugs(12, DBG_IMPORTANT, "neighborsUdpAck: '" << storeKeyText(key) << "' has no locks");
62e76326 980 neighborCountIgnored(p);
981 return;
090089c4 982 }
62e76326 983
cb365059 984 if (!mem->ircb_data) {
d816f28d 985 debugs(12, DBG_IMPORTANT, "ERROR: Squid BUG: missing ICP callback data for " << *entry);
cb365059
EB
986 neighborCountIgnored(p);
987 return;
988 }
989
a555a85b 990 debugs(15, 3, opcode_d << " for " << storeKeyText(key) << " from " << RawPointer(p).orNil("source"));
62e76326 991
26ac0430 992 if (p) {
5c51bffb 993 ntype = neighborType(p, mem->request->url);
44f2f2e4 994 }
62e76326 995
cb365059 996 if (ignoreMulticastReply(p, mem->ircb_data)) {
62e76326 997 neighborCountIgnored(p);
26ac0430 998 } else if (opcode == ICP_MISS) {
aee3523a 999 if (p == nullptr) {
62e76326 1000 neighborIgnoreNonPeer(from, opcode);
1001 } else {
0c3d3f65 1002 mem->ping_reply_callback(p, ntype, AnyP::PROTO_ICP, header, mem->ircb_data);
62e76326 1003 }
26ac0430 1004 } else if (opcode == ICP_HIT) {
aee3523a 1005 if (p == nullptr) {
62e76326 1006 neighborIgnoreNonPeer(from, opcode);
1007 } else {
1008 header->opcode = ICP_HIT;
0c3d3f65 1009 mem->ping_reply_callback(p, ntype, AnyP::PROTO_ICP, header, mem->ircb_data);
62e76326 1010 }
26ac0430 1011 } else if (opcode == ICP_DECHO) {
aee3523a 1012 if (p == nullptr) {
62e76326 1013 neighborIgnoreNonPeer(from, opcode);
1014 } else if (ntype == PEER_SIBLING) {
1015 debug_trap("neighborsUdpAck: Found non-ICP cache as SIBLING\n");
1016 debug_trap("neighborsUdpAck: non-ICP neighbors must be a PARENT\n");
1017 } else {
0c3d3f65 1018 mem->ping_reply_callback(p, ntype, AnyP::PROTO_ICP, header, mem->ircb_data);
62e76326 1019 }
26ac0430 1020 } else if (opcode == ICP_SECHO) {
62e76326 1021 if (p) {
a555a85b 1022 debugs(15, DBG_IMPORTANT, "Ignoring SECHO from neighbor " << *p);
62e76326 1023 neighborCountIgnored(p);
62e76326 1024 } else {
e0236918 1025 debugs(15, DBG_IMPORTANT, "Unsolicited SECHO from " << from);
62e76326 1026 }
26ac0430 1027 } else if (opcode == ICP_DENIED) {
aee3523a 1028 if (p == nullptr) {
62e76326 1029 neighborIgnoreNonPeer(from, opcode);
1030 } else if (p->stats.pings_acked > 100) {
1031 if (100 * p->icp.counts[ICP_DENIED] / p->stats.pings_acked > 95) {
a555a85b
AR
1032 debugs(15, DBG_CRITICAL, "Disabling cache_peer " << *p <<
1033 " because over 95% of its replies are UDP_DENIED");
2e24d0bf 1034 DeleteConfigured(p);
aee3523a 1035 p = nullptr;
62e76326 1036 } else {
1037 neighborCountIgnored(p);
1038 }
1039 }
26ac0430 1040 } else if (opcode == ICP_MISS_NOFETCH) {
0c3d3f65 1041 mem->ping_reply_callback(p, ntype, AnyP::PROTO_ICP, header, mem->ircb_data);
26ac0430 1042 } else {
d816f28d 1043 debugs(15, DBG_CRITICAL, "ERROR: neighborsUdpAck: Unexpected ICP reply: " << opcode_d);
d2af9477 1044 }
090089c4 1045}
1046
a3c6762c 1047CachePeer *
a555a85b 1048findCachePeerByName(const char * const name)
98ffb7e4 1049{
2e24d0bf 1050 for (const auto &p: CurrentCachePeers()) {
62e76326 1051 if (!strcasecmp(name, p->name))
2e24d0bf 1052 return p.get();
98ffb7e4 1053 }
2e24d0bf 1054 return nullptr;
98ffb7e4 1055}
b012353a 1056
5269d0bd 1057int
a3c6762c 1058neighborUp(const CachePeer * p)
5269d0bd 1059{
eb406bb7 1060 if (!p->tcp_up) {
16cafa1f 1061 CallService(p->probeCodeContext, [&] {
be532888
EB
1062 peerProbeConnect(const_cast<CachePeer*>(p));
1063 });
8fcf19ec 1064 return 0;
eb406bb7 1065 }
62e76326 1066
64b8103b 1067 /*
a3c6762c 1068 * The CachePeer can not be UP if we don't have any IP addresses
26ac0430 1069 * for it.
64b8103b 1070 */
cc192b50 1071 if (0 == p->n_addresses) {
a555a85b 1072 debugs(15, 8, "DOWN (no-ip): " << *p);
62e76326 1073 return 0;
cc192b50 1074 }
62e76326 1075
cc192b50 1076 if (p->options.no_query) {
a555a85b 1077 debugs(15, 8, "UP (no-query): " << *p);
67c8308c 1078 return 1;
cc192b50 1079 }
67c8308c 1080
1081 if (p->stats.probe_start != 0 &&
cc192b50 1082 squid_curtime - p->stats.probe_start > Config.Timeout.deadPeer) {
a555a85b 1083 debugs(15, 8, "DOWN (dead): " << *p);
67c8308c 1084 return 0;
cc192b50 1085 }
67c8308c 1086
a555a85b 1087 debugs(15, 8, "UP: " << *p);
03b82057 1088 return 1;
5269d0bd 1089}
1090
0ce8e93b
EB
1091time_t
1092positiveTimeout(const time_t timeout)
1093{
1094 return max(static_cast<time_t>(1), timeout);
1095}
1096
dd4bd44e 1097static void
4a3b98d7 1098peerDNSConfigure(const ipcache_addrs *ia, const Dns::LookupDetails &, void *data)
dd4bd44e 1099{
e8dca475
CT
1100 // TODO: connections to no-longer valid IP addresses should be
1101 // closed when we can detect such IP addresses.
1102
a3c6762c 1103 CachePeer *p = (CachePeer *)data;
62e76326 1104
b69f7771 1105 if (p->n_addresses == 0) {
a555a85b 1106 debugs(15, Important(29), "Configuring " << neighborTypeStr(p) << " " << *p);
62e76326 1107
1108 if (p->type == PEER_MULTICAST)
e0236918 1109 debugs(15, DBG_IMPORTANT, " Multicast TTL = " << p->mcast.ttl);
b69f7771 1110 }
62e76326 1111
b69f7771 1112 p->n_addresses = 0;
62e76326 1113
aee3523a 1114 if (ia == nullptr) {
a555a85b 1115 debugs(0, DBG_CRITICAL, "WARNING: DNS lookup for '" << *p << "' failed!");
62e76326 1116 return;
dd4bd44e 1117 }
62e76326 1118
fd9c47d1 1119 if (ia->empty()) {
a555a85b 1120 debugs(0, DBG_CRITICAL, "WARNING: No IP address found for '" << *p << "'!");
62e76326 1121 return;
dd4bd44e 1122 }
62e76326 1123
fd9c47d1
AR
1124 for (const auto &ip: ia->goodAndBad()) { // TODO: Consider using just good().
1125 if (p->n_addresses < PEER_MAX_ADDRESSES) {
1126 const auto idx = p->n_addresses++;
1127 p->addresses[idx] = ip;
1128 debugs(15, 2, "--> IP address #" << idx << ": " << p->addresses[idx]);
1129 } else {
1130 debugs(15, 3, "ignoring remaining " << (ia->size() - p->n_addresses) << " ips");
1131 break;
1132 }
dd4bd44e 1133 }
62e76326 1134
4dd643d5 1135 p->in_addr.setEmpty();
cc192b50 1136 p->in_addr = p->addresses[0];
4dd643d5 1137 p->in_addr.port(p->icp.port);
62e76326 1138
883b9a4d
AR
1139 peerProbeConnect(p, true); // detect any died or revived peers ASAP
1140
b69f7771 1141 if (p->type == PEER_MULTICAST)
62e76326 1142 peerCountMcastPeersSchedule(p, 10);
1143
9b5c4a9a 1144#if USE_ICMP
e51c8197 1145 if (p->type != PEER_MULTICAST && IamWorkerProcess())
62e76326 1146 if (!p->options.no_netdb_exchange)
1147 eventAddIsh("netdbExchangeStart", netdbExchangeStart, p, 30.0, 1);
9b5c4a9a
AJ
1148#endif
1149
e8dca475
CT
1150 if (p->standby.mgr.valid())
1151 PeerPoolMgr::Checkpoint(p->standby.mgr, "resolved peer");
dd4bd44e 1152}
1153
1154static void
dd3292c9 1155peerScheduleDnsRefreshCheck(const double delayInSeconds)
dd4bd44e 1156{
dd3292c9
EB
1157 if (eventFind(peerDnsRefreshCheck, nullptr))
1158 eventDelete(peerDnsRefreshCheck, nullptr);
1159 eventAddIsh("peerDnsRefreshCheck", peerDnsRefreshCheck, nullptr, delayInSeconds, 1);
1160}
62e76326 1161
dd3292c9
EB
1162static void
1163peerDnsRefreshCheck(void *)
1164{
72f2eff8 1165 if (!statSawRecentRequests()) {
62e76326 1166 /* no recent client traffic, wait a bit */
dd3292c9 1167 peerScheduleDnsRefreshCheck(180.0);
62e76326 1168 return;
dd4bd44e 1169 }
62e76326 1170
dd3292c9
EB
1171 peerDnsRefreshStart();
1172}
1173
1174static void
1175peerDnsRefreshStart()
1176{
16cafa1f
EB
1177 const auto savedContext = CodeContext::Current();
1178 for (const auto &p: CurrentCachePeers()) {
1179 CodeContext::Reset(p->probeCodeContext);
2e24d0bf 1180 ipcache_nbgethostbyname(p->host, peerDNSConfigure, p.get());
16cafa1f
EB
1181 }
1182 CodeContext::Reset(savedContext);
62e76326 1183
dd3292c9 1184 peerScheduleDnsRefreshCheck(3600.0);
dd4bd44e 1185}
e924600d 1186
883b9a4d
AR
1187/// whether new TCP probes are currently banned
1188static bool
1189peerProbeIsBusy(const CachePeer *p)
1190{
1191 if (p->testing_now > 0) {
1192 debugs(15, 8, "yes, probing " << p);
1193 return true;
1194 }
1195 if (squid_curtime - p->stats.last_connect_probe == 0) {
1196 debugs(15, 8, "yes, just probed " << p);
1197 return true;
1198 }
1199 return false;
1200}
4ed0e075 1201/*
26ac0430 1202* peerProbeConnect will be called on dead peers by neighborUp
4ed0e075 1203*/
8fcf19ec 1204static void
883b9a4d 1205peerProbeConnect(CachePeer *p, const bool reprobeIfBusy)
e924600d 1206{
883b9a4d
AR
1207 if (peerProbeIsBusy(p)) {
1208 p->reprobe = reprobeIfBusy;
8fcf19ec
EB
1209 return;
1210 }
883b9a4d 1211 p->reprobe = false;
62e76326 1212
5f5d319e 1213 const auto ctimeout = p->connectTimeout();
a3c6762c 1214 /* for each IP address of this CachePeer. find one that we can connect to and probe it. */
5db6bf73 1215 for (int i = 0; i < p->n_addresses; ++i) {
f9b72e0c 1216 Comm::ConnectionPointer conn = new Comm::Connection;
cfd66529 1217 conn->remote = p->addresses[i];
4dd643d5 1218 conn->remote.port(p->http_port);
06c40c46 1219 conn->setPeer(p);
aee3523a 1220 getOutgoingAddress(nullptr, conn);
cc192b50 1221
5db6bf73 1222 ++ p->testing_now;
715b5def 1223
aed188fd 1224 AsyncCall::Pointer call = commCbCall(15,3, "peerProbeConnectDone", CommConnectCbPtrFun(peerProbeConnectDone, p));
4accd6d8 1225 Comm::ConnOpener *cs = new Comm::ConnOpener(conn, call, ctimeout);
aed188fd 1226 cs->setHost(p->host);
855150a4 1227 AsyncJob::Start(cs);
715b5def
AJ
1228 }
1229
eb406bb7 1230 p->stats.last_connect_probe = squid_curtime;
e924600d 1231}
1232
1233static void
ced8def3 1234peerProbeConnectDone(const Comm::ConnectionPointer &conn, Comm::Flag status, int, void *data)
e924600d 1235{
a3c6762c 1236 CachePeer *p = (CachePeer*)data;
62e76326 1237
022dbabd
EB
1238 if (status == Comm::OK)
1239 p->noteSuccess();
1240 else
2e7dea3c 1241 p->noteFailure();
62e76326 1242
5e263176 1243 -- p->testing_now;
db9bf30d
AJ
1244 conn->close();
1245 // TODO: log this traffic.
883b9a4d
AR
1246
1247 if (p->reprobe)
1248 peerProbeConnect(p);
e924600d 1249}
1250
429fdbec 1251static void
a3c6762c 1252peerCountMcastPeersSchedule(CachePeer * p, time_t when)
429fdbec 1253{
b515fc11 1254 if (p->mcast.flags.count_event_pending)
62e76326 1255 return;
1256
429fdbec 1257 eventAdd("peerCountMcastPeersStart",
62e76326 1258 peerCountMcastPeersStart,
1259 p,
1260 (double) when, 1);
1261
ccdf4138 1262 p->mcast.flags.count_event_pending = true;
429fdbec 1263}
1264
1265static void
1266peerCountMcastPeersStart(void *data)
3a8df7e9
EB
1267{
1268 const auto peer = static_cast<CachePeer*>(data);
1269 CallContextCreator([peer] {
1270 peerCountMcastPeersCreateAndSend(peer);
1271 });
1272 peerCountMcastPeersSchedule(peer, MCAST_COUNT_RATE);
1273}
1274
1275/// initiates an ICP transaction to a multicast peer
1276static void
1277peerCountMcastPeersCreateAndSend(CachePeer * const p)
429fdbec 1278{
6043e368
AR
1279 // XXX: Do not create lots of complex fake objects (while abusing their
1280 // APIs) to pass around a few basic data points like start_ping and ping!
429fdbec 1281 MemObject *mem;
007b8be4 1282 int reqnum;
c8ab5ec6 1283 // TODO: use class AnyP::Uri instead of constructing and re-parsing a string
429fdbec 1284 LOCAL_ARRAY(char, url, MAX_URL);
ce66013b 1285 assert(p->type == PEER_MULTICAST);
ccdf4138 1286 p->mcast.flags.count_event_pending = false;
cc192b50 1287 snprintf(url, MAX_URL, "http://");
4dd643d5 1288 p->in_addr.toUrl(url+7, MAX_URL -8 );
cc192b50 1289 strcat(url, "/");
ad05b958 1290 const auto mx = MasterXaction::MakePortless<XactionInitiator::initPeerMcast>();
6c880a16 1291 auto *req = HttpRequest::FromUrlXXX(url, mx);
8babada0 1292 assert(req != nullptr);
3a8df7e9
EB
1293 const AccessLogEntry::Pointer ale = new AccessLogEntry;
1294 ale->request = req;
1295 CodeContext::Reset(ale);
8babada0 1296 StoreEntry *fake = storeCreateEntry(url, url, RequestFlags(), Http::METHOD_GET);
cb835136 1297 const auto psstate = new PeerSelector(nullptr);
b248c2a3
AJ
1298 psstate->request = req;
1299 HTTPMSGLOCK(psstate->request);
429fdbec 1300 psstate->entry = fake;
6043e368 1301 psstate->peerCountMcastPeerXXX = cbdataReference(p);
b4e7f82d 1302 psstate->ping.start = current_time;
3a8df7e9 1303 psstate->al = ale;
429fdbec 1304 mem = fake->mem_obj;
b248c2a3 1305 mem->request = psstate->request;
429fdbec 1306 mem->start_ping = current_time;
b4e7f82d 1307 mem->ping_reply_callback = peerCountHandleIcpReply;
429fdbec 1308 mem->ircb_data = psstate;
e0d28505 1309 mcastSetTtl(icpOutgoingConn->fd, p->mcast.ttl);
007b8be4 1310 p->mcast.id = mem->id;
332dafa2 1311 reqnum = icpSetCacheKey((const cache_key *)fake->key);
d2a6dcba
EB
1312 icpCreateAndSend(ICP_QUERY, 0, url, reqnum, 0,
1313 icpOutgoingConn->fd, p->in_addr, psstate->al);
9865de72 1314 fake->ping_status = PING_WAITING; // TODO: refactor to use PeerSelector::startPingWaiting()
429fdbec 1315 eventAdd("peerCountMcastPeersDone",
62e76326 1316 peerCountMcastPeersDone,
1317 psstate,
99117d82 1318 Config.Timeout.mcast_icp_query / 1000.0, 1);
ccdf4138 1319 p->mcast.flags.counting = true;
429fdbec 1320}
1321
1322static void
1323peerCountMcastPeersDone(void *data)
1324{
cb835136 1325 const auto psstate = static_cast<PeerSelector*>(data);
3a8df7e9
EB
1326 CallBack(psstate->al, [psstate] {
1327 peerCountMcastPeersAbort(psstate);
1328 delete psstate;
1329 });
1330}
1331
1332/// ends counting of multicast ICP replies
1333/// to the ICP query initiated by peerCountMcastPeersCreateAndSend()
1334static void
1335peerCountMcastPeersAbort(PeerSelector * const psstate)
1336{
429fdbec 1337 StoreEntry *fake = psstate->entry;
99117d82 1338
6043e368
AR
1339 if (cbdataReferenceValid(psstate->peerCountMcastPeerXXX)) {
1340 CachePeer *p = (CachePeer *)psstate->peerCountMcastPeerXXX;
ccdf4138 1341 p->mcast.flags.counting = false;
a98bcbee 1342 p->mcast.avg_n_members = Math::doubleAverage(p->mcast.avg_n_members, (double) psstate->ping.n_recv, ++p->mcast.n_times_counted, 10);
a555a85b 1343 debugs(15, DBG_IMPORTANT, "Group " << *p << ": " << psstate->ping.n_recv <<
bf8fe701 1344 " replies, "<< std::setw(4)<< std::setprecision(2) <<
1345 p->mcast.avg_n_members <<" average, RTT " << p->stats.rtt);
99117d82 1346 p->mcast.n_replies_expected = (int) p->mcast.avg_n_members;
1347 }
1348
6043e368 1349 cbdataReferenceDone(psstate->peerCountMcastPeerXXX);
99117d82 1350
2f8abb64 1351 fake->abort(); // sets ENTRY_ABORTED and initiates related cleanup
c43405e7 1352 fake->mem_obj->request = nullptr;
acc5dc4c 1353 fake->unlock("peerCountMcastPeersDone");
429fdbec 1354}
1355
1356static void
ced8def3 1357peerCountHandleIcpReply(CachePeer * p, peer_t, AnyP::ProtocolType proto, void *, void *data)
429fdbec 1358{
cb835136 1359 const auto psstate = static_cast<PeerSelector*>(data);
1a827bc0 1360 StoreEntry *fake = psstate->entry;
ced8def3 1361 assert(fake);
1a827bc0 1362 MemObject *mem = fake->mem_obj;
ced8def3 1363 assert(mem);
1a827bc0 1364 int rtt = tvSubMsec(mem->start_ping, current_time);
0c3d3f65 1365 assert(proto == AnyP::PROTO_ICP);
5db6bf73 1366 ++ psstate->ping.n_recv;
ced8def3 1367 int rtt_av_factor = RTT_AV_FACTOR;
62e76326 1368
d1b63fc8 1369 if (p->options.weighted_roundrobin)
62e76326 1370 rtt_av_factor = RTT_BACKGROUND_AV_FACTOR;
1371
a98bcbee 1372 p->stats.rtt = Math::intAverage(p->stats.rtt, rtt, psstate->ping.n_recv, rtt_av_factor);
429fdbec 1373}
ed7f5615 1374
1375static void
1376neighborDumpPeers(StoreEntry * sentry)
1377{
1378 dump_peers(sentry, Config.peers);
1379}
1380
a369131d 1381void
a3c6762c 1382dump_peer_options(StoreEntry * sentry, CachePeer * p)
a369131d 1383{
20739f57
FC
1384 PackableStream os(*sentry);
1385
cd196bc8 1386 if (p->options.proxy_only)
20739f57 1387 os << " proxy-only";
62e76326 1388
cd196bc8 1389 if (p->options.no_query)
20739f57 1390 os << " no-query";
62e76326 1391
d1b63fc8 1392 if (p->options.background_ping)
20739f57 1393 os << " background-ping";
62e76326 1394
cd196bc8 1395 if (p->options.no_digest)
20739f57 1396 os << " no-digest";
62e76326 1397
cd196bc8 1398 if (p->options.default_parent)
20739f57 1399 os << " default";
62e76326 1400
cd196bc8 1401 if (p->options.roundrobin)
20739f57 1402 os << " round-robin";
62e76326 1403
f7e1d9ce 1404 if (p->options.carp)
20739f57 1405 os << " carp";
354b5a2b 1406
2f1431ea 1407#if USE_AUTH
f7e1d9ce 1408 if (p->options.userhash)
20739f57 1409 os << " userhash";
354b5a2b 1410#endif
f7e1d9ce 1411
354b5a2b 1412 if (p->options.sourcehash)
20739f57 1413 os << " sourcehash";
354b5a2b 1414
d1b63fc8 1415 if (p->options.weighted_roundrobin)
20739f57 1416 os << " weighted-round-robin";
62e76326 1417
cd196bc8 1418 if (p->options.mcast_responder)
20739f57 1419 os << " multicast-responder";
62e76326 1420
8a368316 1421 if (p->options.mcast_siblings)
20739f57 1422 os << " multicast-siblings";
8a368316 1423
678a066c 1424 if (p->weight != 1)
20739f57 1425 os << " weight=" << p->weight;
62e76326 1426
cd196bc8 1427 if (p->options.closest_only)
20739f57 1428 os << " closest-only";
62e76326 1429
dc9d133b 1430#if USE_HTCP
18191440 1431 if (p->options.htcp) {
20739f57 1432 os << " htcp";
ffe95337
FC
1433 std::vector<const char *, PoolingAllocator<const char *> > opts;
1434 if (p->options.htcp_oldsquid)
1435 opts.push_back("oldsquid");
1436 if (p->options.htcp_no_clr)
1437 opts.push_back("no-clr");
1438 if (p->options.htcp_no_purge_clr)
1439 opts.push_back("no-purge-clr");
1440 if (p->options.htcp_only_clr)
1441 opts.push_back("only-clr");
1442 if (p->options.htcp_forward_clr)
1443 opts.push_back("forward-clr");
1444 os << AsList(opts).prefixedBy("=").delimitedBy(",");
18191440 1445 }
dc9d133b 1446#endif
62e76326 1447
cd196bc8 1448 if (p->options.no_netdb_exchange)
20739f57 1449 os << " no-netdb-exchange";
62e76326 1450
9a0a18de 1451#if USE_DELAY_POOLS
cd196bc8 1452 if (p->options.no_delay)
20739f57 1453 os << " no-delay";
95e36d02 1454#endif
62e76326 1455
c68e9c6b 1456 if (p->login)
20739f57 1457 os << " login=" << p->login;
62e76326 1458
a369131d 1459 if (p->mcast.ttl > 0)
20739f57 1460 os << " ttl=" << p->mcast.ttl;
62e76326 1461
0ce8e93b 1462 if (p->connect_timeout_raw > 0)
20739f57 1463 os << " connect-timeout=" << p->connect_timeout_raw;
62e76326 1464
ff9970cc 1465 if (p->connect_fail_limit != PEER_TCP_MAGIC_COUNT)
20739f57 1466 os << " connect-fail-limit=" << p->connect_fail_limit;
ff9970cc 1467
be753325 1468#if USE_CACHE_DIGESTS
62e76326 1469
be753325 1470 if (p->digest_url)
20739f57 1471 os << " digest-url=" << p->digest_url;
62e76326 1472
be753325 1473#endif
62e76326 1474
be753325 1475 if (p->options.allow_miss)
20739f57 1476 os << " allow-miss";
62e76326 1477
b0758e04 1478 if (p->options.no_tproxy)
20739f57 1479 os << " no-tproxy";
b0758e04 1480
be753325 1481 if (p->max_conn > 0)
20739f57
FC
1482 os << " max-conn=" << p->max_conn;
1483
e8dca475 1484 if (p->standby.limit > 0)
20739f57 1485 os << " standby=" << p->standby.limit;
62e76326 1486
be753325 1487 if (p->options.originserver)
20739f57 1488 os << " originserver";
62e76326 1489
be753325 1490 if (p->domain)
20739f57 1491 os << " forceddomain=" << p->domain;
62e76326 1492
26ac0430 1493 if (p->connection_auth == 0)
20739f57 1494 os << " connection-auth=off";
26ac0430 1495 else if (p->connection_auth == 1)
20739f57 1496 os << " connection-auth=on";
26ac0430 1497 else if (p->connection_auth == 2)
20739f57 1498 os << " connection-auth=auto";
d67acb4e 1499
90153ed6 1500 p->secure.dumpCfg(os, "tls-");
20739f57 1501 os << '\n';
a369131d 1502}
1503
ed7f5615 1504static void
2e24d0bf 1505dump_peers(StoreEntry *sentry, CachePeers *peers)
ed7f5615 1506{
cc192b50 1507 char ntoabuf[MAX_IPSTRLEN];
06e6d12a 1508 int i;
62e76326 1509
c3754659 1510 if (!peers) {
62e76326 1511 storeAppendPrintf(sentry, "There are no neighbors installed.\n");
c3754659
EB
1512 return;
1513 }
62e76326 1514
2e24d0bf
EB
1515 for (const auto &peer: *peers) {
1516 const auto e = peer.get();
aee3523a 1517 assert(e->host != nullptr);
62e76326 1518 storeAppendPrintf(sentry, "\n%-11.11s: %s\n",
1519 neighborTypeStr(e),
1520 e->name);
1521 storeAppendPrintf(sentry, "Host : %s/%d/%d\n",
1522 e->host,
1523 e->http_port,
1524 e->icp.port);
1525 storeAppendPrintf(sentry, "Flags :");
1526 dump_peer_options(sentry, e);
1527
5db6bf73 1528 for (i = 0; i < e->n_addresses; ++i) {
62e76326 1529 storeAppendPrintf(sentry, "Address[%d] : %s\n", i,
4dd643d5 1530 e->addresses[i].toStr(ntoabuf,MAX_IPSTRLEN) );
62e76326 1531 }
1532
1533 storeAppendPrintf(sentry, "Status : %s\n",
1534 neighborUp(e) ? "Up" : "Down");
1535 storeAppendPrintf(sentry, "FETCHES : %d\n", e->stats.fetches);
1536 storeAppendPrintf(sentry, "OPEN CONNS : %d\n", e->stats.conn_open);
1537 storeAppendPrintf(sentry, "AVG RTT : %d msec\n", e->stats.rtt);
1538
1539 if (!e->options.no_query) {
1540 storeAppendPrintf(sentry, "LAST QUERY : %8d seconds ago\n",
1541 (int) (squid_curtime - e->stats.last_query));
1542
1543 if (e->stats.last_reply > 0)
1544 storeAppendPrintf(sentry, "LAST REPLY : %8d seconds ago\n",
1545 (int) (squid_curtime - e->stats.last_reply));
1546 else
1547 storeAppendPrintf(sentry, "LAST REPLY : none received\n");
1548
1549 storeAppendPrintf(sentry, "PINGS SENT : %8d\n", e->stats.pings_sent);
1550
1551 storeAppendPrintf(sentry, "PINGS ACKED: %8d %3d%%\n",
1552 e->stats.pings_acked,
a98bcbee 1553 Math::intPercent(e->stats.pings_acked, e->stats.pings_sent));
62e76326 1554 }
1555
a98bcbee 1556 storeAppendPrintf(sentry, "IGNORED : %8d %3d%%\n", e->stats.ignored_replies, Math::intPercent(e->stats.ignored_replies, e->stats.pings_acked));
62e76326 1557
1558 if (!e->options.no_query) {
1559 storeAppendPrintf(sentry, "Histogram of PINGS ACKED:\n");
1994bb24 1560#if USE_HTCP
62e76326 1561
1562 if (e->options.htcp) {
1563 storeAppendPrintf(sentry, "\tMisses\t%8d %3d%%\n",
1564 e->htcp.counts[0],
a98bcbee 1565 Math::intPercent(e->htcp.counts[0], e->stats.pings_acked));
62e76326 1566 storeAppendPrintf(sentry, "\tHits\t%8d %3d%%\n",
1567 e->htcp.counts[1],
a98bcbee 1568 Math::intPercent(e->htcp.counts[1], e->stats.pings_acked));
62e76326 1569 } else {
1994bb24 1570#endif
62e76326 1571
2fefc43c 1572 for (auto op : WholeEnum<icp_opcode>()) {
62e76326 1573 if (e->icp.counts[op] == 0)
1574 continue;
1575
1576 storeAppendPrintf(sentry, " %12.12s : %8d %3d%%\n",
1577 icp_opcode_str[op],
1578 e->icp.counts[op],
a98bcbee 1579 Math::intPercent(e->icp.counts[op], e->stats.pings_acked));
62e76326 1580 }
1581
1994bb24 1582#if USE_HTCP
62e76326 1583
1584 }
1585
1994bb24 1586#endif
62e76326 1587
1588 }
1589
1590 if (e->stats.last_connect_failure) {
1591 storeAppendPrintf(sentry, "Last failed connect() at: %s\n",
20efa1c2 1592 Time::FormatHttpd(e->stats.last_connect_failure));
62e76326 1593 }
1594
a98bcbee 1595 storeAppendPrintf(sentry, "keep-alive ratio: %d%%\n", Math::intPercent(e->stats.n_keepalives_recv, e->stats.n_keepalives_sent));
ed7f5615 1596 }
1597}
86aebcda 1598
1599#if USE_HTCP
1600void
fad2588a 1601neighborsHtcpReply(const cache_key * key, HtcpReplyData * htcp, const Ip::Address &from)
86aebcda 1602{
80d0fe08 1603 StoreEntry *e = Store::Root().findCallbackXXX(key);
aee3523a 1604 MemObject *mem = nullptr;
a3c6762c 1605 CachePeer *p;
399cabec 1606 peer_t ntype = PEER_NONE;
26ac0430
AJ
1607 debugs(15, 6, "neighborsHtcpReply: " <<
1608 (htcp->hit ? "HIT" : "MISS") << " " <<
1609 storeKeyText(key) );
62e76326 1610
aee3523a 1611 if (nullptr != e)
62e76326 1612 mem = e->mem_obj;
1613
399cabec 1614 if ((p = whichPeer(from)))
62e76326 1615 neighborAliveHtcp(p, mem, htcp);
1616
399cabec 1617 /* Does the entry exist? */
aee3523a 1618 if (nullptr == e) {
bf8fe701 1619 debugs(12, 3, "neighyborsHtcpReply: Cache key '" << storeKeyText(key) << "' not found");
62e76326 1620 neighborCountIgnored(p);
1621 return;
399cabec 1622 }
62e76326 1623
399cabec 1624 /* check if someone is already fetching it */
26ac0430 1625 if (EBIT_TEST(e->flags, ENTRY_DISPATCHED)) {
bf8fe701 1626 debugs(15, 3, "neighborsUdpAck: '" << storeKeyText(key) << "' already being fetched.");
62e76326 1627 neighborCountIgnored(p);
1628 return;
399cabec 1629 }
62e76326 1630
aee3523a 1631 if (mem == nullptr) {
bf8fe701 1632 debugs(15, 2, "Ignoring reply for missing mem_obj: " << storeKeyText(key));
62e76326 1633 neighborCountIgnored(p);
1634 return;
399cabec 1635 }
62e76326 1636
26ac0430 1637 if (e->ping_status != PING_WAITING) {
bf8fe701 1638 debugs(15, 2, "neighborsUdpAck: Entry " << storeKeyText(key) << " is not PING_WAITING");
62e76326 1639 neighborCountIgnored(p);
1640 return;
399cabec 1641 }
62e76326 1642
1bfe9ade 1643 if (!e->locked()) {
5bd484b5 1644 // TODO: many entries are unlocked; why is this reported at level 1?
e0236918 1645 debugs(12, DBG_IMPORTANT, "neighborsUdpAck: '" << storeKeyText(key) << "' has no locks");
62e76326 1646 neighborCountIgnored(p);
1647 return;
399cabec 1648 }
62e76326 1649
cb365059 1650 if (!mem->ircb_data) {
d816f28d 1651 debugs(12, DBG_IMPORTANT, "ERROR: Squid BUG: missing HTCP callback data for " << *e);
cb365059
EB
1652 neighborCountIgnored(p);
1653 return;
1654 }
1655
26ac0430 1656 if (p) {
5c51bffb 1657 ntype = neighborType(p, mem->request->url);
62e76326 1658 neighborUpdateRtt(p, mem);
44f2f2e4 1659 }
62e76326 1660
cb365059 1661 if (ignoreMulticastReply(p, mem->ircb_data)) {
62e76326 1662 neighborCountIgnored(p);
1663 return;
399cabec 1664 }
62e76326 1665
bf8fe701 1666 debugs(15, 3, "neighborsHtcpReply: e = " << e);
ccfbe8f4 1667 // TODO: Refactor (ping_reply_callback,ircb_data) to add CodeContext.
0c3d3f65 1668 mem->ping_reply_callback(p, ntype, AnyP::PROTO_HTCP, htcp, mem->ircb_data);
86aebcda 1669}
62e76326 1670
1bd06eff
BR
1671/*
1672 * Send HTCP CLR messages to all peers configured to receive them.
1673 */
4f4fa815 1674void
1ac1d4d3 1675neighborsHtcpClear(StoreEntry * e, HttpRequest * req, const HttpRequestMethod &method, htcp_clr_reason reason)
4f4fa815 1676{
4f4fa815
BR
1677 char buf[128];
1678
2e24d0bf 1679 for (const auto &p: CurrentCachePeers()) {
4f4fa815
BR
1680 if (!p->options.htcp) {
1681 continue;
1682 }
1683 if (p->options.htcp_no_clr) {
1684 continue;
1685 }
1686 if (p->options.htcp_no_purge_clr && reason == HTCP_CLR_PURGE) {
1687 continue;
1688 }
4dd643d5 1689 debugs(15, 3, "neighborsHtcpClear: sending CLR to " << p->in_addr.toUrl(buf, 128));
2e24d0bf 1690 htcpClear(e, req, method, p.get(), reason);
4f4fa815
BR
1691 }
1692}
1693
86aebcda 1694#endif
f53969cc 1695