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