SMB Quota updates
[obnox/wireshark/wip.git] / packet-cops.c
1 /* packet-cops.c
2  * Routines for the COPS (Common Open Policy Service) protocol dissection
3  * RFC2748 & COPS-PR extension RFC3084
4  *
5  * Copyright 2000, Heikki Vatiainen <hessu@cs.tut.fi>
6  *
7  * $Id: packet-cops.c,v 1.25 2002/03/12 10:40:01 guy Exp $
8  *
9  * Ethereal - Network traffic analyzer
10  * By Gerald Combs <gerald@ethereal.com>
11  * Copyright 1998 Gerald Combs
12  * 
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  * 
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  * 
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <ctype.h>
36
37 #include <glib.h>
38 #include <epan/packet.h>
39 #include "packet-ipv6.h"
40 #include "packet-frame.h"
41
42 #include "asn1.h"
43 #include "format-oid.h"
44 #include "prefs.h"
45
46 #define TCP_PORT_COPS 3288
47
48 /* Variable to hold the tcp port preference */
49 static guint global_cops_tcp_port = TCP_PORT_COPS;
50
51 /* desegmentation of COPS */
52 static gboolean cops_desegment = TRUE;
53
54 /* Variable to allow for proper deletion of dissector registration 
55  * when the user changes port from the gui
56  */
57
58 static guint cops_tcp_port = 0;
59
60 #define COPS_OBJECT_HDR_SIZE 4
61
62 /* Null string of type "guchar[]". */
63 static const guchar nullstring[] = "";
64
65 #define SAFE_STRING(s)  (((s) != NULL) ? (s) : nullstring)
66
67 /* COPS PR Tags */
68
69 #define COPS_IPA    0           /* IP Address */
70 #define COPS_U32    2           /* Unsigned 32*/
71 #define COPS_TIT    3           /* TimeTicks */
72 #define COPS_OPQ    4           /* Opaque */
73 #define COPS_I64    10          /* Integer64 */
74 #define COPS_U64    11          /* Uinteger64 */
75
76 /* COPS PR Types */
77
78 #define COPS_NULL                0
79 #define COPS_INTEGER             1    /* l  */
80 #define COPS_OCTETSTR            2    /* c  */
81 #define COPS_OBJECTID            3    /* ul */
82 #define COPS_IPADDR              4    /* uc */
83 #define COPS_UNSIGNED32          5    /* ul */
84 #define COPS_TIMETICKS           7    /* ul */
85 #define COPS_OPAQUE              8    /* c  */
86 #define COPS_INTEGER64           10   /* ll */
87 #define COPS_UNSIGNED64          11   /* ull  */
88
89
90 typedef struct _COPS_CNV COPS_CNV;
91
92 struct _COPS_CNV
93 {
94   guint class;
95   guint tag;
96   gint  syntax;
97   gchar *name;
98 };
99
100 static COPS_CNV CopsCnv [] =
101 {
102   {ASN1_UNI, ASN1_NUL, COPS_NULL,      "NULL"},
103   {ASN1_UNI, ASN1_INT, COPS_INTEGER,   "INTEGER"},
104   {ASN1_UNI, ASN1_OTS, COPS_OCTETSTR,  "OCTET STRING"},
105   {ASN1_UNI, ASN1_OJI, COPS_OBJECTID,  "OBJECTID"},
106   {ASN1_APL, COPS_IPA, COPS_IPADDR,    "IPADDR"},
107   {ASN1_APL, COPS_U32, COPS_UNSIGNED32,"UNSIGNED32"},
108   {ASN1_APL, COPS_TIT, COPS_TIMETICKS, "TIMETICKS"},
109   {ASN1_APL, COPS_OPQ, COPS_OPAQUE,    "OPAQUE"},
110   {ASN1_APL, COPS_I64, COPS_INTEGER64, "INTEGER64"},
111   {ASN1_APL, COPS_U64, COPS_UNSIGNED64, "UNSIGNED64"},
112   {0,       0,         -1,                  NULL}
113 };
114
115 static gchar *
116 cops_tag_cls2syntax ( guint tag, guint cls, gushort *syntax)
117 {
118     COPS_CNV *cnv;
119
120     cnv = CopsCnv;
121     while (cnv->syntax != -1)
122     {
123         if (cnv->tag == tag && cnv->class == cls)
124         {
125             *syntax = cnv->syntax;
126             return cnv->name;
127         }
128         cnv++;
129     }
130     return NULL;
131 }
132
133 static const value_string cops_flags_vals[] = {
134         { 0x00,          "None" },
135         { 0x01,          "Solicited Message Flag Bit" },
136         { 0, NULL },
137 };
138
139 /* The different COPS message types */
140 enum cops_op_code {
141         COPS_NO_MSG,          /* Not a COPS Message type     */ 
142
143         COPS_MSG_REQ,         /* Request (REQ)               */
144         COPS_MSG_DEC,         /* Decision (DEC)              */
145         COPS_MSG_RPT,         /* Report State (RPT)          */
146         COPS_MSG_DRQ,         /* Delete Request State (DRQ)  */
147         COPS_MSG_SSQ,         /* Synchronize State Req (SSQ) */
148         COPS_MSG_OPN,         /* Client-Open (OPN)           */
149         COPS_MSG_CAT,         /* Client-Accept (CAT)         */
150         COPS_MSG_CC,          /* Client-Close (CC)           */
151         COPS_MSG_KA,          /* Keep-Alive (KA)             */
152         COPS_MSG_SSC,         /* Synchronize Complete (SSC)  */
153
154         COPS_LAST_OP_CODE     /* For error checking          */
155 };
156
157 static const value_string cops_op_code_vals[] = {
158         { COPS_MSG_REQ,          "Request (REQ)" },
159         { COPS_MSG_DEC,          "Decision (DEC)" },
160         { COPS_MSG_RPT,          "Report State (RPT)" },
161         { COPS_MSG_DRQ,          "Delete Request State (DRQ)" },
162         { COPS_MSG_SSQ,          "Synchronize State Req (SSQ)" },
163         { COPS_MSG_OPN,          "Client-Open (OPN)" },
164         { COPS_MSG_CAT,          "Client-Accept (CAT)" },
165         { COPS_MSG_CC,           "Client-Close (CC)" },
166         { COPS_MSG_KA,           "Keep-Alive (KA)" },
167         { COPS_MSG_SSC,          "Synchronize Complete (SSC)" },
168         { 0, NULL },
169 };
170
171
172 /* The different objects in COPS messages */
173 enum cops_c_num {
174         COPS_NO_OBJECT,        /* Not a COPS Object type               */
175
176         COPS_OBJ_HANDLE,       /* Handle Object (Handle)               */
177         COPS_OBJ_CONTEXT,      /* Context Object (Context)             */
178         COPS_OBJ_IN_INT,       /* In-Interface Object (IN-Int)         */
179         COPS_OBJ_OUT_INT,      /* Out-Interface Object (OUT-Int)       */
180         COPS_OBJ_REASON,       /* Reason Object (Reason)               */
181         COPS_OBJ_DECISION,     /* Decision Object (Decision)           */
182         COPS_OBJ_LPDPDECISION, /* LPDP Decision Object (LPDPDecision)  */
183         COPS_OBJ_ERROR,        /* Error Object (Error)                 */
184         COPS_OBJ_CLIENTSI,     /* Client Specific Information Object (ClientSI) */
185         COPS_OBJ_KATIMER,      /* Keep-Alive Timer Object (KATimer)    */
186         COPS_OBJ_PEPID,        /* PEP Identification Object (PEPID)    */
187         COPS_OBJ_REPORT_TYPE,  /* Report-Type Object (Report-Type)     */
188         COPS_OBJ_PDPREDIRADDR, /* PDP Redirect Address Object (PDPRedirAddr) */
189         COPS_OBJ_LASTPDPADDR,  /* Last PDP Address (LastPDPaddr)       */
190         COPS_OBJ_ACCTTIMER,    /* Accounting Timer Object (AcctTimer)  */
191         COPS_OBJ_INTEGRITY,    /* Message Integrity Object (Integrity) */       
192         COPS_LAST_C_NUM        /* For error checking                   */
193 };
194
195 static const value_string cops_c_num_vals[] = {
196         { COPS_OBJ_HANDLE,       "Handle Object (Handle)" },
197         { COPS_OBJ_CONTEXT,      "Context Object (Context)" },
198         { COPS_OBJ_IN_INT,       "In-Interface Object (IN-Int)" },
199         { COPS_OBJ_OUT_INT,      "Out-Interface Object (OUT-Int)" },
200         { COPS_OBJ_REASON,       "Reason Object (Reason)" },
201         { COPS_OBJ_DECISION,     "Decision Object (Decision)" },
202         { COPS_OBJ_LPDPDECISION, "LPDP Decision Object (LPDPDecision)" },
203         { COPS_OBJ_ERROR,        "Error Object (Error)" },
204         { COPS_OBJ_CLIENTSI,     "Client Specific Information Object (ClientSI)" },
205         { COPS_OBJ_KATIMER,      "Keep-Alive Timer Object (KATimer)" },
206         { COPS_OBJ_PEPID,        "PEP Identification Object (PEPID)" },
207         { COPS_OBJ_REPORT_TYPE,  "Report-Type Object (Report-Type)" },
208         { COPS_OBJ_PDPREDIRADDR, "PDP Redirect Address Object (PDPRedirAddr)" },
209         { COPS_OBJ_LASTPDPADDR,  "Last PDP Address (LastPDPaddr)" },
210         { COPS_OBJ_ACCTTIMER,    "Accounting Timer Object (AcctTimer)" },
211         { COPS_OBJ_INTEGRITY,    "Message Integrity Object (Integrity)" },
212         { 0, NULL },
213 };
214
215
216 /* The different objects in COPS-PR messages */
217 enum cops_s_num {
218         COPS_NO_PR_OBJECT,     /* Not a COPS-PR Object type               */
219         COPS_OBJ_PRID,         /* Provisioning Instance Identifier (PRID) */
220         COPS_OBJ_PPRID,        /* Prefix Provisioning Instance Identifier (PPRID) */
221         COPS_OBJ_EPD,          /* Encoded Provisioning Instance Data (EPD) */
222         COPS_OBJ_GPERR,        /* Global Provisioning Error Object (GPERR) */
223         COPS_OBJ_CPERR,        /* PRC Class Provisioning Error Object (CPERR) */
224         COPS_OBJ_ERRPRID,      /* Error Provisioning Instance Identifier (ErrorPRID)*/
225         
226         COPS_LAST_S_NUM        /* For error checking                   */
227 };
228
229
230 static const value_string cops_s_num_vals[] = {
231         { COPS_OBJ_PRID,         "Provisioning Instance Identifier (PRID)" },
232         { COPS_OBJ_PPRID,        "Prefix Provisioning Instance Identifier (PPRID)" },
233         { COPS_OBJ_EPD,          "Encoded Provisioning Instance Data (EPD)" },
234         { COPS_OBJ_GPERR,        "Global Provisioning Error Object (GPERR)" },
235         { COPS_OBJ_CPERR,        "PRC Class Provisioning Error Object (CPERR)" },
236         { COPS_OBJ_ERRPRID,      "Error Provisioning Instance Identifier (ErrorPRID)" },
237         { 0, NULL },
238
239 };
240
241 /* R-Type is carried within the Context Object */
242 static const value_string cops_r_type_vals[] = {
243         { 0x01, "Incoming-Message/Admission Control request" },
244         { 0x02, "Resource-Allocation request" },
245         { 0x04, "Outgoing-Message request" },
246         { 0x08, "Configuration request" },
247         { 0, NULL },
248 };
249 /* S-Type is carried within the ClientSI Object for COPS-PR*/
250 static const value_string cops_s_type_vals[] = {
251         { 0x01, "BER" },
252         { 0, NULL },
253 };
254
255 /* Reason-Code is carried within the Reason object */
256 static const value_string cops_reason_vals[] = {
257         { 1,  "Unspecified" },
258         { 2,  "Management" },
259         { 3,  "Preempted (Another request state takes precedence)" },
260         { 4,  "Tear (Used to communicate a signaled state removal)" },
261         { 5,  "Timeout (Local state has timed-out)" },
262         { 6,  "Route Change (Change invalidates request state)" },
263         { 7,  "Insufficient Resources (No local resource available)" },
264         { 8,  "PDP's Directive (PDP decision caused the delete)" },
265         { 9,  "Unsupported decision (PDP decision not supported)" },
266         { 10, "Synchronize Handle Unknown" },
267         { 11, "Transient Handle (stateless event)" },
268         { 12, "Malformed Decision (could not recover)" },
269         { 13, "Unknown COPS Object from PDP" },
270         { 0, NULL },
271 };
272
273 /* Command-Code is carried within the Decision object if C-Type is 1 */
274 static const value_string cops_dec_cmd_code_vals[] = {
275         { 0, "NULL Decision (No configuration data available)" },
276         { 1, "Install (Admit request/Install configuration)" },
277         { 2, "Remove (Remove request/Remove configuration)" },
278         { 0, NULL },
279 };
280
281 /* Decision flags are also carried with the Decision object if C-Type is 1 */
282 static const value_string cops_dec_cmd_flag_vals[] = {
283         { 0x00, "<None set>" },
284         { 0x01, "Trigger Error (Trigger error message if set)" },
285         { 0, NULL },
286 };
287
288 /* Error-Code from Error object */
289 static const value_string cops_error_vals[] = {
290         {1,  "Bad handle" },
291         {2,  "Invalid handle reference" },
292         {3,  "Bad message format (Malformed Message)" },
293         {4,  "Unable to process (server gives up on query)" },
294         {5,  "Mandatory client-specific info missing" },
295         {6,  "Unsupported client" },
296         {7,  "Mandatory COPS object missing" },
297         {8,  "Client Failure" },
298         {9,  "Communication Failure" },
299         {10, "Unspecified" },
300         {11, "Shutting down" },
301         {12, "Redirect to Preferred Server" },
302         {13, "Unknown COPS Object" },
303         {14, "Authentication Failure" },
304         {15, "Authentication Required" },
305         {0,  NULL },
306 };
307 /* Error-Code from GPERR object */
308 static const value_string cops_gperror_vals[] = {
309         {1,  "AvailMemLow" },
310         {2,  "AvailMemExhausted" },
311         {3,  "unknownASN.1Tag" },
312         {4,  "maxMsgSizeExceeded" },
313         {5,  "unknownError" },
314         {6,  "maxRequestStatesOpen" },
315         {7,  "invalidASN.1Length" },
316         {8,  "invalidObjectPad" },
317         {9,  "unknownPIBData" },
318         {10, "unknownCOPSPRObject" },
319         {11, "malformedDecision" },
320         {0,  NULL },
321 };
322
323 /* Error-Code from CPERR object */
324 static const value_string cops_cperror_vals[] = {
325         {1,  "priSpaceExhausted" },
326         {2,  "priInstanceInvalid" },
327         {3,  "attrValueInvalid" },
328         {4,  "attrValueSupLimited" },
329         {5,  "attrEnumSupLimited" },
330         {6,  "attrMaxLengthExceeded" },
331         {7,  "attrReferenceUnknown" },
332         {8,  "priNotifyOnly" },
333         {9,  "unknownPrc" },
334         {10, "tooFewAttrs" },
335         {11, "invalidAttrType" },
336         {12, "deletedInRef" },
337         {13, "priSpecificError" },
338         {0,  NULL },     
339 };
340         
341
342 /* Report-Type from Report-Type object */
343 static const value_string cops_report_type_vals[] = {
344         {1, " Success   : Decision was successful at the PEP" },
345         {2, " Failure   : Decision could not be completed by PEP" },
346         {3, " Accounting: Accounting update for an installed state" },
347         {0, NULL },
348 };
349
350 /* Initialize the protocol and registered fields */
351 static gint proto_cops = -1;
352 static gint hf_cops_ver_flags = -1;
353 static gint hf_cops_version = -1;
354 static gint hf_cops_flags = -1;
355
356 static gint hf_cops_op_code = -1;
357 static gint hf_cops_client_type = -1;
358 static gint hf_cops_msg_len = -1;
359
360 static gint hf_cops_obj_len = -1;
361 static gint hf_cops_obj_c_num = -1;
362 static gint hf_cops_obj_c_type = -1;
363
364 static gint hf_cops_obj_s_num = -1;
365 static gint hf_cops_obj_s_type = -1;
366
367 static gint hf_cops_r_type_flags = -1;
368 static gint hf_cops_m_type_flags = -1;
369
370 static gint hf_cops_in_int_ipv4 = -1;
371 static gint hf_cops_in_int_ipv6 = -1;
372 static gint hf_cops_out_int_ipv4 = -1;
373 static gint hf_cops_out_int_ipv6 = -1;
374 static gint hf_cops_int_ifindex = -1;
375
376 static gint hf_cops_reason = -1;
377 static gint hf_cops_reason_sub = -1;
378
379 static gint hf_cops_dec_cmd_code = -1;
380 static gint hf_cops_dec_flags = -1;
381
382 static gint hf_cops_error = -1;
383 static gint hf_cops_error_sub = -1;
384
385 static gint hf_cops_gperror = -1;
386 static gint hf_cops_gperror_sub = -1;
387
388 static gint hf_cops_cperror = -1;
389 static gint hf_cops_cperror_sub = -1;
390
391 static gint hf_cops_katimer = -1;
392
393 static gint hf_cops_pepid = -1;
394
395 static gint hf_cops_report_type = -1;
396
397 static gint hf_cops_pdprediraddr_ipv4 = -1;
398 static gint hf_cops_pdprediraddr_ipv6 = -1;
399 static gint hf_cops_lastpdpaddr_ipv4 = -1;
400 static gint hf_cops_lastpdpaddr_ipv6 = -1;
401 static gint hf_cops_pdp_tcp_port = -1;
402
403 static gint hf_cops_accttimer = -1;
404
405 static gint hf_cops_key_id = -1;
406 static gint hf_cops_seq_num = -1;
407
408 /* Initialize the subtree pointers */
409 static gint ett_cops = -1;
410 static gint ett_cops_ver_flags = -1;
411 static gint ett_cops_obj = -1;
412 static gint ett_cops_pr_obj = -1;
413 static gint ett_cops_obj_data = -1;
414 static gint ett_cops_r_type_flags = -1;
415 static gint ett_cops_itf = -1;
416 static gint ett_cops_reason = -1;
417 static gint ett_cops_decision = -1;
418 static gint ett_cops_error = -1;
419 static gint ett_cops_gperror = -1;
420 static gint ett_cops_cperror = -1;
421 static gint ett_cops_pdp = -1;
422
423 void proto_reg_handoff_cops(void);
424
425 static void dissect_cops_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
426
427 static int dissect_cops_object(tvbuff_t *tvb, guint32 offset, proto_tree *tree);
428 static int dissect_cops_object_data(tvbuff_t *tvb, guint32 offset, proto_tree *tree,
429                                     guint8 c_num, guint8 c_type, guint16 len);
430
431 static int dissect_cops_pr_objects(tvbuff_t *tvb, guint32 offset, proto_tree *tree, guint16 pr_len);
432 static int dissect_cops_pr_object_data(tvbuff_t *tvb, guint32 offset, proto_tree *tree,
433                                        guint8 s_num, guint8 s_type, guint16 len);
434
435 /* Code to actually dissect the packets */
436 static void
437 dissect_cops(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
438 {
439         volatile int offset = 0;
440         int length_remaining;
441         guint32 msg_len;
442         int length;
443         tvbuff_t *next_tvb;
444
445         while (tvb_reported_length_remaining(tvb, offset) != 0) {
446                 length_remaining = tvb_length_remaining(tvb, offset);
447                 if (length_remaining == -1)
448                         THROW(BoundsError);
449
450                 /*
451                  * Can we do reassembly?
452                  */
453                 if (cops_desegment && pinfo->can_desegment) {
454                         /*
455                          * Yes - is the COPS header split across segment
456                          * boundaries?
457                          */
458                         if (length_remaining < 8) {
459                                 /*
460                                  * Yes.  Tell the TCP dissector where
461                                  * the data for this message starts in
462                                  * the data it handed us, and how many
463                                  * more bytes we need, and return.
464                                  */
465                                 pinfo->desegment_offset = offset;
466                                 pinfo->desegment_len = 8 - length_remaining;
467                                 return;
468                         }
469                 }
470
471                 /*
472                  * Get the length of the COPS message.
473                  */
474                 msg_len = tvb_get_ntohl(tvb, offset + 4);
475
476                 /*
477                  * Can we do reassembly?
478                  */
479                 if (cops_desegment && pinfo->can_desegment) {
480                         /*
481                          * Yes - is the DNS packet split across segment
482                          * boundaries?
483                          */
484                         if ((guint32)length_remaining < msg_len) {
485                                 /*
486                                  * Yes.  Tell the TCP dissector where
487                                  * the data for this message starts in
488                                  * the data it handed us, and how many
489                                  * more bytes we need, and return.
490                                  */
491                                 pinfo->desegment_offset = offset;
492                                 pinfo->desegment_len =
493                                     msg_len - length_remaining;
494                                 return;
495                         }
496                 }
497
498                 /*
499                  * Construct a tvbuff containing the amount of the payload
500                  * we have available.  Make its reported length the
501                  * amount of data in the COPS packet.
502                  *
503                  * XXX - if reassembly isn't enabled. the subdissector
504                  * will throw a BoundsError exception, rather than a
505                  * ReportedBoundsError exception.  We really want
506                  * a tvbuff where the length is "length", the reported
507                  * length is "plen + 2", and the "if the snapshot length
508                  * were infinite" length were the minimum of the
509                  * reported length of the tvbuff handed to us and "plen+2",
510                  * with a new type of exception thrown if the offset is
511                  * within the reported length but beyond that third length,
512                  * with that exception getting the "Unreassembled Packet"
513                  * error.
514                  */
515                 length = length_remaining;
516                 if ((guint32)length > msg_len)
517                         length = msg_len;
518                 next_tvb = tvb_new_subset(tvb, offset, length, msg_len);
519
520                 /*
521                  * Dissect the COPS packet.
522                  *
523                  * Catch the ReportedBoundsError exception; if this
524                  * particular message happens to get a ReportedBoundsError
525                  * exception, that doesn't mean that we should stop
526                  * dissecting COPS messages within this frame or chunk
527                  * of reassembled data.
528                  *
529                  * If it gets a BoundsError, we can stop, as there's nothing
530                  * more to see, so we just re-throw it.
531                  */
532                 TRY {
533                         dissect_cops_pdu(next_tvb, pinfo, tree);
534                 }
535                 CATCH(BoundsError) {
536                         RETHROW;
537                 }
538                 CATCH(ReportedBoundsError) {
539                         show_reported_bounds_error(tvb, pinfo, tree);
540                 }
541                 ENDTRY;
542
543                 /*
544                  * Skip the COPS packet.
545                  */
546                 offset += msg_len;
547         }
548 }
549
550 static void
551 dissect_cops_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
552 {
553         guint8 op_code;
554
555         if (check_col(pinfo->cinfo, COL_PROTOCOL)) 
556                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "COPS");
557         if (check_col(pinfo->cinfo, COL_INFO)) 
558                 col_clear(pinfo->cinfo, COL_INFO);
559     
560         op_code = tvb_get_guint8(tvb, 1);
561         if (check_col(pinfo->cinfo, COL_INFO))
562                 col_add_fstr(pinfo->cinfo, COL_INFO, "COPS %s",
563                              val_to_str(op_code, cops_op_code_vals, "Unknown Op Code"));
564
565         if (tree) {
566                 proto_item *ti, *tv;
567                 proto_tree *cops_tree, *ver_flags_tree;
568                 guint32 msg_len;
569                 guint32 offset = 0;
570                 guint8 ver_flags;
571                 gint garbage;
572
573                 ti = proto_tree_add_item(tree, proto_cops, tvb, offset, -1, FALSE);
574                 cops_tree = proto_item_add_subtree(ti, ett_cops);
575
576                 /* Version and flags share the same byte, put them in a subtree */
577                 ver_flags = tvb_get_guint8(tvb, offset);
578                 tv = proto_tree_add_uint_format(cops_tree, hf_cops_ver_flags, tvb, offset, 1,
579                                                   ver_flags, "Version: %u, Flags: %s",
580                                                   hi_nibble(ver_flags),
581                                                   val_to_str(lo_nibble(ver_flags), cops_flags_vals, "Unknown"));
582                 ver_flags_tree = proto_item_add_subtree(tv, ett_cops_ver_flags);
583                 proto_tree_add_uint(ver_flags_tree, hf_cops_version, tvb, offset, 1, ver_flags);
584                 proto_tree_add_uint(ver_flags_tree, hf_cops_flags, tvb, offset, 1, ver_flags);
585                 offset++;
586
587                 proto_tree_add_uint(cops_tree, hf_cops_op_code, tvb, offset, 1, tvb_get_guint8(tvb, offset));
588                 offset ++;
589                 proto_tree_add_uint(cops_tree, hf_cops_client_type, tvb, offset, 2, tvb_get_ntohs(tvb, offset));
590                 offset += 2;
591
592                 msg_len = tvb_get_ntohl(tvb, offset);
593                 proto_tree_add_uint(cops_tree, hf_cops_msg_len, tvb, offset, 4, tvb_get_ntohl(tvb, offset));
594                 offset += 4;
595
596                 while (tvb_reported_length_remaining(tvb, offset) >= COPS_OBJECT_HDR_SIZE)
597                         offset += dissect_cops_object(tvb, offset, cops_tree);
598
599                 garbage = tvb_length_remaining(tvb, offset);
600                 if (garbage > 0)
601                         proto_tree_add_text(cops_tree, tvb, offset, garbage,
602                                             "Trailing garbage: %d byte%s", garbage,
603                                             plurality(garbage, "", "s"));
604         }
605
606         return;
607 }
608
609 static char *cops_c_type_to_str(guint8 c_num, guint8 c_type)
610 {
611         switch (c_num) {
612         case COPS_OBJ_HANDLE:
613                 if (c_type == 1)
614                         return "Client Handle";
615                 break;
616         case COPS_OBJ_IN_INT:
617         case COPS_OBJ_OUT_INT:
618                 if (c_type == 1)
619                         return "IPv4 Address + Interface";
620                 else if (c_type == 2)
621                         return "IPv6 Address + Interface";
622                 break;
623         case COPS_OBJ_DECISION:
624         case COPS_OBJ_LPDPDECISION:
625                 if (c_type == 1)
626                         return "Decision Flags (Mandatory)";
627                 else if (c_type == 2)
628                         return "Stateless Data";
629                 else if (c_type == 3)
630                         return "Replacement Data";
631                 else if (c_type == 4)
632                         return "Client Specific Decision Data";
633                 else if (c_type == 5)
634                         return "Named Decision Data";
635                 break;
636         case COPS_OBJ_CLIENTSI:
637                 if (c_type == 1)
638                         return "Signaled ClientSI";
639                 else if (c_type == 2)
640                         return "Named ClientSI";
641                 break;
642         case COPS_OBJ_KATIMER:
643                 if (c_type == 1)
644                         return "Keep-alive timer value";
645                 break;
646         case COPS_OBJ_PDPREDIRADDR:
647         case COPS_OBJ_LASTPDPADDR:
648                 if (c_type == 1)
649                         return "IPv4 Address + TCP Port";
650                 else if (c_type == 2)
651                         return "IPv6 Address + TCP Port";
652                 break;
653         case COPS_OBJ_ACCTTIMER:
654                 if (c_type == 1)
655                         return "Accounting timer value";
656                 break;
657         case COPS_OBJ_INTEGRITY:
658                 if (c_type == 1)
659                         return "HMAC digest";
660                 break;
661         }
662
663         return "";
664 }
665
666 static int dissect_cops_object(tvbuff_t *tvb, guint32 offset, proto_tree *tree)
667 {
668         guint16 object_len, contents_len;
669         guint8 c_num, c_type;
670         proto_item *ti;
671         proto_tree *obj_tree;
672         char *type_str;
673         int ret;
674
675         object_len = tvb_get_ntohs(tvb, offset);
676         c_num = tvb_get_guint8(tvb, offset + 2);
677         c_type = tvb_get_guint8(tvb, offset + 3);
678
679         ti = proto_tree_add_uint_format(tree, hf_cops_obj_c_num, tvb, offset, object_len, c_num,
680                                         "%s: %s", val_to_str(c_num, cops_c_num_vals, "Unknown"),
681                                         cops_c_type_to_str(c_num, c_type));
682         obj_tree = proto_item_add_subtree(ti, ett_cops_obj);
683
684         proto_tree_add_uint(obj_tree, hf_cops_obj_len, tvb, offset, 2, tvb_get_ntohs(tvb, offset));
685         offset += 2;
686
687         proto_tree_add_uint(obj_tree, hf_cops_obj_c_num, tvb, offset, 1, c_num);
688         offset++;
689
690         type_str = cops_c_type_to_str(c_num, c_type);
691         proto_tree_add_text(obj_tree, tvb, offset, 1, "C-Type: %s%s%u%s",
692                             type_str,
693                             strlen(type_str) ? " (" : "",
694                             c_type,
695                             strlen(type_str) ? ")" : "");
696         offset++;
697
698         contents_len = object_len - COPS_OBJECT_HDR_SIZE;
699         ret = dissect_cops_object_data(tvb, offset, obj_tree, c_num, c_type, contents_len);
700         if (ret < 0) return 0;
701
702         /* Pad to 32bit boundary */
703         if (object_len % sizeof (guint32))
704                 object_len += (sizeof (guint32) - object_len % sizeof (guint32));
705         
706         return object_len;        
707 }
708
709 static int dissect_cops_pr_objects(tvbuff_t *tvb, guint32 offset, proto_tree *tree, guint16 pr_len)
710 {
711         guint16 object_len, contents_len;
712         guint8 s_num, s_type;
713         char *type_str;
714         int ret;
715         proto_tree *cops_pr_tree, *obj_tree;
716         proto_item *ti;
717
718         cops_pr_tree = proto_item_add_subtree(tree, ett_cops_pr_obj);
719         
720         while (pr_len >= COPS_OBJECT_HDR_SIZE) { 
721                 object_len = tvb_get_ntohs(tvb, offset);
722                 s_num = tvb_get_guint8(tvb, offset + 2);
723                 s_type = tvb_get_guint8(tvb, offset + 3);
724
725                 ti = proto_tree_add_uint_format(cops_pr_tree, hf_cops_obj_s_num, tvb, offset, object_len, s_num,
726                                         "%s", val_to_str(s_num, cops_s_num_vals, "Unknown"));
727                 obj_tree = proto_item_add_subtree(cops_pr_tree, ett_cops_pr_obj);
728
729                 proto_tree_add_uint(obj_tree, hf_cops_obj_len, tvb, offset, 2, tvb_get_ntohs(tvb, offset));
730                 offset += 2;
731                 pr_len -= 2;
732  
733                 proto_tree_add_uint(obj_tree, hf_cops_obj_s_num, tvb, offset, 1, s_num);
734                 offset++;
735                 pr_len--;
736
737                 type_str = val_to_str(s_type, cops_s_type_vals, "Unknown");
738                 proto_tree_add_text(obj_tree, tvb, offset, 1, "S-Type: %s%s%u%s",
739                             type_str,
740                             strlen(type_str) ? " (" : "",
741                             s_type,
742                             strlen(type_str) ? ")" : "");
743                 offset++;
744                 pr_len--;
745
746                 contents_len = object_len - COPS_OBJECT_HDR_SIZE;
747                 ret = dissect_cops_pr_object_data(tvb, offset, obj_tree, s_num, s_type, contents_len);
748                 if (ret < 0)
749                         break;
750
751                 /*Pad to 32bit boundary */
752                 if (object_len % sizeof (guint32))
753                         object_len += (sizeof (guint32) - object_len % sizeof (guint32));
754            
755                 pr_len -= object_len - COPS_OBJECT_HDR_SIZE;
756                 offset += object_len - COPS_OBJECT_HDR_SIZE;
757         }
758
759         return 0;
760 }
761
762 static int dissect_cops_object_data(tvbuff_t *tvb, guint32 offset, proto_tree *tree,
763                                     guint8 c_num, guint8 c_type, guint16 len)
764 {
765         proto_item *ti;
766         proto_tree *r_type_tree, *itf_tree, *reason_tree, *dec_tree, *error_tree, *pdp_tree;
767         guint16 r_type, m_type, reason, reason_sub, cmd_code, cmd_flags, error, error_sub, tcp_port;
768         guint32 ipv4addr, ifindex;
769         struct e_in6_addr ipv6addr;
770
771         switch (c_num) {
772         case COPS_OBJ_CONTEXT:
773                 r_type = tvb_get_ntohs(tvb, offset);
774                 m_type = tvb_get_ntohs(tvb, offset + 2);
775                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: R-Type: %s, M-Type: %u",
776                                          val_to_str(r_type, cops_r_type_vals, "Unknown"),
777                                          m_type);
778
779                 r_type_tree = proto_item_add_subtree(ti, ett_cops_r_type_flags);
780                 proto_tree_add_uint(r_type_tree, hf_cops_r_type_flags, tvb, offset, 2, r_type);
781                 offset += 2;
782                 proto_tree_add_uint(r_type_tree, hf_cops_m_type_flags, tvb, offset, 2, m_type);
783
784                 return 0;
785                 break;
786         case COPS_OBJ_IN_INT:
787         case COPS_OBJ_OUT_INT:
788                 if (c_type == 1) {          /* IPv4 */
789                         tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset, 4);
790                         ifindex = tvb_get_ntohl(tvb, offset + 4);
791                         ti = proto_tree_add_text(tree, tvb, offset, 8, "Contents: IPv4 address %s, ifIndex: %u",
792                                                  ip_to_str((guint8 *)&ipv4addr), ifindex);
793                         itf_tree = proto_item_add_subtree(ti, ett_cops_itf);
794                         proto_tree_add_ipv4(itf_tree,
795                                             (c_num == COPS_OBJ_IN_INT) ? hf_cops_in_int_ipv4 : hf_cops_out_int_ipv4,
796                                             tvb, offset, 4, ipv4addr);
797                         offset += 4;
798                 } else if (c_type == 2) {   /* IPv6 */
799                         tvb_memcpy(tvb, (guint8 *)&ipv6addr, offset, sizeof ipv6addr);
800                         ifindex = tvb_get_ntohl(tvb, offset + sizeof ipv6addr);
801                         ti = proto_tree_add_text(tree, tvb, offset, 20, "Contents: IPv6 address %s, ifIndex: %u",
802                                                  ip6_to_str(&ipv6addr), ifindex);
803                         itf_tree = proto_item_add_subtree(ti, ett_cops_itf);
804                         proto_tree_add_ipv6(itf_tree,
805                                             (c_num == COPS_OBJ_IN_INT) ? hf_cops_in_int_ipv6 : hf_cops_out_int_ipv6,
806                                             tvb, offset, 16, (guint8 *)&ipv6addr);
807                         offset += 16;
808                 } else {
809                         break;
810                 }
811                 proto_tree_add_uint(itf_tree, hf_cops_int_ifindex, tvb, offset, 4, ifindex);
812
813                 return 0;
814                 break;
815         case COPS_OBJ_REASON:
816                 reason = tvb_get_ntohs(tvb, offset);
817                 reason_sub = tvb_get_ntohs(tvb, offset + 2);
818                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Reason-Code: %s, Reason Sub-code: 0x%04x",
819                                          val_to_str(reason, cops_reason_vals, "<Unknown value>"), reason_sub);
820                 reason_tree = proto_item_add_subtree(ti, ett_cops_reason);
821                 proto_tree_add_uint(reason_tree, hf_cops_reason, tvb, offset, 2, reason);
822                 offset += 2;
823                 if (reason == 13) {
824                         proto_tree_add_text(reason_tree, tvb, offset, 2, "Reason Sub-code: "
825                                             "Unknown object's C-Num %u, C-Type %u",
826                                             tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset + 1));
827                 } else 
828                         proto_tree_add_uint(reason_tree, hf_cops_reason_sub, tvb, offset, 2, reason_sub);
829
830                 return 0;
831                 break;
832         case COPS_OBJ_DECISION:
833         case COPS_OBJ_LPDPDECISION:
834                 if (c_type == 1) {
835                         cmd_code = tvb_get_ntohs(tvb, offset);
836                         cmd_flags = tvb_get_ntohs(tvb, offset + 2);
837                         ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Command-Code: %s, Flags: %s",
838                                          val_to_str(cmd_code, cops_dec_cmd_code_vals, "<Unknown value>"),
839                                          val_to_str(cmd_flags, cops_dec_cmd_flag_vals, "<Unknown flag>"));
840                         dec_tree = proto_item_add_subtree(ti, ett_cops_decision);
841                         proto_tree_add_uint(dec_tree, hf_cops_dec_cmd_code, tvb, offset, 2, cmd_code);
842                         offset += 2;
843                         proto_tree_add_uint(dec_tree, hf_cops_dec_flags, tvb, offset, 2, cmd_flags);
844                 } else if (c_type == 5) { /*COPS-PR Data*/
845                         ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: %u bytes", len);
846                         dissect_cops_pr_objects(tvb, offset, ti, len);
847                 } else 
848                         break;
849
850                 return 0;
851                 break;
852         case COPS_OBJ_ERROR:
853                 if (c_type != 1)
854                         break;
855                 
856                 error = tvb_get_ntohs(tvb, offset);
857                 error_sub = tvb_get_ntohs(tvb, offset + 2);
858                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Error-Code: %s, Error Sub-code: 0x%04x",
859                                          val_to_str(error, cops_error_vals, "<Unknown value>"), error_sub);
860                 error_tree = proto_item_add_subtree(ti, ett_cops_error);
861                 proto_tree_add_uint(error_tree, hf_cops_error, tvb, offset, 2, error);
862                 offset += 2;
863                 if (error == 13) {
864                         proto_tree_add_text(error_tree, tvb, offset, 2, "Error Sub-code: "
865                                             "Unknown object's C-Num %u, C-Type %u",
866                                             tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset + 1));
867                 } else 
868                         proto_tree_add_uint(error_tree, hf_cops_error_sub, tvb, offset, 2, error_sub);
869
870                 return 0;
871                 break;
872         case COPS_OBJ_CLIENTSI:
873           
874                 if (c_type != 2) /*Not COPS-PR data*/
875                       break;
876
877                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: %u bytes", len);
878
879                 dissect_cops_pr_objects(tvb, offset, ti, len);
880
881                 return 0;
882                 break;
883         case COPS_OBJ_KATIMER:
884                 if (c_type != 1)
885                         break;
886
887                 proto_tree_add_item(tree, hf_cops_katimer, tvb, offset + 2, 2, FALSE);
888                 if (tvb_get_ntohs(tvb, offset + 2) == 0)
889                         proto_tree_add_text(tree, tvb, offset, 0, "Value of zero implies infinity.");
890                 
891                 return 0;
892                 break;
893         case COPS_OBJ_PEPID:
894                 if (c_type != 1)
895                         break;
896
897                 if (tvb_strnlen(tvb, offset, len) == -1)
898                         proto_tree_add_text(tree, tvb, offset, len, "<PEP Id is not a NUL terminated ASCII string>");
899                 else
900                         proto_tree_add_item(tree, hf_cops_pepid, tvb, offset,
901                                             tvb_strnlen(tvb, offset, len) + 1, FALSE);
902
903                 return 0;
904                 break;
905         case COPS_OBJ_REPORT_TYPE:
906                 if (c_type != 1)
907                         break;
908
909                 proto_tree_add_item(tree, hf_cops_report_type, tvb, offset, 2, FALSE);
910
911                 return 0;
912                 break;
913         case COPS_OBJ_PDPREDIRADDR:
914         case COPS_OBJ_LASTPDPADDR:
915                 if (c_type == 1) {          /* IPv4 */
916                         tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset, 4);
917                         tcp_port = tvb_get_ntohs(tvb, offset + 4 + 2);
918                         ti = proto_tree_add_text(tree, tvb, offset, 8, "Contents: IPv4 address %s, TCP Port Number: %u",
919                                                  ip_to_str((guint8 *)&ipv4addr), tcp_port);
920                         pdp_tree = proto_item_add_subtree(ti, ett_cops_pdp);
921                         proto_tree_add_ipv4(pdp_tree,
922                                             (c_num == COPS_OBJ_PDPREDIRADDR) ? hf_cops_pdprediraddr_ipv4 : hf_cops_lastpdpaddr_ipv4,
923                                             tvb, offset, 4, ipv4addr);
924                         offset += 4;
925                 } else if (c_type == 2) {   /* IPv6 */
926                         tvb_memcpy(tvb, (guint8 *)&ipv6addr, offset, sizeof ipv6addr);
927                         tcp_port = tvb_get_ntohs(tvb, offset + sizeof ipv6addr + 2);
928                         ti = proto_tree_add_text(tree, tvb, offset, 20, "Contents: IPv6 address %s, TCP Port Number: %u",
929                                                  ip6_to_str(&ipv6addr), tcp_port);
930                         pdp_tree = proto_item_add_subtree(ti, ett_cops_pdp);
931                         proto_tree_add_ipv6(pdp_tree,
932                                             (c_num == COPS_OBJ_PDPREDIRADDR) ? hf_cops_pdprediraddr_ipv6 : hf_cops_lastpdpaddr_ipv6,
933                                             tvb, offset, 16, (guint8 *)&ipv6addr);
934                         offset += 16;
935                 } else {
936                         break;
937                 }
938                 offset += 2;
939                 proto_tree_add_uint(pdp_tree, hf_cops_pdp_tcp_port, tvb, offset, 2, tcp_port);
940
941                 return 0;
942                 break;
943         case COPS_OBJ_ACCTTIMER:
944                 if (c_type != 1)
945                         break;
946
947                 proto_tree_add_item(tree, hf_cops_accttimer, tvb, offset + 2, 2, FALSE);
948                 if (tvb_get_ntohs(tvb, offset + 2) == 0)
949                         proto_tree_add_text(tree, tvb, offset, 0, "Value of zero means "
950                                             "there SHOULD be no unsolicited accounting updates.");
951
952                 return 0;
953                 break;
954         case COPS_OBJ_INTEGRITY:
955                 if (c_type != 1)
956                         break;      /* Not HMAC digest */
957
958                 proto_tree_add_item(tree, hf_cops_key_id, tvb, offset, 4, FALSE);
959                 proto_tree_add_item(tree, hf_cops_seq_num, tvb, offset + 4, 4, FALSE);
960                 proto_tree_add_text(tree, tvb, offset + 8 , len - 8, "Contents: Keyed Message Digest");
961
962                 return 0;
963                 break;
964
965         default:
966                 break;
967         }
968
969         ti = proto_tree_add_text(tree, tvb, offset, len, "Contents: %u bytes", len);
970
971         return 0;
972 }
973
974 static int decode_cops_pr_asn1_data(tvbuff_t *tvb, guint32 offset, proto_tree *tree, guint epdlen)
975 {
976         ASN1_SCK asn1; 
977         int start;
978         gboolean def;
979         guint length;
980
981         guint vb_length;
982         gushort vb_type;
983         gchar *vb_type_name;
984
985         int ret;
986         guint cls, con, tag;
987
988         subid_t *variable_oid;
989         guint variable_oid_length;
990
991         gint32 vb_integer_value;
992         guint32 vb_uinteger_value;
993
994         guint8 *vb_octet_string;
995
996         subid_t *vb_oid;
997         guint vb_oid_length;
998
999         gchar *vb_display_string;
1000
1001         guint variable_length;
1002
1003         unsigned int i;
1004         gchar *buf;
1005         int len;
1006
1007         while (epdlen > 0){ /*while there is stuff to be decoded*/
1008
1009         asn1_open(&asn1, tvb, offset);
1010
1011
1012         /* parse the type of the object */
1013
1014         start = asn1.offset;
1015
1016         ret = asn1_header_decode (&asn1, &cls, &con, &tag, &def, &vb_length);
1017         if (ret != ASN1_ERR_NOERROR)
1018           return 0;
1019         if (!def)
1020           return ASN1_ERR_LENGTH_NOT_DEFINITE;
1021
1022         /* Convert the class, constructed flag, and tag to a type. */
1023         vb_type_name = cops_tag_cls2syntax(tag, cls, &vb_type);
1024         if (vb_type_name == NULL) {
1025                 /*
1026                  * Unsupported type.
1027                  * Dissect the value as an opaque string of octets.
1028                  */
1029                 vb_type_name = "unsupported type";
1030                 vb_type = COPS_OPAQUE;
1031         }
1032
1033         /* parse the value */
1034
1035         switch (vb_type) {
1036
1037         case  COPS_INTEGER : 
1038                 ret = asn1_int32_value_decode(&asn1, vb_length,
1039                     &vb_integer_value);
1040                 if (ret != ASN1_ERR_NOERROR)
1041                         return ret;
1042                 length = asn1.offset - start;
1043                 if (tree) {
1044                         proto_tree_add_text(tree, asn1.tvb, offset, length,
1045                             "Value: %s: %d (%#x)", vb_type_name,
1046                             vb_integer_value, vb_integer_value);
1047                 }
1048                 break;
1049
1050
1051         case COPS_UNSIGNED32:
1052         case COPS_TIMETICKS:
1053                 ret = asn1_uint32_value_decode(&asn1, vb_length,
1054                     &vb_uinteger_value);
1055                 if (ret != ASN1_ERR_NOERROR)
1056                         return ret;
1057                 length = asn1.offset - start;
1058                 if (tree) {
1059                         proto_tree_add_text(tree, asn1.tvb, offset, length,
1060                             "Value: %s: %u (%#x)", vb_type_name,
1061                             vb_uinteger_value, vb_uinteger_value);
1062                 }
1063                 break;
1064
1065         case COPS_OCTETSTR:
1066         case COPS_IPADDR:
1067         case COPS_OPAQUE:
1068         case COPS_UNSIGNED64:
1069         case COPS_INTEGER64:
1070                 ret = asn1_string_value_decode (&asn1, vb_length,
1071                     &vb_octet_string);
1072                 if (ret != ASN1_ERR_NOERROR)
1073                         return ret;
1074                 length = asn1.offset - start;
1075                 if (tree) {
1076                         /*
1077                          * If some characters are not printable, display
1078                          * the string as bytes.
1079                          */
1080                         for (i = 0; i < vb_length; i++) {
1081                                 if (!(isprint(vb_octet_string[i])
1082                                     || isspace(vb_octet_string[i])))
1083                                         break;
1084                         }
1085                         if (i < vb_length) {
1086                                 /*
1087                                  * We stopped, due to a non-printable
1088                                  * character, before we got to the end
1089                                  * of the string.
1090                                  */
1091                                 vb_display_string = g_malloc(4*vb_length);
1092                                 buf = &vb_display_string[0];
1093                                 len = sprintf(buf, "%03u", vb_octet_string[0]);
1094                                 buf += len;
1095                                 for (i = 1; i < vb_length; i++) {
1096                                         len = sprintf(buf, ".%03u",
1097                                             vb_octet_string[i]);
1098                                         buf += len;
1099                                 }
1100                                 proto_tree_add_text(tree, asn1.tvb, offset, length,
1101                                     "Value: %s: %s", vb_type_name,
1102                                     vb_display_string);
1103                                 g_free(vb_display_string);
1104                         } else {
1105                                 proto_tree_add_text(tree, asn1.tvb, offset, length,
1106                                     "Value: %s: %.*s", vb_type_name,
1107                                     (int)vb_length,
1108                                     SAFE_STRING(vb_octet_string));
1109                         }
1110                 }
1111                 g_free(vb_octet_string);
1112                 break;
1113
1114         case COPS_NULL:
1115                 ret = asn1_null_decode (&asn1, vb_length);
1116                 if (ret != ASN1_ERR_NOERROR)
1117                         return ret;
1118                 length = asn1.offset - start;
1119                 if (tree) {
1120                         proto_tree_add_text(tree, asn1.tvb, offset, length,
1121                             "Value: %s", vb_type_name);
1122                 }
1123                 break;
1124
1125         case COPS_OBJECTID:
1126                 ret = asn1_oid_value_decode (&asn1, vb_length, &vb_oid,
1127                     &vb_oid_length);
1128                 if (ret != ASN1_ERR_NOERROR)
1129                         return ret;
1130                 length = asn1.offset - start;
1131
1132                 if (tree) {
1133                         vb_display_string = format_oid(vb_oid, vb_oid_length);
1134                         proto_tree_add_text(tree, asn1.tvb, offset, length,
1135                             "Value: %s: %s", vb_type_name, vb_display_string);
1136                         g_free(vb_display_string);
1137                 }
1138
1139 #ifdef OLD
1140
1141                 if (tree) {
1142 #ifdef HAVE_SPRINT_VALUE
1143                         if (!unsafe) {
1144
1145                    
1146                                 variable.val.objid = vb_oid;
1147                                 vb_display_string = format_var(&variable,
1148                                     variable_oid, variable_oid_length, vb_type,
1149                                     vb_length);
1150                                 proto_tree_add_text(tree, asn1.tvb, offset,
1151                                     length,
1152                                     "Value: %s", vb_display_string);
1153                                 break;  /* we added formatted version to the tree */
1154                         }
1155 #endif /* HAVE_SPRINT_VALUE */
1156
1157                         vb_display_string = format_oid(vb_oid, vb_oid_length);
1158                         proto_tree_add_text(tree, asn1.tvb, offset, length,
1159                             "Value: %s: %s", vb_type_name, vb_display_string);
1160                         g_free(vb_display_string);
1161                 }
1162 #endif
1163
1164
1165                 g_free(vb_oid);
1166                 break;
1167
1168         default:
1169                 g_assert_not_reached();
1170                 return ASN1_ERR_WRONG_TYPE;
1171         }
1172   
1173         asn1_close(&asn1,&offset);
1174  
1175         epdlen -= length;
1176         }
1177         return 0;
1178 }
1179
1180 static int dissect_cops_pr_object_data(tvbuff_t *tvb, guint32 offset, proto_tree *tree,
1181                                     guint8 s_num, guint8 s_type, guint16 len)
1182 {
1183         proto_item *ti;
1184         proto_tree *gperror_tree, *cperror_tree;
1185         guint16 gperror=0, gperror_sub=0, cperror=0, cperror_sub=0;
1186
1187         switch (s_num){
1188         case COPS_OBJ_PRID:
1189                if (s_type != 1) /* Not Provisioning Instance Identifier (PRID) */
1190                         break; 
1191
1192                 ti=proto_tree_add_text(tree, tvb, offset, len, "Contents:");
1193
1194                 decode_cops_pr_asn1_data(tvb, offset, ti, len);
1195
1196                 return 0;
1197
1198                 break;
1199         case COPS_OBJ_PPRID: 
1200                 if (s_type != 1) /* Not Prefix Provisioning Instance Identifier (PPRID) */
1201                         break; 
1202
1203                 ti = proto_tree_add_text(tree, tvb, offset, len, "Contents:");
1204
1205                 decode_cops_pr_asn1_data(tvb, offset, ti, len);
1206
1207                 return 0;               
1208                 break;
1209         case COPS_OBJ_EPD:
1210                 if (s_type != 1) /* Not  Encoded Provisioning Instance Data (EPD) */
1211                         break; 
1212
1213                 ti = proto_tree_add_text(tree, tvb, offset, len, "Contents:");
1214
1215                 decode_cops_pr_asn1_data(tvb, offset, ti, len);
1216                         
1217                 return 0;
1218                 break;
1219         case COPS_OBJ_GPERR:
1220                 if (s_type != 1) /* Not Global Provisioning Error Object (GPERR) */
1221                         break;
1222                 
1223                 gperror = tvb_get_ntohs(tvb, offset);
1224                 gperror_sub = tvb_get_ntohs(tvb, offset + 2);
1225                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Error-Code: %s, Error Sub-code: 0x%04x",
1226                                          val_to_str(gperror, cops_gperror_vals, "<Unknown value>"), gperror_sub);
1227                 gperror_tree = proto_item_add_subtree(ti, ett_cops_gperror);
1228                 proto_tree_add_uint(gperror_tree, hf_cops_gperror, tvb, offset, 2, gperror);
1229                 offset += 2;
1230                 if (cperror == 13) {
1231                         proto_tree_add_text(gperror_tree, tvb, offset, 2, "Error Sub-code: "
1232                                             "Unknown object's C-Num %u, C-Type %u",
1233                                             tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset + 1));
1234                 } else 
1235                         proto_tree_add_uint(gperror_tree, hf_cops_gperror_sub, tvb, offset, 2, gperror_sub);
1236
1237                 return 0;
1238                 break;
1239         case COPS_OBJ_CPERR:
1240                 if (s_type != 1) /*Not PRC Class Provisioning Error Object (CPERR) */
1241                         break;
1242                 
1243                 break;
1244
1245                 cperror = tvb_get_ntohs(tvb, offset);
1246                 cperror_sub = tvb_get_ntohs(tvb, offset + 2);
1247                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Error-Code: %s, Error Sub-code: 0x%04x",
1248                                          val_to_str(cperror, cops_cperror_vals, "<Unknown value>"), cperror_sub);
1249                 cperror_tree = proto_item_add_subtree(ti, ett_cops_cperror);
1250                 proto_tree_add_uint(cperror_tree, hf_cops_cperror, tvb, offset, 2, cperror);
1251                 offset += 2;
1252                 if (cperror == 13) {
1253                         proto_tree_add_text(cperror_tree, tvb, offset, 2, "Error Sub-code: "
1254                                             "Unknown object's S-Num %u, C-Type %u",
1255                                             tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset + 1));
1256                 } else 
1257                         proto_tree_add_uint(cperror_tree, hf_cops_cperror_sub, tvb, offset, 2, cperror_sub);
1258
1259                 return 0;
1260                 break;
1261         case COPS_OBJ_ERRPRID:
1262                 if (s_type != 1) /*Not  Error Provisioning Instance Identifier (ErrorPRID)*/
1263                         break;
1264
1265                 ti = proto_tree_add_text(tree, tvb, offset, len, "Contents:");
1266
1267                 decode_cops_pr_asn1_data(tvb, offset, ti, len);
1268
1269                 return 0;
1270                 break;
1271         default:
1272                 break;
1273         }
1274
1275         ti = proto_tree_add_text(tree, tvb, offset, len, "Contents: %u bytes", len);
1276
1277         return 0;
1278 }
1279
1280
1281 /* Register the protocol with Ethereal */
1282 void proto_register_cops(void)
1283 {                 
1284
1285         /* Setup list of header fields */
1286         static hf_register_info hf[] = {
1287                 { &hf_cops_ver_flags,
1288                         { "Version and Flags",           "cops.ver_flags",
1289                         FT_UINT8, BASE_HEX, NULL, 0x0,
1290                         "Version and Flags in COPS Common Header", HFILL }
1291                 },
1292                 { &hf_cops_version,
1293                         { "Version",           "cops.version",
1294                         FT_UINT8, BASE_DEC, NULL, 0xF0,
1295                         "Version in COPS Common Header", HFILL }
1296                 },
1297                 { &hf_cops_flags,
1298                         { "Flags",           "cops.flags",
1299                         FT_UINT8, BASE_HEX, VALS(cops_flags_vals), 0x0F,
1300                         "Flags in COPS Common Header", HFILL }
1301                 },
1302                 { &hf_cops_op_code,
1303                         { "Op Code",           "cops.op_code",
1304                         FT_UINT8, BASE_DEC, VALS(cops_op_code_vals), 0x0,
1305                         "Op Code in COPS Common Header", HFILL }
1306                 },
1307                 { &hf_cops_client_type,
1308                         { "Client Type",           "cops.client_type",
1309                         FT_UINT16, BASE_DEC, NULL, 0x0,
1310                         "Client Type in COPS Common Header", HFILL }
1311                 },
1312                 { &hf_cops_msg_len,
1313                         { "Message Length",           "cops.msg_len",
1314                         FT_UINT32, BASE_DEC, NULL, 0x0,
1315                         "Message Length in COPS Common Header", HFILL }
1316                 },
1317                 { &hf_cops_obj_len,
1318                         { "Object Length",           "cops.obj.len",
1319                         FT_UINT32, BASE_DEC, NULL, 0x0,
1320                         "Object Length in COPS Object Header", HFILL }
1321                 },
1322                 { &hf_cops_obj_c_num,
1323                         { "C-Num",           "cops.c_num",
1324                         FT_UINT8, BASE_DEC, VALS(cops_c_num_vals), 0x0,
1325                         "C-Num in COPS Object Header", HFILL }
1326                 },
1327                 { &hf_cops_obj_c_type,
1328                         { "C-Type",           "cops.c_type",
1329                         FT_UINT8, BASE_DEC, NULL, 0x0,
1330                         "C-Type in COPS Object Header", HFILL }
1331                 },
1332
1333                 { &hf_cops_obj_s_num,
1334                         { "S-Num",           "cops.s_num",
1335                         FT_UINT8, BASE_DEC, VALS(cops_s_num_vals), 0x0,
1336                         "S-Num in COPS-PR Object Header", HFILL }
1337                 },
1338                 { &hf_cops_obj_s_type,
1339                         { "S-Type",           "cops.s_type",
1340                         FT_UINT8, BASE_DEC, NULL, 0x0,
1341                         "S-Type in COPS-PR Object Header", HFILL }
1342                 },
1343
1344                 { &hf_cops_r_type_flags,
1345                         { "R-Type",           "cops.context.r_type",
1346                         FT_UINT16, BASE_HEX, VALS(cops_r_type_vals), 0xFFFF,
1347                         "R-Type in COPS Context Object", HFILL }
1348                 },
1349                 { &hf_cops_m_type_flags,
1350                         { "M-Type",           "cops.context.m_type",
1351                         FT_UINT16, BASE_HEX, NULL, 0xFFFF,
1352                         "M-Type in COPS Context Object", HFILL }
1353                 },
1354                 { &hf_cops_in_int_ipv4,
1355                         { "IPv4 address",           "cops.in-int.ipv4",
1356                         FT_IPv4, 0, NULL, 0xFFFF,
1357                         "IPv4 address in COPS IN-Int object", HFILL }
1358                 },
1359                 { &hf_cops_in_int_ipv6,
1360                         { "IPv6 address",           "cops.in-int.ipv6",
1361                         FT_IPv6, 0, NULL, 0xFFFF,
1362                         "IPv6 address in COPS IN-Int object", HFILL }
1363                 },
1364                 { &hf_cops_out_int_ipv4,
1365                         { "IPv4 address",           "cops.out-int.ipv4",
1366                         FT_IPv4, 0, NULL, 0xFFFF,
1367                         "IPv4 address in COPS OUT-Int object", HFILL }
1368                 },
1369                 { &hf_cops_out_int_ipv6,
1370                         { "IPv6 address",           "cops.out-int.ipv6",
1371                         FT_IPv6, 0, NULL, 0xFFFF,
1372                         "IPv6 address in COPS OUT-Int", HFILL }
1373                 },
1374                 { &hf_cops_int_ifindex,
1375                         { "ifIndex",           "cops.in-out-int.ifindex",
1376                         FT_UINT32, BASE_DEC, NULL, 0x0,
1377                         "If SNMP is supported, corresponds to MIB-II ifIndex", HFILL }
1378                 },
1379                 { &hf_cops_reason,
1380                         { "Reason",           "cops.reason",
1381                         FT_UINT16, BASE_DEC, VALS(cops_reason_vals), 0,
1382                         "Reason in Reason object", HFILL }
1383                 },
1384                 { &hf_cops_reason_sub,
1385                         { "Reason Sub-code",           "cops.reason_sub",
1386                         FT_UINT16, BASE_HEX, NULL, 0,
1387                         "Reason Sub-code in Reason object", HFILL }
1388                 },
1389                 { &hf_cops_dec_cmd_code,
1390                         { "Command-Code",           "cops.decision.cmd",
1391                         FT_UINT16, BASE_DEC, VALS(cops_dec_cmd_code_vals), 0,
1392                         "Command-Code in Decision/LPDP Decision object", HFILL }
1393                 },
1394                 { &hf_cops_dec_flags,
1395                         { "Flags",           "cops.decision.flags",
1396                         FT_UINT16, BASE_HEX, VALS(cops_dec_cmd_flag_vals), 0xffff,
1397                         "Flags in Decision/LPDP Decision object", HFILL }
1398                 },
1399                 { &hf_cops_error,
1400                         { "Error",           "cops.error",
1401                         FT_UINT16, BASE_DEC, VALS(cops_error_vals), 0,
1402                         "Error in Error object", HFILL }
1403                 },
1404                 { &hf_cops_error_sub,
1405                         { "Error Sub-code",           "cops.error_sub",
1406                         FT_UINT16, BASE_HEX, NULL, 0,
1407                         "Error Sub-code in Error object", HFILL }
1408                 },
1409                 { &hf_cops_katimer,
1410                         { "Contents: KA Timer Value",           "cops.katimer.value",
1411                         FT_UINT16, BASE_DEC, NULL, 0,
1412                         "Keep-Alive Timer Value in KATimer object", HFILL }
1413                 },
1414                 { &hf_cops_pepid,
1415                         { "Contents: PEP Id",           "cops.pepid.id",
1416                         FT_STRING, BASE_NONE, NULL, 0,
1417                         "PEP Id in PEPID object", HFILL }
1418                 },
1419                 { &hf_cops_report_type,
1420                         { "Contents: Report-Type",           "cops.report_type",
1421                         FT_UINT16, BASE_DEC, VALS(cops_report_type_vals), 0,
1422                         "Report-Type in Report-Type object", HFILL }
1423                 },
1424                 { &hf_cops_pdprediraddr_ipv4,
1425                         { "IPv4 address",           "cops.pdprediraddr.ipv4",
1426                         FT_IPv4, 0, NULL, 0xFFFF,
1427                         "IPv4 address in COPS PDPRedirAddr object", HFILL }
1428                 },
1429                 { &hf_cops_pdprediraddr_ipv6,
1430                         { "IPv6 address",           "cops.pdprediraddr.ipv6",
1431                         FT_IPv6, 0, NULL, 0xFFFF,
1432                         "IPv6 address in COPS PDPRedirAddr object", HFILL }
1433                 },
1434                 { &hf_cops_lastpdpaddr_ipv4,
1435                         { "IPv4 address",           "cops.lastpdpaddr.ipv4",
1436                         FT_IPv4, 0, NULL, 0xFFFF,
1437                         "IPv4 address in COPS LastPDPAddr object", HFILL }
1438                 },
1439                 { &hf_cops_lastpdpaddr_ipv6,
1440                         { "IPv6 address",           "cops.lastpdpaddr.ipv6",
1441                         FT_IPv6, 0, NULL, 0xFFFF,
1442                         "IPv6 address in COPS LastPDPAddr object", HFILL }
1443                 },
1444                 { &hf_cops_pdp_tcp_port,
1445                         { "TCP Port Number",           "cops.pdp.tcp_port",
1446                         FT_UINT32, BASE_DEC, NULL, 0x0,
1447                          "TCP Port Number of PDP in PDPRedirAddr/LastPDPAddr object", HFILL }
1448                 },
1449                 { &hf_cops_accttimer,
1450                         { "Contents: ACCT Timer Value",           "cops.accttimer.value",
1451                         FT_UINT16, BASE_DEC, NULL, 0,
1452                         "Accounting Timer Value in AcctTimer object", HFILL }
1453                 },
1454                 { &hf_cops_key_id,
1455                         { "Contents: Key ID",           "cops.integrity.key_id",
1456                         FT_UINT32, BASE_DEC, NULL, 0,
1457                         "Key ID in Integrity object", HFILL }
1458                 },
1459                 { &hf_cops_seq_num,
1460                         { "Contents: Sequence Number",           "cops.integrity.seq_num",
1461                         FT_UINT32, BASE_DEC, NULL, 0,
1462                         "Sequence Number in Integrity object", HFILL }
1463                 },
1464                 { &hf_cops_gperror,
1465                         { "Error",           "cops.gperror",
1466                         FT_UINT16, BASE_DEC, VALS(cops_gperror_vals), 0,
1467                         "Error in Error object", HFILL }
1468                 },
1469                 { &hf_cops_gperror_sub,
1470                         { "Error Sub-code",           "cops.gperror_sub",
1471                         FT_UINT16, BASE_HEX, NULL, 0,
1472                         "Error Sub-code in Error object", HFILL }
1473                 },
1474                 { &hf_cops_cperror,
1475                         { "Error",           "cops.cperror",
1476                         FT_UINT16, BASE_DEC, VALS(cops_cperror_vals), 0,
1477                         "Error in Error object", HFILL }
1478                 },
1479                 { &hf_cops_cperror_sub,
1480                         { "Error Sub-code",           "cops.cperror_sub",
1481                         FT_UINT16, BASE_HEX, NULL, 0,
1482                         "Error Sub-code in Error object", HFILL }
1483                 },
1484
1485         };
1486
1487         /* Setup protocol subtree array */
1488         static gint *ett[] = {
1489                 &ett_cops,
1490                 &ett_cops_ver_flags,
1491                 &ett_cops_obj,
1492                 &ett_cops_obj_data,
1493                 &ett_cops_r_type_flags,
1494                 &ett_cops_itf,
1495                 &ett_cops_reason,
1496                 &ett_cops_decision,
1497                 &ett_cops_error,
1498                 &ett_cops_pdp,
1499                 &ett_cops_pr_obj,
1500         };
1501
1502         module_t* cops_module;
1503
1504         /* Register the protocol name and description */
1505         proto_cops = proto_register_protocol("Common Open Policy Service",
1506             "COPS", "cops");
1507
1508         /* Required function calls to register the header fields and subtrees used */
1509         proto_register_field_array(proto_cops, hf, array_length(hf));
1510         proto_register_subtree_array(ett, array_length(ett));
1511         
1512         /* Register our configuration options for cops, 
1513          * particularly our ports
1514          */
1515         cops_module = prefs_register_protocol(proto_cops,
1516                                               proto_reg_handoff_cops);
1517         prefs_register_uint_preference(cops_module,"tcp.cops_port",
1518                                        "COPS TCP Port",
1519                                        "Set the TCP port for COPS messages",
1520                                        10,&global_cops_tcp_port);
1521         prefs_register_bool_preference(cops_module, "desegment",
1522             "Desegment all COPS messages spanning multiple TCP segments",
1523             "Whether the COPS dissector should desegment all messages spanning multiple TCP segments",
1524             &cops_desegment);
1525 };
1526
1527 void
1528 proto_reg_handoff_cops(void)
1529 {
1530         static int cops_prefs_initialized = FALSE;
1531         static dissector_handle_t cops_handle;
1532
1533         if(!cops_prefs_initialized){
1534           cops_handle = create_dissector_handle(dissect_cops, proto_cops);
1535           cops_prefs_initialized = TRUE;
1536         }
1537         else {
1538           dissector_delete("tcp.port",cops_tcp_port,cops_handle);
1539         }
1540         
1541         /* Set our port numbers for future use */
1542         cops_tcp_port = global_cops_tcp_port;
1543
1544         dissector_add("tcp.port", cops_tcp_port, cops_handle);
1545 }