/* * $Id$ * * DEBUG: section 80 WCCP Support * AUTHOR: Glenn Chisholm * * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ * ---------------------------------------------------------- * * Squid is the result of efforts by numerous individuals from the * Internet community. Development is led by Duane Wessels of the * National Laboratory for Applied Network Research and funded by the * National Science Foundation. Squid is Copyrighted (C) 1998 by * Duane Wessels and the University of California San Diego. Please * see the COPYRIGHT file for full details. Squid incorporates * software developed and/or copyrighted by other sources. Please 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" #include #if USE_WCCPv2 #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 theInWccpConnection = -1; static int theOutWccpConnection = -1; //static int change; static struct in_addr local_ip; /* 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 // ftp made up, not part of spec #define WCCP2_SERVICE_ID_FTP 21 #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 typedef struct { uint32_t type; uint16_t version; uint16_t length; uint16_t security_type; uint16_t security_length; uint32_t security_option; uint16_t service_type; // service_info uint16_t service_length; uint8_t service; // 0||1 (standard or dynamic) uint8_t serviceid; // well-known 0 - 50 // char service_filler[22]; uint8_t srv_priority; // well-known srv has prio of 240 uint8_t srv_protocol; // ip protocol identifier uint32_t srv_flags; uint16_t srv_ports[8]; // zero-terminated uint16_t cache_identity_type; uint16_t cache_identity_length; struct in_addr cache_identity_addr; char cache_identity_filler[40]; // router-maintained hash uint16_t cache_view_type; uint16_t cache_view_length; uint32_t cache_view_version; uint32_t cache_view_num_routers; struct in_addr cache_view_rtr1_addr; uint32_t cache_view_rtr1_receive_id; uint32_t cache_view_num_caches; int id; } wccp2_here_i_am_t; typedef struct { uint32_t type; uint16_t version; uint16_t length; char data[WCCP_RESPONSE_SIZE]; int id; } wccp2_i_see_you_t; static wccp2_i_see_you_t wccp2_i_see_you; struct wccp2_item_header_t { uint16_t type; uint16_t length; }; static struct wccp2_item_header_t wccp2_item_header; struct wccp2_router_id_element_t { struct in_addr router_addr; uint32_t received_id; }; static struct wccp2_router_id_element_t wccp2_router_id_element; struct wccp2_router_info_t { uint16_t type; uint16_t length; uint32_t member_change; }; static struct wccp2_router_info_t wccp2_router_info; struct wccp2_redirect_assign_t { uint32_t type; uint16_t version; uint16_t length; uint16_t security_type; uint16_t security_length; uint32_t security_option; uint16_t service_type; uint16_t service_length; uint8_t service; uint8_t serviceid; // char service_filler[22]; uint8_t srv_priority; // well-known srv has prio of 240 uint8_t srv_protocol; // ip protocol identifier uint32_t srv_flags; uint16_t srv_ports[8]; // zero-terminated uint16_t assignment_type; uint16_t assignment_length; struct in_addr assignment_key; uint32_t assignment_key_change; uint32_t assignment_num_routers; struct in_addr assignment_router1_addr; uint32_t assignment_router1_receive_id; uint32_t assignment_router1_change_number; uint32_t assignment_num_caches; struct in_addr assignment_cache1_addr; char buckets[WCCP_BUCKETS]; }; static struct wccp2_redirect_assign_t wccp2_redirect_assign; struct wccp2_assign_bucket_t { int type; int id; int number; }; static uint32_t wccp2_received_id; static struct in_addr wccp2_router_addr; /* END WCCP V2 */ typedef struct { uint8_t service; // 0||1 (standard or dynamic) uint8_t serviceid; // well-known 0 - 50 uint8_t srv_priority; // well-known srv has prio of 240 uint8_t srv_protocol; // ip protocol identifier uint32_t srv_flags; uint16_t srv_ports[8]; // zero-terminated } service_t; struct _router_t { struct wccp2_router_id_element_t element; int state; struct _router_t *next; }; typedef struct _router_t router_t; typedef struct { int change; int n_routers; router_t *routers; int n_caches; router_t *caches; // ugly hack for now service_t service; wccp2_here_i_am_t h; } service_group_t; CBDATA_TYPE(service_group_t); struct service_list_t { service_group_t *sg; struct service_list_t *next; }; typedef struct service_list_t* service_list; static service_list services = NULL; router_t *routerFind (service_group_t *g, struct in_addr *addr) { router_t *s; if ((g == NULL) || (addr == NULL)) return NULL; for (s = g->routers; s; s = s->next) if (s->element.router_addr.s_addr == addr->s_addr) return s; return NULL; } void routerAdd(service_group_t *g, router_t *r) { if ((g == NULL) || (r == NULL)) return; r->next = NULL; if (g->routers == NULL) g->routers = r; else { r->next = g->routers; g->routers = r; } } service_group_t *serviceFind(uint8_t service_type, uint8_t service_id) { service_list s; for (s = services; s; s=s->next) if ((s->sg->service.service == service_type) && (s->sg->service.serviceid == service_id)) return s->sg; return NULL; } void serviceAdd(service_group_t *t) { struct service_list_t *q; if (serviceFind(t->service.service, t->service.serviceid)) return; q = xmalloc(sizeof(struct service_list_t)); q->sg = t; q->next = NULL; if (services == NULL) { services = q; } else { q->next = services; services = q; } return; } static PF wccp2HandleUdp; static int wccp2LowestIP(void); static EVH wccp2HereIam; static EVH wccp2AssignBuckets; /* * The functions used during startup: * wccp2Init * wccp2ConnectionOpen * wccp2ConnectionShutdown * wccp2ConnectionClose */ void make_hereiam(service_group_t *); void wccp2Init(void) { int n_routers, n_services; wordlist *s = Config.Wccp2.routers; router_t *rr; service_list sl; int i; service_group_t *http_sg, *ftp_sg; debug(80, 5) ("wccp2Init: Called\n"); if (eventFind(wccp2HereIam, NULL)) return; if (Config.Wccp2.routers == 0) return; for (s = Config.Wccp2.routers, n_routers = 0; s; s = s->next,n_routers++); CBDATA_INIT_TYPE(service_group_t); http_sg = cbdataAlloc(service_group_t); ftp_sg = cbdataAlloc(service_group_t); memset(http_sg, 0, sizeof(*http_sg)); memset(ftp_sg, 0, sizeof(*ftp_sg)); http_sg->change = 1; ftp_sg->change = 0; http_sg->service.service = WCCP2_SERVICE_STANDARD; ftp_sg->service.service = WCCP2_SERVICE_DYNAMIC; http_sg->service.serviceid = WCCP2_SERVICE_ID_HTTP; ftp_sg->service.serviceid = WCCP2_SERVICE_ID_FTP; http_sg->n_caches = 1; // for now ftp_sg->n_caches = 1; // for now // we assume all the routers do both groups for now http_sg->n_routers = n_routers; ftp_sg->n_routers = n_routers; http_sg->service.srv_priority = 240; ftp_sg->service.srv_priority = 240; http_sg->service.srv_protocol = IPPROTO_TCP; ftp_sg->service.srv_protocol = IPPROTO_TCP; http_sg->service.srv_flags = 0; ftp_sg->service.srv_flags = 0; for (i = 0; i < 8; i++) { http_sg->service.srv_ports[i] = 0; ftp_sg->service.srv_ports[i] = 0; } // http_sg->service.srv_ports[0] = 80; ftp_sg->service.srv_ports[0] = 21; ftp_sg->service.srv_ports[1] = 20; //serviceAdd (http_sg); serviceAdd(ftp_sg); cbdataFree(http_sg); for (s = Config.Wccp2.routers; s; s = s->next) { router_t *r = xmalloc(sizeof(router_t)); debug (80,9) ("JHALL: processing %s\n",s->key); memset(r, 0, sizeof(*r)); r->element.router_addr.s_addr = inet_addr(s->key); r->element.received_id = 0; r->state = 0; if (r->element.router_addr.s_addr != any_addr.s_addr) { // routerAdd(http_sg, r); routerAdd(ftp_sg, r); } } for (sl = services,n_services=0; sl; sl = sl->next,n_services++); debug (80,9) ("JHALL: there are %d services\n",n_services); for (sl = services; sl; sl = sl->next) { make_hereiam(sl->sg); } for (rr = ftp_sg->routers; rr; rr = rr->next) if (rr->element.router_addr.s_addr != any_addr.s_addr) if (!eventFind(wccp2HereIam, NULL)) eventAdd("wccp2HereIam", wccp2HereIam, NULL, 10.0, 1); } void make_hereiam(service_group_t *srv) { int i; if (srv == NULL) return; if (srv->routers == NULL) return; debug(80, 5) ("make_hereiam: Called for %d:%d\n", srv->service.service, srv->service.serviceid); srv->h.type = htonl(WCCP2_HERE_I_AM); srv->h.version = htons(WCCP2_VERSION); srv->h.length = htons(sizeof(srv->h)-8); srv->h.security_type = htons(WCCP2_SECURITY_INFO); srv->h.security_length = htons(sizeof(srv->h.security_option)); srv->h.security_option = htonl(WCCP2_NO_SECURITY); srv->h.service_type = htons(WCCP2_SERVICE_INFO); srv->h.service_length = htons(sizeof(srv->h.service) + sizeof(srv->h.serviceid) + sizeof (srv->h.srv_priority) + sizeof(srv->h.srv_protocol) + sizeof(srv->h.srv_flags) + (8*sizeof(uint16_t))); srv->h.service = srv->service.service; srv->h.serviceid = srv->service.serviceid; srv->h.srv_priority = srv->service.srv_priority; srv->h.srv_protocol = srv->service.srv_protocol; srv->h.srv_flags = srv->service.srv_flags; // memset(&srv->h.srv_ports, 0, sizeof(uint16_t)*2); for (i = 0; i < 8; i++) srv->h.srv_ports[i] = srv->service.srv_ports[i]; srv->h.cache_identity_type = htons(WCCP2_WC_ID_INFO); srv->h.cache_identity_length = htons(sizeof(srv->h.cache_identity_addr) + sizeof(srv->h.cache_identity_filler)); memset(&srv->h.cache_identity_filler, '\0', sizeof(srv->h.cache_identity_filler)); srv->h.cache_view_type = htons(WCCP2_WC_VIEW_INFO); srv->h.cache_view_length = htons(sizeof(srv->h.cache_view_version) + sizeof(srv->h.cache_view_num_routers)+ sizeof(srv->h.cache_view_num_caches) + sizeof(srv->h.cache_view_rtr1_addr) + sizeof(srv->h.cache_view_rtr1_receive_id)); srv->h.cache_view_version = htonl(1); srv->h.cache_view_num_routers = htonl(srv->n_routers); srv->h.cache_view_rtr1_addr = srv->routers->element.router_addr; srv->h.cache_view_rtr1_receive_id = srv->routers->element.received_id; srv->h.cache_view_num_caches = htonl(1); } void wccp2ConnectionOpen(void) { u_short port = WCCP_PORT; struct sockaddr_in router, local; router_t *s; service_list srv; int local_len, router_len; debug(80, 5) ("wccp2ConnectionOpen: Called\n"); for (srv = services; srv; srv=srv->next) { debug (80,9) ("JHALL: service type %d id %d\n",srv->sg->service.service, srv->sg->service.serviceid); for (s = srv->sg->routers; s; s=s->next) { debug (80,9) ("JHALL: found addr %s\n",inet_ntoa(s->element.router_addr)); if (s->element.router_addr.s_addr == any_addr.s_addr) { debug (80,9) ("JHALL: line %d\n", __LINE__); debug(1, 1) ("WCCP Disabled.\n"); return; } } } theInWccpConnection = comm_open(SOCK_DGRAM, 0, Config.Wccp2.incoming, port, COMM_NONBLOCKING, "WCCP Socket"); if (theInWccpConnection < 0) fatal("Cannot open WCCP Port"); commSetSelect(theInWccpConnection, COMM_SELECT_READ, wccp2HandleUdp, NULL, 0); debug(1, 1) ("Accepting WCCP v2 messages on port %d, FD %d.\n", (int) port, theInWccpConnection); 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(1, 1) ("Outgoing WCCP v2 messages on port %d, FD %d.\n", (int) port, theOutWccpConnection); fd_note(theOutWccpConnection, "Outgoing WCCP socket"); fd_note(theInWccpConnection, "Incoming WCCP socket"); } else { theOutWccpConnection = theInWccpConnection; } #if 0 router_len = sizeof(router); memset(&router, '\0', router_len); router.sin_family = AF_INET; router.sin_port = htons(port); router.sin_addr = Config.Wccp2.routers->s.sin_addr; if (connect(theOutWccpConnection, (struct sockaddr *) &router, router_len)) fatal("Unable to connect WCCP out socket"); local_len = sizeof(local); memset(&local, '\0', local_len); if (getsockname(theOutWccpConnection, (struct sockaddr *) &local, &local_len)) fatal("Unable to getsockname on WCCP out socket"); local_ip.s_addr = local.sin_addr.s_addr; #else local_len = sizeof(local); memset(&local, '\0', local_len); local_ip.s_addr = Config.Wccp2.router_id.s_addr; #endif } void wccp2ConnectionShutdown(void) { if (theInWccpConnection < 0) return; if (theInWccpConnection != theOutWccpConnection) { debug(80, 1) ("FD %d Closing WCCP socket\n", theInWccpConnection); comm_close(theInWccpConnection); } assert(theOutWccpConnection > -1); commSetSelect(theOutWccpConnection, COMM_SELECT_READ, NULL, NULL, 0); } void wccp2ConnectionClose(void) { service_list sl; wccp2ConnectionShutdown(); if (theOutWccpConnection > -1) { debug(80, 1) ("FD %d Closing WCCP socket\n", theOutWccpConnection); comm_close(theOutWccpConnection); } for (sl = services; sl; sl = sl->next) { router_t *rr; service_group_t *srv = sl->sg; for (rr = sl->sg->routers; rr; rr = rr->next) safe_free(rr); srv->routers = 0; cbdataFree(srv); } services = 0; } /* * Functions for handling the requests. */ /* * Accept the UDP packet */ static void wccp2HandleUdp(int sock, void *not_used) { struct sockaddr_in from; wordlist *s; socklen_t from_len; int len, offset; // uint32_t tmp; wccp2_i_see_you_t wccp2_i_see_you; service_group_t *srv; service_t service; 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); memset(&wccp2_i_see_you, '\0', sizeof(wccp2_i_see_you)); statCounter.syscalls.sock.recvfroms++; len = recvfrom(sock, &wccp2_i_see_you, WCCP_RESPONSE_SIZE, 0, (struct sockaddr *) &from, &from_len); if (len < 0) return; for (s = Config.Wccp2.routers; s; s = s->next) if (inet_addr(s->key) == from.sin_addr.s_addr) break; if (s == NULL) { debug (80, 1) ("WCCPV2: %s is unknown, dropping him\n",inet_ntoa(from.sin_addr)); return; } if (ntohs(wccp2_i_see_you.version) != WCCP2_VERSION) return; if (ntohl(wccp2_i_see_you.type) != WCCP2_I_SEE_YOU) return; memset(&service, 0, sizeof(service)); debug(80, 1) ("Incoming WCCP v2 I_SEE_YOU from %s: length %d.\n", inet_ntoa(from.sin_addr), ntohs(wccp2_i_see_you.length)); xmemcpy(&wccp2_item_header, &wccp2_i_see_you.data[0], sizeof(wccp2_item_header)); if (ntohs(wccp2_item_header.type) != WCCP2_SECURITY_INFO) { debug(80,1) ("WCCP2_I_SEE_YOU missing WCCP2_SECURITY_INFO\n"); return; } offset = ntohs(wccp2_item_header.length) + 4; xmemcpy(&wccp2_item_header, &wccp2_i_see_you.data[offset], sizeof(wccp2_item_header)); if (ntohs(wccp2_item_header.type) != WCCP2_SERVICE_INFO) { debug(80,1) ("WCCP2_I_SEE_YOU from %s missing WCCP2_SERVICE_INFO offset %d\n", inet_ntoa(from.sin_addr), offset); return; } xmemcpy (&service, &wccp2_i_see_you.data[offset+4], ntohs(wccp2_item_header.length)); srv = serviceFind(service.service, service.serviceid); if (srv == 0) { debug(80,1) ("WARNING! router %s thinks we're in a service group we aren't, type %d id %d\n",inet_ntoa(from.sin_addr), service.service, service.serviceid); return; } offset += ntohs(wccp2_item_header.length) + 4; /* Skip WCCP2_SERVICE_INFO */ xmemcpy(&wccp2_item_header, &wccp2_i_see_you.data[offset], sizeof(wccp2_item_header)); if (ntohs(wccp2_item_header.type) != WCCP2_ROUTER_ID_INFO) { debug(80,1) ("WCCP2_I_SEE_YOU from %s missing WCCP2_ROUTER_ID_INFO\n", inet_ntoa(from.sin_addr)); return; } xmemcpy(&wccp2_router_id_element, &wccp2_i_see_you.data[offset+4], sizeof(wccp2_router_id_element)); debug(80, 1) ("Incoming WCCP2_I_SEE_YOU received from %s: id = %d.\n", inet_ntoa(wccp2_router_id_element.router_addr), ntohl(wccp2_router_id_element.received_id)); // start here router_t *r = routerFind(srv, &wccp2_router_id_element.router_addr); if (r == NULL) { debug (80,1) ("WARNING: router %s not found in service %d\n",inet_ntoa(wccp2_router_id_element.router_addr), service.serviceid); return; } r->element.received_id = wccp2_router_id_element.received_id; offset += ntohs(wccp2_item_header.length) + 4; xmemcpy (&wccp2_router_info, &wccp2_i_see_you.data[offset], sizeof(wccp2_router_info)); debug(80, 1) ("Incoming WCCP2_I_SEE_YOU from %s member change = %d tmp=%d.\n", inet_ntoa(r->element.router_addr), srv->change,ntohl(wccp2_router_info.member_change)); debug (80,9) ("JHALL: line %d\n", __LINE__); if (!srv->change) { debug (80,9) ("JHALL: line %d\n", __LINE__); srv->change = ntohl(wccp2_router_info.member_change); debug(80, 1) ("Incoming WCCP2_I_SEE_YOU member from %s change = %d.\n", inet_ntoa(r->element.router_addr), srv->change); // return; } debug (80,9) ("JHALL: line %d\n", __LINE__); if (srv->change != ntohl(wccp2_router_info.member_change)) { debug (80,9) ("JHALL: line %d\n", __LINE__); srv->change = ntohl(wccp2_router_info.member_change); if (wccp2LowestIP()) if (!eventFind(wccp2AssignBuckets, srv)) { debug (80,9) ("JHALL: line %d\n", __LINE__); eventAdd("wccp2AssignBuckets", wccp2AssignBuckets, srv, 25.0, 1); debug (80,9) ("JHALL: line %d\n", __LINE__); } // deleteme } } static int wccp2LowestIP(void) { /* Force Election for now int loop; for (loop = 0; loop < ntohl(wccp2_i_see_you.number); loop++) { if (wccp2_i_see_you.wccp2_cache_entry[loop].ip_addr.s_addr < local_ip.s_addr) return 0; } */ return 1; } static void wccp2HereIam(void *voidnotused) { service_list sl; router_t *r; service_group_t *srv; debug(80, 6) ("wccp2HereIam: Called\n"); for (sl = services; sl; sl = sl->next) { srv = sl->sg; for (r = srv->routers; r; r = r->next) { struct sockaddr_in from; memset (&from, 0, sizeof(from)); from.sin_addr.s_addr = r->element.router_addr.s_addr; from.sin_port = htons(WCCP_PORT); srv->h.cache_identity_addr = local_ip; srv->h.id = srv->change; srv->h.cache_view_rtr1_receive_id = r->element.received_id; srv->h.cache_view_rtr1_addr = r->element.router_addr; comm_udp_sendto(theOutWccpConnection, &from, sizeof(struct sockaddr_in), &srv->h, sizeof(srv->h)); } } if (!eventFind(wccp2HereIam, NULL)) eventAdd("wccp2HereIam", wccp2HereIam, NULL, 10.0, 1); } static void wccp2AssignBuckets(void *voidsrv) { service_group_t *srv = (service_group_t *)voidsrv; struct wccp2_assign_bucket_t wccp2_assign_bucket; int buckets_per_cache; router_t *r; int loop, i; int number_caches; int bucket = 0; int *caches; int offset; char buckets[WCCP_BUCKETS]; char *buf; struct sockaddr_in from; debug(80, 6) ("wccp2AssignBuckets: Called\n"); memset (&from, 0, sizeof(from)); if (srv == 0) return; debug(80, 1) ("WCCP2 Assigning Redirect\n"); memset(&wccp2_redirect_assign.buckets, '\0', sizeof(wccp2_redirect_assign.buckets)); memset(&wccp2_redirect_assign.buckets, 0xFF, WCCP_BUCKETS); for (bucket = 0; bucket < WCCP_BUCKETS; bucket++) { wccp2_redirect_assign.buckets[bucket] = 0; } wccp2_redirect_assign.type = htonl(WCCP2_REDIRECT_ASSIGN); wccp2_redirect_assign.version = htons(WCCP2_VERSION); wccp2_redirect_assign.length = htons(sizeof(wccp2_redirect_assign)-8); wccp2_redirect_assign.security_type = htons(WCCP2_SECURITY_INFO); wccp2_redirect_assign.security_length = htons(sizeof(wccp2_redirect_assign.security_option)); wccp2_redirect_assign.security_option = htonl(WCCP2_NO_SECURITY); wccp2_redirect_assign.service_type = htons(WCCP2_SERVICE_INFO); wccp2_redirect_assign.service_length = htons(sizeof(wccp2_redirect_assign.service) + sizeof(wccp2_redirect_assign.serviceid) + sizeof (srv->h.srv_priority) + sizeof(srv->h.srv_protocol) + sizeof(srv->h.srv_flags) + (8*sizeof(uint16_t))); wccp2_redirect_assign.service = srv->service.service; wccp2_redirect_assign.serviceid = srv->service.serviceid; for (i = 0; i <8; i++) wccp2_redirect_assign.srv_ports[i] = htons(srv->service.srv_ports[i]); wccp2_redirect_assign.srv_flags = 0; wccp2_redirect_assign.srv_priority = htons(240); wccp2_redirect_assign.srv_protocol = htons(srv->service.srv_protocol); wccp2_redirect_assign.assignment_type = htons(WCCP2_REDIRECT_ASSIGNMENT); wccp2_redirect_assign.assignment_length = htons(sizeof(wccp2_redirect_assign.assignment_key) + sizeof(wccp2_redirect_assign.assignment_key_change) + sizeof(wccp2_redirect_assign.assignment_num_routers) + sizeof(wccp2_redirect_assign.assignment_router1_addr) + sizeof(wccp2_redirect_assign.assignment_router1_receive_id) + sizeof(wccp2_redirect_assign.assignment_router1_change_number) + sizeof(wccp2_redirect_assign.assignment_num_caches) + sizeof(wccp2_redirect_assign.assignment_cache1_addr) + sizeof(wccp2_redirect_assign.buckets)); wccp2_redirect_assign.assignment_key = srv->h.cache_identity_addr; wccp2_redirect_assign.assignment_key_change = htonl(srv->change); wccp2_redirect_assign.assignment_num_routers = htonl(srv->n_routers); wccp2_redirect_assign.assignment_router1_addr = wccp2_router_addr; wccp2_redirect_assign.assignment_router1_receive_id = wccp2_received_id; wccp2_redirect_assign.assignment_router1_change_number = htonl(srv->change); wccp2_redirect_assign.assignment_num_caches = htonl(1); wccp2_redirect_assign.assignment_cache1_addr = srv->h.cache_identity_addr; for (r = srv->routers; r; r = r->next) { from.sin_addr.s_addr = r->element.router_addr.s_addr; from.sin_port = htons(WCCP_PORT); comm_udp_sendto(theOutWccpConnection, &from, sizeof(struct sockaddr_in), &wccp2_redirect_assign, sizeof(wccp2_redirect_assign)); } // srv->change = 0; } #endif /* USE_WCCPv2 */