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