3 * DEBUG: section 80 WCCP Support
4 * AUTHOR: Glenn Chisholm
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
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.
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.
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.
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.
37 #include "comm/Connection.h"
38 #include "comm/Loops.h"
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
49 #define WCCP_HERE_I_AM 7
50 #define WCCP_I_SEE_YOU 8
51 #define WCCP_ASSIGN_BUCKET 9
53 struct wccp_here_i_am_t
{
57 char hash
[WCCP_HASH_SIZE
];
62 struct wccp_cache_entry_t
{
63 struct in_addr ip_addr
; // WCCP on-the-wire in 32-bit IPv4-only.
65 char hash
[WCCP_HASH_SIZE
];
69 struct wccp_i_see_you_t
{
76 struct wccp_cache_entry_t wccp_cache_entry
[WCCP_ACTIVE_CACHES
];
79 struct wccp_assign_bucket_t
{
85 static int theWccpConnection
= -1;
87 static struct wccp_here_i_am_t wccp_here_i_am
;
89 static struct wccp_i_see_you_t wccp_i_see_you
;
90 static int last_change
;
92 static int last_assign_buckets_change
;
93 static unsigned int number_caches
;
95 static Ip::Address local_ip
;
97 static PF wccpHandleUdp
;
98 static int wccpLowestIP(void);
99 static EVH wccpHereIam
;
100 static void wccpAssignBuckets(void);
103 * The functions used during startup:
106 * wccpConnectionShutdown
107 * wccpConnectionClose
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
);
120 last_assign_buckets_change
= 0;
123 if (!Config
.Wccp
.router
.IsAnyAddr())
124 if (!eventFind(wccpHereIam
, NULL
))
125 eventAdd("wccpHereIam", wccpHereIam
, NULL
, 5.0, 1);
129 wccpConnectionOpen(void)
131 debugs(80, 5, "wccpConnectionOpen: Called");
133 if (Config
.Wccp
.router
.IsAnyAddr()) {
134 debugs(80, 2, "WCCPv1 disabled.");
138 if ( !Config
.Wccp
.router
.SetIPv4() ) {
139 debugs(80, DBG_CRITICAL
, "WCCPv1 Disabled. Router " << Config
.Wccp
.router
<< " is not an IPv4 address.");
143 if ( !Config
.Wccp
.address
.SetIPv4() ) {
144 debugs(80, DBG_CRITICAL
, "WCCPv1 Disabled. Local address " << Config
.Wccp
.address
<< " is not an IPv4 address.");
148 Config
.Wccp
.address
.SetPort(WCCP_PORT
);
149 Config
.Wccp
.router
.SetPort(WCCP_PORT
);
151 theWccpConnection
= comm_open_listener(SOCK_DGRAM
,
157 if (theWccpConnection
< 0)
158 fatal("Cannot open WCCP Port");
160 Comm::SetSelect(theWccpConnection
, COMM_SELECT_READ
, wccpHandleUdp
, NULL
, 0);
162 debugs(80, DBG_IMPORTANT
, "Accepting WCCPv1 messages on " << Config
.Wccp
.address
<< ", FD " << theWccpConnection
<< ".");
164 // Sadly WCCP only does IPv4
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");
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");
181 wccpConnectionClose(void)
183 if (theWccpConnection
> -1) {
184 debugs(80, DBG_IMPORTANT
, "FD " << theWccpConnection
<< " Closing WCCPv1 socket");
185 comm_close(theWccpConnection
);
186 theWccpConnection
= -1;
191 * Functions for handling the requests.
195 * Accept the UDP packet
198 wccpHandleUdp(int sock
, void *not_used
)
203 debugs(80, 6, "wccpHandleUdp: Called.");
205 Comm::SetSelect(sock
, COMM_SELECT_READ
, wccpHandleUdp
, NULL
, 0);
207 memset(&wccp_i_see_you
, '\0', sizeof(wccp_i_see_you
));
209 len
= comm_udp_recvfrom(sock
,
210 (void *) &wccp_i_see_you
,
211 sizeof(wccp_i_see_you
),
214 debugs(80, 3, "wccpHandleUdp: " << len
<< " bytes WCCP pkt from " << from
<<
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
));
225 if (from
!= Config
.Wccp
.router
)
228 if ((unsigned) ntohl(wccp_i_see_you
.version
) != (unsigned) Config
.Wccp
.version
)
231 if (ntohl(wccp_i_see_you
.type
) != WCCP_I_SEE_YOU
)
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
));
242 last_id
= wccp_i_see_you
.id
;
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
) {
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.
251 * Don't update change here. Instead, fall through to
252 * the next block to call wccpAssignBuckets() again.
256 last_change
= wccp_i_see_you
.change
;
261 if (last_change
!= wccp_i_see_you
.change
) {
262 last_change
= wccp_i_see_you
.change
;
264 if (wccpLowestIP() && wccp_i_see_you
.number
) {
265 last_assign_buckets_change
= last_change
;
278 * We sanity checked wccp_i_see_you.number back in wccpHandleUdp()
281 for (loop
= 0; loop
< (unsigned) ntohl(wccp_i_see_you
.number
); ++loop
) {
282 assert(loop
< WCCP_ACTIVE_CACHES
);
284 if (local_ip
> wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
)
287 if (local_ip
== wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
)
295 wccpHereIam(void *voidnotused
)
297 debugs(80, 6, "wccpHereIam: Called");
299 wccp_here_i_am
.id
= last_id
;
300 comm_udp_send(theWccpConnection
,
302 sizeof(wccp_here_i_am
),
305 if (!eventFind(wccpHereIam
, NULL
))
306 eventAdd("wccpHereIam", wccpHereIam
, NULL
, 10.0, 1);
310 wccpAssignBuckets(void)
313 struct wccp_assign_bucket_t
*wccp_assign_bucket
;
316 int buckets_per_cache
;
323 debugs(80, 6, "wccpAssignBuckets: Called");
324 number_caches
= ntohl(wccp_i_see_you
.number
);
326 assert(number_caches
> 0);
327 assert(number_caches
<= WCCP_ACTIVE_CACHES
);
329 wab_len
= sizeof(struct wccp_assign_bucket_t
);
331 cache_len
= WCCP_CACHE_LEN
* number_caches
;
333 buf
= (char *)xmalloc(wab_len
+
337 wccp_assign_bucket
= (struct wccp_assign_bucket_t
*) buf
;
339 caches
= (int *) (buf
+ wab_len
);
341 buckets
= buf
+ wab_len
+ cache_len
;
343 memset(wccp_assign_bucket
, '\0', sizeof(*wccp_assign_bucket
));
345 memset(buckets
, 0xFF, WCCP_BUCKETS
);
347 buckets_per_cache
= WCCP_BUCKETS
/ number_caches
;
349 for (loop
= 0; loop
< number_caches
; ++loop
) {
351 memcpy(&caches
[loop
],
352 &wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
,
355 for (i
= 0; i
< buckets_per_cache
; ++i
) {
356 assert(bucket
< WCCP_BUCKETS
);
357 buckets
[bucket
] = loop
;
362 while (bucket
< WCCP_BUCKETS
) {
363 buckets
[bucket
] = number_caches
- 1;
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
;
371 comm_udp_send(theWccpConnection
,
373 wab_len
+ WCCP_BUCKETS
+ cache_len
,
379 #endif /* USE_WCCP */