Have "proto_register_protocol()" build a list of data structures for
[obnox/wireshark/wip.git] / packet-cops.c
1 /* packet-cops.c
2  * Routines for the COPS (Common Open Policy Service) protocol dissection
3  * RFC2748
4  *
5  * Copyright 2000, Heikki Vatiainen <hessu@cs.tut.fi>
6  *
7  * $Id: packet-cops.c,v 1.8 2001/01/03 06:55:27 guy Exp $
8  *
9  * Ethereal - Network traffic analyzer
10  * By Gerald Combs <gerald@zing.org>
11  * Copyright 1998 Gerald Combs
12  *
13  * 
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  * 
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
27  */
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <stdio.h>
34 #include <stdlib.h>
35
36 #include <string.h>
37 #include <glib.h>
38 #include "packet.h"
39 #include "packet-ipv6.h"
40
41 #define TCP_PORT_COPS 3288
42
43 #define COPS_OBJECT_HDR_SIZE 4
44
45 static const value_string cops_flags_vals[] = {
46         { 0x00,          "None" },
47         { 0x01,          "Solicited Message Flag Bit" },
48         { 0, NULL },
49 };
50
51 /* The different COPS message types */
52 enum cops_op_code {
53         COPS_NO_MSG,          /* Not a COPS Message type     */ 
54
55         COPS_MSG_REQ,         /* Request (REQ)               */
56         COPS_MSG_DEC,         /* Decision (DEC)              */
57         COPS_MSG_RPT,         /* Report State (RPT)          */
58         COPS_MSG_DRQ,         /* Delete Request State (DRQ)  */
59         COPS_MSG_SSQ,         /* Synchronize State Req (SSQ) */
60         COPS_MSG_OPN,         /* Client-Open (OPN)           */
61         COPS_MSG_CAT,         /* Client-Accept (CAT)         */
62         COPS_MSG_CC,          /* Client-Close (CC)           */
63         COPS_MSG_KA,          /* Keep-Alive (KA)             */
64         COPS_MSG_SSC,         /* Synchronize Complete (SSC)  */
65
66         COPS_LAST_OP_CODE     /* For error checking          */
67 };
68
69 static const value_string cops_op_code_vals[] = {
70         { COPS_MSG_REQ,          "Request (REQ)" },
71         { COPS_MSG_DEC,          "Decision (DEC)" },
72         { COPS_MSG_RPT,          "Report State (RPT)" },
73         { COPS_MSG_DRQ,          "Delete Request State (DRQ)" },
74         { COPS_MSG_SSQ,          "Synchronize State Req (SSQ)" },
75         { COPS_MSG_OPN,          "Client-Open (OPN)" },
76         { COPS_MSG_CAT,          "Client-Accept (CAT)" },
77         { COPS_MSG_CC,           "Client-Close (CC)" },
78         { COPS_MSG_KA,           "Keep-Alive (KA)" },
79         { COPS_MSG_SSC,          "Synchronize Complete (SSC)" },
80         { 0, NULL },
81 };
82
83
84 /* The different objects in COPS messages */
85 enum cops_c_num {
86         COPS_NO_OBJECT,        /* Not a COPS Object type               */
87
88         COPS_OBJ_HANDLE,       /* Handle Object (Handle)               */
89         COPS_OBJ_CONTEXT,      /* Context Object (Context)             */
90         COPS_OBJ_IN_INT,       /* In-Interface Object (IN-Int)         */
91         COPS_OBJ_OUT_INT,      /* Out-Interface Object (OUT-Int)       */
92         COPS_OBJ_REASON,       /* Reason Object (Reason)               */
93         COPS_OBJ_DECISION,     /* Decision Object (Decision)           */
94         COPS_OBJ_LPDPDECISION, /* LPDP Decision Object (LPDPDecision)  */
95         COPS_OBJ_ERROR,        /* Error Object (Error)                 */
96         COPS_OBJ_CLIENTSI,     /* Client Specific Information Object (ClientSI) */
97         COPS_OBJ_KATIMER,      /* Keep-Alive Timer Object (KATimer)    */
98         COPS_OBJ_PEPID,        /* PEP Identification Object (PEPID)    */
99         COPS_OBJ_REPORT_TYPE,  /* Report-Type Object (Report-Type)     */
100         COPS_OBJ_PDPREDIRADDR, /* PDP Redirect Address Object (PDPRedirAddr) */
101         COPS_OBJ_LASTPDPADDR,  /* Last PDP Address (LastPDPaddr)       */
102         COPS_OBJ_ACCTTIMER,    /* Accounting Timer Object (AcctTimer)  */
103         COPS_OBJ_INTEGRITY,    /* Message Integrity Object (Integrity) */
104
105         COPS_LAST_C_NUM        /* For error checking                   */
106 };
107
108 static const value_string cops_c_num_vals[] = {
109         { COPS_OBJ_HANDLE,       "Handle Object (Handle)" },
110         { COPS_OBJ_CONTEXT,      "Context Object (Context)" },
111         { COPS_OBJ_IN_INT,       "In-Interface Object (IN-Int)" },
112         { COPS_OBJ_OUT_INT,      "Out-Interface Object (OUT-Int)" },
113         { COPS_OBJ_REASON,       "Reason Object (Reason)" },
114         { COPS_OBJ_DECISION,     "Decision Object (Decision)" },
115         { COPS_OBJ_LPDPDECISION, "LPDP Decision Object (LPDPDecision)" },
116         { COPS_OBJ_ERROR,        "Error Object (Error)" },
117         { COPS_OBJ_CLIENTSI,     "Client Specific Information Object (ClientSI)" },
118         { COPS_OBJ_KATIMER,      "Keep-Alive Timer Object (KATimer)" },
119         { COPS_OBJ_PEPID,        "PEP Identification Object (PEPID)" },
120         { COPS_OBJ_REPORT_TYPE,  "Report-Type Object (Report-Type)" },
121         { COPS_OBJ_PDPREDIRADDR, "PDP Redirect Address Object (PDPRedirAddr)" },
122         { COPS_OBJ_LASTPDPADDR,  "Last PDP Address (LastPDPaddr)" },
123         { COPS_OBJ_ACCTTIMER,    "Accounting Timer Object (AcctTimer)" },
124         { COPS_OBJ_INTEGRITY,    "Message Integrity Object (Integrity)" },
125         { 0, NULL },
126
127 };
128
129 /* R-Type is carried within the Context Object */
130 static const value_string cops_r_type_vals[] = {
131         { 0x01, "Incoming-Message/Admission Control request" },
132         { 0x02, "Resource-Allocation request" },
133         { 0x04, "Outgoing-Message request" },
134         { 0x08, "Configuration request" },
135         { 0, NULL },
136 };
137
138 /* Reason-Code is carried within the Reason object */
139 static const value_string cops_reason_vals[] = {
140         { 1,  "Unspecified" },
141         { 2,  "Management" },
142         { 3,  "Preempted (Another request state takes precedence)" },
143         { 4,  "Tear (Used to communicate a signaled state removal)" },
144         { 5,  "Timeout (Local state has timed-out)" },
145         { 6,  "Route Change (Change invalidates request state)" },
146         { 7,  "Insufficient Resources (No local resource available)" },
147         { 8,  "PDP's Directive (PDP decision caused the delete)" },
148         { 9,  "Unsupported decision (PDP decision not supported)" },
149         { 10, "Synchronize Handle Unknown" },
150         { 11, "Transient Handle (stateless event)" },
151         { 12, "Malformed Decision (could not recover)" },
152         { 13, "Unknown COPS Object from PDP" },
153         { 0, NULL },
154 };
155
156 /* Command-Code is carried within the Decision object if C-Type is 1 */
157 static const value_string cops_dec_cmd_code_vals[] = {
158         { 0, "NULL Decision (No configuration data available)" },
159         { 1, "Install (Admit request/Install configuration)" },
160         { 2, "Remove (Remove request/Remove configuration)" },
161         { 0, NULL },
162 };
163
164 /* Decision flags are also carried with the Decision object if C-Type is 1 */
165 static const value_string cops_dec_cmd_flag_vals[] = {
166         { 0x00, "<None set>" },
167         { 0x01, "Trigger Error (Trigger error message if set)" },
168         { 0, NULL },
169 };
170
171 /* Error-Code from Error object */
172 static const value_string cops_error_vals[] = {
173         {1,  "Bad handle" },
174         {2,  "Invalid handle reference" },
175         {3,  "Bad message format (Malformed Message)" },
176         {4,  "Unable to process (server gives up on query)" },
177         {5,  "Mandatory client-specific info missing" },
178         {6,  "Unsupported client" },
179         {7,  "Mandatory COPS object missing" },
180         {8,  "Client Failure" },
181         {9,  "Communication Failure" },
182         {10, "Unspecified" },
183         {11, "Shutting down" },
184         {12, "Redirect to Preferred Server" },
185         {13, "Unknown COPS Object" },
186         {14, "Authentication Failure" },
187         {15, "Authentication Required" },
188 };
189
190 /* Report-Type from Report-Type object */
191 static const value_string cops_report_type_vals[] = {
192         {1, " Success   : Decision was successful at the PEP" },
193         {2, " Failure   : Decision could not be completed by PEP" },
194         {3, " Accounting: Accounting update for an installed state" },
195 };
196
197 /* Initialize the protocol and registered fields */
198 static gint proto_cops = -1;
199 static gint hf_cops_ver_flags = -1;
200 static gint hf_cops_version = -1;
201 static gint hf_cops_flags = -1;
202
203 static gint hf_cops_op_code = -1;
204 static gint hf_cops_client_type = -1;
205 static gint hf_cops_msg_len = -1;
206
207 static gint hf_cops_obj_len = -1;
208 static gint hf_cops_obj_c_num = -1;
209 static gint hf_cops_obj_c_type = -1;
210
211 static gint hf_cops_r_type_flags = -1;
212 static gint hf_cops_m_type_flags = -1;
213
214 static gint hf_cops_in_int_ipv4 = -1;
215 static gint hf_cops_in_int_ipv6 = -1;
216 static gint hf_cops_out_int_ipv4 = -1;
217 static gint hf_cops_out_int_ipv6 = -1;
218 static gint hf_cops_int_ifindex = -1;
219
220 static gint hf_cops_reason = -1;
221 static gint hf_cops_reason_sub = -1;
222
223 static gint hf_cops_dec_cmd_code = -1;
224 static gint hf_cops_dec_flags = -1;
225
226 static gint hf_cops_error = -1;
227 static gint hf_cops_error_sub = -1;
228
229 static gint hf_cops_katimer = -1;
230
231 static gint hf_cops_pepid = -1;
232
233 static gint hf_cops_report_type = -1;
234
235 static gint hf_cops_pdprediraddr_ipv4 = -1;
236 static gint hf_cops_pdprediraddr_ipv6 = -1;
237 static gint hf_cops_lastpdpaddr_ipv4 = -1;
238 static gint hf_cops_lastpdpaddr_ipv6 = -1;
239 static gint hf_cops_pdp_tcp_port = -1;
240
241 static gint hf_cops_accttimer = -1;
242
243 static gint hf_cops_key_id = -1;
244 static gint hf_cops_seq_num = -1;
245
246 /* Initialize the subtree pointers */
247 static gint ett_cops = -1;
248 static gint ett_cops_ver_flags = -1;
249 static gint ett_cops_obj = -1;
250 static gint ett_cops_obj_data = -1;
251 static gint ett_cops_r_type_flags = -1;
252 static gint ett_cops_itf = -1;
253 static gint ett_cops_reason = -1;
254 static gint ett_cops_decision = -1;
255 static gint ett_cops_error = -1;
256 static gint ett_cops_pdp = -1;
257
258 static int dissect_cops_object(tvbuff_t *tvb, guint32 offset, proto_tree *tree);
259 static int dissect_cops_object_data(tvbuff_t *tvb, guint32 offset, proto_tree *tree,
260                                     guint8 c_num, guint8 c_type, guint16 len);
261
262 /* Code to actually dissect the packets */
263 static void dissect_cops(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
264 {
265         guint8 op_code;
266
267         CHECK_DISPLAY_AS_DATA(proto_cops, tvb, pinfo, tree);
268
269         pinfo->current_proto = "COPS";
270         if (check_col(pinfo->fd, COL_PROTOCOL)) 
271                 col_set_str(pinfo->fd, COL_PROTOCOL, "COPS");
272     
273         op_code = tvb_get_guint8(tvb, 1);
274         if (check_col(pinfo->fd, COL_INFO))
275                 col_add_fstr(pinfo->fd, COL_INFO, "COPS %s",
276                              val_to_str(op_code, cops_op_code_vals, "Unknown Op Code"));
277
278         if (tree) {
279                 proto_item *ti, *tv;
280                 proto_tree *cops_tree, *ver_flags_tree;
281                 guint32 offset, msg_len;
282                 guint8 ver_flags;
283                 gint garbage;
284
285                 offset = 0;
286                 ti = proto_tree_add_item(tree, proto_cops, tvb, offset, tvb_length(tvb), FALSE);
287                 cops_tree = proto_item_add_subtree(ti, ett_cops);
288
289                 /* Version and flags share the same byte, put them in a subtree */
290                 ver_flags = tvb_get_guint8(tvb, offset);
291                 tv = proto_tree_add_uint_format(cops_tree, hf_cops_ver_flags, tvb, offset, 1,
292                                                   ver_flags, "Version: %u, Flags: %s",
293                                                   hi_nibble(ver_flags),
294                                                   val_to_str(lo_nibble(ver_flags), cops_flags_vals, "Unknown"));
295                 ver_flags_tree = proto_item_add_subtree(tv, ett_cops_ver_flags);
296                 proto_tree_add_uint(ver_flags_tree, hf_cops_version, tvb, offset, 1, ver_flags);
297                 proto_tree_add_uint(ver_flags_tree, hf_cops_flags, tvb, offset, 1, ver_flags);
298                 offset++;
299
300                 proto_tree_add_uint(cops_tree, hf_cops_op_code, tvb, offset, 1, tvb_get_guint8(tvb, offset));
301                 offset ++;
302                 proto_tree_add_uint(cops_tree, hf_cops_client_type, tvb, offset, 2, tvb_get_ntohs(tvb, offset));
303                 offset += 2;
304
305                 msg_len = tvb_get_ntohl(tvb, offset);
306                 proto_tree_add_uint(cops_tree, hf_cops_msg_len, tvb, offset, 4, tvb_get_ntohl(tvb, offset));
307                 offset += 4;
308
309                 while (msg_len >= COPS_OBJECT_HDR_SIZE) {
310                         int consumed;
311
312                         consumed = dissect_cops_object(tvb, offset, cops_tree);
313                         if (consumed == 0)
314                                 break;
315                         msg_len -= consumed;
316                         offset += consumed;
317                 }
318
319                 garbage = tvb_length_remaining(tvb, offset);
320                 if (garbage > 0)
321                         proto_tree_add_text(cops_tree, tvb, offset, garbage,
322                                             "Trailing garbage: %d byte%s", garbage,
323                                             plurality(garbage, "", "s"));
324         }
325
326         return;
327 }
328
329 static char *cops_c_type_to_str(guint8 c_num, guint8 c_type)
330 {
331         switch (c_num) {
332         case COPS_OBJ_HANDLE:
333                 if (c_type == 1)
334                         return "Client Handle";
335                 break;
336         case COPS_OBJ_IN_INT:
337         case COPS_OBJ_OUT_INT:
338                 if (c_type == 1)
339                         return "IPv4 Address + Interface";
340                 else if (c_type == 2)
341                         return "IPv6 Address + Interface";
342                 break;
343         case COPS_OBJ_DECISION:
344         case COPS_OBJ_LPDPDECISION:
345                 if (c_type == 1)
346                         return "Decision Flags (Mandatory)";
347                 else if (c_type == 2)
348                         return "Stateless Data";
349                 else if (c_type == 3)
350                         return "Replacement Data";
351                 else if (c_type == 4)
352                         return "Client Specific Decision Data";
353                 else if (c_type == 5)
354                         return "Named Decision Data";
355                 break;
356         case COPS_OBJ_CLIENTSI:
357                 if (c_type == 1)
358                         return "Signaled ClientSI";
359                 else if (c_type == 2)
360                         return "Named ClientSI";
361                 break;
362         case COPS_OBJ_KATIMER:
363                 if (c_type == 1)
364                         return "Keep-alive timer value";
365                 break;
366         case COPS_OBJ_PDPREDIRADDR:
367         case COPS_OBJ_LASTPDPADDR:
368                 if (c_type == 1)
369                         return "IPv4 Address + TCP Port";
370                 else if (c_type == 2)
371                         return "IPv6 Address + TCP Port";
372                 break;
373         case COPS_OBJ_ACCTTIMER:
374                 if (c_type == 1)
375                         return "Accounting timer value";
376                 break;
377         case COPS_OBJ_INTEGRITY:
378                 if (c_type == 1)
379                         return "HMAC digest";
380                 break;
381         }
382
383         return "";
384 }
385
386 static int dissect_cops_object(tvbuff_t *tvb, guint32 offset, proto_tree *tree)
387 {
388         guint16 object_len, contents_len;
389         guint8 c_num, c_type;
390         proto_item *ti;
391         proto_tree *obj_tree;
392         char *type_str;
393         int ret;
394
395         if (tvb_length_remaining(tvb, offset) < COPS_OBJECT_HDR_SIZE)
396                 return 0;
397
398         object_len = tvb_get_ntohs(tvb, offset);
399         c_num = tvb_get_guint8(tvb, offset + 2);
400         c_type = tvb_get_guint8(tvb, offset + 3);
401
402         ti = proto_tree_add_uint_format(tree, hf_cops_obj_c_num, tvb, offset, object_len, c_num,
403                                         "%s: %s", val_to_str(c_num, cops_c_num_vals, "Unknown"),
404                                         cops_c_type_to_str(c_num, c_type));
405         obj_tree = proto_item_add_subtree(ti, ett_cops_obj);
406
407         proto_tree_add_uint(obj_tree, hf_cops_obj_len, tvb, offset, 2, tvb_get_ntohs(tvb, offset));
408         offset += 2;
409
410         proto_tree_add_uint(obj_tree, hf_cops_obj_c_num, tvb, offset, 1, c_num);
411         offset++;
412
413         type_str = cops_c_type_to_str(c_num, c_type);
414         proto_tree_add_text(obj_tree, tvb, offset, 1, "C-Type: %s%s%u%s",
415                             type_str,
416                             strlen(type_str) ? " (" : "",
417                             c_type,
418                             strlen(type_str) ? ")" : "");
419         offset++;
420
421         contents_len = object_len - COPS_OBJECT_HDR_SIZE;
422         ret = dissect_cops_object_data(tvb, offset, obj_tree, c_num, c_type, contents_len);
423         if (ret < 0) return 0;
424
425         /* Pad to 32bit boundary */
426         if (object_len % sizeof (guint32))
427                 object_len += (sizeof (guint32) - object_len % sizeof (guint32));
428
429         return object_len;
430         
431 }
432
433 static int dissect_cops_object_data(tvbuff_t *tvb, guint32 offset, proto_tree *tree,
434                                     guint8 c_num, guint8 c_type, guint16 len)
435 {
436         proto_item *ti;
437         proto_tree *r_type_tree, *itf_tree, *reason_tree, *dec_tree, *error_tree, *pdp_tree;
438         guint16 r_type, m_type, reason, reason_sub, cmd_code, cmd_flags, error, error_sub, tcp_port;
439         guint32 ipv4addr, ifindex;
440         struct e_in6_addr ipv6addr;
441
442         switch (c_num) {
443         case COPS_OBJ_CONTEXT:
444                 r_type = tvb_get_ntohs(tvb, offset);
445                 m_type = tvb_get_ntohs(tvb, offset + 2);
446                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: R-Type: %s, M-Type: %u",
447                                          val_to_str(r_type, cops_r_type_vals, "Unknown"),
448                                          m_type);
449
450                 r_type_tree = proto_item_add_subtree(ti, ett_cops_r_type_flags);
451                 proto_tree_add_uint(r_type_tree, hf_cops_r_type_flags, tvb, offset, 2, r_type);
452                 offset += 2;
453                 proto_tree_add_uint(r_type_tree, hf_cops_m_type_flags, tvb, offset, 2, m_type);
454
455                 return 0;
456                 break;
457         case COPS_OBJ_IN_INT:
458         case COPS_OBJ_OUT_INT:
459                 if (c_type == 1) {          /* IPv4 */
460                         tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset, 4);
461                         ifindex = tvb_get_ntohl(tvb, offset + 4);
462                         ti = proto_tree_add_text(tree, tvb, offset, 8, "Contents: IPv4 address %s, ifIndex: %u",
463                                                  ip_to_str((guint8 *)&ipv4addr), ifindex);
464                         itf_tree = proto_item_add_subtree(ti, ett_cops_itf);
465                         proto_tree_add_ipv4(itf_tree,
466                                             (c_num == COPS_OBJ_IN_INT) ? hf_cops_in_int_ipv4 : hf_cops_out_int_ipv4,
467                                             tvb, offset, 4, ipv4addr);
468                         offset += 4;
469                 } else if (c_type == 2) {   /* IPv6 */
470                         tvb_memcpy(tvb, (guint8 *)&ipv6addr, offset, sizeof ipv6addr);
471                         ifindex = tvb_get_ntohl(tvb, offset + sizeof ipv6addr);
472                         ti = proto_tree_add_text(tree, tvb, offset, 20, "Contents: IPv6 address %s, ifIndex: %u",
473                                                  ip6_to_str(&ipv6addr), ifindex);
474                         itf_tree = proto_item_add_subtree(ti, ett_cops_itf);
475                         proto_tree_add_ipv6(itf_tree,
476                                             (c_num == COPS_OBJ_IN_INT) ? hf_cops_in_int_ipv6 : hf_cops_out_int_ipv6,
477                                             tvb, offset, 16, (guint8 *)&ipv6addr);
478                         offset += 16;
479                 } else {
480                         break;
481                 }
482                 proto_tree_add_uint(itf_tree, hf_cops_int_ifindex, tvb, offset, 4, ifindex);
483
484                 return 0;
485                 break;
486         case COPS_OBJ_REASON:
487                 reason = tvb_get_ntohs(tvb, offset);
488                 reason_sub = tvb_get_ntohs(tvb, offset + 2);
489                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Reason-Code: %s, Reason Sub-code: 0x%04x",
490                                          val_to_str(reason, cops_reason_vals, "<Unknown value>"), reason_sub);
491                 reason_tree = proto_item_add_subtree(ti, ett_cops_reason);
492                 proto_tree_add_uint(reason_tree, hf_cops_reason, tvb, offset, 2, reason);
493                 offset += 2;
494                 if (reason == 13) {
495                         proto_tree_add_text(reason_tree, tvb, offset, 2, "Reason Sub-code: "
496                                             "Unknown object's C-Num %u, C-Type %u",
497                                             tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset + 1));
498                 } else 
499                         proto_tree_add_uint(reason_tree, hf_cops_reason_sub, tvb, offset, 2, reason_sub);
500
501                 return 0;
502                 break;
503         case COPS_OBJ_DECISION:
504         case COPS_OBJ_LPDPDECISION:
505                 if (c_type != 1)
506                         break;
507
508                 cmd_code = tvb_get_ntohs(tvb, offset);
509                 cmd_flags = tvb_get_ntohs(tvb, offset + 2);
510                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Command-Code: %s, Flags: %s",
511                                          val_to_str(cmd_code, cops_dec_cmd_code_vals, "<Unknown value>"),
512                                          val_to_str(cmd_flags, cops_dec_cmd_flag_vals, "<Unknown flag>"));
513                 dec_tree = proto_item_add_subtree(ti, ett_cops_decision);
514                 proto_tree_add_uint(dec_tree, hf_cops_dec_cmd_code, tvb, offset, 2, cmd_code);
515                 offset += 2;
516                 proto_tree_add_uint(dec_tree, hf_cops_dec_flags, tvb, offset, 2, cmd_flags);
517                 
518                 return 0;
519                 break;
520         case COPS_OBJ_ERROR:
521                 if (c_type != 1)
522                         break;
523                 
524                 error = tvb_get_ntohs(tvb, offset);
525                 error_sub = tvb_get_ntohs(tvb, offset + 2);
526                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Error-Code: %s, Error Sub-code: 0x%04x",
527                                          val_to_str(error, cops_error_vals, "<Unknown value>"), error_sub);
528                 error_tree = proto_item_add_subtree(ti, ett_cops_error);
529                 proto_tree_add_uint(error_tree, hf_cops_error, tvb, offset, 2, error);
530                 offset += 2;
531                 if (error == 13) {
532                         proto_tree_add_text(error_tree, tvb, offset, 2, "Error Sub-code: "
533                                             "Unknown object's C-Num %u, C-Type %u",
534                                             tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset + 1));
535                 } else 
536                         proto_tree_add_uint(error_tree, hf_cops_error_sub, tvb, offset, 2, error_sub);
537
538                 return 0;
539                 break;
540         case COPS_OBJ_KATIMER:
541                 if (c_type != 1)
542                         break;
543
544                 proto_tree_add_item(tree, hf_cops_katimer, tvb, offset + 2, 2, FALSE);
545                 if (tvb_get_ntohs(tvb, offset + 2) == 0)
546                         proto_tree_add_text(tree, tvb, offset, 0, "Value of zero implies infinity.");
547                 
548                 return 0;
549                 break;
550         case COPS_OBJ_PEPID:
551                 if (c_type != 1)
552                         break;
553
554                 if (tvb_strnlen(tvb, offset, len) == -1)
555                         proto_tree_add_text(tree, tvb, offset, len, "<PEP Id is not a NUL terminated ASCII string>");
556                 else
557                         proto_tree_add_item(tree, hf_cops_pepid, tvb, offset,
558                                             tvb_strnlen(tvb, offset, len) + 1, FALSE);
559
560                 return 0;
561                 break;
562         case COPS_OBJ_REPORT_TYPE:
563                 if (c_type != 1)
564                         break;
565
566                 proto_tree_add_item(tree, hf_cops_report_type, tvb, offset, 2, FALSE);
567
568                 return 0;
569                 break;
570         case COPS_OBJ_PDPREDIRADDR:
571         case COPS_OBJ_LASTPDPADDR:
572                 if (c_type == 1) {          /* IPv4 */
573                         tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset, 4);
574                         tcp_port = tvb_get_ntohs(tvb, offset + 4 + 2);
575                         ti = proto_tree_add_text(tree, tvb, offset, 8, "Contents: IPv4 address %s, TCP Port Number: %u",
576                                                  ip_to_str((guint8 *)&ipv4addr), tcp_port);
577                         pdp_tree = proto_item_add_subtree(ti, ett_cops_pdp);
578                         proto_tree_add_ipv4(pdp_tree,
579                                             (c_num == COPS_OBJ_PDPREDIRADDR) ? hf_cops_pdprediraddr_ipv4 : hf_cops_lastpdpaddr_ipv4,
580                                             tvb, offset, 4, ipv4addr);
581                         offset += 4;
582                 } else if (c_type == 2) {   /* IPv6 */
583                         tvb_memcpy(tvb, (guint8 *)&ipv6addr, offset, sizeof ipv6addr);
584                         tcp_port = tvb_get_ntohs(tvb, offset + sizeof ipv6addr + 2);
585                         ti = proto_tree_add_text(tree, tvb, offset, 20, "Contents: IPv6 address %s, TCP Port Number: %u",
586                                                  ip6_to_str(&ipv6addr), tcp_port);
587                         pdp_tree = proto_item_add_subtree(ti, ett_cops_pdp);
588                         proto_tree_add_ipv6(pdp_tree,
589                                             (c_num == COPS_OBJ_PDPREDIRADDR) ? hf_cops_pdprediraddr_ipv6 : hf_cops_lastpdpaddr_ipv6,
590                                             tvb, offset, 16, (guint8 *)&ipv6addr);
591                         offset += 16;
592                 } else {
593                         break;
594                 }
595                 offset += 2;
596                 proto_tree_add_uint(pdp_tree, hf_cops_pdp_tcp_port, tvb, offset, 2, tcp_port);
597
598                 return 0;
599                 break;
600         case COPS_OBJ_ACCTTIMER:
601                 if (c_type != 1)
602                         break;
603
604                 proto_tree_add_item(tree, hf_cops_accttimer, tvb, offset + 2, 2, FALSE);
605                 if (tvb_get_ntohs(tvb, offset + 2) == 0)
606                         proto_tree_add_text(tree, tvb, offset, 0, "Value of zero means "
607                                             "there SHOULD be no unsolicited accounting updates.");
608
609                 return 0;
610                 break;
611         case COPS_OBJ_INTEGRITY:
612                 if (c_type != 1)
613                         break;      /* Not HMAC digest */
614
615                 proto_tree_add_item(tree, hf_cops_key_id, tvb, offset, 4, FALSE);
616                 proto_tree_add_item(tree, hf_cops_seq_num, tvb, offset + 4, 4, FALSE);
617                 proto_tree_add_text(tree, tvb, offset + 8 , len - 8, "Contents: Keyed Message Digest");
618
619                 return 0;
620                 break;
621         default:
622                 break;
623         }
624
625         ti = proto_tree_add_text(tree, tvb, offset, len, "Contents: %u bytes", len);
626
627         return 0;
628 }
629
630 /* Register the protocol with Ethereal */
631 void proto_register_cops(void)
632 {                 
633
634         /* Setup list of header fields */
635         static hf_register_info hf[] = {
636                 { &hf_cops_ver_flags,
637                         { "Version and Flags",           "cops.ver_flags",
638                         FT_UINT8, BASE_HEX, NULL, 0x0,
639                         "Version and Flags in COPS Common Header" }
640                 },
641                 { &hf_cops_version,
642                         { "Version",           "cops.version",
643                         FT_UINT8, BASE_DEC, NULL, 0xF0,
644                         "Version in COPS Common Header" }
645                 },
646                 { &hf_cops_flags,
647                         { "Flags",           "cops.flags",
648                         FT_UINT8, BASE_HEX, VALS(cops_flags_vals), 0x0F,
649                         "Flags in COPS Common Header" }
650                 },
651                 { &hf_cops_op_code,
652                         { "Op Code",           "cops.op_code",
653                         FT_UINT8, BASE_DEC, VALS(cops_op_code_vals), 0x0,
654                         "Op Code in COPS Common Header" }
655                 },
656                 { &hf_cops_client_type,
657                         { "Client Type",           "cops.client_type",
658                         FT_UINT16, BASE_DEC, NULL, 0x0,
659                         "Client Type in COPS Common Header" }
660                 },
661                 { &hf_cops_msg_len,
662                         { "Message Length",           "cops.msg_len",
663                         FT_UINT32, BASE_DEC, NULL, 0x0,
664                         "Message Length in COPS Common Header" }
665                 },
666                 { &hf_cops_obj_len,
667                         { "Object Length",           "cops.obj.len",
668                         FT_UINT32, BASE_DEC, NULL, 0x0,
669                         "Object Length in COPS Object Header" }
670                 },
671                 { &hf_cops_obj_c_num,
672                         { "C-Num",           "cops.c_num",
673                         FT_UINT8, BASE_DEC, VALS(cops_c_num_vals), 0x0,
674                         "C-Num in COPS Object Header" }
675                 },
676                 { &hf_cops_obj_c_type,
677                         { "C-Type",           "cops.c_type",
678                         FT_UINT8, BASE_DEC, NULL, 0x0,
679                         "C-Type in COPS Object Header" }
680                 },
681                 { &hf_cops_r_type_flags,
682                         { "R-Type",           "cops.context.r_type",
683                         FT_UINT16, BASE_HEX, VALS(cops_r_type_vals), 0xFFFF,
684                         "R-Type in COPS Context Object" }
685                 },
686                 { &hf_cops_m_type_flags,
687                         { "M-Type",           "cops.context.m_type",
688                         FT_UINT16, BASE_HEX, NULL, 0xFFFF,
689                         "M-Type in COPS Context Object" }
690                 },
691                 { &hf_cops_in_int_ipv4,
692                         { "IPv4 address",           "cops.in-int.ipv4",
693                         FT_IPv4, 0, NULL, 0xFFFF,
694                         "IPv4 address in COPS IN-Int object" }
695                 },
696                 { &hf_cops_in_int_ipv6,
697                         { "IPv6 address",           "cops.in-int.ipv6",
698                         FT_IPv6, 0, NULL, 0xFFFF,
699                         "IPv6 address in COPS IN-Int object" }
700                 },
701                 { &hf_cops_out_int_ipv4,
702                         { "IPv4 address",           "cops.out-int.ipv4",
703                         FT_IPv4, 0, NULL, 0xFFFF,
704                         "IPv4 address in COPS OUT-Int object" }
705                 },
706                 { &hf_cops_out_int_ipv6,
707                         { "IPv6 address",           "cops.out-int.ipv6",
708                         FT_IPv6, 0, NULL, 0xFFFF,
709                         "IPv6 address in COPS OUT-Int" }
710                 },
711                 { &hf_cops_int_ifindex,
712                         { "ifIndex",           "cops.in-out-int.ifindex",
713                         FT_UINT32, BASE_DEC, NULL, 0x0,
714                         "If SNMP is supported, corresponds to MIB-II ifIndex" } 
715                 },
716                 { &hf_cops_reason,
717                         { "Reason",           "cops.reason",
718                         FT_UINT16, BASE_DEC, VALS(cops_reason_vals), 0,
719                         "Reason in Reason object" }
720                 },
721                 { &hf_cops_reason_sub,
722                         { "Reason Sub-code",           "cops.reason_sub",
723                         FT_UINT16, BASE_HEX, NULL, 0,
724                         "Reason Sub-code in Reason object" }
725                 },
726                 { &hf_cops_dec_cmd_code,
727                         { "Command-Code",           "cops.decision.cmd",
728                         FT_UINT16, BASE_DEC, VALS(cops_dec_cmd_code_vals), 0,
729                         "Command-Code in Decision/LPDP Decision object" }
730                 },
731                 { &hf_cops_dec_flags,
732                         { "Flags",           "cops.decision.flags",
733                         FT_UINT16, BASE_HEX, VALS(cops_dec_cmd_flag_vals), 0xffff,
734                         "Flags in Decision/LPDP Decision object" }
735                 },
736                 { &hf_cops_error,
737                         { "Error",           "cops.error",
738                         FT_UINT16, BASE_DEC, VALS(cops_error_vals), 0,
739                         "Error in Error object" }
740                 },
741                 { &hf_cops_error_sub,
742                         { "Error Sub-code",           "cops.error_sub",
743                         FT_UINT16, BASE_HEX, NULL, 0,
744                         "Error Sub-code in Error object" }
745                 },
746                 { &hf_cops_katimer,
747                         { "Contents: KA Timer Value",           "cops.katimer.value",
748                         FT_UINT16, BASE_DEC, NULL, 0,
749                         "Keep-Alive Timer Value in KATimer object" }
750                 },
751                 { &hf_cops_pepid,
752                         { "Contents: PEP Id",           "cops.pepid.id",
753                         FT_STRING, BASE_NONE, NULL, 0,
754                         "PEP Id in PEPID object" }
755                 },
756                 { &hf_cops_report_type,
757                         { "Contents: Report-Type",           "cops.report_type",
758                         FT_UINT16, BASE_DEC, VALS(cops_report_type_vals), 0,
759                         "Report-Type in Report-Type object" }
760                 },
761                 { &hf_cops_pdprediraddr_ipv4,
762                         { "IPv4 address",           "cops.pdprediraddr.ipv4",
763                         FT_IPv4, 0, NULL, 0xFFFF,
764                         "IPv4 address in COPS PDPRedirAddr object" }
765                 },
766                 { &hf_cops_pdprediraddr_ipv6,
767                         { "IPv6 address",           "cops.pdprediraddr.ipv6",
768                         FT_IPv6, 0, NULL, 0xFFFF,
769                         "IPv6 address in COPS PDPRedirAddr object" }
770                 },
771                 { &hf_cops_lastpdpaddr_ipv4,
772                         { "IPv4 address",           "cops.lastpdpaddr.ipv4",
773                         FT_IPv4, 0, NULL, 0xFFFF,
774                         "IPv4 address in COPS LastPDPAddr object" }
775                 },
776                 { &hf_cops_lastpdpaddr_ipv6,
777                         { "IPv6 address",           "cops.lastpdpaddr.ipv6",
778                         FT_IPv6, 0, NULL, 0xFFFF,
779                         "IPv6 address in COPS LastPDPAddr object" }
780                 },
781                 { &hf_cops_pdp_tcp_port,
782                         { "TCP Port Number",           "cops.pdp.tcp_port",
783                         FT_UINT32, BASE_DEC, NULL, 0x0,
784                          "TCP Port Number of PDP in PDPRedirAddr/LastPDPAddr object" }
785                 },
786                 { &hf_cops_accttimer,
787                         { "Contents: ACCT Timer Value",           "cops.accttimer.value",
788                         FT_UINT16, BASE_DEC, NULL, 0,
789                         "Accounting Timer Value in AcctTimer object" }
790                 },
791                 { &hf_cops_key_id,
792                         { "Contents: Key ID",           "cops.integrity.key_id",
793                         FT_UINT32, BASE_DEC, NULL, 0,
794                         "Key ID in Integrity object" }
795                 },
796                 { &hf_cops_seq_num,
797                         { "Contents: Sequence Number",           "cops.integrity.seq_num",
798                         FT_UINT32, BASE_DEC, NULL, 0,
799                         "Sequence Number in Integrity object" }
800                 },
801         };
802
803         /* Setup protocol subtree array */
804         static gint *ett[] = {
805                 &ett_cops,
806                 &ett_cops_ver_flags,
807                 &ett_cops_obj,
808                 &ett_cops_obj_data,
809                 &ett_cops_r_type_flags,
810                 &ett_cops_itf,
811                 &ett_cops_reason,
812                 &ett_cops_decision,
813                 &ett_cops_error,
814                 &ett_cops_pdp,
815         };
816
817         /* Register the protocol name and description */
818         proto_cops = proto_register_protocol("Common Open Policy Service",
819             "COPS", "cops");
820
821         /* Required function calls to register the header fields and subtrees used */
822         proto_register_field_array(proto_cops, hf, array_length(hf));
823         proto_register_subtree_array(ett, array_length(ett));
824 };
825
826 void
827 proto_reg_handoff_cops(void)
828 {
829         dissector_add("tcp.port", TCP_PORT_COPS, dissect_cops);
830 }