2 * Copyright (C) 1996-2020 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 */
17 #include "comm/Connection.h"
18 #include "comm/Loops.h"
19 #include "ConfigParser.h"
21 #include "ip/Address.h"
24 #include "SquidConfig.h"
31 #define WCCP_PORT 2048
32 #define WCCP_RESPONSE_SIZE 12448
33 #define WCCP_BUCKETS 256
35 static int theWccp2Connection
= -1;
36 static int wccp2_connected
= 0;
38 static PF wccp2HandleUdp
;
39 static EVH wccp2HereIam
;
40 static EVH wccp2AssignBuckets
;
44 #define WCCP2_HASH_ASSIGNMENT 0x00
45 #define WCCP2_MASK_ASSIGNMENT 0x01
47 #define WCCP2_NONE_SECURITY_LEN 0
48 #define WCCP2_MD5_SECURITY_LEN SQUID_MD5_DIGEST_LENGTH // 16
51 #define WCCP2_NUMPORTS 8
52 #define WCCP2_PASSWORD_LEN 8
54 /* WCCPv2 Pakcet format structures */
55 /* Defined in draft-wilson-wccp-v2-12-oct-2001.txt */
57 /** \interface WCCPv2_Protocol
58 * Generic header struct
60 struct wccp2_item_header_t
{
65 /* item type values */
66 #define WCCP2_SECURITY_INFO 0
67 #define WCCP2_SERVICE_INFO 1
68 #define WCCP2_ROUTER_ID_INFO 2
69 #define WCCP2_WC_ID_INFO 3
70 #define WCCP2_RTR_VIEW_INFO 4
71 #define WCCP2_WC_VIEW_INFO 5
72 #define WCCP2_REDIRECT_ASSIGNMENT 6
73 #define WCCP2_QUERY_INFO 7
74 #define WCCP2_CAPABILITY_INFO 8
75 #define WCCP2_ALT_ASSIGNMENT 13
76 #define WCCP2_ASSIGN_MAP 14
77 #define WCCP2_COMMAND_EXTENSION 15
79 /** \interface WCCPv2_Protocol
80 * Sect 5.5 WCCP Message Header
82 struct wccp2_message_header_t
{
85 #define WCCP2_VERSION 0x200
89 static struct wccp2_message_header_t wccp2_here_i_am_header
;
92 #define WCCP2_HERE_I_AM 10
93 #define WCCP2_I_SEE_YOU 11
94 #define WCCP2_REDIRECT_ASSIGN 12
95 #define WCCP2_REMOVAL_QUERY 13
97 /** \interface WCCPv2_Protocol
98 * Sect 5.6.1 Security Info Component
100 * Basic security Header. Matches "no security" case exactly.
102 struct wccp2_security_none_t
{
103 uint16_t security_type
;
104 uint16_t security_length
;
105 uint32_t security_option
;
108 /* security options */
109 #define WCCP2_NO_SECURITY 0
110 #define WCCP2_MD5_SECURITY 1
112 /** \interface WCCPv2_Protocol
113 * Sect 5.6.1 Security Info Component
115 * Extended security section. Matches "MD5 security" type exactly.
116 * Including the security header.
118 struct wccp2_security_md5_t
{
119 uint16_t security_type
;
120 uint16_t security_length
;
121 uint32_t security_option
;
122 uint8_t security_implementation
[WCCP2_MD5_SECURITY_LEN
];
125 /* Service info struct */
127 /** \interface WCCPv2_Protocol
128 * Sect 5.6.2 Service Info Component
130 struct wccp2_service_info_t
{
131 uint16_t service_type
;
132 uint16_t service_length
;
135 uint8_t service_priority
;
136 uint8_t service_protocol
;
137 uint32_t service_flags
;
148 #define WCCP2_SERVICE_STANDARD 0
149 #define WCCP2_SERVICE_DYNAMIC 1
152 #define WCCP2_SERVICE_ID_HTTP 0x00
155 #define WCCP2_SERVICE_SRC_IP_HASH 0x1
156 #define WCCP2_SERVICE_DST_IP_HASH 0x2
157 #define WCCP2_SERVICE_SRC_PORT_HASH 0x4
158 #define WCCP2_SERVICE_DST_PORT_HASH 0x8
159 #define WCCP2_SERVICE_PORTS_DEFINED 0x10
160 #define WCCP2_SERVICE_PORTS_SOURCE 0x20
161 #define WCCP2_SERVICE_SRC_IP_ALT_HASH 0x100
162 #define WCCP2_SERVICE_DST_IP_ALT_HASH 0x200
163 #define WCCP2_SERVICE_SRC_PORT_ALT_HASH 0x400
164 #define WCCP2_SERVICE_DST_PORT_ALT_HASH 0x800
166 /* TODO the following structures need to be re-defined for correct full operation.
167 wccp2_cache_identity_element needs to be merged as a sub-struct of
168 wccp2_identity_info_t (identity_type); which frees up the identifty info
169 structures so mask_assigment_data_element can become variable length
170 and cope with multiple fail-over caches hanging off one router.
173 /** \interface WCCPv2_Protocol
174 * Sect 5.7.2 Web-Cache Identity Element
176 struct wccp2_cache_identity_info_t
{
178 uint16_t hash_revision
;
180 //#define WCCP2_HASH_ASSIGNMENT_DATA 0x0
182 /* 5.7.2 Hash Assignment Data Element */
183 char buckets
[32]; /* Draft indicates 8x 32-bit buckets but it's just a mask so doesn't matter how we define. */
188 /** \interface WCCPv2_Protocol
189 * Sect 5.6.4 Web-Cache Identity Info Component
191 struct wccp2_identity_info_t
{
192 uint16_t cache_identity_type
;
193 uint16_t cache_identity_length
;
195 struct wccp2_cache_identity_info_t cache_identity
;
198 static struct wccp2_identity_info_t wccp2_identity_info
;
200 /** \interface WCCPv2_Protocol
201 * Sect 5.7.7 Mask Element
203 struct wccp2_mask_element_t
{
204 uint32_t source_ip_mask
;
205 uint32_t dest_ip_mask
;
206 uint16_t source_port_mask
;
207 uint16_t dest_port_mask
;
208 uint32_t number_values
;
211 /** \interface WCCPv2_Protocol
212 * Sect 5.7.2 Web-Cache Identity Element
214 struct wccp2_cache_mask_identity_info_t
{
216 uint16_t hash_revision
;
218 #define WCCP2_MASK_ASSIGNMENT_DATA (0x2)
220 /* Sect 5.7.2 Mask Assignment Data Element
222 * NP: draft specifies a variable-length set of keys here.
223 * the following fields only matche the special case Squid sends outbound (single-cache).
225 uint32_t mask_element_count
;
227 /* Sect 5.7.6 Mask/Value Set Element */
228 /* special case: single mask element. no values. */
229 struct wccp2_mask_element_t mask
;
231 /* Sect 5.7.2 Mask Assignment Data Element */
236 /** \interface WCCPv2_Protocol
237 * Sect 5.6.4 Web-Cache Identity Info Component
239 struct wccp2_mask_identity_info_t
{
240 uint16_t cache_identity_type
;
241 uint16_t cache_identity_length
;
243 struct wccp2_cache_mask_identity_info_t cache_identity
;
246 static struct wccp2_mask_identity_info_t wccp2_mask_identity_info
;
248 /** \interface WCCPv2_Protocol
249 * Sect 5.6.5 Router View Info Component
250 * Sect 5.6.6 Web Cache View Info Component
252 * first three fields. (shared by both view components)
254 struct wccp2_cache_view_header_t
{
255 uint16_t cache_view_type
;
256 uint16_t cache_view_length
;
257 uint32_t cache_view_version
;
260 static struct wccp2_cache_view_header_t wccp2_cache_view_header
;
262 /// \interface WCCPv2_Protocol
263 /* NP: special-case 5.6.5 or 5.6.6 * View Info when no routers or caches are advertised? */
264 struct wccp2_cache_view_info_t
{
265 uint32_t num_routers
;
269 static struct wccp2_cache_view_info_t wccp2_cache_view_info
;
271 /** \interface WCCPv2_Protocol
272 * Sect 5.7.1 Router ID Element
274 struct wccp2_router_id_element_t
{
275 struct in_addr router_address
;
276 uint32_t received_id
;
280 // static struct wccp2_router_id_element_t wccp2_router_id_element;
282 /** \interface WCCPv2_Protocol
283 * Sect 5.6.9 Capabilities Info Component
285 struct wccp2_capability_info_header_t
{
286 uint16_t capability_info_type
;
287 uint16_t capability_info_length
;
288 /* dynamic length capabilities list */
291 static struct wccp2_capability_info_header_t wccp2_capability_info_header
;
293 /** \interface WCCPv2_Protocol
294 * 5.7.5 Capability Element
296 struct wccp2_capability_element_t
{
297 uint16_t capability_type
;
298 uint16_t capability_length
;
299 uint32_t capability_value
;
301 static struct wccp2_capability_element_t wccp2_capability_element
;
303 /* capability types */
304 #define WCCP2_CAPABILITY_FORWARDING_METHOD 0x01
305 #define WCCP2_CAPABILITY_ASSIGNMENT_METHOD 0x02
306 #define WCCP2_CAPABILITY_RETURN_METHOD 0x03
307 // 0x04 ?? - advertised by a 4507 (ios v15.1) Cisco switch
308 // 0x05 ?? - advertised by a 4507 (ios v15.1) Cisco switch
310 /* capability values */
311 #define WCCP2_METHOD_GRE 0x00000001
312 #define WCCP2_METHOD_L2 0x00000002
313 /* when type=WCCP2_CAPABILITY_FORWARDING_METHOD */
314 #define WCCP2_FORWARDING_METHOD_GRE WCCP2_METHOD_GRE
315 #define WCCP2_FORWARDING_METHOD_L2 WCCP2_METHOD_L2
316 /* when type=WCCP2_CAPABILITY_ASSIGNMENT_METHOD */
317 #define WCCP2_ASSIGNMENT_METHOD_HASH 0x00000001
318 #define WCCP2_ASSIGNMENT_METHOD_MASK 0x00000002
319 /* when type=WCCP2_CAPABILITY_RETURN_METHOD */
320 #define WCCP2_PACKET_RETURN_METHOD_GRE WCCP2_METHOD_GRE
321 #define WCCP2_PACKET_RETURN_METHOD_L2 WCCP2_METHOD_L2
323 /** \interface WCCPv2_Protocol
324 * 5.7.8 Value Element
326 struct wccp2_value_element_t
{
327 uint32_t source_ip_value
;
328 uint32_t dest_ip_value
;
329 uint16_t source_port_value
;
330 uint16_t dest_port_value
;
332 struct in_addr cache_ip
;
335 /* RECEIVED PACKET STRUCTURE */
337 /** \interface WCCPv2_Protocol
338 * 5.2 'I See You' Message
340 struct wccp2_i_see_you_t
{
344 char data
[WCCP_RESPONSE_SIZE
];
347 static struct wccp2_i_see_you_t wccp2_i_see_you
;
349 /** \interface WCCPv2_Protocol
350 * 5.7.4 Router Assignment Element
352 struct wccp2_router_assign_element_t
{
353 struct in_addr router_address
;
354 uint32_t received_id
;
355 uint32_t change_number
;
358 /* Router identity struct */
360 /** \interface WCCPv2_Protocol
361 * 5.6.3 Router Identity Info Component (partial)
363 struct router_identity_info_t
{
365 struct wccp2_item_header_t header
;
367 struct wccp2_router_id_element_t router_id_element
;
369 struct in_addr router_address
;
370 uint32_t number_caches
;
371 /* dynamic list of cache IP addresses */
374 /* The received packet for a mask assignment is unusual */
376 /** \interface WCCPv2_Protocol
377 * Sect 5.7.7 Mask Element ???
378 * see code below. apparently the supposed IP address at position num1 can be equal to 3.
380 struct cache_mask_info_t
{
387 /** \interface WCCPv2_Protocol
388 * 5.7.3 Assignment Key Element
390 struct assignment_key_t
{
391 struct in_addr master_ip
;
392 uint32_t master_number
;
395 /** \interface WCCPv2_Protocol
396 * 5.6.5 Router View Info Component (first three fields)
398 struct router_view_t
{
399 struct wccp2_item_header_t header
;
400 uint32_t change_number
;
401 struct assignment_key_t assignment_key
;
402 /* dynamic lists of routers and caches elided */
405 /* Lists used to keep track of caches, routers and services */
407 /// \interface WCCPv2_Protocol
408 struct wccp2_cache_list_t
{
410 struct in_addr cache_ip
;
414 struct wccp2_cache_list_t
*next
;
417 /// \interface WCCPv2_Protocol
418 struct wccp2_router_list_t
{
420 struct wccp2_router_id_element_t
*info
;
422 struct in_addr local_ip
;
424 struct in_addr router_sendto_address
;
425 uint32_t member_change
;
428 struct wccp2_cache_list_t cache_list_head
;
430 struct wccp2_router_list_t
*next
;
433 static int wccp2_numrouters
;
435 /// \interface WCCPv2_Protocol
436 struct wccp2_service_list_t
{
438 struct wccp2_service_info_t info
;
439 uint32_t num_routers
;
441 struct wccp2_router_list_t router_list_head
;
445 char *wccp2_identity_info_ptr
;
447 struct wccp2_security_md5_t
*security_info
;
449 struct wccp2_service_info_t
*service_info
;
450 char wccp_packet
[WCCP_RESPONSE_SIZE
];
451 size_t wccp_packet_size
;
453 struct wccp2_service_list_t
*next
;
454 char wccp_password
[WCCP2_PASSWORD_LEN
+ 1]; /* hold the trailing C-string NUL */
455 uint32_t wccp2_security_type
;
458 static struct wccp2_service_list_t
*wccp2_service_list_head
= NULL
;
460 int empty_portlist
[WCCP2_NUMPORTS
] = {0, 0, 0, 0, 0, 0, 0, 0};
462 /* END WCCP V2 PROTOCOL TYPES DEFINITION */
464 void wccp2_add_service_list(int service
, int service_id
, int service_priority
,
465 int service_proto
, int service_flags
, int ports
[], int security_type
, char *password
);
466 static void wccp2SortCacheList(struct wccp2_cache_list_t
*head
);
469 * The functions used during startup:
471 * wccp2ConnectionOpen
472 * wccp2ConnectionClose
476 wccp2InitServices(void)
478 debugs(80, 5, "wccp2InitServices: called");
482 wccp2_update_service(struct wccp2_service_list_t
*srv
, int service
,
483 int service_id
, int service_priority
, int service_proto
, int service_flags
,
486 /* XXX check what needs to be wrapped in htons()! */
487 srv
->info
.service
= service
;
488 srv
->info
.service_id
= service_id
;
489 srv
->info
.service_priority
= service_priority
;
490 srv
->info
.service_protocol
= service_proto
;
491 srv
->info
.service_flags
= htonl(service_flags
);
492 srv
->info
.port0
= htons(ports
[0]);
493 srv
->info
.port1
= htons(ports
[1]);
494 srv
->info
.port2
= htons(ports
[2]);
495 srv
->info
.port3
= htons(ports
[3]);
496 srv
->info
.port4
= htons(ports
[4]);
497 srv
->info
.port5
= htons(ports
[5]);
498 srv
->info
.port6
= htons(ports
[6]);
499 srv
->info
.port7
= htons(ports
[7]);
503 wccp2_add_service_list(int service
, int service_id
, int service_priority
,
504 int service_proto
, int service_flags
, int ports
[], int security_type
,
508 struct wccp2_service_list_t
*wccp2_service_list_ptr
;
510 wccp2_service_list_ptr
= (wccp2_service_list_t
*) xcalloc(1, sizeof(struct wccp2_service_list_t
));
512 debugs(80, 5, "wccp2_add_service_list: added service id " << service_id
);
514 /* XXX check what needs to be wrapped in htons()! */
515 wccp2_service_list_ptr
->info
.service_type
= htons(WCCP2_SERVICE_INFO
);
517 wccp2_service_list_ptr
->info
.service_length
= htons(sizeof(struct wccp2_service_info_t
) - 4);
518 wccp2_service_list_ptr
->change_num
= 0;
519 wccp2_update_service(wccp2_service_list_ptr
, service
, service_id
,
520 service_priority
, service_proto
, service_flags
, ports
);
521 wccp2_service_list_ptr
->wccp2_security_type
= security_type
;
522 memset(wccp2_service_list_ptr
->wccp_password
, 0, WCCP2_PASSWORD_LEN
+ 1);
523 strncpy(wccp2_service_list_ptr
->wccp_password
, password
, WCCP2_PASSWORD_LEN
);
524 /* add to linked list - XXX this should use the Squid dlink* routines! */
525 wccp2_service_list_ptr
->next
= wccp2_service_list_head
;
526 wccp2_service_list_head
= wccp2_service_list_ptr
;
529 static struct wccp2_service_list_t
*
530 wccp2_get_service_by_id(int service
, int service_id
) {
532 struct wccp2_service_list_t
*p
;
534 p
= wccp2_service_list_head
;
537 if (p
->info
.service
== service
&& p
->info
.service_id
== service_id
) {
548 * Update the md5 security header, if possible
550 * Returns: 1 if we set it, 0 if not (eg, no security section, or non-md5)
553 wccp2_update_md5_security(char *password
, char *ptr
, char *packet
, int len
)
555 uint8_t md5Digest
[SQUID_MD5_DIGEST_LENGTH
];
556 char pwd
[WCCP2_PASSWORD_LEN
];
559 struct wccp2_security_md5_t
*ws
;
561 debugs(80, 5, "wccp2_update_md5_security: called");
563 /* The password field, for the MD5 hash, needs to be 8 bytes and NUL padded. */
564 memset(pwd
, 0, sizeof(pwd
));
565 strncpy(pwd
, password
, sizeof(pwd
));
566 pwd
[sizeof(pwd
) - 1] = '\0';
568 ws
= (struct wccp2_security_md5_t
*) ptr
;
569 assert(ntohs(ws
->security_type
) == WCCP2_SECURITY_INFO
);
570 /* Its the security part */
572 if (ntohl(ws
->security_option
) != WCCP2_MD5_SECURITY
) {
573 debugs(80, 5, "wccp2_update_md5_security: this service ain't md5'ing, abort");
577 /* And now its the MD5 section! */
578 /* According to the draft, the MD5 security hash is the combination of
579 * the 8-octet password (padded w/ NUL bytes) and the entire WCCP packet,
580 * including the WCCP message header. The WCCP security implementation
581 * area should be zero'ed before calculating the MD5 hash.
583 /* XXX eventually we should be able to kill md5Digest and blit it directly in */
584 memset(ws
->security_implementation
, 0, sizeof(ws
->security_implementation
));
588 SquidMD5Update(&M
, pwd
, sizeof(pwd
));
590 SquidMD5Update(&M
, packet
, len
);
592 SquidMD5Final(md5Digest
, &M
);
594 memcpy(ws
->security_implementation
, md5Digest
, sizeof(md5Digest
));
601 * Check the given WCCP2 packet against the given password.
605 wccp2_check_security(struct wccp2_service_list_t
*srv
, char *security
, char *packet
, int len
)
608 struct wccp2_security_md5_t
*ws
= (struct wccp2_security_md5_t
*) security
;
609 uint8_t md5Digest
[SQUID_MD5_DIGEST_LENGTH
], md5_challenge
[SQUID_MD5_DIGEST_LENGTH
];
610 char pwd
[WCCP2_PASSWORD_LEN
];
613 /* Make sure the security type matches what we expect */
615 if (ntohl(ws
->security_option
) != srv
->wccp2_security_type
) {
616 debugs(80, DBG_IMPORTANT
, "wccp2_check_security: received packet has the wrong security option");
620 if (srv
->wccp2_security_type
== WCCP2_NO_SECURITY
) {
624 if (srv
->wccp2_security_type
!= WCCP2_MD5_SECURITY
) {
625 debugs(80, DBG_IMPORTANT
, "wccp2_check_security: invalid security option");
629 /* If execution makes it here then we have an MD5 security */
631 /* The password field, for the MD5 hash, needs to be 8 bytes and NUL padded. */
632 memset(pwd
, 0, sizeof(pwd
));
633 strncpy(pwd
, srv
->wccp_password
, sizeof(pwd
));
634 pwd
[sizeof(pwd
) - 1] = '\0';
636 /* Take a copy of the challenge: we need to NUL it before comparing */
637 memcpy(md5_challenge
, ws
->security_implementation
, sizeof(md5_challenge
));
639 memset(ws
->security_implementation
, 0, sizeof(ws
->security_implementation
));
643 SquidMD5Update(&M
, pwd
, sizeof(pwd
));
645 SquidMD5Update(&M
, packet
, len
);
647 SquidMD5Final(md5Digest
, &M
);
649 return (memcmp(md5Digest
, md5_challenge
, SQUID_MD5_DIGEST_LENGTH
) == 0);
657 uint32_t service_flags
;
659 struct wccp2_service_list_t
*service_list_ptr
;
661 struct wccp2_router_list_t
*router_list_ptr
;
663 debugs(80, 5, "wccp2Init: Called");
665 if (wccp2_connected
== 1)
668 wccp2_numrouters
= 0;
670 /* Calculate the number of routers configured in the config file */
671 for (s
= Config
.Wccp2
.router
; s
; s
= s
->next
) {
672 if (!s
->s
.isAnyAddr()) {
673 /* Increment the counter */
678 if (wccp2_numrouters
== 0) {
682 struct wccp2_security_md5_t wccp2_security_md5
;
683 memset(&wccp2_security_md5
, 0, sizeof(wccp2_security_md5
));
685 /* Initialise the list of services */
688 service_list_ptr
= wccp2_service_list_head
;
690 while (service_list_ptr
!= NULL
) {
691 /* Set up our list pointers */
692 router_list_ptr
= &service_list_ptr
->router_list_head
;
694 /* start the wccp header */
695 wccp2_here_i_am_header
.type
= htonl(WCCP2_HERE_I_AM
);
696 wccp2_here_i_am_header
.version
= htons(WCCP2_VERSION
);
697 wccp2_here_i_am_header
.length
= 0;
698 ptr
= service_list_ptr
->wccp_packet
+ sizeof(wccp2_here_i_am_header
);
700 /* add the security section */
701 /* XXX this is ugly */
703 if (service_list_ptr
->wccp2_security_type
== WCCP2_MD5_SECURITY
) {
704 wccp2_security_md5
.security_option
= htonl(WCCP2_MD5_SECURITY
);
706 wccp2_security_md5
.security_length
= htons(sizeof(struct wccp2_security_md5_t
) - 4);
707 } else if (service_list_ptr
->wccp2_security_type
== WCCP2_NO_SECURITY
) {
708 wccp2_security_md5
.security_option
= htonl(WCCP2_NO_SECURITY
);
709 /* XXX I hate magic length numbers! */
710 wccp2_security_md5
.security_length
= htons(4);
712 fatalf("Bad WCCP2 security type\n");
715 wccp2_here_i_am_header
.length
+= ntohs(wccp2_security_md5
.security_length
) + 4;
716 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
717 wccp2_security_md5
.security_type
= htons(WCCP2_SECURITY_INFO
);
719 service_list_ptr
->security_info
= (struct wccp2_security_md5_t
*) ptr
;
721 if (service_list_ptr
->wccp2_security_type
== WCCP2_MD5_SECURITY
) {
722 memcpy(ptr
, &wccp2_security_md5
, sizeof(struct wccp2_security_md5_t
));
723 ptr
+= sizeof(struct wccp2_security_md5_t
);
725 /* assume NONE, and XXX I hate magic length numbers */
726 memcpy(ptr
, &wccp2_security_md5
, 8);
730 /* Add the service info section */
732 wccp2_here_i_am_header
.length
+= sizeof(struct wccp2_service_info_t
);
734 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
736 memcpy(ptr
, &service_list_ptr
->info
, sizeof(struct wccp2_service_info_t
));
738 service_list_ptr
->service_info
= (struct wccp2_service_info_t
*) ptr
;
740 ptr
+= sizeof(struct wccp2_service_info_t
);
742 /* Add the cache identity section */
744 switch (Config
.Wccp2
.assignment_method
) {
746 case WCCP2_ASSIGNMENT_METHOD_HASH
:
748 wccp2_here_i_am_header
.length
+= sizeof(struct wccp2_identity_info_t
);
749 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
750 wccp2_identity_info
.cache_identity_type
= htons(WCCP2_WC_ID_INFO
);
751 wccp2_identity_info
.cache_identity_length
= htons(sizeof(wccp2_identity_info
.cache_identity
));
752 memset(&wccp2_identity_info
.cache_identity
.addr
, '\0', sizeof(struct in_addr
));
753 memset(&wccp2_identity_info
.cache_identity
.hash_revision
, '\0', sizeof(wccp2_identity_info
.cache_identity
.hash_revision
));
754 memset(&wccp2_identity_info
.cache_identity
.bits
, '\0', sizeof(wccp2_identity_info
.cache_identity
.bits
));
755 memset(&wccp2_identity_info
.cache_identity
.buckets
, '\0', sizeof(wccp2_identity_info
.cache_identity
.buckets
));
756 wccp2_identity_info
.cache_identity
.weight
= htons(Config
.Wccp2
.weight
);
757 memset(&wccp2_identity_info
.cache_identity
.status
, '\0', sizeof(wccp2_identity_info
.cache_identity
.status
));
759 memcpy(ptr
, &wccp2_identity_info
, sizeof(struct wccp2_identity_info_t
));
760 service_list_ptr
->wccp2_identity_info_ptr
= ptr
;
762 ptr
+= sizeof(struct wccp2_identity_info_t
);
765 case WCCP2_ASSIGNMENT_METHOD_MASK
:
767 wccp2_here_i_am_header
.length
+= sizeof(struct wccp2_mask_identity_info_t
);
768 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
769 wccp2_mask_identity_info
.cache_identity_type
= htons(WCCP2_WC_ID_INFO
);
770 wccp2_mask_identity_info
.cache_identity_length
= htons(sizeof(wccp2_mask_identity_info
.cache_identity
));
771 memset(&wccp2_mask_identity_info
.cache_identity
.addr
, '\0', sizeof(struct in_addr
));
772 wccp2_mask_identity_info
.cache_identity
.bits
= htons(WCCP2_MASK_ASSIGNMENT_DATA
);
773 wccp2_mask_identity_info
.cache_identity
.mask_element_count
= htonl(1);
774 service_flags
= ntohl(service_list_ptr
->service_info
->service_flags
);
776 memset(&wccp2_mask_identity_info
.cache_identity
.mask
, 0, sizeof(struct wccp2_mask_element_t
));
778 if ((service_flags
& WCCP2_SERVICE_SRC_IP_HASH
) || (service_flags
& WCCP2_SERVICE_SRC_IP_ALT_HASH
)) {
779 wccp2_mask_identity_info
.cache_identity
.mask
.source_ip_mask
= htonl(0x00001741);
780 } else if ((service_list_ptr
->info
.service
== WCCP2_SERVICE_STANDARD
) || (service_flags
& WCCP2_SERVICE_DST_IP_HASH
) || (service_flags
& WCCP2_SERVICE_DST_IP_ALT_HASH
)) {
781 wccp2_mask_identity_info
.cache_identity
.mask
.dest_ip_mask
= htonl(0x00001741);
782 } else if ((service_flags
& WCCP2_SERVICE_SRC_PORT_HASH
) || (service_flags
& WCCP2_SERVICE_SRC_PORT_ALT_HASH
)) {
783 wccp2_mask_identity_info
.cache_identity
.mask
.source_port_mask
= htons(0x1741);
784 } else if ((service_flags
& WCCP2_SERVICE_DST_PORT_HASH
) || (service_flags
& WCCP2_SERVICE_DST_PORT_ALT_HASH
)) {
785 wccp2_mask_identity_info
.cache_identity
.mask
.dest_port_mask
= htons(0x1741);
787 fatalf("Unknown service hash method\n");
790 wccp2_mask_identity_info
.cache_identity
.weight
= 0;
791 wccp2_mask_identity_info
.cache_identity
.status
= 0;
793 memcpy(ptr
, &wccp2_mask_identity_info
, sizeof(struct wccp2_mask_identity_info_t
));
794 service_list_ptr
->wccp2_identity_info_ptr
= ptr
;
796 ptr
+= sizeof(struct wccp2_mask_identity_info_t
);
800 fatalf("Unknown Wccp2 assignment method\n");
803 /* Add the cache view section */
804 wccp2_here_i_am_header
.length
+= sizeof(wccp2_cache_view_header
);
806 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
808 wccp2_cache_view_header
.cache_view_type
= htons(WCCP2_WC_VIEW_INFO
);
810 wccp2_cache_view_header
.cache_view_length
= htons(sizeof(wccp2_cache_view_header
) - 4 +
811 sizeof(wccp2_cache_view_info
) + (wccp2_numrouters
* sizeof(wccp2_router_id_element_t
)));
813 wccp2_cache_view_header
.cache_view_version
= htonl(1);
815 memcpy(ptr
, &wccp2_cache_view_header
, sizeof(wccp2_cache_view_header
));
817 ptr
+= sizeof(wccp2_cache_view_header
);
819 /* Add the number of routers to the packet */
820 wccp2_here_i_am_header
.length
+= sizeof(service_list_ptr
->num_routers
);
822 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
824 service_list_ptr
->num_routers
= htonl(wccp2_numrouters
);
826 memcpy(ptr
, &service_list_ptr
->num_routers
, sizeof(service_list_ptr
->num_routers
));
828 ptr
+= sizeof(service_list_ptr
->num_routers
);
830 /* Add each router. Keep this functionality here to make sure the received_id can be updated in the packet */
831 for (s
= Config
.Wccp2
.router
; s
; s
= s
->next
) {
832 if (!s
->s
.isAnyAddr()) {
834 wccp2_here_i_am_header
.length
+= sizeof(struct wccp2_router_id_element_t
);
835 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
837 /* Add a pointer to the router list for this router */
839 router_list_ptr
->info
= (struct wccp2_router_id_element_t
*) ptr
;
840 s
->s
.getInAddr(router_list_ptr
->info
->router_address
);
841 router_list_ptr
->info
->received_id
= htonl(0);
842 s
->s
.getInAddr(router_list_ptr
->router_sendto_address
);
843 router_list_ptr
->member_change
= htonl(0);
845 /* Build the next struct */
847 router_list_ptr
->next
= (wccp2_router_list_t
*) xcalloc(1, sizeof(struct wccp2_router_list_t
));
849 /* update the pointer */
850 router_list_ptr
= router_list_ptr
->next
;
851 router_list_ptr
->next
= NULL
;
853 /* no need to copy memory - we've just set the values directly in the packet above */
855 ptr
+= sizeof(struct wccp2_router_id_element_t
);
859 /* Add the number of caches (0) */
860 wccp2_here_i_am_header
.length
+= sizeof(wccp2_cache_view_info
.num_caches
);
862 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
864 wccp2_cache_view_info
.num_caches
= htonl(0);
866 memcpy(ptr
, &wccp2_cache_view_info
.num_caches
, sizeof(wccp2_cache_view_info
.num_caches
));
868 ptr
+= sizeof(wccp2_cache_view_info
.num_caches
);
870 /* Add the extra capability header */
871 wccp2_here_i_am_header
.length
+= sizeof(wccp2_capability_info_header
);
873 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
875 wccp2_capability_info_header
.capability_info_type
= htons(WCCP2_CAPABILITY_INFO
);
877 wccp2_capability_info_header
.capability_info_length
= htons(3 * sizeof(wccp2_capability_element
));
879 memcpy(ptr
, &wccp2_capability_info_header
, sizeof(wccp2_capability_info_header
));
881 ptr
+= sizeof(wccp2_capability_info_header
);
883 /* Add the forwarding method */
884 wccp2_here_i_am_header
.length
+= sizeof(wccp2_capability_element
);
886 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
888 wccp2_capability_element
.capability_type
= htons(WCCP2_CAPABILITY_FORWARDING_METHOD
);
890 wccp2_capability_element
.capability_length
= htons(sizeof(wccp2_capability_element
.capability_value
));
892 wccp2_capability_element
.capability_value
= htonl(Config
.Wccp2
.forwarding_method
);
894 memcpy(ptr
, &wccp2_capability_element
, sizeof(wccp2_capability_element
));
896 ptr
+= sizeof(wccp2_capability_element
);
898 /* Add the assignment method */
899 wccp2_here_i_am_header
.length
+= sizeof(wccp2_capability_element
);
901 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
903 wccp2_capability_element
.capability_type
= htons(WCCP2_CAPABILITY_ASSIGNMENT_METHOD
);
905 wccp2_capability_element
.capability_length
= htons(sizeof(wccp2_capability_element
.capability_value
));
907 wccp2_capability_element
.capability_value
= htonl(Config
.Wccp2
.assignment_method
);
909 memcpy(ptr
, &wccp2_capability_element
, sizeof(wccp2_capability_element
));
911 ptr
+= sizeof(wccp2_capability_element
);
913 /* Add the return method */
914 wccp2_here_i_am_header
.length
+= sizeof(wccp2_capability_element
);
916 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
918 wccp2_capability_element
.capability_type
= htons(WCCP2_CAPABILITY_RETURN_METHOD
);
920 wccp2_capability_element
.capability_length
= htons(sizeof(wccp2_capability_element
.capability_value
));
922 wccp2_capability_element
.capability_value
= htonl(Config
.Wccp2
.return_method
);
924 memcpy(ptr
, &wccp2_capability_element
, sizeof(wccp2_capability_element
));
926 ptr
+= sizeof(wccp2_capability_element
);
928 /* Finally, fix the total length to network order, and copy to the appropriate memory blob */
929 wccp2_here_i_am_header
.length
= htons(wccp2_here_i_am_header
.length
);
931 memcpy(&service_list_ptr
->wccp_packet
, &wccp2_here_i_am_header
, sizeof(wccp2_here_i_am_header
));
933 service_list_ptr
->wccp_packet_size
= ntohs(wccp2_here_i_am_header
.length
) + sizeof(wccp2_here_i_am_header
);
935 /* Add the event if everything initialised correctly */
936 debugs(80,3,"wccp2Init: scheduled 'HERE_I_AM' message to " << wccp2_numrouters
<< "routers.");
937 if (wccp2_numrouters
) {
938 if (!eventFind(wccp2HereIam
, NULL
)) {
939 eventAdd("wccp2HereIam", wccp2HereIam
, NULL
, 1, 1);
941 debugs(80,3,"wccp2Init: skip duplicate 'HERE_I_AM'.");
944 service_list_ptr
= service_list_ptr
->next
;
949 wccp2ConnectionOpen(void)
951 struct sockaddr_in router
, local
, null
;
952 socklen_t local_len
, router_len
;
954 struct wccp2_service_list_t
*service_list_ptr
;
956 struct wccp2_router_list_t
*router_list_ptr
;
958 debugs(80, 5, "wccp2ConnectionOpen: Called");
960 if (wccp2_numrouters
== 0 || !wccp2_service_list_head
) {
961 debugs(80, 2, "WCCPv2 Disabled. No IPv4 Router(s) configured.");
965 if ( !Config
.Wccp2
.address
.setIPv4() ) {
966 debugs(80, DBG_CRITICAL
, "WCCPv2 Disabled. Local address " << Config
.Wccp2
.address
<< " is not an IPv4 address.");
970 Config
.Wccp2
.address
.port(WCCP_PORT
);
971 theWccp2Connection
= comm_open_listener(SOCK_DGRAM
,
973 Config
.Wccp2
.address
,
977 if (theWccp2Connection
< 0)
978 fatal("Cannot open WCCP Port");
980 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
982 int i
= IP_PMTUDISC_DONT
;
983 if (setsockopt(theWccp2Connection
, SOL_IP
, IP_MTU_DISCOVER
, &i
, sizeof i
) < 0) {
985 debugs(80, 2, "WARNING: Path MTU discovery could not be disabled on FD " << theWccp2Connection
<< ": " << xstrerr(xerrno
));
990 Comm::SetSelect(theWccp2Connection
, COMM_SELECT_READ
, wccp2HandleUdp
, NULL
, 0);
992 debugs(80, DBG_IMPORTANT
, "Accepting WCCPv2 messages on port " << WCCP_PORT
<< ", FD " << theWccp2Connection
<< ".");
993 debugs(80, DBG_IMPORTANT
, "Initialising all WCCPv2 lists");
995 /* Initialise all routers on all services */
996 memset(&null
, 0, sizeof(null
));
998 null
.sin_family
= AF_UNSPEC
;
1000 service_list_ptr
= wccp2_service_list_head
;
1002 while (service_list_ptr
!= NULL
) {
1003 for (router_list_ptr
= &service_list_ptr
->router_list_head
; router_list_ptr
->next
!= NULL
; router_list_ptr
= router_list_ptr
->next
) {
1004 router_len
= sizeof(router
);
1005 memset(&router
, '\0', router_len
);
1006 router
.sin_family
= AF_INET
;
1007 router
.sin_port
= htons(WCCP_PORT
);
1008 router
.sin_addr
= router_list_ptr
->router_sendto_address
;
1010 if (connect(theWccp2Connection
, (struct sockaddr
*) &router
, router_len
))
1011 fatal("Unable to connect WCCP out socket");
1013 local_len
= sizeof(local
);
1015 memset(&local
, '\0', local_len
);
1017 if (getsockname(theWccp2Connection
, (struct sockaddr
*) &local
, &local_len
))
1018 fatal("Unable to getsockname on WCCP out socket");
1020 router_list_ptr
->local_ip
= local
.sin_addr
;
1022 /* Disconnect the sending socket. Note: FreeBSD returns error
1023 * but disconnects anyway so we have to just assume it worked
1025 if (wccp2_numrouters
> 1) {
1026 (void)connect(theWccp2Connection
, (struct sockaddr
*) &null
, router_len
);
1030 service_list_ptr
= service_list_ptr
->next
;
1033 wccp2_connected
= 1;
1037 wccp2ConnectionClose(void)
1040 struct wccp2_service_list_t
*service_list_ptr
;
1042 struct wccp2_service_list_t
*service_list_ptr_next
;
1044 struct wccp2_router_list_t
*router_list_ptr
;
1046 struct wccp2_router_list_t
*router_list_next
;
1048 struct wccp2_cache_list_t
*cache_list_ptr
;
1050 struct wccp2_cache_list_t
*cache_list_ptr_next
;
1052 if (wccp2_connected
== 0) {
1056 /* TODO A shutting-down cache should generate a removal query, informing the router
1057 * (and therefore the caches in the group) that this cache is going
1058 * away and no new traffic should be forwarded to it.
1061 if (theWccp2Connection
> -1) {
1062 debugs(80, DBG_IMPORTANT
, "FD " << theWccp2Connection
<< " Closing WCCPv2 socket");
1063 comm_close(theWccp2Connection
);
1064 theWccp2Connection
= -1;
1067 /* free all stored router state */
1068 service_list_ptr
= wccp2_service_list_head
;
1070 while (service_list_ptr
!= NULL
) {
1071 for (router_list_ptr
= &service_list_ptr
->router_list_head
; router_list_ptr
!= NULL
; router_list_ptr
= router_list_next
) {
1072 for (cache_list_ptr
= &router_list_ptr
->cache_list_head
; cache_list_ptr
; cache_list_ptr
= cache_list_ptr_next
) {
1073 cache_list_ptr_next
= cache_list_ptr
->next
;
1075 if (cache_list_ptr
!= &router_list_ptr
->cache_list_head
) {
1076 xfree(cache_list_ptr
);
1079 memset(cache_list_ptr
, '\0', sizeof(struct wccp2_cache_list_t
));
1083 router_list_next
= router_list_ptr
->next
;
1085 if (router_list_ptr
!= &service_list_ptr
->router_list_head
) {
1086 xfree(router_list_ptr
);
1089 memset(router_list_ptr
, '\0', sizeof(struct wccp2_router_list_t
));
1093 service_list_ptr_next
= service_list_ptr
->next
;
1094 xfree(service_list_ptr
);
1095 service_list_ptr
= service_list_ptr_next
;
1098 wccp2_service_list_head
= NULL
;
1099 eventDelete(wccp2HereIam
, NULL
);
1100 eventDelete(wccp2AssignBuckets
, NULL
);
1101 eventDelete(wccp2HereIam
, NULL
);
1102 wccp2_connected
= 0;
1106 * Functions for handling the requests.
1110 * Accept the UDP packet
1113 wccp2HandleUdp(int sock
, void *)
1115 struct wccp2_service_list_t
*service_list_ptr
;
1117 struct wccp2_router_list_t
*router_list_ptr
;
1119 struct wccp2_cache_list_t
*cache_list_ptr
;
1121 struct wccp2_cache_list_t
*cache_list_ptr_next
;
1123 /* These structs form the parts of the packet */
1125 struct wccp2_item_header_t
*header
= NULL
;
1127 struct wccp2_security_none_t
*security_info
= NULL
;
1129 struct wccp2_service_info_t
*service_info
= NULL
;
1131 struct router_identity_info_t
*router_identity_info
= NULL
;
1133 struct router_view_t
*router_view_header
= NULL
;
1135 struct wccp2_cache_mask_identity_info_t
*cache_mask_identity
= NULL
;
1137 struct cache_mask_info_t
*cache_mask_info
= NULL
;
1139 struct wccp2_cache_identity_info_t
*cache_identity
= NULL
;
1141 struct wccp2_capability_info_header_t
*router_capability_header
= NULL
;
1143 struct wccp2_capability_element_t
*router_capability_element
;
1145 struct sockaddr_in from
;
1147 struct in_addr cache_address
;
1149 short int data_length
, offset
;
1154 debugs(80, 6, "wccp2HandleUdp: Called.");
1156 Comm::SetSelect(sock
, COMM_SELECT_READ
, wccp2HandleUdp
, NULL
, 0);
1158 /* FIXME INET6 : drop conversion boundary */
1159 Ip::Address from_tmp
;
1162 len
= comm_udp_recvfrom(sock
,
1171 if (ntohs(wccp2_i_see_you
.version
) != WCCP2_VERSION
)
1174 if (ntohl(wccp2_i_see_you
.type
) != WCCP2_I_SEE_YOU
)
1177 /* FIXME INET6 : drop conversion boundary */
1178 from_tmp
.getSockAddr(from
);
1180 debugs(80, 3, "Incoming WCCPv2 I_SEE_YOU length " << ntohs(wccp2_i_see_you
.length
) << ".");
1182 /* Record the total data length */
1183 data_length
= ntohs(wccp2_i_see_you
.length
);
1187 if (data_length
> len
) {
1188 debugs(80, DBG_IMPORTANT
, "ERROR: Malformed WCCPv2 packet claiming it's bigger than received data");
1192 /* Go through the data structure */
1193 while (data_length
> offset
) {
1195 char *data
= wccp2_i_see_you
.data
;
1197 header
= (struct wccp2_item_header_t
*) &data
[offset
];
1199 switch (ntohs(header
->type
)) {
1201 case WCCP2_SECURITY_INFO
:
1203 if (security_info
!= NULL
) {
1204 debugs(80, DBG_IMPORTANT
, "Duplicate security definition");
1208 security_info
= (struct wccp2_security_none_t
*) &wccp2_i_see_you
.data
[offset
];
1211 case WCCP2_SERVICE_INFO
:
1213 if (service_info
!= NULL
) {
1214 debugs(80, DBG_IMPORTANT
, "Duplicate service_info definition");
1218 service_info
= (struct wccp2_service_info_t
*) &wccp2_i_see_you
.data
[offset
];
1221 case WCCP2_ROUTER_ID_INFO
:
1223 if (router_identity_info
!= NULL
) {
1224 debugs(80, DBG_IMPORTANT
, "Duplicate router_identity_info definition");
1228 router_identity_info
= (struct router_identity_info_t
*) &wccp2_i_see_you
.data
[offset
];
1231 case WCCP2_RTR_VIEW_INFO
:
1233 if (router_view_header
!= NULL
) {
1234 debugs(80, DBG_IMPORTANT
, "Duplicate router_view definition");
1238 router_view_header
= (struct router_view_t
*) &wccp2_i_see_you
.data
[offset
];
1241 case WCCP2_CAPABILITY_INFO
:
1243 if (router_capability_header
!= NULL
) {
1244 debugs(80, DBG_IMPORTANT
, "Duplicate router_capability definition");
1248 router_capability_header
= (struct wccp2_capability_info_header_t
*) &wccp2_i_see_you
.data
[offset
];
1251 /* Nothing to do for the types below */
1253 case WCCP2_ASSIGN_MAP
:
1254 case WCCP2_REDIRECT_ASSIGNMENT
:
1258 debugs(80, DBG_IMPORTANT
, "Unknown record type in WCCPv2 Packet (" << ntohs(header
->type
) << ").");
1261 offset
+= sizeof(struct wccp2_item_header_t
);
1262 offset
+= ntohs(header
->length
);
1264 if (offset
> data_length
) {
1265 debugs(80, DBG_IMPORTANT
, "Error: WCCPv2 packet tried to tell us there is data beyond the end of the packet");
1270 if ((security_info
== NULL
) || (service_info
== NULL
) || (router_identity_info
== NULL
) || (router_view_header
== NULL
)) {
1271 debugs(80, DBG_IMPORTANT
, "Incomplete WCCPv2 Packet");
1275 debugs(80, 5, "Complete packet received");
1277 /* Check that the service in the packet is configured on this router */
1278 service_list_ptr
= wccp2_service_list_head
;
1280 while (service_list_ptr
!= NULL
) {
1281 if (service_info
->service_id
== service_list_ptr
->service_info
->service_id
) {
1285 service_list_ptr
= service_list_ptr
->next
;
1288 if (service_list_ptr
== NULL
) {
1289 debugs(80, DBG_IMPORTANT
, "WCCPv2 Unknown service received from router (" << service_info
->service_id
<< ")");
1293 if (ntohl(security_info
->security_option
) != ntohl(service_list_ptr
->security_info
->security_option
)) {
1294 debugs(80, DBG_IMPORTANT
, "Invalid security option in WCCPv2 Packet (" << ntohl(security_info
->security_option
) << " vs " << ntohl(service_list_ptr
->security_info
->security_option
) << ").");
1298 if (!wccp2_check_security(service_list_ptr
, (char *) security_info
, (char *) &wccp2_i_see_you
, len
)) {
1299 debugs(80, DBG_IMPORTANT
, "Received WCCPv2 Packet failed authentication");
1303 /* Check that the router address is configured on this router */
1304 for (router_list_ptr
= &service_list_ptr
->router_list_head
; router_list_ptr
->next
!= NULL
; router_list_ptr
= router_list_ptr
->next
) {
1305 if (router_list_ptr
->router_sendto_address
.s_addr
== from
.sin_addr
.s_addr
)
1309 if (router_list_ptr
->next
== NULL
) {
1310 debugs(80, DBG_IMPORTANT
, "WCCPv2 Packet received from unknown router");
1314 /* Set the router id */
1315 router_list_ptr
->info
->router_address
= router_identity_info
->router_id_element
.router_address
;
1317 /* Increment the received id in the packet */
1318 if (ntohl(router_list_ptr
->info
->received_id
) != ntohl(router_identity_info
->router_id_element
.received_id
)) {
1319 debugs(80, 3, "Incoming WCCP2_I_SEE_YOU Received ID old=" << ntohl(router_list_ptr
->info
->received_id
) << " new=" << ntohl(router_identity_info
->router_id_element
.received_id
) << ".");
1320 router_list_ptr
->info
->received_id
= router_identity_info
->router_id_element
.received_id
;
1323 /* TODO: check return/forwarding methods */
1324 if (router_capability_header
== NULL
) {
1325 if ((Config
.Wccp2
.return_method
!= WCCP2_PACKET_RETURN_METHOD_GRE
) || (Config
.Wccp2
.forwarding_method
!= WCCP2_FORWARDING_METHOD_GRE
)) {
1326 debugs(80, DBG_IMPORTANT
, "wccp2HandleUdp: fatal error - A WCCP router does not support the forwarding method specified, only GRE supported");
1327 wccp2ConnectionClose();
1332 char *end
= ((char *) router_capability_header
) + sizeof(*router_capability_header
) + ntohs(router_capability_header
->capability_info_length
) - sizeof(struct wccp2_capability_info_header_t
);
1334 router_capability_element
= (struct wccp2_capability_element_t
*) (((char *) router_capability_header
) + sizeof(*router_capability_header
));
1336 while ((char *) router_capability_element
<= end
) {
1338 switch (ntohs(router_capability_element
->capability_type
)) {
1340 case WCCP2_CAPABILITY_FORWARDING_METHOD
:
1342 if (!(ntohl(router_capability_element
->capability_value
) & Config
.Wccp2
.forwarding_method
)) {
1343 debugs(80, DBG_IMPORTANT
, "wccp2HandleUdp: fatal error - A WCCP router has specified a different forwarding method " << ntohl(router_capability_element
->capability_value
) << ", expected " << Config
.Wccp2
.forwarding_method
);
1344 wccp2ConnectionClose();
1350 case WCCP2_CAPABILITY_ASSIGNMENT_METHOD
:
1352 if (!(ntohl(router_capability_element
->capability_value
) & Config
.Wccp2
.assignment_method
)) {
1353 debugs(80, DBG_IMPORTANT
, "wccp2HandleUdp: fatal error - A WCCP router has specified a different assignment method " << ntohl(router_capability_element
->capability_value
) << ", expected "<< Config
.Wccp2
.assignment_method
);
1354 wccp2ConnectionClose();
1360 case WCCP2_CAPABILITY_RETURN_METHOD
:
1362 if (!(ntohl(router_capability_element
->capability_value
) & Config
.Wccp2
.return_method
)) {
1363 debugs(80, DBG_IMPORTANT
, "wccp2HandleUdp: fatal error - A WCCP router has specified a different return method " << ntohl(router_capability_element
->capability_value
) << ", expected " << Config
.Wccp2
.return_method
);
1364 wccp2ConnectionClose();
1372 break; // ignore silently for now
1375 debugs(80, DBG_IMPORTANT
, "Unknown capability type in WCCPv2 Packet (" << ntohs(router_capability_element
->capability_type
) << ").");
1378 router_capability_element
= (struct wccp2_capability_element_t
*) (((char *) router_capability_element
) + sizeof(struct wccp2_item_header_t
) + ntohs(router_capability_element
->capability_length
));
1382 debugs(80, 5, "Cleaning out cache list");
1383 /* clean out the old cache list */
1385 for (cache_list_ptr
= &router_list_ptr
->cache_list_head
; cache_list_ptr
; cache_list_ptr
= cache_list_ptr_next
) {
1386 cache_list_ptr_next
= cache_list_ptr
->next
;
1388 if (cache_list_ptr
!= &router_list_ptr
->cache_list_head
) {
1389 xfree(cache_list_ptr
);
1393 router_list_ptr
->num_caches
= htonl(0);
1396 /* Check to see if we're the master cache and update the cache list */
1398 service_list_ptr
->lowest_ip
= 1;
1399 cache_list_ptr
= &router_list_ptr
->cache_list_head
;
1401 /* to find the list of caches, we start at the end of the router view header */
1403 ptr
= (char *) (router_view_header
) + sizeof(struct router_view_t
);
1405 /* Then we read the number of routers */
1406 memcpy(&tmp
, ptr
, sizeof(tmp
));
1408 /* skip the number plus all the ip's */
1410 ptr
+= sizeof(tmp
) + (ntohl(tmp
) * sizeof(struct in_addr
));
1412 /* Then read the number of caches */
1413 memcpy(&tmp
, ptr
, sizeof(tmp
));
1416 if (ntohl(tmp
) != 0) {
1417 /* search through the list of received-from ip addresses */
1419 for (num_caches
= 0; num_caches
< (int) ntohl(tmp
); ++num_caches
) {
1420 /* Get a copy of the ip */
1421 memset(&cache_address
, 0, sizeof(cache_address
)); // Make GCC happy
1423 switch (Config
.Wccp2
.assignment_method
) {
1425 case WCCP2_ASSIGNMENT_METHOD_HASH
:
1427 cache_identity
= (struct wccp2_cache_identity_info_t
*) ptr
;
1429 ptr
+= sizeof(struct wccp2_cache_identity_info_t
);
1431 memcpy(&cache_address
, &cache_identity
->addr
, sizeof(struct in_addr
));
1433 cache_list_ptr
->weight
= ntohs(cache_identity
->weight
);
1436 case WCCP2_ASSIGNMENT_METHOD_MASK
:
1438 cache_mask_info
= (struct cache_mask_info_t
*) ptr
;
1440 /* The mask assignment has an undocumented variable length entry here */
1442 if (ntohl(cache_mask_info
->num1
) == 3) {
1444 cache_mask_identity
= (struct wccp2_cache_mask_identity_info_t
*) ptr
;
1446 ptr
+= sizeof(struct wccp2_cache_mask_identity_info_t
);
1448 memcpy(&cache_address
, &cache_mask_identity
->addr
, sizeof(struct in_addr
));
1451 ptr
+= sizeof(struct cache_mask_info_t
);
1453 memcpy(&cache_address
, &cache_mask_info
->addr
, sizeof(struct in_addr
));
1456 cache_list_ptr
->weight
= 0;
1460 fatalf("Unknown Wccp2 assignment method\n");
1463 /* Update the cache list */
1464 cache_list_ptr
->cache_ip
= cache_address
;
1466 cache_list_ptr
->next
= (wccp2_cache_list_t
*) xcalloc(1, sizeof(struct wccp2_cache_list_t
));
1468 cache_list_ptr
= cache_list_ptr
->next
;
1470 cache_list_ptr
->next
= NULL
;
1472 debugs (80, 5, "checking cache list: (" << std::hex
<< cache_address
.s_addr
<< ":" << router_list_ptr
->local_ip
.s_addr
<< ")");
1474 /* Check to see if it's the master, or us */
1476 if (cache_address
.s_addr
== router_list_ptr
->local_ip
.s_addr
) {
1480 if (cache_address
.s_addr
< router_list_ptr
->local_ip
.s_addr
) {
1481 service_list_ptr
->lowest_ip
= 0;
1485 debugs(80, 5, "Adding ourselves as the only cache");
1487 /* Update the cache list */
1488 cache_list_ptr
->cache_ip
= router_list_ptr
->local_ip
;
1490 cache_list_ptr
->next
= (wccp2_cache_list_t
*) xcalloc(1, sizeof(struct wccp2_cache_list_t
));
1491 cache_list_ptr
= cache_list_ptr
->next
;
1492 cache_list_ptr
->next
= NULL
;
1494 service_list_ptr
->lowest_ip
= 1;
1499 wccp2SortCacheList(&router_list_ptr
->cache_list_head
);
1501 router_list_ptr
->num_caches
= htonl(num_caches
);
1503 if ((found
== 1) && (service_list_ptr
->lowest_ip
== 1)) {
1504 if (ntohl(router_view_header
->change_number
) != router_list_ptr
->member_change
) {
1505 debugs(80, 4, "Change detected - queueing up new assignment");
1506 router_list_ptr
->member_change
= ntohl(router_view_header
->change_number
);
1507 eventDelete(wccp2AssignBuckets
, NULL
);
1508 eventAdd("wccp2AssignBuckets", wccp2AssignBuckets
, NULL
, 15.0, 1);
1510 debugs(80, 5, "Change not detected (" << ntohl(router_view_header
->change_number
) << " = " << router_list_ptr
->member_change
<< ")");
1513 eventDelete(wccp2AssignBuckets
, NULL
);
1514 debugs(80, 5, "I am not the lowest ip cache - not assigning buckets");
1519 wccp2HereIam(void *)
1521 struct wccp2_service_list_t
*service_list_ptr
;
1523 struct wccp2_router_list_t
*router_list_ptr
;
1525 struct wccp2_identity_info_t
*wccp2_identity_info_ptr
;
1527 struct wccp2_mask_identity_info_t
*wccp2_mask_identity_info_ptr
;
1531 debugs(80, 6, "wccp2HereIam: Called");
1533 if (wccp2_connected
== 0) {
1534 debugs(80, DBG_IMPORTANT
, "wccp2HereIam: wccp2 socket closed. Shutting down WCCP2");
1538 /* Wait if store dirs are rebuilding */
1539 if (StoreController::store_dirs_rebuilding
&& Config
.Wccp2
.rebuildwait
) {
1540 eventAdd("wccp2HereIam", wccp2HereIam
, NULL
, 1.0, 1);
1544 router
.port(WCCP_PORT
);
1546 /* for each router on each service send a packet */
1547 service_list_ptr
= wccp2_service_list_head
;
1549 while (service_list_ptr
!= NULL
) {
1550 debugs(80, 5, "wccp2HereIam: sending to service id " << service_list_ptr
->info
.service_id
);
1552 for (router_list_ptr
= &service_list_ptr
->router_list_head
; router_list_ptr
->next
!= NULL
; router_list_ptr
= router_list_ptr
->next
) {
1553 router
= router_list_ptr
->router_sendto_address
;
1555 /* Set the cache id (ip) */
1557 switch (Config
.Wccp2
.assignment_method
) {
1559 case WCCP2_ASSIGNMENT_METHOD_HASH
:
1561 wccp2_identity_info_ptr
= (struct wccp2_identity_info_t
*) service_list_ptr
->wccp2_identity_info_ptr
;
1562 wccp2_identity_info_ptr
->cache_identity
.addr
= router_list_ptr
->local_ip
;
1565 case WCCP2_ASSIGNMENT_METHOD_MASK
:
1567 wccp2_mask_identity_info_ptr
= (struct wccp2_mask_identity_info_t
*) service_list_ptr
->wccp2_identity_info_ptr
;
1568 wccp2_mask_identity_info_ptr
->cache_identity
.addr
= router_list_ptr
->local_ip
;
1572 fatalf("Unknown Wccp2 assignment method\n");
1575 /* Security update, if needed */
1577 if (service_list_ptr
->wccp2_security_type
== WCCP2_MD5_SECURITY
) {
1578 wccp2_update_md5_security(service_list_ptr
->wccp_password
, (char *) service_list_ptr
->security_info
, service_list_ptr
->wccp_packet
, service_list_ptr
->wccp_packet_size
);
1581 debugs(80, 3, "Sending HereIam packet size " << service_list_ptr
->wccp_packet_size
);
1582 /* Send the packet */
1584 if (wccp2_numrouters
> 1) {
1585 comm_udp_sendto(theWccp2Connection
,
1587 &service_list_ptr
->wccp_packet
,
1588 service_list_ptr
->wccp_packet_size
);
1590 if (send(theWccp2Connection
, &service_list_ptr
->wccp_packet
, service_list_ptr
->wccp_packet_size
, 0) < static_cast<int>(service_list_ptr
->wccp_packet_size
)) {
1592 debugs(80, 2, "ERROR: failed to send WCCPv2 HERE_I_AM packet to " << router
<< " : " << xstrerr(xerrno
));
1597 service_list_ptr
= service_list_ptr
->next
;
1600 eventAdd("wccp2HereIam", wccp2HereIam
, NULL
, 10.0, 1);
1604 wccp2AssignBuckets(void *)
1606 struct wccp2_service_list_t
*service_list_ptr
;
1608 struct wccp2_router_list_t
*router_list_ptr
;
1610 struct wccp2_cache_list_t
*cache_list_ptr
;
1611 char wccp_packet
[WCCP_RESPONSE_SIZE
];
1612 short int offset
, saved_offset
, assignment_offset
, alt_assignment_offset
;
1614 struct sockaddr_in router
;
1617 uint32_t service_flags
;
1618 unsigned short port
= WCCP_PORT
;
1620 /* Packet segments */
1622 struct wccp2_message_header_t
*main_header
;
1624 struct wccp2_security_md5_t
*security
= NULL
;
1625 /* service from service struct */
1627 struct wccp2_item_header_t
*assignment_header
;
1629 struct wccp2_item_header_t
*alt_assignment_type_header
= NULL
;
1631 struct assignment_key_t
*assignment_key
;
1632 /* number of routers */
1634 struct wccp2_router_assign_element_t
*router_assign
;
1635 /* number of caches */
1637 struct in_addr
*cache_address
;
1638 /* Alternative assignment mask/values */
1641 struct wccp2_mask_element_t
*mask_element
;
1643 struct wccp2_value_element_t
*value_element
;
1644 int valuecounter
, value
;
1647 assignment_offset
= alt_assignment_offset
= 0;
1649 router_len
= sizeof(router
);
1650 memset(&router
, '\0', router_len
);
1651 router
.sin_family
= AF_INET
;
1652 router
.sin_port
= htons(port
);
1654 /* Start main header - fill in length later */
1657 main_header
= (struct wccp2_message_header_t
*) &wccp_packet
[offset
];
1658 main_header
->type
= htonl(WCCP2_REDIRECT_ASSIGN
);
1659 main_header
->version
= htons(WCCP2_VERSION
);
1661 debugs(80, 2, "Running wccp2AssignBuckets");
1662 service_list_ptr
= wccp2_service_list_head
;
1664 while (service_list_ptr
!= NULL
) {
1665 /* If we're not the lowest, we don't need to worry */
1667 if (service_list_ptr
->lowest_ip
== 0) {
1669 service_list_ptr
= service_list_ptr
->next
;
1673 /* reset the offset */
1675 offset
= sizeof(struct wccp2_message_header_t
);
1677 /* build packet header from hereIam packet */
1679 /* XXX this should be made more generic! */
1680 /* XXX and I hate magic numbers! */
1681 switch (service_list_ptr
->wccp2_security_type
) {
1683 case WCCP2_NO_SECURITY
:
1685 security
= (struct wccp2_security_md5_t
*) &wccp_packet
[offset
];
1686 memcpy(security
, service_list_ptr
->security_info
, 8);
1690 case WCCP2_MD5_SECURITY
:
1692 security
= (struct wccp2_security_md5_t
*) &wccp_packet
[offset
];
1694 memcpy(security
, service_list_ptr
->security_info
, sizeof(struct wccp2_security_md5_t
));
1696 offset
+= sizeof(struct wccp2_security_md5_t
);
1700 fatalf("Unknown Wccp2 security type\n");
1705 memcpy(&wccp_packet
[offset
], service_list_ptr
->service_info
, sizeof(struct wccp2_service_info_t
));
1707 offset
+= sizeof(struct wccp2_service_info_t
);
1709 /* assignment header - fill in length later */
1711 assignment_header
= (struct wccp2_item_header_t
*) &wccp_packet
[offset
];
1713 switch (Config
.Wccp2
.assignment_method
) {
1715 case WCCP2_ASSIGNMENT_METHOD_HASH
:
1716 assignment_header
->type
= htons(WCCP2_REDIRECT_ASSIGNMENT
);
1718 offset
+= sizeof(struct wccp2_item_header_t
);
1719 assignment_offset
= offset
;
1722 case WCCP2_ASSIGNMENT_METHOD_MASK
:
1723 assignment_header
->type
= htons(WCCP2_ALT_ASSIGNMENT
);
1725 offset
+= sizeof(struct wccp2_item_header_t
);
1726 assignment_offset
= offset
;
1728 /* The alternative assignment has an extra header, fill in length later */
1730 alt_assignment_type_header
= (struct wccp2_item_header_t
*) &wccp_packet
[offset
];
1731 alt_assignment_type_header
->type
= htons(WCCP2_MASK_ASSIGNMENT
);
1733 offset
+= sizeof(struct wccp2_item_header_t
);
1734 alt_assignment_offset
= offset
;
1739 fatalf("Unknown Wccp2 assignment method\n");
1742 /* Assignment key - fill in master ip later */
1744 assignment_key
= (struct assignment_key_t
*) &wccp_packet
[offset
];
1746 ++service_list_ptr
->change_num
;
1747 assignment_key
->master_number
= htonl(service_list_ptr
->change_num
);
1749 offset
+= sizeof(struct assignment_key_t
);
1751 /* Number of routers */
1752 memcpy(&wccp_packet
[offset
], &service_list_ptr
->num_routers
, sizeof(service_list_ptr
->num_routers
));
1754 offset
+= sizeof(service_list_ptr
->num_routers
);
1756 for (router_list_ptr
= &service_list_ptr
->router_list_head
; router_list_ptr
->next
!= NULL
; router_list_ptr
= router_list_ptr
->next
) {
1760 router_assign
= (struct wccp2_router_assign_element_t
*) &wccp_packet
[offset
];
1761 router_assign
->router_address
= router_list_ptr
->info
->router_address
;
1762 router_assign
->received_id
= router_list_ptr
->info
->received_id
;
1763 router_assign
->change_number
= htonl(router_list_ptr
->member_change
);
1765 offset
+= sizeof(struct wccp2_router_assign_element_t
);
1768 saved_offset
= offset
;
1770 for (router_list_ptr
= &service_list_ptr
->router_list_head
; router_list_ptr
->next
!= NULL
; router_list_ptr
= router_list_ptr
->next
) {
1771 unsigned long *weight
= (unsigned long *)xcalloc(sizeof(*weight
), ntohl(router_list_ptr
->num_caches
));
1772 unsigned long total_weight
= 0;
1773 int num_caches
= ntohl(router_list_ptr
->num_caches
);
1775 offset
= saved_offset
;
1777 switch (Config
.Wccp2
.assignment_method
) {
1779 case WCCP2_ASSIGNMENT_METHOD_HASH
:
1780 /* Number of caches */
1781 memcpy(&wccp_packet
[offset
], &router_list_ptr
->num_caches
, sizeof(router_list_ptr
->num_caches
));
1782 offset
+= sizeof(router_list_ptr
->num_caches
);
1787 for (cache
= 0, cache_list_ptr
= &router_list_ptr
->cache_list_head
; cache_list_ptr
->next
; cache_list_ptr
= cache_list_ptr
->next
, ++cache
) {
1790 cache_address
= (struct in_addr
*) &wccp_packet
[offset
];
1792 memcpy(cache_address
, &cache_list_ptr
->cache_ip
, sizeof(struct in_addr
));
1793 total_weight
+= cache_list_ptr
->weight
<< 12;
1794 weight
[cache
] = cache_list_ptr
->weight
<< 12;
1796 offset
+= sizeof(struct in_addr
);
1801 buckets
= (char *) &wccp_packet
[offset
];
1803 memset(buckets
, '\0', WCCP_BUCKETS
);
1805 if (num_caches
!= 0) {
1806 if (total_weight
== 0) {
1807 for (bucket_counter
= 0; bucket_counter
< WCCP_BUCKETS
; ++bucket_counter
) {
1808 buckets
[bucket_counter
] = (char) (bucket_counter
% num_caches
);
1811 unsigned long *assigned
= (unsigned long *)xcalloc(sizeof(*assigned
), num_caches
);
1812 unsigned long done
= 0;
1814 unsigned long per_bucket
= total_weight
/ WCCP_BUCKETS
;
1816 for (bucket_counter
= 0; bucket_counter
< WCCP_BUCKETS
; ++bucket_counter
) {
1820 for (n
= num_caches
; n
; --n
) {
1823 if (cache
>= num_caches
)
1826 if (!weight
[cache
]) {
1831 if (assigned
[cache
] <= done
)
1835 buckets
[bucket_counter
] = (char) cache
;
1836 step
= per_bucket
* total_weight
/ weight
[cache
];
1837 assigned
[cache
] += step
;
1841 safe_free(assigned
);
1845 offset
+= (WCCP_BUCKETS
* sizeof(char));
1849 case WCCP2_ASSIGNMENT_METHOD_MASK
:
1850 num_maskval
= htonl(1);
1851 memcpy(&wccp_packet
[offset
], &num_maskval
, sizeof(int));
1852 offset
+= sizeof(int);
1854 mask_element
= (struct wccp2_mask_element_t
*) &wccp_packet
[offset
];
1855 service_flags
= ntohl(service_list_ptr
->service_info
->service_flags
);
1857 if ((service_flags
& WCCP2_SERVICE_SRC_IP_HASH
) || (service_flags
& WCCP2_SERVICE_SRC_IP_ALT_HASH
)) {
1858 mask_element
->source_ip_mask
= htonl(0x00001741);
1859 mask_element
->dest_ip_mask
= 0;
1860 mask_element
->source_port_mask
= 0;
1861 mask_element
->dest_port_mask
= 0;
1862 } else if ((service_list_ptr
->info
.service
== WCCP2_SERVICE_STANDARD
) || (service_flags
& WCCP2_SERVICE_DST_IP_HASH
) || (service_flags
& WCCP2_SERVICE_DST_IP_ALT_HASH
)) {
1863 mask_element
->source_ip_mask
= 0;
1864 mask_element
->dest_ip_mask
= htonl(0x00001741);
1865 mask_element
->source_port_mask
= 0;
1866 mask_element
->dest_port_mask
= 0;
1867 } else if ((service_flags
& WCCP2_SERVICE_SRC_PORT_HASH
) || (service_flags
& WCCP2_SERVICE_SRC_PORT_ALT_HASH
)) {
1868 mask_element
->source_ip_mask
= 0;
1869 mask_element
->dest_ip_mask
= 0;
1870 mask_element
->source_port_mask
= htons(0x1741);
1871 mask_element
->dest_port_mask
= 0;
1872 } else if ((service_flags
& WCCP2_SERVICE_DST_PORT_HASH
) || (service_flags
& WCCP2_SERVICE_DST_PORT_ALT_HASH
)) {
1873 mask_element
->source_ip_mask
= 0;
1874 mask_element
->dest_ip_mask
= 0;
1875 mask_element
->source_port_mask
= 0;
1876 mask_element
->dest_port_mask
= htons(0x1741);
1878 fatalf("Unknown service hash method\n");
1881 mask_element
->number_values
= htonl(64);
1883 offset
+= sizeof(struct wccp2_mask_element_t
);
1885 cache_list_ptr
= &router_list_ptr
->cache_list_head
;
1888 for (valuecounter
= 0; valuecounter
< 64; ++valuecounter
) {
1890 value_element
= (struct wccp2_value_element_t
*) &wccp_packet
[offset
];
1892 /* Update the value according the the "correct" formula */
1894 for (; (value
& 0x1741) != value
; ++value
) {
1895 assert(value
<= 0x1741);
1898 if ((service_flags
& WCCP2_SERVICE_SRC_IP_HASH
) || (service_flags
& WCCP2_SERVICE_SRC_IP_ALT_HASH
)) {
1899 value_element
->source_ip_value
= htonl(value
);
1900 value_element
->dest_ip_value
= 0;
1901 value_element
->source_port_value
= 0;
1902 value_element
->dest_port_value
= 0;
1903 } else if ((service_list_ptr
->info
.service
== WCCP2_SERVICE_STANDARD
) || (service_flags
& WCCP2_SERVICE_DST_IP_HASH
) || (service_flags
& WCCP2_SERVICE_DST_IP_ALT_HASH
)) {
1904 value_element
->source_ip_value
= 0;
1905 value_element
->dest_ip_value
= htonl(value
);
1906 value_element
->source_port_value
= 0;
1907 value_element
->dest_port_value
= 0;
1908 } else if ((service_flags
& WCCP2_SERVICE_SRC_PORT_HASH
) || (service_flags
& WCCP2_SERVICE_SRC_PORT_ALT_HASH
)) {
1909 value_element
->source_ip_value
= 0;
1910 value_element
->dest_ip_value
= 0;
1911 value_element
->source_port_value
= htons(value
);
1912 value_element
->dest_port_value
= 0;
1913 } else if ((service_flags
& WCCP2_SERVICE_DST_PORT_HASH
) || (service_flags
& WCCP2_SERVICE_DST_PORT_ALT_HASH
)) {
1914 value_element
->source_ip_value
= 0;
1915 value_element
->dest_ip_value
= 0;
1916 value_element
->source_port_value
= 0;
1917 value_element
->dest_port_value
= htons(value
);
1919 fatalf("Unknown service hash method\n");
1922 value_element
->cache_ip
= cache_list_ptr
->cache_ip
;
1924 offset
+= sizeof(struct wccp2_value_element_t
);
1927 /* Assign the next value to the next cache */
1929 if ((cache_list_ptr
->next
) && (cache_list_ptr
->next
->next
))
1930 cache_list_ptr
= cache_list_ptr
->next
;
1932 cache_list_ptr
= &router_list_ptr
->cache_list_head
;
1935 /* Fill in length */
1936 alt_assignment_type_header
->length
= htons(offset
- alt_assignment_offset
);
1941 fatalf("Unknown Wccp2 assignment method\n");
1944 /* Fill in length */
1946 assignment_header
->length
= htons(offset
- assignment_offset
);
1948 /* Fill in assignment key */
1949 assignment_key
->master_ip
= router_list_ptr
->local_ip
;
1953 main_header
->length
= htons(offset
- sizeof(struct wccp2_message_header_t
));
1955 /* set the destination address */
1956 router
.sin_addr
= router_list_ptr
->router_sendto_address
;
1958 /* Security update, if needed */
1960 if (service_list_ptr
->wccp2_security_type
== WCCP2_MD5_SECURITY
) {
1961 wccp2_update_md5_security(service_list_ptr
->wccp_password
, (char *) security
, wccp_packet
, offset
);
1964 if (ntohl(router_list_ptr
->num_caches
)) {
1967 /* FIXME INET6 : drop temp conversion */
1968 Ip::Address
tmp_rtr(router
);
1970 if (wccp2_numrouters
> 1) {
1971 comm_udp_sendto(theWccp2Connection
,
1976 if (send(theWccp2Connection
, &wccp_packet
, offset
, 0) < static_cast<int>(offset
)) {
1978 debugs(80, 2, "ERROR: failed to send WCCPv2 HERE_I_AM packet to " << tmp_rtr
<< " : " << xstrerr(xerrno
));
1985 service_list_ptr
= service_list_ptr
->next
;
1990 * Configuration option parsing code
1994 * Parse wccp2_return_method and wccp2_forwarding_method options
1995 * they can be '1' aka 'gre' or '2' aka 'l2'
1996 * representing the integer numeric of the same.
1999 parse_wccp2_method(int *method
)
2003 /* Snarf the method */
2004 if ((t
= ConfigParser::NextToken()) == NULL
) {
2005 debugs(80, DBG_CRITICAL
, "wccp2_*_method: missing setting.");
2010 /* update configuration if its valid */
2011 if (strcmp(t
, "gre") == 0 || strcmp(t
, "1") == 0) {
2012 *method
= WCCP2_METHOD_GRE
;
2013 } else if (strcmp(t
, "l2") == 0 || strcmp(t
, "2") == 0) {
2014 *method
= WCCP2_METHOD_L2
;
2016 debugs(80, DBG_CRITICAL
, "wccp2_*_method: unknown setting, got " << t
);
2022 dump_wccp2_method(StoreEntry
* e
, const char *label
, int v
)
2025 case WCCP2_METHOD_GRE
:
2026 storeAppendPrintf(e
, "%s gre\n", label
);
2028 case WCCP2_METHOD_L2
:
2029 storeAppendPrintf(e
, "%s l2\n", label
);
2032 debugs(80, DBG_CRITICAL
, "FATAL: WCCPv2 configured method (" << v
<< ") is not valid.");
2038 free_wccp2_method(int *)
2042 * Parse wccp2_assignment_method option
2043 * they can be '1' aka 'hash' or '2' aka 'mask'
2044 * representing the integer numeric of the same.
2047 parse_wccp2_amethod(int *method
)
2051 /* Snarf the method */
2052 if ((t
= ConfigParser::NextToken()) == NULL
) {
2053 debugs(80, DBG_CRITICAL
, "wccp2_assignment_method: missing setting.");
2058 /* update configuration if its valid */
2059 if (strcmp(t
, "hash") == 0 || strcmp(t
, "1") == 0) {
2060 *method
= WCCP2_ASSIGNMENT_METHOD_HASH
;
2061 } else if (strcmp(t
, "mask") == 0 || strcmp(t
, "2") == 0) {
2062 *method
= WCCP2_ASSIGNMENT_METHOD_MASK
;
2064 debugs(80, DBG_CRITICAL
, "wccp2_assignment_method: unknown setting, got " << t
);
2070 dump_wccp2_amethod(StoreEntry
* e
, const char *label
, int v
)
2073 case WCCP2_ASSIGNMENT_METHOD_HASH
:
2074 storeAppendPrintf(e
, "%s hash\n", label
);
2076 case WCCP2_ASSIGNMENT_METHOD_MASK
:
2077 storeAppendPrintf(e
, "%s mask\n", label
);
2080 debugs(80, DBG_CRITICAL
, "FATAL: WCCPv2 configured " << label
<< " (" << v
<< ") is not valid.");
2086 free_wccp2_amethod(int *)
2092 * wccp2_service {standard|dynamic} {id} (password=password)
2095 parse_wccp2_service(void *)
2100 int security_type
= WCCP2_NO_SECURITY
;
2101 char wccp_password
[WCCP2_PASSWORD_LEN
+ 1];
2103 if (wccp2_connected
== 1) {
2104 debugs(80, DBG_IMPORTANT
, "WCCPv2: Somehow reparsing the configuration without having shut down WCCP! Try reloading squid again.");
2108 /* Snarf the type */
2109 if ((t
= ConfigParser::NextToken()) == NULL
) {
2110 debugs(80, DBG_CRITICAL
, "wccp2ParseServiceInfo: missing service info type (standard|dynamic)");
2115 if (strcmp(t
, "standard") == 0) {
2116 service
= WCCP2_SERVICE_STANDARD
;
2117 } else if (strcmp(t
, "dynamic") == 0) {
2118 service
= WCCP2_SERVICE_DYNAMIC
;
2120 debugs(80, DBG_CRITICAL
, "wccp2ParseServiceInfo: bad service info type (expected standard|dynamic, got " << t
<< ")");
2126 service_id
= GetInteger();
2128 if (service_id
< 0 || service_id
> 255) {
2129 debugs(80, DBG_CRITICAL
, "ERROR: invalid WCCP service id " << service_id
<< " (must be between 0 .. 255)");
2134 memset(wccp_password
, 0, sizeof(wccp_password
));
2135 /* Handle password, if any */
2137 if ((t
= ConfigParser::NextToken()) != NULL
) {
2138 if (strncmp(t
, "password=", 9) == 0) {
2139 security_type
= WCCP2_MD5_SECURITY
;
2140 strncpy(wccp_password
, t
+ 9, WCCP2_PASSWORD_LEN
);
2144 /* Create a placeholder service record */
2145 wccp2_add_service_list(service
, service_id
, 0, 0, 0, empty_portlist
, security_type
, wccp_password
);
2149 dump_wccp2_service(StoreEntry
* e
, const char *label
, void *)
2151 struct wccp2_service_list_t
*srv
;
2152 srv
= wccp2_service_list_head
;
2154 while (srv
!= NULL
) {
2155 debugs(80, 3, "dump_wccp2_service: id " << srv
->info
.service_id
<< ", type " << srv
->info
.service
);
2156 storeAppendPrintf(e
, "%s %s %d", label
,
2157 (srv
->info
.service
== WCCP2_SERVICE_DYNAMIC
) ? "dynamic" : "standard",
2158 srv
->info
.service_id
);
2160 if (srv
->wccp2_security_type
== WCCP2_MD5_SECURITY
) {
2161 storeAppendPrintf(e
, " %s", srv
->wccp_password
);
2164 storeAppendPrintf(e
, "\n");
2171 free_wccp2_service(void *)
2175 check_null_wccp2_service(void *)
2177 return !wccp2_service_list_head
;
2183 * wccp2_service_info {id} stuff..
2187 * + flags=flag,flag,flag..
2188 * + proto=protocol (tcp|udp)
2189 * + ports=port,port,port (up to a max of 8)
2190 * + priority=priority (0->255)
2192 * The flags here are:
2193 * src_ip_hash, dst_ip_hash, source_port_hash, dst_port_hash, ports_defined,
2194 * ports_source, src_ip_alt_hash, dst_ip_alt_hash, src_port_alt_hash, dst_port_alt_hash
2197 parse_wccp2_service_flags(char *flags
)
2205 while (size_t len
= strcspn(flag
, ",")) {
2207 if (strncmp(flag
, "src_ip_hash", len
) == 0) {
2208 retflag
|= WCCP2_SERVICE_SRC_IP_HASH
;
2209 } else if (strncmp(flag
, "dst_ip_hash", len
) == 0) {
2210 retflag
|= WCCP2_SERVICE_DST_IP_HASH
;
2211 } else if (strncmp(flag
, "source_port_hash", len
) == 0) {
2212 retflag
|= WCCP2_SERVICE_SRC_PORT_HASH
;
2213 } else if (strncmp(flag
, "dst_port_hash", len
) == 0) {
2214 retflag
|= WCCP2_SERVICE_DST_PORT_HASH
;
2215 } else if (strncmp(flag
, "ports_source", len
) == 0) {
2216 retflag
|= WCCP2_SERVICE_PORTS_SOURCE
;
2217 } else if (strncmp(flag
, "src_ip_alt_hash", len
) == 0) {
2218 retflag
|= WCCP2_SERVICE_SRC_IP_ALT_HASH
;
2219 } else if (strncmp(flag
, "dst_ip_alt_hash", len
) == 0) {
2220 retflag
|= WCCP2_SERVICE_DST_IP_ALT_HASH
;
2221 } else if (strncmp(flag
, "src_port_alt_hash", len
) == 0) {
2222 retflag
|= WCCP2_SERVICE_SRC_PORT_ALT_HASH
;
2223 } else if (strncmp(flag
, "dst_port_alt_hash", len
) == 0) {
2224 retflag
|= WCCP2_SERVICE_DST_PORT_ALT_HASH
;
2227 fatalf("Unknown wccp2 service flag: %s\n", flag
);
2230 if (flag
[len
] == '\0')
2240 parse_wccp2_service_ports(char *options
, int portlist
[])
2247 char *tmp
= options
;
2248 static char copy
[10];
2250 while (size_t len
= strcspn(tmp
, ",")) {
2251 if (i
>= WCCP2_NUMPORTS
) {
2252 fatalf("parse_wccp2_service_ports: too many ports (maximum: 8) in list '%s'\n", options
);
2254 if (len
> 6) { // 6 because "65535,"
2255 fatalf("parse_wccp2_service_ports: port value '%s' isn't valid (1..65535)\n", tmp
);
2258 memcpy(copy
, tmp
, len
);
2260 int p
= xatoi(copy
);
2262 if (p
< 1 || p
> 65535) {
2263 fatalf("parse_wccp2_service_ports: port value '%s' isn't valid (1..65535)\n", tmp
);
2268 if (tmp
[len
] == '\0')
2275 parse_wccp2_service_info(void *)
2280 int portlist
[WCCP2_NUMPORTS
];
2281 int protocol
= -1; /* IPPROTO_TCP | IPPROTO_UDP */
2283 struct wccp2_service_list_t
*srv
;
2286 if (wccp2_connected
== 1) {
2287 debugs(80, DBG_IMPORTANT
, "WCCPv2: Somehow reparsing the configuration without having shut down WCCP! Try reloading squid again.");
2291 debugs(80, 5, "parse_wccp2_service_info: called");
2292 memset(portlist
, 0, sizeof(portlist
));
2293 /* First argument: id */
2294 service_id
= GetInteger();
2296 if (service_id
< 0 || service_id
> 255) {
2297 debugs(80, DBG_CRITICAL
, "ERROR: invalid WCCP service id " << service_id
<< " (must be between 0 .. 255)");
2302 /* Next: find the (hopefully!) existing service */
2303 srv
= wccp2_get_service_by_id(WCCP2_SERVICE_DYNAMIC
, service_id
);
2306 fatalf("parse_wccp2_service_info: unknown dynamic service id %d: you need to define it using wccp2_service (and make sure you wish to configure it as a dynamic service.)\n", service_id
);
2309 /* Next: loop until we don't have any more tokens */
2310 while ((t
= ConfigParser::NextToken()) != NULL
) {
2311 if (strncmp(t
, "flags=", 6) == 0) {
2312 /* XXX eww, string pointer math */
2313 flags
= parse_wccp2_service_flags(t
+ 6);
2314 } else if (strncmp(t
, "ports=", 6) == 0) {
2315 parse_wccp2_service_ports(t
+ 6, portlist
);
2316 flags
|= WCCP2_SERVICE_PORTS_DEFINED
;
2317 } else if (strncmp(t
, "protocol=tcp", 12) == 0) {
2318 protocol
= IPPROTO_TCP
;
2319 } else if (strncmp(t
, "protocol=udp", 12) == 0) {
2320 protocol
= IPPROTO_UDP
;
2321 } else if (strncmp(t
, "protocol=", 9) == 0) {
2322 fatalf("parse_wccp2_service_info: id %d: unknown protocol (%s) - must be tcp or udp!\n", service_id
, t
);
2323 } else if (strncmp(t
, "priority=", 9) == 0) {
2324 priority
= strtol(t
+ 9, &end
, 0);
2326 if (priority
< 0 || priority
> 255) {
2327 fatalf("parse_wccp2_service_info: id %d: %s out of range (0..255)!\n", service_id
, t
);
2330 fatalf("parse_wccp2_service_info: id %d: unknown option '%s'\n", service_id
, t
);
2334 /* Check everything is set */
2335 if (priority
== -1) {
2336 fatalf("parse_wccp2_service_info: service %d: no priority defined (valid: 0..255)!\n", service_id
);
2339 if (protocol
== -1) {
2340 fatalf("parse_wccp2_service_info: service %d: no protocol defined (valid: tcp or udp)!\n", service_id
);
2343 if (!(flags
& WCCP2_SERVICE_PORTS_DEFINED
)) {
2344 fatalf("parse_wccp2_service_info: service %d: no ports defined!\n", service_id
);
2347 /* rightio! now we can update */
2348 wccp2_update_service(srv
, WCCP2_SERVICE_DYNAMIC
, service_id
, priority
,
2349 protocol
, flags
, portlist
);
2355 dump_wccp2_service_info(StoreEntry
* e
, const char *label
, void *)
2357 struct wccp2_service_list_t
*srv
;
2359 srv
= wccp2_service_list_head
;
2361 while (srv
!= NULL
) {
2362 debugs(80, 3, "dump_wccp2_service_info: id " << srv
->info
.service_id
<< " (type " << srv
->info
.service
<< ")");
2364 /* We don't need to spit out information for standard services */
2366 if (srv
->info
.service
== WCCP2_SERVICE_STANDARD
) {
2367 debugs(80, 3, "dump_wccp2_service_info: id " << srv
->info
.service_id
<< ": standard service, not dumping info");
2374 storeAppendPrintf(e
, "%s %d", label
, srv
->info
.service_id
);
2377 storeAppendPrintf(e
, " priority=%d", srv
->info
.service_priority
);
2380 flags
= ntohl(srv
->info
.service_flags
);
2384 storeAppendPrintf(e
, " flags=");
2386 if (flags
& WCCP2_SERVICE_SRC_IP_HASH
) {
2387 storeAppendPrintf(e
, "src_ip_hash");
2391 if (flags
& WCCP2_SERVICE_DST_IP_HASH
) {
2392 storeAppendPrintf(e
, "%sdst_ip_hash", comma
? "," : "");
2396 if (flags
& WCCP2_SERVICE_SRC_PORT_HASH
) {
2397 storeAppendPrintf(e
, "%ssource_port_hash", comma
? "," : "");
2401 if (flags
& WCCP2_SERVICE_DST_PORT_HASH
) {
2402 storeAppendPrintf(e
, "%sdst_port_hash", comma
? "," : "");
2406 if (flags
& WCCP2_SERVICE_PORTS_DEFINED
) {
2407 storeAppendPrintf(e
, "%sports_defined", comma
? "," : "");
2411 if (flags
& WCCP2_SERVICE_PORTS_SOURCE
) {
2412 storeAppendPrintf(e
, "%sports_source", comma
? "," : "");
2416 if (flags
& WCCP2_SERVICE_SRC_IP_ALT_HASH
) {
2417 storeAppendPrintf(e
, "%ssrc_ip_alt_hash", comma
? "," : "");
2421 if (flags
& WCCP2_SERVICE_DST_IP_ALT_HASH
) {
2422 storeAppendPrintf(e
, "%ssrc_ip_alt_hash", comma
? "," : "");
2426 if (flags
& WCCP2_SERVICE_SRC_PORT_ALT_HASH
) {
2427 storeAppendPrintf(e
, "%ssrc_port_alt_hash", comma
? "," : "");
2431 if (flags
& WCCP2_SERVICE_DST_PORT_ALT_HASH
) {
2432 storeAppendPrintf(e
, "%sdst_port_alt_hash", comma
? "," : "");
2433 //comma = true; // uncomment if more options added
2440 if (srv
->info
.port0
!= 0) {
2441 storeAppendPrintf(e
, " ports=%d", ntohs(srv
->info
.port0
));
2445 if (srv
->info
.port1
!= 0) {
2446 storeAppendPrintf(e
, "%s%d", comma
? "," : "ports=", ntohs(srv
->info
.port1
));
2450 if (srv
->info
.port2
!= 0) {
2451 storeAppendPrintf(e
, "%s%d", comma
? "," : "ports=", ntohs(srv
->info
.port2
));
2455 if (srv
->info
.port3
!= 0) {
2456 storeAppendPrintf(e
, "%s%d", comma
? "," : "ports=", ntohs(srv
->info
.port3
));
2460 if (srv
->info
.port4
!= 0) {
2461 storeAppendPrintf(e
, "%s%d", comma
? "," : "ports=", ntohs(srv
->info
.port4
));
2465 if (srv
->info
.port5
!= 0) {
2466 storeAppendPrintf(e
, "%s%d", comma
? "," : "ports=", ntohs(srv
->info
.port5
));
2470 if (srv
->info
.port6
!= 0) {
2471 storeAppendPrintf(e
, "%s%d", comma
? "," : "ports=", ntohs(srv
->info
.port6
));
2475 if (srv
->info
.port7
!= 0) {
2476 storeAppendPrintf(e
, "%s%d", comma
? "," : "ports=", ntohs(srv
->info
.port7
));
2477 // comma = true; // uncomment if more options are added
2481 storeAppendPrintf(e
, " protocol=%s", (srv
->info
.service_protocol
== IPPROTO_TCP
) ? "tcp" : "udp");
2483 storeAppendPrintf(e
, "\n");
2489 /* Sort the cache list by doing a "selection sort" by IP address */
2491 wccp2SortCacheList(struct wccp2_cache_list_t
*head
)
2493 struct wccp2_cache_list_t tmp
;
2494 struct wccp2_cache_list_t
*this_item
;
2495 struct wccp2_cache_list_t
*find_item
;
2496 struct wccp2_cache_list_t
*next_lowest
;
2498 /* Go through each position in the list one at a time */
2499 for (this_item
= head
; this_item
->next
; this_item
= this_item
->next
) {
2500 /* Find the item with the lowest IP */
2501 next_lowest
= this_item
;
2503 for (find_item
= this_item
; find_item
->next
; find_item
= find_item
->next
) {
2504 if (find_item
->cache_ip
.s_addr
< next_lowest
->cache_ip
.s_addr
) {
2505 next_lowest
= find_item
;
2508 /* Swap if we need to */
2509 if (next_lowest
!= this_item
) {
2510 /* First make a copy of the current item */
2511 memcpy(&tmp
, this_item
, sizeof(struct wccp2_cache_list_t
));
2513 /* Next update the pointers to maintain the linked list */
2514 tmp
.next
= next_lowest
->next
;
2515 next_lowest
->next
= this_item
->next
;
2517 /* Finally copy the updated items to their correct location */
2518 memcpy(this_item
, next_lowest
, sizeof(struct wccp2_cache_list_t
));
2519 memcpy(next_lowest
, &tmp
, sizeof(struct wccp2_cache_list_t
));
2525 free_wccp2_service_info(void *)
2528 #endif /* USE_WCCPv2 */