]> git.ipfire.org Git - thirdparty/squid.git/blame - src/wccp.cc
CI: Remove unnecessary test-functionality test wrappers (#1393)
[thirdparty/squid.git] / src / wccp.cc
CommitLineData
320e9f36 1/*
b8ae064d 2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
320e9f36 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.
320e9f36 7 */
bbc27441
AJ
8
9/* DEBUG: section 80 WCCP Support */
10
f7f3304a 11#include "squid.h"
320e9f36 12
eb824054 13#if USE_WCCP
f9b72e0c
AJ
14#include "comm.h"
15#include "comm/Connection.h"
d841c88d 16#include "comm/Loops.h"
f9b72e0c 17#include "event.h"
ed6e9fb9 18#include "fatal.h"
4d5904f7 19#include "SquidConfig.h"
8b082ed9 20#include "wccp.h"
582c2af2 21
320e9f36 22#define WCCP_PORT 2048
320e9f36 23#define WCCP_REVISION 0
320e9f36 24#define WCCP_ACTIVE_CACHES 32
25#define WCCP_HASH_SIZE 32
26#define WCCP_BUCKETS 256
7e3ce7b9 27#define WCCP_CACHE_LEN 4
320e9f36 28
29#define WCCP_HERE_I_AM 7
30#define WCCP_I_SEE_YOU 8
31#define WCCP_ASSIGN_BUCKET 9
32
26ac0430 33struct wccp_here_i_am_t {
320e9f36 34 int type;
35 int version;
36 int revision;
37 char hash[WCCP_HASH_SIZE];
38 int reserved;
39 int id;
40};
41
26ac0430 42struct wccp_cache_entry_t {
b7ac5457 43 struct in_addr ip_addr; // WCCP on-the-wire in 32-bit IPv4-only.
320e9f36 44 int revision;
ede4c165 45 char hash[WCCP_HASH_SIZE];
320e9f36 46 int reserved;
47};
48
26ac0430 49struct wccp_i_see_you_t {
b7ac5457
AJ
50 int32_t type;
51 int32_t version;
52 int32_t change;
53 int32_t id;
54 int32_t number;
62e76326 55
320e9f36 56 struct wccp_cache_entry_t wccp_cache_entry[WCCP_ACTIVE_CACHES];
57};
58
26ac0430 59struct wccp_assign_bucket_t {
f6308664 60 int type;
61 int id;
62 int number;
320e9f36 63};
64
0b0cfcf2 65static int theWccpConnection = -1;
62e76326 66
320e9f36 67static struct wccp_here_i_am_t wccp_here_i_am;
62e76326 68
b5b86e2c 69static struct wccp_i_see_you_t wccp_i_see_you;
eccaff9c 70static int last_change;
71static int last_id;
1addf64a 72static int last_assign_buckets_change;
908b22c4 73static unsigned int number_caches;
62e76326 74
b7ac5457 75static Ip::Address local_ip;
b5b86e2c 76
1f38f50a 77static PF wccpHandleUdp;
78static int wccpLowestIP(void);
79static EVH wccpHereIam;
7e3ce7b9 80static void wccpAssignBuckets(void);
320e9f36 81
320e9f36 82/*
83 * The functions used during startup:
84 * wccpInit
85 * wccpConnectionOpen
86 * wccpConnectionShutdown
87 * wccpConnectionClose
88 */
89
90void
91wccpInit(void)
92{
bf8fe701 93 debugs(80, 5, "wccpInit: Called");
320e9f36 94 memset(&wccp_here_i_am, '\0', sizeof(wccp_here_i_am));
95 wccp_here_i_am.type = htonl(WCCP_HERE_I_AM);
d20b1cd0 96 wccp_here_i_am.version = htonl(Config.Wccp.version);
f6308664 97 wccp_here_i_am.revision = htonl(WCCP_REVISION);
eccaff9c 98 last_change = 0;
99 last_id = 0;
1addf64a 100 last_assign_buckets_change = 0;
eccaff9c 101 number_caches = 0;
62e76326 102
4dd643d5 103 if (!Config.Wccp.router.isAnyAddr())
aee3523a
AR
104 if (!eventFind(wccpHereIam, nullptr))
105 eventAdd("wccpHereIam", wccpHereIam, nullptr, 5.0, 1);
320e9f36 106}
107
108void
109wccpConnectionOpen(void)
110{
bf8fe701 111 debugs(80, 5, "wccpConnectionOpen: Called");
62e76326 112
4dd643d5 113 if (Config.Wccp.router.isAnyAddr()) {
76dc4ca3 114 debugs(80, 2, "WCCPv1 disabled.");
62e76326 115 return;
1f38f50a 116 }
62e76326 117
4dd643d5 118 if ( !Config.Wccp.router.setIPv4() ) {
30c48b1a 119 debugs(80, DBG_CRITICAL, "WCCPv1 Disabled. Router " << Config.Wccp.router << " is not an IPv4 address.");
cc192b50 120 return;
121 }
122
4dd643d5 123 if ( !Config.Wccp.address.setIPv4() ) {
30c48b1a 124 debugs(80, DBG_CRITICAL, "WCCPv1 Disabled. Local address " << Config.Wccp.address << " is not an IPv4 address.");
cc192b50 125 return;
126 }
127
4dd643d5
AJ
128 Config.Wccp.address.port(WCCP_PORT);
129 Config.Wccp.router.port(WCCP_PORT);
cc192b50 130
31be869c 131 theWccpConnection = comm_open_listener(SOCK_DGRAM,
04f7fd38
AJ
132 IPPROTO_UDP,
133 Config.Wccp.address,
134 COMM_NONBLOCKING,
135 "WCCP Socket");
62e76326 136
0b0cfcf2 137 if (theWccpConnection < 0)
62e76326 138 fatal("Cannot open WCCP Port");
139
aee3523a 140 Comm::SetSelect(theWccpConnection, COMM_SELECT_READ, wccpHandleUdp, nullptr, 0);
62e76326 141
e0236918 142 debugs(80, DBG_IMPORTANT, "Accepting WCCPv1 messages on " << Config.Wccp.address << ", FD " << theWccpConnection << ".");
0b0cfcf2 143
306bfbb2 144 // Sadly WCCP only does IPv4
62e76326 145
306bfbb2 146 struct sockaddr_in router;
4dd643d5 147 Config.Wccp.router.getSockAddr(router);
9055f14b 148 if (connect(theWccpConnection, (struct sockaddr*)&router, sizeof(router)))
306bfbb2 149 fatal("Unable to connect WCCP out socket");
62e76326 150
306bfbb2
AJ
151 struct sockaddr_in local;
152 memset(&local, '\0', sizeof(local));
153 socklen_t slen = sizeof(local);
9055f14b 154 if (getsockname(theWccpConnection, (struct sockaddr*)&local, &slen))
62e76326 155 fatal("Unable to getsockname on WCCP out socket");
156
306bfbb2 157 local_ip = local;
320e9f36 158}
159
320e9f36 160void
161wccpConnectionClose(void)
162{
0b0cfcf2 163 if (theWccpConnection > -1) {
e0236918 164 debugs(80, DBG_IMPORTANT, "FD " << theWccpConnection << " Closing WCCPv1 socket");
0b0cfcf2 165 comm_close(theWccpConnection);
166 theWccpConnection = -1;
320e9f36 167 }
168}
169
62e76326 170/*
320e9f36 171 * Functions for handling the requests.
172 */
173
62e76326 174/*
320e9f36 175 * Accept the UDP packet
f6308664 176 */
1f38f50a 177static void
ced8def3 178wccpHandleUdp(int sock, void *)
f6308664 179{
b7ac5457 180 Ip::Address from;
47ac1af3 181 int len;
320e9f36 182
bf8fe701 183 debugs(80, 6, "wccpHandleUdp: Called.");
f6308664 184
aee3523a 185 Comm::SetSelect(sock, COMM_SELECT_READ, wccpHandleUdp, nullptr, 0);
62e76326 186
b5b86e2c 187 memset(&wccp_i_see_you, '\0', sizeof(wccp_i_see_you));
f6308664 188
7d21986b 189 len = comm_udp_recvfrom(sock,
62e76326 190 (void *) &wccp_i_see_you,
1205909e 191 sizeof(wccp_i_see_you),
62e76326 192 0,
cc192b50 193 from);
194 debugs(80, 3, "wccpHandleUdp: " << len << " bytes WCCP pkt from " << from <<
26ac0430 195 ": type=" <<
bf8fe701 196 (unsigned) ntohl(wccp_i_see_you.type) << ", version=" <<
197 (unsigned) ntohl(wccp_i_see_you.version) << ", change=" <<
198 (unsigned) ntohl(wccp_i_see_you.change) << ", id=" <<
199 (unsigned) ntohl(wccp_i_see_you.id) << ", number=" <<
200 (unsigned) ntohl(wccp_i_see_you.number));
62e76326 201
b5b86e2c 202 if (len < 0)
62e76326 203 return;
204
cc192b50 205 if (from != Config.Wccp.router)
62e76326 206 return;
207
e4049756 208 if ((unsigned) ntohl(wccp_i_see_you.version) != (unsigned) Config.Wccp.version)
62e76326 209 return;
210
b5b86e2c 211 if (ntohl(wccp_i_see_you.type) != WCCP_I_SEE_YOU)
62e76326 212 return;
213
0b0cfcf2 214 if (ntohl(wccp_i_see_you.number) > WCCP_ACTIVE_CACHES) {
e0236918 215 debugs(80, DBG_IMPORTANT, "Ignoring WCCP_I_SEE_YOU from " <<
cc192b50 216 from << " with number of caches set to " <<
bf8fe701 217 (int) ntohl(wccp_i_see_you.number));
218
eedab643 219 return;
220 }
221
eccaff9c 222 last_id = wccp_i_see_you.id;
eedab643 223
eccaff9c 224 if ((0 == last_change) && (number_caches == (unsigned) ntohl(wccp_i_see_you.number))) {
62e76326 225 if (last_assign_buckets_change == wccp_i_see_you.change) {
226 /*
227 * After a WCCP_ASSIGN_BUCKET message, the router should
228 * update the change value. If not, maybe the route didn't
229 * receive our WCCP_ASSIGN_BUCKET message, so send it again.
230 *
231 * Don't update change here. Instead, fall through to
232 * the next block to call wccpAssignBuckets() again.
233 */
234 (void) 0;
235 } else {
eccaff9c 236 last_change = wccp_i_see_you.change;
62e76326 237 return;
238 }
b5b86e2c 239 }
62e76326 240
eccaff9c 241 if (last_change != wccp_i_see_you.change) {
242 last_change = wccp_i_see_you.change;
62e76326 243
244 if (wccpLowestIP() && wccp_i_see_you.number) {
eccaff9c 245 last_assign_buckets_change = last_change;
62e76326 246 wccpAssignBuckets();
247 }
b5b86e2c 248 }
249}
f6308664 250
1f38f50a 251static int
252wccpLowestIP(void)
b5b86e2c 253{
908b22c4 254 unsigned int loop;
eccaff9c 255 int found = 0;
62e76326 256
eedab643 257 /*
258 * We sanity checked wccp_i_see_you.number back in wccpHandleUdp()
259 */
260
14942edd 261 for (loop = 0; loop < (unsigned) ntohl(wccp_i_see_you.number); ++loop) {
eedab643 262 assert(loop < WCCP_ACTIVE_CACHES);
263
185d5b63 264 if (local_ip > wccp_i_see_you.wccp_cache_entry[loop].ip_addr)
62e76326 265 return 0;
eccaff9c 266
b7ac5457 267 if (local_ip == wccp_i_see_you.wccp_cache_entry[loop].ip_addr)
eccaff9c 268 found = 1;
f6308664 269 }
62e76326 270
eccaff9c 271 return found;
f6308664 272}
320e9f36 273
1f38f50a 274static void
ced8def3 275wccpHereIam(void *)
320e9f36 276{
bf8fe701 277 debugs(80, 6, "wccpHereIam: Called");
320e9f36 278
eccaff9c 279 wccp_here_i_am.id = last_id;
182106f3 280 double interval = 10.0; // TODO: make this configurable, possibly negotiate with the router.
182106f3
AJ
281 ssize_t sent = comm_udp_send(theWccpConnection, &wccp_here_i_am, sizeof(wccp_here_i_am), 0);
282
283 // if we failed to send the whole lot, try again at a shorter interval (20%)
284 if (sent != sizeof(wccp_here_i_am)) {
b69e9ffa
AJ
285 int xerrno = errno;
286 debugs(80, 2, "ERROR: failed to send WCCP HERE_I_AM packet: " << xstrerr(xerrno));
182106f3
AJ
287 interval = 2.0;
288 }
320e9f36 289
aee3523a
AR
290 if (!eventFind(wccpHereIam, nullptr))
291 eventAdd("wccpHereIam", wccpHereIam, nullptr, interval, 1);
320e9f36 292}
293
1f38f50a 294static void
7e3ce7b9 295wccpAssignBuckets(void)
320e9f36 296{
62e76326 297
7e3ce7b9 298 struct wccp_assign_bucket_t *wccp_assign_bucket;
299 int wab_len;
300 char *buckets;
9bc73deb 301 int buckets_per_cache;
908b22c4 302 unsigned int loop;
1f38f50a 303 int bucket = 0;
304 int *caches;
7e3ce7b9 305 int cache_len;
1f38f50a 306 char *buf;
320e9f36 307
bf8fe701 308 debugs(80, 6, "wccpAssignBuckets: Called");
b5b86e2c 309 number_caches = ntohl(wccp_i_see_you.number);
62e76326 310
eedab643 311 assert(number_caches > 0);
312 assert(number_caches <= WCCP_ACTIVE_CACHES);
62e76326 313
7e3ce7b9 314 wab_len = sizeof(struct wccp_assign_bucket_t);
62e76326 315
7e3ce7b9 316 cache_len = WCCP_CACHE_LEN * number_caches;
317
908b22c4 318 buf = (char *)xmalloc(wab_len +
62e76326 319 WCCP_BUCKETS +
320 cache_len);
321
7e3ce7b9 322 wccp_assign_bucket = (struct wccp_assign_bucket_t *) buf;
62e76326 323
7e3ce7b9 324 caches = (int *) (buf + wab_len);
62e76326 325
7e3ce7b9 326 buckets = buf + wab_len + cache_len;
327
55788a77 328 memset(wccp_assign_bucket, '\0', sizeof(*wccp_assign_bucket));
62e76326 329
7e3ce7b9 330 memset(buckets, 0xFF, WCCP_BUCKETS);
320e9f36 331
9bc73deb 332 buckets_per_cache = WCCP_BUCKETS / number_caches;
62e76326 333
14942edd 334 for (loop = 0; loop < number_caches; ++loop) {
62e76326 335 int i;
41d00cd3 336 memcpy(&caches[loop],
e34763f4
A
337 &wccp_i_see_you.wccp_cache_entry[loop].ip_addr,
338 sizeof(*caches));
62e76326 339
14942edd 340 for (i = 0; i < buckets_per_cache; ++i) {
62e76326 341 assert(bucket < WCCP_BUCKETS);
14942edd
FC
342 buckets[bucket] = loop;
343 ++bucket;
62e76326 344 }
320e9f36 345 }
62e76326 346
994f613f 347 while (bucket < WCCP_BUCKETS) {
14942edd
FC
348 buckets[bucket] = number_caches - 1;
349 ++bucket;
994f613f 350 }
62e76326 351
7e3ce7b9 352 wccp_assign_bucket->type = htonl(WCCP_ASSIGN_BUCKET);
353 wccp_assign_bucket->id = wccp_i_see_you.id;
354 wccp_assign_bucket->number = wccp_i_see_you.number;
355
0b0cfcf2 356 comm_udp_send(theWccpConnection,
62e76326 357 buf,
358 wab_len + WCCP_BUCKETS + cache_len,
359 0);
eccaff9c 360 last_change = 0;
1f38f50a 361 xfree(buf);
320e9f36 362}
eb824054 363
364#endif /* USE_WCCP */
f53969cc 365