]> git.ipfire.org Git - thirdparty/squid.git/blame - src/icp_v2.cc
force direct for REQ_LOOPDETECT flag
[thirdparty/squid.git] / src / icp_v2.cc
CommitLineData
194dd3b8 1
7a2f978b 2#include "squid.h"
3
17b6e784 4static void icpLogIcp(struct in_addr, log_type, int, const char *, int);
7a2f978b 5static void icpHandleIcpV2(int, struct sockaddr_in, char *, int);
17b6e784 6static PF icpUdpSendQueue;
071a3ae7 7static void icpCount(void *, int, size_t, int);
17b6e784 8
9static icpUdpData *UdpQueueHead = NULL;
10static icpUdpData *UdpQueueTail = NULL;
7a2f978b 11
12static void
17b6e784 13icpLogIcp(struct in_addr caddr, log_type logcode, int len, const char *url, int delay)
7a2f978b 14{
7a2f978b 15 AccessLogEntry al;
96c617da 16 if (LOG_TAG_NONE == logcode)
17 return;
071a3ae7 18 if (LOG_ICP_QUERY == logcode)
19 return;
17b6e784 20 clientdbUpdate(caddr, logcode, PROTO_ICP, len);
7a2f978b 21 if (!Config.onoff.log_udp)
22 return;
17b6e784 23 memset(&al, '\0', sizeof(al));
27cd7235 24 al.icp.opcode = ICP_QUERY;
7a2f978b 25 al.url = url;
17b6e784 26 al.cache.caddr = caddr;
27 al.cache.size = len;
28 al.cache.code = logcode;
29 al.cache.msec = delay;
7a2f978b 30 accessLogLog(&al);
31}
32
17b6e784 33static void
34icpUdpSendQueue(int fd, void *unused)
7a2f978b 35{
17b6e784 36 icpUdpData *q;
7a2f978b 37 int x;
17b6e784 38 int delay;
39 while ((q = UdpQueueHead) != NULL) {
40 delay = tvSubUsec(q->queue_time, current_time);
41 /* increment delay to prevent looping */
42 x = icpUdpSend(fd, &q->address, q->msg, q->logcode, ++delay);
43 UdpQueueHead = q->next;
44 safe_free(q);
45 if (x < 0)
46 break;
7a2f978b 47 }
48}
49
50void *
51icpCreateMessage(
52 icp_opcode opcode,
53 int flags,
54 const char *url,
55 int reqnum,
56 int pad)
57{
58 char *buf = NULL;
59 icp_common_t *headerp = NULL;
60 char *urloffset = NULL;
61 int buf_len;
62 buf_len = sizeof(icp_common_t) + strlen(url) + 1;
27cd7235 63 if (opcode == ICP_QUERY)
7a2f978b 64 buf_len += sizeof(u_num32);
65 buf = xcalloc(buf_len, 1);
66 headerp = (icp_common_t *) (void *) buf;
79d39a72 67 headerp->opcode = (char) opcode;
7a2f978b 68 headerp->version = ICP_VERSION_CURRENT;
79d39a72 69 headerp->length = (u_short) htons(buf_len);
7a2f978b 70 headerp->reqnum = htonl(reqnum);
71 headerp->flags = htonl(flags);
72 headerp->pad = htonl(pad);
67129385 73 headerp->shostid = theOutICPAddr.s_addr;
7a2f978b 74 urloffset = buf + sizeof(icp_common_t);
27cd7235 75 if (opcode == ICP_QUERY)
7a2f978b 76 urloffset += sizeof(u_num32);
77 xmemcpy(urloffset, url, strlen(url));
78 return buf;
79}
80
17b6e784 81int
7a2f978b 82icpUdpSend(int fd,
83 const struct sockaddr_in *to,
84 icp_common_t * msg,
85 log_type logcode,
17b6e784 86 int delay)
7a2f978b 87{
17b6e784 88 icpUdpData *queue;
8e68922c 89 int x;
17b6e784 90 int len;
91 len = (int) ntohs(msg->length);
92 debug(12, 5) ("icpUdpSend: FD %d sending %s, %d bytes to %s:%d\n",
8e68922c 93 fd,
17b6e784 94 icp_opcode_str[msg->opcode],
95 len,
96 inet_ntoa(to->sin_addr),
97 ntohs(to->sin_port));
98 x = comm_udp_sendto(fd, to, sizeof(*to), msg, len);
9f80ddf1 99 if (x >= 0) {
17b6e784 100 /* successfully written */
101 icpLogIcp(to->sin_addr, logcode, len, (char *) (msg + 1), delay);
071a3ae7 102 icpCount(msg, SENT, (size_t) len, delay);
17b6e784 103 safe_free(msg);
104 } else if (0 == delay) {
105 /* send failed, but queue it */
106 queue = xcalloc(1, sizeof(icpUdpData));
107 queue->address = *to;
108 queue->msg = msg;
109 queue->len = (int) ntohs(msg->length);
110 queue->queue_time = current_time;
111 queue->logcode = logcode;
112 if (UdpQueueHead == NULL) {
113 UdpQueueHead = queue;
114 UdpQueueTail = queue;
115 } else if (UdpQueueTail == UdpQueueHead) {
116 UdpQueueTail = queue;
117 UdpQueueHead->next = queue;
118 } else {
119 UdpQueueTail->next = queue;
120 UdpQueueTail = queue;
121 }
122 commSetSelect(fd, COMM_SELECT_WRITE, icpUdpSendQueue, NULL, 0);
8e68922c 123 Counter.icp.replies_queued++;
124 } else {
17b6e784 125 /* don't queue it */
126 Counter.icp.replies_dropped++;
8e68922c 127 }
17b6e784 128 return x;
7a2f978b 129}
130
131int
132icpCheckUdpHit(StoreEntry * e, request_t * request)
133{
134 if (e == NULL)
135 return 0;
136 if (!storeEntryValidToSend(e))
137 return 0;
138 if (Config.onoff.icp_hit_stale)
139 return 1;
140 if (refreshCheck(e, request, 30))
141 return 0;
7a2f978b 142 return 1;
143}
7a2f978b 144
145static void
146icpHandleIcpV2(int fd, struct sockaddr_in from, char *buf, int len)
147{
148 icp_common_t header;
7a2f978b 149 StoreEntry *entry = NULL;
150 char *url = NULL;
151 const cache_key *key;
152 request_t *icp_request = NULL;
153 int allow = 0;
7a2f978b 154 aclCheck_t checklist;
155 icp_common_t *reply;
156 int src_rtt = 0;
157 u_num32 flags = 0;
5ad33356 158 method_t method;
194dd3b8 159 int rtt = 0;
160 int hops = 0;
c8f6c9c7 161 xmemcpy(&header, buf, sizeof(icp_common_t));
162 /*
163 * Only these fields need to be converted
164 */
165 header.length = ntohs(header.length);
166 header.reqnum = ntohl(header.reqnum);
167 header.flags = ntohl(header.flags);
168 header.pad = ntohl(header.pad);
7a2f978b 169
5ad33356 170 method = header.reqnum >> 24;
db1046b1 171 /* Squid-1.1 doesn't use the "method bits" for METHOD_GET */
172 if (METHOD_NONE == method || METHOD_ENUM_END <= method)
173 method = METHOD_GET;
7a2f978b 174 switch (header.opcode) {
27cd7235 175 case ICP_QUERY:
7a2f978b 176 /* We have a valid packet */
c8f6c9c7 177 url = buf + sizeof(icp_common_t) + sizeof(u_num32);
5ad33356 178 if ((icp_request = urlParse(method, url)) == NULL) {
27cd7235 179 reply = icpCreateMessage(ICP_ERR, 0, url, header.reqnum, 0);
17b6e784 180 icpUdpSend(fd, &from, reply, LOG_UDP_INVALID, 0);
7a2f978b 181 break;
182 }
183 checklist.src_addr = from.sin_addr;
184 checklist.request = icp_request;
185 allow = aclCheckFast(Config.accessList.icp, &checklist);
186 if (!allow) {
187 debug(12, 2) ("icpHandleIcpV2: Access Denied for %s by %s.\n",
188 inet_ntoa(from.sin_addr), AclMatchedName);
e711a2ff 189 if (clientdbCutoffDenied(from.sin_addr)) {
1a47dc5a 190 /*
191 * count this DENIED query in the clientdb, even though
192 * we're not sending an ICP reply...
193 */
1afe05c5 194 clientdbUpdate(from.sin_addr, LOG_UDP_DENIED, Config.Port.icp, 0);
e711a2ff 195 } else {
196 reply = icpCreateMessage(ICP_DENIED, 0, url, header.reqnum, 0);
17b6e784 197 icpUdpSend(fd, &from, reply, LOG_UDP_DENIED, 0);
7a2f978b 198 }
199 break;
200 }
201 if (header.flags & ICP_FLAG_SRC_RTT) {
194dd3b8 202 rtt = netdbHostRtt(icp_request->host);
203 hops = netdbHostHops(icp_request->host);
7a2f978b 204 src_rtt = ((hops & 0xFFFF) << 16) | (rtt & 0xFFFF);
205 if (rtt)
206 flags |= ICP_FLAG_SRC_RTT;
207 }
208 /* The peer is allowed to use this cache */
5ad33356 209 key = storeKeyPublic(url, method);
7a2f978b 210 entry = storeGet(key);
27cd7235 211 debug(12, 5) ("icpHandleIcpV2: OPCODE %s\n", icp_opcode_str[header.opcode]);
7a2f978b 212 if (icpCheckUdpHit(entry, icp_request)) {
067bea91 213 reply = icpCreateMessage(ICP_HIT, flags, url, header.reqnum, src_rtt);
17b6e784 214 icpUdpSend(fd, &from, reply, LOG_UDP_HIT, 0);
067bea91 215 break;
7a2f978b 216 }
194dd3b8 217 if (Config.onoff.test_reachability && rtt == 0) {
218 if ((rtt = netdbHostRtt(icp_request->host)) == 0)
219 netdbPingSite(icp_request->host);
220 }
7a2f978b 221 /* if store is rebuilding, return a UDP_HIT, but not a MISS */
222 if (store_rebuilding && opt_reload_hit_only) {
27cd7235 223 reply = icpCreateMessage(ICP_MISS_NOFETCH, flags, url, header.reqnum, src_rtt);
17b6e784 224 icpUdpSend(fd, &from, reply, LOG_UDP_MISS_NOFETCH, 0);
7a2f978b 225 } else if (hit_only_mode_until > squid_curtime) {
27cd7235 226 reply = icpCreateMessage(ICP_MISS_NOFETCH, flags, url, header.reqnum, src_rtt);
17b6e784 227 icpUdpSend(fd, &from, reply, LOG_UDP_MISS_NOFETCH, 0);
194dd3b8 228 } else if (Config.onoff.test_reachability && rtt == 0) {
229 reply = icpCreateMessage(ICP_MISS_NOFETCH, flags, url, header.reqnum, src_rtt);
17b6e784 230 icpUdpSend(fd, &from, reply, LOG_UDP_MISS_NOFETCH, 0);
7a2f978b 231 } else {
27cd7235 232 reply = icpCreateMessage(ICP_MISS, flags, url, header.reqnum, src_rtt);
17b6e784 233 icpUdpSend(fd, &from, reply, LOG_UDP_MISS, 0);
7a2f978b 234 }
235 break;
236
27cd7235 237 case ICP_HIT:
238 case ICP_SECHO:
239 case ICP_DECHO:
240 case ICP_MISS:
241 case ICP_DENIED:
242 case ICP_MISS_NOFETCH:
7a2f978b 243 if (neighbors_do_private_keys && header.reqnum == 0) {
244 debug(12, 0) ("icpHandleIcpV2: Neighbor %s returned reqnum = 0\n",
245 inet_ntoa(from.sin_addr));
246 debug(12, 0) ("icpHandleIcpV2: Disabling use of private keys\n");
247 neighbors_do_private_keys = 0;
248 }
c8f6c9c7 249 url = buf + sizeof(icp_common_t);
7a2f978b 250 debug(12, 3) ("icpHandleIcpV2: %s from %s for '%s'\n",
27cd7235 251 icp_opcode_str[header.opcode],
7a2f978b 252 inet_ntoa(from.sin_addr),
253 url);
254 if (neighbors_do_private_keys && header.reqnum)
5ad33356 255 key = storeKeyPrivate(url, method, header.reqnum);
7a2f978b 256 else
5ad33356 257 key = storeKeyPublic(url, method);
258 /* call neighborsUdpAck even if ping_status != PING_WAITING */
259 neighborsUdpAck(key, &header, &from);
7a2f978b 260 break;
261
27cd7235 262 case ICP_INVALID:
263 case ICP_ERR:
7a2f978b 264 break;
265
266 default:
267 debug(12, 0) ("icpHandleIcpV2: UNKNOWN OPCODE: %d from %s\n",
268 header.opcode, inet_ntoa(from.sin_addr));
269 break;
270 }
02922e76 271 if (icp_request) {
99edd1c3 272#if OLD_CODE
02922e76 273 stringClean(&icp_request->urlpath);
3f6c0fb2 274 memFree(MEM_REQUEST_T, icp_request);
99edd1c3 275#else
276 requestDestroy(icp_request);
277#endif
02922e76 278 }
7a2f978b 279}
280
281#ifdef ICP_PKT_DUMP
282static void
283icpPktDump(icp_common_t * pkt)
284{
285 struct in_addr a;
286
287 debug(12, 9) ("opcode: %3d %s\n",
288 (int) pkt->opcode,
27cd7235 289 icp_opcode_str[pkt->opcode]);
7a2f978b 290 debug(12, 9) ("version: %-8d\n", (int) pkt->version);
291 debug(12, 9) ("length: %-8d\n", (int) ntohs(pkt->length));
292 debug(12, 9) ("reqnum: %-8d\n", ntohl(pkt->reqnum));
293 debug(12, 9) ("flags: %-8x\n", ntohl(pkt->flags));
67129385 294 a.s_addr = pkt->shostid;
7a2f978b 295 debug(12, 9) ("shostid: %s\n", inet_ntoa(a));
296 debug(12, 9) ("payload: %s\n", (char *) pkt + sizeof(icp_common_t));
297}
298#endif
299
300void
79d39a72 301icpHandleUdp(int sock, void *datanotused)
7a2f978b 302{
303 struct sockaddr_in from;
304 int from_len;
305 LOCAL_ARRAY(char, buf, SQUID_UDP_SO_RCVBUF);
306 int len;
7a2f978b 307 int icp_version;
5b0aaa58 308 int max = 16;
7a2f978b 309 commSetSelect(sock, COMM_SELECT_READ, icpHandleUdp, NULL, 0);
5b0aaa58 310 while (max--) {
311 from_len = sizeof(from);
312 memset(&from, '\0', from_len);
313 len = recvfrom(sock,
314 buf,
315 SQUID_UDP_SO_RCVBUF - 1,
316 0,
317 (struct sockaddr *) &from,
318 &from_len);
319 if (len == 0)
320 break;
321 if (len < 0) {
322 if (ignoreErrno(errno))
323 break;
7a2f978b 324#ifdef _SQUID_LINUX_
5b0aaa58 325 /* Some Linux systems seem to set the FD for reading and then
326 * return ECONNREFUSED when sendto() fails and generates an ICMP
327 * port unreachable message. */
328 /* or maybe an EHOSTUNREACH "No route to host" message */
329 if (errno != ECONNREFUSED && errno != EHOSTUNREACH)
7a2f978b 330#endif
5b0aaa58 331 debug(50, 1) ("icpHandleUdp: FD %d recvfrom: %s\n",
332 sock, xstrerror());
333 break;
334 }
335 icpCount(buf, RECV, (size_t) len, 0);
336 buf[len] = '\0';
337 debug(12, 4) ("icpHandleUdp: FD %d: received %d bytes from %s.\n",
338 sock,
339 len,
340 inet_ntoa(from.sin_addr));
7a2f978b 341#ifdef ICP_PACKET_DUMP
5b0aaa58 342 icpPktDump(buf);
7a2f978b 343#endif
5b0aaa58 344 if (len < sizeof(icp_common_t)) {
345 debug(12, 4) ("icpHandleUdp: Ignoring too-small UDP packet\n");
346 break;
347 }
348 icp_version = (int) buf[1]; /* cheat! */
349 if (icp_version == ICP_VERSION_2)
350 icpHandleIcpV2(sock, from, buf, len);
351 else if (icp_version == ICP_VERSION_3)
352 icpHandleIcpV3(sock, from, buf, len);
353 else
354 debug(12, 0) ("WARNING: Unused ICP version %d received from %s:%d\n",
355 icp_version,
356 inet_ntoa(from.sin_addr),
357 ntohs(from.sin_port));
7a2f978b 358 }
7a2f978b 359}
15df8349 360
361void
362icpConnectionsOpen(void)
363{
364 u_short port;
365 struct in_addr addr;
366 struct sockaddr_in xaddr;
367 int x;
368 int len;
369 wordlist *s;
370 if (Config2.Accel.on && !Config.onoff.accel_with_proxy)
371 return;
372 if ((port = Config.Port.icp) <= 0)
373 return;
374 enter_suid();
375 theInIcpConnection = comm_open(SOCK_DGRAM,
376 0,
377 Config.Addrs.udp_incoming,
378 port,
379 COMM_NONBLOCKING,
de0df6f5 380 "ICP Socket");
15df8349 381 leave_suid();
382 if (theInIcpConnection < 0)
383 fatal("Cannot open ICP Port");
15df8349 384 commSetSelect(theInIcpConnection,
385 COMM_SELECT_READ,
386 icpHandleUdp,
387 NULL, 0);
388 for (s = Config.mcast_group_list; s; s = s->next)
389 ipcache_nbgethostbyname(s->key, mcastJoinGroups, NULL);
17e6c0a1 390 debug(12, 1) ("Accepting ICP messages on port %d, FD %d.\n",
15df8349 391 (int) port, theInIcpConnection);
392 if ((addr = Config.Addrs.udp_outgoing).s_addr != no_addr.s_addr) {
393 enter_suid();
394 theOutIcpConnection = comm_open(SOCK_DGRAM,
395 0,
396 addr,
397 port,
398 COMM_NONBLOCKING,
399 "ICP Port");
400 leave_suid();
401 if (theOutIcpConnection < 0)
402 fatal("Cannot open Outgoing ICP Port");
403 commSetSelect(theOutIcpConnection,
404 COMM_SELECT_READ,
405 icpHandleUdp,
406 NULL, 0);
17e6c0a1 407 debug(12, 1) ("Outgoing ICP messages on port %d, FD %d.\n",
1e948d78 408 (int) port, theOutIcpConnection);
15df8349 409 fd_note(theOutIcpConnection, "Outgoing ICP socket");
410 fd_note(theInIcpConnection, "Incoming ICP socket");
411 } else {
412 theOutIcpConnection = theInIcpConnection;
413 }
414 memset(&theOutICPAddr, '\0', sizeof(struct in_addr));
415 len = sizeof(struct sockaddr_in);
416 memset(&xaddr, '\0', len);
417 x = getsockname(theOutIcpConnection,
418 (struct sockaddr *) &xaddr, &len);
419 if (x < 0)
420 debug(50, 1) ("theOutIcpConnection FD %d: getsockname: %s\n",
421 theOutIcpConnection, xstrerror());
422 else
423 theOutICPAddr = xaddr.sin_addr;
424}
c0fbae16 425
17e6c0a1 426/*
427 * icpConnectionShutdown only closes the 'in' socket if it is
428 * different than the 'out' socket.
429 */
c0fbae16 430void
17e6c0a1 431icpConnectionShutdown(void)
c0fbae16 432{
433 if (theInIcpConnection < 0)
434 return;
17e6c0a1 435 if (theInIcpConnection != theOutIcpConnection) {
c7863c6f 436 debug(12, 1) ("FD %d Closing ICP connection\n", theInIcpConnection);
c0fbae16 437 comm_close(theInIcpConnection);
17e6c0a1 438 }
c0fbae16 439 /*
440 * Here we set 'theInIcpConnection' to -1 even though the ICP 'in'
441 * and 'out' sockets might be just one FD. This prevents this
442 * function from executing repeatedly. When we are really ready to
443 * exit or restart, main will comm_close the 'out' descriptor.
444 */
445 theInIcpConnection = -1;
446 /*
447 * Normally we only write to the outgoing ICP socket, but
448 * we also have a read handler there to catch messages sent
449 * to that specific interface. During shutdown, we must
450 * disable reading on the outgoing socket.
451 */
452 assert(theOutIcpConnection > -1);
453 commSetSelect(theOutIcpConnection, COMM_SELECT_READ, NULL, NULL, 0);
454}
17e6c0a1 455
456void
457icpConnectionClose(void)
458{
459 icpConnectionShutdown();
460 if (theOutIcpConnection > -1) {
c7863c6f 461 debug(12, 1) ("FD %d Closing ICP connection\n", theOutIcpConnection);
17e6c0a1 462 comm_close(theOutIcpConnection);
463 }
464}
071a3ae7 465
466static void
467icpCount(void *buf, int which, size_t len, int delay)
468{
469 icp_common_t *icp = buf;
470 if (len < sizeof(*icp))
471 return;
472 if (SENT == which) {
473 Counter.icp.pkts_sent++;
474 kb_incr(&Counter.icp.kbytes_sent, len);
475 if (ICP_QUERY == icp->opcode) {
476 Counter.icp.queries_sent++;
477 kb_incr(&Counter.icp.q_kbytes_sent, len);
478 } else {
479 Counter.icp.replies_sent++;
480 kb_incr(&Counter.icp.r_kbytes_sent, len);
481 /* this is the sent-reply service time */
4b4cd312 482 statHistCount(&Counter.icp.reply_svc_time, delay);
071a3ae7 483 }
4b4cd312 484 if (ICP_HIT == icp->opcode)
071a3ae7 485 Counter.icp.hits_sent++;
486 } else if (RECV == which) {
487 Counter.icp.pkts_recv++;
488 kb_incr(&Counter.icp.kbytes_recv, len);
489 if (ICP_QUERY == icp->opcode) {
490 Counter.icp.queries_recv++;
491 kb_incr(&Counter.icp.q_kbytes_recv, len);
492 } else {
493 Counter.icp.replies_recv++;
494 kb_incr(&Counter.icp.r_kbytes_recv, len);
495 /* Counter.icp.query_svc_time set in clientUpdateCounters */
496 }
4b4cd312 497 if (ICP_HIT == icp->opcode)
071a3ae7 498 Counter.icp.hits_recv++;
499 }
500}