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