]> git.ipfire.org Git - thirdparty/squid.git/blob - src/wccp.cc
Merge from trunk
[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 "config.h"
36
37 #if USE_WCCP
38
39 #include "squid.h"
40
41 #include "comm.h"
42 #include "comm/Connection.h"
43 #include "comm/Loops.h"
44 #include "event.h"
45
46 #define WCCP_PORT 2048
47 #define WCCP_REVISION 0
48 #define WCCP_ACTIVE_CACHES 32
49 #define WCCP_HASH_SIZE 32
50 #define WCCP_BUCKETS 256
51 #define WCCP_CACHE_LEN 4
52
53 #define WCCP_HERE_I_AM 7
54 #define WCCP_I_SEE_YOU 8
55 #define WCCP_ASSIGN_BUCKET 9
56
57 struct wccp_here_i_am_t {
58 int type;
59 int version;
60 int revision;
61 char hash[WCCP_HASH_SIZE];
62 int reserved;
63 int id;
64 };
65
66 struct wccp_cache_entry_t {
67 struct in_addr ip_addr; // WCCP on-the-wire in 32-bit IPv4-only.
68 int revision;
69 char hash[WCCP_HASH_SIZE];
70 int reserved;
71 };
72
73 struct wccp_i_see_you_t {
74 int32_t type;
75 int32_t version;
76 int32_t change;
77 int32_t id;
78 int32_t number;
79
80 struct wccp_cache_entry_t wccp_cache_entry[WCCP_ACTIVE_CACHES];
81 };
82
83 struct wccp_assign_bucket_t {
84 int type;
85 int id;
86 int number;
87 };
88
89 static int theWccpConnection = -1;
90
91 static struct wccp_here_i_am_t wccp_here_i_am;
92
93 static struct wccp_i_see_you_t wccp_i_see_you;
94 static int last_change;
95 static int last_id;
96 static int last_assign_buckets_change;
97 static unsigned int number_caches;
98
99 static Ip::Address local_ip;
100
101 static PF wccpHandleUdp;
102 static int wccpLowestIP(void);
103 static EVH wccpHereIam;
104 static void wccpAssignBuckets(void);
105
106 /*
107 * The functions used during startup:
108 * wccpInit
109 * wccpConnectionOpen
110 * wccpConnectionShutdown
111 * wccpConnectionClose
112 */
113
114 void
115 wccpInit(void)
116 {
117 debugs(80, 5, "wccpInit: Called");
118 memset(&wccp_here_i_am, '\0', sizeof(wccp_here_i_am));
119 wccp_here_i_am.type = htonl(WCCP_HERE_I_AM);
120 wccp_here_i_am.version = htonl(Config.Wccp.version);
121 wccp_here_i_am.revision = htonl(WCCP_REVISION);
122 last_change = 0;
123 last_id = 0;
124 last_assign_buckets_change = 0;
125 number_caches = 0;
126
127 if (!Config.Wccp.router.IsAnyAddr())
128 if (!eventFind(wccpHereIam, NULL))
129 eventAdd("wccpHereIam", wccpHereIam, NULL, 5.0, 1);
130 }
131
132 void
133 wccpConnectionOpen(void)
134 {
135 struct addrinfo *router = NULL, *local = NULL;
136 debugs(80, 5, "wccpConnectionOpen: Called");
137
138 if (Config.Wccp.router.IsAnyAddr()) {
139 debugs(80, 2, "WCCPv1 disabled.");
140 return;
141 }
142
143 if ( !Config.Wccp.router.SetIPv4() ) {
144 debugs(1, 1, "WCCPv1 Disabled. Router " << Config.Wccp.router << " is not IPv4.");
145 return;
146 }
147
148 if ( !Config.Wccp.address.SetIPv4() ) {
149 debugs(1, 1, "WCCPv1 Disabled. Local address " << Config.Wccp.address << " is not IPv4.");
150 return;
151 }
152
153 Config.Wccp.address.SetPort(WCCP_PORT);
154 Config.Wccp.router.SetPort(WCCP_PORT);
155
156 theWccpConnection = comm_open_listener(SOCK_DGRAM,
157 IPPROTO_UDP,
158 Config.Wccp.address,
159 COMM_NONBLOCKING,
160 "WCCP Socket");
161
162 if (theWccpConnection < 0)
163 fatal("Cannot open WCCP Port");
164
165 Comm::SetSelect(theWccpConnection, COMM_SELECT_READ, wccpHandleUdp, NULL, 0);
166
167 debugs(80, 1, "Accepting WCCPv1 messages on " << Config.Wccp.address << ", FD " << theWccpConnection << ".");
168
169 Config.Wccp.router.GetAddrInfo(router,AF_INET);
170
171 if (connect(theWccpConnection, router->ai_addr, router->ai_addrlen))
172 fatal("Unable to connect WCCP out socket");
173
174 Config.Wccp.router.FreeAddrInfo(router);
175
176 Config.Wccp.address.InitAddrInfo(local);
177
178 if (getsockname(theWccpConnection, local->ai_addr, &local->ai_addrlen))
179 fatal("Unable to getsockname on WCCP out socket");
180
181 local_ip = *local;
182
183 Config.Wccp.address.FreeAddrInfo(local);
184 }
185
186
187 void
188 wccpConnectionClose(void)
189 {
190 if (theWccpConnection > -1) {
191 debugs(80, 1, "FD " << theWccpConnection << " Closing WCCPv1 socket");
192 comm_close(theWccpConnection);
193 theWccpConnection = -1;
194 }
195 }
196
197 /*
198 * Functions for handling the requests.
199 */
200
201 /*
202 * Accept the UDP packet
203 */
204 static void
205 wccpHandleUdp(int sock, void *not_used)
206 {
207 Ip::Address from;
208 int len;
209
210 debugs(80, 6, "wccpHandleUdp: Called.");
211
212 Comm::SetSelect(sock, COMM_SELECT_READ, wccpHandleUdp, NULL, 0);
213
214 memset(&wccp_i_see_you, '\0', sizeof(wccp_i_see_you));
215
216 len = comm_udp_recvfrom(sock,
217 (void *) &wccp_i_see_you,
218 sizeof(wccp_i_see_you),
219 0,
220 from);
221 debugs(80, 3, "wccpHandleUdp: " << len << " bytes WCCP pkt from " << from <<
222 ": type=" <<
223 (unsigned) ntohl(wccp_i_see_you.type) << ", version=" <<
224 (unsigned) ntohl(wccp_i_see_you.version) << ", change=" <<
225 (unsigned) ntohl(wccp_i_see_you.change) << ", id=" <<
226 (unsigned) ntohl(wccp_i_see_you.id) << ", number=" <<
227 (unsigned) ntohl(wccp_i_see_you.number));
228
229 if (len < 0)
230 return;
231
232 if (from != Config.Wccp.router)
233 return;
234
235 if ((unsigned) ntohl(wccp_i_see_you.version) != (unsigned) Config.Wccp.version)
236 return;
237
238 if (ntohl(wccp_i_see_you.type) != WCCP_I_SEE_YOU)
239 return;
240
241 if (ntohl(wccp_i_see_you.number) > WCCP_ACTIVE_CACHES) {
242 debugs(80, 1, "Ignoring WCCP_I_SEE_YOU from " <<
243 from << " with number of caches set to " <<
244 (int) ntohl(wccp_i_see_you.number));
245
246 return;
247 }
248
249 last_id = wccp_i_see_you.id;
250
251 if ((0 == last_change) && (number_caches == (unsigned) ntohl(wccp_i_see_you.number))) {
252 if (last_assign_buckets_change == wccp_i_see_you.change) {
253 /*
254 * After a WCCP_ASSIGN_BUCKET message, the router should
255 * update the change value. If not, maybe the route didn't
256 * receive our WCCP_ASSIGN_BUCKET message, so send it again.
257 *
258 * Don't update change here. Instead, fall through to
259 * the next block to call wccpAssignBuckets() again.
260 */
261 (void) 0;
262 } else {
263 last_change = wccp_i_see_you.change;
264 return;
265 }
266 }
267
268 if (last_change != wccp_i_see_you.change) {
269 last_change = wccp_i_see_you.change;
270
271 if (wccpLowestIP() && wccp_i_see_you.number) {
272 last_assign_buckets_change = last_change;
273 wccpAssignBuckets();
274 }
275 }
276 }
277
278 static int
279 wccpLowestIP(void)
280 {
281 unsigned int loop;
282 int found = 0;
283
284 /*
285 * We sanity checked wccp_i_see_you.number back in wccpHandleUdp()
286 */
287
288 for (loop = 0; loop < (unsigned) ntohl(wccp_i_see_you.number); loop++) {
289 assert(loop < WCCP_ACTIVE_CACHES);
290
291 if (local_ip > wccp_i_see_you.wccp_cache_entry[loop].ip_addr)
292 return 0;
293
294 if (local_ip == wccp_i_see_you.wccp_cache_entry[loop].ip_addr)
295 found = 1;
296 }
297
298 return found;
299 }
300
301 static void
302 wccpHereIam(void *voidnotused)
303 {
304 debugs(80, 6, "wccpHereIam: Called");
305
306 wccp_here_i_am.id = last_id;
307 comm_udp_send(theWccpConnection,
308 &wccp_here_i_am,
309 sizeof(wccp_here_i_am),
310 0);
311
312 if (!eventFind(wccpHereIam, NULL))
313 eventAdd("wccpHereIam", wccpHereIam, NULL, 10.0, 1);
314 }
315
316 static void
317 wccpAssignBuckets(void)
318 {
319
320 struct wccp_assign_bucket_t *wccp_assign_bucket;
321 int wab_len;
322 char *buckets;
323 int buckets_per_cache;
324 unsigned int loop;
325 int bucket = 0;
326 int *caches;
327 int cache_len;
328 char *buf;
329
330 debugs(80, 6, "wccpAssignBuckets: Called");
331 number_caches = ntohl(wccp_i_see_you.number);
332
333 assert(number_caches > 0);
334 assert(number_caches <= WCCP_ACTIVE_CACHES);
335
336 wab_len = sizeof(struct wccp_assign_bucket_t);
337
338 cache_len = WCCP_CACHE_LEN * number_caches;
339
340 buf = (char *)xmalloc(wab_len +
341 WCCP_BUCKETS +
342 cache_len);
343
344 wccp_assign_bucket = (struct wccp_assign_bucket_t *) buf;
345
346 caches = (int *) (buf + wab_len);
347
348 buckets = buf + wab_len + cache_len;
349
350 memset(wccp_assign_bucket, '\0', sizeof(wccp_assign_bucket));
351
352 memset(buckets, 0xFF, WCCP_BUCKETS);
353
354 buckets_per_cache = WCCP_BUCKETS / number_caches;
355
356 for (loop = 0; loop < number_caches; loop++) {
357 int i;
358 memcpy(&caches[loop],
359 &wccp_i_see_you.wccp_cache_entry[loop].ip_addr,
360 sizeof(*caches));
361
362 for (i = 0; i < buckets_per_cache; i++) {
363 assert(bucket < WCCP_BUCKETS);
364 buckets[bucket++] = loop;
365 }
366 }
367
368 while (bucket < WCCP_BUCKETS) {
369 buckets[bucket++] = number_caches - 1;
370 }
371
372 wccp_assign_bucket->type = htonl(WCCP_ASSIGN_BUCKET);
373 wccp_assign_bucket->id = wccp_i_see_you.id;
374 wccp_assign_bucket->number = wccp_i_see_you.number;
375
376 comm_udp_send(theWccpConnection,
377 buf,
378 wab_len + WCCP_BUCKETS + cache_len,
379 0);
380 last_change = 0;
381 xfree(buf);
382 }
383
384 #endif /* USE_WCCP */