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