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