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