]> git.ipfire.org Git - thirdparty/squid.git/blob - src/wccp.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / wccp.cc
1
2 /*
3 * $Id$
4 *
5 * DEBUG: section 80 WCCP Support
6 * AUTHOR: Glenn Chisholm
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35 #include "squid.h"
36
37 #if USE_WCCP
38 #include "comm.h"
39 #include "comm/Connection.h"
40 #include "comm/Loops.h"
41 #include "event.h"
42 #include "structs.h"
43
44 #define WCCP_PORT 2048
45 #define WCCP_REVISION 0
46 #define WCCP_ACTIVE_CACHES 32
47 #define WCCP_HASH_SIZE 32
48 #define WCCP_BUCKETS 256
49 #define WCCP_CACHE_LEN 4
50
51 #define WCCP_HERE_I_AM 7
52 #define WCCP_I_SEE_YOU 8
53 #define WCCP_ASSIGN_BUCKET 9
54
55 struct wccp_here_i_am_t {
56 int type;
57 int version;
58 int revision;
59 char hash[WCCP_HASH_SIZE];
60 int reserved;
61 int id;
62 };
63
64 struct wccp_cache_entry_t {
65 struct in_addr ip_addr; // WCCP on-the-wire in 32-bit IPv4-only.
66 int revision;
67 char hash[WCCP_HASH_SIZE];
68 int reserved;
69 };
70
71 struct wccp_i_see_you_t {
72 int32_t type;
73 int32_t version;
74 int32_t change;
75 int32_t id;
76 int32_t number;
77
78 struct wccp_cache_entry_t wccp_cache_entry[WCCP_ACTIVE_CACHES];
79 };
80
81 struct wccp_assign_bucket_t {
82 int type;
83 int id;
84 int number;
85 };
86
87 static int theWccpConnection = -1;
88
89 static struct wccp_here_i_am_t wccp_here_i_am;
90
91 static struct wccp_i_see_you_t wccp_i_see_you;
92 static int last_change;
93 static int last_id;
94 static int last_assign_buckets_change;
95 static unsigned int number_caches;
96
97 static Ip::Address local_ip;
98
99 static PF wccpHandleUdp;
100 static int wccpLowestIP(void);
101 static EVH wccpHereIam;
102 static void wccpAssignBuckets(void);
103
104 /*
105 * The functions used during startup:
106 * wccpInit
107 * wccpConnectionOpen
108 * wccpConnectionShutdown
109 * wccpConnectionClose
110 */
111
112 void
113 wccpInit(void)
114 {
115 debugs(80, 5, "wccpInit: Called");
116 memset(&wccp_here_i_am, '\0', sizeof(wccp_here_i_am));
117 wccp_here_i_am.type = htonl(WCCP_HERE_I_AM);
118 wccp_here_i_am.version = htonl(Config.Wccp.version);
119 wccp_here_i_am.revision = htonl(WCCP_REVISION);
120 last_change = 0;
121 last_id = 0;
122 last_assign_buckets_change = 0;
123 number_caches = 0;
124
125 if (!Config.Wccp.router.IsAnyAddr())
126 if (!eventFind(wccpHereIam, NULL))
127 eventAdd("wccpHereIam", wccpHereIam, NULL, 5.0, 1);
128 }
129
130 void
131 wccpConnectionOpen(void)
132 {
133 debugs(80, 5, "wccpConnectionOpen: Called");
134
135 if (Config.Wccp.router.IsAnyAddr()) {
136 debugs(80, 2, "WCCPv1 disabled.");
137 return;
138 }
139
140 if ( !Config.Wccp.router.SetIPv4() ) {
141 debugs(80, DBG_CRITICAL, "WCCPv1 Disabled. Router " << Config.Wccp.router << " is not an IPv4 address.");
142 return;
143 }
144
145 if ( !Config.Wccp.address.SetIPv4() ) {
146 debugs(80, DBG_CRITICAL, "WCCPv1 Disabled. Local address " << Config.Wccp.address << " is not an IPv4 address.");
147 return;
148 }
149
150 Config.Wccp.address.SetPort(WCCP_PORT);
151 Config.Wccp.router.SetPort(WCCP_PORT);
152
153 theWccpConnection = comm_open_listener(SOCK_DGRAM,
154 IPPROTO_UDP,
155 Config.Wccp.address,
156 COMM_NONBLOCKING,
157 "WCCP Socket");
158
159 if (theWccpConnection < 0)
160 fatal("Cannot open WCCP Port");
161
162 Comm::SetSelect(theWccpConnection, COMM_SELECT_READ, wccpHandleUdp, NULL, 0);
163
164 debugs(80, DBG_IMPORTANT, "Accepting WCCPv1 messages on " << Config.Wccp.address << ", FD " << theWccpConnection << ".");
165
166 // Sadly WCCP only does IPv4
167
168 struct sockaddr_in router;
169 Config.Wccp.router.GetSockAddr(router);
170 if (connect(theWccpConnection, (struct sockaddr*)&router, sizeof(router)))
171 fatal("Unable to connect WCCP out socket");
172
173 struct sockaddr_in local;
174 memset(&local, '\0', sizeof(local));
175 socklen_t slen = sizeof(local);
176 if (getsockname(theWccpConnection, (struct sockaddr*)&local, &slen))
177 fatal("Unable to getsockname on WCCP out socket");
178
179 local_ip = local;
180 }
181
182 void
183 wccpConnectionClose(void)
184 {
185 if (theWccpConnection > -1) {
186 debugs(80, DBG_IMPORTANT, "FD " << theWccpConnection << " Closing WCCPv1 socket");
187 comm_close(theWccpConnection);
188 theWccpConnection = -1;
189 }
190 }
191
192 /*
193 * Functions for handling the requests.
194 */
195
196 /*
197 * Accept the UDP packet
198 */
199 static void
200 wccpHandleUdp(int sock, void *not_used)
201 {
202 Ip::Address from;
203 int len;
204
205 debugs(80, 6, "wccpHandleUdp: Called.");
206
207 Comm::SetSelect(sock, COMM_SELECT_READ, wccpHandleUdp, NULL, 0);
208
209 memset(&wccp_i_see_you, '\0', sizeof(wccp_i_see_you));
210
211 len = comm_udp_recvfrom(sock,
212 (void *) &wccp_i_see_you,
213 sizeof(wccp_i_see_you),
214 0,
215 from);
216 debugs(80, 3, "wccpHandleUdp: " << len << " bytes WCCP pkt from " << from <<
217 ": type=" <<
218 (unsigned) ntohl(wccp_i_see_you.type) << ", version=" <<
219 (unsigned) ntohl(wccp_i_see_you.version) << ", change=" <<
220 (unsigned) ntohl(wccp_i_see_you.change) << ", id=" <<
221 (unsigned) ntohl(wccp_i_see_you.id) << ", number=" <<
222 (unsigned) ntohl(wccp_i_see_you.number));
223
224 if (len < 0)
225 return;
226
227 if (from != Config.Wccp.router)
228 return;
229
230 if ((unsigned) ntohl(wccp_i_see_you.version) != (unsigned) Config.Wccp.version)
231 return;
232
233 if (ntohl(wccp_i_see_you.type) != WCCP_I_SEE_YOU)
234 return;
235
236 if (ntohl(wccp_i_see_you.number) > WCCP_ACTIVE_CACHES) {
237 debugs(80, DBG_IMPORTANT, "Ignoring WCCP_I_SEE_YOU from " <<
238 from << " with number of caches set to " <<
239 (int) ntohl(wccp_i_see_you.number));
240
241 return;
242 }
243
244 last_id = wccp_i_see_you.id;
245
246 if ((0 == last_change) && (number_caches == (unsigned) ntohl(wccp_i_see_you.number))) {
247 if (last_assign_buckets_change == wccp_i_see_you.change) {
248 /*
249 * After a WCCP_ASSIGN_BUCKET message, the router should
250 * update the change value. If not, maybe the route didn't
251 * receive our WCCP_ASSIGN_BUCKET message, so send it again.
252 *
253 * Don't update change here. Instead, fall through to
254 * the next block to call wccpAssignBuckets() again.
255 */
256 (void) 0;
257 } else {
258 last_change = wccp_i_see_you.change;
259 return;
260 }
261 }
262
263 if (last_change != wccp_i_see_you.change) {
264 last_change = wccp_i_see_you.change;
265
266 if (wccpLowestIP() && wccp_i_see_you.number) {
267 last_assign_buckets_change = last_change;
268 wccpAssignBuckets();
269 }
270 }
271 }
272
273 static int
274 wccpLowestIP(void)
275 {
276 unsigned int loop;
277 int found = 0;
278
279 /*
280 * We sanity checked wccp_i_see_you.number back in wccpHandleUdp()
281 */
282
283 for (loop = 0; loop < (unsigned) ntohl(wccp_i_see_you.number); ++loop) {
284 assert(loop < WCCP_ACTIVE_CACHES);
285
286 if (local_ip > wccp_i_see_you.wccp_cache_entry[loop].ip_addr)
287 return 0;
288
289 if (local_ip == wccp_i_see_you.wccp_cache_entry[loop].ip_addr)
290 found = 1;
291 }
292
293 return found;
294 }
295
296 static void
297 wccpHereIam(void *voidnotused)
298 {
299 debugs(80, 6, "wccpHereIam: Called");
300
301 wccp_here_i_am.id = last_id;
302 comm_udp_send(theWccpConnection,
303 &wccp_here_i_am,
304 sizeof(wccp_here_i_am),
305 0);
306
307 if (!eventFind(wccpHereIam, NULL))
308 eventAdd("wccpHereIam", wccpHereIam, NULL, 10.0, 1);
309 }
310
311 static void
312 wccpAssignBuckets(void)
313 {
314
315 struct wccp_assign_bucket_t *wccp_assign_bucket;
316 int wab_len;
317 char *buckets;
318 int buckets_per_cache;
319 unsigned int loop;
320 int bucket = 0;
321 int *caches;
322 int cache_len;
323 char *buf;
324
325 debugs(80, 6, "wccpAssignBuckets: Called");
326 number_caches = ntohl(wccp_i_see_you.number);
327
328 assert(number_caches > 0);
329 assert(number_caches <= WCCP_ACTIVE_CACHES);
330
331 wab_len = sizeof(struct wccp_assign_bucket_t);
332
333 cache_len = WCCP_CACHE_LEN * number_caches;
334
335 buf = (char *)xmalloc(wab_len +
336 WCCP_BUCKETS +
337 cache_len);
338
339 wccp_assign_bucket = (struct wccp_assign_bucket_t *) buf;
340
341 caches = (int *) (buf + wab_len);
342
343 buckets = buf + wab_len + cache_len;
344
345 memset(wccp_assign_bucket, '\0', sizeof(*wccp_assign_bucket));
346
347 memset(buckets, 0xFF, WCCP_BUCKETS);
348
349 buckets_per_cache = WCCP_BUCKETS / number_caches;
350
351 for (loop = 0; loop < number_caches; ++loop) {
352 int i;
353 memcpy(&caches[loop],
354 &wccp_i_see_you.wccp_cache_entry[loop].ip_addr,
355 sizeof(*caches));
356
357 for (i = 0; i < buckets_per_cache; ++i) {
358 assert(bucket < WCCP_BUCKETS);
359 buckets[bucket] = loop;
360 ++bucket;
361 }
362 }
363
364 while (bucket < WCCP_BUCKETS) {
365 buckets[bucket] = number_caches - 1;
366 ++bucket;
367 }
368
369 wccp_assign_bucket->type = htonl(WCCP_ASSIGN_BUCKET);
370 wccp_assign_bucket->id = wccp_i_see_you.id;
371 wccp_assign_bucket->number = wccp_i_see_you.number;
372
373 comm_udp_send(theWccpConnection,
374 buf,
375 wab_len + WCCP_BUCKETS + cache_len,
376 0);
377 last_change = 0;
378 xfree(buf);
379 }
380
381 #endif /* USE_WCCP */