]> git.ipfire.org Git - thirdparty/squid.git/blame - src/neighbors.cc
Fix SQUID_YESNO 'syntax error near unexpected token' (#2117)
[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"
675b8408 23#include "debug/Messages.h"
a553a5a3 24#include "event.h"
eb13c21e 25#include "FwdState.h"
582c2af2 26#include "globals.h"
aa839030 27#include "htcp.h"
528b2c61 28#include "HttpRequest.h"
582c2af2 29#include "icmp/net_db.h"
aa839030 30#include "ICP.h"
f9b6ff6e 31#include "int.h"
582c2af2 32#include "ip/Address.h"
b283ea3f 33#include "ip/tools.h"
714e68b7 34#include "ipcache.h"
528b2c61 35#include "MemObject.h"
582c2af2 36#include "mgr/Registration.h"
afabcc13 37#include "multicast.h"
f0ba2534 38#include "neighbors.h"
602d9612 39#include "NeighborTypeDomainList.h"
e8dca475 40#include "pconn.h"
aa839030 41#include "PeerDigest.h"
e8dca475 42#include "PeerPoolMgr.h"
b24880fe 43#include "PeerSelectState.h"
f206b652 44#include "RequestFlags.h"
4d5904f7 45#include "SquidConfig.h"
a98bcbee 46#include "SquidMath.h"
6ea5959e 47#include "stat.h"
aa839030 48#include "Store.h"
fb548aaf 49#include "store_key_md5.h"
8d03bdb4 50#include "tools.h"
090089c4 51
429fdbec 52/* count mcast group peers every 15 minutes */
53#define MCAST_COUNT_RATE 900
54
cb365059
EB
55bool peerAllowedToUse(const CachePeer *, PeerSelector *);
56static int peerWouldBePinged(const CachePeer *, PeerSelector *);
a3c6762c 57static void neighborAlive(CachePeer *, const MemObject *, const icp_common_t *);
399cabec 58#if USE_HTCP
fad2588a 59static void neighborAliveHtcp(CachePeer *, const MemObject *, const HtcpReplyData *);
399cabec 60#endif
a3c6762c 61static void neighborCountIgnored(CachePeer *);
dd3292c9
EB
62static void peerDnsRefreshCheck(void *);
63static void peerDnsRefreshStart();
b69f7771 64static IPH peerDNSConfigure;
883b9a4d 65static void peerProbeConnect(CachePeer *, const bool reprobeIfBusy = false);
eb406bb7 66static CNCB peerProbeConnectDone;
f5b8bbc4 67static void peerCountMcastPeersDone(void *data);
68static void peerCountMcastPeersStart(void *data);
a3c6762c 69static void peerCountMcastPeersSchedule(CachePeer * p, time_t when);
3a8df7e9
EB
70static void peerCountMcastPeersAbort(PeerSelector *);
71static void peerCountMcastPeersCreateAndSend(CachePeer *p);
b3264694 72static IRCB peerCountHandleIcpReply;
62e76326 73
b7ac5457 74static void neighborIgnoreNonPeer(const Ip::Address &, icp_opcode);
ed7f5615 75static OBJH neighborDumpPeers;
2e24d0bf 76static void dump_peers(StoreEntry *, CachePeers *);
090089c4 77
f45dd259 78static unsigned short echo_port;
30a4f2a8 79
c7a3724d 80static int NLateReplies = 0;
28070024 81
a2c963ae 82const char *
a3c6762c 83neighborTypeStr(const CachePeer * p)
69a71bd7 84{
e102ebda 85 if (p->type == PEER_NONE)
62e76326 86 return "Non-Peer";
87
b69f7771 88 if (p->type == PEER_SIBLING)
62e76326 89 return "Sibling";
90
b69f7771 91 if (p->type == PEER_MULTICAST)
62e76326 92 return "Multicast Group";
93
69a71bd7 94 return "Parent";
95}
96
a3c6762c 97CachePeer *
b7ac5457 98whichPeer(const Ip::Address &from)
090089c4 99{
22e4fa85 100 int j;
62e76326 101
cc192b50 102 debugs(15, 3, "whichPeer: from " << from);
62e76326 103
2e24d0bf 104 for (const auto &p: CurrentCachePeers()) {
5db6bf73 105 for (j = 0; j < p->n_addresses; ++j) {
4dd643d5 106 if (from == p->addresses[j] && from.port() == p->icp.port) {
2e24d0bf 107 return p.get();
62e76326 108 }
109 }
090089c4 110 }
62e76326 111
aee3523a 112 return nullptr;
090089c4 113}
114
701a8572 115peer_t
c8ab5ec6 116neighborType(const CachePeer * p, const AnyP::Uri &url)
24a1003d 117{
62e76326 118
aee3523a 119 const NeighborTypeDomainList *d = nullptr;
62e76326 120
b69f7771 121 for (d = p->typelist; d; d = d->next) {
5c51bffb 122 if (0 == matchDomainName(url.host(), d->domain))
62e76326 123 if (d->type != PEER_NONE)
124 return d->type;
24a1003d 125 }
8e4a911a
AJ
126
127 if (p->type == PEER_MULTICAST && p->options.mcast_siblings)
128 return PEER_SIBLING;
62e76326 129
b69f7771 130 return p->type;
24a1003d 131}
132
2efeb0b7
AJ
133/**
134 * \return Whether it is appropriate to fetch REQUEST from PEER.
7b3bd12c 135 */
2efeb0b7 136bool
cb365059 137peerAllowedToUse(const CachePeer * p, PeerSelector * ps)
090089c4 138{
cb365059
EB
139 assert(ps);
140 HttpRequest *request = ps->request;
aee3523a 141 assert(request != nullptr);
62e76326 142
5c51bffb 143 if (neighborType(p, request->url) == PEER_SIBLING) {
8a368316 144 if (p->type == PEER_MULTICAST && p->options.mcast_siblings &&
450fe1cb 145 (request->flags.noCache || request->flags.refresh || request->flags.loopDetected || request->flags.needValidation))
a555a85b 146 debugs(15, 2, "multicast-siblings optimization match for " << *p << ", " << request->url.authority());
8e4a911a 147
450fe1cb 148 if (request->flags.noCache)
2efeb0b7 149 return false;
62e76326 150
45e5102d 151 if (request->flags.refresh)
2efeb0b7 152 return false;
62e76326 153
450fe1cb 154 if (request->flags.loopDetected)
2efeb0b7 155 return false;
62e76326 156
450fe1cb 157 if (request->flags.needValidation)
2efeb0b7 158 return false;
9689d97c 159 }
62e76326 160
ad46590b 161 // CONNECT requests are proxy requests. Not to be forwarded to origin servers.
a3c6762c 162 // Unless the destination port matches, in which case we MAY perform a 'DIRECT' to this CachePeer.
d9a7bc71 163 if (p->options.originserver && request->method == Http::METHOD_CONNECT && request->url.port() != p->http_port)
2efeb0b7 164 return false;
ad46590b 165
aee3523a 166 if (p->access == nullptr)
f1a5d071 167 return true;
62e76326 168
e94ff527 169 ACLFilledChecklist checklist(p->access, request);
b1c2ea7a 170 checklist.updateAle(ps->al);
cb365059 171 checklist.syncAle(request, nullptr);
06bf5384 172 return checklist.fastCheck().allowed();
090089c4 173}
174
a3c6762c 175/* Return TRUE if it is okay to send an ICP request to this CachePeer. */
62fd6124 176static int
cb365059 177peerWouldBePinged(const CachePeer * p, PeerSelector * ps)
62fd6124 178{
cb365059
EB
179 assert(ps);
180 HttpRequest *request = ps->request;
181
6315602c 182 if (p->icp.port == 0)
62e76326 183 return 0;
184
cd196bc8 185 if (p->options.no_query)
62e76326 186 return 0;
187
cd196bc8 188 if (p->options.mcast_responder)
62e76326 189 return 0;
190
83b381d5 191 if (p->n_addresses == 0)
62e76326 192 return 0;
193
6315602c 194 if (p->options.background_ping && (squid_curtime - p->stats.last_query < Config.backgroundPingRate))
62e76326 195 return 0;
196
62fd6124 197 /* the case below seems strange, but can happen if the
198 * URL host is on the other side of a firewall */
b69f7771 199 if (p->type == PEER_SIBLING)
45e5102d 200 if (!request->flags.hierarchical)
62e76326 201 return 0;
202
cb365059 203 if (!peerAllowedToUse(p, ps))
6315602c
JGD
204 return 0;
205
83b381d5 206 /* Ping dead peers every timeout interval */
207 if (squid_curtime - p->stats.last_query > Config.Timeout.deadPeer)
62e76326 208 return 1;
209
5537c535 210 if (!neighborUp(p))
211 return 0;
62e76326 212
62fd6124 213 return 1;
214}
215
e8dca475
CT
216bool
217peerCanOpenMore(const CachePeer *p)
218{
219 const int effectiveLimit = p->max_conn <= 0 ? Squid_MaxFD : p->max_conn;
220 const int remaining = effectiveLimit - p->stats.conn_open;
221 debugs(15, 7, remaining << '=' << effectiveLimit << '-' << p->stats.conn_open);
222 return remaining > 0;
223}
224
225bool
226peerHasConnAvailable(const CachePeer *p)
227{
228 // Standby connections can be used without opening new connections.
229 const int standbys = p->standby.pool ? p->standby.pool->count() : 0;
230
231 // XXX: Some idle pconns can be used without opening new connections.
232 // Complication: Idle pconns cannot be reused for some requests.
233 const int usableIdles = 0;
234
235 const int available = standbys + usableIdles;
236 debugs(15, 7, available << '=' << standbys << '+' << usableIdles);
237 return available > 0;
238}
239
240void
241peerConnClosed(CachePeer *p)
242{
243 --p->stats.conn_open;
244 if (p->standby.waitingForClose && peerCanOpenMore(p)) {
245 p->standby.waitingForClose = false;
246 PeerPoolMgr::Checkpoint(p->standby.mgr, "conn closed");
247 }
248}
249
a3c6762c 250/* Return TRUE if it is okay to send an HTTP request to this CachePeer. */
8ff4505b 251int
cb365059 252peerHTTPOkay(const CachePeer * p, PeerSelector * ps)
62fd6124 253{
e8dca475
CT
254 if (!peerCanOpenMore(p) && !peerHasConnAvailable(p))
255 return 0;
6315602c 256
cb365059 257 if (!peerAllowedToUse(p, ps))
62e76326 258 return 0;
259
b69f7771 260 if (!neighborUp(p))
62e76326 261 return 0;
262
62fd6124 263 return 1;
264}
265
3c6e634f 266int
cb365059 267neighborsCount(PeerSelector *ps)
090089c4 268{
090089c4 269 int count = 0;
62e76326 270
2e24d0bf
EB
271 for (const auto &p: CurrentCachePeers())
272 if (peerWouldBePinged(p.get(), ps))
5db6bf73 273 ++count;
62e76326 274
bf8fe701 275 debugs(15, 3, "neighborsCount: " << count);
62e76326 276
3c6e634f 277 return count;
278}
090089c4 279
a3c6762c 280CachePeer *
cb365059 281getFirstUpParent(PeerSelector *ps)
090089c4 282{
cb365059
EB
283 assert(ps);
284 HttpRequest *request = ps->request;
285
2e24d0bf 286 for (const auto &peer: CurrentCachePeers()) {
e9eb5900 287 const auto p = peer.get();
2e24d0bf 288
62e76326 289 if (!neighborUp(p))
290 continue;
291
5c51bffb 292 if (neighborType(p, request->url) != PEER_PARENT)
62e76326 293 continue;
294
cb365059 295 if (!peerHTTPOkay(p, ps))
62e76326 296 continue;
297
e9eb5900
EB
298 debugs(15, 3, "returning " << *p);
299 return p;
090089c4 300 }
62e76326 301
e9eb5900
EB
302 debugs(15, 3, "none found");
303 return nullptr;
090089c4 304}
305
a3c6762c 306CachePeer *
cb365059 307getRoundRobinParent(PeerSelector *ps)
48b38d01 308{
cb365059
EB
309 assert(ps);
310 HttpRequest *request = ps->request;
311
aee3523a 312 CachePeer *q = nullptr;
62e76326 313
2e24d0bf
EB
314 for (const auto &peer: CurrentCachePeers()) {
315 const auto p = peer.get();
62e76326 316 if (!p->options.roundrobin)
317 continue;
318
5c51bffb 319 if (neighborType(p, request->url) != PEER_PARENT)
62e76326 320 continue;
321
cb365059 322 if (!peerHTTPOkay(p, ps))
62e76326 323 continue;
324
1d0bef67 325 if (p->weight == 0)
0d5a2006 326 continue;
1d0bef67
AJ
327
328 if (q) {
329 if (p->weight == q->weight) {
330 if (q->rr_count < p->rr_count)
331 continue;
2d3d0b4e 332 } else if ( ((double) q->rr_count / q->weight) < ((double) p->rr_count / p->weight)) {
1d0bef67
AJ
333 continue;
334 }
0d5a2006 335 }
62e76326 336
337 q = p;
48b38d01 338 }
62e76326 339
b69f7771 340 if (q)
5db6bf73 341 ++ q->rr_count;
62e76326 342
a555a85b 343 debugs(15, 3, "returning " << RawPointer(q).orNil());
62e76326 344
b69f7771 345 return q;
48b38d01 346}
347
a3c6762c 348CachePeer *
cb365059 349getWeightedRoundRobinParent(PeerSelector *ps)
d1b63fc8 350{
cb365059
EB
351 assert(ps);
352 HttpRequest *request = ps->request;
353
aee3523a 354 CachePeer *q = nullptr;
d1b63fc8 355 int weighted_rtt;
62e76326 356
2e24d0bf
EB
357 for (const auto &peer: CurrentCachePeers()) {
358 const auto p = peer.get();
359
62e76326 360 if (!p->options.weighted_roundrobin)
361 continue;
362
5c51bffb 363 if (neighborType(p, request->url) != PEER_PARENT)
62e76326 364 continue;
365
cb365059 366 if (!peerHTTPOkay(p, ps))
62e76326 367 continue;
368
369 if (q && q->rr_count < p->rr_count)
370 continue;
371
372 q = p;
d1b63fc8 373 }
62e76326 374
d1b63fc8 375 if (q && q->rr_count > 1000000)
2e24d0bf 376 for (const auto &p: CurrentCachePeers()) {
62e76326 377 if (!p->options.weighted_roundrobin)
378 continue;
379
2e24d0bf 380 if (neighborType(p.get(), request->url) != PEER_PARENT)
62e76326 381 continue;
382
383 p->rr_count = 0;
384 }
385
d1b63fc8 386 if (q) {
62e76326 387 weighted_rtt = (q->stats.rtt - q->basetime) / q->weight;
d1b63fc8 388
62e76326 389 if (weighted_rtt < 1)
390 weighted_rtt = 1;
391
392 q->rr_count += weighted_rtt;
393
4a7a3d56 394 debugs(15, 3, "getWeightedRoundRobinParent: weighted_rtt " << weighted_rtt);
d1b63fc8 395 }
62e76326 396
a555a85b 397 debugs(15, 3, "returning " << RawPointer(q).orNil());
d1b63fc8 398 return q;
399}
400
32a47e3e
AJ
401/**
402 * This gets called every 5 minutes to clear the round-robin counter.
403 * The exact timing is an arbitrary default, set on estimate timing of a
404 * large number of requests in a high-performance environment during the
405 * period. The larger the number of requests between cycled resets the
406 * more balanced the operations.
407 *
9837567d
AJ
408 * \param data unused
409 *
410 * TODO: Make the reset timing a selectable parameter in squid.conf
32a47e3e 411 */
26ac0430 412static void
32a47e3e
AJ
413peerClearRRLoop(void *data)
414{
415 peerClearRR();
416 eventAdd("peerClearRR", peerClearRRLoop, data, 5 * 60.0, 0);
417}
418
419/**
a3c6762c 420 * This gets called on startup and restart to kick off the CachePeer round-robin
32a47e3e
AJ
421 * maintenance event. It ensures that no matter how many times its called
422 * no more than one event is scheduled.
423 */
82056f1e 424void
32a47e3e 425peerClearRRStart(void)
82056f1e 426{
7a3f0061 427 static bool event_added = false;
32a47e3e 428 if (!event_added) {
aee3523a 429 peerClearRRLoop(nullptr);
7a3f0061 430 event_added=true;
32a47e3e
AJ
431 }
432}
62e76326 433
32a47e3e
AJ
434/**
435 * Called whenever the round-robin counters need to be reset to a sane state.
436 * So far those times are:
d85b8894 437 * - On startup and reconfigure - to set the counters to sane initial settings.
a3c6762c 438 * - When a CachePeer has revived from dead, to prevent the revived CachePeer being
32a47e3e
AJ
439 * flooded with requests which it has 'missed' during the down period.
440 */
441void
442peerClearRR()
443{
2e24d0bf 444 for (const auto &p: CurrentCachePeers())
2da06da0 445 p->rr_count = 1;
32a47e3e 446}
62e76326 447
022dbabd 448void
a3c6762c 449peerAlive(CachePeer *p)
32a47e3e
AJ
450{
451 if (p->stats.logged_state == PEER_DEAD && p->tcp_up) {
a555a85b 452 debugs(15, DBG_IMPORTANT, "Detected REVIVED " << neighborTypeStr(p) << ": " << *p);
32a47e3e
AJ
453 p->stats.logged_state = PEER_ALIVE;
454 peerClearRR();
e8dca475
CT
455 if (p->standby.mgr.valid())
456 PeerPoolMgr::Checkpoint(p->standby.mgr, "revived peer");
32a47e3e 457 }
62e76326 458
32a47e3e
AJ
459 p->stats.last_reply = squid_curtime;
460 p->stats.probe_start = 0;
022dbabd
EB
461
462 // TODO: Remove or explain how we could detect an alive peer without IP addresses
463 if (!p->n_addresses)
464 ipcache_nbgethostbyname(p->host, peerDNSConfigure, p);
82056f1e 465}
466
a3c6762c 467CachePeer *
cb365059 468getDefaultParent(PeerSelector *ps)
5269d0bd 469{
cb365059
EB
470 assert(ps);
471 HttpRequest *request = ps->request;
472
2e24d0bf
EB
473 for (const auto &peer: CurrentCachePeers()) {
474 const auto p = peer.get();
62e76326 475
5c51bffb 476 if (neighborType(p, request->url) != PEER_PARENT)
62e76326 477 continue;
478
479 if (!p->options.default_parent)
480 continue;
481
cb365059 482 if (!peerHTTPOkay(p, ps))
62e76326 483 continue;
484
a555a85b 485 debugs(15, 3, "returning " << *p);
62e76326 486
487 return p;
5269d0bd 488 }
62e76326 489
a555a85b
AR
490 // TODO: Refactor similar get*() functions to use our return/reporting style
491 debugs(15, 3, "none found");
aee3523a 492 return nullptr;
5269d0bd 493}
494
5f5e883f
FC
495static void
496neighborsRegisterWithCacheManager()
497{
8822ebee 498 Mgr::RegisterAction("server_list",
d9fc6862
A
499 "Peer Cache Statistics",
500 neighborDumpPeers, 0, 1);
5f5e883f
FC
501}
502
b8d8561b 503void
a86029b9 504neighbors_init(void)
090089c4 505{
aee3523a 506 struct servent *sep = nullptr;
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
9ca56f05 539 sep = getservbyname("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