]> git.ipfire.org Git - thirdparty/squid.git/blob - src/wccp.cc
Polish: update Ip::Address to follow Squid coding guidelines
[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.port(WCCP_PORT);
149 Config.Wccp.router.port(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 double interval = 10.0; // TODO: make this configurable, possibly negotiate with the router.
301 errno = 0;
302 ssize_t sent = comm_udp_send(theWccpConnection, &wccp_here_i_am, sizeof(wccp_here_i_am), 0);
303
304 // if we failed to send the whole lot, try again at a shorter interval (20%)
305 if (sent != sizeof(wccp_here_i_am)) {
306 debugs(80, 2, "ERROR: failed to send WCCP HERE_I_AM packet: " << xstrerror());
307 interval = 2.0;
308 }
309
310 if (!eventFind(wccpHereIam, NULL))
311 eventAdd("wccpHereIam", wccpHereIam, NULL, interval, 1);
312 }
313
314 static void
315 wccpAssignBuckets(void)
316 {
317
318 struct wccp_assign_bucket_t *wccp_assign_bucket;
319 int wab_len;
320 char *buckets;
321 int buckets_per_cache;
322 unsigned int loop;
323 int bucket = 0;
324 int *caches;
325 int cache_len;
326 char *buf;
327
328 debugs(80, 6, "wccpAssignBuckets: Called");
329 number_caches = ntohl(wccp_i_see_you.number);
330
331 assert(number_caches > 0);
332 assert(number_caches <= WCCP_ACTIVE_CACHES);
333
334 wab_len = sizeof(struct wccp_assign_bucket_t);
335
336 cache_len = WCCP_CACHE_LEN * number_caches;
337
338 buf = (char *)xmalloc(wab_len +
339 WCCP_BUCKETS +
340 cache_len);
341
342 wccp_assign_bucket = (struct wccp_assign_bucket_t *) buf;
343
344 caches = (int *) (buf + wab_len);
345
346 buckets = buf + wab_len + cache_len;
347
348 memset(wccp_assign_bucket, '\0', sizeof(*wccp_assign_bucket));
349
350 memset(buckets, 0xFF, WCCP_BUCKETS);
351
352 buckets_per_cache = WCCP_BUCKETS / number_caches;
353
354 for (loop = 0; loop < number_caches; ++loop) {
355 int i;
356 memcpy(&caches[loop],
357 &wccp_i_see_you.wccp_cache_entry[loop].ip_addr,
358 sizeof(*caches));
359
360 for (i = 0; i < buckets_per_cache; ++i) {
361 assert(bucket < WCCP_BUCKETS);
362 buckets[bucket] = loop;
363 ++bucket;
364 }
365 }
366
367 while (bucket < WCCP_BUCKETS) {
368 buckets[bucket] = number_caches - 1;
369 ++bucket;
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 */