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.
42 #include "comm/Connection.h"
43 #include "comm/Loops.h"
46 #define WCCP_PORT 2048
47 #define WCCP_REVISION 0
48 #define WCCP_ACTIVE_CACHES 32
49 #define WCCP_HASH_SIZE 32
50 #define WCCP_BUCKETS 256
51 #define WCCP_CACHE_LEN 4
53 #define WCCP_HERE_I_AM 7
54 #define WCCP_I_SEE_YOU 8
55 #define WCCP_ASSIGN_BUCKET 9
57 struct wccp_here_i_am_t
{
61 char hash
[WCCP_HASH_SIZE
];
66 struct wccp_cache_entry_t
{
67 struct in_addr ip_addr
; // WCCP on-the-wire in 32-bit IPv4-only.
69 char hash
[WCCP_HASH_SIZE
];
73 struct wccp_i_see_you_t
{
80 struct wccp_cache_entry_t wccp_cache_entry
[WCCP_ACTIVE_CACHES
];
83 struct wccp_assign_bucket_t
{
89 static int theWccpConnection
= -1;
91 static struct wccp_here_i_am_t wccp_here_i_am
;
93 static struct wccp_i_see_you_t wccp_i_see_you
;
94 static int last_change
;
96 static int last_assign_buckets_change
;
97 static unsigned int number_caches
;
99 static Ip::Address local_ip
;
101 static PF wccpHandleUdp
;
102 static int wccpLowestIP(void);
103 static EVH wccpHereIam
;
104 static void wccpAssignBuckets(void);
107 * The functions used during startup:
110 * wccpConnectionShutdown
111 * wccpConnectionClose
117 debugs(80, 5, "wccpInit: Called");
118 memset(&wccp_here_i_am
, '\0', sizeof(wccp_here_i_am
));
119 wccp_here_i_am
.type
= htonl(WCCP_HERE_I_AM
);
120 wccp_here_i_am
.version
= htonl(Config
.Wccp
.version
);
121 wccp_here_i_am
.revision
= htonl(WCCP_REVISION
);
124 last_assign_buckets_change
= 0;
127 if (!Config
.Wccp
.router
.IsAnyAddr())
128 if (!eventFind(wccpHereIam
, NULL
))
129 eventAdd("wccpHereIam", wccpHereIam
, NULL
, 5.0, 1);
133 wccpConnectionOpen(void)
135 struct addrinfo
*router
= NULL
, *local
= NULL
;
136 debugs(80, 5, "wccpConnectionOpen: Called");
138 if (Config
.Wccp
.router
.IsAnyAddr()) {
139 debugs(80, 2, "WCCPv1 disabled.");
143 if ( !Config
.Wccp
.router
.SetIPv4() ) {
144 debugs(1, 1, "WCCPv1 Disabled. Router " << Config
.Wccp
.router
<< " is not IPv4.");
148 if ( !Config
.Wccp
.address
.SetIPv4() ) {
149 debugs(1, 1, "WCCPv1 Disabled. Local address " << Config
.Wccp
.address
<< " is not IPv4.");
153 Config
.Wccp
.address
.SetPort(WCCP_PORT
);
154 Config
.Wccp
.router
.SetPort(WCCP_PORT
);
156 theWccpConnection
= comm_open_listener(SOCK_DGRAM
,
162 if (theWccpConnection
< 0)
163 fatal("Cannot open WCCP Port");
165 Comm::SetSelect(theWccpConnection
, COMM_SELECT_READ
, wccpHandleUdp
, NULL
, 0);
167 debugs(80, 1, "Accepting WCCPv1 messages on " << Config
.Wccp
.address
<< ", FD " << theWccpConnection
<< ".");
169 Config
.Wccp
.router
.GetAddrInfo(router
,AF_INET
);
171 if (connect(theWccpConnection
, router
->ai_addr
, router
->ai_addrlen
))
172 fatal("Unable to connect WCCP out socket");
174 Config
.Wccp
.router
.FreeAddrInfo(router
);
176 Config
.Wccp
.address
.InitAddrInfo(local
);
178 if (getsockname(theWccpConnection
, local
->ai_addr
, &local
->ai_addrlen
))
179 fatal("Unable to getsockname on WCCP out socket");
183 Config
.Wccp
.address
.FreeAddrInfo(local
);
188 wccpConnectionClose(void)
190 if (theWccpConnection
> -1) {
191 debugs(80, 1, "FD " << theWccpConnection
<< " Closing WCCPv1 socket");
192 comm_close(theWccpConnection
);
193 theWccpConnection
= -1;
198 * Functions for handling the requests.
202 * Accept the UDP packet
205 wccpHandleUdp(int sock
, void *not_used
)
210 debugs(80, 6, "wccpHandleUdp: Called.");
212 Comm::SetSelect(sock
, COMM_SELECT_READ
, wccpHandleUdp
, NULL
, 0);
214 memset(&wccp_i_see_you
, '\0', sizeof(wccp_i_see_you
));
216 len
= comm_udp_recvfrom(sock
,
217 (void *) &wccp_i_see_you
,
218 sizeof(wccp_i_see_you
),
221 debugs(80, 3, "wccpHandleUdp: " << len
<< " bytes WCCP pkt from " << from
<<
223 (unsigned) ntohl(wccp_i_see_you
.type
) << ", version=" <<
224 (unsigned) ntohl(wccp_i_see_you
.version
) << ", change=" <<
225 (unsigned) ntohl(wccp_i_see_you
.change
) << ", id=" <<
226 (unsigned) ntohl(wccp_i_see_you
.id
) << ", number=" <<
227 (unsigned) ntohl(wccp_i_see_you
.number
));
232 if (from
!= Config
.Wccp
.router
)
235 if ((unsigned) ntohl(wccp_i_see_you
.version
) != (unsigned) Config
.Wccp
.version
)
238 if (ntohl(wccp_i_see_you
.type
) != WCCP_I_SEE_YOU
)
241 if (ntohl(wccp_i_see_you
.number
) > WCCP_ACTIVE_CACHES
) {
242 debugs(80, 1, "Ignoring WCCP_I_SEE_YOU from " <<
243 from
<< " with number of caches set to " <<
244 (int) ntohl(wccp_i_see_you
.number
));
249 last_id
= wccp_i_see_you
.id
;
251 if ((0 == last_change
) && (number_caches
== (unsigned) ntohl(wccp_i_see_you
.number
))) {
252 if (last_assign_buckets_change
== wccp_i_see_you
.change
) {
254 * After a WCCP_ASSIGN_BUCKET message, the router should
255 * update the change value. If not, maybe the route didn't
256 * receive our WCCP_ASSIGN_BUCKET message, so send it again.
258 * Don't update change here. Instead, fall through to
259 * the next block to call wccpAssignBuckets() again.
263 last_change
= wccp_i_see_you
.change
;
268 if (last_change
!= wccp_i_see_you
.change
) {
269 last_change
= wccp_i_see_you
.change
;
271 if (wccpLowestIP() && wccp_i_see_you
.number
) {
272 last_assign_buckets_change
= last_change
;
285 * We sanity checked wccp_i_see_you.number back in wccpHandleUdp()
288 for (loop
= 0; loop
< (unsigned) ntohl(wccp_i_see_you
.number
); loop
++) {
289 assert(loop
< WCCP_ACTIVE_CACHES
);
291 if (local_ip
> wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
)
294 if (local_ip
== wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
)
302 wccpHereIam(void *voidnotused
)
304 debugs(80, 6, "wccpHereIam: Called");
306 wccp_here_i_am
.id
= last_id
;
307 comm_udp_send(theWccpConnection
,
309 sizeof(wccp_here_i_am
),
312 if (!eventFind(wccpHereIam
, NULL
))
313 eventAdd("wccpHereIam", wccpHereIam
, NULL
, 10.0, 1);
317 wccpAssignBuckets(void)
320 struct wccp_assign_bucket_t
*wccp_assign_bucket
;
323 int buckets_per_cache
;
330 debugs(80, 6, "wccpAssignBuckets: Called");
331 number_caches
= ntohl(wccp_i_see_you
.number
);
333 assert(number_caches
> 0);
334 assert(number_caches
<= WCCP_ACTIVE_CACHES
);
336 wab_len
= sizeof(struct wccp_assign_bucket_t
);
338 cache_len
= WCCP_CACHE_LEN
* number_caches
;
340 buf
= (char *)xmalloc(wab_len
+
344 wccp_assign_bucket
= (struct wccp_assign_bucket_t
*) buf
;
346 caches
= (int *) (buf
+ wab_len
);
348 buckets
= buf
+ wab_len
+ cache_len
;
350 memset(wccp_assign_bucket
, '\0', sizeof(wccp_assign_bucket
));
352 memset(buckets
, 0xFF, WCCP_BUCKETS
);
354 buckets_per_cache
= WCCP_BUCKETS
/ number_caches
;
356 for (loop
= 0; loop
< number_caches
; loop
++) {
358 memcpy(&caches
[loop
],
359 &wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
,
362 for (i
= 0; i
< buckets_per_cache
; i
++) {
363 assert(bucket
< WCCP_BUCKETS
);
364 buckets
[bucket
++] = loop
;
368 while (bucket
< WCCP_BUCKETS
) {
369 buckets
[bucket
++] = number_caches
- 1;
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
;
376 comm_udp_send(theWccpConnection
,
378 wab_len
+ WCCP_BUCKETS
+ cache_len
,
384 #endif /* USE_WCCP */