/*
 * $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 {
struct in_addr addr;
uint16_t version;
uint16_t unassigned;
uint32_t buckets[8];
uint16_t weight;
uint16_t status;
} wccp2_cache_identity_element_t;
#if 0
typedef struct {
uint16_t type;
uint16_t length;
uint32_t change;
uint32_t key;
uint32_t n_routers;
struct in_addr routers[];
uint32_t n_caches;
wccp2_cache_identity_element_t caches[];
} wccp2_router_view_t;

typedef struct {
uint16_t type;
uint16_t length;
uint32_t version;
uint32_t n_routers;
wccp2_router_id_element_t routers[];
uint32_t n_caches;
struct in_addr caches[];
} wccp2_wc_view_info_t;
#endif

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;
	char data[WCCP_RESPONSE_SIZE];
	int data_len;
	wccp2_cache_identity_element_t identity;
} 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;


struct wccp2_item_header_t {
	uint16_t type;
	uint16_t length;
};


struct wccp2_router_id_element_t {
	struct in_addr router_addr;
	uint32_t received_id;
};


struct wccp2_router_info_t {
	uint16_t type;
	uint16_t length;
	uint32_t member_change;
struct in_addr key_address;
uint32_t key_version;
uint32_t n_routers;
};

typedef struct {
	struct in_addr router_id;
	uint32_t received_id;
	uint32_t version;
} wccp2_router_assignment_element_t;

struct wccp2_assign_bucket_t {
    int type;
    int id;
    int number;
};

#define WCCP2_SRC_IP 0x0001
#define WCCP2_DST_IP 0x0002
#define WCCP2_SRC_PORT 0x0004
#define WCCP2_DST_PORT 0x0008
#define WCCP2_PORTS_DEFINED 0x0010
#define WCCP2_PORTS_SRC 0x0020
#define WCCP2_ALT_SRC_IP 0x0100
#define WCCP2_ALT_DST_IP 0x0200
#define WCCP2_ALT_SRC_PORT 0x0400
#define WCCP2_ALT_DST_PORT 0x0800
/* 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;
	uint32_t assignment_version;
	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;
uint32_t assignment_version;
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;

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;
	service_t service;
	uint16_t assignment_type;
	uint16_t assignment_length;
	struct in_addr assignment_key_address;
	uint32_t assignment_key_version;
	uint32_t assignment_num_routers;
	char data[WCCP_RESPONSE_SIZE];
};

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 = 1;
    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;
    ftp_sg->service.srv_flags &= WCCP2_DST_PORT;
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] = htons(21);
    ftp_sg->service.srv_ports[1] = htons(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);
		} else {
		xfree(r);
		}
		}

	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, offset, len, n_routers;
char *Short = 0;
uint16_t tmp;
uint32_t tmpl;
router_t *rs = 0;

	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.identity.buckets[i] = htonl(0);
        }
	srv->h.cache_identity_type = htons(WCCP2_WC_ID_INFO);
	srv->h.data_len = 0;
	offset = 0;
srv->h.identity.addr.s_addr = Config.Wccp2.router_id.s_addr;
srv->h.identity.version = htons(srv->change);
srv->h.identity.unassigned = htons(0);
srv->h.identity.weight = htons(0);
srv->h.identity.status = htons(0);
	srv->h.data_len += sizeof(srv->h.identity);
	offset = srv->h.data_len;
	xmemcpy (&srv->h.data[0], &srv->h.identity, srv->h.data_len);
	srv->h.cache_identity_length = htons(srv->h.data_len);
	tmp = htons(WCCP2_WC_VIEW_INFO);
	xmemcpy(&srv->h.data[offset], &tmp, sizeof(uint16_t));
	Short = &srv->h.data[offset+sizeof(uint16_t)]; // placeholder for len once it is known
	offset += 2 * sizeof(uint16_t); // type and length
	len = offset; // so we can do offset - len later
	tmpl = htonl(srv->change);
	xmemcpy (&srv->h.data[offset], &tmpl, sizeof(tmpl));
	offset += sizeof(tmpl);
	n_routers = 0;
	for (rs = srv->routers; rs; rs = rs->next)
		if (rs->state)
		n_routers++;
	tmpl = htonl(n_routers);
	xmemcpy (&srv->h.data[offset], &tmpl, sizeof(tmpl));
	offset += sizeof(tmpl);
	if (n_routers > 0)
		for (rs = srv->routers; rs; rs = rs->next)
		if (rs->state > 0) {
			xmemcpy(&srv->h.data[offset], &rs->element, sizeof(rs->element));
		offset += sizeof(rs->element);
		}
	tmpl = htonl(1); // n_caches
	xmemcpy (&srv->h.data[offset], &tmpl, sizeof(tmpl));
	offset += sizeof(tmpl);
	struct in_addr in = Config.Wccp2.router_id;
	xmemcpy(&srv->h.data[offset], &in, sizeof(in));
	offset += sizeof (struct in_addr);
	tmp = htons(offset - len);
	xmemcpy(Short, &tmp, sizeof(uint16_t));
		srv->h.data_len = offset;
	debug(80,5) ("JHALL: offset %d\n",offset);
		srv->h.length = htons(srv->h.data_len + 
		sizeof(srv->h.security_type) +
		sizeof(srv->h.security_length) +
		sizeof(srv->h.security_option) +
		sizeof(srv->h.service_type) +
		sizeof(srv->h.service_length) +
		ntohs(srv->h.service_length) +
		(2 * sizeof(uint16_t))); // cache info type and length
}


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;
    wccp2_i_see_you_t wccp2_i_see_you;
    service_group_t *srv;
service_t service;
struct wccp2_router_info_t wccp2_router_info;
struct wccp2_item_header_t wccp2_item_header;
struct wccp2_router_id_element_t wccp2_router_id_element;

    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));

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;
		} 
debug (80,9) ("JHALL: router with id %d, stored %d\n",ntohl(wccp2_router_id_element.received_id), ntohl(r->element.received_id));
	if ((ntohl(r->element.received_id) +1) != ntohl(wccp2_router_id_element.received_id))
		r->state = 0;
	if (((ntohl(r->element.received_id) +1) == ntohl(wccp2_router_id_element.received_id)) && (r->state == 0)) {
		r->state = 1;
		debug (80,6) ("Router %s now up\n", inet_ntoa(r->element.router_addr));
		srv->change ++;
	if (wccp2LowestIP())
    if (!eventFind(wccp2AssignBuckets, srv))
		eventAdd("wccp2AssignBuckets", wccp2AssignBuckets, srv, 25.0, 1);
		}
	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, key %s:%d, number of routers %d.\n", inet_ntoa(r->element.router_addr), srv->change,ntohl(wccp2_router_info.member_change), inet_ntoa(wccp2_router_info.key_address), ntohl(wccp2_router_info.key_version), ntohl(wccp2_router_info.n_routers));
    r->assignment_version = ntohl(wccp2_router_info.member_change);
    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__);
}

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;

    make_hereiam(srv);
		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);

    debug (80,6) ("sending %d bytes to %s\n",ntohs(srv->h.length), inet_ntoa(from.sin_addr));
    comm_udp_sendto(theOutWccpConnection,
	&from,
	sizeof(struct sockaddr_in),
	&srv->h,
	ntohs(srv->h.length) + 8);
		}
	}

    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 assign_bucket;
    int buckets_per_cache;
    router_t *r;
    int loop, i;
    uint32_t tmpl;
    int offset, len;
    char buckets[WCCP_BUCKETS];
struct sockaddr_in from;
struct wccp2_redirect_assign_t redirect_assign;
int n_routers = 0;

    debug(80, 6) ("wccp2AssignBuckets: Called\n");
if (srv == 0)
return;
		memset (&from, 0, sizeof(from));
		memset (&buckets, 0, 256);
// bit 0 of the bucket is either 0 or 1, whether the data here is historic or
// current.  The other 7 bits are meant to be the cache index, so (index << 1)
// since we're now forcing one cache for now, this all winds up to be 0
// but an enthusiastic soul might want to know this should they decide
// to improve on this code. a value of 0xff means bucket unassigned.
// deciding how to round-robin this hash to multiple caches probably depends
// on the service definition and relies on a far greater inteligence than mine
// jhall@maoz.com


	offset = 0;
	memset (&redirect_assign, 0, sizeof(redirect_assign));
    debug(80, 1) ("WCCP2 Assigning Redirect\n");
	for (r = srv->routers; r; r = r->next)
		if (r->state == 1) {
			n_routers++;
			xmemcpy(&redirect_assign.data[offset], &r->element, sizeof(r->element));
			offset += sizeof(r->element);
			tmpl = htonl(r->assignment_version);
			xmemcpy(&redirect_assign.data[offset], &tmpl, sizeof(tmpl));
			offset += sizeof(tmpl);
			debug (80,6) ("making %s:%d version %d\n",inet_ntoa(r->element.router_addr), ntohl(r->element.received_id), ntohl(tmpl));
			}

    redirect_assign.type = htonl(WCCP2_REDIRECT_ASSIGN);
    redirect_assign.version = htons(WCCP2_VERSION);
	redirect_assign.security_type = htons(WCCP2_SECURITY_INFO);
	redirect_assign.security_length = htons(sizeof(redirect_assign.security_option));
	redirect_assign.security_option = htonl(WCCP2_NO_SECURITY);
	redirect_assign.service_type = htons(WCCP2_SERVICE_INFO);
	redirect_assign.service_length = htons(sizeof(service_t));
	xmemcpy (&redirect_assign.service, &srv->service, sizeof(service_t));
	redirect_assign.assignment_type = htons(WCCP2_REDIRECT_ASSIGNMENT);
	redirect_assign.assignment_key_address = Config.Wccp2.router_id;
	redirect_assign.assignment_key_version = htonl(srv->change);
	redirect_assign.assignment_num_routers = htonl(n_routers);
	tmpl = htonl(1); // n_caches
	xmemcpy(&redirect_assign.data[offset], &tmpl, sizeof(tmpl));
	offset += sizeof(tmpl);
	xmemcpy (&redirect_assign.data[offset], &Config.Wccp2.router_id, sizeof(struct in_addr));
	offset += sizeof(struct in_addr);
	xmemcpy(&redirect_assign.data[offset], &buckets, sizeof (char) * 256);
	offset += 256;
	redirect_assign.assignment_length =
        htons(sizeof(redirect_assign.assignment_key_address) +
              sizeof(redirect_assign.assignment_key_version) +
              sizeof(redirect_assign.assignment_num_routers) + offset);
	redirect_assign.length = htons(ntohs(redirect_assign.assignment_length) +
				 sizeof(service_t) +
				 sizeof(redirect_assign.security_type) +
				 sizeof(redirect_assign.security_length) +
				 sizeof(redirect_assign.security_option) +
				 sizeof(redirect_assign.service_type) +
				 sizeof(redirect_assign.service_length) +
				 sizeof(redirect_assign.assignment_type) +
				 sizeof(redirect_assign.assignment_length) +
				 sizeof(redirect_assign.assignment_key_address) +
				 sizeof(redirect_assign.assignment_key_version) +
				 sizeof(redirect_assign.assignment_num_routers));

	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);
    debug (80,6) ("sending %d bytes to %s\n",ntohs(redirect_assign.length), inet_ntoa(from.sin_addr));
    comm_udp_sendto(theOutWccpConnection,
	&from,
	sizeof(struct sockaddr_in),
	&redirect_assign,
	ntohs(redirect_assign.length) + 8);
	}
//    srv->change = 0;
}

#endif /* USE_WCCPv2 */


