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