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.
40 #include "comm/Connection.h"
43 #define WCCP_PORT 2048
44 #define WCCP_REVISION 0
45 #define WCCP_ACTIVE_CACHES 32
46 #define WCCP_HASH_SIZE 32
47 #define WCCP_BUCKETS 256
48 #define WCCP_CACHE_LEN 4
50 #define WCCP_HERE_I_AM 7
51 #define WCCP_I_SEE_YOU 8
52 #define WCCP_ASSIGN_BUCKET 9
54 struct wccp_here_i_am_t
{
58 char hash
[WCCP_HASH_SIZE
];
63 struct wccp_cache_entry_t
{
64 struct in_addr ip_addr
; // WCCP on-the-wire in 32-bit IPv4-only.
66 char hash
[WCCP_HASH_SIZE
];
70 struct wccp_i_see_you_t
{
77 struct wccp_cache_entry_t wccp_cache_entry
[WCCP_ACTIVE_CACHES
];
80 struct wccp_assign_bucket_t
{
86 static int theWccpConnection
= -1;
88 static struct wccp_here_i_am_t wccp_here_i_am
;
90 static struct wccp_i_see_you_t wccp_i_see_you
;
91 static int last_change
;
93 static int last_assign_buckets_change
;
94 static unsigned int number_caches
;
96 static Ip::Address local_ip
;
98 static PF wccpHandleUdp
;
99 static int wccpLowestIP(void);
100 static EVH wccpHereIam
;
101 static void wccpAssignBuckets(void);
104 * The functions used during startup:
107 * wccpConnectionShutdown
108 * wccpConnectionClose
114 debugs(80, 5, "wccpInit: Called");
115 memset(&wccp_here_i_am
, '\0', sizeof(wccp_here_i_am
));
116 wccp_here_i_am
.type
= htonl(WCCP_HERE_I_AM
);
117 wccp_here_i_am
.version
= htonl(Config
.Wccp
.version
);
118 wccp_here_i_am
.revision
= htonl(WCCP_REVISION
);
121 last_assign_buckets_change
= 0;
124 if (!Config
.Wccp
.router
.IsAnyAddr())
125 if (!eventFind(wccpHereIam
, NULL
))
126 eventAdd("wccpHereIam", wccpHereIam
, NULL
, 5.0, 1);
130 wccpConnectionOpen(void)
132 struct addrinfo
*router
= NULL
, *local
= NULL
;
133 debugs(80, 5, "wccpConnectionOpen: Called");
135 if (Config
.Wccp
.router
.IsAnyAddr()) {
136 debugs(80, 2, "WCCPv1 disabled.");
140 if ( !Config
.Wccp
.router
.SetIPv4() ) {
141 debugs(1, 1, "WCCPv1 Disabled. Router " << Config
.Wccp
.router
<< " is not IPv4.");
145 if ( !Config
.Wccp
.address
.SetIPv4() ) {
146 debugs(1, 1, "WCCPv1 Disabled. Local address " << Config
.Wccp
.address
<< " is not IPv4.");
150 Config
.Wccp
.address
.SetPort(WCCP_PORT
);
151 Config
.Wccp
.router
.SetPort(WCCP_PORT
);
153 theWccpConnection
= comm_open_listener(SOCK_DGRAM
,
159 if (theWccpConnection
< 0)
160 fatal("Cannot open WCCP Port");
162 commSetSelect(theWccpConnection
,
168 debugs(80, 1, "Accepting WCCPv1 messages on " << Config
.Wccp
.address
<< ", FD " << theWccpConnection
<< ".");
170 Config
.Wccp
.router
.GetAddrInfo(router
,AF_INET
);
172 if (connect(theWccpConnection
, router
->ai_addr
, router
->ai_addrlen
))
173 fatal("Unable to connect WCCP out socket");
175 Config
.Wccp
.router
.FreeAddrInfo(router
);
177 Config
.Wccp
.address
.InitAddrInfo(local
);
179 if (getsockname(theWccpConnection
, local
->ai_addr
, &local
->ai_addrlen
))
180 fatal("Unable to getsockname on WCCP out socket");
184 Config
.Wccp
.address
.FreeAddrInfo(local
);
189 wccpConnectionClose(void)
191 if (theWccpConnection
> -1) {
192 debugs(80, 1, "FD " << theWccpConnection
<< " Closing WCCPv1 socket");
193 comm_close(theWccpConnection
);
194 theWccpConnection
= -1;
199 * Functions for handling the requests.
203 * Accept the UDP packet
206 wccpHandleUdp(int sock
, void *not_used
)
211 debugs(80, 6, "wccpHandleUdp: Called.");
213 commSetSelect(sock
, COMM_SELECT_READ
, wccpHandleUdp
, NULL
, 0);
215 memset(&wccp_i_see_you
, '\0', sizeof(wccp_i_see_you
));
217 len
= comm_udp_recvfrom(sock
,
218 (void *) &wccp_i_see_you
,
219 sizeof(wccp_i_see_you
),
222 debugs(80, 3, "wccpHandleUdp: " << len
<< " bytes WCCP pkt from " << from
<<
224 (unsigned) ntohl(wccp_i_see_you
.type
) << ", version=" <<
225 (unsigned) ntohl(wccp_i_see_you
.version
) << ", change=" <<
226 (unsigned) ntohl(wccp_i_see_you
.change
) << ", id=" <<
227 (unsigned) ntohl(wccp_i_see_you
.id
) << ", number=" <<
228 (unsigned) ntohl(wccp_i_see_you
.number
));
233 if (from
!= Config
.Wccp
.router
)
236 if ((unsigned) ntohl(wccp_i_see_you
.version
) != (unsigned) Config
.Wccp
.version
)
239 if (ntohl(wccp_i_see_you
.type
) != WCCP_I_SEE_YOU
)
242 if (ntohl(wccp_i_see_you
.number
) > WCCP_ACTIVE_CACHES
) {
243 debugs(80, 1, "Ignoring WCCP_I_SEE_YOU from " <<
244 from
<< " with number of caches set to " <<
245 (int) ntohl(wccp_i_see_you
.number
));
250 last_id
= wccp_i_see_you
.id
;
252 if ((0 == last_change
) && (number_caches
== (unsigned) ntohl(wccp_i_see_you
.number
))) {
253 if (last_assign_buckets_change
== wccp_i_see_you
.change
) {
255 * After a WCCP_ASSIGN_BUCKET message, the router should
256 * update the change value. If not, maybe the route didn't
257 * receive our WCCP_ASSIGN_BUCKET message, so send it again.
259 * Don't update change here. Instead, fall through to
260 * the next block to call wccpAssignBuckets() again.
264 last_change
= wccp_i_see_you
.change
;
269 if (last_change
!= wccp_i_see_you
.change
) {
270 last_change
= wccp_i_see_you
.change
;
272 if (wccpLowestIP() && wccp_i_see_you
.number
) {
273 last_assign_buckets_change
= last_change
;
286 * We sanity checked wccp_i_see_you.number back in wccpHandleUdp()
289 for (loop
= 0; loop
< (unsigned) ntohl(wccp_i_see_you
.number
); loop
++) {
290 assert(loop
< WCCP_ACTIVE_CACHES
);
292 if (local_ip
> wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
)
295 if (local_ip
== wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
)
303 wccpHereIam(void *voidnotused
)
305 debugs(80, 6, "wccpHereIam: Called");
307 wccp_here_i_am
.id
= last_id
;
308 comm_udp_send(theWccpConnection
,
310 sizeof(wccp_here_i_am
),
313 if (!eventFind(wccpHereIam
, NULL
))
314 eventAdd("wccpHereIam", wccpHereIam
, NULL
, 10.0, 1);
318 wccpAssignBuckets(void)
321 struct wccp_assign_bucket_t
*wccp_assign_bucket
;
324 int buckets_per_cache
;
331 debugs(80, 6, "wccpAssignBuckets: Called");
332 number_caches
= ntohl(wccp_i_see_you
.number
);
334 assert(number_caches
> 0);
335 assert(number_caches
<= WCCP_ACTIVE_CACHES
);
337 wab_len
= sizeof(struct wccp_assign_bucket_t
);
339 cache_len
= WCCP_CACHE_LEN
* number_caches
;
341 buf
= (char *)xmalloc(wab_len
+
345 wccp_assign_bucket
= (struct wccp_assign_bucket_t
*) buf
;
347 caches
= (int *) (buf
+ wab_len
);
349 buckets
= buf
+ wab_len
+ cache_len
;
351 memset(wccp_assign_bucket
, '\0', sizeof(wccp_assign_bucket
));
353 memset(buckets
, 0xFF, WCCP_BUCKETS
);
355 buckets_per_cache
= WCCP_BUCKETS
/ number_caches
;
357 for (loop
= 0; loop
< number_caches
; loop
++) {
359 xmemcpy(&caches
[loop
],
360 &wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
,
363 for (i
= 0; i
< buckets_per_cache
; i
++) {
364 assert(bucket
< WCCP_BUCKETS
);
365 buckets
[bucket
++] = loop
;
369 while (bucket
< WCCP_BUCKETS
) {
370 buckets
[bucket
++] = number_caches
- 1;
373 wccp_assign_bucket
->type
= htonl(WCCP_ASSIGN_BUCKET
);
374 wccp_assign_bucket
->id
= wccp_i_see_you
.id
;
375 wccp_assign_bucket
->number
= wccp_i_see_you
.number
;
377 comm_udp_send(theWccpConnection
,
379 wab_len
+ WCCP_BUCKETS
+ cache_len
,
385 #endif /* USE_WCCP */