2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 80 WCCP Support */
15 #include "comm/Connection.h"
16 #include "comm/Loops.h"
19 #include "SquidConfig.h"
21 #define WCCP_PORT 2048
22 #define WCCP_REVISION 0
23 #define WCCP_ACTIVE_CACHES 32
24 #define WCCP_HASH_SIZE 32
25 #define WCCP_BUCKETS 256
26 #define WCCP_CACHE_LEN 4
28 #define WCCP_HERE_I_AM 7
29 #define WCCP_I_SEE_YOU 8
30 #define WCCP_ASSIGN_BUCKET 9
32 struct wccp_here_i_am_t
{
36 char hash
[WCCP_HASH_SIZE
];
41 struct wccp_cache_entry_t
{
42 struct in_addr ip_addr
; // WCCP on-the-wire in 32-bit IPv4-only.
44 char hash
[WCCP_HASH_SIZE
];
48 struct wccp_i_see_you_t
{
55 struct wccp_cache_entry_t wccp_cache_entry
[WCCP_ACTIVE_CACHES
];
58 struct wccp_assign_bucket_t
{
64 static int theWccpConnection
= -1;
66 static struct wccp_here_i_am_t wccp_here_i_am
;
68 static struct wccp_i_see_you_t wccp_i_see_you
;
69 static int last_change
;
71 static int last_assign_buckets_change
;
72 static unsigned int number_caches
;
74 static Ip::Address local_ip
;
76 static PF wccpHandleUdp
;
77 static int wccpLowestIP(void);
78 static EVH wccpHereIam
;
79 static void wccpAssignBuckets(void);
82 * The functions used during startup:
85 * wccpConnectionShutdown
92 debugs(80, 5, "wccpInit: Called");
93 memset(&wccp_here_i_am
, '\0', sizeof(wccp_here_i_am
));
94 wccp_here_i_am
.type
= htonl(WCCP_HERE_I_AM
);
95 wccp_here_i_am
.version
= htonl(Config
.Wccp
.version
);
96 wccp_here_i_am
.revision
= htonl(WCCP_REVISION
);
99 last_assign_buckets_change
= 0;
102 if (!Config
.Wccp
.router
.isAnyAddr())
103 if (!eventFind(wccpHereIam
, NULL
))
104 eventAdd("wccpHereIam", wccpHereIam
, NULL
, 5.0, 1);
108 wccpConnectionOpen(void)
110 debugs(80, 5, "wccpConnectionOpen: Called");
112 if (Config
.Wccp
.router
.isAnyAddr()) {
113 debugs(80, 2, "WCCPv1 disabled.");
117 if ( !Config
.Wccp
.router
.setIPv4() ) {
118 debugs(80, DBG_CRITICAL
, "WCCPv1 Disabled. Router " << Config
.Wccp
.router
<< " is not an IPv4 address.");
122 if ( !Config
.Wccp
.address
.setIPv4() ) {
123 debugs(80, DBG_CRITICAL
, "WCCPv1 Disabled. Local address " << Config
.Wccp
.address
<< " is not an IPv4 address.");
127 Config
.Wccp
.address
.port(WCCP_PORT
);
128 Config
.Wccp
.router
.port(WCCP_PORT
);
130 theWccpConnection
= comm_open_listener(SOCK_DGRAM
,
136 if (theWccpConnection
< 0)
137 fatal("Cannot open WCCP Port");
139 Comm::SetSelect(theWccpConnection
, COMM_SELECT_READ
, wccpHandleUdp
, NULL
, 0);
141 debugs(80, DBG_IMPORTANT
, "Accepting WCCPv1 messages on " << Config
.Wccp
.address
<< ", FD " << theWccpConnection
<< ".");
143 // Sadly WCCP only does IPv4
145 struct sockaddr_in router
;
146 Config
.Wccp
.router
.getSockAddr(router
);
147 if (connect(theWccpConnection
, (struct sockaddr
*)&router
, sizeof(router
)))
148 fatal("Unable to connect WCCP out socket");
150 struct sockaddr_in local
;
151 memset(&local
, '\0', sizeof(local
));
152 socklen_t slen
= sizeof(local
);
153 if (getsockname(theWccpConnection
, (struct sockaddr
*)&local
, &slen
))
154 fatal("Unable to getsockname on WCCP out socket");
160 wccpConnectionClose(void)
162 if (theWccpConnection
> -1) {
163 debugs(80, DBG_IMPORTANT
, "FD " << theWccpConnection
<< " Closing WCCPv1 socket");
164 comm_close(theWccpConnection
);
165 theWccpConnection
= -1;
170 * Functions for handling the requests.
174 * Accept the UDP packet
177 wccpHandleUdp(int sock
, void *not_used
)
182 debugs(80, 6, "wccpHandleUdp: Called.");
184 Comm::SetSelect(sock
, COMM_SELECT_READ
, wccpHandleUdp
, NULL
, 0);
186 memset(&wccp_i_see_you
, '\0', sizeof(wccp_i_see_you
));
188 len
= comm_udp_recvfrom(sock
,
189 (void *) &wccp_i_see_you
,
190 sizeof(wccp_i_see_you
),
193 debugs(80, 3, "wccpHandleUdp: " << len
<< " bytes WCCP pkt from " << from
<<
195 (unsigned) ntohl(wccp_i_see_you
.type
) << ", version=" <<
196 (unsigned) ntohl(wccp_i_see_you
.version
) << ", change=" <<
197 (unsigned) ntohl(wccp_i_see_you
.change
) << ", id=" <<
198 (unsigned) ntohl(wccp_i_see_you
.id
) << ", number=" <<
199 (unsigned) ntohl(wccp_i_see_you
.number
));
204 if (from
!= Config
.Wccp
.router
)
207 if ((unsigned) ntohl(wccp_i_see_you
.version
) != (unsigned) Config
.Wccp
.version
)
210 if (ntohl(wccp_i_see_you
.type
) != WCCP_I_SEE_YOU
)
213 if (ntohl(wccp_i_see_you
.number
) > WCCP_ACTIVE_CACHES
) {
214 debugs(80, DBG_IMPORTANT
, "Ignoring WCCP_I_SEE_YOU from " <<
215 from
<< " with number of caches set to " <<
216 (int) ntohl(wccp_i_see_you
.number
));
221 last_id
= wccp_i_see_you
.id
;
223 if ((0 == last_change
) && (number_caches
== (unsigned) ntohl(wccp_i_see_you
.number
))) {
224 if (last_assign_buckets_change
== wccp_i_see_you
.change
) {
226 * After a WCCP_ASSIGN_BUCKET message, the router should
227 * update the change value. If not, maybe the route didn't
228 * receive our WCCP_ASSIGN_BUCKET message, so send it again.
230 * Don't update change here. Instead, fall through to
231 * the next block to call wccpAssignBuckets() again.
235 last_change
= wccp_i_see_you
.change
;
240 if (last_change
!= wccp_i_see_you
.change
) {
241 last_change
= wccp_i_see_you
.change
;
243 if (wccpLowestIP() && wccp_i_see_you
.number
) {
244 last_assign_buckets_change
= last_change
;
257 * We sanity checked wccp_i_see_you.number back in wccpHandleUdp()
260 for (loop
= 0; loop
< (unsigned) ntohl(wccp_i_see_you
.number
); ++loop
) {
261 assert(loop
< WCCP_ACTIVE_CACHES
);
263 if (local_ip
> wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
)
266 if (local_ip
== wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
)
274 wccpHereIam(void *voidnotused
)
276 debugs(80, 6, "wccpHereIam: Called");
278 wccp_here_i_am
.id
= last_id
;
279 double interval
= 10.0; // TODO: make this configurable, possibly negotiate with the router.
281 ssize_t sent
= comm_udp_send(theWccpConnection
, &wccp_here_i_am
, sizeof(wccp_here_i_am
), 0);
283 // if we failed to send the whole lot, try again at a shorter interval (20%)
284 if (sent
!= sizeof(wccp_here_i_am
)) {
285 debugs(80, 2, "ERROR: failed to send WCCP HERE_I_AM packet: " << xstrerror());
289 if (!eventFind(wccpHereIam
, NULL
))
290 eventAdd("wccpHereIam", wccpHereIam
, NULL
, interval
, 1);
294 wccpAssignBuckets(void)
297 struct wccp_assign_bucket_t
*wccp_assign_bucket
;
300 int buckets_per_cache
;
307 debugs(80, 6, "wccpAssignBuckets: Called");
308 number_caches
= ntohl(wccp_i_see_you
.number
);
310 assert(number_caches
> 0);
311 assert(number_caches
<= WCCP_ACTIVE_CACHES
);
313 wab_len
= sizeof(struct wccp_assign_bucket_t
);
315 cache_len
= WCCP_CACHE_LEN
* number_caches
;
317 buf
= (char *)xmalloc(wab_len
+
321 wccp_assign_bucket
= (struct wccp_assign_bucket_t
*) buf
;
323 caches
= (int *) (buf
+ wab_len
);
325 buckets
= buf
+ wab_len
+ cache_len
;
327 memset(wccp_assign_bucket
, '\0', sizeof(*wccp_assign_bucket
));
329 memset(buckets
, 0xFF, WCCP_BUCKETS
);
331 buckets_per_cache
= WCCP_BUCKETS
/ number_caches
;
333 for (loop
= 0; loop
< number_caches
; ++loop
) {
335 memcpy(&caches
[loop
],
336 &wccp_i_see_you
.wccp_cache_entry
[loop
].ip_addr
,
339 for (i
= 0; i
< buckets_per_cache
; ++i
) {
340 assert(bucket
< WCCP_BUCKETS
);
341 buckets
[bucket
] = loop
;
346 while (bucket
< WCCP_BUCKETS
) {
347 buckets
[bucket
] = number_caches
- 1;
351 wccp_assign_bucket
->type
= htonl(WCCP_ASSIGN_BUCKET
);
352 wccp_assign_bucket
->id
= wccp_i_see_you
.id
;
353 wccp_assign_bucket
->number
= wccp_i_see_you
.number
;
355 comm_udp_send(theWccpConnection
,
357 wab_len
+ WCCP_BUCKETS
+ cache_len
,
363 #endif /* USE_WCCP */