Index: src/cf.data.pre
===================================================================
RCS file: /server/cvs-server/squid/squid/src/cf.data.pre,v
retrieving revision 1.368
diff -u -r1.368 cf.data.pre
--- src/cf.data.pre	18 Sep 2006 23:09:39 -0000	1.368
+++ src/cf.data.pre	20 Sep 2006 09:28:06 -0000
@@ -4291,6 +4291,26 @@
 	option is set to GRE.
 DOC_END
 
+NAME: wccp2_assignment_method
+TYPE: int
+LOC: Config.Wccp2.assignment_method
+DEFAULT: 1
+IFDEF: USE_WCCPv2
+DOC_START
+	WCCP2 allows the setting of methods to assign the WCCP hash
+	Valid values are as follows:
+
+	1 - Hash assignment
+	2 - Mask assignment
+
+	Currently (as of IOS 12.4) cisco routers only support assignment.
+	Cisco switches support the L2 redirect assignment method.
+
+	On a cisco 7600, enabling the "accelerated" feature on a wccp service
+	forces the router to use L2 forwarding/return methods and a mask
+	assignment method
+DOC_END
+
 NAME: wccp2_service
 TYPE: wccp2_service
 LOC: Config.Wccp2.info
Index: src/structs.h
===================================================================
RCS file: /server/cvs-server/squid/squid/src/structs.h,v
retrieving revision 1.501
diff -u -r1.501 structs.h
--- src/structs.h	18 Sep 2006 23:09:39 -0000	1.501
+++ src/structs.h	20 Sep 2006 09:28:08 -0000
@@ -507,6 +507,7 @@
 	struct in_addr address;
 	int forwarding_method;
 	int return_method;
+	int assignment_method;
 	int weight;
 	int rebuildwait;
 	void *info;
Index: src/wccp2.c
===================================================================
RCS file: /server/cvs-server/squid/squid/src/wccp2.c,v
retrieving revision 1.25
diff -u -r1.25 wccp2.c
--- src/wccp2.c	18 Sep 2006 23:09:39 -0000	1.25
+++ src/wccp2.c	20 Sep 2006 09:28:11 -0000
@@ -67,7 +67,7 @@
 
 #define WCCP2_SERVICE_SRC_IP_HASH	0x1
 #define WCCP2_SERVICE_DST_IP_HASH	0x2
-#define WCCP2_SERVICE_SOURCE_PORT_HASH	0x4
+#define WCCP2_SERVICE_SRC_PORT_HASH	0x4
 #define WCCP2_SERVICE_DST_PORT_HASH	0x8
 #define WCCP2_SERVICE_PORTS_DEFINED	0x10
 #define WCCP2_SERVICE_PORTS_SOURCE	0x20
@@ -90,6 +90,12 @@
 
 #define WCCP2_CAPABILITY_INFO		8
 
+#define WCCP2_ALT_ASSIGNMENT		13
+
+#define WCCP2_ASSIGN_MAP		14
+
+#define WCCP2_COMMAND_EXTENSION		15
+
 #define WCCP2_CAPABILITY_FORWARDING_METHOD	0x01
 #define WCCP2_CAPABILITY_ASSIGNMENT_METHOD	0x02
 #define WCCP2_CAPABILITY_RETURN_METHOD		0x03
@@ -103,6 +109,9 @@
 #define WCCP2_PACKET_RETURN_METHOD_GRE		0x00000001
 #define WCCP2_PACKET_RETURN_METHOD_L2		0x00000002
 
+#define WCCP2_HASH_ASSIGNMENT		0x00
+#define WCCP2_MASK_ASSIGNMENT		0x01
+
 #define	WCCP2_NONE_SECURITY_LEN	0
 #define	WCCP2_MD5_SECURITY_LEN	16
 
@@ -170,6 +179,27 @@
 
 static struct wccp2_identity_info_t wccp2_identity_info;
 
+struct wccp2_cache_mask_identity_info_t {
+    struct in_addr addr;
+    uint32_t num1;
+    uint32_t num2;
+    uint32_t source_ip_mask;
+    uint32_t dest_ip_mask;
+    uint16_t source_port_mask;
+    uint16_t dest_port_mask;
+    uint32_t num3;
+    uint32_t num4;
+};
+
+/* Web Cache identity info */
+struct wccp2_mask_identity_info_t {
+    uint16_t cache_identity_type;
+    uint16_t cache_identity_length;
+    struct wccp2_cache_mask_identity_info_t cache_identity;
+};
+
+static struct wccp2_mask_identity_info_t wccp2_mask_identity_info;
+
 /* View header */
 struct wccp2_cache_view_header_t {
     uint16_t cache_view_type;
@@ -218,6 +248,23 @@
 
 static struct wccp2_capability_element_t wccp2_capability_element;
 
+/* Mask Element */
+struct wccp2_mask_element_t {
+    uint32_t source_ip_mask;
+    uint32_t dest_ip_mask;
+    uint16_t source_port_mask;
+    uint16_t dest_port_mask;
+    uint32_t number_values;
+};
+
+/* Value Element */
+struct wccp2_value_element_t {
+    uint32_t source_ip_value;
+    uint32_t dest_ip_value;
+    uint16_t source_port_value;
+    uint16_t dest_port_value;
+    struct in_addr cache_ip;
+};
 
 /* RECEIVED PACKET STRUCTURE */
 struct wccp2_i_see_you_t {
@@ -250,6 +297,14 @@
     uint32_t number_caches;
 };
 
+/* The received packet for a mask assignment is unusual */
+struct cache_mask_info_t {
+    struct in_addr addr;
+    uint32_t num1;
+    uint32_t num2;
+    uint32_t num3;
+};
+
 /* assigment key */
 struct assignment_key_t {
     struct in_addr master_ip;
@@ -263,7 +318,6 @@
     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;
@@ -289,7 +343,7 @@
     struct wccp2_router_list_t router_list_head;
     int lowest_ip;
     uint32_t change_num;
-    struct wccp2_identity_info_t *wccp2_identity_info_ptr;
+    char *wccp2_identity_info_ptr;
     struct wccp2_security_md5_t *security_info;
     struct wccp2_service_info_t *service_info;
     char wccp_packet[WCCP_RESPONSE_SIZE];
@@ -471,6 +525,7 @@
 {
     sockaddr_in_list *s;
     char *ptr;
+    uint32_t service_flags;
     struct wccp2_service_list_t *service_list_ptr;
     struct wccp2_router_list_t *router_list_ptr;
     struct wccp2_security_md5_t wccp2_security_md5;
@@ -537,19 +592,64 @@
 	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(Config.Wccp2.weight);
-	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);
+	switch (Config.Wccp2.assignment_method) {
+	case WCCP2_ASSIGNMENT_METHOD_HASH:
+	    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(Config.Wccp2.weight);
+	    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 = ptr;
+	    ptr += sizeof(struct wccp2_identity_info_t);
+	    break;
+	case WCCP2_ASSIGNMENT_METHOD_MASK:
+	    wccp2_here_i_am_header.length += sizeof(struct wccp2_mask_identity_info_t);
+	    assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
+	    wccp2_mask_identity_info.cache_identity_type = htons(WCCP2_WC_ID_INFO);
+	    wccp2_mask_identity_info.cache_identity_length = htons(sizeof(wccp2_mask_identity_info.cache_identity));
+	    memset(&wccp2_mask_identity_info.cache_identity.addr, '\0', sizeof(wccp2_mask_identity_info.cache_identity.addr));
+	    wccp2_mask_identity_info.cache_identity.num1 = htonl(2);
+	    wccp2_mask_identity_info.cache_identity.num2 = htonl(1);
+	    service_flags = ntohl(service_list_ptr->service_info->service_flags);
+	    if ((service_flags & WCCP2_SERVICE_SRC_IP_HASH) || (service_flags & WCCP2_SERVICE_SRC_IP_ALT_HASH)) {
+		wccp2_mask_identity_info.cache_identity.source_ip_mask = htonl(0x00001741);
+		wccp2_mask_identity_info.cache_identity.dest_ip_mask = 0;
+		wccp2_mask_identity_info.cache_identity.source_port_mask = 0;
+		wccp2_mask_identity_info.cache_identity.dest_port_mask = 0;
+	    } else if ((service_flags & WCCP2_SERVICE_DST_IP_HASH) || (service_flags & WCCP2_SERVICE_DST_IP_ALT_HASH)) {
+		wccp2_mask_identity_info.cache_identity.source_ip_mask = 0;
+		wccp2_mask_identity_info.cache_identity.dest_ip_mask = htonl(0x00001741);
+		wccp2_mask_identity_info.cache_identity.source_port_mask = 0;
+		wccp2_mask_identity_info.cache_identity.dest_port_mask = 0;
+	    } else if ((service_flags & WCCP2_SERVICE_SRC_PORT_HASH) || (service_flags & WCCP2_SERVICE_SRC_PORT_ALT_HASH)) {
+		wccp2_mask_identity_info.cache_identity.source_ip_mask = 0;
+		wccp2_mask_identity_info.cache_identity.dest_ip_mask = 0;
+		wccp2_mask_identity_info.cache_identity.source_port_mask = htons(0x1741);
+		wccp2_mask_identity_info.cache_identity.dest_port_mask = 0;
+	    } else if ((service_flags & WCCP2_SERVICE_DST_PORT_HASH) || (service_flags & WCCP2_SERVICE_DST_PORT_ALT_HASH)) {
+		wccp2_mask_identity_info.cache_identity.source_ip_mask = 0;
+		wccp2_mask_identity_info.cache_identity.dest_ip_mask = 0;
+		wccp2_mask_identity_info.cache_identity.source_port_mask = 0;
+		wccp2_mask_identity_info.cache_identity.dest_port_mask = htons(0x1741);
+	    } else {
+		fatalf("Unknown service hash method\n");
+	    }
+	    wccp2_mask_identity_info.cache_identity.num3 = 0;
+	    wccp2_mask_identity_info.cache_identity.num4 = 0;
+
+	    xmemcpy(ptr, &wccp2_mask_identity_info, sizeof(struct wccp2_mask_identity_info_t));
+	    service_list_ptr->wccp2_identity_info_ptr = ptr;
+	    ptr += sizeof(struct wccp2_mask_identity_info_t);
+	    break;
+	default:
+	    fatalf("Unknown Wccp2 assignment method\n");
+	}
 
 	/* Add the cache view section */
 	wccp2_here_i_am_header.length += sizeof(wccp2_cache_view_header);
@@ -622,7 +722,7 @@
 	assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
 	wccp2_capability_element.capability_type = htons(WCCP2_CAPABILITY_ASSIGNMENT_METHOD);
 	wccp2_capability_element.capability_length = htons(sizeof(wccp2_capability_element.capability_value));
-	wccp2_capability_element.capability_value = htonl(WCCP2_ASSIGNMENT_METHOD_HASH);
+	wccp2_capability_element.capability_value = htonl(Config.Wccp2.assignment_method);
 	xmemcpy(ptr, &wccp2_capability_element, sizeof(wccp2_capability_element));
 	ptr += sizeof(wccp2_capability_element);
 
@@ -784,6 +884,8 @@
     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_cache_mask_identity_info_t *cache_mask_identity = NULL;
+    struct cache_mask_info_t *cache_mask_info = NULL;
     struct wccp2_capability_info_header_t *router_capability_header = NULL;
     struct wccp2_capability_element_t *router_capability_element;
 
@@ -868,6 +970,9 @@
 	    }
 	    router_capability_header = (struct wccp2_capability_info_header_t *) &wccp2_i_see_you.data[offset];
 	    break;
+	    /* Nothing to do for the types below */
+	case WCCP2_ASSIGN_MAP:
+	    break;
 	default:
 	    debug(80, 1) ("Unknown record type in WCCPv2 Packet (%d).\n",
 		ntohs(header->type));
@@ -946,8 +1051,8 @@
 		}
 		break;
 	    case WCCP2_CAPABILITY_ASSIGNMENT_METHOD:
-		if (!(ntohl(router_capability_element->capability_value) & WCCP2_ASSIGNMENT_METHOD_HASH)) {
-		    debug(80, 1) ("wccp2HandleUdp: fatal error - A WCCP router has specified a different assignment method %d, expected %d\n", ntohl(router_capability_element->capability_value), WCCP2_ASSIGNMENT_METHOD_HASH);
+		if (!(ntohl(router_capability_element->capability_value) & Config.Wccp2.assignment_method)) {
+		    debug(80, 1) ("wccp2HandleUdp: fatal error - A WCCP router has specified a different assignment method %d, expected %d\n", ntohl(router_capability_element->capability_value), Config.Wccp2.assignment_method);
 		    wccp2ConnectionClose();
 		    return;
 		}
@@ -1000,9 +1105,28 @@
 	/* 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));
+	    switch (Config.Wccp2.assignment_method) {
+	    case WCCP2_ASSIGNMENT_METHOD_HASH:
+		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));
+		break;
+	    case WCCP2_ASSIGNMENT_METHOD_MASK:
+		cache_mask_info = (struct cache_mask_info_t *) ptr;
+
+		/* The mask assignment has an undocumented variable length entry here */
+		if (ntohl(cache_mask_info->num1) == 3) {
+		    cache_mask_identity = (struct wccp2_cache_mask_identity_info_t *) ptr;
+		    ptr += sizeof(struct wccp2_cache_mask_identity_info_t);
+		    memcpy(&cache_address, &cache_mask_identity->addr, sizeof(struct in_addr));
+		} else {
+		    ptr += sizeof(struct cache_mask_info_t);
+		    memcpy(&cache_address, &cache_mask_info->addr, sizeof(struct in_addr));
+		}
+		break;
+	    default:
+		fatalf("Unknown Wccp2 assignment method\n");
+	    }
 
 	    /* Update the cache list */
 	    cache_list_ptr->cache_ip = cache_address;
@@ -1021,6 +1145,7 @@
 	    }
 	}
     } else {
+	debug(80, 5) ("Adding ourselves as the only cache\n");
 
 	/* Update the cache list */
 	cache_list_ptr->cache_ip = router_list_ptr->local_ip;
@@ -1028,7 +1153,7 @@
 	cache_list_ptr = cache_list_ptr->next;
 	cache_list_ptr->next = NULL;
 
-	service_list_ptr->lowest_ip = 0;
+	service_list_ptr->lowest_ip = 1;
 	found = 1;
 	num_caches = 1;
     }
@@ -1040,8 +1165,11 @@
 	    router_list_ptr->member_change = ntohl(router_view_header->change_number);
 	    eventDelete(wccp2AssignBuckets, NULL);
 	    eventAdd("wccp2AssignBuckets", wccp2AssignBuckets, NULL, 15.0, 1);
+	} else {
+	    debug(80, 5) ("Change not detected (%d = %d)\n", ntohl(router_view_header->change_number), router_list_ptr->member_change);
 	}
     } else {
+	eventDelete(wccp2AssignBuckets, NULL);
 	debug(80, 5) ("I am not the lowest ip cache - not assigning buckets\n");
     }
 }
@@ -1051,6 +1179,8 @@
 {
     struct wccp2_service_list_t *service_list_ptr;
     struct wccp2_router_list_t *router_list_ptr;
+    struct wccp2_identity_info_t *wccp2_identity_info_ptr;
+    struct wccp2_mask_identity_info_t *wccp2_mask_identity_info_ptr;
     struct sockaddr_in router;
     int router_len;
     u_short port = WCCP_PORT;
@@ -1078,7 +1208,19 @@
 	    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;
+	    switch (Config.Wccp2.assignment_method) {
+	    case WCCP2_ASSIGNMENT_METHOD_HASH:
+		wccp2_identity_info_ptr = (struct wccp2_identity_info_t *) service_list_ptr->wccp2_identity_info_ptr;
+		wccp2_identity_info_ptr->cache_identity.addr = router_list_ptr->local_ip;
+		break;
+	    case WCCP2_ASSIGNMENT_METHOD_MASK:
+		wccp2_mask_identity_info_ptr = (struct wccp2_mask_identity_info_t *) service_list_ptr->wccp2_identity_info_ptr;
+		wccp2_mask_identity_info_ptr->cache_identity.addr = router_list_ptr->local_ip;
+		break;
+	    default:
+		fatalf("Unknown Wccp2 assignment method\n");
+	    }
+
 	    /* Security update, if needed */
 	    if (service_list_ptr->wccp2_security_type == WCCP2_MD5_SECURITY) {
 		wccp2_update_md5_security(service_list_ptr->wccp_password, (char *) service_list_ptr->security_info, service_list_ptr->wccp_packet, service_list_ptr->wccp_packet_size);
@@ -1112,10 +1254,11 @@
     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;
+    short int offset, saved_offset, assignment_offset, alt_assignment_offset;
     struct sockaddr_in router;
     int router_len;
     int bucket_counter;
+    uint32_t service_flags;
     u_short port = WCCP_PORT;
 
     /* Packet segments */
@@ -1123,13 +1266,21 @@
     struct wccp2_security_md5_t *security = NULL;
     /* service from service struct */
     struct wccp2_item_header_t *assignment_header;
+    struct wccp2_item_header_t *alt_assignment_type_header = NULL;
     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;
+    /* Alternative assignement mask/values */
+    int num_maskval;
+    struct wccp2_mask_element_t *mask_element;
+    struct wccp2_value_element_t *value_element;
+    int valuecounter, value;
     char *buckets;
 
+    assignment_offset = alt_assignment_offset = 0;
+
     router_len = sizeof(router);
     memset(&router, '\0', router_len);
     router.sin_family = AF_INET;
@@ -1178,8 +1329,27 @@
 
 	/* 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);
+	switch (Config.Wccp2.assignment_method) {
+	case WCCP2_ASSIGNMENT_METHOD_HASH:
+	    assignment_header->type = htons(WCCP2_REDIRECT_ASSIGNMENT);
+	    offset += sizeof(struct wccp2_item_header_t);
+	    assignment_offset = offset;
+	    break;
+	case WCCP2_ASSIGNMENT_METHOD_MASK:
+	    assignment_header->type = htons(WCCP2_ALT_ASSIGNMENT);
+	    offset += sizeof(struct wccp2_item_header_t);
+	    assignment_offset = offset;
+
+	    /* The alternative assignment has an extra header, fill in length later */
+	    alt_assignment_type_header = (struct wccp2_item_header_t *) &wccp_packet[offset];
+	    alt_assignment_type_header->type = htons(WCCP2_MASK_ASSIGNMENT);
+	    offset += sizeof(struct wccp2_item_header_t);
+	    alt_assignment_offset = offset;
+
+	    break;
+	default:
+	    fatalf("Unknown Wccp2 assignment method\n");
+	}
 
 	/* Assignment key - fill in master ip later */
 	assignment_key = (struct assignment_key_t *) &wccp_packet[offset];
@@ -1207,64 +1377,143 @@
 	    int num_caches = ntohl(router_list_ptr->num_caches);
 	    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 (num_caches) {
-		int cache;
-		for (cache = 0, cache_list_ptr = &router_list_ptr->cache_list_head; cache_list_ptr->next; cache_list_ptr = cache_list_ptr->next, cache++) {
-		    /* add caches */
-		    cache_address = (struct in_addr *) &wccp_packet[offset];
-		    xmemcpy(cache_address, &cache_list_ptr->cache_ip, sizeof(struct in_addr));
-		    total_weight += cache_list_ptr->weight << 12;
-		    weight[cache] = cache_list_ptr->weight << 12;
-		    offset += sizeof(struct in_addr);
-		}
-	    }
-	    /* Add buckets */
-	    buckets = (char *) &wccp_packet[offset];
-	    memset(buckets, '\0', WCCP_BUCKETS);
-	    if (num_caches != 0) {
-		if (total_weight == 0) {
-		    for (bucket_counter = 0; bucket_counter < WCCP_BUCKETS; bucket_counter++) {
-			buckets[bucket_counter] = (char) (bucket_counter % num_caches);
+	    switch (Config.Wccp2.assignment_method) {
+	    case WCCP2_ASSIGNMENT_METHOD_HASH:
+		/* 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 (num_caches) {
+		    int cache;
+		    for (cache = 0, cache_list_ptr = &router_list_ptr->cache_list_head; cache_list_ptr->next; cache_list_ptr = cache_list_ptr->next, cache++) {
+			/* add caches */
+			cache_address = (struct in_addr *) &wccp_packet[offset];
+			xmemcpy(cache_address, &cache_list_ptr->cache_ip, sizeof(struct in_addr));
+			total_weight += cache_list_ptr->weight << 12;
+			weight[cache] = cache_list_ptr->weight << 12;
+			offset += sizeof(struct in_addr);
 		    }
-		} else {
-		    unsigned long *assigned = xcalloc(sizeof(*assigned), num_caches);
-		    unsigned long done = 0;
-		    int cache = -1;
-		    unsigned long per_bucket = total_weight / WCCP_BUCKETS;
-		    for (bucket_counter = 0; bucket_counter < WCCP_BUCKETS; bucket_counter++) {
-			int n;
-			unsigned long step;
-			for (n = num_caches; n; n--) {
-			    cache++;
-			    if (cache >= num_caches)
-				cache = 0;
-			    if (!weight[cache]) {
-				n++;
-				continue;
+		}
+		/* Add buckets */
+		buckets = (char *) &wccp_packet[offset];
+		memset(buckets, '\0', WCCP_BUCKETS);
+		if (num_caches != 0) {
+		    if (total_weight == 0) {
+			for (bucket_counter = 0; bucket_counter < WCCP_BUCKETS; bucket_counter++) {
+			    buckets[bucket_counter] = (char) (bucket_counter % num_caches);
+			}
+		    } else {
+			unsigned long *assigned = xcalloc(sizeof(*assigned), num_caches);
+			unsigned long done = 0;
+			int cache = -1;
+			unsigned long per_bucket = total_weight / WCCP_BUCKETS;
+			for (bucket_counter = 0; bucket_counter < WCCP_BUCKETS; bucket_counter++) {
+			    int n;
+			    unsigned long step;
+			    for (n = num_caches; n; n--) {
+				cache++;
+				if (cache >= num_caches)
+				    cache = 0;
+				if (!weight[cache]) {
+				    n++;
+				    continue;
+				}
+				if (assigned[cache] <= done)
+				    break;
 			    }
-			    if (assigned[cache] <= done)
-				break;
+			    buckets[bucket_counter] = (char) cache;
+			    step = per_bucket * total_weight / weight[cache];
+			    assigned[cache] += step;
+			    done += per_bucket;
 			}
-			buckets[bucket_counter] = (char) cache;
-			step = per_bucket * total_weight / weight[cache];
-			assigned[cache] += step;
-			done += per_bucket;
+			safe_free(assigned);
 		    }
-		    safe_free(assigned);
 		}
-	    }
-	    offset += (WCCP_BUCKETS * sizeof(char));
-	    safe_free(weight);
+		offset += (WCCP_BUCKETS * sizeof(char));
+		safe_free(weight);
+		break;
+	    case WCCP2_ASSIGNMENT_METHOD_MASK:
+		num_maskval = htonl(1);
+		xmemcpy(&wccp_packet[offset], &num_maskval, sizeof(int));
+		offset += sizeof(int);
+
+		mask_element = (struct wccp2_mask_element_t *) &wccp_packet[offset];
+		service_flags = ntohl(service_list_ptr->service_info->service_flags);
+		if ((service_flags & WCCP2_SERVICE_SRC_IP_HASH) || (service_flags & WCCP2_SERVICE_SRC_IP_ALT_HASH)) {
+		    mask_element->source_ip_mask = htonl(0x00001741);
+		    mask_element->dest_ip_mask = 0;
+		    mask_element->source_port_mask = 0;
+		    mask_element->dest_port_mask = 0;
+		} else if ((service_flags & WCCP2_SERVICE_DST_IP_HASH) || (service_flags & WCCP2_SERVICE_DST_IP_ALT_HASH)) {
+		    mask_element->source_ip_mask = 0;
+		    mask_element->dest_ip_mask = htonl(0x00001741);
+		    mask_element->source_port_mask = 0;
+		    mask_element->dest_port_mask = 0;
+		} else if ((service_flags & WCCP2_SERVICE_SRC_PORT_HASH) || (service_flags & WCCP2_SERVICE_SRC_PORT_ALT_HASH)) {
+		    mask_element->source_ip_mask = 0;
+		    mask_element->dest_ip_mask = 0;
+		    mask_element->source_port_mask = htons(0x1741);
+		    mask_element->dest_port_mask = 0;
+		} else if ((service_flags & WCCP2_SERVICE_DST_PORT_HASH) || (service_flags & WCCP2_SERVICE_DST_PORT_ALT_HASH)) {
+		    mask_element->source_ip_mask = 0;
+		    mask_element->dest_ip_mask = 0;
+		    mask_element->source_port_mask = 0;
+		    mask_element->dest_port_mask = htons(0x1741);
+		} else {
+		    fatalf("Unknown service hash method\n");
+		}
+		mask_element->number_values = htonl(64);
+		offset += sizeof(struct wccp2_mask_element_t);
 
+		cache_list_ptr = &router_list_ptr->cache_list_head;
+		value = 0;
+		for (valuecounter = 0; valuecounter < 64; valuecounter++) {
+		    value_element = (struct wccp2_value_element_t *) &wccp_packet[offset];
+
+		    if ((service_flags & WCCP2_SERVICE_SRC_IP_HASH) || (service_flags & WCCP2_SERVICE_SRC_IP_ALT_HASH)) {
+			value_element->source_ip_value = htonl(value);
+			value_element->dest_ip_value = 0;
+			value_element->source_port_value = 0;
+			value_element->dest_port_value = 0;
+		    } else if ((service_flags & WCCP2_SERVICE_DST_IP_HASH) || (service_flags & WCCP2_SERVICE_DST_IP_ALT_HASH)) {
+			value_element->source_ip_value = 0;
+			value_element->dest_ip_value = htonl(value);
+			value_element->source_port_value = 0;
+			value_element->dest_port_value = 0;
+		    } else if ((service_flags & WCCP2_SERVICE_SRC_PORT_HASH) || (service_flags & WCCP2_SERVICE_SRC_PORT_ALT_HASH)) {
+			value_element->source_ip_value = 0;
+			value_element->dest_ip_value = 0;
+			value_element->source_port_value = htons(value);
+			value_element->dest_port_value = 0;
+		    } else if ((service_flags & WCCP2_SERVICE_DST_PORT_HASH) || (service_flags & WCCP2_SERVICE_DST_PORT_ALT_HASH)) {
+			value_element->source_ip_value = 0;
+			value_element->dest_ip_value = 0;
+			value_element->source_port_value = 0;
+			value_element->dest_port_value = htons(value);
+		    } else {
+			fatalf("Unknown service hash method\n");
+		    }
+		    value_element->cache_ip = cache_list_ptr->cache_ip;
+		    offset += sizeof(struct wccp2_value_element_t);
+
+		    /* Update the value according the the "correct" formula */
+		    for (value++; (value & 0x1741) != value; value++);
+
+		    /* Assign the next value to the next cache */
+		    if ((cache_list_ptr->next) && (cache_list_ptr->next->next))
+			cache_list_ptr = cache_list_ptr->next;
+		    else
+			cache_list_ptr = &router_list_ptr->cache_list_head;
+		}
+		/* Fill in length */
+		alt_assignment_type_header->length = htons(offset - alt_assignment_offset);
+
+		break;
+	    default:
+		fatalf("Unknown Wccp2 assignment method\n");
+	    }
 	    /* 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)));
+	    assignment_header->length = htons(offset - assignment_offset);
 
 	    /* Fill in assignment key */
 	    assignment_key->master_ip = router_list_ptr->local_ip;
@@ -1419,7 +1668,7 @@
 	} else if (strcmp(flag, "dst_ip_hash") == 0) {
 	    retflag |= WCCP2_SERVICE_DST_IP_HASH;
 	} else if (strcmp(flag, "source_port_hash") == 0) {
-	    retflag |= WCCP2_SERVICE_SOURCE_PORT_HASH;
+	    retflag |= WCCP2_SERVICE_SRC_PORT_HASH;
 	} else if (strcmp(flag, "dst_port_hash") == 0) {
 	    retflag |= WCCP2_SERVICE_DST_PORT_HASH;
 	} else if (strcmp(flag, "ports_source") == 0) {
@@ -1575,7 +1824,7 @@
 		storeAppendPrintf(e, "%sdst_ip_hash", comma ? "," : "");
 		comma = 1;
 	    }
-	    if (flags & WCCP2_SERVICE_SOURCE_PORT_HASH) {
+	    if (flags & WCCP2_SERVICE_SRC_PORT_HASH) {
 		storeAppendPrintf(e, "%ssource_port_hash", comma ? "," : "");
 		comma = 1;
 	    }

