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