2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 12 Internet Cache Protocol (ICP) */
12 \defgroup ServerProtocolICPInternal2 ICPv2 Internals
13 \ingroup ServerProtocolICPAPI
17 #include "AccessLogEntry.h"
19 #include "acl/FilledChecklist.h"
20 #include "base/AsyncCallbacks.h"
21 #include "client_db.h"
23 #include "comm/Connection.h"
24 #include "comm/Loops.h"
26 #include "HttpRequest.h"
27 #include "icmp/net_db.h"
29 #include "ip/Address.h"
31 #include "ipc/StartListening.h"
34 #include "multicast.h"
35 #include "neighbors.h"
38 #include "SquidConfig.h"
39 #include "StatCounters.h"
41 #include "store_key_md5.h"
47 /// a delayed icpUdpSend() call
48 class DelayedUdpSend
{
50 Ip::Address address
; ///< remote peer (which may not be a cache_peer)
51 icp_common_t
*msg
= nullptr; ///< ICP message with network byte order fields
52 DelayedUdpSend
*next
= nullptr; ///< invasive FIFO queue of delayed ICP messages
53 AccessLogEntryPointer ale
; ///< sender's master transaction summary
54 struct timeval queue_time
= {}; ///< queuing timestamp
57 static void icpIncomingConnectionOpened(Ipc::StartListeningAnswer
&);
59 /// \ingroup ServerProtocolICPInternal2
60 static void icpLogIcp(const Ip::Address
&, const LogTags_ot
, int, const char *, const int, AccessLogEntryPointer
&);
62 /// \ingroup ServerProtocolICPInternal2
63 static void icpHandleIcpV2(int, Ip::Address
&, char *, int);
65 /// \ingroup ServerProtocolICPInternal2
66 static void icpCount(void *, int, size_t, int);
68 static LogTags_ot
icpLogFromICPCode(icp_opcode
);
70 static int icpUdpSend(int fd
, const Ip::Address
&to
, icp_common_t
* msg
, int delay
, AccessLogEntryPointer al
);
73 icpSyncAle(AccessLogEntryPointer
&al
, const Ip::Address
&caddr
, const char *url
, int len
, int delay
)
76 al
= new AccessLogEntry();
77 al
->icp
.opcode
= ICP_QUERY
;
78 al
->cache
.caddr
= caddr
;
80 al
->setVirginUrlForMissingRequest(al
->url
);
81 // XXX: move to use icp.clientReply instead
82 al
->http
.clientReplySz
.payloadData
= len
;
83 al
->cache
.start_time
= current_time
;
84 al
->cache
.start_time
.tv_sec
-= delay
;
85 al
->cache
.trTime
.tv_sec
= delay
;
86 al
->cache
.trTime
.tv_usec
= 0;
90 \ingroup ServerProtocolICPInternal2
91 * IcpQueueHead is global so comm_incoming() knows whether or not
92 * to call icpUdpSendQueue.
94 static DelayedUdpSend
*IcpQueueHead
= nullptr;
95 /// \ingroup ServerProtocolICPInternal2
96 static DelayedUdpSend
*IcpQueueTail
= nullptr;
98 /// \ingroup ServerProtocolICPInternal2
99 Comm::ConnectionPointer icpIncomingConn
= nullptr;
100 /// \ingroup ServerProtocolICPInternal2
101 Comm::ConnectionPointer icpOutgoingConn
= nullptr;
104 icp_common_t::icp_common_t() :
105 opcode(ICP_INVALID
), version(0), length(0), reqnum(0),
106 flags(0), pad(0), shostid(0)
109 icp_common_t::icp_common_t(char *buf
, unsigned int len
) :
110 opcode(ICP_INVALID
), version(0), reqnum(0), flags(0), pad(0), shostid(0)
112 if (len
< sizeof(icp_common_t
)) {
113 /* mark as invalid */
118 memcpy(this, buf
, sizeof(icp_common_t
));
120 * Convert network order sensitive fields
122 length
= ntohs(length
);
123 reqnum
= ntohl(reqnum
);
124 flags
= ntohl(flags
);
129 icp_common_t::getOpCode() const
131 if (opcode
> static_cast<char>(icp_opcode::ICP_END
))
134 return static_cast<icp_opcode
>(opcode
);
139 ICPState::ICPState(icp_common_t
&aHeader
, HttpRequest
*aRequest
):
145 HTTPMSGLOCK(request
);
148 ICPState::~ICPState()
151 HTTPMSGUNLOCK(request
);
155 ICPState::isHit() const
157 const auto e
= storeGetPublic(url
, Http::METHOD_GET
);
159 const auto hit
= e
&& confirmAndPrepHit(*e
);
162 e
->abandon(__func__
);
168 ICPState::confirmAndPrepHit(const StoreEntry
&e
) const
170 if (!e
.validToSend())
173 if (e
.hittingRequiresCollapsing() && !startCollapsingOn(e
, false))
176 if (!Config
.onoff
.icp_hit_stale
&& !didCollapse
&& refreshCheckICP(&e
, request
))
183 ICPState::loggingTags() const
185 // calling icpSyncAle(LOG_TAG_NONE) here would not change cache.code
187 al
= new AccessLogEntry();
188 return &al
->cache
.code
;
192 ICPState::fillChecklist(ACLFilledChecklist
&checklist
) const
194 checklist
.setRequest(request
);
195 icpSyncAle(al
, from
, url
, 0, 0);
203 /// \ingroup ServerProtocolICPInternal2
204 class ICP2State
: public ICPState
208 ICP2State(icp_common_t
& aHeader
, HttpRequest
*aRequest
):
209 ICPState(aHeader
, aRequest
),rtt(0),src_rtt(0),flags(0) {}
211 ~ICP2State() override
;
218 ICP2State::~ICP2State()
223 /// updates ALE (if any) and logs the transaction (if needed)
225 icpLogIcp(const Ip::Address
&caddr
, const LogTags_ot logcode
, const int len
, const char *url
, int delay
, AccessLogEntry::Pointer
&al
)
227 assert(logcode
!= LOG_TAG_NONE
);
229 // Optimization: No premature (ALE creation in) icpSyncAle().
231 icpSyncAle(al
, caddr
, url
, len
, delay
);
232 al
->cache
.code
.update(logcode
);
235 if (logcode
== LOG_ICP_QUERY
)
236 return; // we never log queries
238 if (!Config
.onoff
.log_udp
) {
239 clientdbUpdate(caddr
, al
? al
->cache
.code
: LogTags(logcode
), AnyP::PROTO_ICP
, len
);
244 // The above attempt to optimize ALE creation has failed. We do need it.
245 icpSyncAle(al
, caddr
, url
, len
, delay
);
246 al
->cache
.code
.update(logcode
);
248 clientdbUpdate(caddr
, al
->cache
.code
, AnyP::PROTO_ICP
, len
);
249 accessLogLog(al
, nullptr);
252 /// \ingroup ServerProtocolICPInternal2
254 icpUdpSendQueue(int fd
, void *)
258 while ((q
= IcpQueueHead
) != nullptr) {
259 int delay
= tvSubUsec(q
->queue_time
, current_time
);
260 /* increment delay to prevent looping */
261 const int x
= icpUdpSend(fd
, q
->address
, q
->msg
, ++delay
, q
->ale
);
262 IcpQueueHead
= q
->next
;
271 icp_common_t::CreateMessage(
279 icp_common_t
*headerp
= nullptr;
280 char *urloffset
= nullptr;
282 buf_len
= sizeof(icp_common_t
) + strlen(url
) + 1;
284 if (opcode
== ICP_QUERY
)
285 buf_len
+= sizeof(uint32_t);
287 buf
= (char *) xcalloc(buf_len
, 1);
289 headerp
= (icp_common_t
*) (void *) buf
;
291 headerp
->opcode
= (char) opcode
;
293 headerp
->version
= ICP_VERSION_CURRENT
;
295 headerp
->length
= (uint16_t) htons(buf_len
);
297 headerp
->reqnum
= htonl(reqnum
);
299 headerp
->flags
= htonl(flags
);
301 headerp
->pad
= htonl(pad
);
303 headerp
->shostid
= 0;
305 urloffset
= buf
+ sizeof(icp_common_t
);
307 if (opcode
== ICP_QUERY
)
308 urloffset
+= sizeof(uint32_t);
310 memcpy(urloffset
, url
, strlen(url
));
312 return (icp_common_t
*)buf
;
315 // TODO: Move retries to icpCreateAndSend(); the other caller does not retry.
316 /// writes the given UDP msg to the socket; queues a retry on the first failure
317 /// \returns a negative number on failures
320 const Ip::Address
&to
,
323 AccessLogEntryPointer al
)
327 len
= (int) ntohs(msg
->length
);
328 debugs(12, 5, "icpUdpSend: FD " << fd
<< " sending " <<
329 icp_opcode_str
[msg
->opcode
] << ", " << len
<< " bytes to " << to
);
331 x
= comm_udp_sendto(fd
, to
, msg
, len
);
334 /* successfully written */
335 const auto logcode
= icpLogFromICPCode(static_cast<icp_opcode
>(msg
->opcode
));
336 icpLogIcp(to
, logcode
, len
, (char *) (msg
+ 1), delay
, al
);
337 icpCount(msg
, SENT
, (size_t) len
, delay
);
339 } else if (0 == delay
) {
340 /* send failed, but queue it */
341 const auto queue
= new DelayedUdpSend();
344 queue
->queue_time
= current_time
;
347 if (IcpQueueHead
== nullptr) {
348 IcpQueueHead
= queue
;
349 IcpQueueTail
= queue
;
350 } else if (IcpQueueTail
== IcpQueueHead
) {
351 IcpQueueTail
= queue
;
352 IcpQueueHead
->next
= queue
;
354 IcpQueueTail
->next
= queue
;
355 IcpQueueTail
= queue
;
358 Comm::SetSelect(fd
, COMM_SELECT_WRITE
, icpUdpSendQueue
, nullptr, 0);
359 ++statCounter
.icp
.replies_queued
;
362 // XXX: safe_free(msg)
363 ++statCounter
.icp
.replies_dropped
;
370 * This routine selects an ICP opcode for ICP misses.
372 \retval ICP_ERR no opcode selected here
373 \retval ICP_MISS_NOFETCH store is rebuilding, no fetch is possible yet
378 /* if store is rebuilding, return a UDP_MISS_NOFETCH */
380 if ((StoreController::store_dirs_rebuilding
&& opt_reload_hit_only
) ||
381 hit_only_mode_until
> squid_curtime
) {
382 return ICP_MISS_NOFETCH
;
389 icpLogFromICPCode(icp_opcode opcode
)
391 if (opcode
== ICP_ERR
)
392 return LOG_UDP_INVALID
;
394 if (opcode
== ICP_DENIED
)
395 return LOG_UDP_DENIED
;
397 if (opcode
== ICP_HIT
)
400 if (opcode
== ICP_MISS
)
403 if (opcode
== ICP_MISS_NOFETCH
)
404 return LOG_UDP_MISS_NOFETCH
;
406 if (opcode
== ICP_DECHO
)
407 return LOG_ICP_QUERY
;
409 if (opcode
== ICP_QUERY
)
410 return LOG_ICP_QUERY
;
412 fatal("expected ICP opcode\n");
414 return LOG_UDP_INVALID
;
418 icpCreateAndSend(icp_opcode opcode
, int flags
, char const *url
, int reqnum
, int pad
, int fd
, const Ip::Address
&from
, AccessLogEntry::Pointer al
)
420 // update potentially shared ALE ASAP; the ICP query itself may be delayed
422 al
->cache
.code
.update(icpLogFromICPCode(opcode
));
423 icp_common_t
*reply
= icp_common_t::CreateMessage(opcode
, flags
, url
, reqnum
, pad
);
424 icpUdpSend(fd
, from
, reply
, 0, al
);
428 icpDenyAccess(Ip::Address
&from
, char *url
, int reqnum
, int fd
)
430 if (clientdbCutoffDenied(from
)) {
432 * count this DENIED query in the clientdb, even though
433 * we're not sending an ICP reply...
435 clientdbUpdate(from
, LogTags(LOG_UDP_DENIED
), AnyP::PROTO_ICP
, 0);
437 icpCreateAndSend(ICP_DENIED
, 0, url
, reqnum
, 0, fd
, from
, nullptr);
442 icpAccessAllowed(Ip::Address
&from
, HttpRequest
* icp_request
)
444 if (!Config
.accessList
.icp
) {
445 debugs(12, 2, "Access Denied due to lack of ICP access rules.");
449 ACLFilledChecklist
checklist(Config
.accessList
.icp
, icp_request
);
450 checklist
.src_addr
= from
;
451 checklist
.my_addr
.setNoAddr();
452 const auto &answer
= checklist
.fastCheck();
453 if (answer
.allowed())
456 debugs(12, 2, "Access Denied for " << from
<< " by " << answer
.lastCheckDescription() << ".");
461 icpGetRequest(char *url
, int reqnum
, int fd
, Ip::Address
&from
)
463 if (strpbrk(url
, w_space
)) {
464 url
= rfc1738_escape(url
);
465 icpCreateAndSend(ICP_ERR
, 0, rfc1738_escape(url
), reqnum
, 0, fd
, from
, nullptr);
469 const auto mx
= MasterXaction::MakePortless
<XactionInitiator::initIcp
>();
470 auto *result
= HttpRequest::FromUrlXXX(url
, mx
);
472 icpCreateAndSend(ICP_ERR
, 0, url
, reqnum
, 0, fd
, from
, nullptr);
479 doV2Query(int fd
, Ip::Address
&from
, char *buf
, icp_common_t header
)
484 /* We have a valid packet */
485 char *url
= buf
+ sizeof(icp_common_t
) + sizeof(uint32_t);
486 HttpRequest
*icp_request
= icpGetRequest(url
, header
.reqnum
, fd
, from
);
491 HTTPMSGLOCK(icp_request
);
493 if (!icpAccessAllowed(from
, icp_request
)) {
494 icpDenyAccess(from
, url
, header
.reqnum
, fd
);
495 HTTPMSGUNLOCK(icp_request
);
499 if (header
.flags
& ICP_FLAG_SRC_RTT
) {
500 rtt
= netdbHostRtt(icp_request
->url
.host());
501 int hops
= netdbHostHops(icp_request
->url
.host());
502 src_rtt
= ((hops
& 0xFFFF) << 16) | (rtt
& 0xFFFF);
505 flags
|= ICP_FLAG_SRC_RTT
;
507 #endif /* USE_ICMP */
509 /* The peer is allowed to use this cache */
510 ICP2State
state(header
, icp_request
);
513 state
.url
= xstrdup(url
);
516 state
.src_rtt
= src_rtt
;
518 icp_opcode codeToSend
;
521 codeToSend
= ICP_HIT
;
524 if (Config
.onoff
.test_reachability
&& state
.rtt
== 0) {
525 if ((state
.rtt
= netdbHostRtt(state
.request
->url
.host())) == 0)
526 netdbPingSite(state
.request
->url
.host());
528 #endif /* USE_ICMP */
530 if (icpGetCommonOpcode() != ICP_ERR
)
531 codeToSend
= icpGetCommonOpcode();
532 else if (Config
.onoff
.test_reachability
&& rtt
== 0)
533 codeToSend
= ICP_MISS_NOFETCH
;
535 codeToSend
= ICP_MISS
;
538 icpCreateAndSend(codeToSend
, flags
, url
, header
.reqnum
, src_rtt
, fd
, from
, state
.al
);
540 HTTPMSGUNLOCK(icp_request
);
544 icp_common_t::handleReply(char *buf
, Ip::Address
&from
)
546 if (neighbors_do_private_keys
&& reqnum
== 0) {
547 debugs(12, DBG_CRITICAL
, "icpHandleIcpV2: Neighbor " << from
<< " returned reqnum = 0");
548 debugs(12, DBG_CRITICAL
, "icpHandleIcpV2: Disabling use of private keys");
549 neighbors_do_private_keys
= 0;
552 char *url
= buf
+ sizeof(icp_common_t
);
553 debugs(12, 3, "icpHandleIcpV2: " << icp_opcode_str
[opcode
] << " from " << from
<< " for '" << url
<< "'");
555 const cache_key
*key
= icpGetCacheKey(url
, (int) reqnum
);
556 /* call neighborsUdpAck even if ping_status != PING_WAITING */
557 neighborsUdpAck(key
, this, from
);
561 icpHandleIcpV2(int fd
, Ip::Address
&from
, char *buf
, int len
)
564 debugs(12, 3, "icpHandleIcpV2: ICP message is too small");
568 icp_common_t
header(buf
, len
);
570 * Length field should match the number of bytes read
573 if (len
!= header
.length
) {
574 debugs(12, 3, "icpHandleIcpV2: ICP message is too small");
578 debugs(12, 5, "OPCODE " << icp_opcode_str
[header
.getOpCode()] << '=' << uint8_t(header
.opcode
));
580 switch (header
.opcode
) {
583 /* We have a valid packet */
584 doV2Query(fd
, from
, buf
, header
);
595 case ICP_MISS_NOFETCH
:
596 header
.handleReply(buf
, from
);
605 debugs(12, DBG_CRITICAL
, "ERROR: icpHandleIcpV2: Unknown opcode: " << header
.opcode
<< " from " << from
);
612 icpHandleUdp(int sock
, void *)
614 int *N
= &incoming_sockets_accepted
;
617 LOCAL_ARRAY(char, buf
, SQUID_UDP_SO_RCVBUF
);
620 int max
= INCOMING_UDP_MAX
;
621 Comm::SetSelect(sock
, COMM_SELECT_READ
, icpHandleUdp
, nullptr, 0);
625 len
= comm_udp_recvfrom(sock
,
627 SQUID_UDP_SO_RCVBUF
- 1,
636 if (ignoreErrno(xerrno
))
640 /* Some Linux systems seem to set the FD for reading and then
641 * return ECONNREFUSED when sendto() fails and generates an ICMP
642 * port unreachable message. */
643 /* or maybe an EHOSTUNREACH "No route to host" message */
644 if (xerrno
!= ECONNREFUSED
&& xerrno
!= EHOSTUNREACH
)
646 debugs(50, DBG_IMPORTANT
, "icpHandleUdp: FD " << sock
<< " recvfrom: " << xstrerr(xerrno
));
652 icpCount(buf
, RECV
, (size_t) len
, 0);
654 debugs(12, 4, "icpHandleUdp: FD " << sock
<< ": received " <<
655 (unsigned long int)len
<< " bytes from " << from
);
657 if ((size_t) len
< sizeof(icp_common_t
)) {
658 debugs(12, 4, "icpHandleUdp: Ignoring too-small UDP packet");
662 icp_version
= (int) buf
[1]; /* cheat! */
664 if (icpOutgoingConn
->local
== from
)
665 // ignore ICP packets which loop back (multicast usually)
666 debugs(12, 4, "icpHandleUdp: Ignoring UDP packet sent by myself");
667 else if (icp_version
== ICP_VERSION_2
)
668 icpHandleIcpV2(sock
, from
, buf
, len
);
669 else if (icp_version
== ICP_VERSION_3
)
670 icpHandleIcpV3(sock
, from
, buf
, len
);
672 debugs(12, DBG_IMPORTANT
, "WARNING: Unused ICP version " << icp_version
<<
673 " received from " << from
);
682 if ((port
= Config
.Port
.icp
) <= 0)
685 icpIncomingConn
= new Comm::Connection
;
686 icpIncomingConn
->local
= Config
.Addrs
.udp_incoming
;
687 icpIncomingConn
->local
.port(port
);
689 if (!Ip::EnableIpv6
&& !icpIncomingConn
->local
.setIPv4()) {
690 debugs(12, DBG_CRITICAL
, "ERROR: IPv6 is disabled. " << icpIncomingConn
->local
<< " is not an IPv4 address.");
691 fatal("ICP port cannot be opened.");
693 /* split-stack for now requires default IPv4-only ICP */
694 if (Ip::EnableIpv6
&IPV6_SPECIAL_SPLITSTACK
&& icpIncomingConn
->local
.isAnyAddr()) {
695 icpIncomingConn
->local
.setIPv4();
698 auto call
= asyncCallbackFun(12, 2, icpIncomingConnectionOpened
);
699 Ipc::StartListening(SOCK_DGRAM
,
702 Ipc::fdnInIcpSocket
, call
);
704 if ( !Config
.Addrs
.udp_outgoing
.isNoAddr() ) {
705 icpOutgoingConn
= new Comm::Connection
;
706 icpOutgoingConn
->local
= Config
.Addrs
.udp_outgoing
;
707 icpOutgoingConn
->local
.port(port
);
709 if (!Ip::EnableIpv6
&& !icpOutgoingConn
->local
.setIPv4()) {
710 debugs(49, DBG_CRITICAL
, "ERROR: IPv6 is disabled. " << icpOutgoingConn
->local
<< " is not an IPv4 address.");
711 fatal("ICP port cannot be opened.");
713 /* split-stack for now requires default IPv4-only ICP */
714 if (Ip::EnableIpv6
&IPV6_SPECIAL_SPLITSTACK
&& icpOutgoingConn
->local
.isAnyAddr()) {
715 icpOutgoingConn
->local
.setIPv4();
719 comm_open_listener(SOCK_DGRAM
, IPPROTO_UDP
, icpOutgoingConn
, "Outgoing ICP Port");
722 if (!Comm::IsConnOpen(icpOutgoingConn
))
723 fatal("Cannot open Outgoing ICP Port");
725 debugs(12, DBG_CRITICAL
, "Sending ICP messages from " << icpOutgoingConn
->local
);
727 Comm::SetSelect(icpOutgoingConn
->fd
, COMM_SELECT_READ
, icpHandleUdp
, nullptr, 0);
728 fd_note(icpOutgoingConn
->fd
, "Outgoing ICP socket");
733 icpIncomingConnectionOpened(Ipc::StartListeningAnswer
&answer
)
735 const auto &conn
= answer
.conn
;
737 if (!Comm::IsConnOpen(conn
))
738 fatal("Cannot open ICP Port");
740 Comm::SetSelect(conn
->fd
, COMM_SELECT_READ
, icpHandleUdp
, nullptr, 0);
742 for (const wordlist
*s
= Config
.mcast_group_list
; s
; s
= s
->next
)
743 ipcache_nbgethostbyname(s
->key
, mcastJoinGroups
, nullptr); // XXX: pass the conn for mcastJoinGroups usage.
745 debugs(12, DBG_IMPORTANT
, "Accepting ICP messages on " << conn
->local
);
747 fd_note(conn
->fd
, "Incoming ICP port");
749 if (Config
.Addrs
.udp_outgoing
.isNoAddr()) {
750 icpOutgoingConn
= conn
;
751 debugs(12, DBG_IMPORTANT
, "Sending ICP messages from " << icpOutgoingConn
->local
);
756 * icpConnectionShutdown only closes the 'in' socket if it is
757 * different than the 'out' socket.
760 icpConnectionShutdown(void)
762 if (!Comm::IsConnOpen(icpIncomingConn
))
765 debugs(12, DBG_IMPORTANT
, "Stop receiving ICP on " << icpIncomingConn
->local
);
767 /** Release the 'in' socket for lazy closure.
768 * in and out sockets may be sharing one same FD.
769 * This prevents this function from executing repeatedly.
771 icpIncomingConn
= nullptr;
774 * Normally we only write to the outgoing ICP socket, but
775 * we also have a read handler there to catch messages sent
776 * to that specific interface. During shutdown, we must
777 * disable reading on the outgoing socket.
779 assert(Comm::IsConnOpen(icpOutgoingConn
));
781 Comm::SetSelect(icpOutgoingConn
->fd
, COMM_SELECT_READ
, nullptr, nullptr, 0);
787 icpConnectionShutdown();
789 if (icpOutgoingConn
!= nullptr) {
790 debugs(12, DBG_IMPORTANT
, "Stop sending ICP from " << icpOutgoingConn
->local
);
791 icpOutgoingConn
= nullptr;
796 icpCount(void *buf
, int which
, size_t len
, int delay
)
798 icp_common_t
*icp
= (icp_common_t
*) buf
;
800 if (len
< sizeof(*icp
))
804 ++statCounter
.icp
.pkts_sent
;
805 statCounter
.icp
.kbytes_sent
+= len
;
807 if (ICP_QUERY
== icp
->opcode
) {
808 ++statCounter
.icp
.queries_sent
;
809 statCounter
.icp
.q_kbytes_sent
+= len
;
811 ++statCounter
.icp
.replies_sent
;
812 statCounter
.icp
.r_kbytes_sent
+= len
;
813 /* this is the sent-reply service time */
814 statCounter
.icp
.replySvcTime
.count(delay
);
817 if (ICP_HIT
== icp
->opcode
)
818 ++statCounter
.icp
.hits_sent
;
819 } else if (RECV
== which
) {
820 ++statCounter
.icp
.pkts_recv
;
821 statCounter
.icp
.kbytes_recv
+= len
;
823 if (ICP_QUERY
== icp
->opcode
) {
824 ++statCounter
.icp
.queries_recv
;
825 statCounter
.icp
.q_kbytes_recv
+= len
;
827 ++statCounter
.icp
.replies_recv
;
828 statCounter
.icp
.r_kbytes_recv
+= len
;
829 /* statCounter.icp.querySvcTime set in clientUpdateCounters */
832 if (ICP_HIT
== icp
->opcode
)
833 ++statCounter
.icp
.hits_recv
;
837 #define N_QUERIED_KEYS 8192
838 #define N_QUERIED_KEYS_MASK 8191
839 static cache_key queried_keys
[N_QUERIED_KEYS
][SQUID_MD5_DIGEST_LENGTH
];
842 icpSetCacheKey(const cache_key
* key
)
844 static int reqnum
= 0;
849 storeKeyCopy(queried_keys
[reqnum
& N_QUERIED_KEYS_MASK
], key
);
855 icpGetCacheKey(const char *url
, int reqnum
)
857 if (neighbors_do_private_keys
&& reqnum
)
858 return queried_keys
[reqnum
& N_QUERIED_KEYS_MASK
];
860 return storeKeyPublic(url
, Http::METHOD_GET
);