]> git.ipfire.org Git - thirdparty/squid.git/blame - src/wccp2.cc
Useful error message from NCSA when DES blocked.
[thirdparty/squid.git] / src / wccp2.cc
CommitLineData
0b0cfcf2 1/*
0b0cfcf2 2 * DEBUG: section 80 WCCP Support
47c151e0 3 * AUTHOR: Steven Wilton
0b0cfcf2 4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
26ac0430 21 *
0b0cfcf2 22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26ac0430 26 *
0b0cfcf2 27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 */
bf8fe701 32
0b0cfcf2 33#include "squid.h"
c898ced1
AJ
34
35#if USE_WCCPv2
36
0b0cfcf2 37#include "comm.h"
f9b72e0c 38#include "comm/Connection.h"
d841c88d 39#include "comm/Loops.h"
27bc2077 40#include "compat/strsep.h"
a553a5a3 41#include "event.h"
96d89ea0 42#include "ip/Address.h"
0b0cfcf2 43#include "Parsing.h"
a553a5a3 44#include "Store.h"
bef81ea5 45#include "SwapDir.h"
0b0cfcf2 46
5fefeec1 47#if HAVE_NETDB_H
0b0cfcf2 48#include <netdb.h>
5fefeec1 49#endif
0b0cfcf2 50
51#define WCCP_PORT 2048
0b0cfcf2 52#define WCCP_RESPONSE_SIZE 12448
0b0cfcf2 53#define WCCP_BUCKETS 256
54
0b0cfcf2 55static int theWccp2Connection = -1;
56static int wccp2_connected = 0;
57
58static PF wccp2HandleUdp;
59static EVH wccp2HereIam;
60static EVH wccp2AssignBuckets;
61
62/* KDW WCCP V2 */
c1f55201 63
c898ced1
AJ
64#define WCCP2_HASH_ASSIGNMENT 0x00
65#define WCCP2_MASK_ASSIGNMENT 0x01
0b0cfcf2 66
c898ced1
AJ
67#define WCCP2_NONE_SECURITY_LEN 0
68#define WCCP2_MD5_SECURITY_LEN 16
0b0cfcf2 69
c898ced1
AJ
70/* Useful defines */
71#define WCCP2_NUMPORTS 8
72#define WCCP2_PASSWORD_LEN 8
0b0cfcf2 73
0b0cfcf2 74
c898ced1
AJ
75/* WCCPv2 Pakcet format structures */
76/* Defined in draft-wilson-wccp-v2-12-oct-2001.txt */
0b0cfcf2 77
0b0cfcf2 78
c898ced1
AJ
79/** \interface WCCPv2_Protocol
80 * Generic header struct
81 */
82struct wccp2_item_header_t {
83 uint16_t type;
84 uint16_t length;
85};
0b0cfcf2 86
c898ced1
AJ
87/* item type values */
88#define WCCP2_SECURITY_INFO 0
89#define WCCP2_SERVICE_INFO 1
90#define WCCP2_ROUTER_ID_INFO 2
91#define WCCP2_WC_ID_INFO 3
92#define WCCP2_RTR_VIEW_INFO 4
0b0cfcf2 93#define WCCP2_WC_VIEW_INFO 5
0b0cfcf2 94#define WCCP2_REDIRECT_ASSIGNMENT 6
0b0cfcf2 95#define WCCP2_QUERY_INFO 7
0b0cfcf2 96#define WCCP2_CAPABILITY_INFO 8
b7d249f9 97#define WCCP2_ALT_ASSIGNMENT 13
b7d249f9 98#define WCCP2_ASSIGN_MAP 14
b7d249f9 99#define WCCP2_COMMAND_EXTENSION 15
100
0b0cfcf2 101
b7d249f9 102
c898ced1
AJ
103/** \interface WCCPv2_Protocol
104 * Sect 5.5 WCCP Message Header
105 */
106struct wccp2_message_header_t {
0b0cfcf2 107 uint32_t type;
108 uint16_t version;
c898ced1
AJ
109#define WCCP2_VERSION 0x200
110
0b0cfcf2 111 uint16_t length;
112};
c898ced1 113static struct wccp2_message_header_t wccp2_here_i_am_header;
0b0cfcf2 114
c898ced1
AJ
115/* message types */
116#define WCCP2_HERE_I_AM 10
117#define WCCP2_I_SEE_YOU 11
118#define WCCP2_REDIRECT_ASSIGN 12
119#define WCCP2_REMOVAL_QUERY 13
0b0cfcf2 120
0b0cfcf2 121
c898ced1
AJ
122/** \interface WCCPv2_Protocol
123 * Sect 5.6.1 Security Info Component
124 *
125 * Basic security Header. Matches "no security" case exactly.
126 */
26ac0430 127struct wccp2_security_none_t {
0b0cfcf2 128 uint16_t security_type;
129 uint16_t security_length;
130 uint32_t security_option;
131};
132
c898ced1
AJ
133/* security options */
134#define WCCP2_NO_SECURITY 0
135#define WCCP2_MD5_SECURITY 1
136
137
138/** \interface WCCPv2_Protocol
139 * Sect 5.6.1 Security Info Component
140 *
141 * Extended security section. Matches "MD5 security" type exactly.
142 * Including the security header.
143 */
26ac0430 144struct wccp2_security_md5_t {
0b0cfcf2 145 uint16_t security_type;
146 uint16_t security_length;
147 uint32_t security_option;
148 uint8_t security_implementation[WCCP2_MD5_SECURITY_LEN];
149};
150
151/* Service info struct */
152
c898ced1
AJ
153/** \interface WCCPv2_Protocol
154 * Sect 5.6.2 Service Info Component
155 */
26ac0430 156struct wccp2_service_info_t {
0b0cfcf2 157 uint16_t service_type;
158 uint16_t service_length;
159 uint8_t service;
160 uint8_t service_id;
161 uint8_t service_priority;
162 uint8_t service_protocol;
163 uint32_t service_flags;
164 uint16_t port0;
165 uint16_t port1;
166 uint16_t port2;
167 uint16_t port3;
168 uint16_t port4;
169 uint16_t port5;
170 uint16_t port6;
171 uint16_t port7;
172};
c898ced1
AJ
173/* services */
174#define WCCP2_SERVICE_STANDARD 0
175#define WCCP2_SERVICE_DYNAMIC 1
0b0cfcf2 176
c898ced1
AJ
177/* service IDs */
178#define WCCP2_SERVICE_ID_HTTP 0x00
179
180/* service flags */
181#define WCCP2_SERVICE_SRC_IP_HASH 0x1
182#define WCCP2_SERVICE_DST_IP_HASH 0x2
183#define WCCP2_SERVICE_SRC_PORT_HASH 0x4
184#define WCCP2_SERVICE_DST_PORT_HASH 0x8
185#define WCCP2_SERVICE_PORTS_DEFINED 0x10
186#define WCCP2_SERVICE_PORTS_SOURCE 0x20
187#define WCCP2_SERVICE_SRC_IP_ALT_HASH 0x100
188#define WCCP2_SERVICE_DST_IP_ALT_HASH 0x200
189#define WCCP2_SERVICE_SRC_PORT_ALT_HASH 0x400
190#define WCCP2_SERVICE_DST_PORT_ALT_HASH 0x800
191
192
193/* TODO the following structures need to be re-defined for correct full operation.
194 wccp2_cache_identity_element needs to be merged as a sub-struct of
195 wccp2_identity_info_t (identity_type); which frees up the identifty info
196 structures so mask_assigment_data_element can become variable length
197 and cope with multiple fail-over caches hanging off one router.
198 */
199
200/** \interface WCCPv2_Protocol
201 * Sect 5.7.2 Web-Cache Identity Element
202 */
26ac0430 203struct wccp2_cache_identity_info_t {
cc192b50 204 struct in_addr addr;
0b0cfcf2 205 uint16_t hash_revision;
85442144 206 uint16_t bits;
c898ced1
AJ
207//#define WCCP2_HASH_ASSIGNMENT_DATA 0x0
208
af6a12ee 209 /* 5.7.2 Hash Assignment Data Element */
c898ced1 210 char buckets[32]; /* Draft indicates 8x 32-bit buckets but it's just a mask so doesn't matter how we define. */
0b0cfcf2 211 uint16_t weight;
212 uint16_t status;
213};
214
c898ced1
AJ
215/** \interface WCCPv2_Protocol
216 * Sect 5.6.4 Web-Cache Identity Info Component
217 */
26ac0430 218struct wccp2_identity_info_t {
0b0cfcf2 219 uint16_t cache_identity_type;
220 uint16_t cache_identity_length;
221
222 struct wccp2_cache_identity_info_t cache_identity;
223};
224
225static struct wccp2_identity_info_t wccp2_identity_info;
226
c898ced1
AJ
227/** \interface WCCPv2_Protocol
228 * Sect 5.7.7 Mask Element
229 */
230struct wccp2_mask_element_t {
b7d249f9 231 uint32_t source_ip_mask;
232 uint32_t dest_ip_mask;
233 uint16_t source_port_mask;
234 uint16_t dest_port_mask;
c898ced1 235 uint32_t number_values;
b7d249f9 236};
237
b7d249f9 238
c898ced1
AJ
239/** \interface WCCPv2_Protocol
240 * Sect 5.7.2 Web-Cache Identity Element
241 */
242struct wccp2_cache_mask_identity_info_t {
243 struct in_addr addr;
244 uint16_t hash_revision;
245 uint16_t bits;
85442144 246#define WCCP2_MASK_ASSIGNMENT_DATA (0x2)
c898ced1 247
af6a12ee
AJ
248 /* Sect 5.7.2 Mask Assignment Data Element
249 *
250 * NP: draft specifies a variable-length set of keys here.
251 * the following fields only matche the special case Squid sends outbound (single-cache).
252 */
253 uint32_t mask_element_count;
c898ced1 254
af6a12ee
AJ
255 /* Sect 5.7.6 Mask/Value Set Element */
256 /* special case: single mask element. no values. */
c898ced1
AJ
257 struct wccp2_mask_element_t mask;
258
af6a12ee 259 /* Sect 5.7.2 Mask Assignment Data Element */
c898ced1
AJ
260 uint16_t weight;
261 uint16_t status;
262};
263
264/** \interface WCCPv2_Protocol
265 * Sect 5.6.4 Web-Cache Identity Info Component
266 */
26ac0430 267struct wccp2_mask_identity_info_t {
b7d249f9 268 uint16_t cache_identity_type;
269 uint16_t cache_identity_length;
270
271 struct wccp2_cache_mask_identity_info_t cache_identity;
272};
273
274static struct wccp2_mask_identity_info_t wccp2_mask_identity_info;
275
c898ced1
AJ
276/** \interface WCCPv2_Protocol
277 * Sect 5.6.5 Router View Info Component
278 * Sect 5.6.6 Web Cache View Info Component
279 *
280 * first three fields. (shared by both view components)
281 */
26ac0430 282struct wccp2_cache_view_header_t {
0b0cfcf2 283 uint16_t cache_view_type;
284 uint16_t cache_view_length;
285 uint32_t cache_view_version;
286};
287
288static struct wccp2_cache_view_header_t wccp2_cache_view_header;
289
e041b6d2 290/// \interface WCCPv2_Protocol
c898ced1 291/* NP: special-case 5.6.5 or 5.6.6 * View Info when no routers or caches are advertised? */
26ac0430 292struct wccp2_cache_view_info_t {
0b0cfcf2 293 uint32_t num_routers;
294 uint32_t num_caches;
295};
296
297static struct wccp2_cache_view_info_t wccp2_cache_view_info;
298
c898ced1
AJ
299/** \interface WCCPv2_Protocol
300 * Sect 5.7.1 Router ID Element
301 */
26ac0430 302struct wccp2_router_id_element_t {
cc192b50 303 struct in_addr router_address;
0b0cfcf2 304 uint32_t received_id;
305};
306
307static struct wccp2_router_id_element_t wccp2_router_id_element;
308
bd8d3feb 309/** \interface WCCPv2_Protocol
c898ced1
AJ
310 * Sect 5.6.9 Capabilities Info Component
311 */
26ac0430 312struct wccp2_capability_info_header_t {
0b0cfcf2 313 uint16_t capability_info_type;
314 uint16_t capability_info_length;
c898ced1 315 /* dynamic length capabilities list */
0b0cfcf2 316};
317
318static struct wccp2_capability_info_header_t wccp2_capability_info_header;
319
c898ced1
AJ
320/** \interface WCCPv2_Protocol
321 * 5.7.5 Capability Element
322 */
26ac0430 323struct wccp2_capability_element_t {
0b0cfcf2 324 uint16_t capability_type;
325 uint16_t capability_length;
326 uint32_t capability_value;
327};
0b0cfcf2 328static struct wccp2_capability_element_t wccp2_capability_element;
329
c898ced1
AJ
330/* capability types */
331#define WCCP2_CAPABILITY_FORWARDING_METHOD 0x01
332#define WCCP2_CAPABILITY_ASSIGNMENT_METHOD 0x02
333#define WCCP2_CAPABILITY_RETURN_METHOD 0x03
b7d249f9 334
c898ced1
AJ
335/* capability values */
336#define WCCP2_METHOD_GRE 0x00000001
337#define WCCP2_METHOD_L2 0x00000002
af6a12ee 338/* when type=WCCP2_CAPABILITY_FORWARDING_METHOD */
c898ced1
AJ
339#define WCCP2_FORWARDING_METHOD_GRE WCCP2_METHOD_GRE
340#define WCCP2_FORWARDING_METHOD_L2 WCCP2_METHOD_L2
af6a12ee 341/* when type=WCCP2_CAPABILITY_ASSIGNMENT_METHOD */
c898ced1
AJ
342#define WCCP2_ASSIGNMENT_METHOD_HASH 0x00000001
343#define WCCP2_ASSIGNMENT_METHOD_MASK 0x00000002
af6a12ee 344/* when type=WCCP2_CAPABILITY_RETURN_METHOD */
c898ced1
AJ
345#define WCCP2_PACKET_RETURN_METHOD_GRE WCCP2_METHOD_GRE
346#define WCCP2_PACKET_RETURN_METHOD_L2 WCCP2_METHOD_L2
b7d249f9 347
b7d249f9 348
c898ced1
AJ
349
350/** \interface WCCPv2_Protocol
351 * 5.7.8 Value Element
352 */
26ac0430 353struct wccp2_value_element_t {
b7d249f9 354 uint32_t source_ip_value;
355 uint32_t dest_ip_value;
356 uint16_t source_port_value;
357 uint16_t dest_port_value;
358
cc192b50 359 struct in_addr cache_ip;
b7d249f9 360};
0b0cfcf2 361
362/* RECEIVED PACKET STRUCTURE */
363
c898ced1
AJ
364/** \interface WCCPv2_Protocol
365 * 5.2 'I See You' Message
366 */
26ac0430 367struct wccp2_i_see_you_t {
0b0cfcf2 368 uint32_t type;
369 uint16_t version;
370 uint16_t length;
371 char data[WCCP_RESPONSE_SIZE];
372};
373
374static struct wccp2_i_see_you_t wccp2_i_see_you;
375
c898ced1
AJ
376/** \interface WCCPv2_Protocol
377 * 5.7.4 Router Assignment Element
378 */
26ac0430 379struct wccp2_router_assign_element_t {
cc192b50 380 struct in_addr router_address;
0b0cfcf2 381 uint32_t received_id;
382 uint32_t change_number;
383};
384
0b0cfcf2 385/* Router identity struct */
386
c898ced1
AJ
387/** \interface WCCPv2_Protocol
388 * 5.6.3 Router Identity Info Component (partial)
389 */
26ac0430 390struct router_identity_info_t {
0b0cfcf2 391
392 struct wccp2_item_header_t header;
393
394 struct wccp2_router_id_element_t router_id_element;
395
2adffc6f 396 struct in_addr router_address;
0b0cfcf2 397 uint32_t number_caches;
c898ced1 398 /* dynamic list of cache IP addresses */
0b0cfcf2 399};
400
b7d249f9 401/* The received packet for a mask assignment is unusual */
402
c898ced1
AJ
403/** \interface WCCPv2_Protocol
404 * Sect 5.7.7 Mask Element ???
405 * see code below. apparently the supposed IP address at position num1 can be equal to 3.
406 */
26ac0430 407struct cache_mask_info_t {
cc192b50 408 struct in_addr addr;
b7d249f9 409 uint32_t num1;
410 uint32_t num2;
411 uint32_t num3;
412};
413
c898ced1
AJ
414/** \interface WCCPv2_Protocol
415 * 5.7.3 Assignment Key Element
416 */
26ac0430 417struct assignment_key_t {
cc192b50 418 struct in_addr master_ip;
0b0cfcf2 419 uint32_t master_number;
420};
421
c898ced1
AJ
422/** \interface WCCPv2_Protocol
423 * 5.6.5 Router View Info Component (first three fields)
424 */
26ac0430 425struct router_view_t {
0b0cfcf2 426 struct wccp2_item_header_t header;
427 uint32_t change_number;
0b0cfcf2 428 struct assignment_key_t assignment_key;
c898ced1 429 /* dynamic lists of routers and caches elided */
0b0cfcf2 430};
431
0b0cfcf2 432/* Lists used to keep track of caches, routers and services */
433
e041b6d2 434/// \interface WCCPv2_Protocol
26ac0430 435struct wccp2_cache_list_t {
0b0cfcf2 436
cc192b50 437 struct in_addr cache_ip;
0b0cfcf2 438
f16fbc82 439 int weight;
440
0b0cfcf2 441 struct wccp2_cache_list_t *next;
442};
443
e041b6d2 444/// \interface WCCPv2_Protocol
26ac0430 445struct wccp2_router_list_t {
0b0cfcf2 446
447 struct wccp2_router_id_element_t *info;
448
cc192b50 449 struct in_addr local_ip;
0b0cfcf2 450
cc192b50 451 struct in_addr router_sendto_address;
0b0cfcf2 452 uint32_t member_change;
453 uint32_t num_caches;
454
455 struct wccp2_cache_list_t cache_list_head;
456
457 struct wccp2_router_list_t *next;
458};
459
460static int wccp2_numrouters;
461
e041b6d2 462/// \interface WCCPv2_Protocol
26ac0430 463struct wccp2_service_list_t {
0b0cfcf2 464
465 struct wccp2_service_info_t info;
466 uint32_t num_routers;
467
468 struct wccp2_router_list_t router_list_head;
469 int lowest_ip;
470 uint32_t change_num;
471
b7d249f9 472 char *wccp2_identity_info_ptr;
0b0cfcf2 473
474 struct wccp2_security_md5_t *security_info;
475
476 struct wccp2_service_info_t *service_info;
477 char wccp_packet[WCCP_RESPONSE_SIZE];
478 size_t wccp_packet_size;
479
480 struct wccp2_service_list_t *next;
481 char wccp_password[WCCP2_PASSWORD_LEN + 1]; /* hold the trailing C-string NUL */
482 uint32_t wccp2_security_type;
483};
484
485static struct wccp2_service_list_t *wccp2_service_list_head = NULL;
486
26ac0430 487int empty_portlist[WCCP2_NUMPORTS] = {0, 0, 0, 0, 0, 0, 0, 0};
0b0cfcf2 488
c898ced1
AJ
489/* END WCCP V2 PROTOCL TYPES DEFINITION */
490
0b0cfcf2 491void wccp2_add_service_list(int service, int service_id, int service_priority,
492 int service_proto, int service_flags, int ports[], int security_type, char *password);
264096e2 493static void wccp2SortCacheList(struct wccp2_cache_list_t *head);
0b0cfcf2 494
495/*
496 * The functions used during startup:
497 * wccp2Init
498 * wccp2ConnectionOpen
499 * wccp2ConnectionClose
500 */
501
502static void
503wccp2InitServices(void)
504{
bf8fe701 505 debugs(80, 5, "wccp2InitServices: called");
0b0cfcf2 506}
507
508static void
0b0cfcf2 509wccp2_update_service(struct wccp2_service_list_t *srv, int service,
510 int service_id, int service_priority, int service_proto, int service_flags,
511 int ports[])
512{
513 /* XXX check what needs to be wrapped in htons()! */
514 srv->info.service = service;
515 srv->info.service_id = service_id;
516 srv->info.service_priority = service_priority;
517 srv->info.service_protocol = service_proto;
518 srv->info.service_flags = htonl(service_flags);
519 srv->info.port0 = htons(ports[0]);
520 srv->info.port1 = htons(ports[1]);
521 srv->info.port2 = htons(ports[2]);
522 srv->info.port3 = htons(ports[3]);
523 srv->info.port4 = htons(ports[4]);
524 srv->info.port5 = htons(ports[5]);
525 srv->info.port6 = htons(ports[6]);
526 srv->info.port7 = htons(ports[7]);
527}
528
529void
530wccp2_add_service_list(int service, int service_id, int service_priority,
531 int service_proto, int service_flags, int ports[], int security_type,
532 char *password)
533{
534
535 struct wccp2_service_list_t *wccp2_service_list_ptr;
536
537 wccp2_service_list_ptr = (wccp2_service_list_t *) xcalloc(1, sizeof(struct wccp2_service_list_t));
538
bf8fe701 539 debugs(80, 5, "wccp2_add_service_list: added service id " << service_id);
0b0cfcf2 540
541 /* XXX check what needs to be wrapped in htons()! */
542 wccp2_service_list_ptr->info.service_type = htons(WCCP2_SERVICE_INFO);
543
544 wccp2_service_list_ptr->info.service_length = htons(sizeof(struct wccp2_service_info_t) - 4);
545 wccp2_service_list_ptr->change_num = 0;
546 wccp2_update_service(wccp2_service_list_ptr, service, service_id,
547 service_priority, service_proto, service_flags, ports);
548 wccp2_service_list_ptr->wccp2_security_type = security_type;
a302ad3c 549 memset(wccp2_service_list_ptr->wccp_password, 0, WCCP2_PASSWORD_LEN + 1);
0b0cfcf2 550 strncpy(wccp2_service_list_ptr->wccp_password, password, WCCP2_PASSWORD_LEN);
551 /* add to linked list - XXX this should use the Squid dlink* routines! */
552 wccp2_service_list_ptr->next = wccp2_service_list_head;
553 wccp2_service_list_head = wccp2_service_list_ptr;
554}
555
556static struct wccp2_service_list_t *
e1381638 557wccp2_get_service_by_id(int service, int service_id) {
0b0cfcf2 558
559 struct wccp2_service_list_t *p;
560
561 p = wccp2_service_list_head;
562
26ac0430 563 while (p != NULL) {
0b0cfcf2 564 if (p->info.service == service && p->info.service_id == service_id) {
565 return p;
566 }
567
568 p = p->next;
569 }
570
571 return NULL;
572}
573
574/*
575 * Update the md5 security header, if possible
576 *
577 * Returns: 1 if we set it, 0 if not (eg, no security section, or non-md5)
578 */
579static char
580wccp2_update_md5_security(char *password, char *ptr, char *packet, int len)
581{
09aabd84 582 uint8_t md5_digest[16];
0b0cfcf2 583 char pwd[WCCP2_PASSWORD_LEN];
c3031d67 584 SquidMD5_CTX M;
0b0cfcf2 585
586 struct wccp2_security_md5_t *ws;
587
bf8fe701 588 debugs(80, 5, "wccp2_update_md5_security: called");
0b0cfcf2 589
590 /* The password field, for the MD5 hash, needs to be 8 bytes and NUL padded. */
a302ad3c 591 memset(pwd, 0, sizeof(pwd));
0b0cfcf2 592 strncpy(pwd, password, sizeof(pwd));
593
594 ws = (struct wccp2_security_md5_t *) ptr;
595 assert(ntohs(ws->security_type) == WCCP2_SECURITY_INFO);
596 /* Its the security part */
597
598 if (ntohl(ws->security_option) != WCCP2_MD5_SECURITY) {
bf8fe701 599 debugs(80, 5, "wccp2_update_md5_security: this service ain't md5'ing, abort");
0b0cfcf2 600 return 0;
601 }
602
603 /* And now its the MD5 section! */
604 /* According to the draft, the MD5 security hash is the combination of
605 * the 8-octet password (padded w/ NUL bytes) and the entire WCCP packet,
606 * including the WCCP message header. The WCCP security implementation
607 * area should be zero'ed before calculating the MD5 hash.
608 */
609 /* XXX eventually we should be able to kill md5_digest and blit it directly in */
a302ad3c 610 memset(ws->security_implementation, 0, sizeof(ws->security_implementation));
0b0cfcf2 611
c3031d67 612 SquidMD5Init(&M);
0b0cfcf2 613
c3031d67 614 SquidMD5Update(&M, pwd, 8);
0b0cfcf2 615
c3031d67 616 SquidMD5Update(&M, packet, len);
0b0cfcf2 617
c3031d67 618 SquidMD5Final(md5_digest, &M);
0b0cfcf2 619
620 memcpy(ws->security_implementation, md5_digest, sizeof(md5_digest));
621
622 /* Finished! */
623 return 1;
624}
625
626
627/*
628 * Check the given WCCP2 packet against the given password.
629 */
630static char
631
632wccp2_check_security(struct wccp2_service_list_t *srv, char *security, char *packet, int len)
633{
634
635 struct wccp2_security_md5_t *ws = (struct wccp2_security_md5_t *) security;
09aabd84 636 uint8_t md5_digest[16], md5_challenge[16];
0b0cfcf2 637 char pwd[WCCP2_PASSWORD_LEN];
c3031d67 638 SquidMD5_CTX M;
0b0cfcf2 639
640 /* Make sure the security type matches what we expect */
641
26ac0430 642 if (ntohl(ws->security_option) != srv->wccp2_security_type) {
bf8fe701 643 debugs(80, 1, "wccp2_check_security: received packet has the wrong security option");
0b0cfcf2 644 return 0;
645 }
646
26ac0430 647 if (srv->wccp2_security_type == WCCP2_NO_SECURITY) {
0b0cfcf2 648 return 1;
649 }
650
26ac0430 651 if (srv->wccp2_security_type != WCCP2_MD5_SECURITY) {
bf8fe701 652 debugs(80, 1, "wccp2_check_security: invalid security option");
0b0cfcf2 653 return 0;
654 }
655
656 /* If execution makes it here then we have an MD5 security */
657
658 /* The password field, for the MD5 hash, needs to be 8 bytes and NUL padded. */
a302ad3c 659 memset(pwd, 0, sizeof(pwd));
0b0cfcf2 660
661 strncpy(pwd, srv->wccp_password, sizeof(pwd));
662
663 /* Take a copy of the challenge: we need to NUL it before comparing */
664 memcpy(md5_challenge, ws->security_implementation, 16);
665
a302ad3c 666 memset(ws->security_implementation, 0, sizeof(ws->security_implementation));
0b0cfcf2 667
c3031d67 668 SquidMD5Init(&M);
0b0cfcf2 669
c3031d67 670 SquidMD5Update(&M, pwd, 8);
0b0cfcf2 671
c3031d67 672 SquidMD5Update(&M, packet, len);
0b0cfcf2 673
c3031d67 674 SquidMD5Final(md5_digest, &M);
0b0cfcf2 675
676 return (memcmp(md5_digest, md5_challenge, 16) == 0);
677}
678
679
680void
681wccp2Init(void)
682{
b7ac5457 683 Ip::Address_list *s;
0b0cfcf2 684 char *ptr;
b7d249f9 685 uint32_t service_flags;
0b0cfcf2 686
687 struct wccp2_service_list_t *service_list_ptr;
688
689 struct wccp2_router_list_t *router_list_ptr;
690
691 struct wccp2_security_md5_t wccp2_security_md5;
692
bf8fe701 693 debugs(80, 5, "wccp2Init: Called");
0b0cfcf2 694
695 if (wccp2_connected == 1)
696 return;
697
698 wccp2_numrouters = 0;
699
700 /* Calculate the number of routers configured in the config file */
701 for (s = Config.Wccp2.router; s; s = s->next) {
cc192b50 702 if (!s->s.IsAnyAddr()) {
0b0cfcf2 703 /* Increment the counter */
704 wccp2_numrouters++;
705 }
706 }
707
708 if (wccp2_numrouters == 0) {
709 return;
710 }
711
712 /* Initialise the list of services */
713 wccp2InitServices();
714
715 service_list_ptr = wccp2_service_list_head;
716
717 while (service_list_ptr != NULL) {
718 /* Set up our list pointers */
719 router_list_ptr = &service_list_ptr->router_list_head;
720
721 /* start the wccp header */
722 wccp2_here_i_am_header.type = htonl(WCCP2_HERE_I_AM);
723 wccp2_here_i_am_header.version = htons(WCCP2_VERSION);
724 wccp2_here_i_am_header.length = 0;
725 ptr = service_list_ptr->wccp_packet + sizeof(wccp2_here_i_am_header);
726
727 /* add the security section */
728 /* XXX this is ugly */
729
730 if (service_list_ptr->wccp2_security_type == WCCP2_MD5_SECURITY) {
731 wccp2_security_md5.security_option = htonl(WCCP2_MD5_SECURITY);
732
733 wccp2_security_md5.security_length = htons(sizeof(struct wccp2_security_md5_t) - 4);
734 } else if (service_list_ptr->wccp2_security_type == WCCP2_NO_SECURITY) {
735 wccp2_security_md5.security_option = htonl(WCCP2_NO_SECURITY);
736 /* XXX I hate magic length numbers! */
737 wccp2_security_md5.security_length = htons(4);
738 } else {
739 fatalf("Bad WCCP2 security type\n");
740 }
741
742 wccp2_here_i_am_header.length += ntohs(wccp2_security_md5.security_length) + 4;
743 assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
744 wccp2_security_md5.security_type = htons(WCCP2_SECURITY_INFO);
745
746 service_list_ptr->security_info = (struct wccp2_security_md5_t *) ptr;
747
748 if (service_list_ptr->wccp2_security_type == WCCP2_MD5_SECURITY) {
41d00cd3 749 memcpy(ptr, &wccp2_security_md5, sizeof(struct wccp2_security_md5_t));
0b0cfcf2 750 ptr += sizeof(struct wccp2_security_md5_t);
751 } else {
752 /* assume NONE, and XXX I hate magic length numbers */
41d00cd3 753 memcpy(ptr, &wccp2_security_md5, 8);
0b0cfcf2 754 ptr += 8;
755 }
756
757 /* Add the service info section */
758
759 wccp2_here_i_am_header.length += sizeof(struct wccp2_service_info_t);
760
761 assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
762
41d00cd3 763 memcpy(ptr, &service_list_ptr->info, sizeof(struct wccp2_service_info_t));
0b0cfcf2 764
765 service_list_ptr->service_info = (struct wccp2_service_info_t *) ptr;
766
767 ptr += sizeof(struct wccp2_service_info_t);
768
769 /* Add the cache identity section */
770
b7d249f9 771 switch (Config.Wccp2.assignment_method) {
0b0cfcf2 772
b7d249f9 773 case WCCP2_ASSIGNMENT_METHOD_HASH:
0b0cfcf2 774
b7d249f9 775 wccp2_here_i_am_header.length += sizeof(struct wccp2_identity_info_t);
776 assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
777 wccp2_identity_info.cache_identity_type = htons(WCCP2_WC_ID_INFO);
778 wccp2_identity_info.cache_identity_length = htons(sizeof(wccp2_identity_info.cache_identity));
cc192b50 779 memset(&wccp2_identity_info.cache_identity.addr, '\0', sizeof(struct in_addr));
b7d249f9 780 memset(&wccp2_identity_info.cache_identity.hash_revision, '\0', sizeof(wccp2_identity_info.cache_identity.hash_revision));
781 memset(&wccp2_identity_info.cache_identity.bits, '\0', sizeof(wccp2_identity_info.cache_identity.bits));
782 memset(&wccp2_identity_info.cache_identity.buckets, '\0', sizeof(wccp2_identity_info.cache_identity.buckets));
783 wccp2_identity_info.cache_identity.weight = htons(Config.Wccp2.weight);
784 memset(&wccp2_identity_info.cache_identity.status, '\0', sizeof(wccp2_identity_info.cache_identity.status));
0b0cfcf2 785
41d00cd3 786 memcpy(ptr, &wccp2_identity_info, sizeof(struct wccp2_identity_info_t));
b7d249f9 787 service_list_ptr->wccp2_identity_info_ptr = ptr;
0b0cfcf2 788
b7d249f9 789 ptr += sizeof(struct wccp2_identity_info_t);
790 break;
0b0cfcf2 791
b7d249f9 792 case WCCP2_ASSIGNMENT_METHOD_MASK:
793
794 wccp2_here_i_am_header.length += sizeof(struct wccp2_mask_identity_info_t);
795 assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
796 wccp2_mask_identity_info.cache_identity_type = htons(WCCP2_WC_ID_INFO);
797 wccp2_mask_identity_info.cache_identity_length = htons(sizeof(wccp2_mask_identity_info.cache_identity));
cc192b50 798 memset(&wccp2_mask_identity_info.cache_identity.addr, '\0', sizeof(struct in_addr));
85442144 799 wccp2_mask_identity_info.cache_identity.bits = htons(WCCP2_MASK_ASSIGNMENT_DATA);
c898ced1 800 wccp2_mask_identity_info.cache_identity.mask_element_count = htonl(1);
b7d249f9 801 service_flags = ntohl(service_list_ptr->service_info->service_flags);
802
c898ced1
AJ
803 memset(&wccp2_mask_identity_info.cache_identity.mask, 0, sizeof(struct wccp2_mask_element_t));
804
b7d249f9 805 if ((service_flags & WCCP2_SERVICE_SRC_IP_HASH) || (service_flags & WCCP2_SERVICE_SRC_IP_ALT_HASH)) {
c898ced1 806 wccp2_mask_identity_info.cache_identity.mask.source_ip_mask = htonl(0x00001741);
b7d249f9 807 } 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)) {
c898ced1 808 wccp2_mask_identity_info.cache_identity.mask.dest_ip_mask = htonl(0x00001741);
b7d249f9 809 } else if ((service_flags & WCCP2_SERVICE_SRC_PORT_HASH) || (service_flags & WCCP2_SERVICE_SRC_PORT_ALT_HASH)) {
c898ced1 810 wccp2_mask_identity_info.cache_identity.mask.source_port_mask = htons(0x1741);
b7d249f9 811 } else if ((service_flags & WCCP2_SERVICE_DST_PORT_HASH) || (service_flags & WCCP2_SERVICE_DST_PORT_ALT_HASH)) {
c898ced1 812 wccp2_mask_identity_info.cache_identity.mask.dest_port_mask = htons(0x1741);
b7d249f9 813 } else {
814 fatalf("Unknown service hash method\n");
815 }
0b0cfcf2 816
c898ced1
AJ
817 wccp2_mask_identity_info.cache_identity.weight = 0;
818 wccp2_mask_identity_info.cache_identity.status = 0;
0b0cfcf2 819
41d00cd3 820 memcpy(ptr, &wccp2_mask_identity_info, sizeof(struct wccp2_mask_identity_info_t));
b7d249f9 821 service_list_ptr->wccp2_identity_info_ptr = ptr;
0b0cfcf2 822
b7d249f9 823 ptr += sizeof(struct wccp2_mask_identity_info_t);
824 break;
0b0cfcf2 825
b7d249f9 826 default:
827 fatalf("Unknown Wccp2 assignment method\n");
828 }
0b0cfcf2 829
830 /* Add the cache view section */
831 wccp2_here_i_am_header.length += sizeof(wccp2_cache_view_header);
832
833 assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
834
835 wccp2_cache_view_header.cache_view_type = htons(WCCP2_WC_VIEW_INFO);
836
837 wccp2_cache_view_header.cache_view_length = htons(sizeof(wccp2_cache_view_header) - 4 +
838 sizeof(wccp2_cache_view_info) + (wccp2_numrouters * sizeof(wccp2_router_id_element)));
839
840 wccp2_cache_view_header.cache_view_version = htonl(1);
841
41d00cd3 842 memcpy(ptr, &wccp2_cache_view_header, sizeof(wccp2_cache_view_header));
0b0cfcf2 843
844 ptr += sizeof(wccp2_cache_view_header);
845
846 /* Add the number of routers to the packet */
847 wccp2_here_i_am_header.length += sizeof(service_list_ptr->num_routers);
848
849 assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
850
851 service_list_ptr->num_routers = htonl(wccp2_numrouters);
852
41d00cd3 853 memcpy(ptr, &service_list_ptr->num_routers, sizeof(service_list_ptr->num_routers));
0b0cfcf2 854
855 ptr += sizeof(service_list_ptr->num_routers);
856
857 /* Add each router. Keep this functionality here to make sure the received_id can be updated in the packet */
858 for (s = Config.Wccp2.router; s; s = s->next) {
cc192b50 859 if (!s->s.IsAnyAddr()) {
0b0cfcf2 860
861 wccp2_here_i_am_header.length += sizeof(struct wccp2_router_id_element_t);
862 assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
863
864 /* Add a pointer to the router list for this router */
865
866 router_list_ptr->info = (struct wccp2_router_id_element_t *) ptr;
cc192b50 867 s->s.GetInAddr(router_list_ptr->info->router_address);
0b0cfcf2 868 router_list_ptr->info->received_id = htonl(0);
cc192b50 869 s->s.GetInAddr(router_list_ptr->router_sendto_address);
0b0cfcf2 870 router_list_ptr->member_change = htonl(0);
871
872 /* Build the next struct */
873
874 router_list_ptr->next = (wccp2_router_list_t*) xcalloc(1, sizeof(struct wccp2_router_list_t));
875
876 /* update the pointer */
877 router_list_ptr = router_list_ptr->next;
878 router_list_ptr->next = NULL;
879
880 /* no need to copy memory - we've just set the values directly in the packet above */
881
882 ptr += sizeof(struct wccp2_router_id_element_t);
883 }
884 }
885
886 /* Add the number of caches (0) */
887 wccp2_here_i_am_header.length += sizeof(wccp2_cache_view_info.num_caches);
888
889 assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
890
891 wccp2_cache_view_info.num_caches = htonl(0);
892
41d00cd3 893 memcpy(ptr, &wccp2_cache_view_info.num_caches, sizeof(wccp2_cache_view_info.num_caches));
0b0cfcf2 894
895 ptr += sizeof(wccp2_cache_view_info.num_caches);
896
897 /* Add the extra capability header */
898 wccp2_here_i_am_header.length += sizeof(wccp2_capability_info_header);
899
900 assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
901
902 wccp2_capability_info_header.capability_info_type = htons(WCCP2_CAPABILITY_INFO);
903
c1f55201 904 wccp2_capability_info_header.capability_info_length = htons(3 * sizeof(wccp2_capability_element));
0b0cfcf2 905
41d00cd3 906 memcpy(ptr, &wccp2_capability_info_header, sizeof(wccp2_capability_info_header));
0b0cfcf2 907
908 ptr += sizeof(wccp2_capability_info_header);
909
910 /* Add the forwarding method */
911 wccp2_here_i_am_header.length += sizeof(wccp2_capability_element);
912
913 assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
914
915 wccp2_capability_element.capability_type = htons(WCCP2_CAPABILITY_FORWARDING_METHOD);
916
917 wccp2_capability_element.capability_length = htons(sizeof(wccp2_capability_element.capability_value));
918
919 wccp2_capability_element.capability_value = htonl(Config.Wccp2.forwarding_method);
920
41d00cd3 921 memcpy(ptr, &wccp2_capability_element, sizeof(wccp2_capability_element));
0b0cfcf2 922
923 ptr += sizeof(wccp2_capability_element);
924
c1f55201 925 /* Add the assignment method */
926 wccp2_here_i_am_header.length += sizeof(wccp2_capability_element);
927
928 assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
929
930 wccp2_capability_element.capability_type = htons(WCCP2_CAPABILITY_ASSIGNMENT_METHOD);
931
932 wccp2_capability_element.capability_length = htons(sizeof(wccp2_capability_element.capability_value));
933
b7d249f9 934 wccp2_capability_element.capability_value = htonl(Config.Wccp2.assignment_method);
c1f55201 935
41d00cd3 936 memcpy(ptr, &wccp2_capability_element, sizeof(wccp2_capability_element));
c1f55201 937
938 ptr += sizeof(wccp2_capability_element);
939
0b0cfcf2 940 /* Add the return method */
941 wccp2_here_i_am_header.length += sizeof(wccp2_capability_element);
942
943 assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
944
945 wccp2_capability_element.capability_type = htons(WCCP2_CAPABILITY_RETURN_METHOD);
946
947 wccp2_capability_element.capability_length = htons(sizeof(wccp2_capability_element.capability_value));
948
949 wccp2_capability_element.capability_value = htonl(Config.Wccp2.return_method);
950
41d00cd3 951 memcpy(ptr, &wccp2_capability_element, sizeof(wccp2_capability_element));
0b0cfcf2 952
953 ptr += sizeof(wccp2_capability_element);
954
955 /* Finally, fix the total length to network order, and copy to the appropriate memory blob */
956 wccp2_here_i_am_header.length = htons(wccp2_here_i_am_header.length);
957
958 memcpy(&service_list_ptr->wccp_packet, &wccp2_here_i_am_header, sizeof(wccp2_here_i_am_header));
959
960 service_list_ptr->wccp_packet_size = ntohs(wccp2_here_i_am_header.length) + sizeof(wccp2_here_i_am_header);
961
962 /* Add the event if everything initialised correctly */
51676485 963 debugs(80,3,"wccp2Init: scheduled 'HERE_I_AM' message to " << wccp2_numrouters << "routers.");
0b0cfcf2 964 if (wccp2_numrouters) {
965 if (!eventFind(wccp2HereIam, NULL)) {
966 eventAdd("wccp2HereIam", wccp2HereIam, NULL, 1, 1);
af6a12ee 967 } else
51676485 968 debugs(80,3,"wccp2Init: skip duplicate 'HERE_I_AM'.");
0b0cfcf2 969 }
970
971 service_list_ptr = service_list_ptr->next;
972 }
973}
974
975void
976wccp2ConnectionOpen(void)
977{
c1f55201 978 struct sockaddr_in router, local, null;
0b0cfcf2 979 socklen_t local_len, router_len;
980
981 struct wccp2_service_list_t *service_list_ptr;
982
983 struct wccp2_router_list_t *router_list_ptr;
984
bf8fe701 985 debugs(80, 5, "wccp2ConnectionOpen: Called");
0b0cfcf2 986
987 if (wccp2_numrouters == 0 || !wccp2_service_list_head) {
bf8fe701 988 debugs(80, 2, "WCCPv2 Disabled.");
0b0cfcf2 989 return;
990 }
991
26ac0430 992 if ( !Config.Wccp2.address.SetIPv4() ) {
cc192b50 993 debugs(80, 0, "WCCPv2 Disabled. " << Config.Wccp2.address << " is not an IPv4 address.");
994 return;
995 }
996
997 Config.Wccp2.address.SetPort(WCCP_PORT);
31be869c 998 theWccp2Connection = comm_open_listener(SOCK_DGRAM,
04f7fd38
AJ
999 0,
1000 Config.Wccp2.address,
1001 COMM_NONBLOCKING,
1002 "WCCPv2 Socket");
0b0cfcf2 1003
1004 if (theWccp2Connection < 0)
1005 fatal("Cannot open WCCP Port");
1006
85d25b95 1007#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
1008 {
1009 int i = IP_PMTUDISC_DONT;
1010 setsockopt(theWccp2Connection, SOL_IP, IP_MTU_DISCOVER, &i, sizeof i);
1011 }
1012
1013#endif
d841c88d 1014 Comm::SetSelect(theWccp2Connection, COMM_SELECT_READ, wccp2HandleUdp, NULL, 0);
0b0cfcf2 1015
cc192b50 1016 debugs(80, 1, "Accepting WCCPv2 messages on port " << WCCP_PORT << ", FD " << theWccp2Connection << ".");
bf8fe701 1017 debugs(80, 1, "Initialising all WCCPv2 lists");
0b0cfcf2 1018
1019 /* Initialise all routers on all services */
c1f55201 1020 memset(&null, 0, sizeof(null));
1021
1022 null.sin_family = AF_UNSPEC;
1023
0b0cfcf2 1024 service_list_ptr = wccp2_service_list_head;
1025
1026 while (service_list_ptr != NULL) {
1027 for (router_list_ptr = &service_list_ptr->router_list_head; router_list_ptr->next != NULL; router_list_ptr = router_list_ptr->next) {
1028 router_len = sizeof(router);
1029 memset(&router, '\0', router_len);
1030 router.sin_family = AF_INET;
cc192b50 1031 router.sin_port = htons(WCCP_PORT);
0b0cfcf2 1032 router.sin_addr = router_list_ptr->router_sendto_address;
1033
1034 if (connect(theWccp2Connection, (struct sockaddr *) &router, router_len))
1035 fatal("Unable to connect WCCP out socket");
1036
1037 local_len = sizeof(local);
1038
1039 memset(&local, '\0', local_len);
1040
1041 if (getsockname(theWccp2Connection, (struct sockaddr *) &local, &local_len))
1042 fatal("Unable to getsockname on WCCP out socket");
1043
1044 router_list_ptr->local_ip = local.sin_addr;
1045
c1f55201 1046 /* Disconnect the sending socket. Note: FreeBSD returns error
1047 * but disconnects anyway so we have to just assume it worked
1048 */
1049 if (wccp2_numrouters > 1)
c1f55201 1050 connect(theWccp2Connection, (struct sockaddr *) &null, router_len);
0b0cfcf2 1051 }
1052
1053 service_list_ptr = service_list_ptr->next;
1054 }
1055
0b0cfcf2 1056 wccp2_connected = 1;
1057}
1058
1059void
1060wccp2ConnectionClose(void)
1061{
1062
1063 struct wccp2_service_list_t *service_list_ptr;
1064
1065 struct wccp2_service_list_t *service_list_ptr_next;
1066
1067 struct wccp2_router_list_t *router_list_ptr;
1068
1069 struct wccp2_router_list_t *router_list_next;
1070
1071 struct wccp2_cache_list_t *cache_list_ptr;
1072
1073 struct wccp2_cache_list_t *cache_list_ptr_next;
1074
1075 if (wccp2_connected == 0) {
1076 return;
1077 }
1078
1079 if (theWccp2Connection > -1) {
76dc4ca3 1080 debugs(80, 1, "FD " << theWccp2Connection << " Closing WCCPv2 socket");
0b0cfcf2 1081 comm_close(theWccp2Connection);
1082 theWccp2Connection = -1;
1083 }
1084
1085 /* for each router on each service send a packet */
1086 service_list_ptr = wccp2_service_list_head;
1087
1088 while (service_list_ptr != NULL) {
1089 for (router_list_ptr = &service_list_ptr->router_list_head; router_list_ptr != NULL; router_list_ptr = router_list_next) {
1090 for (cache_list_ptr = &router_list_ptr->cache_list_head; cache_list_ptr; cache_list_ptr = cache_list_ptr_next) {
1091 cache_list_ptr_next = cache_list_ptr->next;
1092
1093 if (cache_list_ptr != &router_list_ptr->cache_list_head) {
1094 xfree(cache_list_ptr);
1095 } else {
1096
1097 memset(cache_list_ptr, '\0', sizeof(struct wccp2_cache_list_t));
1098 }
1099 }
1100
1101 router_list_next = router_list_ptr->next;
1102
1103 if (router_list_ptr != &service_list_ptr->router_list_head) {
1104 xfree(router_list_ptr);
1105 } else {
1106
1107 memset(router_list_ptr, '\0', sizeof(struct wccp2_router_list_t));
1108 }
1109 }
1110
1111 service_list_ptr_next = service_list_ptr->next;
1112 xfree(service_list_ptr);
1113 service_list_ptr = service_list_ptr_next;
1114 }
1115
1116 wccp2_service_list_head = NULL;
1117 eventDelete(wccp2HereIam, NULL);
1118 eventDelete(wccp2AssignBuckets, NULL);
1119 eventDelete(wccp2HereIam, NULL);
1120 wccp2_connected = 0;
1121}
1122
1123/*
1124 * Functions for handling the requests.
1125 */
1126
1127/*
1128 * Accept the UDP packet
1129 */
1130static void
1131wccp2HandleUdp(int sock, void *not_used)
1132{
1133
1134 struct wccp2_service_list_t *service_list_ptr;
1135
1136 struct wccp2_router_list_t *router_list_ptr;
1137
1138 struct wccp2_cache_list_t *cache_list_ptr;
1139
1140 struct wccp2_cache_list_t *cache_list_ptr_next;
1141
1142 /* These structs form the parts of the packet */
1143
1144 struct wccp2_item_header_t *header = NULL;
1145
1146 struct wccp2_security_none_t *security_info = NULL;
1147
1148 struct wccp2_service_info_t *service_info = NULL;
1149
1150 struct router_identity_info_t *router_identity_info = NULL;
1151
1152 struct router_view_t *router_view_header = NULL;
1153
b7d249f9 1154 struct wccp2_cache_mask_identity_info_t *cache_mask_identity = NULL;
1155
1156 struct cache_mask_info_t *cache_mask_info = NULL;
1157
0b0cfcf2 1158 struct wccp2_cache_identity_info_t *cache_identity = NULL;
1159
1160 struct wccp2_capability_info_header_t *router_capability_header = NULL;
1161
1162 struct wccp2_capability_element_t *router_capability_element;
1163
1164 struct sockaddr_in from;
1165
2adffc6f 1166 struct in_addr cache_address;
0b0cfcf2 1167 int len, found;
1168 short int data_length, offset;
1169 uint32_t tmp;
1170 char *ptr;
1171 int num_caches;
0b0cfcf2 1172
bf8fe701 1173 debugs(80, 6, "wccp2HandleUdp: Called.");
0b0cfcf2 1174
d841c88d 1175 Comm::SetSelect(sock, COMM_SELECT_READ, wccp2HandleUdp, NULL, 0);
0b0cfcf2 1176
26ac0430 1177 /* FIXME INET6 : drop conversion boundary */
b7ac5457 1178 Ip::Address from_tmp;
0b0cfcf2 1179
1180 len = comm_udp_recvfrom(sock,
1181 &wccp2_i_see_you,
1182 WCCP_RESPONSE_SIZE,
1183 0,
cc192b50 1184 from_tmp);
26ac0430
AJ
1185 /* FIXME INET6 : drop conversion boundary */
1186 from_tmp.GetSockAddr(from);
0b0cfcf2 1187
1188 if (len < 0)
1189 return;
1190
1191 if (ntohs(wccp2_i_see_you.version) != WCCP2_VERSION)
1192 return;
1193
1194 if (ntohl(wccp2_i_see_you.type) != WCCP2_I_SEE_YOU)
1195 return;
1196
bf8fe701 1197 debugs(80, 3, "Incoming WCCPv2 I_SEE_YOU length " << ntohs(wccp2_i_see_you.length) << ".");
0b0cfcf2 1198
1199 /* Record the total data length */
1200 data_length = ntohs(wccp2_i_see_you.length);
1201
1202 offset = 0;
1203
1204 if (data_length > len) {
bf8fe701 1205 debugs(80, 1, "ERROR: Malformed WCCPv2 packet claiming it's bigger than received data");
0b0cfcf2 1206 return;
1207 }
1208
1209 /* Go through the data structure */
1210 while (data_length > offset) {
1211
af6a12ee 1212 char *data = wccp2_i_see_you.data;
c6ba4504
HN
1213
1214 header = (struct wccp2_item_header_t *) &data[offset];
0b0cfcf2 1215
1216 switch (ntohs(header->type)) {
1217
1218 case WCCP2_SECURITY_INFO:
1219
1220 if (security_info != NULL) {
bf8fe701 1221 debugs(80, 1, "Duplicate security definition");
0b0cfcf2 1222 return;
1223 }
1224
1225 security_info = (struct wccp2_security_none_t *) &wccp2_i_see_you.data[offset];
1226 break;
1227
1228 case WCCP2_SERVICE_INFO:
1229
1230 if (service_info != NULL) {
bf8fe701 1231 debugs(80, 1, "Duplicate service_info definition");
0b0cfcf2 1232 return;
1233 }
1234
1235 service_info = (struct wccp2_service_info_t *) &wccp2_i_see_you.data[offset];
1236 break;
1237
1238 case WCCP2_ROUTER_ID_INFO:
1239
1240 if (router_identity_info != NULL) {
bf8fe701 1241 debugs(80, 1, "Duplicate router_identity_info definition");
0b0cfcf2 1242 return;
1243 }
1244
1245 router_identity_info = (struct router_identity_info_t *) &wccp2_i_see_you.data[offset];
1246 break;
1247
1248 case WCCP2_RTR_VIEW_INFO:
1249
1250 if (router_view_header != NULL) {
bf8fe701 1251 debugs(80, 1, "Duplicate router_view definition");
0b0cfcf2 1252 return;
1253 }
1254
1255 router_view_header = (struct router_view_t *) &wccp2_i_see_you.data[offset];
1256 break;
1257
1258 case WCCP2_CAPABILITY_INFO:
1259
1260 if (router_capability_header != NULL) {
bf8fe701 1261 debugs(80, 1, "Duplicate router_capability definition");
0b0cfcf2 1262 return;
1263 }
1264
1265 router_capability_header = (struct wccp2_capability_info_header_t *) &wccp2_i_see_you.data[offset];
c1f55201 1266 break;
0b0cfcf2 1267
b7d249f9 1268 /* Nothing to do for the types below */
1269
1270 case WCCP2_ASSIGN_MAP:
40228366 1271 case WCCP2_REDIRECT_ASSIGNMENT:
b7d249f9 1272 break;
1273
0b0cfcf2 1274 default:
bf8fe701 1275 debugs(80, 1, "Unknown record type in WCCPv2 Packet (" << ntohs(header->type) << ").");
0b0cfcf2 1276 }
1277
1278 offset += sizeof(struct wccp2_item_header_t);
1279 offset += ntohs(header->length);
1280
1281 if (offset > data_length) {
bf8fe701 1282 debugs(80, 1, "Error: WCCPv2 packet tried to tell us there is data beyond the end of the packet");
0b0cfcf2 1283 return;
1284 }
1285 }
1286
1287 if ((security_info == NULL) || (service_info == NULL) || (router_identity_info == NULL) || (router_view_header == NULL)) {
bf8fe701 1288 debugs(80, 1, "Incomplete WCCPv2 Packet");
0b0cfcf2 1289 return;
1290 }
1291
bf8fe701 1292 debugs(80, 5, "Complete packet received");
0b0cfcf2 1293
1294 /* Check that the service in the packet is configured on this router */
1295 service_list_ptr = wccp2_service_list_head;
1296
1297 while (service_list_ptr != NULL) {
1298 if (service_info->service_id == service_list_ptr->service_info->service_id) {
1299 break;
1300 }
1301
1302 service_list_ptr = service_list_ptr->next;
1303 }
1304
1305 if (service_list_ptr == NULL) {
bf8fe701 1306 debugs(80, 1, "WCCPv2 Unknown service received from router (" << service_info->service_id << ")");
0b0cfcf2 1307 return;
1308 }
1309
1310 if (ntohl(security_info->security_option) != ntohl(service_list_ptr->security_info->security_option)) {
6013702c 1311 debugs(80, 1, "Invalid security option in WCCPv2 Packet (" << ntohl(security_info->security_option) << " vs " << ntohl(service_list_ptr->security_info->security_option) << ").");
0b0cfcf2 1312 return;
1313 }
1314
1315 if (!wccp2_check_security(service_list_ptr, (char *) security_info, (char *) &wccp2_i_see_you, len)) {
bf8fe701 1316 debugs(80, 1, "Received WCCPv2 Packet failed authentication");
0b0cfcf2 1317 return;
1318 }
1319
1320 /* Check that the router address is configured on this router */
1321 for (router_list_ptr = &service_list_ptr->router_list_head; router_list_ptr->next != NULL; router_list_ptr = router_list_ptr->next) {
1322 if (router_list_ptr->router_sendto_address.s_addr == from.sin_addr.s_addr)
1323 break;
1324 }
1325
1326 if (router_list_ptr->next == NULL) {
bf8fe701 1327 debugs(80, 1, "WCCPv2 Packet received from unknown router");
0b0cfcf2 1328 return;
1329 }
1330
1331 /* Set the router id */
1332 router_list_ptr->info->router_address = router_identity_info->router_id_element.router_address;
1333
1334 /* Increment the received id in the packet */
1335 if (ntohl(router_list_ptr->info->received_id) != ntohl(router_identity_info->router_id_element.received_id)) {
6013702c 1336 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) << ".");
0b0cfcf2 1337 router_list_ptr->info->received_id = router_identity_info->router_id_element.received_id;
1338 }
1339
1340 /* TODO: check return/forwarding methods */
1341 if (router_capability_header == NULL) {
c1f55201 1342 if ((Config.Wccp2.return_method != WCCP2_PACKET_RETURN_METHOD_GRE) || (Config.Wccp2.forwarding_method != WCCP2_FORWARDING_METHOD_GRE)) {
bf8fe701 1343 debugs(80, 1, "wccp2HandleUdp: fatal error - A WCCP router does not support the forwarding method specified, only GRE supported");
0b0cfcf2 1344 wccp2ConnectionClose();
1345 return;
1346 }
1347 } else {
0b0cfcf2 1348
c1f55201 1349 char *end = ((char *) router_capability_header) + sizeof(*router_capability_header) + ntohs(router_capability_header->capability_info_length) - sizeof(struct wccp2_capability_info_header_t);
1350
1351 router_capability_element = (struct wccp2_capability_element_t *) (((char *) router_capability_header) + sizeof(*router_capability_header));
0b0cfcf2 1352
c1f55201 1353 while ((char *) router_capability_element <= end) {
0b0cfcf2 1354
1355 switch (ntohs(router_capability_element->capability_type)) {
1356
1357 case WCCP2_CAPABILITY_FORWARDING_METHOD:
1358
c1f55201 1359 if (!(ntohl(router_capability_element->capability_value) & Config.Wccp2.forwarding_method)) {
6013702c 1360 debugs(80, 1, "wccp2HandleUdp: fatal error - A WCCP router has specified a different forwarding method " << ntohl(router_capability_element->capability_value) << ", expected " << Config.Wccp2.forwarding_method);
0b0cfcf2 1361 wccp2ConnectionClose();
1362 return;
1363 }
1364
1365 break;
1366
1367 case WCCP2_CAPABILITY_ASSIGNMENT_METHOD:
c1f55201 1368
b7d249f9 1369 if (!(ntohl(router_capability_element->capability_value) & Config.Wccp2.assignment_method)) {
1370 debugs(80, 1, "wccp2HandleUdp: fatal error - A WCCP router has specified a different assignment method " << ntohl(router_capability_element->capability_value) << ", expected "<< Config.Wccp2.assignment_method);
c1f55201 1371 wccp2ConnectionClose();
1372 return;
1373 }
1374
0b0cfcf2 1375 break;
1376
1377 case WCCP2_CAPABILITY_RETURN_METHOD:
1378
c1f55201 1379 if (!(ntohl(router_capability_element->capability_value) & Config.Wccp2.return_method)) {
6013702c 1380 debugs(80, 1, "wccp2HandleUdp: fatal error - A WCCP router has specified a different return method " << ntohl(router_capability_element->capability_value) << ", expected " << Config.Wccp2.return_method);
0b0cfcf2 1381 wccp2ConnectionClose();
1382 return;
1383 }
1384
1385 break;
1386
1387 default:
bf8fe701 1388 debugs(80, 1, "Unknown capability type in WCCPv2 Packet (" << ntohs(router_capability_element->capability_type) << ").");
0b0cfcf2 1389 }
c1f55201 1390
c898ced1 1391 router_capability_element = (struct wccp2_capability_element_t *) (((char *) router_capability_element) + sizeof(struct wccp2_item_header_t) + ntohs(router_capability_element->capability_length));
0b0cfcf2 1392 }
1393 }
1394
bf8fe701 1395 debugs(80, 5, "Cleaning out cache list");
0b0cfcf2 1396 /* clean out the old cache list */
1397
1398 for (cache_list_ptr = &router_list_ptr->cache_list_head; cache_list_ptr; cache_list_ptr = cache_list_ptr_next) {
1399 cache_list_ptr_next = cache_list_ptr->next;
1400
1401 if (cache_list_ptr != &router_list_ptr->cache_list_head) {
1402 xfree(cache_list_ptr);
1403 }
1404 }
1405
1406 router_list_ptr->num_caches = htonl(0);
1407 num_caches = 0;
1408
1409 /* Check to see if we're the master cache and update the cache list */
1410 found = 0;
1411 service_list_ptr->lowest_ip = 1;
1412 cache_list_ptr = &router_list_ptr->cache_list_head;
1413
1414 /* to find the list of caches, we start at the end of the router view header */
1415
1416 ptr = (char *) (router_view_header) + sizeof(struct router_view_t);
1417
1418 /* Then we read the number of routers */
1419 memcpy(&tmp, ptr, sizeof(tmp));
1420
1421 /* skip the number plus all the ip's */
1422
2adffc6f 1423 ptr += sizeof(tmp) + (ntohl(tmp) * sizeof(struct in_addr));
0b0cfcf2 1424
1425 /* Then read the number of caches */
1426 memcpy(&tmp, ptr, sizeof(tmp));
1427 ptr += sizeof(tmp);
1428
1429 if (ntohl(tmp) != 0) {
1430 /* search through the list of received-from ip addresses */
1431
1432 for (num_caches = 0; num_caches < (int) ntohl(tmp); num_caches++) {
1433 /* Get a copy of the ip */
af6a12ee 1434 memset(&cache_address, 0, sizeof(cache_address)); // Make GCC happy
0b0cfcf2 1435
b7d249f9 1436 switch (Config.Wccp2.assignment_method) {
1437
1438 case WCCP2_ASSIGNMENT_METHOD_HASH:
1439
1440 cache_identity = (struct wccp2_cache_identity_info_t *) ptr;
1441
1442 ptr += sizeof(struct wccp2_cache_identity_info_t);
1443
2adffc6f 1444 memcpy(&cache_address, &cache_identity->addr, sizeof(struct in_addr));
b7d249f9 1445
1446 cache_list_ptr->weight = ntohs(cache_identity->weight);
1447 break;
1448
1449 case WCCP2_ASSIGNMENT_METHOD_MASK:
1450
1451 cache_mask_info = (struct cache_mask_info_t *) ptr;
1452
1453 /* The mask assignment has an undocumented variable length entry here */
1454
1455 if (ntohl(cache_mask_info->num1) == 3) {
1456
1457 cache_mask_identity = (struct wccp2_cache_mask_identity_info_t *) ptr;
1458
1459 ptr += sizeof(struct wccp2_cache_mask_identity_info_t);
0b0cfcf2 1460
2adffc6f 1461 memcpy(&cache_address, &cache_mask_identity->addr, sizeof(struct in_addr));
b7d249f9 1462 } else {
1463
1464 ptr += sizeof(struct cache_mask_info_t);
1465
2adffc6f 1466 memcpy(&cache_address, &cache_mask_info->addr, sizeof(struct in_addr));
b7d249f9 1467 }
0b0cfcf2 1468
b7d249f9 1469 cache_list_ptr->weight = 0;
1470 break;
1471
1472 default:
1473 fatalf("Unknown Wccp2 assignment method\n");
1474 }
0b0cfcf2 1475
1476 /* Update the cache list */
1477 cache_list_ptr->cache_ip = cache_address;
b7d249f9 1478
0b0cfcf2 1479 cache_list_ptr->next = (wccp2_cache_list_t*) xcalloc(1, sizeof(struct wccp2_cache_list_t));
b7d249f9 1480
0b0cfcf2 1481 cache_list_ptr = cache_list_ptr->next;
b7d249f9 1482
0b0cfcf2 1483 cache_list_ptr->next = NULL;
1484
bf8fe701 1485 debugs (80, 5, "checking cache list: (" << std::hex << cache_address.s_addr << ":" << router_list_ptr->local_ip.s_addr << ")");
b7d249f9 1486
0b0cfcf2 1487 /* Check to see if it's the master, or us */
1488
1489 if (cache_address.s_addr == router_list_ptr->local_ip.s_addr) {
1490 found = 1;
1491 }
1492
1493 if (cache_address.s_addr < router_list_ptr->local_ip.s_addr) {
1494 service_list_ptr->lowest_ip = 0;
1495 }
1496 }
1497 } else {
bf8fe701 1498 debugs(80, 5, "Adding ourselves as the only cache");
0b0cfcf2 1499
1500 /* Update the cache list */
1501 cache_list_ptr->cache_ip = router_list_ptr->local_ip;
1502
1503 cache_list_ptr->next = (wccp2_cache_list_t*) xcalloc(1, sizeof(struct wccp2_cache_list_t));
1504 cache_list_ptr = cache_list_ptr->next;
1505 cache_list_ptr->next = NULL;
1506
b7d249f9 1507 service_list_ptr->lowest_ip = 1;
0b0cfcf2 1508 found = 1;
1509 num_caches = 1;
1510 }
1511
264096e2 1512 wccp2SortCacheList(&router_list_ptr->cache_list_head);
1513
0b0cfcf2 1514 router_list_ptr->num_caches = htonl(num_caches);
1515
1516 if ((found == 1) && (service_list_ptr->lowest_ip == 1)) {
1517 if (ntohl(router_view_header->change_number) != router_list_ptr->member_change) {
bf8fe701 1518 debugs(80, 4, "Change detected - queueing up new assignment");
0b0cfcf2 1519 router_list_ptr->member_change = ntohl(router_view_header->change_number);
c1f55201 1520 eventDelete(wccp2AssignBuckets, NULL);
1521 eventAdd("wccp2AssignBuckets", wccp2AssignBuckets, NULL, 15.0, 1);
b7d249f9 1522 } else {
4a7a3d56 1523 debugs(80, 5, "Change not detected (" << ntohl(router_view_header->change_number) << " = " << router_list_ptr->member_change << ")");
0b0cfcf2 1524 }
1525 } else {
b7d249f9 1526 eventDelete(wccp2AssignBuckets, NULL);
bf8fe701 1527 debugs(80, 5, "I am not the lowest ip cache - not assigning buckets");
0b0cfcf2 1528 }
1529}
1530
1531static void
1532wccp2HereIam(void *voidnotused)
1533{
1534
1535 struct wccp2_service_list_t *service_list_ptr;
1536
1537 struct wccp2_router_list_t *router_list_ptr;
1538
b7d249f9 1539 struct wccp2_identity_info_t *wccp2_identity_info_ptr;
1540
1541 struct wccp2_mask_identity_info_t *wccp2_mask_identity_info_ptr;
1542
b7ac5457 1543 Ip::Address router;
0b0cfcf2 1544
bf8fe701 1545 debugs(80, 6, "wccp2HereIam: Called");
0b0cfcf2 1546
1547 if (wccp2_connected == 0) {
bf8fe701 1548 debugs(80, 1, "wccp2HereIam: wccp2 socket closed. Shutting down WCCP2");
0b0cfcf2 1549 return;
1550 }
1551
bef81ea5 1552 /* Wait if store dirs are rebuilding */
1553 if (StoreController::store_dirs_rebuilding && Config.Wccp2.rebuildwait) {
f67332d3 1554 eventAdd("wccp2HereIam", wccp2HereIam, NULL, 1.0, 1);
1555 return;
1556 }
1557
cc192b50 1558 router.SetPort(WCCP_PORT);
0b0cfcf2 1559
1560 /* for each router on each service send a packet */
1561 service_list_ptr = wccp2_service_list_head;
1562
1563 while (service_list_ptr != NULL) {
bf8fe701 1564 debugs(80, 5, "wccp2HereIam: sending to service id " << service_list_ptr->info.service_id);
0b0cfcf2 1565
1566 for (router_list_ptr = &service_list_ptr->router_list_head; router_list_ptr->next != NULL; router_list_ptr = router_list_ptr->next) {
cc192b50 1567 router = router_list_ptr->router_sendto_address;
0b0cfcf2 1568
1569 /* Set the cache id (ip) */
b7d249f9 1570
1571 switch (Config.Wccp2.assignment_method) {
1572
1573 case WCCP2_ASSIGNMENT_METHOD_HASH:
1574
1575 wccp2_identity_info_ptr = (struct wccp2_identity_info_t *) service_list_ptr->wccp2_identity_info_ptr;
1576 wccp2_identity_info_ptr->cache_identity.addr = router_list_ptr->local_ip;
1577 break;
1578
1579 case WCCP2_ASSIGNMENT_METHOD_MASK:
1580
1581 wccp2_mask_identity_info_ptr = (struct wccp2_mask_identity_info_t *) service_list_ptr->wccp2_identity_info_ptr;
1582 wccp2_mask_identity_info_ptr->cache_identity.addr = router_list_ptr->local_ip;
1583 break;
1584
1585 default:
1586 fatalf("Unknown Wccp2 assignment method\n");
1587 }
1588
0b0cfcf2 1589 /* Security update, if needed */
1590
1591 if (service_list_ptr->wccp2_security_type == WCCP2_MD5_SECURITY) {
1592 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);
1593 }
1594
4a7a3d56 1595 debugs(80, 3, "Sending HereIam packet size " << service_list_ptr->wccp_packet_size);
0b0cfcf2 1596 /* Send the packet */
0b0cfcf2 1597
c1f55201 1598 if (wccp2_numrouters > 1) {
1599 comm_udp_sendto(theWccp2Connection,
cc192b50 1600 router,
c1f55201 1601 &service_list_ptr->wccp_packet,
1602 service_list_ptr->wccp_packet_size);
1603 } else {
1604 send(theWccp2Connection,
1605 &service_list_ptr->wccp_packet,
1606 service_list_ptr->wccp_packet_size,
1607 0);
1608 }
0b0cfcf2 1609 }
1610
1611 service_list_ptr = service_list_ptr->next;
1612 }
1613
f67332d3 1614 eventAdd("wccp2HereIam", wccp2HereIam, NULL, 10.0, 1);
0b0cfcf2 1615}
1616
1617static void
1618wccp2AssignBuckets(void *voidnotused)
1619{
1620
1621 struct wccp2_service_list_t *service_list_ptr;
1622
1623 struct wccp2_router_list_t *router_list_ptr;
1624
1625 struct wccp2_cache_list_t *cache_list_ptr;
1626 char wccp_packet[WCCP_RESPONSE_SIZE];
b7d249f9 1627 short int offset, saved_offset, assignment_offset, alt_assignment_offset;
0b0cfcf2 1628
1629 struct sockaddr_in router;
1630 int router_len;
1631 int bucket_counter;
b7d249f9 1632 uint32_t service_flags;
f45dd259 1633 unsigned short port = WCCP_PORT;
0b0cfcf2 1634
1635 /* Packet segments */
1636
c898ced1 1637 struct wccp2_message_header_t *main_header;
0b0cfcf2 1638
1639 struct wccp2_security_md5_t *security = NULL;
1640 /* service from service struct */
1641
1642 struct wccp2_item_header_t *assignment_header;
1643
b7d249f9 1644 struct wccp2_item_header_t *alt_assignment_type_header = NULL;
1645
0b0cfcf2 1646 struct assignment_key_t *assignment_key;
1647 /* number of routers */
1648
1649 struct wccp2_router_assign_element_t *router_assign;
1650 /* number of caches */
1651
2adffc6f 1652 struct in_addr *cache_address;
b7d249f9 1653 /* Alternative assignement mask/values */
1654 int num_maskval;
1655
1656 struct wccp2_mask_element_t *mask_element;
1657
1658 struct wccp2_value_element_t *value_element;
1659 int valuecounter, value;
0b0cfcf2 1660 char *buckets;
1661
b7d249f9 1662 assignment_offset = alt_assignment_offset = 0;
1663
0b0cfcf2 1664 router_len = sizeof(router);
1665 memset(&router, '\0', router_len);
1666 router.sin_family = AF_INET;
1667 router.sin_port = htons(port);
1668
1669 /* Start main header - fill in length later */
1670 offset = 0;
1671
c898ced1 1672 main_header = (struct wccp2_message_header_t *) &wccp_packet[offset];
0b0cfcf2 1673 main_header->type = htonl(WCCP2_REDIRECT_ASSIGN);
1674 main_header->version = htons(WCCP2_VERSION);
1675
bf8fe701 1676 debugs(80, 2, "Running wccp2AssignBuckets");
0b0cfcf2 1677 service_list_ptr = wccp2_service_list_head;
1678
1679 while (service_list_ptr != NULL) {
1680 /* If we're not the lowest, we don't need to worry */
1681
1682 if (service_list_ptr->lowest_ip == 0) {
1683 /* XXX eww */
1684 service_list_ptr = service_list_ptr->next;
1685 continue;
1686 }
1687
1688 /* reset the offset */
1689
c898ced1 1690 offset = sizeof(struct wccp2_message_header_t);
0b0cfcf2 1691
1692 /* build packet header from hereIam packet */
1693 /* Security info */
1694 /* XXX this should be made more generic! */
1695 /* XXX and I hate magic numbers! */
1696 switch (service_list_ptr->wccp2_security_type) {
1697
1698 case WCCP2_NO_SECURITY:
1699
1700 security = (struct wccp2_security_md5_t *) &wccp_packet[offset];
1701 memcpy(security, service_list_ptr->security_info, 8);
1702 offset += 8;
1703 break;
1704
1705 case WCCP2_MD5_SECURITY:
1706
1707 security = (struct wccp2_security_md5_t *) &wccp_packet[offset];
1708
1709 memcpy(security, service_list_ptr->security_info, sizeof(struct wccp2_security_md5_t));
1710
1711 offset += sizeof(struct wccp2_security_md5_t);
1712 break;
1713
1714 default:
1715 fatalf("Unknown Wccp2 security type\n");
1716 }
1717
1718 /* Service info */
1719
1720 memcpy(&wccp_packet[offset], service_list_ptr->service_info, sizeof(struct wccp2_service_info_t));
1721
1722 offset += sizeof(struct wccp2_service_info_t);
1723
1724 /* assignment header - fill in length later */
1725
1726 assignment_header = (struct wccp2_item_header_t *) &wccp_packet[offset];
1727
b7d249f9 1728 switch (Config.Wccp2.assignment_method) {
0b0cfcf2 1729
b7d249f9 1730 case WCCP2_ASSIGNMENT_METHOD_HASH:
1731 assignment_header->type = htons(WCCP2_REDIRECT_ASSIGNMENT);
1732
1733 offset += sizeof(struct wccp2_item_header_t);
1734 assignment_offset = offset;
1735 break;
1736
1737 case WCCP2_ASSIGNMENT_METHOD_MASK:
1738 assignment_header->type = htons(WCCP2_ALT_ASSIGNMENT);
1739
1740 offset += sizeof(struct wccp2_item_header_t);
1741 assignment_offset = offset;
1742
1743 /* The alternative assignment has an extra header, fill in length later */
1744
1745 alt_assignment_type_header = (struct wccp2_item_header_t *) &wccp_packet[offset];
1746 alt_assignment_type_header->type = htons(WCCP2_MASK_ASSIGNMENT);
1747
1748 offset += sizeof(struct wccp2_item_header_t);
1749 alt_assignment_offset = offset;
1750
1751 break;
1752
1753 default:
1754 fatalf("Unknown Wccp2 assignment method\n");
1755 }
0b0cfcf2 1756
1757 /* Assignment key - fill in master ip later */
1758
1759 assignment_key = (struct assignment_key_t *) &wccp_packet[offset];
1760
1761 assignment_key->master_number = htonl(++service_list_ptr->change_num);
1762
1763 offset += sizeof(struct assignment_key_t);
1764
1765 /* Number of routers */
41d00cd3 1766 memcpy(&wccp_packet[offset], &service_list_ptr->num_routers, sizeof(service_list_ptr->num_routers));
0b0cfcf2 1767
1768 offset += sizeof(service_list_ptr->num_routers);
1769
1770 for (router_list_ptr = &service_list_ptr->router_list_head; router_list_ptr->next != NULL; router_list_ptr = router_list_ptr->next) {
1771
1772 /* Add routers */
1773
1774 router_assign = (struct wccp2_router_assign_element_t *) &wccp_packet[offset];
1775 router_assign->router_address = router_list_ptr->info->router_address;
1776 router_assign->received_id = router_list_ptr->info->received_id;
1777 router_assign->change_number = htonl(router_list_ptr->member_change);
1778
1779 offset += sizeof(struct wccp2_router_assign_element_t);
1780 }
1781
1782 saved_offset = offset;
1783
1784 for (router_list_ptr = &service_list_ptr->router_list_head; router_list_ptr->next != NULL; router_list_ptr = router_list_ptr->next) {
b7d249f9 1785 unsigned long *weight = (unsigned long *)xcalloc(sizeof(*weight), ntohl(router_list_ptr->num_caches));
1786 unsigned long total_weight = 0;
1787 int num_caches = ntohl(router_list_ptr->num_caches);
f16fbc82 1788
0b0cfcf2 1789 offset = saved_offset;
1790
b7d249f9 1791 switch (Config.Wccp2.assignment_method) {
1792
1793 case WCCP2_ASSIGNMENT_METHOD_HASH:
1794 /* Number of caches */
41d00cd3 1795 memcpy(&wccp_packet[offset], &router_list_ptr->num_caches, sizeof(router_list_ptr->num_caches));
b7d249f9 1796 offset += sizeof(router_list_ptr->num_caches);
1797
1798 if (num_caches) {
1799 int cache;
1800
1801 for (cache = 0, cache_list_ptr = &router_list_ptr->cache_list_head; cache_list_ptr->next; cache_list_ptr = cache_list_ptr->next, cache++) {
1802 /* add caches */
0b0cfcf2 1803
2adffc6f 1804 cache_address = (struct in_addr *) &wccp_packet[offset];
0b0cfcf2 1805
41d00cd3 1806 memcpy(cache_address, &cache_list_ptr->cache_ip, sizeof(struct in_addr));
b7d249f9 1807 total_weight += cache_list_ptr->weight << 12;
1808 weight[cache] = cache_list_ptr->weight << 12;
0b0cfcf2 1809
2adffc6f 1810 offset += sizeof(struct in_addr);
b7d249f9 1811 }
0b0cfcf2 1812 }
0b0cfcf2 1813
b7d249f9 1814 /* Add buckets */
1815 buckets = (char *) &wccp_packet[offset];
1816
1817 memset(buckets, '\0', WCCP_BUCKETS);
1818
1819 if (num_caches != 0) {
1820 if (total_weight == 0) {
1821 for (bucket_counter = 0; bucket_counter < WCCP_BUCKETS; bucket_counter++) {
1822 buckets[bucket_counter] = (char) (bucket_counter % num_caches);
1823 }
1824 } else {
1825 unsigned long *assigned = (unsigned long *)xcalloc(sizeof(*assigned), num_caches);
1826 unsigned long done = 0;
1827 int cache = -1;
1828 unsigned long per_bucket = total_weight / WCCP_BUCKETS;
1829
1830 for (bucket_counter = 0; bucket_counter < WCCP_BUCKETS; bucket_counter++) {
1831 int n;
1832 unsigned long step;
1833
1834 for (n = num_caches; n; n--) {
1835 cache++;
1836
1837 if (cache >= num_caches)
1838 cache = 0;
1839
1840 if (!weight[cache]) {
1841 n++;
1842 continue;
1843 }
1844
1845 if (assigned[cache] <= done)
1846 break;
1847 }
1848
1849 buckets[bucket_counter] = (char) cache;
1850 step = per_bucket * total_weight / weight[cache];
1851 assigned[cache] += step;
1852 done += per_bucket;
1853 }
1854
1855 safe_free(assigned);
1856 }
1857 }
0b0cfcf2 1858
b7d249f9 1859 offset += (WCCP_BUCKETS * sizeof(char));
1860 safe_free(weight);
1861 break;
0b0cfcf2 1862
b7d249f9 1863 case WCCP2_ASSIGNMENT_METHOD_MASK:
1864 num_maskval = htonl(1);
41d00cd3 1865 memcpy(&wccp_packet[offset], &num_maskval, sizeof(int));
b7d249f9 1866 offset += sizeof(int);
1867
1868 mask_element = (struct wccp2_mask_element_t *) &wccp_packet[offset];
1869 service_flags = ntohl(service_list_ptr->service_info->service_flags);
1870
1871 if ((service_flags & WCCP2_SERVICE_SRC_IP_HASH) || (service_flags & WCCP2_SERVICE_SRC_IP_ALT_HASH)) {
1872 mask_element->source_ip_mask = htonl(0x00001741);
1873 mask_element->dest_ip_mask = 0;
1874 mask_element->source_port_mask = 0;
1875 mask_element->dest_port_mask = 0;
1876 } 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)) {
1877 mask_element->source_ip_mask = 0;
1878 mask_element->dest_ip_mask = htonl(0x00001741);
1879 mask_element->source_port_mask = 0;
1880 mask_element->dest_port_mask = 0;
1881 } else if ((service_flags & WCCP2_SERVICE_SRC_PORT_HASH) || (service_flags & WCCP2_SERVICE_SRC_PORT_ALT_HASH)) {
1882 mask_element->source_ip_mask = 0;
1883 mask_element->dest_ip_mask = 0;
1884 mask_element->source_port_mask = htons(0x1741);
1885 mask_element->dest_port_mask = 0;
1886 } else if ((service_flags & WCCP2_SERVICE_DST_PORT_HASH) || (service_flags & WCCP2_SERVICE_DST_PORT_ALT_HASH)) {
1887 mask_element->source_ip_mask = 0;
1888 mask_element->dest_ip_mask = 0;
1889 mask_element->source_port_mask = 0;
1890 mask_element->dest_port_mask = htons(0x1741);
1891 } else {
1892 fatalf("Unknown service hash method\n");
1893 }
1894
1895 mask_element->number_values = htonl(64);
1896
1897 offset += sizeof(struct wccp2_mask_element_t);
1898
1899 cache_list_ptr = &router_list_ptr->cache_list_head;
1900 value = 0;
1901
1902 for (valuecounter = 0; valuecounter < 64; valuecounter++) {
1903
1904 value_element = (struct wccp2_value_element_t *) &wccp_packet[offset];
1905
25e26086 1906 /* Update the value according the the "correct" formula */
1907
9366bab7 1908 for (; (value & 0x1741) != value; value++) {
25e26086 1909 assert(value <= 0x1741);
1910 }
1911
b7d249f9 1912 if ((service_flags & WCCP2_SERVICE_SRC_IP_HASH) || (service_flags & WCCP2_SERVICE_SRC_IP_ALT_HASH)) {
1913 value_element->source_ip_value = htonl(value);
1914 value_element->dest_ip_value = 0;
1915 value_element->source_port_value = 0;
1916 value_element->dest_port_value = 0;
1917 } 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)) {
1918 value_element->source_ip_value = 0;
1919 value_element->dest_ip_value = htonl(value);
1920 value_element->source_port_value = 0;
1921 value_element->dest_port_value = 0;
1922 } else if ((service_flags & WCCP2_SERVICE_SRC_PORT_HASH) || (service_flags & WCCP2_SERVICE_SRC_PORT_ALT_HASH)) {
1923 value_element->source_ip_value = 0;
1924 value_element->dest_ip_value = 0;
1925 value_element->source_port_value = htons(value);
1926 value_element->dest_port_value = 0;
1927 } else if ((service_flags & WCCP2_SERVICE_DST_PORT_HASH) || (service_flags & WCCP2_SERVICE_DST_PORT_ALT_HASH)) {
1928 value_element->source_ip_value = 0;
1929 value_element->dest_ip_value = 0;
1930 value_element->source_port_value = 0;
1931 value_element->dest_port_value = htons(value);
1932 } else {
1933 fatalf("Unknown service hash method\n");
1934 }
1935
1936 value_element->cache_ip = cache_list_ptr->cache_ip;
1937
1938 offset += sizeof(struct wccp2_value_element_t);
25e26086 1939 value++;
b7d249f9 1940
1941 /* Assign the next value to the next cache */
25e26086 1942
b7d249f9 1943 if ((cache_list_ptr->next) && (cache_list_ptr->next->next))
1944 cache_list_ptr = cache_list_ptr->next;
1945 else
1946 cache_list_ptr = &router_list_ptr->cache_list_head;
1947 }
0b0cfcf2 1948
b7d249f9 1949 /* Fill in length */
1950 alt_assignment_type_header->length = htons(offset - alt_assignment_offset);
1951
1952 break;
1953
1954 default:
1955 fatalf("Unknown Wccp2 assignment method\n");
1956 }
1957
1958 /* Fill in length */
0b0cfcf2 1959
b7d249f9 1960 assignment_header->length = htons(offset - assignment_offset);
0b0cfcf2 1961
1962 /* Fill in assignment key */
1963 assignment_key->master_ip = router_list_ptr->local_ip;
1964
1965 /* finish length */
1966
c898ced1 1967 main_header->length = htons(offset - sizeof(struct wccp2_message_header_t));
0b0cfcf2 1968
1969 /* set the destination address */
1970 router.sin_addr = router_list_ptr->router_sendto_address;
1971
1972 /* Security update, if needed */
1973
1974 if (service_list_ptr->wccp2_security_type == WCCP2_MD5_SECURITY) {
1975 wccp2_update_md5_security(service_list_ptr->wccp_password, (char *) security, wccp_packet, offset);
1976 }
1977
1978 if (ntohl(router_list_ptr->num_caches)) {
1979 /* send packet */
c1f55201 1980
1981 if (wccp2_numrouters > 1) {
26ac0430 1982 /* FIXME INET6 : drop temp conversion */
b7ac5457 1983 Ip::Address tmp_rtr(router);
c1f55201 1984 comm_udp_sendto(theWccp2Connection,
cc192b50 1985 tmp_rtr,
c1f55201 1986 &wccp_packet,
1987 offset);
1988 } else {
1989 send(theWccp2Connection,
1990 &wccp_packet,
1991 offset,
1992 0);
1993 }
0b0cfcf2 1994 }
1995 }
1996
1997 service_list_ptr = service_list_ptr->next;
1998 }
1999}
2000
2001
2002/*
2003 * Configuration option parsing code
2004 */
2005
e313ab0a
AJ
2006/**
2007 * Parse wccp2_return_method and wccp2_forwarding_method options
72e2b394 2008 * they can be '1' aka 'gre' or '2' aka 'l2'
e313ab0a
AJ
2009 * repesenting the integer numeric of the same.
2010 */
2011void
2012parse_wccp2_method(int *method)
2013{
2014 char *t;
2015
2016 /* Snarf the method */
2017 if ((t = strtok(NULL, w_space)) == NULL) {
21ec1b94 2018 debugs(80, DBG_CRITICAL, "wccp2_*_method: missing setting.");
e313ab0a
AJ
2019 self_destruct();
2020 }
2021
2022 /* update configuration if its valid */
451c4786 2023 if (strcmp(t, "gre") == 0 || strcmp(t, "1") == 0) {
21ec1b94 2024 *method = WCCP2_METHOD_GRE;
451c4786 2025 } else if (strcmp(t, "l2") == 0 || strcmp(t, "2") == 0) {
21ec1b94 2026 *method = WCCP2_METHOD_L2;
e313ab0a 2027 } else {
21ec1b94 2028 debugs(80, DBG_CRITICAL, "wccp2_*_method: unknown setting, got " << t );
e313ab0a
AJ
2029 self_destruct();
2030 }
2031}
2032
2033void
2034dump_wccp2_method(StoreEntry * e, const char *label, int v)
2035{
26ac0430 2036 switch (v) {
451c4786
AJ
2037 case WCCP2_METHOD_GRE:
2038 storeAppendPrintf(e, "%s gre\n", label);
e313ab0a 2039 break;
451c4786
AJ
2040 case WCCP2_METHOD_L2:
2041 storeAppendPrintf(e, "%s l2\n", label);
e313ab0a
AJ
2042 break;
2043 default:
21ec1b94 2044 debugs(80, DBG_CRITICAL, "FATAL: WCCPv2 configured method (" << v << ") is not valid.");
e313ab0a
AJ
2045 self_destruct();
2046 }
2047}
2048
2049void
2050free_wccp2_method(int *v)
2051{ }
2052
451c4786
AJ
2053/**
2054 * Parse wccp2_assignment_method option
2055 * they can be '1' aka 'hash' or '2' aka 'mask'
2056 * repesenting the integer numeric of the same.
2057 */
2058void
72e2b394 2059parse_wccp2_amethod(int *method)
451c4786
AJ
2060{
2061 char *t;
2062
2063 /* Snarf the method */
2064 if ((t = strtok(NULL, w_space)) == NULL) {
2065 debugs(80, DBG_CRITICAL, "wccp2_assignment_method: missing setting.");
2066 self_destruct();
2067 }
2068
2069 /* update configuration if its valid */
2070 if (strcmp(t, "hash") == 0 || strcmp(t, "1") == 0) {
2071 *method = WCCP2_ASSIGNMENT_METHOD_HASH;
2072 } else if (strcmp(t, "mask") == 0 || strcmp(t, "2") == 0) {
2073 *method = WCCP2_ASSIGNMENT_METHOD_MASK;
2074 } else {
2075 debugs(80, DBG_CRITICAL, "wccp2_assignment_method: unknown setting, got " << t );
2076 self_destruct();
2077 }
2078}
2079
2080void
2081dump_wccp2_amethod(StoreEntry * e, const char *label, int v)
2082{
26ac0430 2083 switch (v) {
451c4786
AJ
2084 case WCCP2_ASSIGNMENT_METHOD_HASH:
2085 storeAppendPrintf(e, "%s hash\n", label);
2086 break;
2087 case WCCP2_ASSIGNMENT_METHOD_MASK:
2088 storeAppendPrintf(e, "%s mask\n", label);
2089 break;
2090 default:
2091 debugs(80, DBG_CRITICAL, "FATAL: WCCPv2 configured " << label << " (" << v << ") is not valid.");
2092 self_destruct();
2093 }
2094}
2095
2096void
2097free_wccp2_amethod(int *v)
2098{ }
2099
0b0cfcf2 2100/*
2101 * Format:
2102 *
2103 * wccp2_service {standard|dynamic} {id} (password=password)
2104 */
2105void
2106parse_wccp2_service(void *v)
2107{
2108 char *t;
2109 int service = 0;
2110 int service_id = 0;
2111 int security_type = WCCP2_NO_SECURITY;
2112 char wccp_password[WCCP2_PASSWORD_LEN + 1];
2113
2114 if (wccp2_connected == 1) {
bf8fe701 2115 debugs(80, 1, "WCCPv2: Somehow reparsing the configuration without having shut down WCCP! Try reloading squid again.");
0b0cfcf2 2116 return;
2117 }
2118
2119 /* Snarf the type */
2120 if ((t = strtok(NULL, w_space)) == NULL) {
bf8fe701 2121 debugs(80, 0, "wccp2ParseServiceInfo: missing service info type (standard|dynamic)");
0b0cfcf2 2122 self_destruct();
2123 }
2124
2125 if (strcmp(t, "standard") == 0) {
2126 service = WCCP2_SERVICE_STANDARD;
2127 } else if (strcmp(t, "dynamic") == 0) {
2128 service = WCCP2_SERVICE_DYNAMIC;
2129 } else {
bf8fe701 2130 debugs(80, 0, "wccp2ParseServiceInfo: bad service info type (expected standard|dynamic, got " << t << ")");
0b0cfcf2 2131 self_destruct();
2132 }
2133
2134 /* Snarf the ID */
2135 service_id = GetInteger();
2136
2137 if (service_id < 0 || service_id > 255) {
bf8fe701 2138 debugs(80, 0, "wccp2ParseServiceInfo: service info id " << service_id << " is out of range (0..255)");
0b0cfcf2 2139 self_destruct();
2140 }
2141
2142 memset(wccp_password, 0, sizeof(wccp_password));
2143 /* Handle password, if any */
2144
2145 if ((t = strtok(NULL, w_space)) != NULL) {
2146 if (strncmp(t, "password=", 9) == 0) {
2147 security_type = WCCP2_MD5_SECURITY;
2148 strncpy(wccp_password, t + 9, WCCP2_PASSWORD_LEN);
2149 }
2150 }
2151
2152 /* Create a placeholder service record */
2153 wccp2_add_service_list(service, service_id, 0, 0, 0, empty_portlist, security_type, wccp_password);
2154}
2155
2156void
2157dump_wccp2_service(StoreEntry * e, const char *label, void *v)
2158{
2159
2160 struct wccp2_service_list_t *srv;
2161 srv = wccp2_service_list_head;
2162
2163 while (srv != NULL) {
bf8fe701 2164 debugs(80, 3, "dump_wccp2_service: id " << srv->info.service_id << ", type " << srv->info.service);
0b0cfcf2 2165 storeAppendPrintf(e, "%s %s %d", label,
2166 (srv->info.service == WCCP2_SERVICE_DYNAMIC) ? "dynamic" : "standard",
2167 srv->info.service_id);
2168
2169 if (srv->wccp2_security_type == WCCP2_MD5_SECURITY) {
2170 storeAppendPrintf(e, " %s", srv->wccp_password);
2171 }
2172
c208fd0e 2173 storeAppendPrintf(e, "\n");
2174
0b0cfcf2 2175 srv = srv->next;
2176 }
2177}
2178
2179void
2180free_wccp2_service(void *v)
2181{}
2182
2183int
2184check_null_wccp2_service(void *v)
2185{
2186 return !wccp2_service_list_head;
2187}
2188
2189/*
2190 * Format:
2191 *
2192 * wccp2_service_info {id} stuff..
2193 *
2194 * Where stuff is:
2195 *
2196 * + flags=flag,flag,flag..
2197 * + proto=protocol (tcp|udp)
2198 * + ports=port,port,port (up to a max of 8)
2199 * + priority=priority (0->255)
2200 *
2201 * The flags here are:
2202 * src_ip_hash, dst_ip_hash, source_port_hash, dst_port_hash, ports_defined,
2203 * ports_source, src_ip_alt_hash, dst_ip_alt_hash, src_port_alt_hash, dst_port_alt_hash
2204 */
2205static int
2206parse_wccp2_service_flags(char *flags)
2207{
2208 char *tmp, *tmp2;
2209 char *flag;
2210 int retflag = 0;
2211
2212 if (!flags) {
2213 return 0;
2214 }
2215
2216 tmp = xstrdup(flags);
2217 tmp2 = tmp;
2218
2219 flag = strsep(&tmp2, ",");
2220
2221 while (flag) {
2222 if (strcmp(flag, "src_ip_hash") == 0) {
2223 retflag |= WCCP2_SERVICE_SRC_IP_HASH;
2224 } else if (strcmp(flag, "dst_ip_hash") == 0) {
2225 retflag |= WCCP2_SERVICE_DST_IP_HASH;
2226 } else if (strcmp(flag, "source_port_hash") == 0) {
b7d249f9 2227 retflag |= WCCP2_SERVICE_SRC_PORT_HASH;
0b0cfcf2 2228 } else if (strcmp(flag, "dst_port_hash") == 0) {
2229 retflag |= WCCP2_SERVICE_DST_PORT_HASH;
2230 } else if (strcmp(flag, "ports_source") == 0) {
2231 retflag |= WCCP2_SERVICE_PORTS_SOURCE;
2232 } else if (strcmp(flag, "src_ip_alt_hash") == 0) {
2233 retflag |= WCCP2_SERVICE_SRC_IP_ALT_HASH;
2234 } else if (strcmp(flag, "dst_ip_alt_hash") == 0) {
2235 retflag |= WCCP2_SERVICE_DST_IP_ALT_HASH;
2236 } else if (strcmp(flag, "src_port_alt_hash") == 0) {
2237 retflag |= WCCP2_SERVICE_SRC_PORT_ALT_HASH;
2238 } else if (strcmp(flag, "dst_port_alt_hash") == 0) {
2239 retflag |= WCCP2_SERVICE_DST_PORT_ALT_HASH;
2240 } else {
2241 fatalf("Unknown wccp2 service flag: %s\n", flag);
2242 }
2243
2244 flag = strsep(&tmp2, ",");
2245 }
2246
2247 xfree(tmp);
2248 return retflag;
2249}
2250
2251static void
2252parse_wccp2_service_ports(char *options, int portlist[])
2253{
2254 int i = 0;
2255 int p;
2256 char *tmp, *tmp2, *port, *end;
2257
2258 if (!options) {
2259 return;
2260 }
2261
2262 tmp = xstrdup(options);
2263 tmp2 = tmp;
2264
2265 port = strsep(&tmp2, ",");
2266
2267 while (port && i < WCCP2_NUMPORTS) {
2268 p = strtol(port, &end, 0);
2269
2270 if (p < 1 || p > 65535) {
2271 fatalf("parse_wccp2_service_ports: port value '%s' isn't valid (1..65535)\n", port);
2272 }
2273
2274 portlist[i] = p;
2275 i++;
2276 port = strsep(&tmp2, ",");
2277 }
2278
5355e54f 2279 if (i == WCCP2_NUMPORTS && port) {
0b0cfcf2 2280 fatalf("parse_wccp2_service_ports: too many ports (maximum: 8) in list '%s'\n", options);
2281 }
2282
2283 xfree(tmp);
2284}
2285
2286void
2287parse_wccp2_service_info(void *v)
2288{
2289 char *t, *end;
2290 int service_id = 0;
2291 int flags = 0;
2292 int portlist[WCCP2_NUMPORTS];
2293 int protocol = -1; /* IPPROTO_TCP | IPPROTO_UDP */
2294
2295 struct wccp2_service_list_t *srv;
2296 int priority = -1;
2297
2298 if (wccp2_connected == 1) {
bf8fe701 2299 debugs(80, 1, "WCCPv2: Somehow reparsing the configuration without having shut down WCCP! Try reloading squid again.");
0b0cfcf2 2300 return;
2301 }
2302
bf8fe701 2303 debugs(80, 5, "parse_wccp2_service_info: called");
a302ad3c 2304 memset(portlist, 0, sizeof(portlist));
0b0cfcf2 2305 /* First argument: id */
2306 service_id = GetInteger();
2307
2308 if (service_id < 0 || service_id > 255) {
bf8fe701 2309 debugs(80, 1, "parse_wccp2_service_info: invalid service id " << service_id << " (must be between 0 .. 255)");
0b0cfcf2 2310 self_destruct();
2311 }
2312
2313 /* Next: find the (hopefully!) existing service */
2314 srv = wccp2_get_service_by_id(WCCP2_SERVICE_DYNAMIC, service_id);
2315
2316 if (srv == NULL) {
2317 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);
2318 }
2319
2320 /* Next: loop until we don't have any more tokens */
2321 while ((t = strtok(NULL, w_space)) != NULL) {
2322 if (strncmp(t, "flags=", 6) == 0) {
2323 /* XXX eww, string pointer math */
2324 flags = parse_wccp2_service_flags(t + 6);
2325 } else if (strncmp(t, "ports=", 6) == 0) {
2326 parse_wccp2_service_ports(t + 6, portlist);
2327 flags |= WCCP2_SERVICE_PORTS_DEFINED;
2328 } else if (strncmp(t, "protocol=tcp", 12) == 0) {
2329 protocol = IPPROTO_TCP;
2330 } else if (strncmp(t, "protocol=udp", 12) == 0) {
2331 protocol = IPPROTO_UDP;
2332 } else if (strncmp(t, "protocol=", 9) == 0) {
2333 fatalf("parse_wccp2_service_info: id %d: unknown protocol (%s) - must be tcp or udp!\n", service_id, t);
2334 } else if (strncmp(t, "priority=", 9) == 0) {
2335 priority = strtol(t + 9, &end, 0);
2336
2337 if (priority < 0 || priority > 255) {
2338 fatalf("parse_wccp2_service_info: id %d: %s out of range (0..255)!\n", service_id, t);
2339 }
2340 } else {
2341 fatalf("parse_wccp2_service_info: id %d: unknown option '%s'\n", service_id, t);
2342 }
2343 }
2344
2345 /* Check everything is set */
2346 if (priority == -1) {
2347 fatalf("parse_wccp2_service_info: service %d: no priority defined (valid: 0..255)!\n", service_id);
2348 }
2349
2350 if (protocol == -1) {
2351 fatalf("parse_wccp2_service_info: service %d: no protocol defined (valid: tcp or udp)!\n", service_id);
2352 }
2353
2354 if (!(flags & WCCP2_SERVICE_PORTS_DEFINED)) {
2355 fatalf("parse_wccp2_service_info: service %d: no ports defined!\n", service_id);
2356 }
2357
2358 /* rightio! now we can update */
2359 wccp2_update_service(srv, WCCP2_SERVICE_DYNAMIC, service_id, priority,
2360 protocol, flags, portlist);
2361
2362 /* Done! */
2363}
2364
2365void
2366dump_wccp2_service_info(StoreEntry * e, const char *label, void *v)
2367{
2368 char comma;
2369
2370 struct wccp2_service_list_t *srv;
2371 int flags;
2372 srv = wccp2_service_list_head;
2373
2374 while (srv != NULL) {
bf8fe701 2375 debugs(80, 3, "dump_wccp2_service_info: id " << srv->info.service_id << " (type " << srv->info.service << ")");
0b0cfcf2 2376
2377 /* We don't need to spit out information for standard services */
2378
2379 if (srv->info.service == WCCP2_SERVICE_STANDARD) {
bf8fe701 2380 debugs(80, 3, "dump_wccp2_service_info: id " << srv->info.service_id << ": standard service, not dumping info");
2381
0b0cfcf2 2382 /* XXX eww */
2383 srv = srv->next;
2384 continue;
2385 }
2386
2387 storeAppendPrintf(e, "%s %d", label, srv->info.service_id);
2388
2389 /* priority */
2390 storeAppendPrintf(e, " priority=%d", srv->info.service_priority);
2391
2392 /* flags */
2393 flags = ntohl(srv->info.service_flags);
2394
2395 if (flags != 0) {
2396 comma = 0;
2397 storeAppendPrintf(e, " flags=");
2398
2399 if (flags & WCCP2_SERVICE_SRC_IP_HASH) {
2400 storeAppendPrintf(e, "%ssrc_ip_hash", comma ? "," : "");
2401 comma = 1;
2402 }
2403
2404 if (flags & WCCP2_SERVICE_DST_IP_HASH) {
2405 storeAppendPrintf(e, "%sdst_ip_hash", comma ? "," : "");
2406 comma = 1;
2407 }
2408
b7d249f9 2409 if (flags & WCCP2_SERVICE_SRC_PORT_HASH) {
0b0cfcf2 2410 storeAppendPrintf(e, "%ssource_port_hash", comma ? "," : "");
2411 comma = 1;
2412 }
2413
2414 if (flags & WCCP2_SERVICE_DST_PORT_HASH) {
2415 storeAppendPrintf(e, "%sdst_port_hash", comma ? "," : "");
2416 comma = 1;
2417 }
2418
2419 if (flags & WCCP2_SERVICE_PORTS_DEFINED) {
2420 storeAppendPrintf(e, "%sports_defined", comma ? "," : "");
2421 comma = 1;
2422 }
2423
2424 if (flags & WCCP2_SERVICE_PORTS_SOURCE) {
2425 storeAppendPrintf(e, "%sports_source", comma ? "," : "");
2426 comma = 1;
2427 }
2428
2429 if (flags & WCCP2_SERVICE_SRC_IP_ALT_HASH) {
2430 storeAppendPrintf(e, "%ssrc_ip_alt_hash", comma ? "," : "");
2431 comma = 1;
2432 }
2433
2434 if (flags & WCCP2_SERVICE_DST_IP_ALT_HASH) {
2435 storeAppendPrintf(e, "%ssrc_ip_alt_hash", comma ? "," : "");
2436 comma = 1;
2437 }
2438
2439 if (flags & WCCP2_SERVICE_SRC_PORT_ALT_HASH) {
2440 storeAppendPrintf(e, "%ssrc_port_alt_hash", comma ? "," : "");
2441 comma = 1;
2442 }
2443
2444 if (flags & WCCP2_SERVICE_DST_PORT_ALT_HASH) {
2445 storeAppendPrintf(e, "%sdst_port_alt_hash", comma ? "," : "");
2446 comma = 1;
2447 }
2448 }
2449
2450 /* ports */
2451 comma = 0;
2452
2453 if (srv->info.port0 != 0) {
2454 storeAppendPrintf(e, "%s%d", comma ? "," : " ports=", ntohs(srv->info.port0));
2455 comma = 1;
2456 }
2457
2458 if (srv->info.port1 != 0) {
2459 storeAppendPrintf(e, "%s%d", comma ? "," : "ports=", ntohs(srv->info.port1));
2460 comma = 1;
2461 }
2462
2463 if (srv->info.port2 != 0) {
2464 storeAppendPrintf(e, "%s%d", comma ? "," : "ports=", ntohs(srv->info.port2));
2465 comma = 1;
2466 }
2467
2468 if (srv->info.port3 != 0) {
2469 storeAppendPrintf(e, "%s%d", comma ? "," : "ports=", ntohs(srv->info.port3));
2470 comma = 1;
2471 }
2472
2473 if (srv->info.port4 != 0) {
2474 storeAppendPrintf(e, "%s%d", comma ? "," : "ports=", ntohs(srv->info.port4));
2475 comma = 1;
2476 }
2477
2478 if (srv->info.port5 != 0) {
2479 storeAppendPrintf(e, "%s%d", comma ? "," : "ports=", ntohs(srv->info.port5));
2480 comma = 1;
2481 }
2482
2483 if (srv->info.port6 != 0) {
2484 storeAppendPrintf(e, "%s%d", comma ? "," : "ports=", ntohs(srv->info.port6));
2485 comma = 1;
2486 }
2487
2488 if (srv->info.port7 != 0) {
2489 storeAppendPrintf(e, "%s%d", comma ? "," : "ports=", ntohs(srv->info.port7));
2490 comma = 1;
2491 }
2492
2493 /* protocol */
2494 storeAppendPrintf(e, " protocol=%s", (srv->info.service_protocol == IPPROTO_TCP) ? "tcp" : "udp");
2495
2496 storeAppendPrintf(e, "\n");
2497
2498 srv = srv->next;
2499 }
2500}
2501
264096e2 2502/* Sort the cache list by doing a "selection sort" by IP address */
2503static void
2504wccp2SortCacheList(struct wccp2_cache_list_t *head)
2505{
2506 struct wccp2_cache_list_t tmp;
2507 struct wccp2_cache_list_t *this_item;
2508 struct wccp2_cache_list_t *find_item;
2509 struct wccp2_cache_list_t *next_lowest;
2510
2511 /* Go through each position in the list one at a time */
2512 for (this_item = head; this_item->next; this_item = this_item->next) {
26ac0430
AJ
2513 /* Find the item with the lowest IP */
2514 next_lowest = this_item;
2515
2516 for (find_item = this_item; find_item->next; find_item = find_item->next) {
2517 if (find_item->cache_ip.s_addr < next_lowest->cache_ip.s_addr) {
2518 next_lowest = find_item;
2519 }
2520 }
2521 /* Swap if we need to */
2522 if (next_lowest != this_item) {
2523 /* First make a copy of the current item */
2524 memcpy(&tmp, this_item, sizeof(struct wccp2_cache_list_t));
2525
2526 /* Next update the pointers to maintain the linked list */
2527 tmp.next = next_lowest->next;
2528 next_lowest->next = this_item->next;
2529
2530 /* Finally copy the updated items to their correct location */
2531 memcpy(this_item, next_lowest, sizeof(struct wccp2_cache_list_t));
2532 memcpy(next_lowest, &tmp, sizeof(struct wccp2_cache_list_t));
2533 }
264096e2 2534 }
2535}
2536
0b0cfcf2 2537void
2538free_wccp2_service_info(void *v)
2539{}
2540
2541#endif /* USE_WCCPv2 */