2 * Copyright (C) 1996-2023 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"
32 #define WCCP_PORT 2048
33 #define WCCP_RESPONSE_SIZE 12448
34 #define WCCP_BUCKETS 256
36 static int theWccp2Connection
= -1;
37 static int wccp2_connected
= 0;
39 static PF wccp2HandleUdp
;
40 static EVH wccp2HereIam
;
41 static EVH wccp2AssignBuckets
;
45 #define WCCP2_HASH_ASSIGNMENT 0x00
46 #define WCCP2_MASK_ASSIGNMENT 0x01
48 #define WCCP2_NONE_SECURITY_LEN 0
49 #define WCCP2_MD5_SECURITY_LEN SQUID_MD5_DIGEST_LENGTH // 16
52 #define WCCP2_NUMPORTS 8
53 #define WCCP2_PASSWORD_LEN 8 + 1 /* + 1 for C-string NUL terminator */
55 /* WCCPv2 Pakcet format structures */
56 /* Defined in draft-wilson-wccp-v2-12-oct-2001.txt */
58 /** \interface WCCPv2_Protocol
59 * Generic header struct
61 struct wccp2_item_header_t
{
66 /* item type values */
67 #define WCCP2_SECURITY_INFO 0
68 #define WCCP2_SERVICE_INFO 1
69 #define WCCP2_ROUTER_ID_INFO 2
70 #define WCCP2_WC_ID_INFO 3
71 #define WCCP2_RTR_VIEW_INFO 4
72 #define WCCP2_WC_VIEW_INFO 5
73 #define WCCP2_REDIRECT_ASSIGNMENT 6
74 #define WCCP2_QUERY_INFO 7
75 #define WCCP2_CAPABILITY_INFO 8
76 #define WCCP2_ALT_ASSIGNMENT 13
77 #define WCCP2_ASSIGN_MAP 14
78 #define WCCP2_COMMAND_EXTENSION 15
80 /** \interface WCCPv2_Protocol
81 * Sect 5.5 WCCP Message Header
83 struct wccp2_message_header_t
{
86 #define WCCP2_VERSION 0x200
90 static struct wccp2_message_header_t wccp2_here_i_am_header
;
93 #define WCCP2_HERE_I_AM 10
94 #define WCCP2_I_SEE_YOU 11
95 #define WCCP2_REDIRECT_ASSIGN 12
96 #define WCCP2_REMOVAL_QUERY 13
98 /** \interface WCCPv2_Protocol
99 * Sect 5.6.1 Security Info Component
101 * Basic security Header. Matches "no security" case exactly.
103 struct wccp2_security_none_t
{
104 uint16_t security_type
;
105 uint16_t security_length
;
106 uint32_t security_option
;
109 /* security options */
110 #define WCCP2_NO_SECURITY 0
111 #define WCCP2_MD5_SECURITY 1
113 /** \interface WCCPv2_Protocol
114 * Sect 5.6.1 Security Info Component
116 * Extended security section. Matches "MD5 security" type exactly.
117 * Including the security header.
119 struct wccp2_security_md5_t
{
120 uint16_t security_type
;
121 uint16_t security_length
;
122 uint32_t security_option
;
123 uint8_t security_implementation
[WCCP2_MD5_SECURITY_LEN
];
126 /* Service info struct */
128 /** \interface WCCPv2_Protocol
129 * Sect 5.6.2 Service Info Component
131 struct wccp2_service_info_t
{
132 uint16_t service_type
;
133 uint16_t service_length
;
136 uint8_t service_priority
;
137 uint8_t service_protocol
;
138 uint32_t service_flags
;
149 #define WCCP2_SERVICE_STANDARD 0
150 #define WCCP2_SERVICE_DYNAMIC 1
153 #define WCCP2_SERVICE_ID_HTTP 0x00
156 #define WCCP2_SERVICE_SRC_IP_HASH 0x1
157 #define WCCP2_SERVICE_DST_IP_HASH 0x2
158 #define WCCP2_SERVICE_SRC_PORT_HASH 0x4
159 #define WCCP2_SERVICE_DST_PORT_HASH 0x8
160 #define WCCP2_SERVICE_PORTS_DEFINED 0x10
161 #define WCCP2_SERVICE_PORTS_SOURCE 0x20
162 #define WCCP2_SERVICE_SRC_IP_ALT_HASH 0x100
163 #define WCCP2_SERVICE_DST_IP_ALT_HASH 0x200
164 #define WCCP2_SERVICE_SRC_PORT_ALT_HASH 0x400
165 #define WCCP2_SERVICE_DST_PORT_ALT_HASH 0x800
167 /* TODO the following structures need to be re-defined for correct full operation.
168 wccp2_cache_identity_element needs to be merged as a sub-struct of
169 wccp2_identity_info_t (identity_type); which frees up the identifty info
170 structures so mask_assigment_data_element can become variable length
171 and cope with multiple fail-over caches hanging off one router.
174 /** \interface WCCPv2_Protocol
175 * Sect 5.7.2 Web-Cache Identity Element
177 struct wccp2_cache_identity_info_t
{
179 uint16_t hash_revision
;
181 //#define WCCP2_HASH_ASSIGNMENT_DATA 0x0
183 /* 5.7.2 Hash Assignment Data Element */
184 char buckets
[32]; /* Draft indicates 8x 32-bit buckets but it's just a mask so doesn't matter how we define. */
189 /** \interface WCCPv2_Protocol
190 * Sect 5.6.4 Web-Cache Identity Info Component
192 struct wccp2_identity_info_t
{
193 uint16_t cache_identity_type
;
194 uint16_t cache_identity_length
;
196 struct wccp2_cache_identity_info_t cache_identity
;
199 static struct wccp2_identity_info_t wccp2_identity_info
;
201 /** \interface WCCPv2_Protocol
202 * Sect 5.7.7 Mask Element
204 struct wccp2_mask_element_t
{
205 uint32_t source_ip_mask
;
206 uint32_t dest_ip_mask
;
207 uint16_t source_port_mask
;
208 uint16_t dest_port_mask
;
209 uint32_t number_values
;
212 /** \interface WCCPv2_Protocol
213 * Sect 5.7.2 Web-Cache Identity Element
215 struct wccp2_cache_mask_identity_info_t
{
217 uint16_t hash_revision
;
219 #define WCCP2_MASK_ASSIGNMENT_DATA (0x2)
221 /* Sect 5.7.2 Mask Assignment Data Element
223 * NP: draft specifies a variable-length set of keys here.
224 * the following fields only matche the special case Squid sends outbound (single-cache).
226 uint32_t mask_element_count
;
228 /* Sect 5.7.6 Mask/Value Set Element */
229 /* special case: single mask element. no values. */
230 struct wccp2_mask_element_t mask
;
232 /* Sect 5.7.2 Mask Assignment Data Element */
237 /** \interface WCCPv2_Protocol
238 * Sect 5.6.4 Web-Cache Identity Info Component
240 struct wccp2_mask_identity_info_t
{
241 uint16_t cache_identity_type
;
242 uint16_t cache_identity_length
;
244 struct wccp2_cache_mask_identity_info_t cache_identity
;
247 static struct wccp2_mask_identity_info_t wccp2_mask_identity_info
;
249 /** \interface WCCPv2_Protocol
250 * Sect 5.6.5 Router View Info Component
251 * Sect 5.6.6 Web Cache View Info Component
253 * first three fields. (shared by both view components)
255 struct wccp2_cache_view_header_t
{
256 uint16_t cache_view_type
;
257 uint16_t cache_view_length
;
258 uint32_t cache_view_version
;
261 static struct wccp2_cache_view_header_t wccp2_cache_view_header
;
263 /// \interface WCCPv2_Protocol
264 /* NP: special-case 5.6.5 or 5.6.6 * View Info when no routers or caches are advertised? */
265 struct wccp2_cache_view_info_t
{
266 uint32_t num_routers
;
270 static struct wccp2_cache_view_info_t wccp2_cache_view_info
;
272 /** \interface WCCPv2_Protocol
273 * Sect 5.7.1 Router ID Element
275 struct wccp2_router_id_element_t
{
276 struct in_addr router_address
;
277 uint32_t received_id
;
281 // static struct wccp2_router_id_element_t wccp2_router_id_element;
283 /** \interface WCCPv2_Protocol
284 * Sect 5.6.9 Capabilities Info Component
286 struct wccp2_capability_info_header_t
{
287 uint16_t capability_info_type
;
288 uint16_t capability_info_length
;
289 /* dynamic length capabilities list */
292 static struct wccp2_capability_info_header_t wccp2_capability_info_header
;
294 /** \interface WCCPv2_Protocol
295 * 5.7.5 Capability Element
297 struct wccp2_capability_element_t
{
298 uint16_t capability_type
;
299 uint16_t capability_length
;
300 uint32_t capability_value
;
302 static struct wccp2_capability_element_t wccp2_capability_element
;
304 /* capability types */
305 #define WCCP2_CAPABILITY_FORWARDING_METHOD 0x01
306 #define WCCP2_CAPABILITY_ASSIGNMENT_METHOD 0x02
307 #define WCCP2_CAPABILITY_RETURN_METHOD 0x03
308 // 0x04 ?? - advertised by a 4507 (ios v15.1) Cisco switch
309 // 0x05 ?? - advertised by a 4507 (ios v15.1) Cisco switch
311 /* capability values */
312 #define WCCP2_METHOD_GRE 0x00000001
313 #define WCCP2_METHOD_L2 0x00000002
314 /* when type=WCCP2_CAPABILITY_FORWARDING_METHOD */
315 #define WCCP2_FORWARDING_METHOD_GRE WCCP2_METHOD_GRE
316 #define WCCP2_FORWARDING_METHOD_L2 WCCP2_METHOD_L2
317 /* when type=WCCP2_CAPABILITY_ASSIGNMENT_METHOD */
318 #define WCCP2_ASSIGNMENT_METHOD_HASH 0x00000001
319 #define WCCP2_ASSIGNMENT_METHOD_MASK 0x00000002
320 /* when type=WCCP2_CAPABILITY_RETURN_METHOD */
321 #define WCCP2_PACKET_RETURN_METHOD_GRE WCCP2_METHOD_GRE
322 #define WCCP2_PACKET_RETURN_METHOD_L2 WCCP2_METHOD_L2
324 /** \interface WCCPv2_Protocol
325 * 5.7.8 Value Element
327 struct wccp2_value_element_t
{
328 uint32_t source_ip_value
;
329 uint32_t dest_ip_value
;
330 uint16_t source_port_value
;
331 uint16_t dest_port_value
;
333 struct in_addr cache_ip
;
336 /* RECEIVED PACKET STRUCTURE */
338 /** \interface WCCPv2_Protocol
339 * 5.2 'I See You' Message
341 struct wccp2_i_see_you_t
{
345 char data
[WCCP_RESPONSE_SIZE
];
348 static struct wccp2_i_see_you_t wccp2_i_see_you
;
350 /** \interface WCCPv2_Protocol
351 * 5.7.4 Router Assignment Element
353 struct wccp2_router_assign_element_t
{
354 struct in_addr router_address
;
355 uint32_t received_id
;
356 uint32_t change_number
;
359 /* Router identity struct */
361 /** \interface WCCPv2_Protocol
362 * 5.6.3 Router Identity Info Component (partial)
364 struct router_identity_info_t
{
366 struct wccp2_item_header_t header
;
368 struct wccp2_router_id_element_t router_id_element
;
370 struct in_addr router_address
;
371 uint32_t number_caches
;
372 /* dynamic list of cache IP addresses */
375 /* The received packet for a mask assignment is unusual */
377 /** \interface WCCPv2_Protocol
378 * Sect 5.7.7 Mask Element ???
379 * see code below. apparently the supposed IP address at position num1 can be equal to 3.
381 struct cache_mask_info_t
{
388 /** \interface WCCPv2_Protocol
389 * 5.7.3 Assignment Key Element
391 struct assignment_key_t
{
392 struct in_addr master_ip
;
393 uint32_t master_number
;
396 /** \interface WCCPv2_Protocol
397 * 5.6.5 Router View Info Component (first three fields)
399 struct router_view_t
{
400 struct wccp2_item_header_t header
;
401 uint32_t change_number
;
402 struct assignment_key_t assignment_key
;
403 /* dynamic lists of routers and caches elided */
406 /* Lists used to keep track of caches, routers and services */
408 /// \interface WCCPv2_Protocol
409 struct wccp2_cache_list_t
{
411 struct in_addr cache_ip
;
415 struct wccp2_cache_list_t
*next
;
418 /// \interface WCCPv2_Protocol
419 struct wccp2_router_list_t
{
421 struct wccp2_router_id_element_t
*info
;
423 struct in_addr local_ip
;
425 struct in_addr router_sendto_address
;
426 uint32_t member_change
;
429 struct wccp2_cache_list_t cache_list_head
;
431 struct wccp2_router_list_t
*next
;
434 static int wccp2_numrouters
;
436 /// \interface WCCPv2_Protocol
437 struct wccp2_service_list_t
{
439 struct wccp2_service_info_t info
;
440 uint32_t num_routers
;
442 struct wccp2_router_list_t router_list_head
;
446 char *wccp2_identity_info_ptr
;
448 struct wccp2_security_md5_t
*security_info
;
450 struct wccp2_service_info_t
*service_info
;
451 char wccp_packet
[WCCP_RESPONSE_SIZE
];
452 size_t wccp_packet_size
;
454 struct wccp2_service_list_t
*next
;
455 char wccp_password
[WCCP2_PASSWORD_LEN
]; /* hold the trailing C-string NUL */
456 uint32_t wccp2_security_type
;
459 static struct wccp2_service_list_t
*wccp2_service_list_head
= nullptr;
461 int empty_portlist
[WCCP2_NUMPORTS
] = {0, 0, 0, 0, 0, 0, 0, 0};
463 /* END WCCP V2 PROTOCOL TYPES DEFINITION */
465 static void wccp2_add_service_list(int service
, int service_id
, int service_priority
,
466 int service_proto
, int service_flags
, int ports
[], int security_type
, char *password
);
467 static void wccp2SortCacheList(struct wccp2_cache_list_t
*head
);
470 * The functions used during startup:
472 * wccp2ConnectionOpen
473 * wccp2ConnectionClose
477 wccp2InitServices(void)
479 debugs(80, 5, "wccp2InitServices: called");
483 wccp2_update_service(struct wccp2_service_list_t
*srv
, int service
,
484 int service_id
, int service_priority
, int service_proto
, int service_flags
,
487 /* XXX check what needs to be wrapped in htons()! */
488 srv
->info
.service
= service
;
489 srv
->info
.service_id
= service_id
;
490 srv
->info
.service_priority
= service_priority
;
491 srv
->info
.service_protocol
= service_proto
;
492 srv
->info
.service_flags
= htonl(service_flags
);
493 srv
->info
.port0
= htons(ports
[0]);
494 srv
->info
.port1
= htons(ports
[1]);
495 srv
->info
.port2
= htons(ports
[2]);
496 srv
->info
.port3
= htons(ports
[3]);
497 srv
->info
.port4
= htons(ports
[4]);
498 srv
->info
.port5
= htons(ports
[5]);
499 srv
->info
.port6
= htons(ports
[6]);
500 srv
->info
.port7
= htons(ports
[7]);
504 wccp2_add_service_list(int service
, int service_id
, int service_priority
,
505 int service_proto
, int service_flags
, int ports
[], int security_type
,
509 struct wccp2_service_list_t
*wccp2_service_list_ptr
;
511 wccp2_service_list_ptr
= (wccp2_service_list_t
*) xcalloc(1, sizeof(struct wccp2_service_list_t
));
513 debugs(80, 5, "wccp2_add_service_list: added service id " << service_id
);
515 /* XXX check what needs to be wrapped in htons()! */
516 wccp2_service_list_ptr
->info
.service_type
= htons(WCCP2_SERVICE_INFO
);
518 wccp2_service_list_ptr
->info
.service_length
= htons(sizeof(struct wccp2_service_info_t
) - 4);
519 wccp2_service_list_ptr
->change_num
= 0;
520 wccp2_update_service(wccp2_service_list_ptr
, service
, service_id
,
521 service_priority
, service_proto
, service_flags
, ports
);
522 wccp2_service_list_ptr
->wccp2_security_type
= security_type
;
523 memset(wccp2_service_list_ptr
->wccp_password
, 0, WCCP2_PASSWORD_LEN
);
524 xstrncpy(wccp2_service_list_ptr
->wccp_password
, password
, WCCP2_PASSWORD_LEN
);
525 /* add to linked list - XXX this should use the Squid dlink* routines! */
526 wccp2_service_list_ptr
->next
= wccp2_service_list_head
;
527 wccp2_service_list_head
= wccp2_service_list_ptr
;
530 static struct wccp2_service_list_t
*
531 wccp2_get_service_by_id(int service
, int service_id
) {
533 struct wccp2_service_list_t
*p
;
535 p
= wccp2_service_list_head
;
537 while (p
!= nullptr) {
538 if (p
->info
.service
== service
&& p
->info
.service_id
== service_id
) {
549 * Update the md5 security header, if possible
551 * Returns: 1 if we set it, 0 if not (eg, no security section, or non-md5)
554 wccp2_update_md5_security(char *password
, char *ptr
, char *packet
, int len
)
556 uint8_t md5Digest
[SQUID_MD5_DIGEST_LENGTH
];
557 char pwd
[WCCP2_PASSWORD_LEN
];
560 struct wccp2_security_md5_t
*ws
;
562 debugs(80, 5, "wccp2_update_md5_security: called");
564 /* The password field, for the MD5 hash, needs to be 8 bytes and NUL padded. */
565 memset(pwd
, 0, sizeof(pwd
));
566 xstrncpy(pwd
, password
, sizeof(pwd
));
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 static_assert(sizeof(pwd
) - 1 == 8, "WCCP2 password has exactly 8 (padded) octets, excluding storage-terminating NUL");
590 SquidMD5Update(&M
, pwd
, sizeof(pwd
) - 1);
592 SquidMD5Update(&M
, packet
, len
);
594 SquidMD5Final(md5Digest
, &M
);
596 memcpy(ws
->security_implementation
, md5Digest
, sizeof(md5Digest
));
603 * Check the given WCCP2 packet against the given password.
607 wccp2_check_security(struct wccp2_service_list_t
*srv
, char *security
, char *packet
, int len
)
610 struct wccp2_security_md5_t
*ws
= (struct wccp2_security_md5_t
*) security
;
611 uint8_t md5Digest
[SQUID_MD5_DIGEST_LENGTH
], md5_challenge
[SQUID_MD5_DIGEST_LENGTH
];
612 char pwd
[WCCP2_PASSWORD_LEN
];
615 /* Make sure the security type matches what we expect */
617 if (ntohl(ws
->security_option
) != srv
->wccp2_security_type
) {
618 debugs(80, DBG_IMPORTANT
, "wccp2_check_security: received packet has the wrong security option");
622 if (srv
->wccp2_security_type
== WCCP2_NO_SECURITY
) {
626 if (srv
->wccp2_security_type
!= WCCP2_MD5_SECURITY
) {
627 debugs(80, DBG_IMPORTANT
, "ERROR: wccp2_check_security: invalid security option");
631 /* If execution makes it here then we have an MD5 security */
633 /* The password field, for the MD5 hash, needs to be 8 bytes and NUL padded. */
634 memset(pwd
, 0, sizeof(pwd
));
635 xstrncpy(pwd
, srv
->wccp_password
, sizeof(pwd
));
637 /* Take a copy of the challenge: we need to NUL it before comparing */
638 memcpy(md5_challenge
, ws
->security_implementation
, sizeof(md5_challenge
));
640 memset(ws
->security_implementation
, 0, sizeof(ws
->security_implementation
));
644 static_assert(sizeof(pwd
) - 1 == 8, "WCCP2 password has exactly 8 (padded) octets, excluding storage-terminating NUL");
646 SquidMD5Update(&M
, pwd
, sizeof(pwd
) - 1);
648 SquidMD5Update(&M
, packet
, len
);
650 SquidMD5Final(md5Digest
, &M
);
652 return (memcmp(md5Digest
, md5_challenge
, SQUID_MD5_DIGEST_LENGTH
) == 0);
660 uint32_t service_flags
;
662 struct wccp2_service_list_t
*service_list_ptr
;
664 struct wccp2_router_list_t
*router_list_ptr
;
666 debugs(80, 5, "wccp2Init: Called");
668 if (wccp2_connected
== 1)
671 wccp2_numrouters
= 0;
673 /* Calculate the number of routers configured in the config file */
674 for (s
= Config
.Wccp2
.router
; s
; s
= s
->next
) {
675 if (!s
->s
.isAnyAddr()) {
676 /* Increment the counter */
681 if (wccp2_numrouters
== 0) {
685 struct wccp2_security_md5_t wccp2_security_md5
;
686 memset(&wccp2_security_md5
, 0, sizeof(wccp2_security_md5
));
688 /* Initialise the list of services */
691 service_list_ptr
= wccp2_service_list_head
;
693 while (service_list_ptr
!= nullptr) {
694 /* Set up our list pointers */
695 router_list_ptr
= &service_list_ptr
->router_list_head
;
697 /* start the wccp header */
698 wccp2_here_i_am_header
.type
= htonl(WCCP2_HERE_I_AM
);
699 wccp2_here_i_am_header
.version
= htons(WCCP2_VERSION
);
700 wccp2_here_i_am_header
.length
= 0;
701 ptr
= service_list_ptr
->wccp_packet
+ sizeof(wccp2_here_i_am_header
);
703 /* add the security section */
704 /* XXX this is ugly */
706 if (service_list_ptr
->wccp2_security_type
== WCCP2_MD5_SECURITY
) {
707 wccp2_security_md5
.security_option
= htonl(WCCP2_MD5_SECURITY
);
709 wccp2_security_md5
.security_length
= htons(sizeof(struct wccp2_security_md5_t
) - 4);
710 } else if (service_list_ptr
->wccp2_security_type
== WCCP2_NO_SECURITY
) {
711 wccp2_security_md5
.security_option
= htonl(WCCP2_NO_SECURITY
);
712 /* XXX I hate magic length numbers! */
713 wccp2_security_md5
.security_length
= htons(4);
715 fatalf("Bad WCCP2 security type\n");
718 wccp2_here_i_am_header
.length
+= ntohs(wccp2_security_md5
.security_length
) + 4;
719 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
720 wccp2_security_md5
.security_type
= htons(WCCP2_SECURITY_INFO
);
722 service_list_ptr
->security_info
= (struct wccp2_security_md5_t
*) ptr
;
724 if (service_list_ptr
->wccp2_security_type
== WCCP2_MD5_SECURITY
) {
725 memcpy(ptr
, &wccp2_security_md5
, sizeof(struct wccp2_security_md5_t
));
726 ptr
+= sizeof(struct wccp2_security_md5_t
);
728 /* assume NONE, and XXX I hate magic length numbers */
729 memcpy(ptr
, &wccp2_security_md5
, 8);
733 /* Add the service info section */
735 wccp2_here_i_am_header
.length
+= sizeof(struct wccp2_service_info_t
);
737 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
739 memcpy(ptr
, &service_list_ptr
->info
, sizeof(struct wccp2_service_info_t
));
741 service_list_ptr
->service_info
= (struct wccp2_service_info_t
*) ptr
;
743 ptr
+= sizeof(struct wccp2_service_info_t
);
745 /* Add the cache identity section */
747 switch (Config
.Wccp2
.assignment_method
) {
749 case WCCP2_ASSIGNMENT_METHOD_HASH
:
751 wccp2_here_i_am_header
.length
+= sizeof(struct wccp2_identity_info_t
);
752 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
753 wccp2_identity_info
.cache_identity_type
= htons(WCCP2_WC_ID_INFO
);
754 wccp2_identity_info
.cache_identity_length
= htons(sizeof(wccp2_identity_info
.cache_identity
));
755 memset(&wccp2_identity_info
.cache_identity
.addr
, '\0', sizeof(struct in_addr
));
756 memset(&wccp2_identity_info
.cache_identity
.hash_revision
, '\0', sizeof(wccp2_identity_info
.cache_identity
.hash_revision
));
757 memset(&wccp2_identity_info
.cache_identity
.bits
, '\0', sizeof(wccp2_identity_info
.cache_identity
.bits
));
758 memset(&wccp2_identity_info
.cache_identity
.buckets
, '\0', sizeof(wccp2_identity_info
.cache_identity
.buckets
));
759 wccp2_identity_info
.cache_identity
.weight
= htons(Config
.Wccp2
.weight
);
760 memset(&wccp2_identity_info
.cache_identity
.status
, '\0', sizeof(wccp2_identity_info
.cache_identity
.status
));
762 memcpy(ptr
, &wccp2_identity_info
, sizeof(struct wccp2_identity_info_t
));
763 service_list_ptr
->wccp2_identity_info_ptr
= ptr
;
765 ptr
+= sizeof(struct wccp2_identity_info_t
);
768 case WCCP2_ASSIGNMENT_METHOD_MASK
:
770 wccp2_here_i_am_header
.length
+= sizeof(struct wccp2_mask_identity_info_t
);
771 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
772 wccp2_mask_identity_info
.cache_identity_type
= htons(WCCP2_WC_ID_INFO
);
773 wccp2_mask_identity_info
.cache_identity_length
= htons(sizeof(wccp2_mask_identity_info
.cache_identity
));
774 memset(&wccp2_mask_identity_info
.cache_identity
.addr
, '\0', sizeof(struct in_addr
));
775 wccp2_mask_identity_info
.cache_identity
.bits
= htons(WCCP2_MASK_ASSIGNMENT_DATA
);
776 wccp2_mask_identity_info
.cache_identity
.mask_element_count
= htonl(1);
777 service_flags
= ntohl(service_list_ptr
->service_info
->service_flags
);
779 memset(&wccp2_mask_identity_info
.cache_identity
.mask
, 0, sizeof(struct wccp2_mask_element_t
));
781 if ((service_flags
& WCCP2_SERVICE_SRC_IP_HASH
) || (service_flags
& WCCP2_SERVICE_SRC_IP_ALT_HASH
)) {
782 wccp2_mask_identity_info
.cache_identity
.mask
.source_ip_mask
= htonl(0x00001741);
783 } 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
)) {
784 wccp2_mask_identity_info
.cache_identity
.mask
.dest_ip_mask
= htonl(0x00001741);
785 } else if ((service_flags
& WCCP2_SERVICE_SRC_PORT_HASH
) || (service_flags
& WCCP2_SERVICE_SRC_PORT_ALT_HASH
)) {
786 wccp2_mask_identity_info
.cache_identity
.mask
.source_port_mask
= htons(0x1741);
787 } else if ((service_flags
& WCCP2_SERVICE_DST_PORT_HASH
) || (service_flags
& WCCP2_SERVICE_DST_PORT_ALT_HASH
)) {
788 wccp2_mask_identity_info
.cache_identity
.mask
.dest_port_mask
= htons(0x1741);
790 fatalf("Unknown service hash method\n");
793 wccp2_mask_identity_info
.cache_identity
.weight
= 0;
794 wccp2_mask_identity_info
.cache_identity
.status
= 0;
796 memcpy(ptr
, &wccp2_mask_identity_info
, sizeof(struct wccp2_mask_identity_info_t
));
797 service_list_ptr
->wccp2_identity_info_ptr
= ptr
;
799 ptr
+= sizeof(struct wccp2_mask_identity_info_t
);
803 fatalf("Unknown Wccp2 assignment method\n");
806 /* Add the cache view section */
807 wccp2_here_i_am_header
.length
+= sizeof(wccp2_cache_view_header
);
809 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
811 wccp2_cache_view_header
.cache_view_type
= htons(WCCP2_WC_VIEW_INFO
);
813 wccp2_cache_view_header
.cache_view_length
= htons(sizeof(wccp2_cache_view_header
) - 4 +
814 sizeof(wccp2_cache_view_info
) + (wccp2_numrouters
* sizeof(wccp2_router_id_element_t
)));
816 wccp2_cache_view_header
.cache_view_version
= htonl(1);
818 memcpy(ptr
, &wccp2_cache_view_header
, sizeof(wccp2_cache_view_header
));
820 ptr
+= sizeof(wccp2_cache_view_header
);
822 /* Add the number of routers to the packet */
823 wccp2_here_i_am_header
.length
+= sizeof(service_list_ptr
->num_routers
);
825 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
827 service_list_ptr
->num_routers
= htonl(wccp2_numrouters
);
829 memcpy(ptr
, &service_list_ptr
->num_routers
, sizeof(service_list_ptr
->num_routers
));
831 ptr
+= sizeof(service_list_ptr
->num_routers
);
833 /* Add each router. Keep this functionality here to make sure the received_id can be updated in the packet */
834 for (s
= Config
.Wccp2
.router
; s
; s
= s
->next
) {
835 if (!s
->s
.isAnyAddr()) {
837 wccp2_here_i_am_header
.length
+= sizeof(struct wccp2_router_id_element_t
);
838 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
840 /* Add a pointer to the router list for this router */
842 router_list_ptr
->info
= (struct wccp2_router_id_element_t
*) ptr
;
843 s
->s
.getInAddr(router_list_ptr
->info
->router_address
);
844 router_list_ptr
->info
->received_id
= htonl(0);
845 s
->s
.getInAddr(router_list_ptr
->router_sendto_address
);
846 router_list_ptr
->member_change
= htonl(0);
848 /* Build the next struct */
850 router_list_ptr
->next
= (wccp2_router_list_t
*) xcalloc(1, sizeof(struct wccp2_router_list_t
));
852 /* update the pointer */
853 router_list_ptr
= router_list_ptr
->next
;
854 router_list_ptr
->next
= nullptr;
856 /* no need to copy memory - we've just set the values directly in the packet above */
858 ptr
+= sizeof(struct wccp2_router_id_element_t
);
862 /* Add the number of caches (0) */
863 wccp2_here_i_am_header
.length
+= sizeof(wccp2_cache_view_info
.num_caches
);
865 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
867 wccp2_cache_view_info
.num_caches
= htonl(0);
869 memcpy(ptr
, &wccp2_cache_view_info
.num_caches
, sizeof(wccp2_cache_view_info
.num_caches
));
871 ptr
+= sizeof(wccp2_cache_view_info
.num_caches
);
873 /* Add the extra capability header */
874 wccp2_here_i_am_header
.length
+= sizeof(wccp2_capability_info_header
);
876 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
878 wccp2_capability_info_header
.capability_info_type
= htons(WCCP2_CAPABILITY_INFO
);
880 wccp2_capability_info_header
.capability_info_length
= htons(3 * sizeof(wccp2_capability_element
));
882 memcpy(ptr
, &wccp2_capability_info_header
, sizeof(wccp2_capability_info_header
));
884 ptr
+= sizeof(wccp2_capability_info_header
);
886 /* Add the forwarding method */
887 wccp2_here_i_am_header
.length
+= sizeof(wccp2_capability_element
);
889 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
891 wccp2_capability_element
.capability_type
= htons(WCCP2_CAPABILITY_FORWARDING_METHOD
);
893 wccp2_capability_element
.capability_length
= htons(sizeof(wccp2_capability_element
.capability_value
));
895 wccp2_capability_element
.capability_value
= htonl(Config
.Wccp2
.forwarding_method
);
897 memcpy(ptr
, &wccp2_capability_element
, sizeof(wccp2_capability_element
));
899 ptr
+= sizeof(wccp2_capability_element
);
901 /* Add the assignment method */
902 wccp2_here_i_am_header
.length
+= sizeof(wccp2_capability_element
);
904 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
906 wccp2_capability_element
.capability_type
= htons(WCCP2_CAPABILITY_ASSIGNMENT_METHOD
);
908 wccp2_capability_element
.capability_length
= htons(sizeof(wccp2_capability_element
.capability_value
));
910 wccp2_capability_element
.capability_value
= htonl(Config
.Wccp2
.assignment_method
);
912 memcpy(ptr
, &wccp2_capability_element
, sizeof(wccp2_capability_element
));
914 ptr
+= sizeof(wccp2_capability_element
);
916 /* Add the return method */
917 wccp2_here_i_am_header
.length
+= sizeof(wccp2_capability_element
);
919 assert(wccp2_here_i_am_header
.length
<= WCCP_RESPONSE_SIZE
);
921 wccp2_capability_element
.capability_type
= htons(WCCP2_CAPABILITY_RETURN_METHOD
);
923 wccp2_capability_element
.capability_length
= htons(sizeof(wccp2_capability_element
.capability_value
));
925 wccp2_capability_element
.capability_value
= htonl(Config
.Wccp2
.return_method
);
927 memcpy(ptr
, &wccp2_capability_element
, sizeof(wccp2_capability_element
));
929 ptr
+= sizeof(wccp2_capability_element
);
931 /* Finally, fix the total length to network order, and copy to the appropriate memory blob */
932 wccp2_here_i_am_header
.length
= htons(wccp2_here_i_am_header
.length
);
934 memcpy(&service_list_ptr
->wccp_packet
, &wccp2_here_i_am_header
, sizeof(wccp2_here_i_am_header
));
936 service_list_ptr
->wccp_packet_size
= ntohs(wccp2_here_i_am_header
.length
) + sizeof(wccp2_here_i_am_header
);
938 /* Add the event if everything initialised correctly */
939 debugs(80,3,"wccp2Init: scheduled 'HERE_I_AM' message to " << wccp2_numrouters
<< "routers.");
940 if (wccp2_numrouters
) {
941 if (!eventFind(wccp2HereIam
, nullptr)) {
942 eventAdd("wccp2HereIam", wccp2HereIam
, nullptr, 1, 1);
944 debugs(80,3,"wccp2Init: skip duplicate 'HERE_I_AM'.");
947 service_list_ptr
= service_list_ptr
->next
;
952 wccp2ConnectionOpen(void)
954 struct sockaddr_in router
, local
, null
;
955 socklen_t local_len
, router_len
;
957 struct wccp2_service_list_t
*service_list_ptr
;
959 struct wccp2_router_list_t
*router_list_ptr
;
961 debugs(80, 5, "wccp2ConnectionOpen: Called");
963 if (wccp2_numrouters
== 0 || !wccp2_service_list_head
) {
964 debugs(80, 2, "WCCPv2 Disabled. No IPv4 Router(s) configured.");
968 if ( !Config
.Wccp2
.address
.setIPv4() ) {
969 debugs(80, DBG_CRITICAL
, "WCCPv2 Disabled. Local address " << Config
.Wccp2
.address
<< " is not an IPv4 address.");
973 Config
.Wccp2
.address
.port(WCCP_PORT
);
974 theWccp2Connection
= comm_open_listener(SOCK_DGRAM
,
976 Config
.Wccp2
.address
,
980 if (theWccp2Connection
< 0)
981 fatal("Cannot open WCCP Port");
983 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
985 int i
= IP_PMTUDISC_DONT
;
986 if (setsockopt(theWccp2Connection
, SOL_IP
, IP_MTU_DISCOVER
, &i
, sizeof i
) < 0) {
988 debugs(80, 2, "WARNING: Path MTU discovery could not be disabled on FD " << theWccp2Connection
<< ": " << xstrerr(xerrno
));
993 Comm::SetSelect(theWccp2Connection
, COMM_SELECT_READ
, wccp2HandleUdp
, nullptr, 0);
995 debugs(80, DBG_IMPORTANT
, "Accepting WCCPv2 messages on port " << WCCP_PORT
<< ", FD " << theWccp2Connection
<< ".");
996 debugs(80, DBG_IMPORTANT
, "Initialising all WCCPv2 lists");
998 /* Initialise all routers on all services */
999 memset(&null
, 0, sizeof(null
));
1001 null
.sin_family
= AF_UNSPEC
;
1003 service_list_ptr
= wccp2_service_list_head
;
1005 while (service_list_ptr
!= nullptr) {
1006 for (router_list_ptr
= &service_list_ptr
->router_list_head
; router_list_ptr
->next
!= nullptr; router_list_ptr
= router_list_ptr
->next
) {
1007 router_len
= sizeof(router
);
1008 memset(&router
, '\0', router_len
);
1009 router
.sin_family
= AF_INET
;
1010 router
.sin_port
= htons(WCCP_PORT
);
1011 router
.sin_addr
= router_list_ptr
->router_sendto_address
;
1013 if (connect(theWccp2Connection
, (struct sockaddr
*) &router
, router_len
))
1014 fatal("Unable to connect WCCP out socket");
1016 local_len
= sizeof(local
);
1018 memset(&local
, '\0', local_len
);
1020 if (getsockname(theWccp2Connection
, (struct sockaddr
*) &local
, &local_len
))
1021 fatal("Unable to getsockname on WCCP out socket");
1023 router_list_ptr
->local_ip
= local
.sin_addr
;
1025 /* Disconnect the sending socket. Note: FreeBSD returns error
1026 * but disconnects anyway so we have to just assume it worked
1028 if (wccp2_numrouters
> 1) {
1029 (void)connect(theWccp2Connection
, (struct sockaddr
*) &null
, router_len
);
1033 service_list_ptr
= service_list_ptr
->next
;
1036 wccp2_connected
= 1;
1040 wccp2ConnectionClose(void)
1043 struct wccp2_service_list_t
*service_list_ptr
;
1045 struct wccp2_service_list_t
*service_list_ptr_next
;
1047 struct wccp2_router_list_t
*router_list_ptr
;
1049 struct wccp2_router_list_t
*router_list_next
;
1051 struct wccp2_cache_list_t
*cache_list_ptr
;
1053 struct wccp2_cache_list_t
*cache_list_ptr_next
;
1055 if (wccp2_connected
== 0) {
1059 /* TODO A shutting-down cache should generate a removal query, informing the router
1060 * (and therefore the caches in the group) that this cache is going
1061 * away and no new traffic should be forwarded to it.
1064 if (theWccp2Connection
> -1) {
1065 debugs(80, DBG_IMPORTANT
, "FD " << theWccp2Connection
<< " Closing WCCPv2 socket");
1066 comm_close(theWccp2Connection
);
1067 theWccp2Connection
= -1;
1070 /* free all stored router state */
1071 service_list_ptr
= wccp2_service_list_head
;
1073 while (service_list_ptr
!= nullptr) {
1074 for (router_list_ptr
= &service_list_ptr
->router_list_head
; router_list_ptr
!= nullptr; router_list_ptr
= router_list_next
) {
1075 for (cache_list_ptr
= &router_list_ptr
->cache_list_head
; cache_list_ptr
; cache_list_ptr
= cache_list_ptr_next
) {
1076 cache_list_ptr_next
= cache_list_ptr
->next
;
1078 if (cache_list_ptr
!= &router_list_ptr
->cache_list_head
) {
1079 xfree(cache_list_ptr
);
1082 memset(cache_list_ptr
, '\0', sizeof(struct wccp2_cache_list_t
));
1086 router_list_next
= router_list_ptr
->next
;
1088 if (router_list_ptr
!= &service_list_ptr
->router_list_head
) {
1089 xfree(router_list_ptr
);
1092 memset(router_list_ptr
, '\0', sizeof(struct wccp2_router_list_t
));
1096 service_list_ptr_next
= service_list_ptr
->next
;
1097 xfree(service_list_ptr
);
1098 service_list_ptr
= service_list_ptr_next
;
1101 wccp2_service_list_head
= nullptr;
1102 eventDelete(wccp2HereIam
, nullptr);
1103 eventDelete(wccp2AssignBuckets
, nullptr);
1104 eventDelete(wccp2HereIam
, nullptr);
1105 wccp2_connected
= 0;
1109 * Functions for handling the requests.
1112 /// Checks that the given area section ends inside the given (whole) area.
1113 /// \param error the message to throw when the section does not fit
1115 CheckSectionLength(const void *sectionStart
, const size_t sectionLength
, const void *wholeStart
, const size_t wholeSize
, const char *error
)
1117 assert(sectionStart
);
1120 const auto wholeEnd
= static_cast<const char*>(wholeStart
) + wholeSize
;
1121 assert(sectionStart
>= wholeStart
&& "we never go backwards");
1122 assert(sectionStart
<= wholeEnd
&& "we never go beyond our whole (but zero-sized fields are OK)");
1123 static_assert(sizeof(wccp2_i_see_you_t
) <= PTRDIFF_MAX
, "paranoid: no UB when subtracting in-whole pointers");
1124 // subtraction safe due to the three assertions above
1125 const auto remainderDiff
= wholeEnd
- static_cast<const char*>(sectionStart
);
1127 // casting safe due to the assertions above (and size_t definition)
1128 assert(remainderDiff
>= 0);
1129 const auto remainderSize
= static_cast<size_t>(remainderDiff
);
1131 if (sectionLength
<= remainderSize
)
1134 throw TextException(error
, Here());
1137 /// Checks that the area contains at least dataLength bytes after the header.
1138 /// The size of the field header itself is not included in dataLength.
1139 /// \returns the total field size -- the field header and field data combined
1140 template<class FieldHeader
>
1142 CheckFieldDataLength(const FieldHeader
*header
, const size_t dataLength
, const void *areaStart
, const size_t areaSize
, const char *error
)
1145 const auto dataStart
= reinterpret_cast<const char*>(header
) + sizeof(header
);
1146 CheckSectionLength(dataStart
, dataLength
, areaStart
, areaSize
, error
);
1147 return sizeof(header
) + dataLength
; // no overflow after CheckSectionLength()
1150 /// Positions the given field at a given start within a given packet area.
1151 /// The Field type determines the correct field size (used for bounds checking).
1152 /// \param field the field pointer the function should set
1153 /// \param areaStart the start of a packet (sub)structure containing the field
1154 /// \param areaSize the size of the packet (sub)structure starting at areaStart
1155 /// \param fieldStart the start of a field within the given area
1156 /// \param error the message to throw when the field does not fit the area
1157 template<class Field
>
1159 SetField(Field
*&field
, const void *fieldStart
, const void *areaStart
, const size_t areaSize
, const char *error
)
1161 CheckSectionLength(fieldStart
, sizeof(Field
), areaStart
, areaSize
, error
);
1162 field
= static_cast<Field
*>(const_cast<void*>(fieldStart
));
1166 * Accept the UDP packet
1169 wccp2HandleUdp(int sock
, void *)
1171 struct wccp2_service_list_t
*service_list_ptr
;
1173 struct wccp2_router_list_t
*router_list_ptr
;
1175 struct wccp2_cache_list_t
*cache_list_ptr
;
1177 struct wccp2_cache_list_t
*cache_list_ptr_next
;
1179 /* These structs form the parts of the packet */
1181 struct wccp2_security_none_t
*security_info
= nullptr;
1183 struct wccp2_service_info_t
*service_info
= nullptr;
1185 struct router_identity_info_t
*router_identity_info
= nullptr;
1187 struct router_view_t
*router_view_header
= nullptr;
1189 struct wccp2_cache_mask_identity_info_t
*cache_mask_identity
= nullptr;
1191 struct cache_mask_info_t
*cache_mask_info
= nullptr;
1193 struct wccp2_cache_identity_info_t
*cache_identity
= nullptr;
1195 struct wccp2_capability_info_header_t
*router_capability_header
= nullptr;
1196 char *router_capability_data_start
= nullptr;
1198 struct wccp2_capability_element_t
*router_capability_element
;
1200 struct sockaddr_in from
;
1202 struct in_addr cache_address
;
1207 debugs(80, 6, "wccp2HandleUdp: Called.");
1209 Comm::SetSelect(sock
, COMM_SELECT_READ
, wccp2HandleUdp
, nullptr, 0);
1211 // TODO: drop conversion boundary
1212 Ip::Address from_tmp
;
1215 const auto lenOrError
= comm_udp_recvfrom(sock
, &wccp2_i_see_you
, WCCP_RESPONSE_SIZE
, 0, from_tmp
);
1219 const auto len
= static_cast<size_t>(lenOrError
);
1222 // TODO: Remove wccp2_i_see_you.data and use a buffer to read messages.
1223 const auto message_header_size
= sizeof(wccp2_i_see_you
) - sizeof(wccp2_i_see_you
.data
);
1224 Must3(len
>= message_header_size
, "incomplete WCCP message header", Here());
1225 Must3(ntohs(wccp2_i_see_you
.version
) == WCCP2_VERSION
, "WCCP version unsupported", Here());
1226 Must3(ntohl(wccp2_i_see_you
.type
) == WCCP2_I_SEE_YOU
, "WCCP packet type unsupported", Here());
1228 // XXX: drop conversion boundary
1229 from_tmp
.getSockAddr(from
);
1231 debugs(80, 3, "Incoming WCCPv2 I_SEE_YOU length " << ntohs(wccp2_i_see_you
.length
) << ".");
1233 /* Record the total data length */
1234 const auto data_length
= ntohs(wccp2_i_see_you
.length
);
1235 Must3(data_length
<= len
- message_header_size
,
1236 "malformed packet claiming it's bigger than received data", Here());
1240 /* Go through the data structure */
1241 while (offset
+ sizeof(struct wccp2_item_header_t
) <= data_length
) {
1243 char *data
= wccp2_i_see_you
.data
;
1245 const auto itemHeader
= reinterpret_cast<const wccp2_item_header_t
*>(&data
[offset
]);
1246 const auto itemSize
= CheckFieldDataLength(itemHeader
, ntohs(itemHeader
->length
),
1247 data
, data_length
, "truncated record");
1248 // XXX: Check "The specified length must be a multiple of 4 octets"
1249 // requirement to avoid unaligned memory reads after the first item.
1251 switch (ntohs(itemHeader
->type
)) {
1253 case WCCP2_SECURITY_INFO
:
1254 Must3(!security_info
, "duplicate security definition", Here());
1255 SetField(security_info
, itemHeader
, itemHeader
, itemSize
,
1256 "security definition truncated");
1259 case WCCP2_SERVICE_INFO
:
1260 Must3(!service_info
, "duplicate service_info definition", Here());
1261 SetField(service_info
, itemHeader
, itemHeader
, itemSize
,
1262 "service_info definition truncated");
1265 case WCCP2_ROUTER_ID_INFO
:
1266 Must3(!router_identity_info
, "duplicate router_identity_info definition", Here());
1267 SetField(router_identity_info
, itemHeader
, itemHeader
, itemSize
,
1268 "router_identity_info definition truncated");
1271 case WCCP2_RTR_VIEW_INFO
:
1272 Must3(!router_view_header
, "duplicate router_view definition", Here());
1273 SetField(router_view_header
, itemHeader
, itemHeader
, itemSize
,
1274 "router_view definition truncated");
1277 case WCCP2_CAPABILITY_INFO
: {
1278 Must3(!router_capability_header
, "duplicate router_capability definition", Here());
1279 SetField(router_capability_header
, itemHeader
, itemHeader
, itemSize
,
1280 "router_capability definition truncated");
1282 CheckFieldDataLength(router_capability_header
, ntohs(router_capability_header
->capability_info_length
),
1283 itemHeader
, itemSize
, "capability info truncated");
1284 router_capability_data_start
= reinterpret_cast<char*>(router_capability_header
) +
1285 sizeof(*router_capability_header
);
1289 /* Nothing to do for the types below */
1291 case WCCP2_ASSIGN_MAP
:
1292 case WCCP2_REDIRECT_ASSIGNMENT
:
1296 debugs(80, DBG_IMPORTANT
, "ERROR: Unknown record type in WCCPv2 Packet (" << ntohs(itemHeader
->type
) << ").");
1300 assert(offset
<= data_length
&& "CheckFieldDataLength(itemHeader...) established that");
1303 Must3(security_info
, "packet missing security definition", Here());
1304 Must3(service_info
, "packet missing service_info definition", Here());
1305 Must3(router_identity_info
, "packet missing router_identity_info definition", Here());
1306 Must3(router_view_header
, "packet missing router_view definition", Here());
1308 debugs(80, 5, "Complete packet received");
1310 /* Check that the service in the packet is configured on this router */
1311 service_list_ptr
= wccp2_service_list_head
;
1313 while (service_list_ptr
!= nullptr) {
1314 if (service_info
->service_id
== service_list_ptr
->service_info
->service_id
) {
1318 service_list_ptr
= service_list_ptr
->next
;
1321 if (service_list_ptr
== nullptr) {
1322 debugs(80, DBG_IMPORTANT
, "ERROR: WCCPv2 Unknown service received from router (" << service_info
->service_id
<< ")");
1326 if (ntohl(security_info
->security_option
) != ntohl(service_list_ptr
->security_info
->security_option
)) {
1327 debugs(80, DBG_IMPORTANT
, "ERROR: Invalid security option in WCCPv2 Packet (" << ntohl(security_info
->security_option
) << " vs " << ntohl(service_list_ptr
->security_info
->security_option
) << ").");
1331 if (!wccp2_check_security(service_list_ptr
, (char *) security_info
, (char *) &wccp2_i_see_you
, len
)) {
1332 debugs(80, DBG_IMPORTANT
, "ERROR: Received WCCPv2 Packet failed authentication");
1336 /* Check that the router address is configured on this router */
1337 for (router_list_ptr
= &service_list_ptr
->router_list_head
; router_list_ptr
->next
!= nullptr; router_list_ptr
= router_list_ptr
->next
) {
1338 if (router_list_ptr
->router_sendto_address
.s_addr
== from
.sin_addr
.s_addr
)
1342 Must3(router_list_ptr
->next
, "packet received from unknown router", Here());
1344 /* Set the router id */
1345 router_list_ptr
->info
->router_address
= router_identity_info
->router_id_element
.router_address
;
1347 /* Increment the received id in the packet */
1348 if (ntohl(router_list_ptr
->info
->received_id
) != ntohl(router_identity_info
->router_id_element
.received_id
)) {
1349 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
) << ".");
1350 router_list_ptr
->info
->received_id
= router_identity_info
->router_id_element
.received_id
;
1353 /* TODO: check return/forwarding methods */
1354 if (router_capability_header
== nullptr) {
1355 if ((Config
.Wccp2
.return_method
!= WCCP2_PACKET_RETURN_METHOD_GRE
) || (Config
.Wccp2
.forwarding_method
!= WCCP2_FORWARDING_METHOD_GRE
)) {
1356 debugs(80, DBG_IMPORTANT
, "ERROR: wccp2HandleUdp: fatal error - A WCCP router does not support the forwarding method specified, only GRE supported");
1357 wccp2ConnectionClose();
1362 const auto router_capability_data_length
= ntohs(router_capability_header
->capability_info_length
);
1363 assert(router_capability_data_start
);
1364 const auto router_capability_data_end
= router_capability_data_start
+
1365 router_capability_data_length
;
1366 for (auto router_capability_data_current
= router_capability_data_start
;
1367 router_capability_data_current
< router_capability_data_end
;) {
1369 SetField(router_capability_element
, router_capability_data_current
,
1370 router_capability_data_start
, router_capability_data_length
,
1371 "capability element header truncated");
1372 const auto elementSize
= CheckFieldDataLength(
1373 router_capability_element
, ntohs(router_capability_element
->capability_length
),
1374 router_capability_data_start
, router_capability_data_length
,
1375 "capability element truncated");
1377 switch (ntohs(router_capability_element
->capability_type
)) {
1379 case WCCP2_CAPABILITY_FORWARDING_METHOD
:
1381 if (!(ntohl(router_capability_element
->capability_value
) & Config
.Wccp2
.forwarding_method
)) {
1382 debugs(80, DBG_IMPORTANT
, "ERROR: wccp2HandleUdp: fatal error - A WCCP router has specified a different forwarding method " << ntohl(router_capability_element
->capability_value
) << ", expected " << Config
.Wccp2
.forwarding_method
);
1383 wccp2ConnectionClose();
1389 case WCCP2_CAPABILITY_ASSIGNMENT_METHOD
:
1391 if (!(ntohl(router_capability_element
->capability_value
) & Config
.Wccp2
.assignment_method
)) {
1392 debugs(80, DBG_IMPORTANT
, "ERROR: wccp2HandleUdp: fatal error - A WCCP router has specified a different assignment method " << ntohl(router_capability_element
->capability_value
) << ", expected "<< Config
.Wccp2
.assignment_method
);
1393 wccp2ConnectionClose();
1399 case WCCP2_CAPABILITY_RETURN_METHOD
:
1401 if (!(ntohl(router_capability_element
->capability_value
) & Config
.Wccp2
.return_method
)) {
1402 debugs(80, DBG_IMPORTANT
, "ERROR: wccp2HandleUdp: fatal error - A WCCP router has specified a different return method " << ntohl(router_capability_element
->capability_value
) << ", expected " << Config
.Wccp2
.return_method
);
1403 wccp2ConnectionClose();
1411 break; // ignore silently for now
1414 debugs(80, DBG_IMPORTANT
, "ERROR: Unknown capability type in WCCPv2 Packet (" << ntohs(router_capability_element
->capability_type
) << ").");
1417 router_capability_data_current
+= elementSize
;
1421 debugs(80, 5, "Cleaning out cache list");
1422 /* clean out the old cache list */
1424 for (cache_list_ptr
= &router_list_ptr
->cache_list_head
; cache_list_ptr
; cache_list_ptr
= cache_list_ptr_next
) {
1425 cache_list_ptr_next
= cache_list_ptr
->next
;
1427 if (cache_list_ptr
!= &router_list_ptr
->cache_list_head
) {
1428 xfree(cache_list_ptr
);
1432 router_list_ptr
->num_caches
= htonl(0);
1435 /* Check to see if we're the master cache and update the cache list */
1437 service_list_ptr
->lowest_ip
= 1;
1438 cache_list_ptr
= &router_list_ptr
->cache_list_head
;
1440 /* to find the list of caches, we start at the end of the router view header */
1442 ptr
= (char *) (router_view_header
) + sizeof(struct router_view_t
);
1443 const auto router_view_size
= sizeof(struct router_view_t
) +
1444 ntohs(router_view_header
->header
.length
);
1446 /* Then we read the number of routers */
1447 const uint32_t *routerCountRaw
= nullptr;
1448 SetField(routerCountRaw
, ptr
, router_view_header
, router_view_size
,
1449 "malformed packet (truncated router view info w/o number of routers)");
1451 /* skip the number plus all the ip's */
1452 ptr
+= sizeof(*routerCountRaw
);
1453 const auto ipCount
= ntohl(*routerCountRaw
);
1454 const auto ipsSize
= ipCount
* sizeof(struct in_addr
); // we check for unsigned overflow below
1455 Must3(ipsSize
/ sizeof(struct in_addr
) == ipCount
, "huge IP address count", Here());
1456 CheckSectionLength(ptr
, ipsSize
, router_view_header
, router_view_size
, "invalid IP address count");
1459 /* Then read the number of caches */
1460 const uint32_t *cacheCountRaw
= nullptr;
1461 SetField(cacheCountRaw
, ptr
, router_view_header
, router_view_size
,
1462 "malformed packet (truncated router view info w/o cache count)");
1463 memcpy(&tmp
, cacheCountRaw
, sizeof(tmp
)); // TODO: Replace tmp with cacheCount
1466 if (ntohl(tmp
) != 0) {
1467 /* search through the list of received-from ip addresses */
1469 for (num_caches
= 0; num_caches
< (int) ntohl(tmp
); ++num_caches
) {
1470 /* Get a copy of the ip */
1471 memset(&cache_address
, 0, sizeof(cache_address
)); // Make GCC happy
1473 switch (Config
.Wccp2
.assignment_method
) {
1475 case WCCP2_ASSIGNMENT_METHOD_HASH
:
1477 SetField(cache_identity
, ptr
, router_view_header
, router_view_size
,
1478 "malformed packet (truncated router view info cache w/o assignment hash)");
1480 ptr
+= sizeof(struct wccp2_cache_identity_info_t
);
1482 memcpy(&cache_address
, &cache_identity
->addr
, sizeof(struct in_addr
));
1484 cache_list_ptr
->weight
= ntohs(cache_identity
->weight
);
1487 case WCCP2_ASSIGNMENT_METHOD_MASK
:
1489 SetField(cache_mask_info
, ptr
, router_view_header
, router_view_size
,
1490 "malformed packet (truncated router view info cache w/o assignment mask)");
1492 /* The mask assignment has an undocumented variable length entry here */
1494 if (ntohl(cache_mask_info
->num1
) == 3) {
1496 SetField(cache_mask_identity
, ptr
, router_view_header
, router_view_size
,
1497 "malformed packet (truncated router view info cache w/o assignment mask identity)");
1499 ptr
+= sizeof(struct wccp2_cache_mask_identity_info_t
);
1501 memcpy(&cache_address
, &cache_mask_identity
->addr
, sizeof(struct in_addr
));
1504 ptr
+= sizeof(struct cache_mask_info_t
);
1506 memcpy(&cache_address
, &cache_mask_info
->addr
, sizeof(struct in_addr
));
1509 cache_list_ptr
->weight
= 0;
1513 fatalf("Unknown Wccp2 assignment method\n");
1516 /* Update the cache list */
1517 cache_list_ptr
->cache_ip
= cache_address
;
1519 cache_list_ptr
->next
= (wccp2_cache_list_t
*) xcalloc(1, sizeof(struct wccp2_cache_list_t
));
1521 cache_list_ptr
= cache_list_ptr
->next
;
1523 cache_list_ptr
->next
= nullptr;
1525 debugs (80, 5, "checking cache list: (" << std::hex
<< cache_address
.s_addr
<< ":" << router_list_ptr
->local_ip
.s_addr
<< ")");
1527 /* Check to see if it's the master, or us */
1528 found
= found
|| (cache_address
.s_addr
== router_list_ptr
->local_ip
.s_addr
);
1530 if (cache_address
.s_addr
< router_list_ptr
->local_ip
.s_addr
) {
1531 service_list_ptr
->lowest_ip
= 0;
1535 debugs(80, 5, "Adding ourselves as the only cache");
1537 /* Update the cache list */
1538 cache_list_ptr
->cache_ip
= router_list_ptr
->local_ip
;
1540 cache_list_ptr
->next
= (wccp2_cache_list_t
*) xcalloc(1, sizeof(struct wccp2_cache_list_t
));
1541 cache_list_ptr
= cache_list_ptr
->next
;
1542 cache_list_ptr
->next
= nullptr;
1544 service_list_ptr
->lowest_ip
= 1;
1549 wccp2SortCacheList(&router_list_ptr
->cache_list_head
);
1551 router_list_ptr
->num_caches
= htonl(num_caches
);
1553 if (found
&& (service_list_ptr
->lowest_ip
== 1)) {
1554 if (ntohl(router_view_header
->change_number
) != router_list_ptr
->member_change
) {
1555 debugs(80, 4, "Change detected - queueing up new assignment");
1556 router_list_ptr
->member_change
= ntohl(router_view_header
->change_number
);
1557 eventDelete(wccp2AssignBuckets
, nullptr);
1558 eventAdd("wccp2AssignBuckets", wccp2AssignBuckets
, nullptr, 15.0, 1);
1560 debugs(80, 5, "Change not detected (" << ntohl(router_view_header
->change_number
) << " = " << router_list_ptr
->member_change
<< ")");
1563 eventDelete(wccp2AssignBuckets
, nullptr);
1564 debugs(80, 5, "I am not the lowest ip cache - not assigning buckets");
1568 debugs(80, DBG_IMPORTANT
, "ERROR: Ignoring WCCPv2 message: " << CurrentException
);
1573 wccp2HereIam(void *)
1575 struct wccp2_service_list_t
*service_list_ptr
;
1577 struct wccp2_router_list_t
*router_list_ptr
;
1579 struct wccp2_identity_info_t
*wccp2_identity_info_ptr
;
1581 struct wccp2_mask_identity_info_t
*wccp2_mask_identity_info_ptr
;
1585 debugs(80, 6, "wccp2HereIam: Called");
1587 if (wccp2_connected
== 0) {
1588 debugs(80, DBG_IMPORTANT
, "wccp2HereIam: wccp2 socket closed. Shutting down WCCP2");
1592 /* Wait if store dirs are rebuilding */
1593 if (StoreController::store_dirs_rebuilding
&& Config
.Wccp2
.rebuildwait
) {
1594 eventAdd("wccp2HereIam", wccp2HereIam
, nullptr, 1.0, 1);
1598 router
.port(WCCP_PORT
);
1600 /* for each router on each service send a packet */
1601 service_list_ptr
= wccp2_service_list_head
;
1603 while (service_list_ptr
!= nullptr) {
1604 debugs(80, 5, "wccp2HereIam: sending to service id " << service_list_ptr
->info
.service_id
);
1606 for (router_list_ptr
= &service_list_ptr
->router_list_head
; router_list_ptr
->next
!= nullptr; router_list_ptr
= router_list_ptr
->next
) {
1607 router
= router_list_ptr
->router_sendto_address
;
1609 /* Set the cache id (ip) */
1611 switch (Config
.Wccp2
.assignment_method
) {
1613 case WCCP2_ASSIGNMENT_METHOD_HASH
:
1615 wccp2_identity_info_ptr
= (struct wccp2_identity_info_t
*) service_list_ptr
->wccp2_identity_info_ptr
;
1616 wccp2_identity_info_ptr
->cache_identity
.addr
= router_list_ptr
->local_ip
;
1619 case WCCP2_ASSIGNMENT_METHOD_MASK
:
1621 wccp2_mask_identity_info_ptr
= (struct wccp2_mask_identity_info_t
*) service_list_ptr
->wccp2_identity_info_ptr
;
1622 wccp2_mask_identity_info_ptr
->cache_identity
.addr
= router_list_ptr
->local_ip
;
1626 fatalf("Unknown Wccp2 assignment method\n");
1629 /* Security update, if needed */
1631 if (service_list_ptr
->wccp2_security_type
== WCCP2_MD5_SECURITY
) {
1632 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
);
1635 debugs(80, 3, "Sending HereIam packet size " << service_list_ptr
->wccp_packet_size
);
1636 /* Send the packet */
1638 if (wccp2_numrouters
> 1) {
1639 comm_udp_sendto(theWccp2Connection
,
1641 &service_list_ptr
->wccp_packet
,
1642 service_list_ptr
->wccp_packet_size
);
1644 if (send(theWccp2Connection
, &service_list_ptr
->wccp_packet
, service_list_ptr
->wccp_packet_size
, 0) < static_cast<int>(service_list_ptr
->wccp_packet_size
)) {
1646 debugs(80, 2, "ERROR: failed to send WCCPv2 HERE_I_AM packet to " << router
<< " : " << xstrerr(xerrno
));
1651 service_list_ptr
= service_list_ptr
->next
;
1654 eventAdd("wccp2HereIam", wccp2HereIam
, nullptr, 10.0, 1);
1658 wccp2AssignBuckets(void *)
1660 struct wccp2_service_list_t
*service_list_ptr
;
1662 struct wccp2_router_list_t
*router_list_ptr
;
1664 struct wccp2_cache_list_t
*cache_list_ptr
;
1665 char wccp_packet
[WCCP_RESPONSE_SIZE
];
1666 short int offset
, saved_offset
, assignment_offset
, alt_assignment_offset
;
1668 struct sockaddr_in router
;
1671 uint32_t service_flags
;
1672 /* Packet segments */
1674 struct wccp2_message_header_t
*main_header
;
1676 struct wccp2_security_md5_t
*security
= nullptr;
1677 /* service from service struct */
1679 struct wccp2_item_header_t
*assignment_header
;
1681 struct wccp2_item_header_t
*alt_assignment_type_header
= nullptr;
1683 struct assignment_key_t
*assignment_key
;
1684 /* number of routers */
1686 struct wccp2_router_assign_element_t
*router_assign
;
1687 /* number of caches */
1689 struct in_addr
*cache_address
;
1690 /* Alternative assignment mask/values */
1693 struct wccp2_mask_element_t
*mask_element
;
1695 struct wccp2_value_element_t
*value_element
;
1696 int valuecounter
, value
;
1699 assignment_offset
= alt_assignment_offset
= 0;
1701 router_len
= sizeof(router
);
1702 memset(&router
, '\0', router_len
);
1703 router
.sin_family
= AF_INET
;
1704 router
.sin_port
= htons(WCCP_PORT
);
1706 /* Start main header - fill in length later */
1709 main_header
= (struct wccp2_message_header_t
*) &wccp_packet
[offset
];
1710 main_header
->type
= htonl(WCCP2_REDIRECT_ASSIGN
);
1711 main_header
->version
= htons(WCCP2_VERSION
);
1713 debugs(80, 2, "Running wccp2AssignBuckets");
1714 service_list_ptr
= wccp2_service_list_head
;
1716 while (service_list_ptr
!= nullptr) {
1717 /* If we're not the lowest, we don't need to worry */
1719 if (service_list_ptr
->lowest_ip
== 0) {
1721 service_list_ptr
= service_list_ptr
->next
;
1725 /* reset the offset */
1727 offset
= sizeof(struct wccp2_message_header_t
);
1729 /* build packet header from hereIam packet */
1731 /* XXX this should be made more generic! */
1732 /* XXX and I hate magic numbers! */
1733 switch (service_list_ptr
->wccp2_security_type
) {
1735 case WCCP2_NO_SECURITY
:
1737 security
= (struct wccp2_security_md5_t
*) &wccp_packet
[offset
];
1738 memcpy(security
, service_list_ptr
->security_info
, 8);
1742 case WCCP2_MD5_SECURITY
:
1744 security
= (struct wccp2_security_md5_t
*) &wccp_packet
[offset
];
1746 memcpy(security
, service_list_ptr
->security_info
, sizeof(struct wccp2_security_md5_t
));
1748 offset
+= sizeof(struct wccp2_security_md5_t
);
1752 fatalf("Unknown Wccp2 security type\n");
1757 memcpy(&wccp_packet
[offset
], service_list_ptr
->service_info
, sizeof(struct wccp2_service_info_t
));
1759 offset
+= sizeof(struct wccp2_service_info_t
);
1761 /* assignment header - fill in length later */
1763 assignment_header
= (struct wccp2_item_header_t
*) &wccp_packet
[offset
];
1765 switch (Config
.Wccp2
.assignment_method
) {
1767 case WCCP2_ASSIGNMENT_METHOD_HASH
:
1768 assignment_header
->type
= htons(WCCP2_REDIRECT_ASSIGNMENT
);
1770 offset
+= sizeof(struct wccp2_item_header_t
);
1771 assignment_offset
= offset
;
1774 case WCCP2_ASSIGNMENT_METHOD_MASK
:
1775 assignment_header
->type
= htons(WCCP2_ALT_ASSIGNMENT
);
1777 offset
+= sizeof(struct wccp2_item_header_t
);
1778 assignment_offset
= offset
;
1780 /* The alternative assignment has an extra header, fill in length later */
1782 alt_assignment_type_header
= (struct wccp2_item_header_t
*) &wccp_packet
[offset
];
1783 alt_assignment_type_header
->type
= htons(WCCP2_MASK_ASSIGNMENT
);
1785 offset
+= sizeof(struct wccp2_item_header_t
);
1786 alt_assignment_offset
= offset
;
1791 fatalf("Unknown Wccp2 assignment method\n");
1794 /* Assignment key - fill in master ip later */
1796 assignment_key
= (struct assignment_key_t
*) &wccp_packet
[offset
];
1798 ++service_list_ptr
->change_num
;
1799 assignment_key
->master_number
= htonl(service_list_ptr
->change_num
);
1801 offset
+= sizeof(struct assignment_key_t
);
1803 /* Number of routers */
1804 memcpy(&wccp_packet
[offset
], &service_list_ptr
->num_routers
, sizeof(service_list_ptr
->num_routers
));
1806 offset
+= sizeof(service_list_ptr
->num_routers
);
1808 for (router_list_ptr
= &service_list_ptr
->router_list_head
; router_list_ptr
->next
!= nullptr; router_list_ptr
= router_list_ptr
->next
) {
1812 router_assign
= (struct wccp2_router_assign_element_t
*) &wccp_packet
[offset
];
1813 router_assign
->router_address
= router_list_ptr
->info
->router_address
;
1814 router_assign
->received_id
= router_list_ptr
->info
->received_id
;
1815 router_assign
->change_number
= htonl(router_list_ptr
->member_change
);
1817 offset
+= sizeof(struct wccp2_router_assign_element_t
);
1820 saved_offset
= offset
;
1822 for (router_list_ptr
= &service_list_ptr
->router_list_head
; router_list_ptr
->next
!= nullptr; router_list_ptr
= router_list_ptr
->next
) {
1823 unsigned long *weight
= (unsigned long *)xcalloc(sizeof(*weight
), ntohl(router_list_ptr
->num_caches
));
1824 unsigned long total_weight
= 0;
1825 int num_caches
= ntohl(router_list_ptr
->num_caches
);
1827 offset
= saved_offset
;
1829 switch (Config
.Wccp2
.assignment_method
) {
1831 case WCCP2_ASSIGNMENT_METHOD_HASH
:
1832 /* Number of caches */
1833 memcpy(&wccp_packet
[offset
], &router_list_ptr
->num_caches
, sizeof(router_list_ptr
->num_caches
));
1834 offset
+= sizeof(router_list_ptr
->num_caches
);
1839 for (cache
= 0, cache_list_ptr
= &router_list_ptr
->cache_list_head
; cache_list_ptr
->next
; cache_list_ptr
= cache_list_ptr
->next
, ++cache
) {
1842 cache_address
= (struct in_addr
*) &wccp_packet
[offset
];
1844 memcpy(cache_address
, &cache_list_ptr
->cache_ip
, sizeof(struct in_addr
));
1845 total_weight
+= cache_list_ptr
->weight
<< 12;
1846 weight
[cache
] = cache_list_ptr
->weight
<< 12;
1848 offset
+= sizeof(struct in_addr
);
1853 buckets
= (char *) &wccp_packet
[offset
];
1855 memset(buckets
, '\0', WCCP_BUCKETS
);
1857 if (num_caches
!= 0) {
1858 if (total_weight
== 0) {
1859 for (bucket_counter
= 0; bucket_counter
< WCCP_BUCKETS
; ++bucket_counter
) {
1860 buckets
[bucket_counter
] = (char) (bucket_counter
% num_caches
);
1863 unsigned long *assigned
= (unsigned long *)xcalloc(sizeof(*assigned
), num_caches
);
1864 unsigned long done
= 0;
1866 unsigned long per_bucket
= total_weight
/ WCCP_BUCKETS
;
1868 for (bucket_counter
= 0; bucket_counter
< WCCP_BUCKETS
; ++bucket_counter
) {
1872 for (n
= num_caches
; n
; --n
) {
1875 if (cache
>= num_caches
)
1878 if (!weight
[cache
]) {
1883 if (assigned
[cache
] <= done
)
1887 buckets
[bucket_counter
] = (char) cache
;
1888 step
= per_bucket
* total_weight
/ weight
[cache
];
1889 assigned
[cache
] += step
;
1893 safe_free(assigned
);
1897 offset
+= (WCCP_BUCKETS
* sizeof(char));
1901 case WCCP2_ASSIGNMENT_METHOD_MASK
:
1902 num_maskval
= htonl(1);
1903 memcpy(&wccp_packet
[offset
], &num_maskval
, sizeof(int));
1904 offset
+= sizeof(int);
1906 mask_element
= (struct wccp2_mask_element_t
*) &wccp_packet
[offset
];
1907 service_flags
= ntohl(service_list_ptr
->service_info
->service_flags
);
1909 if ((service_flags
& WCCP2_SERVICE_SRC_IP_HASH
) || (service_flags
& WCCP2_SERVICE_SRC_IP_ALT_HASH
)) {
1910 mask_element
->source_ip_mask
= htonl(0x00001741);
1911 mask_element
->dest_ip_mask
= 0;
1912 mask_element
->source_port_mask
= 0;
1913 mask_element
->dest_port_mask
= 0;
1914 } 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
)) {
1915 mask_element
->source_ip_mask
= 0;
1916 mask_element
->dest_ip_mask
= htonl(0x00001741);
1917 mask_element
->source_port_mask
= 0;
1918 mask_element
->dest_port_mask
= 0;
1919 } else if ((service_flags
& WCCP2_SERVICE_SRC_PORT_HASH
) || (service_flags
& WCCP2_SERVICE_SRC_PORT_ALT_HASH
)) {
1920 mask_element
->source_ip_mask
= 0;
1921 mask_element
->dest_ip_mask
= 0;
1922 mask_element
->source_port_mask
= htons(0x1741);
1923 mask_element
->dest_port_mask
= 0;
1924 } else if ((service_flags
& WCCP2_SERVICE_DST_PORT_HASH
) || (service_flags
& WCCP2_SERVICE_DST_PORT_ALT_HASH
)) {
1925 mask_element
->source_ip_mask
= 0;
1926 mask_element
->dest_ip_mask
= 0;
1927 mask_element
->source_port_mask
= 0;
1928 mask_element
->dest_port_mask
= htons(0x1741);
1930 fatalf("Unknown service hash method\n");
1933 mask_element
->number_values
= htonl(64);
1935 offset
+= sizeof(struct wccp2_mask_element_t
);
1937 cache_list_ptr
= &router_list_ptr
->cache_list_head
;
1940 for (valuecounter
= 0; valuecounter
< 64; ++valuecounter
) {
1942 value_element
= (struct wccp2_value_element_t
*) &wccp_packet
[offset
];
1944 /* Update the value according the the "correct" formula */
1946 for (; (value
& 0x1741) != value
; ++value
) {
1947 assert(value
<= 0x1741);
1950 if ((service_flags
& WCCP2_SERVICE_SRC_IP_HASH
) || (service_flags
& WCCP2_SERVICE_SRC_IP_ALT_HASH
)) {
1951 value_element
->source_ip_value
= htonl(value
);
1952 value_element
->dest_ip_value
= 0;
1953 value_element
->source_port_value
= 0;
1954 value_element
->dest_port_value
= 0;
1955 } 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
)) {
1956 value_element
->source_ip_value
= 0;
1957 value_element
->dest_ip_value
= htonl(value
);
1958 value_element
->source_port_value
= 0;
1959 value_element
->dest_port_value
= 0;
1960 } else if ((service_flags
& WCCP2_SERVICE_SRC_PORT_HASH
) || (service_flags
& WCCP2_SERVICE_SRC_PORT_ALT_HASH
)) {
1961 value_element
->source_ip_value
= 0;
1962 value_element
->dest_ip_value
= 0;
1963 value_element
->source_port_value
= htons(value
);
1964 value_element
->dest_port_value
= 0;
1965 } else if ((service_flags
& WCCP2_SERVICE_DST_PORT_HASH
) || (service_flags
& WCCP2_SERVICE_DST_PORT_ALT_HASH
)) {
1966 value_element
->source_ip_value
= 0;
1967 value_element
->dest_ip_value
= 0;
1968 value_element
->source_port_value
= 0;
1969 value_element
->dest_port_value
= htons(value
);
1971 fatalf("Unknown service hash method\n");
1974 value_element
->cache_ip
= cache_list_ptr
->cache_ip
;
1976 offset
+= sizeof(struct wccp2_value_element_t
);
1979 /* Assign the next value to the next cache */
1981 if ((cache_list_ptr
->next
) && (cache_list_ptr
->next
->next
))
1982 cache_list_ptr
= cache_list_ptr
->next
;
1984 cache_list_ptr
= &router_list_ptr
->cache_list_head
;
1987 /* Fill in length */
1988 alt_assignment_type_header
->length
= htons(offset
- alt_assignment_offset
);
1993 fatalf("Unknown Wccp2 assignment method\n");
1996 /* Fill in length */
1998 assignment_header
->length
= htons(offset
- assignment_offset
);
2000 /* Fill in assignment key */
2001 assignment_key
->master_ip
= router_list_ptr
->local_ip
;
2005 main_header
->length
= htons(offset
- sizeof(struct wccp2_message_header_t
));
2007 /* set the destination address */
2008 router
.sin_addr
= router_list_ptr
->router_sendto_address
;
2010 /* Security update, if needed */
2012 if (service_list_ptr
->wccp2_security_type
== WCCP2_MD5_SECURITY
) {
2013 wccp2_update_md5_security(service_list_ptr
->wccp_password
, (char *) security
, wccp_packet
, offset
);
2016 if (ntohl(router_list_ptr
->num_caches
)) {
2019 // XXX: drop temp conversion
2020 Ip::Address
tmp_rtr(router
);
2022 if (wccp2_numrouters
> 1) {
2023 comm_udp_sendto(theWccp2Connection
,
2028 if (send(theWccp2Connection
, &wccp_packet
, offset
, 0) < static_cast<int>(offset
)) {
2030 debugs(80, 2, "ERROR: failed to send WCCPv2 HERE_I_AM packet to " << tmp_rtr
<< " : " << xstrerr(xerrno
));
2037 service_list_ptr
= service_list_ptr
->next
;
2042 * Configuration option parsing code
2046 * Parse wccp2_return_method and wccp2_forwarding_method options
2047 * they can be '1' aka 'gre' or '2' aka 'l2'
2048 * representing the integer numeric of the same.
2051 parse_wccp2_method(int *method
)
2055 /* Snarf the method */
2056 if ((t
= ConfigParser::NextToken()) == nullptr) {
2057 debugs(80, DBG_CRITICAL
, "ERROR: wccp2_*_method: missing setting.");
2062 /* update configuration if its valid */
2063 if (strcmp(t
, "gre") == 0 || strcmp(t
, "1") == 0) {
2064 *method
= WCCP2_METHOD_GRE
;
2065 } else if (strcmp(t
, "l2") == 0 || strcmp(t
, "2") == 0) {
2066 *method
= WCCP2_METHOD_L2
;
2068 debugs(80, DBG_CRITICAL
, "ERROR: wccp2_*_method: unknown setting, got " << t
);
2074 dump_wccp2_method(StoreEntry
* e
, const char *label
, int v
)
2077 case WCCP2_METHOD_GRE
:
2078 storeAppendPrintf(e
, "%s gre\n", label
);
2080 case WCCP2_METHOD_L2
:
2081 storeAppendPrintf(e
, "%s l2\n", label
);
2084 debugs(80, DBG_CRITICAL
, "FATAL: WCCPv2 configured method (" << v
<< ") is not valid.");
2090 free_wccp2_method(int *)
2094 * Parse wccp2_assignment_method option
2095 * they can be '1' aka 'hash' or '2' aka 'mask'
2096 * representing the integer numeric of the same.
2099 parse_wccp2_amethod(int *method
)
2103 /* Snarf the method */
2104 if ((t
= ConfigParser::NextToken()) == nullptr) {
2105 debugs(80, DBG_CRITICAL
, "ERROR: wccp2_assignment_method: missing setting.");
2110 /* update configuration if its valid */
2111 if (strcmp(t
, "hash") == 0 || strcmp(t
, "1") == 0) {
2112 *method
= WCCP2_ASSIGNMENT_METHOD_HASH
;
2113 } else if (strcmp(t
, "mask") == 0 || strcmp(t
, "2") == 0) {
2114 *method
= WCCP2_ASSIGNMENT_METHOD_MASK
;
2116 debugs(80, DBG_CRITICAL
, "ERROR: wccp2_assignment_method: unknown setting, got " << t
);
2122 dump_wccp2_amethod(StoreEntry
* e
, const char *label
, int v
)
2125 case WCCP2_ASSIGNMENT_METHOD_HASH
:
2126 storeAppendPrintf(e
, "%s hash\n", label
);
2128 case WCCP2_ASSIGNMENT_METHOD_MASK
:
2129 storeAppendPrintf(e
, "%s mask\n", label
);
2132 debugs(80, DBG_CRITICAL
, "FATAL: WCCPv2 configured " << label
<< " (" << v
<< ") is not valid.");
2138 free_wccp2_amethod(int *)
2144 * wccp2_service {standard|dynamic} {id} (password=password)
2147 parse_wccp2_service(void *)
2152 int security_type
= WCCP2_NO_SECURITY
;
2153 char wccp_password
[WCCP2_PASSWORD_LEN
];
2155 if (wccp2_connected
== 1) {
2156 debugs(80, DBG_IMPORTANT
, "WCCPv2: Somehow reparsing the configuration without having shut down WCCP! Try reloading squid again.");
2160 /* Snarf the type */
2161 if ((t
= ConfigParser::NextToken()) == nullptr) {
2162 debugs(80, DBG_CRITICAL
, "ERROR: wccp2ParseServiceInfo: missing service info type (standard|dynamic)");
2167 if (strcmp(t
, "standard") == 0) {
2168 service
= WCCP2_SERVICE_STANDARD
;
2169 } else if (strcmp(t
, "dynamic") == 0) {
2170 service
= WCCP2_SERVICE_DYNAMIC
;
2172 debugs(80, DBG_CRITICAL
, "ERROR: wccp2ParseServiceInfo: bad service info type (expected standard|dynamic, got " << t
<< ")");
2178 service_id
= GetInteger();
2180 if (service_id
< 0 || service_id
> 255) {
2181 debugs(80, DBG_CRITICAL
, "ERROR: invalid WCCP service id " << service_id
<< " (must be between 0 .. 255)");
2186 memset(wccp_password
, 0, sizeof(wccp_password
));
2187 /* Handle password, if any */
2189 if ((t
= ConfigParser::NextToken()) != nullptr) {
2190 if (strncmp(t
, "password=", 9) == 0) {
2191 security_type
= WCCP2_MD5_SECURITY
;
2192 xstrncpy(wccp_password
, t
+ 9, sizeof(wccp_password
));
2196 /* Create a placeholder service record */
2197 wccp2_add_service_list(service
, service_id
, 0, 0, 0, empty_portlist
, security_type
, wccp_password
);
2201 dump_wccp2_service(StoreEntry
* e
, const char *label
, void *)
2203 struct wccp2_service_list_t
*srv
;
2204 srv
= wccp2_service_list_head
;
2206 while (srv
!= nullptr) {
2207 debugs(80, 3, "dump_wccp2_service: id " << srv
->info
.service_id
<< ", type " << srv
->info
.service
);
2208 storeAppendPrintf(e
, "%s %s %d", label
,
2209 (srv
->info
.service
== WCCP2_SERVICE_DYNAMIC
) ? "dynamic" : "standard",
2210 srv
->info
.service_id
);
2212 if (srv
->wccp2_security_type
== WCCP2_MD5_SECURITY
) {
2213 storeAppendPrintf(e
, " %s", srv
->wccp_password
);
2216 storeAppendPrintf(e
, "\n");
2223 free_wccp2_service(void *)
2227 check_null_wccp2_service(void *)
2229 return !wccp2_service_list_head
;
2235 * wccp2_service_info {id} stuff..
2239 * + flags=flag,flag,flag..
2240 * + proto=protocol (tcp|udp)
2241 * + ports=port,port,port (up to a max of 8)
2242 * + priority=priority (0->255)
2244 * The flags here are:
2245 * src_ip_hash, dst_ip_hash, source_port_hash, dst_port_hash, ports_defined,
2246 * ports_source, src_ip_alt_hash, dst_ip_alt_hash, src_port_alt_hash, dst_port_alt_hash
2249 parse_wccp2_service_flags(char *flags
)
2257 while (size_t len
= strcspn(flag
, ",")) {
2259 if (strncmp(flag
, "src_ip_hash", len
) == 0) {
2260 retflag
|= WCCP2_SERVICE_SRC_IP_HASH
;
2261 } else if (strncmp(flag
, "dst_ip_hash", len
) == 0) {
2262 retflag
|= WCCP2_SERVICE_DST_IP_HASH
;
2263 } else if (strncmp(flag
, "source_port_hash", len
) == 0) {
2264 retflag
|= WCCP2_SERVICE_SRC_PORT_HASH
;
2265 } else if (strncmp(flag
, "dst_port_hash", len
) == 0) {
2266 retflag
|= WCCP2_SERVICE_DST_PORT_HASH
;
2267 } else if (strncmp(flag
, "ports_source", len
) == 0) {
2268 retflag
|= WCCP2_SERVICE_PORTS_SOURCE
;
2269 } else if (strncmp(flag
, "src_ip_alt_hash", len
) == 0) {
2270 retflag
|= WCCP2_SERVICE_SRC_IP_ALT_HASH
;
2271 } else if (strncmp(flag
, "dst_ip_alt_hash", len
) == 0) {
2272 retflag
|= WCCP2_SERVICE_DST_IP_ALT_HASH
;
2273 } else if (strncmp(flag
, "src_port_alt_hash", len
) == 0) {
2274 retflag
|= WCCP2_SERVICE_SRC_PORT_ALT_HASH
;
2275 } else if (strncmp(flag
, "dst_port_alt_hash", len
) == 0) {
2276 retflag
|= WCCP2_SERVICE_DST_PORT_ALT_HASH
;
2279 fatalf("Unknown wccp2 service flag: %s\n", flag
);
2282 if (flag
[len
] == '\0')
2292 parse_wccp2_service_ports(char *options
, int portlist
[])
2299 char *tmp
= options
;
2300 static char copy
[10];
2302 while (size_t len
= strcspn(tmp
, ",")) {
2303 if (i
>= WCCP2_NUMPORTS
) {
2304 fatalf("parse_wccp2_service_ports: too many ports (maximum: 8) in list '%s'\n", options
);
2306 if (len
> 6) { // 6 because "65535,"
2307 fatalf("parse_wccp2_service_ports: port value '%s' isn't valid (1..65535)\n", tmp
);
2310 memcpy(copy
, tmp
, len
);
2312 int p
= xatoi(copy
);
2314 if (p
< 1 || p
> 65535) {
2315 fatalf("parse_wccp2_service_ports: port value '%s' isn't valid (1..65535)\n", tmp
);
2320 if (tmp
[len
] == '\0')
2327 parse_wccp2_service_info(void *)
2332 int portlist
[WCCP2_NUMPORTS
];
2333 int protocol
= -1; /* IPPROTO_TCP | IPPROTO_UDP */
2335 struct wccp2_service_list_t
*srv
;
2338 if (wccp2_connected
== 1) {
2339 debugs(80, DBG_IMPORTANT
, "WCCPv2: Somehow reparsing the configuration without having shut down WCCP! Try reloading squid again.");
2343 debugs(80, 5, "parse_wccp2_service_info: called");
2344 memset(portlist
, 0, sizeof(portlist
));
2345 /* First argument: id */
2346 service_id
= GetInteger();
2348 if (service_id
< 0 || service_id
> 255) {
2349 debugs(80, DBG_CRITICAL
, "ERROR: invalid WCCP service id " << service_id
<< " (must be between 0 .. 255)");
2354 /* Next: find the (hopefully!) existing service */
2355 srv
= wccp2_get_service_by_id(WCCP2_SERVICE_DYNAMIC
, service_id
);
2357 if (srv
== nullptr) {
2358 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
);
2361 /* Next: loop until we don't have any more tokens */
2362 while ((t
= ConfigParser::NextToken()) != nullptr) {
2363 if (strncmp(t
, "flags=", 6) == 0) {
2364 /* XXX eww, string pointer math */
2365 flags
= parse_wccp2_service_flags(t
+ 6);
2366 } else if (strncmp(t
, "ports=", 6) == 0) {
2367 parse_wccp2_service_ports(t
+ 6, portlist
);
2368 flags
|= WCCP2_SERVICE_PORTS_DEFINED
;
2369 } else if (strncmp(t
, "protocol=tcp", 12) == 0) {
2370 protocol
= IPPROTO_TCP
;
2371 } else if (strncmp(t
, "protocol=udp", 12) == 0) {
2372 protocol
= IPPROTO_UDP
;
2373 } else if (strncmp(t
, "protocol=", 9) == 0) {
2374 fatalf("parse_wccp2_service_info: id %d: unknown protocol (%s) - must be tcp or udp!\n", service_id
, t
);
2375 } else if (strncmp(t
, "priority=", 9) == 0) {
2376 priority
= strtol(t
+ 9, &end
, 0);
2378 if (priority
< 0 || priority
> 255) {
2379 fatalf("parse_wccp2_service_info: id %d: %s out of range (0..255)!\n", service_id
, t
);
2382 fatalf("parse_wccp2_service_info: id %d: unknown option '%s'\n", service_id
, t
);
2386 /* Check everything is set */
2387 if (priority
== -1) {
2388 fatalf("parse_wccp2_service_info: service %d: no priority defined (valid: 0..255)!\n", service_id
);
2391 if (protocol
== -1) {
2392 fatalf("parse_wccp2_service_info: service %d: no protocol defined (valid: tcp or udp)!\n", service_id
);
2395 if (!(flags
& WCCP2_SERVICE_PORTS_DEFINED
)) {
2396 fatalf("parse_wccp2_service_info: service %d: no ports defined!\n", service_id
);
2399 /* rightio! now we can update */
2400 wccp2_update_service(srv
, WCCP2_SERVICE_DYNAMIC
, service_id
, priority
,
2401 protocol
, flags
, portlist
);
2407 dump_wccp2_service_info(StoreEntry
* e
, const char *label
, void *)
2409 struct wccp2_service_list_t
*srv
;
2411 srv
= wccp2_service_list_head
;
2413 while (srv
!= nullptr) {
2414 debugs(80, 3, "dump_wccp2_service_info: id " << srv
->info
.service_id
<< " (type " << srv
->info
.service
<< ")");
2416 /* We don't need to spit out information for standard services */
2418 if (srv
->info
.service
== WCCP2_SERVICE_STANDARD
) {
2419 debugs(80, 3, "dump_wccp2_service_info: id " << srv
->info
.service_id
<< ": standard service, not dumping info");
2426 storeAppendPrintf(e
, "%s %d", label
, srv
->info
.service_id
);
2429 storeAppendPrintf(e
, " priority=%d", srv
->info
.service_priority
);
2432 flags
= ntohl(srv
->info
.service_flags
);
2436 storeAppendPrintf(e
, " flags=");
2438 if (flags
& WCCP2_SERVICE_SRC_IP_HASH
) {
2439 storeAppendPrintf(e
, "src_ip_hash");
2443 if (flags
& WCCP2_SERVICE_DST_IP_HASH
) {
2444 storeAppendPrintf(e
, "%sdst_ip_hash", comma
? "," : "");
2448 if (flags
& WCCP2_SERVICE_SRC_PORT_HASH
) {
2449 storeAppendPrintf(e
, "%ssource_port_hash", comma
? "," : "");
2453 if (flags
& WCCP2_SERVICE_DST_PORT_HASH
) {
2454 storeAppendPrintf(e
, "%sdst_port_hash", comma
? "," : "");
2458 if (flags
& WCCP2_SERVICE_PORTS_DEFINED
) {
2459 storeAppendPrintf(e
, "%sports_defined", comma
? "," : "");
2463 if (flags
& WCCP2_SERVICE_PORTS_SOURCE
) {
2464 storeAppendPrintf(e
, "%sports_source", comma
? "," : "");
2468 if (flags
& WCCP2_SERVICE_SRC_IP_ALT_HASH
) {
2469 storeAppendPrintf(e
, "%ssrc_ip_alt_hash", comma
? "," : "");
2473 if (flags
& WCCP2_SERVICE_DST_IP_ALT_HASH
) {
2474 storeAppendPrintf(e
, "%ssrc_ip_alt_hash", comma
? "," : "");
2478 if (flags
& WCCP2_SERVICE_SRC_PORT_ALT_HASH
) {
2479 storeAppendPrintf(e
, "%ssrc_port_alt_hash", comma
? "," : "");
2483 if (flags
& WCCP2_SERVICE_DST_PORT_ALT_HASH
) {
2484 storeAppendPrintf(e
, "%sdst_port_alt_hash", comma
? "," : "");
2485 //comma = true; // uncomment if more options added
2492 if (srv
->info
.port0
!= 0) {
2493 storeAppendPrintf(e
, " ports=%d", ntohs(srv
->info
.port0
));
2497 if (srv
->info
.port1
!= 0) {
2498 storeAppendPrintf(e
, "%s%d", comma
? "," : "ports=", ntohs(srv
->info
.port1
));
2502 if (srv
->info
.port2
!= 0) {
2503 storeAppendPrintf(e
, "%s%d", comma
? "," : "ports=", ntohs(srv
->info
.port2
));
2507 if (srv
->info
.port3
!= 0) {
2508 storeAppendPrintf(e
, "%s%d", comma
? "," : "ports=", ntohs(srv
->info
.port3
));
2512 if (srv
->info
.port4
!= 0) {
2513 storeAppendPrintf(e
, "%s%d", comma
? "," : "ports=", ntohs(srv
->info
.port4
));
2517 if (srv
->info
.port5
!= 0) {
2518 storeAppendPrintf(e
, "%s%d", comma
? "," : "ports=", ntohs(srv
->info
.port5
));
2522 if (srv
->info
.port6
!= 0) {
2523 storeAppendPrintf(e
, "%s%d", comma
? "," : "ports=", ntohs(srv
->info
.port6
));
2527 if (srv
->info
.port7
!= 0) {
2528 storeAppendPrintf(e
, "%s%d", comma
? "," : "ports=", ntohs(srv
->info
.port7
));
2529 // comma = true; // uncomment if more options are added
2533 storeAppendPrintf(e
, " protocol=%s", (srv
->info
.service_protocol
== IPPROTO_TCP
) ? "tcp" : "udp");
2535 storeAppendPrintf(e
, "\n");
2541 /* Sort the cache list by doing a "selection sort" by IP address */
2543 wccp2SortCacheList(struct wccp2_cache_list_t
*head
)
2545 struct wccp2_cache_list_t tmp
;
2546 struct wccp2_cache_list_t
*this_item
;
2547 struct wccp2_cache_list_t
*find_item
;
2548 struct wccp2_cache_list_t
*next_lowest
;
2550 /* Go through each position in the list one at a time */
2551 for (this_item
= head
; this_item
->next
; this_item
= this_item
->next
) {
2552 /* Find the item with the lowest IP */
2553 next_lowest
= this_item
;
2555 for (find_item
= this_item
; find_item
->next
; find_item
= find_item
->next
) {
2556 if (find_item
->cache_ip
.s_addr
< next_lowest
->cache_ip
.s_addr
) {
2557 next_lowest
= find_item
;
2560 /* Swap if we need to */
2561 if (next_lowest
!= this_item
) {
2562 /* First make a copy of the current item */
2563 memcpy(&tmp
, this_item
, sizeof(struct wccp2_cache_list_t
));
2565 /* Next update the pointers to maintain the linked list */
2566 tmp
.next
= next_lowest
->next
;
2567 next_lowest
->next
= this_item
->next
;
2569 /* Finally copy the updated items to their correct location */
2570 memcpy(this_item
, next_lowest
, sizeof(struct wccp2_cache_list_t
));
2571 memcpy(next_lowest
, &tmp
, sizeof(struct wccp2_cache_list_t
));
2577 free_wccp2_service_info(void *)
2580 #endif /* USE_WCCPv2 */