--------------------- PatchSet 7459 Date: 2006/03/17 10:32:26 Author: hno Branch: wccp2-2_5 Tag: (none) Log: Split WCCPv2 into it's own file Members: src/Makefile.am:1.13.2.11->1.13.2.11.4.1 src/wccp.c:1.5.56.10.10.1->1.5.56.10.10.2 src/wccp2.c:1.1->1.1.2.1 Index: squid/src/Makefile.am =================================================================== RCS file: /cvsroot/squid-sf//squid/src/Makefile.am,v retrieving revision 1.13.2.11 retrieving revision 1.13.2.11.4.1 diff -u -r1.13.2.11 -r1.13.2.11.4.1 --- squid/src/Makefile.am 29 Sep 2005 02:13:47 -0000 1.13.2.11 +++ squid/src/Makefile.am 17 Mar 2006 10:32:26 -0000 1.13.2.11.4.1 @@ -205,6 +205,7 @@ useragent.c \ wais.c \ wccp.c \ + wccp2.c \ whois.c \ $(WIN32SOURCE) Index: squid/src/wccp.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/wccp.c,v retrieving revision 1.5.56.10.10.1 retrieving revision 1.5.56.10.10.2 diff -u -r1.5.56.10.10.1 -r1.5.56.10.10.2 --- squid/src/wccp.c 17 Mar 2006 01:50:20 -0000 1.5.56.10.10.1 +++ squid/src/wccp.c 17 Mar 2006 10:32:26 -0000 1.5.56.10.10.2 @@ -1,6 +1,6 @@ /* - * $Id: wccp.c,v 1.5.56.10.10.1 2006/03/17 01:50:20 swsf Exp $ + * $Id: wccp.c,v 1.5.56.10.10.2 2006/03/17 10:32:26 hno Exp $ * * DEBUG: section 80 WCCP Support * AUTHOR: Glenn Chisholm @@ -368,1138 +368,3 @@ } #endif /* USE_WCCP */ - -#if USE_WCCPv2 -#include - -#define WCCP_PORT 2048 -#define WCCP_VERSION 4 -#define WCCP_REVISION 0 -#define WCCP_RESPONSE_SIZE 12448 -#define WCCP_ACTIVE_CACHES 32 -#define WCCP_HASH_SIZE 32 -#define WCCP_BUCKETS 256 - -#define WCCP_HERE_I_AM 7 -#define WCCP_I_SEE_YOU 8 -#define WCCP_ASSIGN_BUCKET 9 - -static int theInWccp2Connection = -1; -static int theOutWccp2Connection = -1; -static int wccp2_connected = 0; - -static PF wccp2HandleUdp; -static EVH wccp2HereIam; -static EVH wccp2AssignBuckets; - -/* KDW WCCP V2 */ -#define WCCP2_HERE_I_AM 10 -#define WCCP2_I_SEE_YOU 11 -#define WCCP2_REDIRECT_ASSIGN 12 -#define WCCP2_REMOVAL_QUERY 13 -#define WCCP2_VERSION 0x200 - -#define WCCP2_SECURITY_INFO 0 -#define WCCP2_NO_SECURITY 0 -#define WCCP2_MD5_SECURITY 1 /* Not Supported Yet */ - -#define WCCP2_SERVICE_INFO 1 -#define WCCP2_SERVICE_STANDARD 0 -#define WCCP2_SERVICE_DYNAMIC 1 /* Not Supported Yet */ -#define WCCP2_SERVICE_ID_HTTP 0x00 - -#define WCCP2_SERVICE_SRC_IP_HASH 0x1 -#define WCCP2_SERVICE_DST_IP_HASH 0x2 -#define WCCP2_SERVICE_SOURCE_PORT_HASH 0x4 -#define WCCP2_SERVICE_DST_PORT_HASH 0x8 -#define WCCP2_SERVICE_PORTS_DEFINED 0x10 -#define WCCP2_SERVICE_PORTS_SOURCE 0x20 -#define WCCP2_SERVICE_SRC_IP_ALT_HASH 0x100 -#define WCCP2_SERVICE_DST_IP_ALT_HASH 0x200 -#define WCCP2_SERVICE_SRC_PORT_ALT_HASH 0x400 -#define WCCP2_SERVICE_DST_PORT_ALT_HASH 0x800 - -#define WCCP2_ROUTER_ID_INFO 2 - -#define WCCP2_WC_ID_INFO 3 - -#define WCCP2_RTR_VIEW_INFO 4 - -#define WCCP2_WC_VIEW_INFO 5 - -#define WCCP2_REDIRECT_ASSIGNMENT 6 - -#define WCCP2_QUERY_INFO 7 - -#define WCCP2_CAPABILITY_INFO 8 - -#define WCCP2_CAPABILITY_FORWARDING_METHOD 0x01 -#define WCCP2_CAPABILITY_ASSIGNMENT_METHOD 0x02 -#define WCCP2_CAPABILITY_RETURN_METHOD 0x03 - -#define WCCP2_CAPABILITY_GRE 0x00000001 -#define WCCP2_CAPABILITY_L2 0x00000002 - - -/* WCCP v2 packet header */ -struct wccp2_here_i_am_header_t { - uint32_t type; - uint16_t version; - uint16_t length; -}; - -static struct wccp2_here_i_am_header_t wccp2_here_i_am_header; - -/* Security struct for the "no security" option */ -struct wccp2_security_none_t { - uint16_t security_type; - uint16_t security_length; - uint32_t security_option; -}; - -static struct wccp2_security_none_t wccp2_security_none; - -/* Service info struct */ -struct wccp2_service_info_t { - uint16_t service_type; - uint16_t service_length; - uint8_t service; - uint8_t service_id; - uint8_t service_priority; - uint8_t service_protocol; - uint32_t service_flags; - uint16_t port0; - uint16_t port1; - uint16_t port2; - uint16_t port3; - uint16_t port4; - uint16_t port5; - uint16_t port6; - uint16_t port7; -}; - -struct wccp2_cache_identity_info_t { - struct in_addr addr; - uint16_t hash_revision; - char bits[2]; - char buckets[32]; - uint16_t weight; - uint16_t status; -}; - -/* Web Cache identity info */ -struct wccp2_identity_info_t { - uint16_t cache_identity_type; - uint16_t cache_identity_length; - struct wccp2_cache_identity_info_t cache_identity; -}; - -static struct wccp2_identity_info_t wccp2_identity_info; - -/* View header */ -struct wccp2_cache_view_header_t { - uint16_t cache_view_type; - uint16_t cache_view_length; - uint32_t cache_view_version; -}; - -static struct wccp2_cache_view_header_t wccp2_cache_view_header; - -/* View info */ -struct wccp2_cache_view_info_t { - uint32_t num_routers; - uint32_t num_caches; -}; - -static struct wccp2_cache_view_info_t wccp2_cache_view_info; - -/* Router ID element */ -struct wccp2_router_id_element_t { - struct in_addr router_address; - uint32_t received_id; -}; - -static struct wccp2_router_id_element_t wccp2_router_id_element; - -/* Capability info header */ -struct wccp2_capability_info_header_t { - uint16_t capability_info_type; - uint16_t capability_info_length; -}; - -static struct wccp2_capability_info_header_t wccp2_capability_info_header; - -/* Capability element */ -struct wccp2_capability_element_t { - uint16_t capability_type; - uint16_t capability_length; - uint32_t capability_value; -}; - -static struct wccp2_capability_element_t wccp2_capability_element; - - -/* RECEIVED PACKET STRUCTURE */ -struct wccp2_i_see_you_t { - uint32_t type; - uint16_t version; - uint16_t length; - char data[WCCP_RESPONSE_SIZE]; -}; - -static struct wccp2_i_see_you_t wccp2_i_see_you; - -/* Router ID element */ -struct wccp2_router_assign_element_t { - struct in_addr router_address; - uint32_t received_id; - uint32_t change_number; -}; - -/* Generic header struct */ -struct wccp2_item_header_t { - uint16_t type; - uint16_t length; - }; - -/* Router identity struct */ -struct router_identity_info_t { - struct wccp2_item_header_t header; - struct wccp2_router_id_element_t router_id_element; - struct in_addr router_address; - uint32_t number_caches; -}; - -/* assigment key */ -struct assignment_key_t { - struct in_addr master_ip; - uint32_t master_number; -}; - -/* Router view of WCCP */ -struct router_view_t { - struct wccp2_item_header_t header; - uint32_t change_number; - struct assignment_key_t assignment_key; -}; - - -/* Lists used to keep track of caches, routers and services */ -struct wccp2_cache_list_t { - struct in_addr cache_ip; - struct wccp2_cache_list_t *next; -}; - -struct wccp2_router_list_t { - struct wccp2_router_id_element_t *info; - struct in_addr local_ip; - struct in_addr router_sendto_address; - uint32_t member_change; - uint32_t num_caches; - struct wccp2_cache_list_t cache_list_head; - struct wccp2_router_list_t *next; -}; - -static int wccp2_numrouters; - -struct wccp2_service_list_t { - struct wccp2_service_info_t info; - uint32_t num_routers; - struct wccp2_router_list_t router_list_head; - int lowest_ip; - uint32_t change_num; - struct wccp2_identity_info_t *wccp2_identity_info_ptr; - struct wccp2_security_none_t *security_info; - struct wccp2_service_info_t *service_info; - char wccp_packet[WCCP_RESPONSE_SIZE]; - size_t wccp_packet_size; - struct wccp2_service_list_t *next; -}; - -static struct wccp2_service_list_t wccp2_service_list_head; - -/* END WCCP V2 */ - -/* - * The functions used during startup: - * wccp2Init - * wccp2ConnectionOpen - * wccp2ConnectionShutdown - * wccp2ConnectionClose - */ - -void -wccp2InitServices(void) -{ - struct wccp2_service_list_t *wccp2_service_list_ptr; - wccp2_service_list_ptr=&wccp2_service_list_head; - - // TODO: Need to do something like this here: - // for each wccp_service configured { - // if(wccp2_service_list_ptr != &wccp2_service_list_head) { - // wccp2_service_list_ptr->next=xcalloc(1,sizeof(struct wccp2_service_list_t)); - // wccp2_service_list_ptr=wccp2_service_list_ptr->next; - // wccp2_service_list_ptr->next=NULL; - // } - // wccp2_service_list_ptr->info.service_type = htons(WCCP2_SERVICE_INFO); - // wccp2_service_list_ptr->info.service_length = htons(sizeof(struct wccp2_service_info_t)-4); - // wccp2_service_list_ptr->info.service = Config.wccp2.service_type; (dynamic/standard) - // wccp2_service_list_ptr->info.service_id = Config.wccp2.service_type; (WCCP2_SERVICE_ID_HTTP for http, or user-defined value for configured) - // wccp2_service_list_ptr->info.service_priority = Config.wccp2.service_priority; - // wccp2_service_list_ptr->info.service_protocol = Config.wccp2.service_protocol; (0 if service_type is WCCP2_SERVICE_ID_HTTP, or IPPROTO_TCP/IPPROTO_UDP) - // wccp2_service_list_ptr->info.service_flags = htonl(Config.wccp2.service_flags); (must be 0 if service_type is WCCP2_SERVICE_ID_HTTP, else must include WCCP2_SERVICE_PORTS_DEFINED to use the ports below. This deteries the hash method and whether the has is based on source or destination ports). - // - // Possibly have another loop here to define the ports? - // - // wccp2_service_list_ptr->info.port0 = htons(Config.wccp2.service_port1); - // wccp2_service_list_ptr->info.port1 = htons(Config.wccp2.service_port2); - // wccp2_service_list_ptr->info.port2 = htons(Config.wccp2.service_port3); - // wccp2_service_list_ptr->info.port3 = htons(Config.wccp2.service_port4); - // wccp2_service_list_ptr->info.port4 = htons(Config.wccp2.service_port5); - // wccp2_service_list_ptr->info.port5 = htons(Config.wccp2.service_port6); - // wccp2_service_list_ptr->info.port6 = htons(Config.wccp2.service_port7); - // wccp2_service_list_ptr->info.port7 = htons(Config.wccp2.service_port8); - // wccp2_service_list_ptr->change_num=0; - // } - // - // The first thing that needs doing is to define a struct in the Config code - // to store the above information in some form of a linked list. - // - // Most people will only need to define something like: - // wccp2_service 0 100 - // - // This defines the service type as the standard http service with a - // priority of 100. If the service number is 0, no other flags will be - // parsed. - // - // If anyone wanted to define a custom service, we would want the following - // options - // wccp2_service number priority [flags] [ports] - // - // [flags] should include: - // hash_source_ip - to use the source ip address to determine which cache to send the packet to (can be used to make sure the same ip always hits the same cache). This would override the default beahviour of using the destination ip. - // defined_ports_source - to use the source port to determine whether a packet will be redirected by WCCP (useful for catching reply packets if the proxy server is forging the source ip address) - // proto_udp - to override the default protocol type of tcp - // - // [ports] would be up to 8 ports used to determine which packets to redirect - - /* This defines WCCP service number 80 (redirect traffic going to tcp port - 80, using the client ip as the hash) */ - wccp2_service_list_ptr->info.service_type = htons(WCCP2_SERVICE_INFO); - wccp2_service_list_ptr->info.service_length = htons(sizeof(struct wccp2_service_info_t)-4); - wccp2_service_list_ptr->info.service = WCCP2_SERVICE_DYNAMIC; - wccp2_service_list_ptr->info.service_id = 80; - wccp2_service_list_ptr->info.service_priority = 240; - wccp2_service_list_ptr->info.service_protocol = IPPROTO_TCP; - wccp2_service_list_ptr->info.service_flags = htonl(WCCP2_SERVICE_SRC_IP_HASH + WCCP2_SERVICE_PORTS_DEFINED); - wccp2_service_list_ptr->info.port0 = htons(80); - wccp2_service_list_ptr->info.port1 = htons(0); - wccp2_service_list_ptr->info.port2 = htons(0); - wccp2_service_list_ptr->info.port3 = htons(0); - wccp2_service_list_ptr->info.port4 = htons(0); - wccp2_service_list_ptr->info.port5 = htons(0); - wccp2_service_list_ptr->info.port6 = htons(0); - wccp2_service_list_ptr->info.port7 = htons(0); - wccp2_service_list_ptr->change_num=0; - - wccp2_service_list_ptr->next=xcalloc(1,sizeof(struct wccp2_service_list_t)); - wccp2_service_list_ptr=wccp2_service_list_ptr->next; - wccp2_service_list_ptr->next=NULL; - - /* This defines WCCP service number 90 (redirect traffic coming from tcp - port 80, using the client ip as the hash) */ - wccp2_service_list_ptr->info.service_type = htons(WCCP2_SERVICE_INFO); - wccp2_service_list_ptr->info.service_length = htons(sizeof(struct wccp2_service_info_t)-4); - wccp2_service_list_ptr->info.service = WCCP2_SERVICE_DYNAMIC; - wccp2_service_list_ptr->info.service_id = 90; - wccp2_service_list_ptr->info.service_priority = 240; - wccp2_service_list_ptr->info.service_protocol = IPPROTO_TCP; - wccp2_service_list_ptr->info.service_flags = htonl(WCCP2_SERVICE_DST_IP_HASH + WCCP2_SERVICE_PORTS_DEFINED + WCCP2_SERVICE_PORTS_SOURCE); - wccp2_service_list_ptr->info.port0 = htons(80); - wccp2_service_list_ptr->info.port1 = htons(0); - wccp2_service_list_ptr->info.port2 = htons(0); - wccp2_service_list_ptr->info.port3 = htons(0); - wccp2_service_list_ptr->info.port4 = htons(0); - wccp2_service_list_ptr->info.port5 = htons(0); - wccp2_service_list_ptr->info.port6 = htons(0); - wccp2_service_list_ptr->info.port7 = htons(0); - wccp2_service_list_ptr->change_num=0; - wccp2_service_list_ptr->next=xcalloc(1,sizeof(struct wccp2_service_list_t)); - wccp2_service_list_ptr=wccp2_service_list_ptr->next; - wccp2_service_list_ptr->next=NULL; -} - -void -wccp2Init(void) -{ - sockaddr_in_list *s; - char *ptr; - struct wccp2_service_list_t *service_list_ptr; - struct wccp2_router_list_t *router_list_ptr; - - debug(80, 5) ("wccp2Init: Called\n"); - if (wccp2_connected == 1) - return; - wccp2_numrouters=0; - - /* Calculate the number of routers configured in the config file */ - for (s = Config.Wccp2.router; s; s = s->next) { - if(s->s.sin_addr.s_addr != any_addr.s_addr) { - /* Increment the counter */ - wccp2_numrouters++; - } - } - if(wccp2_numrouters == 0) { - return; - } - - /* Initialise the list of services */ - wccp2InitServices(); - - for(service_list_ptr=&wccp2_service_list_head;service_list_ptr->next;service_list_ptr=service_list_ptr->next) { - /* Set up our list pointers */ - router_list_ptr=&service_list_ptr->router_list_head; - - /* start the wccp header */ - wccp2_here_i_am_header.type = htonl(WCCP2_HERE_I_AM); - wccp2_here_i_am_header.version = htons(WCCP2_VERSION); - wccp2_here_i_am_header.length = 0; - ptr = service_list_ptr->wccp_packet + sizeof(wccp2_here_i_am_header); - - /* add the security section */ - wccp2_here_i_am_header.length += sizeof(struct wccp2_security_none_t); - assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); - wccp2_security_none.security_type = htons(WCCP2_SECURITY_INFO); - wccp2_security_none.security_length = htons(sizeof(struct wccp2_security_none_t)-4); - wccp2_security_none.security_option = htonl(WCCP2_NO_SECURITY); /* TODO: MD5 security */ - xmemcpy(ptr,&wccp2_security_none,sizeof(struct wccp2_security_none_t)); - service_list_ptr->security_info=(struct wccp2_security_none_t *)ptr; - ptr += sizeof(struct wccp2_security_none_t); - - /* Add the service info section */ - wccp2_here_i_am_header.length += sizeof(struct wccp2_service_info_t); - assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); - xmemcpy(ptr,&service_list_ptr->info,sizeof(struct wccp2_service_info_t)); - service_list_ptr->service_info = (struct wccp2_service_info_t *)ptr; - ptr += sizeof(struct wccp2_service_info_t); - - /* Add the cache identity section */ - wccp2_here_i_am_header.length += sizeof(struct wccp2_identity_info_t); - assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); - wccp2_identity_info.cache_identity_type = htons(WCCP2_WC_ID_INFO); - wccp2_identity_info.cache_identity_length = htons(sizeof(wccp2_identity_info.cache_identity)); - memset(&wccp2_identity_info.cache_identity.addr,'\0',sizeof(wccp2_identity_info.cache_identity.addr)); - memset(&wccp2_identity_info.cache_identity.hash_revision,'\0',sizeof(wccp2_identity_info.cache_identity.hash_revision)); - memset(&wccp2_identity_info.cache_identity.bits,'\0',sizeof(wccp2_identity_info.cache_identity.bits)); - memset(&wccp2_identity_info.cache_identity.buckets,'\0',sizeof(wccp2_identity_info.cache_identity.buckets)); - wccp2_identity_info.cache_identity.weight = htons(10000); - memset(&wccp2_identity_info.cache_identity.status,'\0',sizeof(wccp2_identity_info.cache_identity.status)); - xmemcpy(ptr,&wccp2_identity_info,sizeof(struct wccp2_identity_info_t)); - service_list_ptr->wccp2_identity_info_ptr = (struct wccp2_identity_info_t *)ptr; - ptr += sizeof(struct wccp2_identity_info_t); - - /* Add the cache view section */ - wccp2_here_i_am_header.length += sizeof(wccp2_cache_view_header); - assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); - wccp2_cache_view_header.cache_view_type = htons(WCCP2_WC_VIEW_INFO); - wccp2_cache_view_header.cache_view_length = htons(sizeof(wccp2_cache_view_header) - 4 + - sizeof(wccp2_cache_view_info) + (wccp2_numrouters * sizeof(wccp2_router_id_element)) ); - wccp2_cache_view_header.cache_view_version = htonl(1); - xmemcpy(ptr,&wccp2_cache_view_header,sizeof(wccp2_cache_view_header)); - ptr += sizeof(wccp2_cache_view_header); - - /* Add the number of routers to the packet */ - wccp2_here_i_am_header.length += sizeof(service_list_ptr->num_routers); - assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); - service_list_ptr->num_routers = htonl(wccp2_numrouters); - xmemcpy(ptr,&service_list_ptr->num_routers,sizeof(service_list_ptr->num_routers)); - ptr += sizeof(service_list_ptr->num_routers); - - /* Add each router. Keep this functionality here to make sure the received_id can be updated in the packet */ - for (s = Config.Wccp2.router; s; s = s->next) { - if(s->s.sin_addr.s_addr != any_addr.s_addr) { - wccp2_here_i_am_header.length += sizeof(struct wccp2_router_id_element_t); - assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); - - /* Add a pointer to the router list for this router */ - router_list_ptr->info=(struct wccp2_router_id_element_t *)ptr; - router_list_ptr->info->router_address=s->s.sin_addr; - router_list_ptr->info->received_id = htonl(0); - router_list_ptr->router_sendto_address=s->s.sin_addr; - router_list_ptr->member_change = htonl(0); - - /* Build the next struct */ - router_list_ptr->next=xcalloc(1,sizeof(struct wccp2_router_list_t)); - - /* update the pointer */ - router_list_ptr=router_list_ptr->next; - router_list_ptr->next=NULL; - - /* no need to copy memory - we've just set the values directly in the packet above */ - ptr+=sizeof(struct wccp2_router_id_element_t); - } - } - - /* Add the number of caches (0) */ - wccp2_here_i_am_header.length += sizeof(wccp2_cache_view_info.num_caches); - assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); - wccp2_cache_view_info.num_caches = htonl(0); - xmemcpy(ptr,&wccp2_cache_view_info.num_caches,sizeof(wccp2_cache_view_info.num_caches)); - ptr += sizeof(wccp2_cache_view_info.num_caches); - - /* Add the extra capability header */ - wccp2_here_i_am_header.length += sizeof(wccp2_capability_info_header); - assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); - wccp2_capability_info_header.capability_info_type = htons(WCCP2_CAPABILITY_INFO); - wccp2_capability_info_header.capability_info_length = htons(2 * sizeof(wccp2_capability_element)); - xmemcpy(ptr,&wccp2_capability_info_header,sizeof(wccp2_capability_info_header)); - ptr += sizeof(wccp2_capability_info_header); - - /* Add the forwarding method */ - wccp2_here_i_am_header.length += sizeof(wccp2_capability_element); - assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); - wccp2_capability_element.capability_type = htons(WCCP2_CAPABILITY_FORWARDING_METHOD); - wccp2_capability_element.capability_length = htons(sizeof(wccp2_capability_element.capability_value)); - wccp2_capability_element.capability_value = htonl(Config.Wccp2.forwarding_method); - xmemcpy(ptr,&wccp2_capability_element,sizeof(wccp2_capability_element)); - ptr += sizeof(wccp2_capability_element); - - /* Add the return method */ - wccp2_here_i_am_header.length += sizeof(wccp2_capability_element); - assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); - wccp2_capability_element.capability_type = htons(WCCP2_CAPABILITY_RETURN_METHOD); - wccp2_capability_element.capability_length = htons(sizeof(wccp2_capability_element.capability_value)); - wccp2_capability_element.capability_value = htonl(Config.Wccp2.return_method); - xmemcpy(ptr,&wccp2_capability_element,sizeof(wccp2_capability_element)); - ptr += sizeof(wccp2_capability_element); - - /* Finally, fix the total length to network order, and copy to the appropriate memory blob */ - wccp2_here_i_am_header.length = htons(wccp2_here_i_am_header.length); - memcpy(&service_list_ptr->wccp_packet,&wccp2_here_i_am_header,sizeof(wccp2_here_i_am_header)); - service_list_ptr->wccp_packet_size=ntohs(wccp2_here_i_am_header.length) + sizeof(wccp2_here_i_am_header); - - /* Add the event if everything initialised correctly */ - if(wccp2_numrouters) { - if (!eventFind(wccp2HereIam, NULL)) { - eventAdd("wccp2HereIam", wccp2HereIam, NULL, 10.0, 1); - } - } - } -} - -void -wccp2ConnectionOpen(void) -{ - u_short port = WCCP_PORT; - struct sockaddr_in router, local; - int local_len, router_len; - struct wccp2_service_list_t *service_list_ptr; - struct wccp2_router_list_t *router_list_ptr; - - debug(80, 5) ("wccp2ConnectionOpen: Called\n"); - if(wccp2_numrouters == 0) { - debug(80, 1) ("WCCPv2 Disabled.\n"); - } - - theInWccp2Connection = comm_open(SOCK_DGRAM, - 0, - Config.Wccp2.incoming, - port, - COMM_NONBLOCKING, - "WCCP Socket"); - if (theInWccp2Connection < 0) - fatal("Cannot open WCCP Port"); - commSetSelect(theInWccp2Connection, - COMM_SELECT_READ, - wccp2HandleUdp, - NULL, - 0); - debug(80, 1) ("Accepting WCCP v2 messages on port %d, FD %d.\n", - (int) port, theInWccp2Connection); - - if (Config.Wccp2.outgoing.s_addr != no_addr.s_addr) { - theOutWccpConnection = comm_open(SOCK_DGRAM, - 0, - Config.Wccp2.outgoing, - port, - COMM_NONBLOCKING, - "WCCP Socket"); - if (theOutWccpConnection < 0) - fatal("Cannot open Outgoing WCCP Port"); - commSetSelect(theOutWccpConnection, - COMM_SELECT_READ, - wccp2HandleUdp, - NULL, 0); - debug(80, 1) ("Outgoing WCCPv2 messages on port %d, FD %d.\n", - (int) port, theOutWccpConnection); - fd_note(theOutWccpConnection, "Outgoing WCCP socket"); - fd_note(theInWccpConnection, "Incoming WCCP socket"); - } - else - { - theOutWccp2Connection = theInWccp2Connection; - } - - debug(80, 1) ("Initialising all WCCPv2 lists\n"); - - /* Initialise all routers on all services */ - for(service_list_ptr=&wccp2_service_list_head;service_list_ptr->next;service_list_ptr=service_list_ptr->next) { - for (router_list_ptr=&service_list_ptr->router_list_head;router_list_ptr->next != NULL;router_list_ptr=router_list_ptr->next) { - router_len = sizeof(router); - memset(&router, '\0', router_len); - router.sin_family = AF_INET; - router.sin_port = htons(port); - router.sin_addr = router_list_ptr->router_sendto_address; - if (connect(theOutWccp2Connection, (struct sockaddr *) &router, router_len)) - fatal("Unable to connect WCCP out socket"); - local_len = sizeof(local); - memset(&local, '\0', local_len); - if (getsockname(theOutWccp2Connection, (struct sockaddr *) &local, &local_len)) - fatal("Unable to getsockname on WCCP out socket"); - local_ip.s_addr = local.sin_addr.s_addr; - - router_list_ptr->local_ip=local_ip; - - /* Disconnect the sending socket */ - router.sin_family = AF_UNSPEC; - if (connect(theOutWccp2Connection, (struct sockaddr *) &router, router_len)) - fatal("Unable to disconnect WCCP out socket"); - } - } - wccp2_connected=1; -} - -void -wccp2ConnectionShutdown(void) -{ - if (theInWccp2Connection < 0) - return; - if(wccp2_connected == 0) - return; - if (theInWccp2Connection != theOutWccp2Connection) { - debug(80, 1) ("FD %d Closing WCCP socket\n", theInWccp2Connection); - comm_close(theInWccp2Connection); - } - assert(theOutWccp2Connection > -1); - commSetSelect(theOutWccp2Connection, COMM_SELECT_READ, NULL, NULL, 0); -} - -void -wccp2ConnectionClose(void) -{ - struct wccp2_service_list_t *service_list_ptr; - struct wccp2_service_list_t *service_list_ptr_next; - struct wccp2_router_list_t *router_list_ptr; - struct wccp2_router_list_t *router_list_next; - struct wccp2_cache_list_t *cache_list_ptr; - struct wccp2_cache_list_t *cache_list_ptr_next; - - if(wccp2_connected == 0) { - return; - } - wccp2ConnectionShutdown(); - if (theOutWccp2Connection > -1) { - debug(80, 1) ("FD %d Closing WCCP socket\n", theOutWccp2Connection); - comm_close(theOutWccp2Connection); - } - /* for each router on each service send a packet */ - for(service_list_ptr=&wccp2_service_list_head;service_list_ptr;service_list_ptr=service_list_ptr_next) { - for (router_list_ptr=&service_list_ptr->router_list_head;router_list_ptr != NULL;router_list_ptr=router_list_next) { - for(cache_list_ptr=&router_list_ptr->cache_list_head;cache_list_ptr;cache_list_ptr=cache_list_ptr_next) { - cache_list_ptr_next=cache_list_ptr->next; - if(cache_list_ptr != &router_list_ptr->cache_list_head) { - xfree(cache_list_ptr); - } - else - { - memset(cache_list_ptr,'\0',sizeof(struct wccp2_cache_list_t)); - } - } - router_list_next=router_list_ptr->next; - if(router_list_ptr != &service_list_ptr->router_list_head) { - xfree(router_list_ptr); - } - else - { - memset(router_list_ptr,'\0',sizeof(struct wccp2_router_list_t)); - } - } - service_list_ptr_next=service_list_ptr->next; - if(service_list_ptr != &wccp2_service_list_head) { - xfree(service_list_ptr); - } - else - { - memset(service_list_ptr,'\0',sizeof(struct wccp2_service_list_t)); - } - } - wccp2_connected=0; -} - -/* - * Functions for handling the requests. - */ - -/* - * Accept the UDP packet - */ -static void -wccp2HandleUdp(int sock, void *not_used) -{ - struct wccp2_service_list_t *service_list_ptr; - struct wccp2_router_list_t *router_list_ptr; - struct wccp2_cache_list_t *cache_list_ptr; - struct wccp2_cache_list_t *cache_list_ptr_next; - - /* These structs form the parts of the packet */ - struct wccp2_item_header_t *header=NULL; - struct wccp2_security_none_t *security_info=NULL; - struct wccp2_service_info_t *service_info=NULL; - struct router_identity_info_t *router_identity_info=NULL; - struct router_view_t *router_view_header=NULL; - struct wccp2_cache_identity_info_t *cache_identity=NULL; - struct wccp2_capability_info_header_t *router_capability_header=NULL; - struct wccp2_capability_element_t *router_capability_element; - - struct sockaddr_in from; - struct in_addr cache_address; - socklen_t from_len; - int len,found; - short int data_length, offset; - uint32_t tmp; - char *ptr; - int num_caches; - uint16_t num_capabilities; - - debug(80, 6) ("wccp2HandleUdp: Called.\n"); - - commSetSelect(sock, COMM_SELECT_READ, wccp2HandleUdp, NULL, 0); - from_len = sizeof(struct sockaddr_in); - memset(&from, '\0', from_len); - - statCounter.syscalls.sock.recvfroms++; - - len = recvfrom(sock, - &wccp2_i_see_you, - WCCP_RESPONSE_SIZE, - 0, - (struct sockaddr *) &from, - &from_len); - - if (len < 0) - return; - if (ntohs(wccp2_i_see_you.version) != WCCP2_VERSION) - return; - if (ntohl(wccp2_i_see_you.type) != WCCP2_I_SEE_YOU) - return; - - debug(80, 3) ("Incoming WCCP v2 I_SEE_YOU length %d.\n", ntohs(wccp2_i_see_you.length)); - - /* Record the total data length */ - data_length=ntohs(wccp2_i_see_you.length); - offset=0; - - if(data_length > len) { - debug(80, 1) ("ERROR: Malformed WCCPv2 packet claiming it's bigger than received data\n"); - return; - } - - /* Go through the data structure */ - while(data_length > offset) { - header=(struct wccp2_item_header_t *)&wccp2_i_see_you.data[offset]; - - switch(ntohs(header->type)) { - case WCCP2_SECURITY_INFO: - if(security_info != NULL ) { - debug(80, 1) ("Duplicate security definition\n"); - return; - } - security_info=(struct wccp2_security_none_t *)&wccp2_i_see_you.data[offset]; - break; - case WCCP2_SERVICE_INFO: - if(service_info != NULL ) { - debug(80, 1) ("Duplicate service_info definition\n"); - return; - } - service_info=(struct wccp2_service_info_t *)&wccp2_i_see_you.data[offset]; - break; - case WCCP2_ROUTER_ID_INFO: - if(router_identity_info != NULL ) { - debug(80, 1) ("Duplicate router_identity_info definition\n"); - return; - } - router_identity_info=(struct router_identity_info_t *)&wccp2_i_see_you.data[offset]; - break; - case WCCP2_RTR_VIEW_INFO: - if(router_view_header != NULL ) { - debug(80, 1) ("Duplicate router_view definition\n"); - return; - } - router_view_header=(struct router_view_t *)&wccp2_i_see_you.data[offset]; - break; - case WCCP2_CAPABILITY_INFO: - if(router_capability_header != NULL ) { - debug(80, 1) ("Duplicate router_capability definition\n"); - return; - } - router_capability_header=(struct wccp2_capability_info_header_t *)&wccp2_i_see_you.data[offset]; - return; - default: - debug(80, 1) ("Unknown record type in WCCPv2 Packet (%d).\n", - ntohs(header->type)); - } - offset+=sizeof(struct wccp2_item_header_t); - offset+=ntohs(header->length); - if(offset > data_length) { - debug(80,1) ("Error: WCCPv2 packet tried to tell us there is data beyond the end of the packet\n"); - return; - } - } - if((security_info == NULL) || (service_info == NULL) || (router_identity_info == NULL) || (router_view_header == NULL)) { - debug(80, 1) ("Incomplete WCCPv2 Packet\n"); - return; - } - - debug (80,5) ("Complete packet received\n"); - - /* Check that the service in the packet is configured on this router */ - for(service_list_ptr=&wccp2_service_list_head;service_list_ptr->next;service_list_ptr=service_list_ptr->next) { - if(service_info->service_id == service_list_ptr->service_info->service_id) { - break; - } - } - if(!(service_list_ptr->next)) { - debug(80, 1) ("WCCPv2 Unknown service received from router\n"); - return; - } - - /* TODO: MD5 security */ - if(ntohl(security_info->security_option) != service_list_ptr->security_info->security_option) { - debug(80, 1) ("Invalid security option in WCCPv2 Packet (%d).\n", - ntohl(security_info->security_option)); - return; - } - - /* Check that the router address is configured on this router */ - for (router_list_ptr=&service_list_ptr->router_list_head;router_list_ptr->next != NULL;router_list_ptr=router_list_ptr->next) { - if(router_list_ptr->router_sendto_address.s_addr == from.sin_addr.s_addr) - break; - } - if(router_list_ptr->next == NULL) { - debug(80, 1) ("WCCPv2 Packet received from unknown router\n"); - return; - } - - /* Set the router id */ - router_list_ptr->info->router_address=router_identity_info->router_id_element.router_address; - - /* Increment the received id in the packet */ - if ( ntohl(router_list_ptr->info->received_id) != ntohl(router_identity_info->router_id_element.received_id) ) { - debug(80, 3) ("Incoming WCCP2_I_SEE_YOU member change = %d tmp=%d.\n", - ntohl(router_list_ptr->info->received_id), ntohl(router_identity_info->router_id_element.received_id)); - router_list_ptr->info->received_id = router_identity_info->router_id_element.received_id; - } - - /* TODO: check return/forwarding methods */ - if(router_capability_header == NULL) { - if((Config.Wccp2.return_method != WCCP2_CAPABILITY_GRE) || (Config.Wccp2.forwarding_method != WCCP2_CAPABILITY_GRE)) { - debug (80,1) ("wccp2HandleUdp: fatal error - A WCCP router does not support the forwarding method specified\n"); - wccp2ConnectionClose(); - return; - } - } - else - { - num_capabilities=ntohs(router_capability_header->capability_info_length); - /* run through each capability element from last to first */ - if(num_capabilities > 0) { - num_capabilities--; - router_capability_element=(struct wccp2_capability_element_t *)(router_capability_header) + sizeof(struct wccp2_capability_info_header_t) + (num_capabilities * sizeof(struct wccp2_capability_element_t)); - switch(ntohs(router_capability_element->capability_type)) { - case WCCP2_CAPABILITY_FORWARDING_METHOD: - if(ntohl(router_capability_element->capability_value) != Config.Wccp2.forwarding_method) { - debug (80,1) ("wccp2HandleUdp: fatal error - A WCCP router has specified a different forwarding method\n"); - wccp2ConnectionClose(); - return; - } - break; - case WCCP2_CAPABILITY_ASSIGNMENT_METHOD: - /* we don't current care */ - break; - case WCCP2_CAPABILITY_RETURN_METHOD: - if(ntohl(router_capability_element->capability_value) != Config.Wccp2.return_method) { - debug (80,1) ("wccp2HandleUdp: fatal error - A WCCP router has specified a different return method\n"); - wccp2ConnectionClose(); - return; - } - break; - default: - debug(80, 1) ("Unknown capability type in WCCPv2 Packet (%d).\n", - ntohs(router_capability_element->capability_type)); - } - } - } - - debug(80, 5) ("Cleaning out cache list\n"); - /* clean out the old cache list */ - for(cache_list_ptr=&router_list_ptr->cache_list_head;cache_list_ptr;cache_list_ptr=cache_list_ptr_next) { - cache_list_ptr_next=cache_list_ptr->next; - if(cache_list_ptr != &router_list_ptr->cache_list_head) { - xfree(cache_list_ptr); - } - } - router_list_ptr->num_caches=htonl(0); - num_caches=0; - - /* Check to see if we're the master cache and update the cache list */ - found=0; - service_list_ptr->lowest_ip=1; - cache_list_ptr=&router_list_ptr->cache_list_head; - - /* to find the list of caches, we start at the end of the router view header */ - ptr=(char *)(router_view_header) + sizeof(struct router_view_t); - - /* Then we read the number of routers */ - memcpy(&tmp,ptr,sizeof(tmp)); - - /* skip the number plus all the ip's */ - ptr+=sizeof(tmp) + (ntohl(tmp) * sizeof(struct in_addr)); - - /* Then read the number of caches */ - memcpy(&tmp,ptr,sizeof(tmp)); - ptr+=sizeof(tmp); - - if(ntohl(tmp) != 0) { - /* search through the list of received-from ip addresses */ - for(num_caches=0;num_caches< ntohl(tmp);num_caches++) { - /* Get a copy of the ip */ - cache_identity=(struct wccp2_cache_identity_info_t *)ptr; - ptr+=sizeof(struct wccp2_cache_identity_info_t); - memcpy(&cache_address,&cache_identity->addr,sizeof(struct in_addr)); - - /* Update the cache list */ - cache_list_ptr->cache_ip=cache_address; - cache_list_ptr->next=xcalloc(1,sizeof(struct wccp2_cache_list_t)); - cache_list_ptr=cache_list_ptr->next; - cache_list_ptr->next=NULL; - - debug(80,5) ("checking cache list: (%x:%x)\n",cache_address.s_addr,router_list_ptr->local_ip.s_addr); - /* Check to see if it's the master, or us */ - if(cache_address.s_addr == router_list_ptr->local_ip.s_addr) { - found=1; - } - if(cache_address.s_addr < router_list_ptr->local_ip.s_addr) { - service_list_ptr->lowest_ip=0; - } - } - } - else - { - - /* Update the cache list */ - cache_list_ptr->cache_ip=router_list_ptr->local_ip; - cache_list_ptr->next=xcalloc(1,sizeof(struct wccp2_cache_list_t)); - cache_list_ptr=cache_list_ptr->next; - cache_list_ptr->next=NULL; - - service_list_ptr->lowest_ip=0; - found=1; - num_caches=1; - } - router_list_ptr->num_caches=htonl(num_caches); - - if ((found == 1) && (service_list_ptr->lowest_ip == 1)) { - if( ntohl(router_view_header->change_number) != router_list_ptr->member_change ) { - debug (80,4) ("Change detected - queueing up new assignment\n"); - router_list_ptr->member_change = ntohl(router_view_header->change_number); - if (!eventFind(wccp2AssignBuckets, NULL)) { - eventAdd("wccp2AssignBuckets", wccp2AssignBuckets, NULL, 15.0, 1); - } - } - } - else - { - debug (80,5) ("I am not the lowest ip cache - not assigning buckets\n"); - } -} - -static void -wccp2HereIam(void *voidnotused) -{ - struct wccp2_service_list_t *service_list_ptr; - struct wccp2_router_list_t *router_list_ptr; - struct sockaddr_in router; - int router_len; - u_short port = WCCP_PORT; - - debug(80, 6) ("wccp2HereIam: Called\n"); - if(wccp2_connected == 0) { - debug (80,1) ("wccp2HereIam: wccp2 socket closed. Shutting down WCCP2\n"); - return; - } - - router_len = sizeof(router); - memset(&router, '\0', router_len); - router.sin_family = AF_INET; - router.sin_port = htons(port); - - /* for each router on each service send a packet */ - for(service_list_ptr=&wccp2_service_list_head;service_list_ptr->next;service_list_ptr=service_list_ptr->next) { - for (router_list_ptr=&service_list_ptr->router_list_head;router_list_ptr->next != NULL;router_list_ptr=router_list_ptr->next) { - router.sin_addr = router_list_ptr->router_sendto_address; - - /* Set the cache id (ip) */ - service_list_ptr->wccp2_identity_info_ptr->cache_identity.addr=router_list_ptr->local_ip; - - debug(80,3) ("Sending HereIam packet size %d\n",service_list_ptr->wccp_packet_size); - /* Send the packet */ - sendto(theOutWccp2Connection, - &service_list_ptr->wccp_packet, - service_list_ptr->wccp_packet_size, - 0, - (struct sockaddr *)&router, - router_len); - } - } - - if (!eventFind(wccp2HereIam, NULL)) - eventAdd("wccp2HereIam", wccp2HereIam, NULL, 10.0, 1); -} - -static void -wccp2AssignBuckets(void *voidnotused) -{ - struct wccp2_service_list_t *service_list_ptr; - struct wccp2_router_list_t *router_list_ptr; - struct wccp2_cache_list_t *cache_list_ptr; - char wccp_packet[WCCP_RESPONSE_SIZE]; - short int offset,saved_offset; - struct sockaddr_in router; - int router_len; - int bucket_counter; - u_short port = WCCP_PORT; - - /* Packet segments */ - struct wccp2_here_i_am_header_t *main_header; - struct wccp2_security_none_t *security; /* TODO: add MD5 */ - /* service from service struct */ - struct wccp2_item_header_t *assignment_header; - struct assignment_key_t *assignment_key; - /* number of routers */ - struct wccp2_router_assign_element_t *router_assign; - /* number of caches */ - struct in_addr *cache_address; - char *buckets; - - router_len = sizeof(router); - memset(&router, '\0', router_len); - router.sin_family = AF_INET; - router.sin_port = htons(port); - - /* Start main header - fill in length later */ - offset=0; - main_header=(struct wccp2_here_i_am_header_t *)&wccp_packet[offset]; - main_header->type=htonl(WCCP2_REDIRECT_ASSIGN); - main_header->version=htons(WCCP2_VERSION); - - debug(80,2) ("Running wccp2AssignBuckets\n"); - for(service_list_ptr=&wccp2_service_list_head;service_list_ptr->next;service_list_ptr=service_list_ptr->next) { - - /* If we're not the lowest, we don't need to worry */ - if(service_list_ptr->lowest_ip == 0) { - continue; - } - - /* reset the offset */ - offset = sizeof(struct wccp2_here_i_am_header_t); - - /* build packet header from hereIam packet */ - /* Security info */ - security=(struct wccp2_security_none_t *)&wccp_packet[offset]; - memcpy(security,service_list_ptr->security_info,sizeof(struct wccp2_security_none_t)); - offset += sizeof(struct wccp2_security_none_t); - - /* Service info */ - memcpy(&wccp_packet[offset],service_list_ptr->service_info,sizeof(struct wccp2_service_info_t)); - offset += sizeof(struct wccp2_service_info_t); - - /* assignment header - fill in length later */ - assignment_header=(struct wccp2_item_header_t *)&wccp_packet[offset]; - assignment_header->type=htons(WCCP2_REDIRECT_ASSIGNMENT); - offset += sizeof(struct wccp2_item_header_t); - - /* Assignment key - fill in master ip later */ - assignment_key=(struct assignment_key_t *)&wccp_packet[offset]; - assignment_key->master_number=htonl(++service_list_ptr->change_num); - offset += sizeof(struct assignment_key_t); - - /* Number of routers */ - xmemcpy(&wccp_packet[offset],&service_list_ptr->num_routers,sizeof(service_list_ptr->num_routers)); - offset += sizeof(service_list_ptr->num_routers); - - for (router_list_ptr=&service_list_ptr->router_list_head;router_list_ptr->next != NULL;router_list_ptr=router_list_ptr->next) { - - /* Add routers */ - router_assign=(struct wccp2_router_assign_element_t *)&wccp_packet[offset]; - router_assign->router_address=router_list_ptr->info->router_address; - router_assign->received_id=router_list_ptr->info->received_id; - router_assign->change_number=htonl(router_list_ptr->member_change); - offset += sizeof(struct wccp2_router_assign_element_t); - } - - saved_offset=offset; - for (router_list_ptr=&service_list_ptr->router_list_head;router_list_ptr->next != NULL;router_list_ptr=router_list_ptr->next) { - offset=saved_offset; - - /* Number of caches */ - xmemcpy(&wccp_packet[offset],&router_list_ptr->num_caches,sizeof(router_list_ptr->num_caches)); - offset += sizeof(router_list_ptr->num_caches); - - if(ntohl(router_list_ptr->num_caches)) { - for(cache_list_ptr=&router_list_ptr->cache_list_head;cache_list_ptr->next;cache_list_ptr=cache_list_ptr->next) { - /* add caches */ - cache_address=(struct in_addr *)&wccp_packet[offset]; - xmemcpy(cache_address,&cache_list_ptr->cache_ip,sizeof(struct in_addr)); - offset += sizeof(struct in_addr); - } - } - - /* Add buckets */ - buckets=(char *)&wccp_packet[offset]; - memset(buckets,'\0',WCCP_BUCKETS); - if(ntohl(router_list_ptr->num_caches) != 0) { - for(bucket_counter=0;bucket_counter < WCCP_BUCKETS;bucket_counter++) { - buckets[bucket_counter]=(char)(bucket_counter % ntohl(router_list_ptr->num_caches)); - } - } - - offset += (WCCP_BUCKETS * sizeof(char)); - - /* Fill in length */ - assignment_header->length=htons( sizeof(struct assignment_key_t) + sizeof(service_list_ptr->num_routers) + - (ntohl(service_list_ptr->num_routers) * sizeof(struct wccp2_router_assign_element_t)) + - sizeof(router_list_ptr->num_caches) + (ntohl(router_list_ptr->num_caches) * sizeof(struct in_addr)) + - (WCCP_BUCKETS * sizeof(char)) ); - - /* Fill in assignment key */ - assignment_key->master_ip=router_list_ptr->local_ip; - - /* finish length */ - main_header->length=htons(offset - sizeof(struct wccp2_here_i_am_header_t)); - - /* set the destination address */ - router.sin_addr = router_list_ptr->router_sendto_address; - - if(ntohl(router_list_ptr->num_caches)) { - /* send packet */ - sendto(theOutWccp2Connection, - &wccp_packet, - offset, - 0, - (struct sockaddr *)&router, - router_len); - } - } - } -} -#endif /* USE_WCCPv2 */ --- /dev/null Wed Feb 14 01:14:08 2007 +++ squid/src/wccp2.c Wed Feb 14 01:14:15 2007 @@ -0,0 +1,1170 @@ + +/* + * $Id: wccp2.c,v 1.1.2.1 2006/03/17 10:32:26 hno Exp $ + * + * DEBUG: section 80 WCCP Support + * AUTHOR: Steven WIlton + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ +#include "squid.h" + +#if USE_WCCPv2 +#include + +#define WCCP_PORT 2048 +#define WCCP_VERSION 4 +#define WCCP_REVISION 0 +#define WCCP_RESPONSE_SIZE 12448 +#define WCCP_ACTIVE_CACHES 32 +#define WCCP_HASH_SIZE 32 +#define WCCP_BUCKETS 256 + +#define WCCP_HERE_I_AM 7 +#define WCCP_I_SEE_YOU 8 +#define WCCP_ASSIGN_BUCKET 9 + +static int theInWccp2Connection = -1; +static int theOutWccp2Connection = -1; +static int wccp2_connected = 0; + +static PF wccp2HandleUdp; +static EVH wccp2HereIam; +static EVH wccp2AssignBuckets; + +/* KDW WCCP V2 */ +#define WCCP2_HERE_I_AM 10 +#define WCCP2_I_SEE_YOU 11 +#define WCCP2_REDIRECT_ASSIGN 12 +#define WCCP2_REMOVAL_QUERY 13 +#define WCCP2_VERSION 0x200 + +#define WCCP2_SECURITY_INFO 0 +#define WCCP2_NO_SECURITY 0 +#define WCCP2_MD5_SECURITY 1 /* Not Supported Yet */ + +#define WCCP2_SERVICE_INFO 1 +#define WCCP2_SERVICE_STANDARD 0 +#define WCCP2_SERVICE_DYNAMIC 1 /* Not Supported Yet */ +#define WCCP2_SERVICE_ID_HTTP 0x00 + +#define WCCP2_SERVICE_SRC_IP_HASH 0x1 +#define WCCP2_SERVICE_DST_IP_HASH 0x2 +#define WCCP2_SERVICE_SOURCE_PORT_HASH 0x4 +#define WCCP2_SERVICE_DST_PORT_HASH 0x8 +#define WCCP2_SERVICE_PORTS_DEFINED 0x10 +#define WCCP2_SERVICE_PORTS_SOURCE 0x20 +#define WCCP2_SERVICE_SRC_IP_ALT_HASH 0x100 +#define WCCP2_SERVICE_DST_IP_ALT_HASH 0x200 +#define WCCP2_SERVICE_SRC_PORT_ALT_HASH 0x400 +#define WCCP2_SERVICE_DST_PORT_ALT_HASH 0x800 + +#define WCCP2_ROUTER_ID_INFO 2 + +#define WCCP2_WC_ID_INFO 3 + +#define WCCP2_RTR_VIEW_INFO 4 + +#define WCCP2_WC_VIEW_INFO 5 + +#define WCCP2_REDIRECT_ASSIGNMENT 6 + +#define WCCP2_QUERY_INFO 7 + +#define WCCP2_CAPABILITY_INFO 8 + +#define WCCP2_CAPABILITY_FORWARDING_METHOD 0x01 +#define WCCP2_CAPABILITY_ASSIGNMENT_METHOD 0x02 +#define WCCP2_CAPABILITY_RETURN_METHOD 0x03 + +#define WCCP2_CAPABILITY_GRE 0x00000001 +#define WCCP2_CAPABILITY_L2 0x00000002 + + +/* WCCP v2 packet header */ +struct wccp2_here_i_am_header_t { + uint32_t type; + uint16_t version; + uint16_t length; +}; + +static struct wccp2_here_i_am_header_t wccp2_here_i_am_header; + +/* Security struct for the "no security" option */ +struct wccp2_security_none_t { + uint16_t security_type; + uint16_t security_length; + uint32_t security_option; +}; + +static struct wccp2_security_none_t wccp2_security_none; + +/* Service info struct */ +struct wccp2_service_info_t { + uint16_t service_type; + uint16_t service_length; + uint8_t service; + uint8_t service_id; + uint8_t service_priority; + uint8_t service_protocol; + uint32_t service_flags; + uint16_t port0; + uint16_t port1; + uint16_t port2; + uint16_t port3; + uint16_t port4; + uint16_t port5; + uint16_t port6; + uint16_t port7; +}; + +struct wccp2_cache_identity_info_t { + struct in_addr addr; + uint16_t hash_revision; + char bits[2]; + char buckets[32]; + uint16_t weight; + uint16_t status; +}; + +/* Web Cache identity info */ +struct wccp2_identity_info_t { + uint16_t cache_identity_type; + uint16_t cache_identity_length; + struct wccp2_cache_identity_info_t cache_identity; +}; + +static struct wccp2_identity_info_t wccp2_identity_info; + +/* View header */ +struct wccp2_cache_view_header_t { + uint16_t cache_view_type; + uint16_t cache_view_length; + uint32_t cache_view_version; +}; + +static struct wccp2_cache_view_header_t wccp2_cache_view_header; + +/* View info */ +struct wccp2_cache_view_info_t { + uint32_t num_routers; + uint32_t num_caches; +}; + +static struct wccp2_cache_view_info_t wccp2_cache_view_info; + +/* Router ID element */ +struct wccp2_router_id_element_t { + struct in_addr router_address; + uint32_t received_id; +}; + +static struct wccp2_router_id_element_t wccp2_router_id_element; + +/* Capability info header */ +struct wccp2_capability_info_header_t { + uint16_t capability_info_type; + uint16_t capability_info_length; +}; + +static struct wccp2_capability_info_header_t wccp2_capability_info_header; + +/* Capability element */ +struct wccp2_capability_element_t { + uint16_t capability_type; + uint16_t capability_length; + uint32_t capability_value; +}; + +static struct wccp2_capability_element_t wccp2_capability_element; + + +/* RECEIVED PACKET STRUCTURE */ +struct wccp2_i_see_you_t { + uint32_t type; + uint16_t version; + uint16_t length; + char data[WCCP_RESPONSE_SIZE]; +}; + +static struct wccp2_i_see_you_t wccp2_i_see_you; + +/* Router ID element */ +struct wccp2_router_assign_element_t { + struct in_addr router_address; + uint32_t received_id; + uint32_t change_number; +}; + +/* Generic header struct */ +struct wccp2_item_header_t { + uint16_t type; + uint16_t length; + }; + +/* Router identity struct */ +struct router_identity_info_t { + struct wccp2_item_header_t header; + struct wccp2_router_id_element_t router_id_element; + struct in_addr router_address; + uint32_t number_caches; +}; + +/* assigment key */ +struct assignment_key_t { + struct in_addr master_ip; + uint32_t master_number; +}; + +/* Router view of WCCP */ +struct router_view_t { + struct wccp2_item_header_t header; + uint32_t change_number; + struct assignment_key_t assignment_key; +}; + + +/* Lists used to keep track of caches, routers and services */ +struct wccp2_cache_list_t { + struct in_addr cache_ip; + struct wccp2_cache_list_t *next; +}; + +struct wccp2_router_list_t { + struct wccp2_router_id_element_t *info; + struct in_addr local_ip; + struct in_addr router_sendto_address; + uint32_t member_change; + uint32_t num_caches; + struct wccp2_cache_list_t cache_list_head; + struct wccp2_router_list_t *next; +}; + +static int wccp2_numrouters; + +struct wccp2_service_list_t { + struct wccp2_service_info_t info; + uint32_t num_routers; + struct wccp2_router_list_t router_list_head; + int lowest_ip; + uint32_t change_num; + struct wccp2_identity_info_t *wccp2_identity_info_ptr; + struct wccp2_security_none_t *security_info; + struct wccp2_service_info_t *service_info; + char wccp_packet[WCCP_RESPONSE_SIZE]; + size_t wccp_packet_size; + struct wccp2_service_list_t *next; +}; + +static struct wccp2_service_list_t wccp2_service_list_head; + +/* END WCCP V2 */ + +/* + * The functions used during startup: + * wccp2Init + * wccp2ConnectionOpen + * wccp2ConnectionShutdown + * wccp2ConnectionClose + */ + +void +wccp2InitServices(void) +{ + struct wccp2_service_list_t *wccp2_service_list_ptr; + wccp2_service_list_ptr=&wccp2_service_list_head; + + // TODO: Need to do something like this here: + // for each wccp_service configured { + // if(wccp2_service_list_ptr != &wccp2_service_list_head) { + // wccp2_service_list_ptr->next=xcalloc(1,sizeof(struct wccp2_service_list_t)); + // wccp2_service_list_ptr=wccp2_service_list_ptr->next; + // wccp2_service_list_ptr->next=NULL; + // } + // wccp2_service_list_ptr->info.service_type = htons(WCCP2_SERVICE_INFO); + // wccp2_service_list_ptr->info.service_length = htons(sizeof(struct wccp2_service_info_t)-4); + // wccp2_service_list_ptr->info.service = Config.wccp2.service_type; (dynamic/standard) + // wccp2_service_list_ptr->info.service_id = Config.wccp2.service_type; (WCCP2_SERVICE_ID_HTTP for http, or user-defined value for configured) + // wccp2_service_list_ptr->info.service_priority = Config.wccp2.service_priority; + // wccp2_service_list_ptr->info.service_protocol = Config.wccp2.service_protocol; (0 if service_type is WCCP2_SERVICE_ID_HTTP, or IPPROTO_TCP/IPPROTO_UDP) + // wccp2_service_list_ptr->info.service_flags = htonl(Config.wccp2.service_flags); (must be 0 if service_type is WCCP2_SERVICE_ID_HTTP, else must include WCCP2_SERVICE_PORTS_DEFINED to use the ports below. This deteries the hash method and whether the has is based on source or destination ports). + // + // Possibly have another loop here to define the ports? + // + // wccp2_service_list_ptr->info.port0 = htons(Config.wccp2.service_port1); + // wccp2_service_list_ptr->info.port1 = htons(Config.wccp2.service_port2); + // wccp2_service_list_ptr->info.port2 = htons(Config.wccp2.service_port3); + // wccp2_service_list_ptr->info.port3 = htons(Config.wccp2.service_port4); + // wccp2_service_list_ptr->info.port4 = htons(Config.wccp2.service_port5); + // wccp2_service_list_ptr->info.port5 = htons(Config.wccp2.service_port6); + // wccp2_service_list_ptr->info.port6 = htons(Config.wccp2.service_port7); + // wccp2_service_list_ptr->info.port7 = htons(Config.wccp2.service_port8); + // wccp2_service_list_ptr->change_num=0; + // } + // + // The first thing that needs doing is to define a struct in the Config code + // to store the above information in some form of a linked list. + // + // Most people will only need to define something like: + // wccp2_service 0 100 + // + // This defines the service type as the standard http service with a + // priority of 100. If the service number is 0, no other flags will be + // parsed. + // + // If anyone wanted to define a custom service, we would want the following + // options + // wccp2_service number priority [flags] [ports] + // + // [flags] should include: + // hash_source_ip - to use the source ip address to determine which cache to send the packet to (can be used to make sure the same ip always hits the same cache). This would override the default beahviour of using the destination ip. + // defined_ports_source - to use the source port to determine whether a packet will be redirected by WCCP (useful for catching reply packets if the proxy server is forging the source ip address) + // proto_udp - to override the default protocol type of tcp + // + // [ports] would be up to 8 ports used to determine which packets to redirect + + /* This defines WCCP service number 80 (redirect traffic going to tcp port + 80, using the client ip as the hash) */ + wccp2_service_list_ptr->info.service_type = htons(WCCP2_SERVICE_INFO); + wccp2_service_list_ptr->info.service_length = htons(sizeof(struct wccp2_service_info_t)-4); + wccp2_service_list_ptr->info.service = WCCP2_SERVICE_DYNAMIC; + wccp2_service_list_ptr->info.service_id = 80; + wccp2_service_list_ptr->info.service_priority = 240; + wccp2_service_list_ptr->info.service_protocol = IPPROTO_TCP; + wccp2_service_list_ptr->info.service_flags = htonl(WCCP2_SERVICE_SRC_IP_HASH + WCCP2_SERVICE_PORTS_DEFINED); + wccp2_service_list_ptr->info.port0 = htons(80); + wccp2_service_list_ptr->info.port1 = htons(0); + wccp2_service_list_ptr->info.port2 = htons(0); + wccp2_service_list_ptr->info.port3 = htons(0); + wccp2_service_list_ptr->info.port4 = htons(0); + wccp2_service_list_ptr->info.port5 = htons(0); + wccp2_service_list_ptr->info.port6 = htons(0); + wccp2_service_list_ptr->info.port7 = htons(0); + wccp2_service_list_ptr->change_num=0; + + wccp2_service_list_ptr->next=xcalloc(1,sizeof(struct wccp2_service_list_t)); + wccp2_service_list_ptr=wccp2_service_list_ptr->next; + wccp2_service_list_ptr->next=NULL; + + /* This defines WCCP service number 90 (redirect traffic coming from tcp + port 80, using the client ip as the hash) */ + wccp2_service_list_ptr->info.service_type = htons(WCCP2_SERVICE_INFO); + wccp2_service_list_ptr->info.service_length = htons(sizeof(struct wccp2_service_info_t)-4); + wccp2_service_list_ptr->info.service = WCCP2_SERVICE_DYNAMIC; + wccp2_service_list_ptr->info.service_id = 90; + wccp2_service_list_ptr->info.service_priority = 240; + wccp2_service_list_ptr->info.service_protocol = IPPROTO_TCP; + wccp2_service_list_ptr->info.service_flags = htonl(WCCP2_SERVICE_DST_IP_HASH + WCCP2_SERVICE_PORTS_DEFINED + WCCP2_SERVICE_PORTS_SOURCE); + wccp2_service_list_ptr->info.port0 = htons(80); + wccp2_service_list_ptr->info.port1 = htons(0); + wccp2_service_list_ptr->info.port2 = htons(0); + wccp2_service_list_ptr->info.port3 = htons(0); + wccp2_service_list_ptr->info.port4 = htons(0); + wccp2_service_list_ptr->info.port5 = htons(0); + wccp2_service_list_ptr->info.port6 = htons(0); + wccp2_service_list_ptr->info.port7 = htons(0); + wccp2_service_list_ptr->change_num=0; + wccp2_service_list_ptr->next=xcalloc(1,sizeof(struct wccp2_service_list_t)); + wccp2_service_list_ptr=wccp2_service_list_ptr->next; + wccp2_service_list_ptr->next=NULL; +} + +void +wccp2Init(void) +{ + sockaddr_in_list *s; + char *ptr; + struct wccp2_service_list_t *service_list_ptr; + struct wccp2_router_list_t *router_list_ptr; + + debug(80, 5) ("wccp2Init: Called\n"); + if (wccp2_connected == 1) + return; + wccp2_numrouters=0; + + /* Calculate the number of routers configured in the config file */ + for (s = Config.Wccp2.router; s; s = s->next) { + if(s->s.sin_addr.s_addr != any_addr.s_addr) { + /* Increment the counter */ + wccp2_numrouters++; + } + } + if(wccp2_numrouters == 0) { + return; + } + + /* Initialise the list of services */ + wccp2InitServices(); + + for(service_list_ptr=&wccp2_service_list_head;service_list_ptr->next;service_list_ptr=service_list_ptr->next) { + /* Set up our list pointers */ + router_list_ptr=&service_list_ptr->router_list_head; + + /* start the wccp header */ + wccp2_here_i_am_header.type = htonl(WCCP2_HERE_I_AM); + wccp2_here_i_am_header.version = htons(WCCP2_VERSION); + wccp2_here_i_am_header.length = 0; + ptr = service_list_ptr->wccp_packet + sizeof(wccp2_here_i_am_header); + + /* add the security section */ + wccp2_here_i_am_header.length += sizeof(struct wccp2_security_none_t); + assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); + wccp2_security_none.security_type = htons(WCCP2_SECURITY_INFO); + wccp2_security_none.security_length = htons(sizeof(struct wccp2_security_none_t)-4); + wccp2_security_none.security_option = htonl(WCCP2_NO_SECURITY); /* TODO: MD5 security */ + xmemcpy(ptr,&wccp2_security_none,sizeof(struct wccp2_security_none_t)); + service_list_ptr->security_info=(struct wccp2_security_none_t *)ptr; + ptr += sizeof(struct wccp2_security_none_t); + + /* Add the service info section */ + wccp2_here_i_am_header.length += sizeof(struct wccp2_service_info_t); + assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); + xmemcpy(ptr,&service_list_ptr->info,sizeof(struct wccp2_service_info_t)); + service_list_ptr->service_info = (struct wccp2_service_info_t *)ptr; + ptr += sizeof(struct wccp2_service_info_t); + + /* Add the cache identity section */ + wccp2_here_i_am_header.length += sizeof(struct wccp2_identity_info_t); + assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); + wccp2_identity_info.cache_identity_type = htons(WCCP2_WC_ID_INFO); + wccp2_identity_info.cache_identity_length = htons(sizeof(wccp2_identity_info.cache_identity)); + memset(&wccp2_identity_info.cache_identity.addr,'\0',sizeof(wccp2_identity_info.cache_identity.addr)); + memset(&wccp2_identity_info.cache_identity.hash_revision,'\0',sizeof(wccp2_identity_info.cache_identity.hash_revision)); + memset(&wccp2_identity_info.cache_identity.bits,'\0',sizeof(wccp2_identity_info.cache_identity.bits)); + memset(&wccp2_identity_info.cache_identity.buckets,'\0',sizeof(wccp2_identity_info.cache_identity.buckets)); + wccp2_identity_info.cache_identity.weight = htons(10000); + memset(&wccp2_identity_info.cache_identity.status,'\0',sizeof(wccp2_identity_info.cache_identity.status)); + xmemcpy(ptr,&wccp2_identity_info,sizeof(struct wccp2_identity_info_t)); + service_list_ptr->wccp2_identity_info_ptr = (struct wccp2_identity_info_t *)ptr; + ptr += sizeof(struct wccp2_identity_info_t); + + /* Add the cache view section */ + wccp2_here_i_am_header.length += sizeof(wccp2_cache_view_header); + assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); + wccp2_cache_view_header.cache_view_type = htons(WCCP2_WC_VIEW_INFO); + wccp2_cache_view_header.cache_view_length = htons(sizeof(wccp2_cache_view_header) - 4 + + sizeof(wccp2_cache_view_info) + (wccp2_numrouters * sizeof(wccp2_router_id_element)) ); + wccp2_cache_view_header.cache_view_version = htonl(1); + xmemcpy(ptr,&wccp2_cache_view_header,sizeof(wccp2_cache_view_header)); + ptr += sizeof(wccp2_cache_view_header); + + /* Add the number of routers to the packet */ + wccp2_here_i_am_header.length += sizeof(service_list_ptr->num_routers); + assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); + service_list_ptr->num_routers = htonl(wccp2_numrouters); + xmemcpy(ptr,&service_list_ptr->num_routers,sizeof(service_list_ptr->num_routers)); + ptr += sizeof(service_list_ptr->num_routers); + + /* Add each router. Keep this functionality here to make sure the received_id can be updated in the packet */ + for (s = Config.Wccp2.router; s; s = s->next) { + if(s->s.sin_addr.s_addr != any_addr.s_addr) { + wccp2_here_i_am_header.length += sizeof(struct wccp2_router_id_element_t); + assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); + + /* Add a pointer to the router list for this router */ + router_list_ptr->info=(struct wccp2_router_id_element_t *)ptr; + router_list_ptr->info->router_address=s->s.sin_addr; + router_list_ptr->info->received_id = htonl(0); + router_list_ptr->router_sendto_address=s->s.sin_addr; + router_list_ptr->member_change = htonl(0); + + /* Build the next struct */ + router_list_ptr->next=xcalloc(1,sizeof(struct wccp2_router_list_t)); + + /* update the pointer */ + router_list_ptr=router_list_ptr->next; + router_list_ptr->next=NULL; + + /* no need to copy memory - we've just set the values directly in the packet above */ + ptr+=sizeof(struct wccp2_router_id_element_t); + } + } + + /* Add the number of caches (0) */ + wccp2_here_i_am_header.length += sizeof(wccp2_cache_view_info.num_caches); + assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); + wccp2_cache_view_info.num_caches = htonl(0); + xmemcpy(ptr,&wccp2_cache_view_info.num_caches,sizeof(wccp2_cache_view_info.num_caches)); + ptr += sizeof(wccp2_cache_view_info.num_caches); + + /* Add the extra capability header */ + wccp2_here_i_am_header.length += sizeof(wccp2_capability_info_header); + assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); + wccp2_capability_info_header.capability_info_type = htons(WCCP2_CAPABILITY_INFO); + wccp2_capability_info_header.capability_info_length = htons(2 * sizeof(wccp2_capability_element)); + xmemcpy(ptr,&wccp2_capability_info_header,sizeof(wccp2_capability_info_header)); + ptr += sizeof(wccp2_capability_info_header); + + /* Add the forwarding method */ + wccp2_here_i_am_header.length += sizeof(wccp2_capability_element); + assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); + wccp2_capability_element.capability_type = htons(WCCP2_CAPABILITY_FORWARDING_METHOD); + wccp2_capability_element.capability_length = htons(sizeof(wccp2_capability_element.capability_value)); + wccp2_capability_element.capability_value = htonl(Config.Wccp2.forwarding_method); + xmemcpy(ptr,&wccp2_capability_element,sizeof(wccp2_capability_element)); + ptr += sizeof(wccp2_capability_element); + + /* Add the return method */ + wccp2_here_i_am_header.length += sizeof(wccp2_capability_element); + assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE); + wccp2_capability_element.capability_type = htons(WCCP2_CAPABILITY_RETURN_METHOD); + wccp2_capability_element.capability_length = htons(sizeof(wccp2_capability_element.capability_value)); + wccp2_capability_element.capability_value = htonl(Config.Wccp2.return_method); + xmemcpy(ptr,&wccp2_capability_element,sizeof(wccp2_capability_element)); + ptr += sizeof(wccp2_capability_element); + + /* Finally, fix the total length to network order, and copy to the appropriate memory blob */ + wccp2_here_i_am_header.length = htons(wccp2_here_i_am_header.length); + memcpy(&service_list_ptr->wccp_packet,&wccp2_here_i_am_header,sizeof(wccp2_here_i_am_header)); + service_list_ptr->wccp_packet_size=ntohs(wccp2_here_i_am_header.length) + sizeof(wccp2_here_i_am_header); + + /* Add the event if everything initialised correctly */ + if(wccp2_numrouters) { + if (!eventFind(wccp2HereIam, NULL)) { + eventAdd("wccp2HereIam", wccp2HereIam, NULL, 10.0, 1); + } + } + } +} + +void +wccp2ConnectionOpen(void) +{ + u_short port = WCCP_PORT; + struct sockaddr_in router, local; + int local_len, router_len; + struct wccp2_service_list_t *service_list_ptr; + struct wccp2_router_list_t *router_list_ptr; + + debug(80, 5) ("wccp2ConnectionOpen: Called\n"); + if(wccp2_numrouters == 0) { + debug(80, 1) ("WCCPv2 Disabled.\n"); + } + + theInWccp2Connection = comm_open(SOCK_DGRAM, + 0, + Config.Wccp2.incoming, + port, + COMM_NONBLOCKING, + "WCCP Socket"); + if (theInWccp2Connection < 0) + fatal("Cannot open WCCP Port"); + commSetSelect(theInWccp2Connection, + COMM_SELECT_READ, + wccp2HandleUdp, + NULL, + 0); + debug(80, 1) ("Accepting WCCP v2 messages on port %d, FD %d.\n", + (int) port, theInWccp2Connection); + + if (Config.Wccp2.outgoing.s_addr != no_addr.s_addr) { + theOutWccpConnection = comm_open(SOCK_DGRAM, + 0, + Config.Wccp2.outgoing, + port, + COMM_NONBLOCKING, + "WCCP Socket"); + if (theOutWccpConnection < 0) + fatal("Cannot open Outgoing WCCP Port"); + commSetSelect(theOutWccpConnection, + COMM_SELECT_READ, + wccp2HandleUdp, + NULL, 0); + debug(80, 1) ("Outgoing WCCPv2 messages on port %d, FD %d.\n", + (int) port, theOutWccpConnection); + fd_note(theOutWccpConnection, "Outgoing WCCP socket"); + fd_note(theInWccpConnection, "Incoming WCCP socket"); + } + else + { + theOutWccp2Connection = theInWccp2Connection; + } + + debug(80, 1) ("Initialising all WCCPv2 lists\n"); + + /* Initialise all routers on all services */ + for(service_list_ptr=&wccp2_service_list_head;service_list_ptr->next;service_list_ptr=service_list_ptr->next) { + for (router_list_ptr=&service_list_ptr->router_list_head;router_list_ptr->next != NULL;router_list_ptr=router_list_ptr->next) { + router_len = sizeof(router); + memset(&router, '\0', router_len); + router.sin_family = AF_INET; + router.sin_port = htons(port); + router.sin_addr = router_list_ptr->router_sendto_address; + if (connect(theOutWccp2Connection, (struct sockaddr *) &router, router_len)) + fatal("Unable to connect WCCP out socket"); + local_len = sizeof(local); + memset(&local, '\0', local_len); + if (getsockname(theOutWccp2Connection, (struct sockaddr *) &local, &local_len)) + fatal("Unable to getsockname on WCCP out socket"); + local_ip.s_addr = local.sin_addr.s_addr; + + router_list_ptr->local_ip=local_ip; + + /* Disconnect the sending socket */ + router.sin_family = AF_UNSPEC; + if (connect(theOutWccp2Connection, (struct sockaddr *) &router, router_len)) + fatal("Unable to disconnect WCCP out socket"); + } + } + wccp2_connected=1; +} + +void +wccp2ConnectionShutdown(void) +{ + if (theInWccp2Connection < 0) + return; + if(wccp2_connected == 0) + return; + if (theInWccp2Connection != theOutWccp2Connection) { + debug(80, 1) ("FD %d Closing WCCP socket\n", theInWccp2Connection); + comm_close(theInWccp2Connection); + } + assert(theOutWccp2Connection > -1); + commSetSelect(theOutWccp2Connection, COMM_SELECT_READ, NULL, NULL, 0); +} + +void +wccp2ConnectionClose(void) +{ + struct wccp2_service_list_t *service_list_ptr; + struct wccp2_service_list_t *service_list_ptr_next; + struct wccp2_router_list_t *router_list_ptr; + struct wccp2_router_list_t *router_list_next; + struct wccp2_cache_list_t *cache_list_ptr; + struct wccp2_cache_list_t *cache_list_ptr_next; + + if(wccp2_connected == 0) { + return; + } + wccp2ConnectionShutdown(); + if (theOutWccp2Connection > -1) { + debug(80, 1) ("FD %d Closing WCCP socket\n", theOutWccp2Connection); + comm_close(theOutWccp2Connection); + } + /* for each router on each service send a packet */ + for(service_list_ptr=&wccp2_service_list_head;service_list_ptr;service_list_ptr=service_list_ptr_next) { + for (router_list_ptr=&service_list_ptr->router_list_head;router_list_ptr != NULL;router_list_ptr=router_list_next) { + for(cache_list_ptr=&router_list_ptr->cache_list_head;cache_list_ptr;cache_list_ptr=cache_list_ptr_next) { + cache_list_ptr_next=cache_list_ptr->next; + if(cache_list_ptr != &router_list_ptr->cache_list_head) { + xfree(cache_list_ptr); + } + else + { + memset(cache_list_ptr,'\0',sizeof(struct wccp2_cache_list_t)); + } + } + router_list_next=router_list_ptr->next; + if(router_list_ptr != &service_list_ptr->router_list_head) { + xfree(router_list_ptr); + } + else + { + memset(router_list_ptr,'\0',sizeof(struct wccp2_router_list_t)); + } + } + service_list_ptr_next=service_list_ptr->next; + if(service_list_ptr != &wccp2_service_list_head) { + xfree(service_list_ptr); + } + else + { + memset(service_list_ptr,'\0',sizeof(struct wccp2_service_list_t)); + } + } + wccp2_connected=0; +} + +/* + * Functions for handling the requests. + */ + +/* + * Accept the UDP packet + */ +static void +wccp2HandleUdp(int sock, void *not_used) +{ + struct wccp2_service_list_t *service_list_ptr; + struct wccp2_router_list_t *router_list_ptr; + struct wccp2_cache_list_t *cache_list_ptr; + struct wccp2_cache_list_t *cache_list_ptr_next; + + /* These structs form the parts of the packet */ + struct wccp2_item_header_t *header=NULL; + struct wccp2_security_none_t *security_info=NULL; + struct wccp2_service_info_t *service_info=NULL; + struct router_identity_info_t *router_identity_info=NULL; + struct router_view_t *router_view_header=NULL; + struct wccp2_cache_identity_info_t *cache_identity=NULL; + struct wccp2_capability_info_header_t *router_capability_header=NULL; + struct wccp2_capability_element_t *router_capability_element; + + struct sockaddr_in from; + struct in_addr cache_address; + socklen_t from_len; + int len,found; + short int data_length, offset; + uint32_t tmp; + char *ptr; + int num_caches; + uint16_t num_capabilities; + + debug(80, 6) ("wccp2HandleUdp: Called.\n"); + + commSetSelect(sock, COMM_SELECT_READ, wccp2HandleUdp, NULL, 0); + from_len = sizeof(struct sockaddr_in); + memset(&from, '\0', from_len); + + statCounter.syscalls.sock.recvfroms++; + + len = recvfrom(sock, + &wccp2_i_see_you, + WCCP_RESPONSE_SIZE, + 0, + (struct sockaddr *) &from, + &from_len); + + if (len < 0) + return; + if (ntohs(wccp2_i_see_you.version) != WCCP2_VERSION) + return; + if (ntohl(wccp2_i_see_you.type) != WCCP2_I_SEE_YOU) + return; + + debug(80, 3) ("Incoming WCCP v2 I_SEE_YOU length %d.\n", ntohs(wccp2_i_see_you.length)); + + /* Record the total data length */ + data_length=ntohs(wccp2_i_see_you.length); + offset=0; + + if(data_length > len) { + debug(80, 1) ("ERROR: Malformed WCCPv2 packet claiming it's bigger than received data\n"); + return; + } + + /* Go through the data structure */ + while(data_length > offset) { + header=(struct wccp2_item_header_t *)&wccp2_i_see_you.data[offset]; + + switch(ntohs(header->type)) { + case WCCP2_SECURITY_INFO: + if(security_info != NULL ) { + debug(80, 1) ("Duplicate security definition\n"); + return; + } + security_info=(struct wccp2_security_none_t *)&wccp2_i_see_you.data[offset]; + break; + case WCCP2_SERVICE_INFO: + if(service_info != NULL ) { + debug(80, 1) ("Duplicate service_info definition\n"); + return; + } + service_info=(struct wccp2_service_info_t *)&wccp2_i_see_you.data[offset]; + break; + case WCCP2_ROUTER_ID_INFO: + if(router_identity_info != NULL ) { + debug(80, 1) ("Duplicate router_identity_info definition\n"); + return; + } + router_identity_info=(struct router_identity_info_t *)&wccp2_i_see_you.data[offset]; + break; + case WCCP2_RTR_VIEW_INFO: + if(router_view_header != NULL ) { + debug(80, 1) ("Duplicate router_view definition\n"); + return; + } + router_view_header=(struct router_view_t *)&wccp2_i_see_you.data[offset]; + break; + case WCCP2_CAPABILITY_INFO: + if(router_capability_header != NULL ) { + debug(80, 1) ("Duplicate router_capability definition\n"); + return; + } + router_capability_header=(struct wccp2_capability_info_header_t *)&wccp2_i_see_you.data[offset]; + return; + default: + debug(80, 1) ("Unknown record type in WCCPv2 Packet (%d).\n", + ntohs(header->type)); + } + offset+=sizeof(struct wccp2_item_header_t); + offset+=ntohs(header->length); + if(offset > data_length) { + debug(80,1) ("Error: WCCPv2 packet tried to tell us there is data beyond the end of the packet\n"); + return; + } + } + if((security_info == NULL) || (service_info == NULL) || (router_identity_info == NULL) || (router_view_header == NULL)) { + debug(80, 1) ("Incomplete WCCPv2 Packet\n"); + return; + } + + debug (80,5) ("Complete packet received\n"); + + /* Check that the service in the packet is configured on this router */ + for(service_list_ptr=&wccp2_service_list_head;service_list_ptr->next;service_list_ptr=service_list_ptr->next) { + if(service_info->service_id == service_list_ptr->service_info->service_id) { + break; + } + } + if(!(service_list_ptr->next)) { + debug(80, 1) ("WCCPv2 Unknown service received from router\n"); + return; + } + + /* TODO: MD5 security */ + if(ntohl(security_info->security_option) != service_list_ptr->security_info->security_option) { + debug(80, 1) ("Invalid security option in WCCPv2 Packet (%d).\n", + ntohl(security_info->security_option)); + return; + } + + /* Check that the router address is configured on this router */ + for (router_list_ptr=&service_list_ptr->router_list_head;router_list_ptr->next != NULL;router_list_ptr=router_list_ptr->next) { + if(router_list_ptr->router_sendto_address.s_addr == from.sin_addr.s_addr) + break; + } + if(router_list_ptr->next == NULL) { + debug(80, 1) ("WCCPv2 Packet received from unknown router\n"); + return; + } + + /* Set the router id */ + router_list_ptr->info->router_address=router_identity_info->router_id_element.router_address; + + /* Increment the received id in the packet */ + if ( ntohl(router_list_ptr->info->received_id) != ntohl(router_identity_info->router_id_element.received_id) ) { + debug(80, 3) ("Incoming WCCP2_I_SEE_YOU member change = %d tmp=%d.\n", + ntohl(router_list_ptr->info->received_id), ntohl(router_identity_info->router_id_element.received_id)); + router_list_ptr->info->received_id = router_identity_info->router_id_element.received_id; + } + + /* TODO: check return/forwarding methods */ + if(router_capability_header == NULL) { + if((Config.Wccp2.return_method != WCCP2_CAPABILITY_GRE) || (Config.Wccp2.forwarding_method != WCCP2_CAPABILITY_GRE)) { + debug (80,1) ("wccp2HandleUdp: fatal error - A WCCP router does not support the forwarding method specified\n"); + wccp2ConnectionClose(); + return; + } + } + else + { + num_capabilities=ntohs(router_capability_header->capability_info_length); + /* run through each capability element from last to first */ + if(num_capabilities > 0) { + num_capabilities--; + router_capability_element=(struct wccp2_capability_element_t *)(router_capability_header) + sizeof(struct wccp2_capability_info_header_t) + (num_capabilities * sizeof(struct wccp2_capability_element_t)); + switch(ntohs(router_capability_element->capability_type)) { + case WCCP2_CAPABILITY_FORWARDING_METHOD: + if(ntohl(router_capability_element->capability_value) != Config.Wccp2.forwarding_method) { + debug (80,1) ("wccp2HandleUdp: fatal error - A WCCP router has specified a different forwarding method\n"); + wccp2ConnectionClose(); + return; + } + break; + case WCCP2_CAPABILITY_ASSIGNMENT_METHOD: + /* we don't current care */ + break; + case WCCP2_CAPABILITY_RETURN_METHOD: + if(ntohl(router_capability_element->capability_value) != Config.Wccp2.return_method) { + debug (80,1) ("wccp2HandleUdp: fatal error - A WCCP router has specified a different return method\n"); + wccp2ConnectionClose(); + return; + } + break; + default: + debug(80, 1) ("Unknown capability type in WCCPv2 Packet (%d).\n", + ntohs(router_capability_element->capability_type)); + } + } + } + + debug(80, 5) ("Cleaning out cache list\n"); + /* clean out the old cache list */ + for(cache_list_ptr=&router_list_ptr->cache_list_head;cache_list_ptr;cache_list_ptr=cache_list_ptr_next) { + cache_list_ptr_next=cache_list_ptr->next; + if(cache_list_ptr != &router_list_ptr->cache_list_head) { + xfree(cache_list_ptr); + } + } + router_list_ptr->num_caches=htonl(0); + num_caches=0; + + /* Check to see if we're the master cache and update the cache list */ + found=0; + service_list_ptr->lowest_ip=1; + cache_list_ptr=&router_list_ptr->cache_list_head; + + /* to find the list of caches, we start at the end of the router view header */ + ptr=(char *)(router_view_header) + sizeof(struct router_view_t); + + /* Then we read the number of routers */ + memcpy(&tmp,ptr,sizeof(tmp)); + + /* skip the number plus all the ip's */ + ptr+=sizeof(tmp) + (ntohl(tmp) * sizeof(struct in_addr)); + + /* Then read the number of caches */ + memcpy(&tmp,ptr,sizeof(tmp)); + ptr+=sizeof(tmp); + + if(ntohl(tmp) != 0) { + /* search through the list of received-from ip addresses */ + for(num_caches=0;num_caches< ntohl(tmp);num_caches++) { + /* Get a copy of the ip */ + cache_identity=(struct wccp2_cache_identity_info_t *)ptr; + ptr+=sizeof(struct wccp2_cache_identity_info_t); + memcpy(&cache_address,&cache_identity->addr,sizeof(struct in_addr)); + + /* Update the cache list */ + cache_list_ptr->cache_ip=cache_address; + cache_list_ptr->next=xcalloc(1,sizeof(struct wccp2_cache_list_t)); + cache_list_ptr=cache_list_ptr->next; + cache_list_ptr->next=NULL; + + debug(80,5) ("checking cache list: (%x:%x)\n",cache_address.s_addr,router_list_ptr->local_ip.s_addr); + /* Check to see if it's the master, or us */ + if(cache_address.s_addr == router_list_ptr->local_ip.s_addr) { + found=1; + } + if(cache_address.s_addr < router_list_ptr->local_ip.s_addr) { + service_list_ptr->lowest_ip=0; + } + } + } + else + { + + /* Update the cache list */ + cache_list_ptr->cache_ip=router_list_ptr->local_ip; + cache_list_ptr->next=xcalloc(1,sizeof(struct wccp2_cache_list_t)); + cache_list_ptr=cache_list_ptr->next; + cache_list_ptr->next=NULL; + + service_list_ptr->lowest_ip=0; + found=1; + num_caches=1; + } + router_list_ptr->num_caches=htonl(num_caches); + + if ((found == 1) && (service_list_ptr->lowest_ip == 1)) { + if( ntohl(router_view_header->change_number) != router_list_ptr->member_change ) { + debug (80,4) ("Change detected - queueing up new assignment\n"); + router_list_ptr->member_change = ntohl(router_view_header->change_number); + if (!eventFind(wccp2AssignBuckets, NULL)) { + eventAdd("wccp2AssignBuckets", wccp2AssignBuckets, NULL, 15.0, 1); + } + } + } + else + { + debug (80,5) ("I am not the lowest ip cache - not assigning buckets\n"); + } +} + +static void +wccp2HereIam(void *voidnotused) +{ + struct wccp2_service_list_t *service_list_ptr; + struct wccp2_router_list_t *router_list_ptr; + struct sockaddr_in router; + int router_len; + u_short port = WCCP_PORT; + + debug(80, 6) ("wccp2HereIam: Called\n"); + if(wccp2_connected == 0) { + debug (80,1) ("wccp2HereIam: wccp2 socket closed. Shutting down WCCP2\n"); + return; + } + + router_len = sizeof(router); + memset(&router, '\0', router_len); + router.sin_family = AF_INET; + router.sin_port = htons(port); + + /* for each router on each service send a packet */ + for(service_list_ptr=&wccp2_service_list_head;service_list_ptr->next;service_list_ptr=service_list_ptr->next) { + for (router_list_ptr=&service_list_ptr->router_list_head;router_list_ptr->next != NULL;router_list_ptr=router_list_ptr->next) { + router.sin_addr = router_list_ptr->router_sendto_address; + + /* Set the cache id (ip) */ + service_list_ptr->wccp2_identity_info_ptr->cache_identity.addr=router_list_ptr->local_ip; + + debug(80,3) ("Sending HereIam packet size %d\n",service_list_ptr->wccp_packet_size); + /* Send the packet */ + sendto(theOutWccp2Connection, + &service_list_ptr->wccp_packet, + service_list_ptr->wccp_packet_size, + 0, + (struct sockaddr *)&router, + router_len); + } + } + + if (!eventFind(wccp2HereIam, NULL)) + eventAdd("wccp2HereIam", wccp2HereIam, NULL, 10.0, 1); +} + +static void +wccp2AssignBuckets(void *voidnotused) +{ + struct wccp2_service_list_t *service_list_ptr; + struct wccp2_router_list_t *router_list_ptr; + struct wccp2_cache_list_t *cache_list_ptr; + char wccp_packet[WCCP_RESPONSE_SIZE]; + short int offset,saved_offset; + struct sockaddr_in router; + int router_len; + int bucket_counter; + u_short port = WCCP_PORT; + + /* Packet segments */ + struct wccp2_here_i_am_header_t *main_header; + struct wccp2_security_none_t *security; /* TODO: add MD5 */ + /* service from service struct */ + struct wccp2_item_header_t *assignment_header; + struct assignment_key_t *assignment_key; + /* number of routers */ + struct wccp2_router_assign_element_t *router_assign; + /* number of caches */ + struct in_addr *cache_address; + char *buckets; + + router_len = sizeof(router); + memset(&router, '\0', router_len); + router.sin_family = AF_INET; + router.sin_port = htons(port); + + /* Start main header - fill in length later */ + offset=0; + main_header=(struct wccp2_here_i_am_header_t *)&wccp_packet[offset]; + main_header->type=htonl(WCCP2_REDIRECT_ASSIGN); + main_header->version=htons(WCCP2_VERSION); + + debug(80,2) ("Running wccp2AssignBuckets\n"); + for(service_list_ptr=&wccp2_service_list_head;service_list_ptr->next;service_list_ptr=service_list_ptr->next) { + + /* If we're not the lowest, we don't need to worry */ + if(service_list_ptr->lowest_ip == 0) { + continue; + } + + /* reset the offset */ + offset = sizeof(struct wccp2_here_i_am_header_t); + + /* build packet header from hereIam packet */ + /* Security info */ + security=(struct wccp2_security_none_t *)&wccp_packet[offset]; + memcpy(security,service_list_ptr->security_info,sizeof(struct wccp2_security_none_t)); + offset += sizeof(struct wccp2_security_none_t); + + /* Service info */ + memcpy(&wccp_packet[offset],service_list_ptr->service_info,sizeof(struct wccp2_service_info_t)); + offset += sizeof(struct wccp2_service_info_t); + + /* assignment header - fill in length later */ + assignment_header=(struct wccp2_item_header_t *)&wccp_packet[offset]; + assignment_header->type=htons(WCCP2_REDIRECT_ASSIGNMENT); + offset += sizeof(struct wccp2_item_header_t); + + /* Assignment key - fill in master ip later */ + assignment_key=(struct assignment_key_t *)&wccp_packet[offset]; + assignment_key->master_number=htonl(++service_list_ptr->change_num); + offset += sizeof(struct assignment_key_t); + + /* Number of routers */ + xmemcpy(&wccp_packet[offset],&service_list_ptr->num_routers,sizeof(service_list_ptr->num_routers)); + offset += sizeof(service_list_ptr->num_routers); + + for (router_list_ptr=&service_list_ptr->router_list_head;router_list_ptr->next != NULL;router_list_ptr=router_list_ptr->next) { + + /* Add routers */ + router_assign=(struct wccp2_router_assign_element_t *)&wccp_packet[offset]; + router_assign->router_address=router_list_ptr->info->router_address; + router_assign->received_id=router_list_ptr->info->received_id; + router_assign->change_number=htonl(router_list_ptr->member_change); + offset += sizeof(struct wccp2_router_assign_element_t); + } + + saved_offset=offset; + for (router_list_ptr=&service_list_ptr->router_list_head;router_list_ptr->next != NULL;router_list_ptr=router_list_ptr->next) { + offset=saved_offset; + + /* Number of caches */ + xmemcpy(&wccp_packet[offset],&router_list_ptr->num_caches,sizeof(router_list_ptr->num_caches)); + offset += sizeof(router_list_ptr->num_caches); + + if(ntohl(router_list_ptr->num_caches)) { + for(cache_list_ptr=&router_list_ptr->cache_list_head;cache_list_ptr->next;cache_list_ptr=cache_list_ptr->next) { + /* add caches */ + cache_address=(struct in_addr *)&wccp_packet[offset]; + xmemcpy(cache_address,&cache_list_ptr->cache_ip,sizeof(struct in_addr)); + offset += sizeof(struct in_addr); + } + } + + /* Add buckets */ + buckets=(char *)&wccp_packet[offset]; + memset(buckets,'\0',WCCP_BUCKETS); + if(ntohl(router_list_ptr->num_caches) != 0) { + for(bucket_counter=0;bucket_counter < WCCP_BUCKETS;bucket_counter++) { + buckets[bucket_counter]=(char)(bucket_counter % ntohl(router_list_ptr->num_caches)); + } + } + + offset += (WCCP_BUCKETS * sizeof(char)); + + /* Fill in length */ + assignment_header->length=htons( sizeof(struct assignment_key_t) + sizeof(service_list_ptr->num_routers) + + (ntohl(service_list_ptr->num_routers) * sizeof(struct wccp2_router_assign_element_t)) + + sizeof(router_list_ptr->num_caches) + (ntohl(router_list_ptr->num_caches) * sizeof(struct in_addr)) + + (WCCP_BUCKETS * sizeof(char)) ); + + /* Fill in assignment key */ + assignment_key->master_ip=router_list_ptr->local_ip; + + /* finish length */ + main_header->length=htons(offset - sizeof(struct wccp2_here_i_am_header_t)); + + /* set the destination address */ + router.sin_addr = router_list_ptr->router_sendto_address; + + if(ntohl(router_list_ptr->num_caches)) { + /* send packet */ + sendto(theOutWccp2Connection, + &wccp_packet, + offset, + 0, + (struct sockaddr *)&router, + router_len); + } + } + } +} +#endif /* USE_WCCPv2 */