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 "comm/Connection.h"
40 #include "comm/Loops.h"
44 #define WCCP_PORT 2048
45 #define WCCP_REVISION 0
46 #define WCCP_ACTIVE_CACHES 32
47 #define WCCP_HASH_SIZE 32
48 #define WCCP_BUCKETS 256
49 #define WCCP_CACHE_LEN 4
51 #define WCCP_HERE_I_AM 7
52 #define WCCP_I_SEE_YOU 8
53 #define WCCP_ASSIGN_BUCKET 9
55 struct wccp_here_i_am_t
{
59 char hash
[WCCP_HASH_SIZE
];
64 struct wccp_cache_entry_t
{
65 struct in_addr ip_addr
; // WCCP on-the-wire in 32-bit IPv4-only.
67 char hash
[WCCP_HASH_SIZE
];
71 struct wccp_i_see_you_t
{
78 struct wccp_cache_entry_t wccp_cache_entry
[WCCP_ACTIVE_CACHES
];
81 struct wccp_assign_bucket_t
{
87 static int theWccpConnection
= -1;
89 static struct wccp_here_i_am_t wccp_here_i_am
;
91 static struct wccp_i_see_you_t wccp_i_see_you
;
92 static int last_change
;
94 static int last_assign_buckets_change
;
95 static unsigned int number_caches
;
97 static Ip::Address local_ip
;
99 static PF wccpHandleUdp
;
100 static int wccpLowestIP(void);
101 static EVH wccpHereIam
;
102 static void wccpAssignBuckets(void);
105 * The functions used during startup:
108 * wccpConnectionShutdown
109 * wccpConnectionClose
115 debugs(80, 5, "wccpInit: Called");
116 memset(&wccp_here_i_am
, '\0', sizeof(wccp_here_i_am
));
117 wccp_here_i_am
.type
= htonl(WCCP_HERE_I_AM
);
118 wccp_here_i_am
.version
= htonl(Config
.Wccp
.version
);
119 wccp_here_i_am
.revision
= htonl(WCCP_REVISION
);
122 last_assign_buckets_change
= 0;
125 if (!Config
.Wccp
.router
.IsAnyAddr())
126 if (!eventFind(wccpHereIam
, NULL
))
127 eventAdd("wccpHereIam", wccpHereIam
, NULL
, 5.0, 1);
131 wccpConnectionOpen(void)
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(80, DBG_CRITICAL
, "WCCPv1 Disabled. Router " << Config
.Wccp
.router
<< " is not an IPv4 address.");
145 if ( !Config
.Wccp
.address
.SetIPv4() ) {
146 debugs(80, DBG_CRITICAL
, "WCCPv1 Disabled. Local address " << Config
.Wccp
.address
<< " is not an IPv4 address.");
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 Comm::SetSelect(theWccpConnection
, COMM_SELECT_READ
, wccpHandleUdp
, NULL
, 0);
164 debugs(80, DBG_IMPORTANT
, "Accepting WCCPv1 messages on " << Config
.Wccp
.address
<< ", FD " << theWccpConnection
<< ".");
166 // Sadly WCCP only does IPv4
168 struct sockaddr_in router
;
169 Config
.Wccp
.router
.GetSockAddr(router
);
170 if (connect(theWccpConnection
, (struct sockaddr
*)&router
, sizeof(router
)))
171 fatal("Unable to connect WCCP out socket");
173 struct sockaddr_in local
;
174 memset(&local
, '\0', sizeof(local
));
175 socklen_t slen
= sizeof(local
);
176 if (getsockname(theWccpConnection
, (struct sockaddr
*)&local
, &slen
))
177 fatal("Unable to getsockname on WCCP out socket");
183 wccpConnectionClose(void)
185 if (theWccpConnection
> -1) {
186 debugs(80, DBG_IMPORTANT
, "FD " << theWccpConnection
<< " Closing WCCPv1 socket");
187 comm_close(theWccpConnection
);
188 theWccpConnection
= -1;
193 * Functions for handling the requests.
197 * Accept the UDP packet
200 wccpHandleUdp(int sock
, void *not_used
)
205 debugs(80, 6, "wccpHandleUdp: Called.");
207 Comm::SetSelect(sock
, COMM_SELECT_READ
, wccpHandleUdp
, NULL
, 0);
209 memset(&wccp_i_see_you
, '\0', sizeof(wccp_i_see_you
));
211 len
= comm_udp_recvfrom(sock
,
212 (void *) &wccp_i_see_you
,
213 sizeof(wccp_i_see_you
),
216 debugs(80, 3, "wccpHandleUdp: " << len
<< " bytes WCCP pkt from " << from
<<
218 (unsigned) ntohl(wccp_i_see_you
.type
) << ", version=" <<
219 (unsigned) ntohl(wccp_i_see_you
.version
) << ", change=" <<
220 (unsigned) ntohl(wccp_i_see_you
.change
) << ", id=" <<
221 (unsigned) ntohl(wccp_i_see_you
.id
) << ", number=" <<
222 (unsigned) ntohl(wccp_i_see_you
.number
));
227 if (from
!= Config
.Wccp
.router
)
230 if ((unsigned) ntohl(wccp_i_see_you
.version
) != (unsigned) Config
.Wccp
.version
)
233 if (ntohl(wccp_i_see_you
.type
) != WCCP_I_SEE_YOU
)
236 if (ntohl(wccp_i_see_you
.number
) > WCCP_ACTIVE_CACHES
) {
237 debugs(80, DBG_IMPORTANT
, "Ignoring WCCP_I_SEE_YOU from " <<
238 from
<< " with number of caches set to " <<
239 (int) ntohl(wccp_i_see_you
.number
));
244 last_id
= wccp_i_see_you
.id
;
246 if ((0 == last_change
) && (number_caches
== (unsigned) ntohl(wccp_i_see_you
.number
))) {
247 if (last_assign_buckets_change
== wccp_i_see_you
.change
) {
249 * After a WCCP_ASSIGN_BUCKET message, the router should
250 * update the change value. If not, maybe the route didn't
251 * receive our WCCP_ASSIGN_BUCKET message, so send it again.
253 * Don't update change here. Instead, fall through to
254 * the next block to call wccpAssignBuckets() again.
258 last_change
= wccp_i_see_you
.change
;
263 if (last_change
!= wccp_i_see_you
.change
) {
264 last_change
= wccp_i_see_you
.change
;
266 if (wccpLowestIP() && wccp_i_see_you
.number
) {
267 last_assign_buckets_change
= last_change
;
280 * We sanity checked wccp_i_see_you.number back in wccpHandleUdp()
283 for (loop
= 0; loop
< (unsigned) ntohl(wccp_i_see_you
.number
); ++loop
) {
284 assert(loop
< WCCP_ACTIVE_CACHES
);
286 if (local_ip
> wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
)
289 if (local_ip
== wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
)
297 wccpHereIam(void *voidnotused
)
299 debugs(80, 6, "wccpHereIam: Called");
301 wccp_here_i_am
.id
= last_id
;
302 comm_udp_send(theWccpConnection
,
304 sizeof(wccp_here_i_am
),
307 if (!eventFind(wccpHereIam
, NULL
))
308 eventAdd("wccpHereIam", wccpHereIam
, NULL
, 10.0, 1);
312 wccpAssignBuckets(void)
315 struct wccp_assign_bucket_t
*wccp_assign_bucket
;
318 int buckets_per_cache
;
325 debugs(80, 6, "wccpAssignBuckets: Called");
326 number_caches
= ntohl(wccp_i_see_you
.number
);
328 assert(number_caches
> 0);
329 assert(number_caches
<= WCCP_ACTIVE_CACHES
);
331 wab_len
= sizeof(struct wccp_assign_bucket_t
);
333 cache_len
= WCCP_CACHE_LEN
* number_caches
;
335 buf
= (char *)xmalloc(wab_len
+
339 wccp_assign_bucket
= (struct wccp_assign_bucket_t
*) buf
;
341 caches
= (int *) (buf
+ wab_len
);
343 buckets
= buf
+ wab_len
+ cache_len
;
345 memset(wccp_assign_bucket
, '\0', sizeof(*wccp_assign_bucket
));
347 memset(buckets
, 0xFF, WCCP_BUCKETS
);
349 buckets_per_cache
= WCCP_BUCKETS
/ number_caches
;
351 for (loop
= 0; loop
< number_caches
; ++loop
) {
353 memcpy(&caches
[loop
],
354 &wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
,
357 for (i
= 0; i
< buckets_per_cache
; ++i
) {
358 assert(bucket
< WCCP_BUCKETS
);
359 buckets
[bucket
] = loop
;
364 while (bucket
< WCCP_BUCKETS
) {
365 buckets
[bucket
] = number_caches
- 1;
369 wccp_assign_bucket
->type
= htonl(WCCP_ASSIGN_BUCKET
);
370 wccp_assign_bucket
->id
= wccp_i_see_you
.id
;
371 wccp_assign_bucket
->number
= wccp_i_see_you
.number
;
373 comm_udp_send(theWccpConnection
,
375 wab_len
+ WCCP_BUCKETS
+ cache_len
,
381 #endif /* USE_WCCP */