Note that pre-0.6 libpcap didn't handle HP-UX as well as 0.6 and later
[obnox/wireshark/wip.git] / packet-wccp.c
1 /* packet-wccp.c
2  * Routines for Web Cache Coordination Protocol dissection
3  * Jerry Talkington <jerryt@netapp.com>
4  *
5  * $Id: packet-wccp.c,v 1.31 2002/04/15 21:12:56 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  * 
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #ifdef HAVE_SYS_TYPES_H
31 # include <sys/types.h>
32 #endif
33
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
36 #endif
37
38 #include <string.h>
39 #include <glib.h>
40 #include <epan/packet.h>
41 #include <epan/strutil.h>
42 #include "packet-wccp.h"
43
44 #ifdef NEED_SNPRINTF_H
45 # include "snprintf.h"
46 #endif
47
48 static int proto_wccp = -1;
49 static int hf_wccp_message_type = -1;   /* the message type */
50 static int hf_wccp_version = -1;        /* protocol version */
51 static int hf_hash_revision = -1;       /* the version of the hash */
52 static int hf_change_num = -1;          /* change number */
53 static int hf_recvd_id = -1;                    
54 static int hf_cache_ip = -1;
55
56 static gint ett_wccp = -1;
57 static gint ett_cache_count = -1;
58 static gint ett_buckets = -1;
59 static gint ett_flags = -1;
60 static gint ett_cache_info = -1;
61 static gint ett_security_info = -1;
62 static gint ett_service_info = -1;
63 static gint ett_service_flags = -1;
64 static gint ett_router_identity_element = -1;
65 static gint ett_router_identity_info = -1;
66 static gint ett_wc_identity_element = -1;
67 static gint ett_wc_identity_info = -1;
68 static gint ett_router_view_info = -1;
69 static gint ett_wc_view_info = -1;
70 static gint ett_router_assignment_element = -1;
71 static gint ett_router_assignment_info = -1;
72 static gint ett_query_info = -1;
73 static gint ett_capabilities_info = -1;
74 static gint ett_capability_element = -1;
75 static gint ett_capability_forwarding_method = -1;
76 static gint ett_capability_assignment_method = -1;
77 static gint ett_capability_return_method = -1;
78 static gint ett_unknown_info = -1;
79
80 /*
81  * At
82  *
83  *      http://www.alternic.org/drafts/drafts-f-g/draft-forster-wrec-wccp-v1-00.html
84  *
85  * is a copy of the now-expired Internet-Draft for WCCP 1.0.
86  *
87  * At
88  *
89  *      http://search.ietf.org/internet-drafts/draft-wilson-wrec-wccp-v2-01.txt
90  *
91  * is an Internet-Draft for WCCP 2.0.
92  */
93
94 #define UDP_PORT_WCCP   2048
95
96 #define WCCPv1                  4
97 #define WCCPv2                  0x0200
98 #define WCCP_HERE_I_AM          7
99 #define WCCP_I_SEE_YOU          8
100 #define WCCP_ASSIGN_BUCKET      9
101 #define WCCP2_HERE_I_AM         10
102 #define WCCP2_I_SEE_YOU         11
103 #define WCCP2_REDIRECT_ASSIGN   12
104 #define WCCP2_REMOVAL_QUERY     13
105
106 static const value_string wccp_type_vals[] = {
107     { WCCP_HERE_I_AM,        "1.0 Here I am" },
108     { WCCP_I_SEE_YOU,        "1.0 I see you" },
109     { WCCP_ASSIGN_BUCKET,    "1.0 Assign bucket" },
110     { WCCP2_HERE_I_AM,       "2.0 Here I am" },
111     { WCCP2_I_SEE_YOU,       "2.0 I see you" },
112     { WCCP2_REDIRECT_ASSIGN, "2.0 Redirect assign" },
113     { WCCP2_REMOVAL_QUERY,   "2.0 Removal query" },
114     { 0,                     NULL }
115 };
116
117 static const value_string wccp_version_val[] = {
118         { WCCPv1, "1"},
119         { WCCPv2, "2"},
120         { 0, NULL}
121 };
122
123 #define HASH_INFO_SIZE  (4*(1+8+1))
124
125 #define WCCP_U_FLAG     0x80000000
126
127 #define WCCP2_SECURITY_INFO             0
128 #define WCCP2_SERVICE_INFO              1
129 #define WCCP2_ROUTER_ID_INFO            2
130 #define WCCP2_WC_ID_INFO                3
131 #define WCCP2_RTR_VIEW_INFO             4
132 #define WCCP2_WC_VIEW_INFO              5
133 #define WCCP2_REDIRECT_ASSIGNMENT       6
134 #define WCCP2_QUERY_INFO                7
135 #define WCCP2_CAPABILITIES_INFO         8
136 #define WCCP2_ALT_ASSIGNMENT            13
137 #define WCCP2_ASSIGN_MAP                14
138 #define WCCP2_COMMAND_EXTENSION         15
139
140 static const value_string info_type_vals[] = {
141         { WCCP2_SECURITY_INFO,       "Security Info" },
142         { WCCP2_SERVICE_INFO,        "Service Info" },
143         { WCCP2_ROUTER_ID_INFO,      "Router Identity Info" },
144         { WCCP2_WC_ID_INFO,          "Web-Cache Identity Info" },
145         { WCCP2_RTR_VIEW_INFO,       "Router View Info" },
146         { WCCP2_WC_VIEW_INFO,        "Web-Cache View Info" },
147         { WCCP2_REDIRECT_ASSIGNMENT, "Assignment Info" },
148         { WCCP2_QUERY_INFO,          "Query Info" },
149         { WCCP2_CAPABILITIES_INFO,   "Capabilities Info" },
150         { WCCP2_COMMAND_EXTENSION,   "Command Extension" },
151         { 0,                         NULL }
152 };
153
154 const value_string service_id_vals[] = {
155     { 0x00, "HTTP" },
156     { 0,    NULL }
157 };
158
159 typedef struct capability_flag {
160         guint32 value;
161         const char *short_name;
162         const char *long_name;
163 } capability_flag;
164
165 static void dissect_hash_data(tvbuff_t *tvb, int offset,
166     proto_tree *wccp_tree);
167 static void dissect_web_cache_list_entry(tvbuff_t *tvb, int offset,
168     int index, proto_tree *wccp_tree);
169 static int wccp_bucket_info(guint8 bucket_info, proto_tree *bucket_tree,
170     guint32 start, tvbuff_t *tvb, int offset);
171 static gchar *bucket_name(guint8 bucket);
172 static guint16 dissect_wccp2_header(tvbuff_t *tvb, int offset,
173     proto_tree *wccp_tree);
174 static void dissect_wccp2_info(tvbuff_t *tvb, int offset, guint16 length,
175     proto_tree *wccp_tree);
176 static gboolean dissect_wccp2_security_info(tvbuff_t *tvb, int offset,
177     int length, proto_tree *info_tree);
178 static gboolean dissect_wccp2_service_info(tvbuff_t *tvb, int offset,
179     int length, proto_tree *info_tree);
180 static gboolean dissect_wccp2_router_identity_info(tvbuff_t *tvb,
181     int offset, int length, proto_tree *info_tree);
182 static gboolean dissect_wccp2_wc_identity_info(tvbuff_t *tvb, int offset,
183     int length, proto_tree *info_tree);
184 static gboolean dissect_wccp2_router_view_info(tvbuff_t *tvb, int offset,
185     int length, proto_tree *info_tree);
186 static gboolean dissect_wccp2_wc_view_info(tvbuff_t *tvb, int offset,
187     int length, proto_tree *info_tree);
188 static gboolean dissect_wccp2_assignment_info(tvbuff_t *tvb, int offset,
189     int length, proto_tree *info_tree);
190 static gboolean dissect_wccp2_router_query_info(tvbuff_t *tvb, int offset,
191     int length, proto_tree *info_tree);
192 static gboolean dissect_wccp2_capability_info(tvbuff_t *tvb, int offset,
193     int length, proto_tree *info_tree);
194 static void dissect_32_bit_capability_flags(tvbuff_t *tvb, int curr_offset,
195     guint16 capability_val_len, gint ett, const capability_flag *flags,
196     proto_tree *element_tree);
197
198 static void 
199 dissect_wccp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
200 {
201         int offset = 0;
202         proto_tree *wccp_tree = NULL;
203         proto_item *wccp_tree_item;
204         guint32 wccp_message_type;
205         guint16 length;
206         guint32 cache_count;
207         guint32 ipaddr;
208         guint i;
209
210         if(check_col(pinfo->cinfo, COL_PROTOCOL)) {
211                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "WCCP");
212         }
213         if(check_col(pinfo->cinfo, COL_INFO)) {
214                 col_clear(pinfo->cinfo, COL_INFO);
215         }
216
217         wccp_message_type = tvb_get_ntohl(tvb, offset);
218
219         if(check_col(pinfo->cinfo, COL_INFO)) {
220                 col_add_str(pinfo->cinfo, COL_INFO, val_to_str(wccp_message_type,
221                     wccp_type_vals, "Unknown WCCP message (%u)"));
222         }
223
224         if(tree != NULL) {
225                 wccp_tree_item = proto_tree_add_item(tree, proto_wccp, tvb, offset,
226                     -1, FALSE);
227                 wccp_tree = proto_item_add_subtree(wccp_tree_item, ett_wccp);
228
229                 proto_tree_add_uint(wccp_tree, hf_wccp_message_type, tvb, offset,
230                     sizeof(wccp_message_type), wccp_message_type);
231                 offset += sizeof(wccp_message_type);
232
233                 switch (wccp_message_type) {
234
235                 case WCCP_HERE_I_AM:
236                         proto_tree_add_item(wccp_tree, hf_wccp_version, tvb,
237                             offset, 4, FALSE);
238                         offset += 4;
239                         dissect_hash_data(tvb, offset, wccp_tree);
240                         offset += HASH_INFO_SIZE;
241                         proto_tree_add_item(wccp_tree, hf_recvd_id, tvb, offset,
242                             4, FALSE);
243                         offset += 4;
244                         break;
245
246                 case WCCP_I_SEE_YOU:
247                         proto_tree_add_item(wccp_tree, hf_wccp_version, tvb,
248                             offset, 4, FALSE);
249                         offset += 4;
250                         proto_tree_add_item(wccp_tree, hf_change_num, tvb, offset,
251                             4, FALSE);
252                         offset += 4;
253                         proto_tree_add_item(wccp_tree, hf_recvd_id, tvb, offset,
254                             4, FALSE);
255                         offset += 4;
256                         cache_count = tvb_get_ntohl(tvb, offset);
257                         proto_tree_add_text(wccp_tree, tvb, offset, 4,
258                             "Number of Web Caches: %u", cache_count);
259                         offset += 4;
260                         for (i = 0; i < cache_count; i++) {
261                                 dissect_web_cache_list_entry(tvb, offset, i,
262                                     wccp_tree);
263                                 offset += 4 + HASH_INFO_SIZE;
264                         }
265                         break;
266
267                 case WCCP_ASSIGN_BUCKET:
268                         /*
269                          * This hasn't been tested, since I don't have any
270                          * traces with this in it.
271                          *
272                          * The V1 spec claims that this does, indeed,
273                          * have a Received ID field after the type,
274                          * rather than a Version field.
275                          */
276                         proto_tree_add_item(wccp_tree, hf_recvd_id, tvb, offset,
277                             4, FALSE);
278                         offset += 4;
279                         cache_count = tvb_get_ntohl(tvb, offset);
280                         proto_tree_add_text(wccp_tree, tvb, offset, 4,
281                             "Number of Web Caches: %u", cache_count);
282                         offset += 4;
283                         for (i = 0; i < cache_count; i++) {
284                                 tvb_memcpy(tvb, (guint8 *)&ipaddr, offset, 4);
285                                 proto_tree_add_ipv4_format(wccp_tree,
286                                     hf_cache_ip, tvb, offset, 4,
287                                     ipaddr,
288                                     "Web Cache %d IP Address: %s", i,
289                                     ip_to_str((guint8 *)&ipaddr));
290                                 offset += 4;
291                         }
292                         for (i = 0; i < 256; i += 4) {
293                                 proto_tree_add_text(wccp_tree, tvb, offset, 4,
294                                     "Buckets %d - %d: %10s %10s %10s %10s",
295                                     i, i + 3,
296                                     bucket_name(tvb_get_guint8(tvb, offset)),
297                                     bucket_name(tvb_get_guint8(tvb, offset+1)),
298                                     bucket_name(tvb_get_guint8(tvb, offset+2)),
299                                     bucket_name(tvb_get_guint8(tvb, offset+3)));
300                                 offset += 4;
301                         }
302                         break;
303
304                 case WCCP2_HERE_I_AM:
305                 case WCCP2_I_SEE_YOU:
306                 case WCCP2_REMOVAL_QUERY:
307                 case WCCP2_REDIRECT_ASSIGN:
308                 default:        /* assume unknown packets are v2 */
309                         length = dissect_wccp2_header(tvb, offset, wccp_tree);
310                         offset += 4;
311                         dissect_wccp2_info(tvb, offset, length, wccp_tree);
312                         break;
313                 }
314         }
315 }
316
317 static void
318 dissect_hash_data(tvbuff_t *tvb, int offset, proto_tree *wccp_tree)
319 {
320         proto_item *bucket_item;
321         proto_tree *bucket_tree;
322         proto_item *tf;
323         proto_tree *field_tree;
324         int i;
325         guint8 bucket_info;
326         int n;
327         guint32 flags;
328
329         proto_tree_add_item(wccp_tree, hf_hash_revision, tvb, offset, 4,
330             FALSE);
331         offset += 4;
332
333         bucket_item = proto_tree_add_text(wccp_tree, tvb, offset, 32,
334             "Hash information");
335         bucket_tree = proto_item_add_subtree(bucket_item, ett_buckets);
336
337         for (i = 0, n = 0; i < 32; i++) {
338                 bucket_info = tvb_get_guint8(tvb, offset);
339                 n = wccp_bucket_info(bucket_info, bucket_tree, n, tvb, offset);
340                 offset += 1;
341         }
342         flags = tvb_get_ntohl(tvb, offset);
343         tf = proto_tree_add_text(wccp_tree, tvb, offset, 4,
344             "Flags: 0x%08X (%s)", flags,
345             ((flags & WCCP_U_FLAG) ?
346               "Hash information is historical" :
347               "Hash information is current"));
348         field_tree = proto_item_add_subtree(tf, ett_flags);
349         proto_tree_add_text(field_tree, tvb, offset, 4, "%s",
350             decode_boolean_bitfield(flags, WCCP_U_FLAG,
351               sizeof (flags)*8,
352               "Hash information is historical",
353               "Hash information is current"));
354 }
355
356 static void
357 dissect_web_cache_list_entry(tvbuff_t *tvb, int offset, int index,
358     proto_tree *wccp_tree)
359 {
360         proto_item *tl;
361         proto_tree *list_entry_tree;
362
363         tl = proto_tree_add_text(wccp_tree, tvb, offset, 4 + HASH_INFO_SIZE,
364             "Web-Cache List Entry(%d)", index);
365         list_entry_tree = proto_item_add_subtree(tl, ett_cache_info);
366         proto_tree_add_item(list_entry_tree, hf_cache_ip, tvb, offset, 4,
367             FALSE);
368         dissect_hash_data(tvb, offset + 4, list_entry_tree);
369 }
370
371 /*
372  * wccp_bucket_info()
373  * takes an integer representing a "Hash Information" bitmap, and spits out
374  * the corresponding proto_tree entries, returning the next bucket number.
375  */
376 static int
377 wccp_bucket_info(guint8 bucket_info, proto_tree *bucket_tree, guint32 start,
378     tvbuff_t *tvb, int offset)
379 {
380         guint32 i;
381
382         for(i = 0; i < 8; i++) {
383                 proto_tree_add_text(bucket_tree, tvb, offset, sizeof(bucket_info), "Bucket %3d: %s", start, (bucket_info & 1<<i ? "Assigned" : "Not Assigned") );
384                 start++;
385         }
386         return(start);
387 }
388
389 static gchar *
390 bucket_name(guint8 bucket)
391 {
392         static gchar str[4][10+1];
393         static gchar *cur;
394
395         if (cur == &str[0][0])
396                 cur = &str[1][0];
397         else if (cur == &str[1][0])
398                 cur = &str[2][0];
399         else if (cur == &str[2][0])
400                 cur = &str[3][0];
401         else
402                 cur = &str[0][0];
403         if (bucket == 0xff)
404                 strcpy(cur, "Unassigned");
405         else
406                 sprintf(cur, "%u", bucket);
407         return cur;
408 }
409
410 static guint16
411 dissect_wccp2_header(tvbuff_t *tvb, int offset, proto_tree *wccp_tree)
412 {
413         guint16 length;
414
415         proto_tree_add_item(wccp_tree, hf_wccp_version, tvb, offset, 2,
416             FALSE);
417         offset += 2;
418         length = tvb_get_ntohs(tvb, offset);
419         proto_tree_add_text(wccp_tree, tvb, offset, 2, "Length: %u",
420             length);
421         return length;
422 }
423
424 static void
425 dissect_wccp2_info(tvbuff_t *tvb, int offset, guint16 length,
426     proto_tree *wccp_tree)
427 {
428         guint16 type;
429         guint16 item_length;
430         proto_item *ti;
431         proto_tree *info_tree;
432         gint ett;
433         gboolean (*dissector)(tvbuff_t *, int, int, proto_tree *);
434
435         while (length != 0) {
436                 type = tvb_get_ntohs(tvb, offset);
437                 item_length = tvb_get_ntohs(tvb, offset+2);
438
439                 switch (type) {
440
441                 case WCCP2_SECURITY_INFO:
442                         ett = ett_security_info;
443                         dissector = dissect_wccp2_security_info;
444                         break;
445
446                 case WCCP2_SERVICE_INFO:
447                         ett = ett_service_info;
448                         dissector = dissect_wccp2_service_info;
449                         break;
450
451                 case WCCP2_ROUTER_ID_INFO:
452                         ett = ett_router_identity_info;
453                         dissector = dissect_wccp2_router_identity_info;
454                         break;
455
456                 case WCCP2_WC_ID_INFO:
457                         ett = ett_wc_identity_info;
458                         dissector = dissect_wccp2_wc_identity_info;
459                         break;
460
461                 case WCCP2_RTR_VIEW_INFO:
462                         ett = ett_router_view_info;
463                         dissector = dissect_wccp2_router_view_info;
464                         break;
465
466                 case WCCP2_WC_VIEW_INFO:
467                         ett = ett_wc_view_info;
468                         dissector = dissect_wccp2_wc_view_info;
469                         break;
470
471                 case WCCP2_REDIRECT_ASSIGNMENT:
472                         ett = ett_router_assignment_info;
473                         dissector = dissect_wccp2_assignment_info;
474                         break;
475
476                 case WCCP2_QUERY_INFO:
477                         ett = ett_query_info;
478                         dissector = dissect_wccp2_router_query_info;
479                         break;
480
481                 case WCCP2_CAPABILITIES_INFO:
482                         ett = ett_capabilities_info;
483                         dissector = dissect_wccp2_capability_info;
484                         break;
485
486                 default:
487                         ett = ett_unknown_info;
488                         dissector = NULL;
489                         break;
490                 }
491
492                 ti = proto_tree_add_text(wccp_tree, tvb, offset, item_length + 4,
493                     val_to_str(type, info_type_vals, "Unknown info type (%u)"));
494                 info_tree = proto_item_add_subtree(ti, ett);
495                 proto_tree_add_text(info_tree, tvb, offset, 2,
496                     "Type: %s",
497                     val_to_str(type, info_type_vals, "Unknown info type (%u)"));
498                 proto_tree_add_text(info_tree, tvb, offset+2, 2,
499                     "Length: %u", item_length);
500                 offset += 4;
501                 length -= 4;
502
503                 /*
504                  * XXX - pass in "length" and check for that as well.
505                  */
506                 if (dissector != NULL) {
507                         if (!(*dissector)(tvb, offset, item_length, info_tree))
508                                 return; /* ran out of data */
509                 } else {
510                         proto_tree_add_text(info_tree, tvb, offset, item_length,
511                             "Data: %u byte%s", item_length,
512                             plurality(item_length, "", "s"));
513                 }
514                 offset += item_length;
515                 length -= item_length;
516         }
517 }
518
519 #define SECURITY_INFO_LEN               4
520
521 #define WCCP2_NO_SECURITY               0
522 #define WCCP2_MD5_SECURITY              1
523
524 static gboolean
525 dissect_wccp2_security_info(tvbuff_t *tvb, int offset, int length,
526     proto_tree *info_tree)
527 {
528         guint32 security_option;
529
530         if (length < SECURITY_INFO_LEN) {
531                 proto_tree_add_text(info_tree, tvb, offset, 0,
532                     "Item length is %u, should be %u", length,
533                     SECURITY_INFO_LEN);
534                 return TRUE;
535         }
536
537         security_option = tvb_get_ntohl(tvb, offset);
538         switch (security_option) {
539
540         case WCCP2_NO_SECURITY:
541                 proto_tree_add_text(info_tree, tvb, offset, 4,
542                     "Security Option: None");
543                 break;
544
545         case WCCP2_MD5_SECURITY:
546                 proto_tree_add_text(info_tree, tvb, offset, 4,
547                     "Security Option: MD5");
548                 offset += 4;
549                 if (length > 4) {
550                         proto_tree_add_text(info_tree, tvb, offset,
551                             length - 4, "MD5 checksum: %s",
552                             tvb_bytes_to_str(tvb, offset, length - 4));
553                 }
554                 break;
555
556         default:
557                 proto_tree_add_text(info_tree, tvb, offset, 4,
558                     "Security Option: Unknown (%u)", security_option);
559                 break;
560         }
561         return TRUE;
562 }
563
564 #define SERVICE_INFO_LEN                (4+4+8*2)
565
566 #define WCCP2_SERVICE_STANDARD          0
567 #define WCCP2_SERVICE_DYNAMIC           1
568
569 /*
570  * Service flags.
571  */
572 #define WCCP2_SI_SRC_IP_HASH            0x0001
573 #define WCCP2_SI_DST_IP_HASH            0x0002
574 #define WCCP2_SI_SRC_PORT_HASH          0x0004
575 #define WCCP2_SI_DST_PORT_HASH          0x0008
576 #define WCCP2_SI_PORTS_DEFINED          0x0010
577 #define WCCP2_SI_PORTS_SOURCE           0x0020
578 #define WCCP2_SI_SRC_IP_ALT_HASH        0x0100
579 #define WCCP2_SI_DST_IP_ALT_HASH        0x0200
580 #define WCCP2_SI_SRC_PORT_ALT_HASH      0x0400
581 #define WCCP2_SI_DST_PORT_ALT_HASH      0x0800
582
583 static gboolean
584 dissect_wccp2_service_info(tvbuff_t *tvb, int offset, int length,
585     proto_tree *info_tree)
586 {
587         guint8 service_type;
588         guint32 flags;
589         proto_item *tf;
590         proto_tree *field_tree;
591         int i;
592
593         if (length != SERVICE_INFO_LEN) {
594                 proto_tree_add_text(info_tree, tvb, offset, 0,
595                     "Item length is %u, should be %u", length,
596                     SERVICE_INFO_LEN);
597                 return TRUE;
598         }
599
600         service_type = tvb_get_guint8(tvb, offset);
601         switch (service_type) {
602
603         case WCCP2_SERVICE_STANDARD:
604                 proto_tree_add_text(info_tree, tvb, offset, 1,
605                     "Service Type: Well-known service");
606                 proto_tree_add_text(info_tree, tvb, offset+1, 1,
607                     "Service ID: %s",
608                     val_to_str(tvb_get_guint8(tvb, offset+1), service_id_vals, 
609                             "Unknown (0x%02X)"));
610
611                 break;
612
613         case WCCP2_SERVICE_DYNAMIC:
614                 proto_tree_add_text(info_tree, tvb, offset, 1,
615                     "Service Type: Dynamic service");
616                 proto_tree_add_text(info_tree, tvb, offset+1, 1,
617                     "Service ID: %s",
618                     val_to_str(tvb_get_guint8(tvb, offset+1), service_id_vals, 
619                             "Unknown (0x%02X)"));
620                 proto_tree_add_text(info_tree, tvb, offset+2, 1,
621                     "Priority: %u", tvb_get_guint8(tvb, offset+2));
622                 /*
623                  * XXX - does "IP protocol identifier" mean this is a
624                  * protocol type of the sort you get in IP headers?
625                  * If so, we should get a table of those from the
626                  * IP dissector, and use that.
627                  */
628                 proto_tree_add_text(info_tree, tvb, offset+3, 1,
629                     "Protocol: %u", tvb_get_guint8(tvb, offset+3));     /* IP protocol identifier */
630                 break;
631
632         default:
633                 proto_tree_add_text(info_tree, tvb, offset, 1,
634                     "Service Type: Unknown (%u)", service_type);
635                 break;
636         }
637         offset += 4;
638
639         flags = tvb_get_ntohl(tvb, offset);
640         tf = proto_tree_add_text(info_tree, tvb, offset, 4,
641             "Flags: 0x%08X", flags);
642         field_tree = proto_item_add_subtree(tf, ett_service_flags);
643         proto_tree_add_text(field_tree, tvb, offset, 4, "%s",
644             decode_boolean_bitfield(flags, WCCP2_SI_SRC_IP_HASH,
645               sizeof (flags)*8,
646               "Use source IP address in primary hash",
647               "Don't use source IP address in primary hash"));
648         proto_tree_add_text(field_tree, tvb, offset, 4, "%s",
649             decode_boolean_bitfield(flags, WCCP2_SI_DST_IP_HASH,
650               sizeof (flags)*8,
651               "Use destination IP address in primary hash",
652               "Don't use destination IP address in primary hash"));
653         proto_tree_add_text(field_tree, tvb, offset, 4, "%s",
654             decode_boolean_bitfield(flags, WCCP2_SI_SRC_PORT_HASH,
655               sizeof (flags)*8,
656               "Use source port in primary hash",
657               "Don't use source port in primary hash"));
658         proto_tree_add_text(field_tree, tvb, offset, 4, "%s",
659             decode_boolean_bitfield(flags, WCCP2_SI_DST_PORT_HASH,
660               sizeof (flags)*8,
661               "Use destination port in primary hash",
662               "Don't use destination port in primary hash"));
663         proto_tree_add_text(field_tree, tvb, offset, 4, "%s",
664             decode_boolean_bitfield(flags, WCCP2_SI_PORTS_DEFINED,
665               sizeof (flags)*8,
666               "Ports defined",
667               "Ports not defined"));
668         if (flags & WCCP2_SI_PORTS_DEFINED) {
669                 proto_tree_add_text(field_tree, tvb, offset, 4, "%s",
670                     decode_boolean_bitfield(flags, WCCP2_SI_PORTS_SOURCE,
671                       sizeof (flags)*8,
672                       "Ports refer to source port",
673                       "Ports refer to destination port"));
674         }
675         proto_tree_add_text(field_tree, tvb, offset, 4, "%s",
676             decode_boolean_bitfield(flags, WCCP2_SI_SRC_IP_ALT_HASH,
677               sizeof (flags)*8,
678               "Use source IP address in secondary hash",
679               "Don't use source IP address in secondary hash"));
680         proto_tree_add_text(field_tree, tvb, offset, 4, "%s",
681             decode_boolean_bitfield(flags, WCCP2_SI_DST_IP_ALT_HASH,
682               sizeof (flags)*8,
683               "Use destination IP address in secondary hash",
684               "Don't use destination IP address in secondary hash"));
685         proto_tree_add_text(field_tree, tvb, offset, 4, "%s",
686             decode_boolean_bitfield(flags, WCCP2_SI_SRC_PORT_ALT_HASH,
687               sizeof (flags)*8,
688               "Use source port in secondary hash",
689               "Don't use source port in secondary hash"));
690         proto_tree_add_text(field_tree, tvb, offset, 4, "%s",
691             decode_boolean_bitfield(flags, WCCP2_SI_DST_PORT_ALT_HASH,
692               sizeof (flags)*8,
693               "Use destination port in secondary hash",
694               "Don't use destination port in secondary hash"));
695         offset += 4;
696
697         if (flags & WCCP2_SI_PORTS_DEFINED) {
698                 for (i = 0; i < 8; i++) {
699                         proto_tree_add_text(info_tree, tvb, offset, 2,
700                             "Port %d: %u", i, tvb_get_ntohs(tvb, offset));
701                         offset += 2;
702                 }
703         }
704
705         return TRUE;
706 }
707
708 #define ROUTER_ID_INFO_MIN_LEN          (8+4+4)
709
710 static void
711 dissect_wccp2_router_identity_element(tvbuff_t *tvb, int offset,
712     proto_tree *tree)
713 {
714         proto_tree_add_text(tree, tvb, offset, 4,
715             "IP Address: %s", ip_to_str(tvb_get_ptr(tvb, offset, 4)));
716         proto_tree_add_text(tree, tvb, offset + 4, 4,
717             "Receive ID: %u", tvb_get_ntohl(tvb, offset + 4));
718 }
719
720 static gboolean
721 dissect_wccp2_router_identity_info(tvbuff_t *tvb, int offset, int length,
722     proto_tree *info_tree)
723 {
724         guint32 n_received_from;
725         guint i;
726         proto_item *te;
727         proto_tree *element_tree;
728
729         if (length < ROUTER_ID_INFO_MIN_LEN) {
730                 proto_tree_add_text(info_tree, tvb, offset, 0,
731                     "Item length is %u, should be >= %u", length,
732                     ROUTER_ID_INFO_MIN_LEN);
733                 return TRUE;
734         }
735
736         te = proto_tree_add_text(info_tree, tvb, offset, 8,
737             "Router Identity Element: IP address %s",
738             ip_to_str(tvb_get_ptr(tvb, offset, 4)));
739         element_tree = proto_item_add_subtree(te,
740             ett_router_identity_element);
741         dissect_wccp2_router_identity_element(tvb, offset, element_tree);
742         offset += 8;
743
744         proto_tree_add_text(info_tree, tvb, offset, 4,
745             "Sent To IP Address: %s", ip_to_str(tvb_get_ptr(tvb, offset, 4)));
746         offset += 4;
747
748         n_received_from = tvb_get_ntohl(tvb, offset);
749         proto_tree_add_text(info_tree, tvb, offset, 4,
750             "Number of Received From IP addresses: %u", n_received_from);
751         offset += 4;
752
753         for (i = 0; i < n_received_from; i++) {
754                 proto_tree_add_text(info_tree, tvb, offset, 4,
755                     "Received From IP Address %d: %s", i,
756                     ip_to_str(tvb_get_ptr(tvb, offset, 4)));
757                 offset += 4;
758         }
759
760         return TRUE;
761 }
762
763 #define WC_ID_INFO_LEN                  (4+4+8*4+4)
764
765 static gboolean
766 dissect_wccp2_web_cache_identity_element(tvbuff_t *tvb, int offset,
767     proto_tree *tree)
768 {
769         proto_item *bucket_item;
770         proto_tree *bucket_tree;
771         proto_item *tf;
772         proto_tree *field_tree;
773         guint16 flags;
774         int i;
775         guint8 bucket_info;
776         int n;
777
778         proto_tree_add_text(tree, tvb, offset, 4,
779             "Web-Cache IP Address: %s", ip_to_str(tvb_get_ptr(tvb, offset, 4)));
780         offset += 4;
781
782         proto_tree_add_text(tree, tvb, offset, 2,
783             "Hash Revision %u", tvb_get_ntohs(tvb, offset));
784         offset += 2;
785
786         flags = tvb_get_ntohs(tvb, offset);
787         tf = proto_tree_add_text(tree, tvb, offset, 2,
788             "Flags: 0x%04X (%s)", flags,
789             ((flags & 0x8000) ?
790               "Hash information is historical" :
791               "Hash information is current"));
792         field_tree = proto_item_add_subtree(tf, ett_flags);
793         proto_tree_add_text(field_tree, tvb, offset, 2, "%s",
794             decode_boolean_bitfield(flags, 0x8000,
795               sizeof (flags)*8,
796               "Hash information is historical",
797               "Hash information is current"));
798         offset += 2;
799
800         bucket_item = proto_tree_add_text(tree, tvb, offset, 8*4,
801             "Hash information");
802         bucket_tree = proto_item_add_subtree(bucket_item, ett_buckets);
803         for (i = 0, n = 0; i < 32; i++) {
804                 bucket_info = tvb_get_guint8(tvb, offset);
805                 n = wccp_bucket_info(bucket_info, bucket_tree, n, tvb, offset);
806                 offset += 1;
807         }
808
809         proto_tree_add_text(tree, tvb, offset, 2,
810             "Assignment Weight: %u", tvb_get_ntohs(tvb, offset));
811         offset += 2;
812
813         proto_tree_add_text(tree, tvb, offset, 2,
814             "Status: 0x%04X", tvb_get_ntohs(tvb, offset));
815         offset += 2;
816
817         return TRUE;
818 }
819
820 static gboolean
821 dissect_wccp2_wc_identity_info(tvbuff_t *tvb, int offset, int length,
822     proto_tree *info_tree)
823 {
824         proto_item *te;
825         proto_tree *element_tree;
826
827         if (length != WC_ID_INFO_LEN) {
828                 proto_tree_add_text(info_tree, tvb, offset, 0,
829                     "Item length is %u, should be %u", length, WC_ID_INFO_LEN);
830                 return TRUE;
831         }
832
833         te = proto_tree_add_text(info_tree, tvb, offset, 4+2+2+32+2+2,
834             "Web-Cache Identity Element: IP address %s",
835             ip_to_str(tvb_get_ptr(tvb, offset, 4)));
836         element_tree = proto_item_add_subtree(te, ett_wc_identity_element);
837         if (!dissect_wccp2_web_cache_identity_element(tvb, offset,
838             element_tree))
839                 return FALSE;   /* ran out of data */
840
841         return TRUE;
842 }
843
844 #define ROUTER_VIEW_INFO_MIN_LEN        (4+8+4)
845
846 static void
847 dissect_wccp2_assignment_key(tvbuff_t *tvb, int offset,
848     proto_tree *info_tree)
849 {
850         proto_tree_add_text(info_tree, tvb, offset, 4,
851             "Assignment Key IP Address: %s",
852             ip_to_str(tvb_get_ptr(tvb, offset, 4)));
853         proto_tree_add_text(info_tree, tvb, offset + 4, 4,
854             "Assignment Key Change Number: %u", tvb_get_ntohl(tvb, offset + 4));
855 }
856
857 static gboolean
858 dissect_wccp2_router_view_info(tvbuff_t *tvb, int offset, int length,
859     proto_tree *info_tree)
860 {
861         guint32 n_routers;
862         guint32 n_web_caches;
863         guint i;
864         proto_item *te;
865         proto_tree *element_tree;
866
867         if (length < ROUTER_VIEW_INFO_MIN_LEN) {
868                 proto_tree_add_text(info_tree, tvb, offset, 0,
869                     "Item length is %u, should be >= %u", length,
870                     ROUTER_VIEW_INFO_MIN_LEN);
871                 return TRUE;
872         }
873
874         proto_tree_add_text(info_tree, tvb, offset, 4,
875             "Member Change Number: %u", tvb_get_ntohl(tvb, offset));
876         offset += 4;
877
878         dissect_wccp2_assignment_key(tvb, offset, info_tree);
879         offset += 8;
880
881         n_routers = tvb_get_ntohl(tvb, offset);
882         proto_tree_add_text(info_tree, tvb, offset, 4,
883             "Number of Routers: %u", n_routers);
884         offset += 4;
885
886         for (i = 0; i < n_routers; i++) {
887                 proto_tree_add_text(info_tree, tvb, offset, 4,
888                     "Router %d IP Address: %s", i,
889                     ip_to_str(tvb_get_ptr(tvb, offset, 4)));
890                 offset += 4;
891         }
892
893         n_web_caches = tvb_get_ntohl(tvb, offset);
894         proto_tree_add_text(info_tree, tvb, offset, 4,
895             "Number of Web Caches: %u", n_web_caches);
896         offset += 4;
897
898         for (i = 0; i < n_web_caches; i++) {
899                 te = proto_tree_add_text(info_tree, tvb, offset, WC_ID_INFO_LEN,
900                     "Web-Cache Identity Element %d: IP address %s", i,
901                     ip_to_str(tvb_get_ptr(tvb, offset, 4)));
902                 element_tree = proto_item_add_subtree(te,
903                     ett_wc_identity_element);
904                 if (!dissect_wccp2_web_cache_identity_element(tvb,
905                     offset, element_tree))
906                         return FALSE;   /* ran out of data */
907                 offset += WC_ID_INFO_LEN;
908         }
909
910         return TRUE;
911 }
912
913 #define WC_VIEW_INFO_MIN_LEN            (4+4)
914
915 static gboolean
916 dissect_wccp2_wc_view_info(tvbuff_t *tvb, int offset, int length,
917     proto_tree *info_tree)
918 {
919         guint32 n_routers;
920         guint32 n_web_caches;
921         guint i;
922         proto_item *te;
923         proto_tree *element_tree;
924
925         if (length < WC_VIEW_INFO_MIN_LEN) {
926                 proto_tree_add_text(info_tree, tvb, offset, 0,
927                     "Item length is %u, should be >= %u", length,
928                     WC_VIEW_INFO_MIN_LEN);
929                 return TRUE;
930         }
931
932         proto_tree_add_text(info_tree, tvb, offset, 4,
933             "Change Number: %u", tvb_get_ntohl(tvb, offset));
934         offset += 4;
935
936         n_routers = tvb_get_ntohl(tvb, offset);
937         proto_tree_add_text(info_tree, tvb, offset, 4,
938             "Number of Routers: %u", n_routers);
939         offset += 4;
940
941         for (i = 0; i < n_routers; i++) {
942                 te = proto_tree_add_text(info_tree, tvb, offset, 8,
943                     "Router %d Identity Element: IP address %s", i,
944                     ip_to_str(tvb_get_ptr(tvb, offset, 4)));
945                 element_tree = proto_item_add_subtree(te,
946                     ett_router_identity_element);
947                 dissect_wccp2_router_identity_element(tvb, offset, element_tree);
948                 offset += 8;
949         }
950
951         n_web_caches = tvb_get_ntohl(tvb, offset);
952         proto_tree_add_text(info_tree, tvb, offset, 4,
953             "Number of Web Caches: %u", n_web_caches);
954         offset += 4;
955
956         for (i = 0; i < n_web_caches; i++) {
957                 proto_tree_add_text(info_tree, tvb, offset, 4,
958                     "Web-Cache %d: IP address %s", i,
959                     ip_to_str(tvb_get_ptr(tvb, offset, 4)));
960                 offset += 4;
961         }
962         
963         return TRUE;
964 }
965
966 #define ASSIGNMENT_INFO_MIN_LEN         (8+4)
967
968 static void
969 dissect_wccp2_router_assignment_element(tvbuff_t *tvb, int offset,
970     proto_tree *tree)
971 {
972         proto_tree_add_text(tree, tvb, offset, 4,
973             "IP Address: %s", ip_to_str(tvb_get_ptr(tvb, offset, 4)));
974         proto_tree_add_text(tree, tvb, offset + 4, 4,
975             "Receive ID: %u", tvb_get_ntohl(tvb, offset + 4));
976         proto_tree_add_text(tree, tvb, offset + 8, 4,
977             "Change Number: %u", tvb_get_ntohl(tvb, offset + 8));
978 }
979
980 static gchar *
981 assignment_bucket_name(guint8 bucket)
982 {
983         static gchar str[4][10+1];
984         static gchar *cur;
985
986         if (cur == &str[0][0])
987                 cur = &str[1][0];
988         else if (cur == &str[1][0])
989                 cur = &str[2][0];
990         else if (cur == &str[2][0])
991                 cur = &str[3][0];
992         else
993                 cur = &str[0][0];
994         if (bucket == 0xff)
995                 strcpy(cur, "Unassigned");
996         else {
997                 sprintf(cur, "%u%s", bucket >> 1,
998                     (bucket & 0x01) ? " (Alt)" : "");
999         }
1000         return cur;
1001 }
1002
1003 static gboolean
1004 dissect_wccp2_assignment_info(tvbuff_t *tvb, int offset, int length,
1005     proto_tree *info_tree)
1006 {
1007         guint32 n_routers;
1008         guint32 n_web_caches;
1009         guint i;
1010         proto_item *te;
1011         proto_tree *element_tree;
1012
1013         if (length < ASSIGNMENT_INFO_MIN_LEN) {
1014                 proto_tree_add_text(info_tree, tvb, offset, 0,
1015                     "Item length is %u, should be >= %u", length,
1016                     ASSIGNMENT_INFO_MIN_LEN);
1017                 return TRUE;
1018         }
1019
1020         dissect_wccp2_assignment_key(tvb, offset, info_tree);
1021         offset += 8;
1022
1023         n_routers = tvb_get_ntohl(tvb, offset);
1024         proto_tree_add_text(info_tree, tvb, offset, 4,
1025             "Number of Routers: %u", n_routers);
1026         offset += 4;
1027
1028         for (i = 0; i < n_routers; i++) {
1029                 te = proto_tree_add_text(info_tree, tvb, offset, 4,
1030                     "Router %d Assignment Element: IP address %s", i,
1031                     ip_to_str(tvb_get_ptr(tvb, offset, 4)));
1032                 element_tree = proto_item_add_subtree(te,
1033                     ett_router_assignment_element);
1034                 dissect_wccp2_router_assignment_element(tvb, offset,
1035                     element_tree);
1036                 offset += 12;
1037         }
1038
1039         n_web_caches = tvb_get_ntohl(tvb, offset);
1040         proto_tree_add_text(info_tree, tvb, offset, 4,
1041             "Number of Web Caches: %u", n_web_caches);
1042         offset += 4;
1043
1044         for (i = 0; i < n_web_caches; i++) {
1045                 proto_tree_add_text(info_tree, tvb, offset, 4,
1046                     "Web-Cache %d: IP address %s", i,
1047                     ip_to_str(tvb_get_ptr(tvb, offset, 4)));
1048                 offset += 4;
1049         }
1050
1051         for (i = 0; i < 256; i += 4) {
1052                 proto_tree_add_text(info_tree, tvb, offset, 4,
1053                     "Buckets %d - %d: %10s %10s %10s %10s",
1054                     i, i + 3,
1055                     assignment_bucket_name(tvb_get_guint8(tvb, offset)),
1056                     assignment_bucket_name(tvb_get_guint8(tvb, offset+1)),
1057                     assignment_bucket_name(tvb_get_guint8(tvb, offset+2)),
1058                     assignment_bucket_name(tvb_get_guint8(tvb, offset+3)));
1059                 offset += 4;
1060         }
1061
1062         return TRUE;
1063 }
1064
1065 #define QUERY_INFO_LEN                  (4+4+4+4)
1066
1067 static gboolean
1068 dissect_wccp2_router_query_info(tvbuff_t *tvb, int offset, int length,
1069     proto_tree *info_tree)
1070 {
1071         if (length != QUERY_INFO_LEN) {
1072                 proto_tree_add_text(info_tree, tvb, offset, 0,
1073                     "Item length is %u, should be %u", length, QUERY_INFO_LEN);
1074                 return TRUE;
1075         }
1076
1077         proto_tree_add_text(info_tree, tvb, offset, 4,
1078             "Router IP Address: %s", ip_to_str(tvb_get_ptr(tvb, offset, 4)));
1079         offset += 4;
1080
1081         proto_tree_add_text(info_tree, tvb, offset, 4,
1082             "Receive ID: %u", tvb_get_ntohl(tvb, offset));
1083         offset += 4;
1084
1085         proto_tree_add_text(info_tree, tvb, offset, 4,
1086             "Sent To IP Address: %s", ip_to_str(tvb_get_ptr(tvb, offset, 4)));
1087         offset += 4;
1088
1089         proto_tree_add_text(info_tree, tvb, offset, 4,
1090             "Target IP Address: %s", ip_to_str(tvb_get_ptr(tvb, offset, 4)));
1091         offset += 4;
1092
1093         return TRUE;
1094 }
1095
1096 #define WCCP2_FORWARDING_METHOD         0x01
1097 #define WCCP2_ASSIGNMENT_METHOD         0x02
1098 #define WCCP2_PACKET_RETURN_METHOD      0x03
1099
1100 static const value_string capability_type_vals[] = {
1101         { WCCP2_FORWARDING_METHOD,    "Forwarding Method" },
1102         { WCCP2_ASSIGNMENT_METHOD,    "Assignment Method" },
1103         { WCCP2_PACKET_RETURN_METHOD, "Return Method" },
1104         { 0,                          NULL }
1105 };
1106
1107 #define WCCP2_FORWARDING_METHOD_GRE     0x00000001
1108 #define WCCP2_FORWARDING_METHOD_L2      0x00000002
1109
1110 static const capability_flag forwarding_method_flags[] = {
1111         { WCCP2_FORWARDING_METHOD_GRE,
1112           "IP-GRE", "GRE-encapsulated" },
1113         { WCCP2_FORWARDING_METHOD_L2,
1114           "L2", "L2 rewrite" },
1115         { 0, 
1116           NULL, NULL }
1117 };
1118
1119 #define WCCP2_ASSIGNMENT_METHOD_HASH    0x00000001
1120 #define WCCP2_ASSIGNMENT_METHOD_MASK    0x00000002
1121
1122 static const capability_flag assignment_method_flags[] = {
1123         { WCCP2_ASSIGNMENT_METHOD_HASH, "Hash", "Hash" },
1124         { WCCP2_ASSIGNMENT_METHOD_MASK, "Mask", "Mask" },
1125         { 0,                            NULL,   NULL }
1126 };
1127
1128 #define WCCP2_PACKET_RETURN_METHOD_GRE  0x00000001
1129 #define WCCP2_PACKET_RETURN_METHOD_L2   0x00000002
1130
1131 static const capability_flag packet_return_method_flags[] = {
1132         { WCCP2_PACKET_RETURN_METHOD_GRE,
1133           "IP-GRE", "GRE-encapsulated" },
1134         { WCCP2_PACKET_RETURN_METHOD_L2,
1135           "L2", "L2 rewrite" },
1136         { 0, 
1137           NULL, NULL }
1138 };
1139
1140 static gboolean
1141 dissect_wccp2_capability_info(tvbuff_t *tvb, int offset, int length,
1142     proto_tree *info_tree)
1143 {
1144         guint16 capability_type;
1145         guint16 capability_val_len;
1146         int curr_offset;
1147         proto_item *te;
1148         proto_tree *element_tree;
1149
1150         for (curr_offset = offset; curr_offset < (length + offset);
1151             curr_offset += (capability_val_len + 4)) {
1152                 capability_type = tvb_get_ntohs(tvb, curr_offset);
1153                 capability_val_len = tvb_get_ntohs(tvb, curr_offset + 2);
1154                 te = proto_tree_add_text(info_tree, tvb, curr_offset,
1155                     capability_val_len + 4, "%s",
1156                     val_to_str(capability_type,
1157                       capability_type_vals, "Unknown Capability Element (0x%08X)"));
1158                 element_tree = proto_item_add_subtree(te,
1159                     ett_capability_element);
1160
1161                 proto_tree_add_text(element_tree, tvb, curr_offset, 2,
1162                     "Type: %s",
1163                     val_to_str(capability_type,
1164                       capability_type_vals, "Unknown (0x%08X)"));
1165
1166                 if (capability_val_len < 4) {
1167                         proto_tree_add_text(element_tree, tvb, curr_offset+2, 2,
1168                             "Value Length: %u (illegal, must be >= 4)",
1169                             capability_val_len);
1170                         break;
1171                 }
1172                 proto_tree_add_text(element_tree, tvb, curr_offset+2, 2,
1173                     "Value Length: %u", capability_val_len);
1174
1175                 switch (capability_type) {
1176
1177                 case WCCP2_FORWARDING_METHOD:
1178                         dissect_32_bit_capability_flags(tvb, curr_offset,
1179                             capability_val_len,
1180                             ett_capability_forwarding_method,
1181                             forwarding_method_flags, element_tree);
1182                         break;
1183
1184                 case WCCP2_ASSIGNMENT_METHOD:
1185                         dissect_32_bit_capability_flags(tvb, curr_offset,
1186                             capability_val_len,
1187                             ett_capability_assignment_method,
1188                             assignment_method_flags, element_tree);
1189                         break;
1190
1191                 case WCCP2_PACKET_RETURN_METHOD:
1192                         dissect_32_bit_capability_flags(tvb, curr_offset,
1193                             capability_val_len,
1194                             ett_capability_return_method,
1195                             packet_return_method_flags, element_tree);
1196                         break;
1197
1198                 default:
1199                         proto_tree_add_text(element_tree, tvb,
1200                             curr_offset+4, capability_val_len,
1201                             "Value: %s",
1202                             tvb_bytes_to_str(tvb, curr_offset+4,
1203                                              capability_val_len));
1204                         break;
1205                 }
1206
1207         }
1208         return TRUE;
1209 }
1210
1211 static void
1212 dissect_32_bit_capability_flags(tvbuff_t *tvb, int curr_offset,
1213     guint16 capability_val_len, gint ett, const capability_flag *flags,
1214     proto_tree *element_tree)
1215 {
1216         guint32 capability_val;
1217         proto_item *tm;
1218         proto_tree *method_tree;
1219         int i;
1220         char flags_string[128+1];
1221         char *p;
1222         int space_left;
1223         char buf[1025];
1224
1225         if (capability_val_len != 4) {
1226                 proto_tree_add_text(element_tree, tvb,
1227                     curr_offset+4, capability_val_len,
1228                     "Illegal length (must be 4)");
1229                 return;
1230         }
1231
1232         capability_val = tvb_get_ntohl(tvb, curr_offset + 4);
1233         flags_string[0] = '\0';
1234         p = &flags_string[0];
1235         space_left = sizeof flags_string;
1236         for (i = 0; flags[i].short_name != NULL; i++) {
1237                 if (capability_val & flags[i].value) {
1238                         if (p != &flags_string[0]) {
1239                                 snprintf(p, space_left, ",");
1240                                 p = &flags_string[strlen(flags_string)];
1241                         }
1242                         snprintf(p, space_left, "%s", flags[i].short_name);
1243                         p = &flags_string[strlen(flags_string)];
1244                 }
1245         }
1246         tm = proto_tree_add_text(element_tree, tvb, curr_offset+4, 4,
1247             "Value: 0x%08X (%s)", capability_val, flags_string);
1248         method_tree = proto_item_add_subtree(tm, ett);
1249         for (i = 0; flags[i].long_name != NULL; i++) {
1250                 p = decode_bitfield_value(buf, capability_val,
1251                       flags[i].value, 32);
1252                 strcpy(p, flags[i].long_name);
1253                 strcat(p, ": ");
1254                 if (capability_val & flags[i].value)
1255                     strcat(p, "Supported");
1256                 else
1257                     strcat(p, "Not supported");
1258                 proto_tree_add_text(method_tree, tvb, curr_offset+4, 4,
1259                     "%s", buf);
1260         }
1261 }
1262
1263 void
1264 proto_register_wccp(void)
1265 {
1266         static hf_register_info hf[] = {
1267                 { &hf_wccp_message_type,
1268                         { "WCCP Message Type", "wccp.message", FT_UINT32, BASE_DEC, VALS(wccp_type_vals), 0x0,
1269                                 "The WCCP message that was sent", HFILL }
1270                 },
1271                 { &hf_wccp_version, 
1272                         { "WCCP Version", "wccp.version", FT_UINT32, BASE_HEX, VALS(wccp_version_val), 0x0,
1273                                 "The WCCP version", HFILL }
1274                 },
1275                 { &hf_hash_revision,
1276                         { "Hash Revision", "wccp.hash_revision", FT_UINT32, BASE_DEC, 0x0, 0x0,
1277                                 "The cache hash revision", HFILL }
1278                 },
1279                 { &hf_change_num,
1280                         { "Change Number", "wccp.change_num", FT_UINT32, BASE_DEC, 0x0, 0x0,
1281                                 "The Web-Cache list entry change number", HFILL }
1282                 },
1283                 { &hf_recvd_id,
1284                         { "Received ID", "wccp.recvd_id", FT_UINT32, BASE_DEC, 0x0, 0x0,
1285                                 "The number of I_SEE_YOU's that have been sent", HFILL }
1286                 },
1287                 { &hf_cache_ip,
1288                         { "Web Cache IP address", "wccp.cache_ip", FT_IPv4, BASE_NONE, NULL, 0x0,
1289                                 "The IP address of a Web cache", HFILL }
1290                 },
1291         };
1292         static gint *ett[] = {
1293                 &ett_wccp,
1294                 &ett_cache_count,
1295                 &ett_buckets,
1296                 &ett_flags,
1297                 &ett_cache_info,
1298                 &ett_security_info,
1299                 &ett_service_info,
1300                 &ett_service_flags,
1301                 &ett_router_identity_element,
1302                 &ett_router_identity_info,
1303                 &ett_wc_identity_element,
1304                 &ett_wc_identity_info,
1305                 &ett_router_view_info,
1306                 &ett_wc_view_info,
1307                 &ett_query_info,
1308                 &ett_router_assignment_element,
1309                 &ett_router_assignment_info,
1310                 &ett_capabilities_info,
1311                 &ett_capability_element,
1312                 &ett_capability_forwarding_method,
1313                 &ett_capability_assignment_method,
1314                 &ett_capability_return_method,
1315                 &ett_unknown_info,
1316         };
1317
1318         proto_wccp = proto_register_protocol("Web Cache Coordination Protocol",
1319             "WCCP", "wccp");
1320         proto_register_field_array(proto_wccp, hf, array_length(hf));
1321         proto_register_subtree_array(ett, array_length(ett));
1322 }
1323
1324 void
1325 proto_reg_handoff_wccp(void)
1326 {
1327         dissector_handle_t wccp_handle;
1328
1329         wccp_handle = create_dissector_handle(dissect_wccp, proto_wccp);
1330         dissector_add("udp.port", UDP_PORT_WCCP, wccp_handle);
1331 }