/*
 * $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 <netdb.h>

#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 */



