]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Forward port of WCCPv2 support and latest WCCPv1 changes from 2.6.
authorserassio <>
Sun, 2 Jul 2006 22:53:46 +0000 (22:53 +0000)
committerserassio <>
Sun, 2 Jul 2006 22:53:46 +0000 (22:53 +0000)
configure.in
doc/README.wccpv2 [new file with mode: 0755]
src/Makefile.am
src/cache_cf.cc
src/cf.data.pre
src/cf_gen_defines
src/main.cc
src/protos.h
src/structs.h
src/wccp.cc
src/wccp2.cc [new file with mode: 0644]

index 67cc30d179c2e3bfdfed2f5fad2e4d02e6f0808d..2a0b2287847e98a4c52e9e90cd14a9b39ceb2ee9 100644 (file)
@@ -1,7 +1,7 @@
 
 dnl  Configuration input file for Squid
 dnl
-dnl  $Id: configure.in,v 1.424 2006/06/20 20:57:25 hno Exp $
+dnl  $Id: configure.in,v 1.425 2006/07/02 16:53:46 serassio Exp $
 dnl
 dnl
 dnl
@@ -11,7 +11,7 @@ AM_CONFIG_HEADER(include/autoconf.h)
 AC_CONFIG_AUX_DIR(cfgaux)
 AC_CONFIG_SRCDIR([src/main.cc])
 AM_INIT_AUTOMAKE([tar-ustar])
-AC_REVISION($Revision: 1.424 $)dnl
+AC_REVISION($Revision: 1.425 $)dnl
 AC_PREFIX_DEFAULT(/usr/local/squid)
 AM_MAINTAINER_MODE
 
@@ -782,18 +782,28 @@ AC_ARG_ENABLE(referer-log,
   fi
 ])
 
-use_wccp=1
+USE_WCCP=1
 AC_ARG_ENABLE(wccp,  
 [  --disable-wccp          Disable Web Cache Coordination Protocol],
 [ if test "$enableval" = "no" ; then
     echo "Web Cache Coordination Protocol disabled"
-    use_wccp=0
+    USE_WCCP=0
   fi
 ])      
-if test $use_wccp = 1; then
-    AC_DEFINE(USE_WCCP, 1,[Define to enable WCCP])
-else
-    AC_DEFINE(USE_WCCP, 0)
+if test $USE_WCCP = 1; then
+    AC_DEFINE(USE_WCCP, 1, [Define to enable WCCP])
+fi
+
+USE_WCCPv2=1
+AC_ARG_ENABLE(wccpv2,
+[  --disable-wccpv2        Disable Web Cache Coordination V2 Protocol],
+[ if test "$enableval" = "no" ; then
+    echo "Web Cache Coordination V2 Protocol disabled"
+    USE_WCCPv2=0
+  fi
+])
+if test $USE_WCCPv2 = 1; then
+    AC_DEFINE(USE_WCCPv2, 1, [Define to enable WCCP V2])
 fi
 
 AC_ARG_ENABLE(kill-parent-hack,
diff --git a/doc/README.wccpv2 b/doc/README.wccpv2
new file mode 100755 (executable)
index 0000000..a3a7f2f
--- /dev/null
@@ -0,0 +1,62 @@
+This version of squid has been patched with the wccpv2 support patch.  To
+enable this option, use the --enable-wccpv2 option on the configure script.
+
+Wccpv2 allows a single cache to associate itself with multiple routers, and
+also allows multiple wccp services to be defined.  The other advantage of
+wccpv2 is that the cisco IOS uses CEF to switch the packets.
+
+Example simple web cache
+------------------------
+
+Internet <-> fa0/0 2621 fa0/1 <-> cache, internal network
+
+squid.conf:
+
+wccp2_router 192.168.2.1:2048
+wccp2_version 4
+wccp2_forwarding_method 1
+wccp2_return_method 1
+wccp2_service standard 0
+
+Router config:
+
+ip wccp web-cache
+!
+interface FastEthernet0/0
+ description uplink
+ ip address 192.168.1.200 255.255.255.0
+ ip wccp web-cache redirect out
+ duplex auto
+ speed auto
+!
+interface FastEthernet0/1
+ description local network
+ ip address 192.168.2.1 255.255.255.0
+ duplex auto
+ speed auto
+!
+
+Note the cache is on the internal network (Fa0/1), the interception occurs
+on the uplink interface(0/0).
+
+Linux (Kernel 2.6.10, i386) side of things:
+
+#!/bin/sh
+echo "1" > /proc/sys/net/ipv4/ip_forward
+echo "0" > /proc/sys/net/ipv4/conf/all/rp_filter
+iptunnel add gre1 mode gre remote 192.168.2.1 local 192.168.2.2 dev eth0
+ifconfig gre1 up 127.0.0.2
+iptables -t nat -F
+# iptables -t nat -A PREROUTING -i eth0 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 3128
+iptables -t nat -A PREROUTING -i gre1 -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.2.2:3128 
+
+
+TODO
+----
+
+* The wccp2 router configuration need to have a better configuration interface
+  (it currently uses ip:port when only the ip address is needed).
+* A shutting-down cache should generate a removal query, informing the router
+  (and therefore the caches in the group) that this cache is going
+  away and no new traffic should be forwarded to it.
+* Some more documentation, examples, etc.
index 8e3bc9f2c3d5466791584173d8c0b3f71f61e6cb..fdf9c77876d1fb55c64bc992a7158e93dd9d1b01 100644 (file)
@@ -1,7 +1,7 @@
 #
 #  Makefile for the Squid Object Cache server
 #
-#  $Id: Makefile.am,v 1.156 2006/06/14 19:18:24 serassio Exp $
+#  $Id: Makefile.am,v 1.157 2006/07/02 16:53:46 serassio Exp $
 #
 #  Uncomment and customize the following to suit your needs:
 #
@@ -568,6 +568,7 @@ squid_SOURCES = \
        useragent.cc \
        wais.cc \
        wccp.cc \
+       wccp2.cc \
        whois.cc \
        wordlist.cc \
        wordlist.h \
@@ -823,6 +824,7 @@ ufsdump_SOURCES = \
        useragent.cc \
        wais.cc \
        wccp.cc \
+       wccp2.cc \
        whois.cc \
        wordlist.cc \
        $(WIN32_SOURCE)
index c1272f7ed4f53ba3ed8ec0c35fd1a362d25ecf3c..699d38a7c12e1342609566ad8155531adb9c5d2a 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: cache_cf.cc,v 1.499 2006/06/14 19:52:54 serassio Exp $
+ * $Id: cache_cf.cc,v 1.500 2006/07/02 16:53:46 serassio Exp $
  *
  * DEBUG: section 3     Configuration File Parsing
  * AUTHOR: Harvest Derived
@@ -124,12 +124,14 @@ static void free_http_header_replace(header_mangler * header);
 static void parse_denyinfo(acl_deny_info_list ** var);
 static void dump_denyinfo(StoreEntry * entry, const char *name, acl_deny_info_list * var);
 static void free_denyinfo(acl_deny_info_list ** var);
-#if CURRENTLY_UNUSED
+#if USE_WCCPv2
 static void parse_sockaddr_in_list(sockaddr_in_list **);
 static void dump_sockaddr_in_list(StoreEntry *, const char *, const sockaddr_in_list *);
 static void free_sockaddr_in_list(sockaddr_in_list **);
+#if CURRENTLY_UNUSED
 static int check_null_sockaddr_in_list(const sockaddr_in_list *);
 #endif /* CURRENTLY_UNUSED */
+#endif
 static void parse_http_port_list(http_port_list **);
 static void dump_http_port_list(StoreEntry *, const char *, const http_port_list *);
 static void free_http_port_list(http_port_list **);
@@ -2606,10 +2608,7 @@ parseNeighborType(const char *s)
     return PEER_SIBLING;
 }
 
-#if CURRENTLY_UNUSED
-/* This code was previously used by http_port. Left as it really should
- * be used by icp_port and htcp_port
- */
+#if USE_WCCPv2
 void
 parse_sockaddr_in_list_token(sockaddr_in_list ** head, char *token)
 {
@@ -2688,6 +2687,10 @@ free_sockaddr_in_list(sockaddr_in_list ** head)
     }
 }
 
+#if CURRENTLY_UNUSED
+/* This code was previously used by http_port. Left as it really should
+ * be used by icp_port and htcp_port
+ */
 static int
 check_null_sockaddr_in_list(const sockaddr_in_list * s)
 {
@@ -2695,6 +2698,7 @@ check_null_sockaddr_in_list(const sockaddr_in_list * s)
 }
 
 #endif /* CURRENTLY_UNUSED */
+#endif /* USE_WCCPv2 */
 
 static void
 parse_http_port_specification(http_port_list * s, char *token)
index e875a40f42fb8ee5923c05f06d142b6e6ef65ec8..abd65b43c81e5da388682195e425eb8a59149ea5 100644 (file)
@@ -1,6 +1,6 @@
 
 #
-# $Id: cf.data.pre,v 1.418 2006/06/14 19:18:24 serassio Exp $
+# $Id: cf.data.pre,v 1.419 2006/07/02 16:53:46 serassio Exp $
 #
 #
 # SQUID Web Proxy Cache                http://www.squid-cache.org/
@@ -4005,10 +4005,22 @@ TYPE: address
 LOC: Config.Wccp.router
 DEFAULT: 0.0.0.0
 IFDEF: USE_WCCP
+DOC_NONE
+NAME: wccp2_router
+TYPE: sockaddr_in_list
+LOC: Config.Wccp2.router
+DEFAULT: none
+IFDEF: USE_WCCPv2
 DOC_START
        Use this option to define your WCCP ``home'' router for
-       Squid.   Setting the 'wccp_router' to 0.0.0.0 (the default)
-       disables WCCP.
+       Squid.
+
+       wccp_router supports a single WCCP(v1) router
+
+       wccp2_router supports multiple WCCPv2 routers
+
+       only one of the two may be used at the same time and defines
+       which version of WCCP to use.
 DOC_END
 
 NAME: wccp_version
@@ -4017,41 +4029,133 @@ LOC: Config.Wccp.version
 DEFAULT: 4
 IFDEF: USE_WCCP
 DOC_START
-       According to some users, Cisco IOS 11.2 only supports WCCP
-       version 3.  If you're using that version of IOS, change
-       this value to 3.
+       This directive is only relevant if you need to set up WCCP(v1)
+       to some very old and end-of-life Cisco routers. In all other
+       setups it must be left unset or at the default setting.
+       It defines an internal version in the WCCP(v1) protocol,
+       with version 4 being the officially documented protocol.
+
+       According to some users, Cisco IOS 11.2 and earlier only
+       support WCCP version 3.  If you're using that or an earlier
+       version of IOS, you may need to change this value to 3, otherwise
+       do not specify this parameter.
+DOC_END
+
+NAME: wccp2_forwarding_method
+TYPE: int
+LOC: Config.Wccp2.forwarding_method
+DEFAULT: 1
+IFDEF: USE_WCCPv2
+DOC_START
+       WCCP2 allows the setting of forwarding methods between the 
+       router/switch and the cache.  Valid values are as follows:
+
+       1 - GRE encapsulation (forward the packet in a GRE/WCCP tunnel)
+       2 - L2 redirect (forward the packet using Layer 2/MAC rewriting)
+
+       Currently (as of IOS 12.4) cisco routers only support GRE.
+       Cisco switches only support the L2 redirect assignment method.
+DOC_END
+
+NAME: wccp2_return_method
+TYPE: int
+LOC: Config.Wccp2.return_method
+DEFAULT: 1
+IFDEF: USE_WCCPv2
+DOC_START
+       WCCP2 allows the setting of return methods between the 
+       router/switch and the cache for packets that the cache
+       decides not to handle.  Valid values are as follows:
+
+       1 - GRE encapsulation (forward the packet in a GRE/WCCP tunnel)
+       2 - L2 redirect (forward the packet using Layer 2/MAC rewriting)
+
+       Currently (as of IOS 12.4) cisco routers only support GRE.
+       Cisco switches only support the L2 redirect assignment.
+
+       If the "ip wccp redirect exclude in" command has been 
+       enabled on the cache interface, then it is still safe for
+       the proxy server to use a l2 redirect method even if this
+       option is set to GRE.
+DOC_END
+
+NAME: wccp2_service
+TYPE: wccp2_service
+LOC: Config.Wccp2.info
+DEFAULT: none
+DEFAULT_IF_NONE: standard 0
+IFDEF: USE_WCCPv2
+DOC_START
+       WCCP2 allows for multiple traffic services. There are two
+       types: "standard" and "dynamic". The standard type defines
+       one service id - http (id 0). The dynamic service ids can be from
+       51 to 255 inclusive.  In order to use a dynamic service id
+       one must define the type of traffic to be redirected; this is done
+       using the wccp2_service_info option.
+
+       The "standard" type does not require a wccp2_service_info option,
+       just specifying the service id will suffice.
+
+       MD5 service authentication can be enabled by adding
+       "password=<password>" to the end of this service declaration.
+
+       Examples:
+
+       wccp2_service standard 0        # for the 'web-cache' standard service
+       wccp2_service dynamic 80        # a dynamic service type which will be
+                                       # fleshed out with subsequent options.
+       wccp2_service standard 0 password=foo
+
 DOC_END
 
-NAME: wccp_incoming_address
+NAME: wccp2_service_info
+TYPE: wccp2_service_info
+LOC: Config.Wccp2.info
+DEFAULT: none
+IFDEF: USE_WCCPv2
+DOC_START
+       Dynamic WCCPv2 services require further information to define the
+       traffic you wish to have diverted.
+
+       The format is:
+
+       wccp2_service_info <id> protocol=<protocol> flags=<flag>,<flag>..
+           priority=<priority> ports=<port>,<port>..
+
+       The relevant WCCPv2 flags:
+       + src_ip_hash, dst_ip_hash
+       + source_port_hash, dest_port_hash
+       + src_ip_alt_hash, dst_ip_alt_hash
+       + src_port_alt_hash, dst_port_alt_hash
+       + ports_source
+
+       The port list can be one to eight entries.
+
+       Example:
+
+       wccp2_service_info 80 protocol=tcp flags=src_ip_hash,ports_source
+           priority=240 ports=80
+
+       Note: the service id must have been defined by a previous
+       'wccp2_service dynamic <id>' entry.
+DOC_END
+
+NAME: wccp_address
 TYPE: address
-LOC: Config.Wccp.incoming
+LOC: Config.Wccp.address
 DEFAULT: 0.0.0.0
 IFDEF: USE_WCCP
 DOC_NONE
-NAME: wccp_outgoing_address
+NAME: wccp2_address
 TYPE: address
-LOC: Config.Wccp.outgoing
-DEFAULT: 255.255.255.255
-IFDEF: USE_WCCP
+LOC: Config.Wccp2.address
+DEFAULT: 0.0.0.0
+IFDEF: USE_WCCPv2
 DOC_START
-       wccp_incoming_address   Use this option if you require WCCP
-                               messages to be received on only one
-                               interface.  Do NOT use this option if
-                               you're unsure how many interfaces you
-                               have, or if you know you have only one
-                               interface.
-
-       wccp_outgoing_address   Use this option if you require WCCP
-                               messages to be sent out on only one
-                               interface.  Do NOT use this option if
-                               you're unsure how many interfaces you
-                               have, or if you know you have only one
-                               interface.
+       Use this option if you require WCCP to use a specific
+       interface address.
 
        The default behavior is to not bind to any specific address.
-
-       NOTE, wccp_incoming_address and wccp_outgoing_address can not have
-       the same value since they both use port 2048.
 DOC_END
 
 
index c5b7ee25d2c1eca365cd4e28966fa3f749bfd69a..e58e6f78981ebaf20fa18c481ce1e860c956ec30 100755 (executable)
@@ -18,6 +18,7 @@ BEGIN {
        define["USE_UNLINKD"]="--enable-unlinkd"
        define["USE_USERAGENT_LOG"]="--enable-useragent-log"
        define["USE_WCCP"]="--enable-wccp"
+       define["USE_WCCPv2"]="--enable-wccpv2"
        define["ESI"]="--enable-esi"
 }
 /^IFDEF:/ {
index a92ef2b4a880bbfeba08f0606cbde937fedf055c..380b0979add27bf7e437253322b753d1b95d9fd3 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: main.cc,v 1.426 2006/06/13 20:52:05 serassio Exp $
+ * $Id: main.cc,v 1.427 2006/07/02 16:53:46 serassio Exp $
  *
  * DEBUG: section 1     Startup and Main Loop
  * AUTHOR: Harvest Derived
@@ -508,6 +508,11 @@ serverConnectionsOpen(void)
     wccpConnectionOpen();
 #endif
 
+#if USE_WCCPv2
+
+    wccp2ConnectionOpen();
+#endif
+
     clientdbInit();
     icmpOpen();
     netdbInit();
@@ -538,7 +543,11 @@ serverConnectionsClose(void)
 #endif
 #if USE_WCCP
 
-    wccpConnectionShutdown();
+    wccpConnectionClose();
+#endif
+#if USE_WCCPv2
+
+    wccp2ConnectionClose();
 #endif
 
     asnFreeMemory();
@@ -560,10 +569,6 @@ mainReconfigure(void)
 
     snmpConnectionClose();
 #endif
-#if USE_WCCP
-
-    wccpConnectionClose();
-#endif
 #if USE_DNSSERVERS
 
     dnsShutdown();
@@ -609,6 +614,10 @@ mainReconfigure(void)
 
     wccpInit();
 #endif
+#if USE_WCCPv2
+
+    wccp2Init();
+#endif
 
     serverConnectionsOpen();
 
@@ -916,6 +925,11 @@ mainInitialize(void)
 #if USE_WCCP
     wccpInit();
 
+#endif
+#if USE_WCCPv2
+
+    wccp2Init();
+
 #endif
 
     serverConnectionsOpen();
@@ -1606,6 +1620,10 @@ SquidShutdown(void *unused)
 
     wccpConnectionClose();
 #endif
+#if USE_WCCPv2
+
+    wccp2ConnectionClose();
+#endif
 
     releaseServerSockets();
     commCloseAllSockets();
index b2d324f7e20ed15ddc429633249b53cb7e8f0e01..d3871ed8fe7f29f9d94c9c24b5c50b7e02013705 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: protos.h,v 1.533 2006/05/29 00:15:02 robertc Exp $
+ * $Id: protos.h,v 1.534 2006/07/02 16:53:46 serassio Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -381,10 +381,15 @@ SQUIDCEXTERN variable_list *snmp_meshCtblFn(variable_list *, snint *);
 #if USE_WCCP
 SQUIDCEXTERN void wccpInit(void);
 SQUIDCEXTERN void wccpConnectionOpen(void);
-SQUIDCEXTERN void wccpConnectionShutdown(void);
 SQUIDCEXTERN void wccpConnectionClose(void);
 #endif /* USE_WCCP */
 
+#if USE_WCCPv2
+extern void wccp2Init(void);
+extern void wccp2ConnectionOpen(void);
+extern void wccp2ConnectionClose(void);
+#endif /* USE_WCCPv2 */
+
 SQUIDCEXTERN void ipcache_nbgethostbyname(const char *name,
         IPH * handler,
         void *handlerData);
@@ -907,4 +912,21 @@ SQUIDCEXTERN void externalAclShutdown(void);
 
 SQUIDCEXTERN char *strtokFile(void);
 
+#if USE_WCCPv2
+SQUIDCEXTERN void parse_wccp2_service(void *v);
+
+SQUIDCEXTERN void free_wccp2_service(void *v);
+
+SQUIDCEXTERN void dump_wccp2_service(StoreEntry * e, const char *label, void *v);
+
+SQUIDCEXTERN int check_null_wccp2_service(void *v);
+
+SQUIDCEXTERN void parse_wccp2_service_info(void *v);
+
+SQUIDCEXTERN void free_wccp2_service_info(void *v);
+
+SQUIDCEXTERN void dump_wccp2_service_info(StoreEntry * e, const char *label, void *v);
+
+#endif
+
 #endif /* SQUID_PROTOS_H */
index c059aa14cd8caed38f4afb98735c107f53e1885c..f7994d70249847912e8261f183866a69bdfd73a4 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: structs.h,v 1.543 2006/05/30 17:31:23 hno Exp $
+ * $Id: structs.h,v 1.544 2006/07/02 16:53:47 serassio Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -312,14 +312,26 @@ struct _SquidConfig
 
         struct IN_ADDR router;
 
-        struct IN_ADDR incoming;
-
-        struct IN_ADDR outgoing;
+        struct IN_ADDR address;
         int version;
     }
 
     Wccp;
 #endif
+#if USE_WCCPv2
+
+    struct
+    {
+        sockaddr_in_list *router;
+
+        struct IN_ADDR address;
+        int forwarding_method;
+        int return_method;
+        void *info;
+    }
+
+    Wccp2;
+#endif
 
     char *as_whois_server;
 
index 1f69e6fa363a22c94fc90b69601c60d33170f4be..90348ffce0517b1e3150e99d06c4312c74c71a75 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: wccp.cc,v 1.39 2005/04/18 21:52:43 hno Exp $
+ * $Id: wccp.cc,v 1.40 2006/07/02 16:53:47 serassio Exp $
  *
  * DEBUG: section 80    WCCP Support
  * AUTHOR: Glenn Chisholm
@@ -85,8 +85,7 @@ struct wccp_assign_bucket_t
     int number;
 };
 
-static int theInWccpConnection = -1;
-static int theOutWccpConnection = -1;
+static int theWccpConnection = -1;
 
 static struct wccp_here_i_am_t wccp_here_i_am;
 
@@ -143,94 +142,57 @@ wccpConnectionOpen(void)
         return;
     }
 
-    theInWccpConnection = comm_open(SOCK_DGRAM,
-                                    IPPROTO_UDP,
-                                    Config.Wccp.incoming,
-                                    port,
-                                    COMM_NONBLOCKING,
-                                    "WCCP Socket");
+    theWccpConnection = comm_open(SOCK_DGRAM,
+                                  IPPROTO_UDP,
+                                  Config.Wccp.address,
+                                  port,
+                                  COMM_NONBLOCKING,
+                                  "WCCP Socket");
 
-    if (theInWccpConnection < 0)
+    if (theWccpConnection < 0)
         fatal("Cannot open WCCP Port");
 
-    commSetSelect(theInWccpConnection,
+    commSetSelect(theWccpConnection,
                   COMM_SELECT_READ,
                   wccpHandleUdp,
                   NULL,
                   0);
 
     debug(1, 1) ("Accepting WCCP messages on port %d, FD %d.\n",
-                 (int) port, theInWccpConnection);
+                 (int) port, theWccpConnection);
 
-    if (Config.Wccp.outgoing.s_addr != no_addr.s_addr) {
-        theOutWccpConnection = comm_open(SOCK_DGRAM,
-                                         IPPROTO_UDP,
-                                         Config.Wccp.outgoing,
-                                         port,
-                                         COMM_NONBLOCKING,
-                                         "WCCP Socket");
-
-        if (theOutWccpConnection < 0)
-            fatal("Cannot open Outgoing WCCP Port");
-
-        commSetSelect(theOutWccpConnection,
-                      COMM_SELECT_READ,
-                      wccpHandleUdp,
-                      NULL, 0);
-
-        debug(1, 1) ("Outgoing WCCP 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;
-    }
 
     router_len = sizeof(router);
+
     memset(&router, '\0', router_len);
+
     router.sin_family = AF_INET;
+
     router.sin_port = htons(port);
+
     router.sin_addr = Config.Wccp.router;
 
-    if (connect(theOutWccpConnection, (struct sockaddr *) &router, router_len))
+    if (connect(theWccpConnection, (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))
+    if (getsockname(theWccpConnection, (struct sockaddr *) &local, &local_len))
         fatal("Unable to getsockname on WCCP out socket");
 
     local_ip.s_addr = local.sin_addr.s_addr;
 }
 
-void
-wccpConnectionShutdown(void)
-{
-    if (theInWccpConnection < 0)
-        return;
-
-    if (theInWccpConnection != theOutWccpConnection) {
-        debug(80, 1) ("FD %d Closing WCCP socket\n", theInWccpConnection);
-        comm_close(theInWccpConnection);
-        theInWccpConnection = -1;
-    }
-
-    assert(theOutWccpConnection > -1);
-    commSetSelect(theOutWccpConnection, COMM_SELECT_READ, NULL, NULL, 0);
-}
 
 void
 wccpConnectionClose(void)
 {
-    wccpConnectionShutdown();
-
-    if (theOutWccpConnection > -1) {
-        debug(80, 1) ("FD %d Closing WCCP socket\n", theOutWccpConnection);
-        comm_close(theOutWccpConnection);
+    if (theWccpConnection > -1) {
+        debug(80, 1) ("FD %d Closing WCCP socket\n", theWccpConnection);
+        comm_close(theWccpConnection);
+        theWccpConnection = -1;
     }
 }
 
@@ -285,7 +247,7 @@ wccpHandleUdp(int sock, void *not_used)
     if (ntohl(wccp_i_see_you.type) != WCCP_I_SEE_YOU)
         return;
 
-    if (ntohl(wccp_i_see_you.number) > WCCP_ACTIVE_CACHES || ntohl(wccp_i_see_you.number) < 0) {
+    if (ntohl(wccp_i_see_you.number) > WCCP_ACTIVE_CACHES) {
         debug(80, 1) ("Ignoring WCCP_I_SEE_YOU from %s with number of caches set to %d\n",
                       inet_ntoa(from.sin_addr), (int) ntohl(wccp_i_see_you.number));
         return;
@@ -349,7 +311,7 @@ wccpHereIam(void *voidnotused)
     debug(80, 6) ("wccpHereIam: Called\n");
 
     wccp_here_i_am.id = last_id;
-    comm_udp_send(theOutWccpConnection,
+    comm_udp_send(theWccpConnection,
                   &wccp_here_i_am,
                   sizeof(wccp_here_i_am),
                   0);
@@ -418,7 +380,7 @@ wccpAssignBuckets(void)
     wccp_assign_bucket->id = wccp_i_see_you.id;
     wccp_assign_bucket->number = wccp_i_see_you.number;
 
-    comm_udp_send(theOutWccpConnection,
+    comm_udp_send(theWccpConnection,
                   buf,
                   wab_len + WCCP_BUCKETS + cache_len,
                   0);
diff --git a/src/wccp2.cc b/src/wccp2.cc
new file mode 100644 (file)
index 0000000..c69a44c
--- /dev/null
@@ -0,0 +1,1974 @@
+
+/*
+ * $Id: wccp2.cc,v 1.1 2006/07/02 16:53:47 serassio Exp $
+ *
+ * DEBUG: section 80    WCCP Support
+ * AUTHOR: Steven WIlton
+ *
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+#include "squid.h"
+#include "comm.h"
+#include "Parsing.h"
+
+#if USE_WCCPv2
+#include <netdb.h>
+
+#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 theWccp2Connection = -1;
+static int wccp2_connected = 0;
+
+static PF wccp2HandleUdp;
+static EVH wccp2HereIam;
+static EVH wccp2AssignBuckets;
+
+/* KDW WCCP V2 */
+#define WCCP2_HERE_I_AM                10
+#define WCCP2_I_SEE_YOU                11
+#define WCCP2_REDIRECT_ASSIGN          12
+#define WCCP2_REMOVAL_QUERY            13
+#define WCCP2_VERSION                  0x200
+
+#define WCCP2_SECURITY_INFO            0
+#define WCCP2_NO_SECURITY              0
+#define WCCP2_MD5_SECURITY             1
+
+#define WCCP2_SERVICE_INFO             1
+#define WCCP2_SERVICE_STANDARD         0
+#define WCCP2_SERVICE_DYNAMIC          1
+#define WCCP2_SERVICE_ID_HTTP          0x00
+
+#define WCCP2_SERVICE_SRC_IP_HASH      0x1
+#define WCCP2_SERVICE_DST_IP_HASH      0x2
+#define WCCP2_SERVICE_SOURCE_PORT_HASH 0x4
+#define WCCP2_SERVICE_DST_PORT_HASH    0x8
+#define WCCP2_SERVICE_PORTS_DEFINED    0x10
+#define WCCP2_SERVICE_PORTS_SOURCE     0x20
+#define WCCP2_SERVICE_SRC_IP_ALT_HASH  0x100
+#define WCCP2_SERVICE_DST_IP_ALT_HASH  0x200
+#define WCCP2_SERVICE_SRC_PORT_ALT_HASH        0x400
+#define WCCP2_SERVICE_DST_PORT_ALT_HASH        0x800
+
+#define WCCP2_ROUTER_ID_INFO           2
+
+#define WCCP2_WC_ID_INFO               3
+
+#define WCCP2_RTR_VIEW_INFO            4
+
+#define WCCP2_WC_VIEW_INFO             5
+
+#define WCCP2_REDIRECT_ASSIGNMENT      6
+
+#define WCCP2_QUERY_INFO               7
+
+#define WCCP2_CAPABILITY_INFO          8
+
+#define WCCP2_CAPABILITY_FORWARDING_METHOD     0x01
+#define WCCP2_CAPABILITY_ASSIGNMENT_METHOD     0x02
+#define WCCP2_CAPABILITY_RETURN_METHOD         0x03
+
+#define WCCP2_CAPABILITY_GRE           0x00000001
+#define WCCP2_CAPABILITY_L2            0x00000002
+
+#define        WCCP2_NONE_SECURITY_LEN 0
+#define        WCCP2_MD5_SECURITY_LEN  16
+
+/* Useful defines */
+#define        WCCP2_NUMPORTS  8
+#define        WCCP2_PASSWORD_LEN      8
+
+/* WCCP v2 packet header */
+
+struct wccp2_here_i_am_header_t
+{
+    uint32_t type;
+    uint16_t version;
+    uint16_t length;
+};
+
+static struct wccp2_here_i_am_header_t wccp2_here_i_am_header;
+
+/* Security struct for the "no security" option */
+
+struct wccp2_security_none_t
+{
+    uint16_t security_type;
+    uint16_t security_length;
+    uint32_t security_option;
+};
+
+struct wccp2_security_md5_t
+{
+    uint16_t security_type;
+    uint16_t security_length;
+    uint32_t security_option;
+    uint8_t security_implementation[WCCP2_MD5_SECURITY_LEN];
+};
+
+/* Service info struct */
+
+struct wccp2_service_info_t
+{
+    uint16_t service_type;
+    uint16_t service_length;
+    uint8_t service;
+    uint8_t service_id;
+    uint8_t service_priority;
+    uint8_t service_protocol;
+    uint32_t service_flags;
+    uint16_t port0;
+    uint16_t port1;
+    uint16_t port2;
+    uint16_t port3;
+    uint16_t port4;
+    uint16_t port5;
+    uint16_t port6;
+    uint16_t port7;
+};
+
+struct wccp2_cache_identity_info_t
+{
+
+    struct IN_ADDR addr;
+    uint16_t hash_revision;
+    char bits[2];
+    char buckets[32];
+    uint16_t weight;
+    uint16_t status;
+};
+
+/* Web Cache identity info */
+
+struct wccp2_identity_info_t
+{
+    uint16_t cache_identity_type;
+    uint16_t cache_identity_length;
+
+    struct wccp2_cache_identity_info_t cache_identity;
+};
+
+static struct wccp2_identity_info_t wccp2_identity_info;
+
+/* View header */
+
+struct wccp2_cache_view_header_t
+{
+    uint16_t cache_view_type;
+    uint16_t cache_view_length;
+    uint32_t cache_view_version;
+};
+
+static struct wccp2_cache_view_header_t wccp2_cache_view_header;
+
+/* View info */
+
+struct wccp2_cache_view_info_t
+{
+    uint32_t num_routers;
+    uint32_t num_caches;
+};
+
+static struct wccp2_cache_view_info_t wccp2_cache_view_info;
+
+/* Router ID element */
+
+struct wccp2_router_id_element_t
+{
+
+    struct IN_ADDR router_address;
+    uint32_t received_id;
+};
+
+static struct wccp2_router_id_element_t wccp2_router_id_element;
+
+/* Capability info header */
+
+struct wccp2_capability_info_header_t
+{
+    uint16_t capability_info_type;
+    uint16_t capability_info_length;
+};
+
+static struct wccp2_capability_info_header_t wccp2_capability_info_header;
+
+/* Capability element */
+
+struct wccp2_capability_element_t
+{
+    uint16_t capability_type;
+    uint16_t capability_length;
+    uint32_t capability_value;
+};
+
+static struct wccp2_capability_element_t wccp2_capability_element;
+
+
+/* RECEIVED PACKET STRUCTURE */
+
+struct wccp2_i_see_you_t
+{
+    uint32_t type;
+    uint16_t version;
+    uint16_t length;
+    char data[WCCP_RESPONSE_SIZE];
+};
+
+static struct wccp2_i_see_you_t wccp2_i_see_you;
+
+/* Router ID element */
+
+struct wccp2_router_assign_element_t
+{
+
+    struct IN_ADDR router_address;
+    uint32_t received_id;
+    uint32_t change_number;
+};
+
+/* Generic header struct */
+
+struct wccp2_item_header_t
+{
+    uint16_t type;
+    uint16_t length;
+};
+
+/* Router identity struct */
+
+struct router_identity_info_t
+{
+
+    struct wccp2_item_header_t header;
+
+    struct wccp2_router_id_element_t router_id_element;
+
+    struct IN_ADDR router_address;
+    uint32_t number_caches;
+};
+
+/* assigment key */
+
+struct assignment_key_t
+{
+
+    struct IN_ADDR master_ip;
+    uint32_t master_number;
+};
+
+/* Router view of WCCP */
+
+struct router_view_t
+{
+
+    struct wccp2_item_header_t header;
+    uint32_t change_number;
+
+    struct assignment_key_t assignment_key;
+};
+
+
+/* Lists used to keep track of caches, routers and services */
+
+struct wccp2_cache_list_t
+{
+
+    struct IN_ADDR cache_ip;
+
+    struct wccp2_cache_list_t *next;
+};
+
+struct wccp2_router_list_t
+{
+
+    struct wccp2_router_id_element_t *info;
+
+    struct IN_ADDR local_ip;
+
+    struct IN_ADDR router_sendto_address;
+    uint32_t member_change;
+    uint32_t num_caches;
+
+    struct wccp2_cache_list_t cache_list_head;
+
+    struct wccp2_router_list_t *next;
+};
+
+static int wccp2_numrouters;
+
+struct wccp2_service_list_t
+{
+
+    struct wccp2_service_info_t info;
+    uint32_t num_routers;
+
+    struct wccp2_router_list_t router_list_head;
+    int lowest_ip;
+    uint32_t change_num;
+
+    struct wccp2_identity_info_t *wccp2_identity_info_ptr;
+
+    struct wccp2_security_md5_t *security_info;
+
+    struct wccp2_service_info_t *service_info;
+    char wccp_packet[WCCP_RESPONSE_SIZE];
+    size_t wccp_packet_size;
+
+    struct wccp2_service_list_t *next;
+    char wccp_password[WCCP2_PASSWORD_LEN + 1];                /* hold the trailing C-string NUL */
+    uint32_t wccp2_security_type;
+};
+
+static struct wccp2_service_list_t *wccp2_service_list_head = NULL;
+
+int empty_portlist[WCCP2_NUMPORTS] =
+    {0, 0, 0, 0, 0, 0, 0, 0};
+
+/* END WCCP V2 */
+void wccp2_add_service_list(int service, int service_id, int service_priority,
+                            int service_proto, int service_flags, int ports[], int security_type, char *password);
+
+/*
+ * The functions used during startup:
+ * wccp2Init
+ * wccp2ConnectionOpen
+ * wccp2ConnectionClose
+ */
+
+static void
+wccp2InitServices(void)
+{
+    debug(80, 5) ("wccp2InitServices: called\n");
+}
+
+static void
+
+wccp2_update_service(struct wccp2_service_list_t *srv, int service,
+                     int service_id, int service_priority, int service_proto, int service_flags,
+                     int ports[])
+{
+    /* XXX check what needs to be wrapped in htons()! */
+    srv->info.service = service;
+    srv->info.service_id = service_id;
+    srv->info.service_priority = service_priority;
+    srv->info.service_protocol = service_proto;
+    srv->info.service_flags = htonl(service_flags);
+    srv->info.port0 = htons(ports[0]);
+    srv->info.port1 = htons(ports[1]);
+    srv->info.port2 = htons(ports[2]);
+    srv->info.port3 = htons(ports[3]);
+    srv->info.port4 = htons(ports[4]);
+    srv->info.port5 = htons(ports[5]);
+    srv->info.port6 = htons(ports[6]);
+    srv->info.port7 = htons(ports[7]);
+}
+
+void
+wccp2_add_service_list(int service, int service_id, int service_priority,
+                       int service_proto, int service_flags, int ports[], int security_type,
+                       char *password)
+{
+
+    struct wccp2_service_list_t *wccp2_service_list_ptr;
+
+    wccp2_service_list_ptr = (wccp2_service_list_t *) xcalloc(1, sizeof(struct wccp2_service_list_t));
+
+    debug(80, 5) ("wccp2_add_service_list: added service id %d\n", service_id);
+
+    /* XXX check what needs to be wrapped in htons()! */
+    wccp2_service_list_ptr->info.service_type = htons(WCCP2_SERVICE_INFO);
+
+    wccp2_service_list_ptr->info.service_length = htons(sizeof(struct wccp2_service_info_t) - 4);
+    wccp2_service_list_ptr->change_num = 0;
+    wccp2_update_service(wccp2_service_list_ptr, service, service_id,
+                         service_priority, service_proto, service_flags, ports);
+    wccp2_service_list_ptr->wccp2_security_type = security_type;
+    bzero(wccp2_service_list_ptr->wccp_password, WCCP2_PASSWORD_LEN + 1);
+    strncpy(wccp2_service_list_ptr->wccp_password, password, WCCP2_PASSWORD_LEN);
+    /* add to linked list - XXX this should use the Squid dlink* routines! */
+    wccp2_service_list_ptr->next = wccp2_service_list_head;
+    wccp2_service_list_head = wccp2_service_list_ptr;
+}
+
+static struct wccp2_service_list_t *
+            wccp2_get_service_by_id(int service, int service_id)
+{
+
+    struct wccp2_service_list_t *p;
+
+    p = wccp2_service_list_head;
+
+    while (p != NULL)
+    {
+        if (p->info.service == service && p->info.service_id == service_id) {
+            return p;
+        }
+
+        p = p->next;
+    }
+
+    return NULL;
+}
+
+/*
+ * Update the md5 security header, if possible
+ *
+ * Returns: 1 if we set it, 0 if not (eg, no security section, or non-md5)
+ */
+static char
+wccp2_update_md5_security(char *password, char *ptr, char *packet, int len)
+{
+    u_int8_t md5_digest[16];
+    char pwd[WCCP2_PASSWORD_LEN];
+    MD5_CTX M;
+
+    struct wccp2_security_md5_t *ws;
+
+    debug(80, 5) ("wccp2_update_md5_security: called\n");
+
+    /* The password field, for the MD5 hash, needs to be 8 bytes and NUL padded. */
+    bzero(pwd, sizeof(pwd));
+    strncpy(pwd, password, sizeof(pwd));
+
+    ws = (struct wccp2_security_md5_t *) ptr;
+    assert(ntohs(ws->security_type) == WCCP2_SECURITY_INFO);
+    /* Its the security part */
+
+    if (ntohl(ws->security_option) != WCCP2_MD5_SECURITY) {
+        debug(80, 5) ("wccp2_update_md5_security: this service ain't md5'ing, abort\n");
+        return 0;
+    }
+
+    /* And now its the MD5 section! */
+    /* According to the draft, the MD5 security hash is the combination of
+     * the 8-octet password (padded w/ NUL bytes) and the entire WCCP packet,
+     * including the WCCP message header. The WCCP security implementation
+     * area should be zero'ed before calculating the MD5 hash.
+     */
+    /* XXX eventually we should be able to kill md5_digest and blit it directly in */
+    bzero(ws->security_implementation, sizeof(ws->security_implementation));
+
+    MD5Init(&M);
+
+    MD5Update(&M, pwd, 8);
+
+    MD5Update(&M, packet, len);
+
+    MD5Final(md5_digest, &M);
+
+    memcpy(ws->security_implementation, md5_digest, sizeof(md5_digest));
+
+    /* Finished! */
+    return 1;
+}
+
+
+/*
+ * Check the given WCCP2 packet against the given password.
+ */
+static char
+
+wccp2_check_security(struct wccp2_service_list_t *srv, char *security, char *packet, int len)
+{
+
+    struct wccp2_security_md5_t *ws = (struct wccp2_security_md5_t *) security;
+    u_int8_t md5_digest[16], md5_challenge[16];
+    char pwd[WCCP2_PASSWORD_LEN];
+    MD5_CTX M;
+
+    /* Make sure the security type matches what we expect */
+
+    if (ntohl(ws->security_option) != srv->wccp2_security_type)
+    {
+        debug(80, 1) ("wccp2_check_security: received packet has the wrong security option\n");
+        return 0;
+    }
+
+    if (srv->wccp2_security_type == WCCP2_NO_SECURITY)
+    {
+        return 1;
+    }
+
+    if (srv->wccp2_security_type != WCCP2_MD5_SECURITY)
+    {
+        debug(80, 1) ("wccp2_check_security: invalid security option\n");
+        return 0;
+    }
+
+    /* If execution makes it here then we have an MD5 security */
+
+    /* The password field, for the MD5 hash, needs to be 8 bytes and NUL padded. */
+    bzero(pwd, sizeof(pwd));
+
+    strncpy(pwd, srv->wccp_password, sizeof(pwd));
+
+    /* Take a copy of the challenge: we need to NUL it before comparing */
+    memcpy(md5_challenge, ws->security_implementation, 16);
+
+    bzero(ws->security_implementation, sizeof(ws->security_implementation));
+
+    MD5Init(&M);
+
+    MD5Update(&M, pwd, 8);
+
+    MD5Update(&M, packet, len);
+
+    MD5Final(md5_digest, &M);
+
+    return (memcmp(md5_digest, md5_challenge, 16) == 0);
+}
+
+
+void
+wccp2Init(void)
+{
+    sockaddr_in_list *s;
+    char *ptr;
+
+    struct wccp2_service_list_t *service_list_ptr;
+
+    struct wccp2_router_list_t *router_list_ptr;
+
+    struct wccp2_security_md5_t wccp2_security_md5;
+
+    debug(80, 5) ("wccp2Init: Called\n");
+
+    if (wccp2_connected == 1)
+        return;
+
+    wccp2_numrouters = 0;
+
+    /* Calculate the number of routers configured in the config file */
+    for (s = Config.Wccp2.router; s; s = s->next) {
+        if (s->s.sin_addr.s_addr != any_addr.s_addr) {
+            /* Increment the counter */
+            wccp2_numrouters++;
+        }
+    }
+
+    if (wccp2_numrouters == 0) {
+        return;
+    }
+
+    /* Initialise the list of services */
+    wccp2InitServices();
+
+    service_list_ptr = wccp2_service_list_head;
+
+    while (service_list_ptr != NULL) {
+        /* Set up our list pointers */
+        router_list_ptr = &service_list_ptr->router_list_head;
+
+        /* start the wccp header */
+        wccp2_here_i_am_header.type = htonl(WCCP2_HERE_I_AM);
+        wccp2_here_i_am_header.version = htons(WCCP2_VERSION);
+        wccp2_here_i_am_header.length = 0;
+        ptr = service_list_ptr->wccp_packet + sizeof(wccp2_here_i_am_header);
+
+        /* add the security section */
+        /* XXX this is ugly */
+
+        if (service_list_ptr->wccp2_security_type == WCCP2_MD5_SECURITY) {
+            wccp2_security_md5.security_option = htonl(WCCP2_MD5_SECURITY);
+
+            wccp2_security_md5.security_length = htons(sizeof(struct wccp2_security_md5_t) - 4);
+        } else if (service_list_ptr->wccp2_security_type == WCCP2_NO_SECURITY) {
+            wccp2_security_md5.security_option = htonl(WCCP2_NO_SECURITY);
+            /* XXX I hate magic length numbers! */
+            wccp2_security_md5.security_length = htons(4);
+        } else {
+            fatalf("Bad WCCP2 security type\n");
+        }
+
+        wccp2_here_i_am_header.length += ntohs(wccp2_security_md5.security_length) + 4;
+        assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
+        wccp2_security_md5.security_type = htons(WCCP2_SECURITY_INFO);
+
+        service_list_ptr->security_info = (struct wccp2_security_md5_t *) ptr;
+
+        if (service_list_ptr->wccp2_security_type == WCCP2_MD5_SECURITY) {
+
+            xmemcpy(ptr, &wccp2_security_md5, sizeof(struct wccp2_security_md5_t));
+
+            ptr += sizeof(struct wccp2_security_md5_t);
+        } else {
+            /* assume NONE, and XXX I hate magic length numbers */
+            xmemcpy(ptr, &wccp2_security_md5, 8);
+            ptr += 8;
+        }
+
+        /* Add the service info section */
+
+        wccp2_here_i_am_header.length += sizeof(struct wccp2_service_info_t);
+
+        assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
+
+        xmemcpy(ptr, &service_list_ptr->info, sizeof(struct wccp2_service_info_t));
+
+        service_list_ptr->service_info = (struct wccp2_service_info_t *) ptr;
+
+        ptr += sizeof(struct wccp2_service_info_t);
+
+        /* Add the cache identity section */
+
+        wccp2_here_i_am_header.length += sizeof(struct wccp2_identity_info_t);
+
+        assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
+
+        wccp2_identity_info.cache_identity_type = htons(WCCP2_WC_ID_INFO);
+
+        wccp2_identity_info.cache_identity_length = htons(sizeof(wccp2_identity_info.cache_identity));
+
+        memset(&wccp2_identity_info.cache_identity.addr, '\0', sizeof(wccp2_identity_info.cache_identity.addr));
+
+        memset(&wccp2_identity_info.cache_identity.hash_revision, '\0', sizeof(wccp2_identity_info.cache_identity.hash_revision));
+
+        memset(&wccp2_identity_info.cache_identity.bits, '\0', sizeof(wccp2_identity_info.cache_identity.bits));
+
+        memset(&wccp2_identity_info.cache_identity.buckets, '\0', sizeof(wccp2_identity_info.cache_identity.buckets));
+
+        wccp2_identity_info.cache_identity.weight = htons(10000);
+
+        memset(&wccp2_identity_info.cache_identity.status, '\0', sizeof(wccp2_identity_info.cache_identity.status));
+
+        xmemcpy(ptr, &wccp2_identity_info, sizeof(struct wccp2_identity_info_t));
+
+        service_list_ptr->wccp2_identity_info_ptr = (struct wccp2_identity_info_t *) ptr;
+
+        ptr += sizeof(struct wccp2_identity_info_t);
+
+        /* Add the cache view section */
+        wccp2_here_i_am_header.length += sizeof(wccp2_cache_view_header);
+
+        assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
+
+        wccp2_cache_view_header.cache_view_type = htons(WCCP2_WC_VIEW_INFO);
+
+        wccp2_cache_view_header.cache_view_length = htons(sizeof(wccp2_cache_view_header) - 4 +
+                sizeof(wccp2_cache_view_info) + (wccp2_numrouters * sizeof(wccp2_router_id_element)));
+
+        wccp2_cache_view_header.cache_view_version = htonl(1);
+
+        xmemcpy(ptr, &wccp2_cache_view_header, sizeof(wccp2_cache_view_header));
+
+        ptr += sizeof(wccp2_cache_view_header);
+
+        /* Add the number of routers to the packet */
+        wccp2_here_i_am_header.length += sizeof(service_list_ptr->num_routers);
+
+        assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
+
+        service_list_ptr->num_routers = htonl(wccp2_numrouters);
+
+        xmemcpy(ptr, &service_list_ptr->num_routers, sizeof(service_list_ptr->num_routers));
+
+        ptr += sizeof(service_list_ptr->num_routers);
+
+        /* Add each router.  Keep this functionality here to make sure the received_id can be updated in the packet */
+        for (s = Config.Wccp2.router; s; s = s->next) {
+            if (s->s.sin_addr.s_addr != any_addr.s_addr) {
+
+                wccp2_here_i_am_header.length += sizeof(struct wccp2_router_id_element_t);
+                assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
+
+                /* Add a pointer to the router list for this router */
+
+                router_list_ptr->info = (struct wccp2_router_id_element_t *) ptr;
+                router_list_ptr->info->router_address = s->s.sin_addr;
+                router_list_ptr->info->received_id = htonl(0);
+                router_list_ptr->router_sendto_address = s->s.sin_addr;
+                router_list_ptr->member_change = htonl(0);
+
+                /* Build the next struct */
+
+                router_list_ptr->next = (wccp2_router_list_t*) xcalloc(1, sizeof(struct wccp2_router_list_t));
+
+                /* update the pointer */
+                router_list_ptr = router_list_ptr->next;
+                router_list_ptr->next = NULL;
+
+                /* no need to copy memory - we've just set the values directly in the packet above */
+
+                ptr += sizeof(struct wccp2_router_id_element_t);
+            }
+        }
+
+        /* Add the number of caches (0) */
+        wccp2_here_i_am_header.length += sizeof(wccp2_cache_view_info.num_caches);
+
+        assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
+
+        wccp2_cache_view_info.num_caches = htonl(0);
+
+        xmemcpy(ptr, &wccp2_cache_view_info.num_caches, sizeof(wccp2_cache_view_info.num_caches));
+
+        ptr += sizeof(wccp2_cache_view_info.num_caches);
+
+        /* Add the extra capability header */
+        wccp2_here_i_am_header.length += sizeof(wccp2_capability_info_header);
+
+        assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
+
+        wccp2_capability_info_header.capability_info_type = htons(WCCP2_CAPABILITY_INFO);
+
+        wccp2_capability_info_header.capability_info_length = htons(2 * sizeof(wccp2_capability_element));
+
+        xmemcpy(ptr, &wccp2_capability_info_header, sizeof(wccp2_capability_info_header));
+
+        ptr += sizeof(wccp2_capability_info_header);
+
+        /* Add the forwarding method */
+        wccp2_here_i_am_header.length += sizeof(wccp2_capability_element);
+
+        assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
+
+        wccp2_capability_element.capability_type = htons(WCCP2_CAPABILITY_FORWARDING_METHOD);
+
+        wccp2_capability_element.capability_length = htons(sizeof(wccp2_capability_element.capability_value));
+
+        wccp2_capability_element.capability_value = htonl(Config.Wccp2.forwarding_method);
+
+        xmemcpy(ptr, &wccp2_capability_element, sizeof(wccp2_capability_element));
+
+        ptr += sizeof(wccp2_capability_element);
+
+        /* Add the return method */
+        wccp2_here_i_am_header.length += sizeof(wccp2_capability_element);
+
+        assert(wccp2_here_i_am_header.length <= WCCP_RESPONSE_SIZE);
+
+        wccp2_capability_element.capability_type = htons(WCCP2_CAPABILITY_RETURN_METHOD);
+
+        wccp2_capability_element.capability_length = htons(sizeof(wccp2_capability_element.capability_value));
+
+        wccp2_capability_element.capability_value = htonl(Config.Wccp2.return_method);
+
+        xmemcpy(ptr, &wccp2_capability_element, sizeof(wccp2_capability_element));
+
+        ptr += sizeof(wccp2_capability_element);
+
+        /* Finally, fix the total length to network order, and copy to the appropriate memory blob */
+        wccp2_here_i_am_header.length = htons(wccp2_here_i_am_header.length);
+
+        memcpy(&service_list_ptr->wccp_packet, &wccp2_here_i_am_header, sizeof(wccp2_here_i_am_header));
+
+        service_list_ptr->wccp_packet_size = ntohs(wccp2_here_i_am_header.length) + sizeof(wccp2_here_i_am_header);
+
+        /* Add the event if everything initialised correctly */
+        if (wccp2_numrouters) {
+            if (!eventFind(wccp2HereIam, NULL)) {
+                eventAdd("wccp2HereIam", wccp2HereIam, NULL, 1, 1);
+            }
+        }
+
+        service_list_ptr = service_list_ptr->next;
+    }
+}
+
+void
+wccp2ConnectionOpen(void)
+{
+    u_short port = WCCP_PORT;
+
+    struct sockaddr_in router, local;
+    socklen_t local_len, router_len;
+
+    struct wccp2_service_list_t *service_list_ptr;
+
+    struct wccp2_router_list_t *router_list_ptr;
+
+    debug(80, 5) ("wccp2ConnectionOpen: Called\n");
+
+    if (wccp2_numrouters == 0 || !wccp2_service_list_head) {
+        debug(80, 2) ("WCCPv2 Disabled.\n");
+        return;
+    }
+
+    theWccp2Connection = comm_open(SOCK_DGRAM,
+                                   0,
+                                   Config.Wccp2.address,
+                                   port,
+                                   COMM_NONBLOCKING,
+                                   "WCCPv2 Socket");
+
+    if (theWccp2Connection < 0)
+        fatal("Cannot open WCCP Port");
+
+    commSetSelect(theWccp2Connection,
+                  COMM_SELECT_READ,
+                  wccp2HandleUdp,
+                  NULL,
+                  0);
+
+    debug(80, 1) ("Accepting WCCPv2 messages on port %d, FD %d.\n",
+                  (int) port, theWccp2Connection);
+
+    debug(80, 1) ("Initialising all WCCPv2 lists\n");
+
+    /* Initialise all routers on all services */
+    service_list_ptr = wccp2_service_list_head;
+
+    while (service_list_ptr != NULL) {
+        for (router_list_ptr = &service_list_ptr->router_list_head; router_list_ptr->next != NULL; router_list_ptr = router_list_ptr->next) {
+            router_len = sizeof(router);
+            memset(&router, '\0', router_len);
+            router.sin_family = AF_INET;
+            router.sin_port = htons(port);
+            router.sin_addr = router_list_ptr->router_sendto_address;
+
+            if (connect(theWccp2Connection, (struct sockaddr *) &router, router_len))
+                fatal("Unable to connect WCCP out socket");
+
+            local_len = sizeof(local);
+
+            memset(&local, '\0', local_len);
+
+            if (getsockname(theWccp2Connection, (struct sockaddr *) &local, &local_len))
+                fatal("Unable to getsockname on WCCP out socket");
+
+            router_list_ptr->local_ip = local.sin_addr;
+
+            /* Disconnect the sending socket */
+            router.sin_family = AF_UNSPEC;
+
+            if (connect(theWccp2Connection, (struct sockaddr *) &router, router_len))
+                fatal("Unable to disconnect WCCP out socket");
+        }
+
+        service_list_ptr = service_list_ptr->next;
+    }
+
+    if (wccp2_numrouters == 1) {
+        router.sin_family = AF_INET;
+
+        connect(theWccp2Connection, (struct sockaddr *) &router, sizeof(router));
+    }
+
+    wccp2_connected = 1;
+}
+
+void
+wccp2ConnectionClose(void)
+{
+
+    struct wccp2_service_list_t *service_list_ptr;
+
+    struct wccp2_service_list_t *service_list_ptr_next;
+
+    struct wccp2_router_list_t *router_list_ptr;
+
+    struct wccp2_router_list_t *router_list_next;
+
+    struct wccp2_cache_list_t *cache_list_ptr;
+
+    struct wccp2_cache_list_t *cache_list_ptr_next;
+
+    if (wccp2_connected == 0) {
+        return;
+    }
+
+    if (theWccp2Connection > -1) {
+        debug(80, 1) ("FD %d Closing WCCP socket\n", theWccp2Connection);
+        comm_close(theWccp2Connection);
+        theWccp2Connection = -1;
+    }
+
+    /* for each router on each service send a packet */
+    service_list_ptr = wccp2_service_list_head;
+
+    while (service_list_ptr != NULL) {
+        for (router_list_ptr = &service_list_ptr->router_list_head; router_list_ptr != NULL; router_list_ptr = router_list_next) {
+            for (cache_list_ptr = &router_list_ptr->cache_list_head; cache_list_ptr; cache_list_ptr = cache_list_ptr_next) {
+                cache_list_ptr_next = cache_list_ptr->next;
+
+                if (cache_list_ptr != &router_list_ptr->cache_list_head) {
+                    xfree(cache_list_ptr);
+                } else {
+
+                    memset(cache_list_ptr, '\0', sizeof(struct wccp2_cache_list_t));
+                }
+            }
+
+            router_list_next = router_list_ptr->next;
+
+            if (router_list_ptr != &service_list_ptr->router_list_head) {
+                xfree(router_list_ptr);
+            } else {
+
+                memset(router_list_ptr, '\0', sizeof(struct wccp2_router_list_t));
+            }
+        }
+
+        service_list_ptr_next = service_list_ptr->next;
+        xfree(service_list_ptr);
+        service_list_ptr = service_list_ptr_next;
+    }
+
+    wccp2_service_list_head = NULL;
+    eventDelete(wccp2HereIam, NULL);
+    eventDelete(wccp2AssignBuckets, NULL);
+    eventDelete(wccp2HereIam, NULL);
+    wccp2_connected = 0;
+}
+
+/*
+ * Functions for handling the requests.
+ */
+
+/*
+ * Accept the UDP packet
+ */
+static void
+wccp2HandleUdp(int sock, void *not_used)
+{
+
+    struct wccp2_service_list_t *service_list_ptr;
+
+    struct wccp2_router_list_t *router_list_ptr;
+
+    struct wccp2_cache_list_t *cache_list_ptr;
+
+    struct wccp2_cache_list_t *cache_list_ptr_next;
+
+    /* These structs form the parts of the packet */
+
+    struct wccp2_item_header_t *header = NULL;
+
+    struct wccp2_security_none_t *security_info = NULL;
+
+    struct wccp2_service_info_t *service_info = NULL;
+
+    struct router_identity_info_t *router_identity_info = NULL;
+
+    struct router_view_t *router_view_header = NULL;
+
+    struct wccp2_cache_identity_info_t *cache_identity = NULL;
+
+    struct wccp2_capability_info_header_t *router_capability_header = NULL;
+
+    struct wccp2_capability_element_t *router_capability_element;
+
+    struct sockaddr_in from;
+
+    struct IN_ADDR cache_address;
+    socklen_t from_len;
+    int len, found;
+    short int data_length, offset;
+    uint32_t tmp;
+    char *ptr;
+    int num_caches;
+    uint16_t num_capabilities;
+
+    debug(80, 6) ("wccp2HandleUdp: Called.\n");
+
+    commSetSelect(sock, COMM_SELECT_READ, wccp2HandleUdp, NULL, 0);
+
+    from_len = sizeof(struct sockaddr_in);
+    memset(&from, '\0', from_len);
+
+    len = comm_udp_recvfrom(sock,
+                            &wccp2_i_see_you,
+                            WCCP_RESPONSE_SIZE,
+                            0,
+
+                            (struct sockaddr *) &from,
+                            &from_len);
+
+    if (len < 0)
+        return;
+
+    if (ntohs(wccp2_i_see_you.version) != WCCP2_VERSION)
+        return;
+
+    if (ntohl(wccp2_i_see_you.type) != WCCP2_I_SEE_YOU)
+        return;
+
+    debug(80, 3) ("Incoming WCCPv2 I_SEE_YOU length %d.\n", ntohs(wccp2_i_see_you.length));
+
+    /* Record the total data length */
+    data_length = ntohs(wccp2_i_see_you.length);
+
+    offset = 0;
+
+    if (data_length > len) {
+        debug(80, 1) ("ERROR: Malformed WCCPv2 packet claiming it's bigger than received data\n");
+        return;
+    }
+
+    /* Go through the data structure */
+    while (data_length > offset) {
+
+        header = (struct wccp2_item_header_t *) &wccp2_i_see_you.data[offset];
+
+        switch (ntohs(header->type)) {
+
+        case WCCP2_SECURITY_INFO:
+
+            if (security_info != NULL) {
+                debug(80, 1) ("Duplicate security definition\n");
+                return;
+            }
+
+            security_info = (struct wccp2_security_none_t *) &wccp2_i_see_you.data[offset];
+            break;
+
+        case WCCP2_SERVICE_INFO:
+
+            if (service_info != NULL) {
+                debug(80, 1) ("Duplicate service_info definition\n");
+                return;
+            }
+
+            service_info = (struct wccp2_service_info_t *) &wccp2_i_see_you.data[offset];
+            break;
+
+        case WCCP2_ROUTER_ID_INFO:
+
+            if (router_identity_info != NULL) {
+                debug(80, 1) ("Duplicate router_identity_info definition\n");
+                return;
+            }
+
+            router_identity_info = (struct router_identity_info_t *) &wccp2_i_see_you.data[offset];
+            break;
+
+        case WCCP2_RTR_VIEW_INFO:
+
+            if (router_view_header != NULL) {
+                debug(80, 1) ("Duplicate router_view definition\n");
+                return;
+            }
+
+            router_view_header = (struct router_view_t *) &wccp2_i_see_you.data[offset];
+            break;
+
+        case WCCP2_CAPABILITY_INFO:
+
+            if (router_capability_header != NULL) {
+                debug(80, 1) ("Duplicate router_capability definition\n");
+                return;
+            }
+
+            router_capability_header = (struct wccp2_capability_info_header_t *) &wccp2_i_see_you.data[offset];
+            return;
+
+        default:
+            debug(80, 1) ("Unknown record type in WCCPv2 Packet (%d).\n",
+                          ntohs(header->type));
+        }
+
+        offset += sizeof(struct wccp2_item_header_t);
+        offset += ntohs(header->length);
+
+        if (offset > data_length) {
+            debug(80, 1) ("Error: WCCPv2 packet tried to tell us there is data beyond the end of the packet\n");
+            return;
+        }
+    }
+
+    if ((security_info == NULL) || (service_info == NULL) || (router_identity_info == NULL) || (router_view_header == NULL)) {
+        debug(80, 1) ("Incomplete WCCPv2 Packet\n");
+        return;
+    }
+
+    debug(80, 5) ("Complete packet received\n");
+
+    /* Check that the service in the packet is configured on this router */
+    service_list_ptr = wccp2_service_list_head;
+
+    while (service_list_ptr != NULL) {
+        if (service_info->service_id == service_list_ptr->service_info->service_id) {
+            break;
+        }
+
+        service_list_ptr = service_list_ptr->next;
+    }
+
+    if (service_list_ptr == NULL) {
+        debug(80, 1) ("WCCPv2 Unknown service received from router (%d)\n", service_info->service_id);
+        return;
+    }
+
+    if (ntohl(security_info->security_option) != ntohl(service_list_ptr->security_info->security_option)) {
+        debug(80, 1) ("Invalid security option in WCCPv2 Packet (%d vs %d).\n",
+                      ntohl(security_info->security_option),
+                      ntohl(service_list_ptr->security_info->security_option));
+        return;
+    }
+
+    if (!wccp2_check_security(service_list_ptr, (char *) security_info, (char *) &wccp2_i_see_you, len)) {
+        debug(80, 1) ("Received WCCPv2 Packet failed authentication\n");
+        return;
+    }
+
+    /* Check that the router address is configured on this router */
+    for (router_list_ptr = &service_list_ptr->router_list_head; router_list_ptr->next != NULL; router_list_ptr = router_list_ptr->next) {
+        if (router_list_ptr->router_sendto_address.s_addr == from.sin_addr.s_addr)
+            break;
+    }
+
+    if (router_list_ptr->next == NULL) {
+        debug(80, 1) ("WCCPv2 Packet received from unknown router\n");
+        return;
+    }
+
+    /* Set the router id */
+    router_list_ptr->info->router_address = router_identity_info->router_id_element.router_address;
+
+    /* Increment the received id in the packet */
+    if (ntohl(router_list_ptr->info->received_id) != ntohl(router_identity_info->router_id_element.received_id)) {
+        debug(80, 3) ("Incoming WCCP2_I_SEE_YOU member change = %d tmp=%d.\n",
+                      ntohl(router_list_ptr->info->received_id), ntohl(router_identity_info->router_id_element.received_id));
+        router_list_ptr->info->received_id = router_identity_info->router_id_element.received_id;
+    }
+
+    /* TODO: check return/forwarding methods */
+    if (router_capability_header == NULL) {
+        if ((Config.Wccp2.return_method != WCCP2_CAPABILITY_GRE) || (Config.Wccp2.forwarding_method != WCCP2_CAPABILITY_GRE)) {
+            debug(80, 1) ("wccp2HandleUdp: fatal error - A WCCP router does not support the forwarding method specified\n");
+            wccp2ConnectionClose();
+            return;
+        }
+    } else {
+        num_capabilities = ntohs(router_capability_header->capability_info_length);
+        /* run through each capability element from last to first */
+
+        if (num_capabilities > 0) {
+            num_capabilities--;
+
+            router_capability_element = (struct wccp2_capability_element_t *) (router_capability_header) + sizeof(struct wccp2_capability_info_header_t) + (num_capabilities * sizeof(struct wccp2_capability_element_t));
+
+            switch (ntohs(router_capability_element->capability_type)) {
+
+            case WCCP2_CAPABILITY_FORWARDING_METHOD:
+
+                if (ntohl(router_capability_element->capability_value) != (unsigned int) Config.Wccp2.forwarding_method) {
+                    debug(80, 1) ("wccp2HandleUdp: fatal error - A WCCP router has specified a different forwarding method\n");
+                    wccp2ConnectionClose();
+                    return;
+                }
+
+                break;
+
+            case WCCP2_CAPABILITY_ASSIGNMENT_METHOD:
+                /* we don't current care */
+                break;
+
+            case WCCP2_CAPABILITY_RETURN_METHOD:
+
+                if (ntohl(router_capability_element->capability_value) != (unsigned int) Config.Wccp2.return_method) {
+                    debug(80, 1) ("wccp2HandleUdp: fatal error - A WCCP router has specified a different return method\n");
+                    wccp2ConnectionClose();
+                    return;
+                }
+
+                break;
+
+            default:
+                debug(80, 1) ("Unknown capability type in WCCPv2 Packet (%d).\n",
+                              ntohs(router_capability_element->capability_type));
+            }
+        }
+    }
+
+    debug(80, 5) ("Cleaning out cache list\n");
+    /* clean out the old cache list */
+
+    for (cache_list_ptr = &router_list_ptr->cache_list_head; cache_list_ptr; cache_list_ptr = cache_list_ptr_next) {
+        cache_list_ptr_next = cache_list_ptr->next;
+
+        if (cache_list_ptr != &router_list_ptr->cache_list_head) {
+            xfree(cache_list_ptr);
+        }
+    }
+
+    router_list_ptr->num_caches = htonl(0);
+    num_caches = 0;
+
+    /* Check to see if we're the master cache and update the cache list */
+    found = 0;
+    service_list_ptr->lowest_ip = 1;
+    cache_list_ptr = &router_list_ptr->cache_list_head;
+
+    /* to find the list of caches, we start at the end of the router view header */
+
+    ptr = (char *) (router_view_header) + sizeof(struct router_view_t);
+
+    /* Then we read the number of routers */
+    memcpy(&tmp, ptr, sizeof(tmp));
+
+    /* skip the number plus all the ip's */
+
+    ptr += sizeof(tmp) + (ntohl(tmp) * sizeof(struct IN_ADDR));
+
+    /* Then read the number of caches */
+    memcpy(&tmp, ptr, sizeof(tmp));
+    ptr += sizeof(tmp);
+
+    if (ntohl(tmp) != 0) {
+        /* search through the list of received-from ip addresses */
+
+        for (num_caches = 0; num_caches < (int) ntohl(tmp); num_caches++) {
+            /* Get a copy of the ip */
+
+            cache_identity = (struct wccp2_cache_identity_info_t *) ptr;
+
+            ptr += sizeof(struct wccp2_cache_identity_info_t);
+
+            memcpy(&cache_address, &cache_identity->addr, sizeof(struct IN_ADDR));
+
+            /* Update the cache list */
+            cache_list_ptr->cache_ip = cache_address;
+
+            cache_list_ptr->next = (wccp2_cache_list_t*) xcalloc(1, sizeof(struct wccp2_cache_list_t));
+            cache_list_ptr = cache_list_ptr->next;
+            cache_list_ptr->next = NULL;
+
+            debug(80, 5) ("checking cache list: (%x:%x)\n", cache_address.s_addr, router_list_ptr->local_ip.s_addr);
+            /* Check to see if it's the master, or us */
+
+            if (cache_address.s_addr == router_list_ptr->local_ip.s_addr) {
+                found = 1;
+            }
+
+            if (cache_address.s_addr < router_list_ptr->local_ip.s_addr) {
+                service_list_ptr->lowest_ip = 0;
+            }
+        }
+    } else {
+
+        /* Update the cache list */
+        cache_list_ptr->cache_ip = router_list_ptr->local_ip;
+
+        cache_list_ptr->next = (wccp2_cache_list_t*) xcalloc(1, sizeof(struct wccp2_cache_list_t));
+        cache_list_ptr = cache_list_ptr->next;
+        cache_list_ptr->next = NULL;
+
+        service_list_ptr->lowest_ip = 0;
+        found = 1;
+        num_caches = 1;
+    }
+
+    router_list_ptr->num_caches = htonl(num_caches);
+
+    if ((found == 1) && (service_list_ptr->lowest_ip == 1)) {
+        if (ntohl(router_view_header->change_number) != router_list_ptr->member_change) {
+            debug(80, 4) ("Change detected - queueing up new assignment\n");
+            router_list_ptr->member_change = ntohl(router_view_header->change_number);
+
+            if (!eventFind(wccp2AssignBuckets, NULL)) {
+                eventAdd("wccp2AssignBuckets", wccp2AssignBuckets, NULL, 15.0, 1);
+            }
+        }
+    } else {
+        debug(80, 5) ("I am not the lowest ip cache - not assigning buckets\n");
+    }
+}
+
+static void
+wccp2HereIam(void *voidnotused)
+{
+
+    struct wccp2_service_list_t *service_list_ptr;
+
+    struct wccp2_router_list_t *router_list_ptr;
+
+    struct sockaddr_in router;
+    int router_len;
+    u_short port = WCCP_PORT;
+
+    debug(80, 6) ("wccp2HereIam: Called\n");
+
+    if (wccp2_connected == 0) {
+        debug(80, 1) ("wccp2HereIam: wccp2 socket closed.  Shutting down WCCP2\n");
+        return;
+    }
+
+    router_len = sizeof(router);
+    memset(&router, '\0', router_len);
+    router.sin_family = AF_INET;
+    router.sin_port = htons(port);
+
+    /* for each router on each service send a packet */
+    service_list_ptr = wccp2_service_list_head;
+
+    while (service_list_ptr != NULL) {
+        debug(80, 5) ("wccp2HereIam: sending to service id %d\n", service_list_ptr->info.service_id);
+
+        for (router_list_ptr = &service_list_ptr->router_list_head; router_list_ptr->next != NULL; router_list_ptr = router_list_ptr->next) {
+            router.sin_addr = router_list_ptr->router_sendto_address;
+
+            /* Set the cache id (ip) */
+            service_list_ptr->wccp2_identity_info_ptr->cache_identity.addr = router_list_ptr->local_ip;
+            /* 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);
+            }
+
+            debug(80, 3) ("Sending HereIam packet size %d\n", (int) service_list_ptr->wccp_packet_size);
+            /* Send the packet */
+            sendto(theWccp2Connection,
+                   &service_list_ptr->wccp_packet,
+                   service_list_ptr->wccp_packet_size,
+                   0,
+
+                   (struct sockaddr *) &router,
+                   router_len);
+        }
+
+        service_list_ptr = service_list_ptr->next;
+    }
+
+    if (!eventFind(wccp2HereIam, NULL))
+        eventAdd("wccp2HereIam", wccp2HereIam, NULL, 10.0, 1);
+}
+
+static void
+wccp2AssignBuckets(void *voidnotused)
+{
+
+    struct wccp2_service_list_t *service_list_ptr;
+
+    struct wccp2_router_list_t *router_list_ptr;
+
+    struct wccp2_cache_list_t *cache_list_ptr;
+    char wccp_packet[WCCP_RESPONSE_SIZE];
+    short int offset, saved_offset;
+
+    struct sockaddr_in router;
+    int router_len;
+    int bucket_counter;
+    u_short port = WCCP_PORT;
+
+    /* Packet segments */
+
+    struct wccp2_here_i_am_header_t *main_header;
+
+    struct wccp2_security_md5_t *security = NULL;
+    /* service from service struct */
+
+    struct wccp2_item_header_t *assignment_header;
+
+    struct assignment_key_t *assignment_key;
+    /* number of routers */
+
+    struct wccp2_router_assign_element_t *router_assign;
+    /* number of caches */
+
+    struct IN_ADDR *cache_address;
+    char *buckets;
+
+    router_len = sizeof(router);
+    memset(&router, '\0', router_len);
+    router.sin_family = AF_INET;
+    router.sin_port = htons(port);
+
+    /* Start main header - fill in length later */
+    offset = 0;
+
+    main_header = (struct wccp2_here_i_am_header_t *) &wccp_packet[offset];
+    main_header->type = htonl(WCCP2_REDIRECT_ASSIGN);
+    main_header->version = htons(WCCP2_VERSION);
+
+    debug(80, 2) ("Running wccp2AssignBuckets\n");
+    service_list_ptr = wccp2_service_list_head;
+
+    while (service_list_ptr != NULL) {
+        /* If we're not the lowest, we don't need to worry */
+
+        if (service_list_ptr->lowest_ip == 0) {
+            /* XXX eww */
+            service_list_ptr = service_list_ptr->next;
+            continue;
+        }
+
+        /* reset the offset */
+
+        offset = sizeof(struct wccp2_here_i_am_header_t);
+
+        /* build packet header from hereIam packet */
+        /* Security info */
+        /* XXX this should be made more generic! */
+        /* XXX and I hate magic numbers! */
+        switch (service_list_ptr->wccp2_security_type) {
+
+        case WCCP2_NO_SECURITY:
+
+            security = (struct wccp2_security_md5_t *) &wccp_packet[offset];
+            memcpy(security, service_list_ptr->security_info, 8);
+            offset += 8;
+            break;
+
+        case WCCP2_MD5_SECURITY:
+
+            security = (struct wccp2_security_md5_t *) &wccp_packet[offset];
+
+            memcpy(security, service_list_ptr->security_info, sizeof(struct wccp2_security_md5_t));
+
+            offset += sizeof(struct wccp2_security_md5_t);
+            break;
+
+        default:
+            fatalf("Unknown Wccp2 security type\n");
+        }
+
+        /* Service info */
+
+        memcpy(&wccp_packet[offset], service_list_ptr->service_info, sizeof(struct wccp2_service_info_t));
+
+        offset += sizeof(struct wccp2_service_info_t);
+
+        /* assignment header - fill in length later */
+
+        assignment_header = (struct wccp2_item_header_t *) &wccp_packet[offset];
+
+        assignment_header->type = htons(WCCP2_REDIRECT_ASSIGNMENT);
+
+        offset += sizeof(struct wccp2_item_header_t);
+
+        /* Assignment key - fill in master ip later */
+
+        assignment_key = (struct assignment_key_t *) &wccp_packet[offset];
+
+        assignment_key->master_number = htonl(++service_list_ptr->change_num);
+
+        offset += sizeof(struct assignment_key_t);
+
+        /* Number of routers */
+        xmemcpy(&wccp_packet[offset], &service_list_ptr->num_routers, sizeof(service_list_ptr->num_routers));
+
+        offset += sizeof(service_list_ptr->num_routers);
+
+        for (router_list_ptr = &service_list_ptr->router_list_head; router_list_ptr->next != NULL; router_list_ptr = router_list_ptr->next) {
+
+            /* Add routers */
+
+            router_assign = (struct wccp2_router_assign_element_t *) &wccp_packet[offset];
+            router_assign->router_address = router_list_ptr->info->router_address;
+            router_assign->received_id = router_list_ptr->info->received_id;
+            router_assign->change_number = htonl(router_list_ptr->member_change);
+
+            offset += sizeof(struct wccp2_router_assign_element_t);
+        }
+
+        saved_offset = offset;
+
+        for (router_list_ptr = &service_list_ptr->router_list_head; router_list_ptr->next != NULL; router_list_ptr = router_list_ptr->next) {
+            offset = saved_offset;
+
+            /* Number of caches */
+            xmemcpy(&wccp_packet[offset], &router_list_ptr->num_caches, sizeof(router_list_ptr->num_caches));
+            offset += sizeof(router_list_ptr->num_caches);
+
+            if (ntohl(router_list_ptr->num_caches)) {
+                for (cache_list_ptr = &router_list_ptr->cache_list_head; cache_list_ptr->next; cache_list_ptr = cache_list_ptr->next) {
+                    /* add caches */
+
+                    cache_address = (struct IN_ADDR *) &wccp_packet[offset];
+
+                    xmemcpy(cache_address, &cache_list_ptr->cache_ip, sizeof(struct IN_ADDR));
+
+                    offset += sizeof(struct IN_ADDR);
+                }
+            }
+
+            /* Add buckets */
+            buckets = (char *) &wccp_packet[offset];
+
+            memset(buckets, '\0', WCCP_BUCKETS);
+
+            if (ntohl(router_list_ptr->num_caches) != 0) {
+                for (bucket_counter = 0; bucket_counter < WCCP_BUCKETS; bucket_counter++) {
+                    buckets[bucket_counter] = (char) (bucket_counter % ntohl(router_list_ptr->num_caches));
+                }
+            }
+
+            offset += (WCCP_BUCKETS * sizeof(char));
+
+            /* Fill in length */
+
+            assignment_header->length = htons(sizeof(struct assignment_key_t) + sizeof(service_list_ptr->num_routers) +
+
+                                              (ntohl(service_list_ptr->num_routers) * sizeof(struct wccp2_router_assign_element_t)) +
+
+                                              sizeof                        (router_list_ptr->num_caches) + (ntohl(router_list_ptr->num_caches) * sizeof(struct IN_ADDR)) +
+                                              (WCCP_BUCKETS * sizeof(char)));
+
+            /* Fill in assignment key */
+            assignment_key->master_ip = router_list_ptr->local_ip;
+
+            /* finish length */
+
+            main_header->length = htons(offset - sizeof(struct wccp2_here_i_am_header_t));
+
+            /* set the destination address */
+            router.sin_addr = router_list_ptr->router_sendto_address;
+
+            /* Security update, if needed */
+
+            if (service_list_ptr->wccp2_security_type == WCCP2_MD5_SECURITY) {
+                wccp2_update_md5_security(service_list_ptr->wccp_password, (char *) security, wccp_packet, offset);
+            }
+
+            if (ntohl(router_list_ptr->num_caches)) {
+                /* send packet */
+                comm_udp_sendto(theWccp2Connection,
+                                &router,
+                                router_len,
+                                &wccp_packet,
+                                offset);
+            }
+        }
+
+        service_list_ptr = service_list_ptr->next;
+    }
+}
+
+
+/*
+ * Configuration option parsing code
+ */
+
+/*
+ * Format:
+ *
+ * wccp2_service {standard|dynamic} {id} (password=password)
+ */
+void
+parse_wccp2_service(void *v)
+{
+    char *t;
+    int service = 0;
+    int service_id = 0;
+    int security_type = WCCP2_NO_SECURITY;
+    char wccp_password[WCCP2_PASSWORD_LEN + 1];
+
+    if (wccp2_connected == 1) {
+        debug(80, 1) ("WCCPv2: Somehow reparsing the configuration "
+                      "without having shut down WCCP! Try reloading squid again.\n");
+        return;
+    }
+
+    /* Snarf the type */
+    if ((t = strtok(NULL, w_space)) == NULL) {
+        debug(80, 0) ("wccp2ParseServiceInfo: missing service info type (standard|dynamic)\n");
+        self_destruct();
+    }
+
+    if (strcmp(t, "standard") == 0) {
+        service = WCCP2_SERVICE_STANDARD;
+    } else if (strcmp(t, "dynamic") == 0) {
+        service = WCCP2_SERVICE_DYNAMIC;
+    } else {
+        debug(80, 0) ("wccp2ParseServiceInfo: bad service info type (expected standard|dynamic, got %s)\n", t);
+        self_destruct();
+    }
+
+    /* Snarf the ID */
+    service_id = GetInteger();
+
+    if (service_id < 0 || service_id > 255) {
+        debug(80, 0) ("wccp2ParseServiceInfo: service info id %d is out of range (0..255)\n", service_id);
+        self_destruct();
+    }
+
+    memset(wccp_password, 0, sizeof(wccp_password));
+    /* Handle password, if any */
+
+    if ((t = strtok(NULL, w_space)) != NULL) {
+        if (strncmp(t, "password=", 9) == 0) {
+            security_type = WCCP2_MD5_SECURITY;
+            strncpy(wccp_password, t + 9, WCCP2_PASSWORD_LEN);
+        }
+    }
+
+    /* Create a placeholder service record */
+    wccp2_add_service_list(service, service_id, 0, 0, 0, empty_portlist, security_type, wccp_password);
+}
+
+void
+dump_wccp2_service(StoreEntry * e, const char *label, void *v)
+{
+
+    struct wccp2_service_list_t *srv;
+    srv = wccp2_service_list_head;
+
+    while (srv != NULL) {
+        debug(80, 3) ("dump_wccp2_service: id %d, type %d\n", srv->info.service_id, srv->info.service);
+        storeAppendPrintf(e, "%s %s %d", label,
+                          (srv->info.service == WCCP2_SERVICE_DYNAMIC) ? "dynamic" : "standard",
+                          srv->info.service_id);
+
+        if (srv->wccp2_security_type == WCCP2_MD5_SECURITY) {
+            storeAppendPrintf(e, " %s", srv->wccp_password);
+        }
+
+        srv = srv->next;
+    }
+}
+
+void
+free_wccp2_service(void *v)
+{}
+
+int
+check_null_wccp2_service(void *v)
+{
+    return !wccp2_service_list_head;
+}
+
+/*
+ * Format:
+ *
+ * wccp2_service_info {id} stuff..
+ *
+ * Where stuff is:
+ *
+ * + flags=flag,flag,flag..
+ * + proto=protocol (tcp|udp)
+ * + ports=port,port,port (up to a max of 8)
+ * + priority=priority (0->255)
+ *
+ * The flags here are:
+ * src_ip_hash, dst_ip_hash, source_port_hash, dst_port_hash, ports_defined,
+ * ports_source, src_ip_alt_hash, dst_ip_alt_hash, src_port_alt_hash, dst_port_alt_hash
+ */
+static int
+parse_wccp2_service_flags(char *flags)
+{
+    char *tmp, *tmp2;
+    char *flag;
+    int retflag = 0;
+
+    if (!flags) {
+        return 0;
+    }
+
+    tmp = xstrdup(flags);
+    tmp2 = tmp;
+
+    flag = strsep(&tmp2, ",");
+
+    while (flag) {
+        if (strcmp(flag, "src_ip_hash") == 0) {
+            retflag |= WCCP2_SERVICE_SRC_IP_HASH;
+        } 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;
+        } else if (strcmp(flag, "dst_port_hash") == 0) {
+            retflag |= WCCP2_SERVICE_DST_PORT_HASH;
+        } else if (strcmp(flag, "ports_source") == 0) {
+            retflag |= WCCP2_SERVICE_PORTS_SOURCE;
+        } else if (strcmp(flag, "src_ip_alt_hash") == 0) {
+            retflag |= WCCP2_SERVICE_SRC_IP_ALT_HASH;
+        } else if (strcmp(flag, "dst_ip_alt_hash") == 0) {
+            retflag |= WCCP2_SERVICE_DST_IP_ALT_HASH;
+        } else if (strcmp(flag, "src_port_alt_hash") == 0) {
+            retflag |= WCCP2_SERVICE_SRC_PORT_ALT_HASH;
+        } else if (strcmp(flag, "dst_port_alt_hash") == 0) {
+            retflag |= WCCP2_SERVICE_DST_PORT_ALT_HASH;
+        } else {
+            fatalf("Unknown wccp2 service flag: %s\n", flag);
+        }
+
+        flag = strsep(&tmp2, ",");
+    }
+
+    xfree(tmp);
+    return retflag;
+}
+
+static void
+parse_wccp2_service_ports(char *options, int portlist[])
+{
+    int i = 0;
+    int p;
+    char *tmp, *tmp2, *port, *end;
+
+    if (!options) {
+        return;
+    }
+
+    tmp = xstrdup(options);
+    tmp2 = tmp;
+
+    port = strsep(&tmp2, ",");
+
+    while (port && i < WCCP2_NUMPORTS) {
+        p = strtol(port, &end, 0);
+
+        if (p < 1 || p > 65535) {
+            fatalf("parse_wccp2_service_ports: port value '%s' isn't valid (1..65535)\n", port);
+        }
+
+        portlist[i] = p;
+        i++;
+        port = strsep(&tmp2, ",");
+    }
+
+    if (i == 8) {
+        fatalf("parse_wccp2_service_ports: too many ports (maximum: 8) in list '%s'\n", options);
+    }
+
+    xfree(tmp);
+}
+
+void
+parse_wccp2_service_info(void *v)
+{
+    char *t, *end;
+    int service_id = 0;
+    int flags = 0;
+    int portlist[WCCP2_NUMPORTS];
+    int protocol = -1;         /* IPPROTO_TCP | IPPROTO_UDP */
+
+    struct wccp2_service_list_t *srv;
+    int priority = -1;
+
+    if (wccp2_connected == 1) {
+        debug(80, 1) ("WCCPv2: Somehow reparsing the configuration "
+                      "without having shut down WCCP! Try reloading squid again.\n");
+        return;
+    }
+
+    debug(80, 5) ("parse_wccp2_service_info: called\n");
+    bzero(portlist, sizeof(portlist));
+    /* First argument: id */
+    service_id = GetInteger();
+
+    if (service_id < 0 || service_id > 255) {
+        debug(80, 1) ("parse_wccp2_service_info: invalid service id %d (must be between 0 .. 255)\n", service_id);
+        self_destruct();
+    }
+
+    /* Next: find the (hopefully!) existing service */
+    srv = wccp2_get_service_by_id(WCCP2_SERVICE_DYNAMIC, service_id);
+
+    if (srv == NULL) {
+        fatalf("parse_wccp2_service_info: unknown dynamic service id %d: you need to define it using wccp2_service (and make sure you wish to configure it as a dynamic service.)\n", service_id);
+    }
+
+    /* Next: loop until we don't have any more tokens */
+    while ((t = strtok(NULL, w_space)) != NULL) {
+        if (strncmp(t, "flags=", 6) == 0) {
+            /* XXX eww, string pointer math */
+            flags = parse_wccp2_service_flags(t + 6);
+        } else if (strncmp(t, "ports=", 6) == 0) {
+            parse_wccp2_service_ports(t + 6, portlist);
+            flags |= WCCP2_SERVICE_PORTS_DEFINED;
+        } else if (strncmp(t, "protocol=tcp", 12) == 0) {
+            protocol = IPPROTO_TCP;
+        } else if (strncmp(t, "protocol=udp", 12) == 0) {
+            protocol = IPPROTO_UDP;
+        } else if (strncmp(t, "protocol=", 9) == 0) {
+            fatalf("parse_wccp2_service_info: id %d: unknown protocol (%s) - must be tcp or udp!\n", service_id, t);
+        } else if (strncmp(t, "priority=", 9) == 0) {
+            priority = strtol(t + 9, &end, 0);
+
+            if (priority < 0 || priority > 255) {
+                fatalf("parse_wccp2_service_info: id %d: %s out of range (0..255)!\n", service_id, t);
+            }
+        } else {
+            fatalf("parse_wccp2_service_info: id %d: unknown option '%s'\n", service_id, t);
+        }
+    }
+
+    /* Check everything is set */
+    if (priority == -1) {
+        fatalf("parse_wccp2_service_info: service %d: no priority defined (valid: 0..255)!\n", service_id);
+    }
+
+    if (protocol == -1) {
+        fatalf("parse_wccp2_service_info: service %d: no protocol defined (valid: tcp or udp)!\n", service_id);
+    }
+
+    if (!(flags & WCCP2_SERVICE_PORTS_DEFINED)) {
+        fatalf("parse_wccp2_service_info: service %d: no ports defined!\n", service_id);
+    }
+
+    /* rightio! now we can update */
+    wccp2_update_service(srv, WCCP2_SERVICE_DYNAMIC, service_id, priority,
+                         protocol, flags, portlist);
+
+    /* Done! */
+}
+
+void
+dump_wccp2_service_info(StoreEntry * e, const char *label, void *v)
+{
+    char comma;
+
+    struct wccp2_service_list_t *srv;
+    int flags;
+    srv = wccp2_service_list_head;
+
+    while (srv != NULL) {
+        debug(80, 3) ("dump_wccp2_service_info: id %d (type %d)\n", srv->info.service_id, srv->info.service);
+
+        /* We don't need to spit out information for standard services */
+
+        if (srv->info.service == WCCP2_SERVICE_STANDARD) {
+            debug(80, 3) ("dump_wccp2_service_info: id %d: standard service, not dumping info\n", srv->info.service_id);
+            /* XXX eww */
+            srv = srv->next;
+            continue;
+        }
+
+        storeAppendPrintf(e, "%s %d", label, srv->info.service_id);
+
+        /* priority */
+        storeAppendPrintf(e, " priority=%d", srv->info.service_priority);
+
+        /* flags */
+        flags = ntohl(srv->info.service_flags);
+
+        if (flags != 0) {
+            comma = 0;
+            storeAppendPrintf(e, " flags=");
+
+            if (flags & WCCP2_SERVICE_SRC_IP_HASH) {
+                storeAppendPrintf(e, "%ssrc_ip_hash", comma ? "," : "");
+                comma = 1;
+            }
+
+            if (flags & WCCP2_SERVICE_DST_IP_HASH) {
+                storeAppendPrintf(e, "%sdst_ip_hash", comma ? "," : "");
+                comma = 1;
+            }
+
+            if (flags & WCCP2_SERVICE_SOURCE_PORT_HASH) {
+                storeAppendPrintf(e, "%ssource_port_hash", comma ? "," : "");
+                comma = 1;
+            }
+
+            if (flags & WCCP2_SERVICE_DST_PORT_HASH) {
+                storeAppendPrintf(e, "%sdst_port_hash", comma ? "," : "");
+                comma = 1;
+            }
+
+            if (flags & WCCP2_SERVICE_PORTS_DEFINED) {
+                storeAppendPrintf(e, "%sports_defined", comma ? "," : "");
+                comma = 1;
+            }
+
+            if (flags & WCCP2_SERVICE_PORTS_SOURCE) {
+                storeAppendPrintf(e, "%sports_source", comma ? "," : "");
+                comma = 1;
+            }
+
+            if (flags & WCCP2_SERVICE_SRC_IP_ALT_HASH) {
+                storeAppendPrintf(e, "%ssrc_ip_alt_hash", comma ? "," : "");
+                comma = 1;
+            }
+
+            if (flags & WCCP2_SERVICE_DST_IP_ALT_HASH) {
+                storeAppendPrintf(e, "%ssrc_ip_alt_hash", comma ? "," : "");
+                comma = 1;
+            }
+
+            if (flags & WCCP2_SERVICE_SRC_PORT_ALT_HASH) {
+                storeAppendPrintf(e, "%ssrc_port_alt_hash", comma ? "," : "");
+                comma = 1;
+            }
+
+            if (flags & WCCP2_SERVICE_DST_PORT_ALT_HASH) {
+                storeAppendPrintf(e, "%sdst_port_alt_hash", comma ? "," : "");
+                comma = 1;
+            }
+        }
+
+        /* ports */
+        comma = 0;
+
+        if (srv->info.port0 != 0) {
+            storeAppendPrintf(e, "%s%d", comma ? "," : " ports=", ntohs(srv->info.port0));
+            comma = 1;
+        }
+
+        if (srv->info.port1 != 0) {
+            storeAppendPrintf(e, "%s%d", comma ? "," : "ports=", ntohs(srv->info.port1));
+            comma = 1;
+        }
+
+        if (srv->info.port2 != 0) {
+            storeAppendPrintf(e, "%s%d", comma ? "," : "ports=", ntohs(srv->info.port2));
+            comma = 1;
+        }
+
+        if (srv->info.port3 != 0) {
+            storeAppendPrintf(e, "%s%d", comma ? "," : "ports=", ntohs(srv->info.port3));
+            comma = 1;
+        }
+
+        if (srv->info.port4 != 0) {
+            storeAppendPrintf(e, "%s%d", comma ? "," : "ports=", ntohs(srv->info.port4));
+            comma = 1;
+        }
+
+        if (srv->info.port5 != 0) {
+            storeAppendPrintf(e, "%s%d", comma ? "," : "ports=", ntohs(srv->info.port5));
+            comma = 1;
+        }
+
+        if (srv->info.port6 != 0) {
+            storeAppendPrintf(e, "%s%d", comma ? "," : "ports=", ntohs(srv->info.port6));
+            comma = 1;
+        }
+
+        if (srv->info.port7 != 0) {
+            storeAppendPrintf(e, "%s%d", comma ? "," : "ports=", ntohs(srv->info.port7));
+            comma = 1;
+        }
+
+        /* protocol */
+        storeAppendPrintf(e, " protocol=%s", (srv->info.service_protocol == IPPROTO_TCP) ? "tcp" : "udp");
+
+        storeAppendPrintf(e, "\n");
+
+        srv = srv->next;
+    }
+}
+
+void
+free_wccp2_service_info(void *v)
+{}
+
+#endif /* USE_WCCPv2 */