5 * DEBUG: section 80 WCCP Support
6 * AUTHOR: Glenn Chisholm
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
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.
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.
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.
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.
39 #include "squid-old.h"
41 #include "comm/Connection.h"
42 #include "comm/Loops.h"
45 #define WCCP_PORT 2048
46 #define WCCP_REVISION 0
47 #define WCCP_ACTIVE_CACHES 32
48 #define WCCP_HASH_SIZE 32
49 #define WCCP_BUCKETS 256
50 #define WCCP_CACHE_LEN 4
52 #define WCCP_HERE_I_AM 7
53 #define WCCP_I_SEE_YOU 8
54 #define WCCP_ASSIGN_BUCKET 9
56 struct wccp_here_i_am_t
{
60 char hash
[WCCP_HASH_SIZE
];
65 struct wccp_cache_entry_t
{
66 struct in_addr ip_addr
; // WCCP on-the-wire in 32-bit IPv4-only.
68 char hash
[WCCP_HASH_SIZE
];
72 struct wccp_i_see_you_t
{
79 struct wccp_cache_entry_t wccp_cache_entry
[WCCP_ACTIVE_CACHES
];
82 struct wccp_assign_bucket_t
{
88 static int theWccpConnection
= -1;
90 static struct wccp_here_i_am_t wccp_here_i_am
;
92 static struct wccp_i_see_you_t wccp_i_see_you
;
93 static int last_change
;
95 static int last_assign_buckets_change
;
96 static unsigned int number_caches
;
98 static Ip::Address local_ip
;
100 static PF wccpHandleUdp
;
101 static int wccpLowestIP(void);
102 static EVH wccpHereIam
;
103 static void wccpAssignBuckets(void);
106 * The functions used during startup:
109 * wccpConnectionShutdown
110 * wccpConnectionClose
116 debugs(80, 5, "wccpInit: Called");
117 memset(&wccp_here_i_am
, '\0', sizeof(wccp_here_i_am
));
118 wccp_here_i_am
.type
= htonl(WCCP_HERE_I_AM
);
119 wccp_here_i_am
.version
= htonl(Config
.Wccp
.version
);
120 wccp_here_i_am
.revision
= htonl(WCCP_REVISION
);
123 last_assign_buckets_change
= 0;
126 if (!Config
.Wccp
.router
.IsAnyAddr())
127 if (!eventFind(wccpHereIam
, NULL
))
128 eventAdd("wccpHereIam", wccpHereIam
, NULL
, 5.0, 1);
132 wccpConnectionOpen(void)
134 debugs(80, 5, "wccpConnectionOpen: Called");
136 if (Config
.Wccp
.router
.IsAnyAddr()) {
137 debugs(80, 2, "WCCPv1 disabled.");
141 if ( !Config
.Wccp
.router
.SetIPv4() ) {
142 debugs(80, DBG_CRITICAL
, "WCCPv1 Disabled. Router " << Config
.Wccp
.router
<< " is not an IPv4 address.");
146 if ( !Config
.Wccp
.address
.SetIPv4() ) {
147 debugs(80, DBG_CRITICAL
, "WCCPv1 Disabled. Local address " << Config
.Wccp
.address
<< " is not an IPv4 address.");
151 Config
.Wccp
.address
.SetPort(WCCP_PORT
);
152 Config
.Wccp
.router
.SetPort(WCCP_PORT
);
154 theWccpConnection
= comm_open_listener(SOCK_DGRAM
,
160 if (theWccpConnection
< 0)
161 fatal("Cannot open WCCP Port");
163 Comm::SetSelect(theWccpConnection
, COMM_SELECT_READ
, wccpHandleUdp
, NULL
, 0);
165 debugs(80, 1, "Accepting WCCPv1 messages on " << Config
.Wccp
.address
<< ", FD " << theWccpConnection
<< ".");
168 // Sadly WCCP only does IPv4
170 struct sockaddr_in router
;
171 Config
.Wccp
.router
.GetSockAddr(router
);
172 if (connect(theWccpConnection
, (struct sockaddr
*)&router
, sizeof(router
)))
173 fatal("Unable to connect WCCP out socket");
175 struct sockaddr_in local
;
176 memset(&local
, '\0', sizeof(local
));
177 socklen_t slen
= sizeof(local
);
178 if (getsockname(theWccpConnection
, (struct sockaddr
*)&local
, &slen
))
179 fatal("Unable to getsockname on WCCP out socket");
186 wccpConnectionClose(void)
188 if (theWccpConnection
> -1) {
189 debugs(80, 1, "FD " << theWccpConnection
<< " Closing WCCPv1 socket");
190 comm_close(theWccpConnection
);
191 theWccpConnection
= -1;
196 * Functions for handling the requests.
200 * Accept the UDP packet
203 wccpHandleUdp(int sock
, void *not_used
)
208 debugs(80, 6, "wccpHandleUdp: Called.");
210 Comm::SetSelect(sock
, COMM_SELECT_READ
, wccpHandleUdp
, NULL
, 0);
212 memset(&wccp_i_see_you
, '\0', sizeof(wccp_i_see_you
));
214 len
= comm_udp_recvfrom(sock
,
215 (void *) &wccp_i_see_you
,
216 sizeof(wccp_i_see_you
),
219 debugs(80, 3, "wccpHandleUdp: " << len
<< " bytes WCCP pkt from " << from
<<
221 (unsigned) ntohl(wccp_i_see_you
.type
) << ", version=" <<
222 (unsigned) ntohl(wccp_i_see_you
.version
) << ", change=" <<
223 (unsigned) ntohl(wccp_i_see_you
.change
) << ", id=" <<
224 (unsigned) ntohl(wccp_i_see_you
.id
) << ", number=" <<
225 (unsigned) ntohl(wccp_i_see_you
.number
));
230 if (from
!= Config
.Wccp
.router
)
233 if ((unsigned) ntohl(wccp_i_see_you
.version
) != (unsigned) Config
.Wccp
.version
)
236 if (ntohl(wccp_i_see_you
.type
) != WCCP_I_SEE_YOU
)
239 if (ntohl(wccp_i_see_you
.number
) > WCCP_ACTIVE_CACHES
) {
240 debugs(80, 1, "Ignoring WCCP_I_SEE_YOU from " <<
241 from
<< " with number of caches set to " <<
242 (int) ntohl(wccp_i_see_you
.number
));
247 last_id
= wccp_i_see_you
.id
;
249 if ((0 == last_change
) && (number_caches
== (unsigned) ntohl(wccp_i_see_you
.number
))) {
250 if (last_assign_buckets_change
== wccp_i_see_you
.change
) {
252 * After a WCCP_ASSIGN_BUCKET message, the router should
253 * update the change value. If not, maybe the route didn't
254 * receive our WCCP_ASSIGN_BUCKET message, so send it again.
256 * Don't update change here. Instead, fall through to
257 * the next block to call wccpAssignBuckets() again.
261 last_change
= wccp_i_see_you
.change
;
266 if (last_change
!= wccp_i_see_you
.change
) {
267 last_change
= wccp_i_see_you
.change
;
269 if (wccpLowestIP() && wccp_i_see_you
.number
) {
270 last_assign_buckets_change
= last_change
;
283 * We sanity checked wccp_i_see_you.number back in wccpHandleUdp()
286 for (loop
= 0; loop
< (unsigned) ntohl(wccp_i_see_you
.number
); loop
++) {
287 assert(loop
< WCCP_ACTIVE_CACHES
);
289 if (local_ip
> wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
)
292 if (local_ip
== wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
)
300 wccpHereIam(void *voidnotused
)
302 debugs(80, 6, "wccpHereIam: Called");
304 wccp_here_i_am
.id
= last_id
;
305 comm_udp_send(theWccpConnection
,
307 sizeof(wccp_here_i_am
),
310 if (!eventFind(wccpHereIam
, NULL
))
311 eventAdd("wccpHereIam", wccpHereIam
, NULL
, 10.0, 1);
315 wccpAssignBuckets(void)
318 struct wccp_assign_bucket_t
*wccp_assign_bucket
;
321 int buckets_per_cache
;
328 debugs(80, 6, "wccpAssignBuckets: Called");
329 number_caches
= ntohl(wccp_i_see_you
.number
);
331 assert(number_caches
> 0);
332 assert(number_caches
<= WCCP_ACTIVE_CACHES
);
334 wab_len
= sizeof(struct wccp_assign_bucket_t
);
336 cache_len
= WCCP_CACHE_LEN
* number_caches
;
338 buf
= (char *)xmalloc(wab_len
+
342 wccp_assign_bucket
= (struct wccp_assign_bucket_t
*) buf
;
344 caches
= (int *) (buf
+ wab_len
);
346 buckets
= buf
+ wab_len
+ cache_len
;
348 memset(wccp_assign_bucket
, '\0', sizeof(*wccp_assign_bucket
));
350 memset(buckets
, 0xFF, WCCP_BUCKETS
);
352 buckets_per_cache
= WCCP_BUCKETS
/ number_caches
;
354 for (loop
= 0; loop
< number_caches
; loop
++) {
356 memcpy(&caches
[loop
],
357 &wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
,
360 for (i
= 0; i
< buckets_per_cache
; i
++) {
361 assert(bucket
< WCCP_BUCKETS
);
362 buckets
[bucket
++] = loop
;
366 while (bucket
< WCCP_BUCKETS
) {
367 buckets
[bucket
++] = number_caches
- 1;
370 wccp_assign_bucket
->type
= htonl(WCCP_ASSIGN_BUCKET
);
371 wccp_assign_bucket
->id
= wccp_i_see_you
.id
;
372 wccp_assign_bucket
->number
= wccp_i_see_you
.number
;
374 comm_udp_send(theWccpConnection
,
376 wab_len
+ WCCP_BUCKETS
+ cache_len
,
382 #endif /* USE_WCCP */