Use "gfloat" and "gdouble", rather than "float" and "double", as the
[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.28 2002/04/05 10:08:24 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_clientsi = -1;
420 static gint ett_cops_asn1 = -1;
421 static gint ett_cops_gperror = -1;
422 static gint ett_cops_cperror = -1;
423 static gint ett_cops_pdp = -1;
424
425 void proto_reg_handoff_cops(void);
426
427 static void dissect_cops_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
428
429 static int dissect_cops_object(tvbuff_t *tvb, guint32 offset, proto_tree *tree);
430 static int dissect_cops_object_data(tvbuff_t *tvb, guint32 offset, proto_tree *tree,
431                                     guint8 c_num, guint8 c_type, guint16 len);
432
433 static int dissect_cops_pr_objects(tvbuff_t *tvb, guint32 offset, proto_tree *tree, guint16 pr_len);
434 static int dissect_cops_pr_object_data(tvbuff_t *tvb, guint32 offset, proto_tree *tree,
435                                        guint8 s_num, guint8 s_type, guint16 len);
436
437 /* Code to actually dissect the packets */
438 static void
439 dissect_cops(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
440 {
441         volatile int offset = 0;
442         int length_remaining;
443         guint32 msg_len;
444         int length;
445         tvbuff_t *next_tvb;
446
447         while (tvb_reported_length_remaining(tvb, offset) != 0) {
448                 length_remaining = tvb_length_remaining(tvb, offset);
449                 if (length_remaining == -1)
450                         THROW(BoundsError);
451
452                 /*
453                  * Can we do reassembly?
454                  */
455                 if (cops_desegment && pinfo->can_desegment) {
456                         /*
457                          * Yes - is the COPS header split across segment
458                          * boundaries?
459                          */
460                         if (length_remaining < 8) {
461                                 /*
462                                  * Yes.  Tell the TCP dissector where
463                                  * the data for this message starts in
464                                  * the data it handed us, and how many
465                                  * more bytes we need, and return.
466                                  */
467                                 pinfo->desegment_offset = offset;
468                                 pinfo->desegment_len = 8 - length_remaining;
469                                 return;
470                         }
471                 }
472
473                 /*
474                  * Get the length of the COPS message.
475                  */
476                 msg_len = tvb_get_ntohl(tvb, offset + 4);
477
478                 /*
479                  * Can we do reassembly?
480                  */
481                 if (cops_desegment && pinfo->can_desegment) {
482                         /*
483                          * Yes - is the DNS packet split across segment
484                          * boundaries?
485                          */
486                         if ((guint32)length_remaining < msg_len) {
487                                 /*
488                                  * Yes.  Tell the TCP dissector where
489                                  * the data for this message starts in
490                                  * the data it handed us, and how many
491                                  * more bytes we need, and return.
492                                  */
493                                 pinfo->desegment_offset = offset;
494                                 pinfo->desegment_len =
495                                     msg_len - length_remaining;
496                                 return;
497                         }
498                 }
499
500                 /*
501                  * Construct a tvbuff containing the amount of the payload
502                  * we have available.  Make its reported length the
503                  * amount of data in the COPS packet.
504                  *
505                  * XXX - if reassembly isn't enabled. the subdissector
506                  * will throw a BoundsError exception, rather than a
507                  * ReportedBoundsError exception.  We really want
508                  * a tvbuff where the length is "length", the reported
509                  * length is "plen + 2", and the "if the snapshot length
510                  * were infinite" length were the minimum of the
511                  * reported length of the tvbuff handed to us and "plen+2",
512                  * with a new type of exception thrown if the offset is
513                  * within the reported length but beyond that third length,
514                  * with that exception getting the "Unreassembled Packet"
515                  * error.
516                  */
517                 length = length_remaining;
518                 if ((guint32)length > msg_len)
519                         length = msg_len;
520                 next_tvb = tvb_new_subset(tvb, offset, length, msg_len);
521
522                 /*
523                  * Dissect the COPS packet.
524                  *
525                  * Catch the ReportedBoundsError exception; if this
526                  * particular message happens to get a ReportedBoundsError
527                  * exception, that doesn't mean that we should stop
528                  * dissecting COPS messages within this frame or chunk
529                  * of reassembled data.
530                  *
531                  * If it gets a BoundsError, we can stop, as there's nothing
532                  * more to see, so we just re-throw it.
533                  */
534                 TRY {
535                         dissect_cops_pdu(next_tvb, pinfo, tree);
536                 }
537                 CATCH(BoundsError) {
538                         RETHROW;
539                 }
540                 CATCH(ReportedBoundsError) {
541                         show_reported_bounds_error(tvb, pinfo, tree);
542                 }
543                 ENDTRY;
544
545                 /*
546                  * Skip the COPS packet.
547                  */
548                 offset += msg_len;
549         }
550 }
551
552 static void
553 dissect_cops_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
554 {
555         guint8 op_code;
556
557         if (check_col(pinfo->cinfo, COL_PROTOCOL)) 
558                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "COPS");
559         if (check_col(pinfo->cinfo, COL_INFO)) 
560                 col_clear(pinfo->cinfo, COL_INFO);
561     
562         op_code = tvb_get_guint8(tvb, 1);
563         if (check_col(pinfo->cinfo, COL_INFO))
564                 col_add_fstr(pinfo->cinfo, COL_INFO, "COPS %s",
565                              val_to_str(op_code, cops_op_code_vals, "Unknown Op Code"));
566
567         if (tree) {
568                 proto_item *ti, *tv;
569                 proto_tree *cops_tree, *ver_flags_tree;
570                 guint32 msg_len;
571                 guint32 offset = 0;
572                 guint8 ver_flags;
573                 gint garbage;
574
575                 ti = proto_tree_add_item(tree, proto_cops, tvb, offset, -1, FALSE);
576                 cops_tree = proto_item_add_subtree(ti, ett_cops);
577
578                 /* Version and flags share the same byte, put them in a subtree */
579                 ver_flags = tvb_get_guint8(tvb, offset);
580                 tv = proto_tree_add_uint_format(cops_tree, hf_cops_ver_flags, tvb, offset, 1,
581                                                   ver_flags, "Version: %u, Flags: %s",
582                                                   hi_nibble(ver_flags),
583                                                   val_to_str(lo_nibble(ver_flags), cops_flags_vals, "Unknown"));
584                 ver_flags_tree = proto_item_add_subtree(tv, ett_cops_ver_flags);
585                 proto_tree_add_uint(ver_flags_tree, hf_cops_version, tvb, offset, 1, ver_flags);
586                 proto_tree_add_uint(ver_flags_tree, hf_cops_flags, tvb, offset, 1, ver_flags);
587                 offset++;
588
589                 proto_tree_add_uint(cops_tree, hf_cops_op_code, tvb, offset, 1, tvb_get_guint8(tvb, offset));
590                 offset ++;
591                 proto_tree_add_uint(cops_tree, hf_cops_client_type, tvb, offset, 2, tvb_get_ntohs(tvb, offset));
592                 offset += 2;
593
594                 msg_len = tvb_get_ntohl(tvb, offset);
595                 proto_tree_add_uint(cops_tree, hf_cops_msg_len, tvb, offset, 4, tvb_get_ntohl(tvb, offset));
596                 offset += 4;
597
598                 while (tvb_reported_length_remaining(tvb, offset) >= COPS_OBJECT_HDR_SIZE)
599                         offset += dissect_cops_object(tvb, offset, cops_tree);
600
601                 garbage = tvb_length_remaining(tvb, offset);
602                 if (garbage > 0)
603                         proto_tree_add_text(cops_tree, tvb, offset, garbage,
604                                             "Trailing garbage: %d byte%s", garbage,
605                                             plurality(garbage, "", "s"));
606         }
607
608         return;
609 }
610
611 static char *cops_c_type_to_str(guint8 c_num, guint8 c_type)
612 {
613         switch (c_num) {
614         case COPS_OBJ_HANDLE:
615                 if (c_type == 1)
616                         return "Client Handle";
617                 break;
618         case COPS_OBJ_IN_INT:
619         case COPS_OBJ_OUT_INT:
620                 if (c_type == 1)
621                         return "IPv4 Address + Interface";
622                 else if (c_type == 2)
623                         return "IPv6 Address + Interface";
624                 break;
625         case COPS_OBJ_DECISION:
626         case COPS_OBJ_LPDPDECISION:
627                 if (c_type == 1)
628                         return "Decision Flags (Mandatory)";
629                 else if (c_type == 2)
630                         return "Stateless Data";
631                 else if (c_type == 3)
632                         return "Replacement Data";
633                 else if (c_type == 4)
634                         return "Client Specific Decision Data";
635                 else if (c_type == 5)
636                         return "Named Decision Data";
637                 break;
638         case COPS_OBJ_CLIENTSI:
639                 if (c_type == 1)
640                         return "Signaled ClientSI";
641                 else if (c_type == 2)
642                         return "Named ClientSI";
643                 break;
644         case COPS_OBJ_KATIMER:
645                 if (c_type == 1)
646                         return "Keep-alive timer value";
647                 break;
648         case COPS_OBJ_PDPREDIRADDR:
649         case COPS_OBJ_LASTPDPADDR:
650                 if (c_type == 1)
651                         return "IPv4 Address + TCP Port";
652                 else if (c_type == 2)
653                         return "IPv6 Address + TCP Port";
654                 break;
655         case COPS_OBJ_ACCTTIMER:
656                 if (c_type == 1)
657                         return "Accounting timer value";
658                 break;
659         case COPS_OBJ_INTEGRITY:
660                 if (c_type == 1)
661                         return "HMAC digest";
662                 break;
663         }
664
665         return "";
666 }
667
668 static int dissect_cops_object(tvbuff_t *tvb, guint32 offset, proto_tree *tree)
669 {
670         guint16 object_len, contents_len;
671         guint8 c_num, c_type;
672         proto_item *ti;
673         proto_tree *obj_tree;
674         char *type_str;
675         int ret;
676
677         object_len = tvb_get_ntohs(tvb, offset);
678         c_num = tvb_get_guint8(tvb, offset + 2);
679         c_type = tvb_get_guint8(tvb, offset + 3);
680
681         ti = proto_tree_add_uint_format(tree, hf_cops_obj_c_num, tvb, offset, object_len, c_num,
682                                         "%s: %s", val_to_str(c_num, cops_c_num_vals, "Unknown"),
683                                         cops_c_type_to_str(c_num, c_type));
684         obj_tree = proto_item_add_subtree(ti, ett_cops_obj);
685
686         proto_tree_add_uint(obj_tree, hf_cops_obj_len, tvb, offset, 2, tvb_get_ntohs(tvb, offset));
687         offset += 2;
688
689         proto_tree_add_uint(obj_tree, hf_cops_obj_c_num, tvb, offset, 1, c_num);
690         offset++;
691
692         type_str = cops_c_type_to_str(c_num, c_type);
693         proto_tree_add_text(obj_tree, tvb, offset, 1, "C-Type: %s%s%u%s",
694                             type_str,
695                             strlen(type_str) ? " (" : "",
696                             c_type,
697                             strlen(type_str) ? ")" : "");
698         offset++;
699
700         contents_len = object_len - COPS_OBJECT_HDR_SIZE;
701         ret = dissect_cops_object_data(tvb, offset, obj_tree, c_num, c_type, contents_len);
702         if (ret < 0) return 0;
703
704         /* Pad to 32bit boundary */
705         if (object_len % sizeof (guint32))
706                 object_len += (sizeof (guint32) - object_len % sizeof (guint32));
707         
708         return object_len;        
709 }
710
711 static int dissect_cops_pr_objects(tvbuff_t *tvb, guint32 offset, proto_tree *tree, guint16 pr_len)
712 {
713         guint16 object_len, contents_len;
714         guint8 s_num, s_type;
715         char *type_str;
716         int ret;
717         proto_tree *cops_pr_tree, *obj_tree;
718         proto_item *ti;
719
720         cops_pr_tree = proto_item_add_subtree(tree, ett_cops_pr_obj);
721         
722         while (pr_len >= COPS_OBJECT_HDR_SIZE) { 
723                 object_len = tvb_get_ntohs(tvb, offset);
724                 s_num = tvb_get_guint8(tvb, offset + 2);
725
726                 ti = proto_tree_add_uint_format(cops_pr_tree, hf_cops_obj_s_num, tvb, offset, object_len, s_num,
727                                         "%s", val_to_str(s_num, cops_s_num_vals, "Unknown"));
728                 obj_tree = proto_item_add_subtree(cops_pr_tree, ett_cops_pr_obj);
729
730                 proto_tree_add_uint(obj_tree, hf_cops_obj_len, tvb, offset, 2, tvb_get_ntohs(tvb, offset));
731                 offset += 2;
732                 pr_len -= 2;
733  
734                 proto_tree_add_uint(obj_tree, hf_cops_obj_s_num, tvb, offset, 1, s_num);
735                 offset++;
736                 pr_len--;
737
738                 s_type = tvb_get_guint8(tvb, offset);
739                 type_str = val_to_str(s_type, cops_s_type_vals, "Unknown");
740                 proto_tree_add_text(obj_tree, tvb, offset, 1, "S-Type: %s%s%u%s",
741                             type_str,
742                             strlen(type_str) ? " (" : "",
743                             s_type,
744                             strlen(type_str) ? ")" : "");
745                 offset++;
746                 pr_len--;
747
748                 contents_len = object_len - COPS_OBJECT_HDR_SIZE;
749                 ret = dissect_cops_pr_object_data(tvb, offset, obj_tree, s_num, s_type, contents_len);
750                 if (ret < 0)
751                         break;
752
753                 /*Pad to 32bit boundary */
754                 if (object_len % sizeof (guint32))
755                         object_len += (sizeof (guint32) - object_len % sizeof (guint32));
756            
757                 pr_len -= object_len - COPS_OBJECT_HDR_SIZE;
758                 offset += object_len - COPS_OBJECT_HDR_SIZE;
759         }
760
761         return 0;
762 }
763
764 static int dissect_cops_object_data(tvbuff_t *tvb, guint32 offset, proto_tree *tree,
765                                     guint8 c_num, guint8 c_type, guint16 len)
766 {
767         proto_item *ti;
768         proto_tree *r_type_tree, *itf_tree, *reason_tree, *dec_tree, *error_tree, *clientsi_tree, *pdp_tree;
769         guint16 r_type, m_type, reason, reason_sub, cmd_code, cmd_flags, error, error_sub, tcp_port;
770         guint32 ipv4addr, ifindex;
771         struct e_in6_addr ipv6addr;
772
773         switch (c_num) {
774         case COPS_OBJ_CONTEXT:
775                 r_type = tvb_get_ntohs(tvb, offset);
776                 m_type = tvb_get_ntohs(tvb, offset + 2);
777                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: R-Type: %s, M-Type: %u",
778                                          val_to_str(r_type, cops_r_type_vals, "Unknown"),
779                                          m_type);
780
781                 r_type_tree = proto_item_add_subtree(ti, ett_cops_r_type_flags);
782                 proto_tree_add_uint(r_type_tree, hf_cops_r_type_flags, tvb, offset, 2, r_type);
783                 offset += 2;
784                 proto_tree_add_uint(r_type_tree, hf_cops_m_type_flags, tvb, offset, 2, m_type);
785
786                 break;
787         case COPS_OBJ_IN_INT:
788         case COPS_OBJ_OUT_INT:
789                 if (c_type == 1) {          /* IPv4 */
790                         tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset, 4);
791                         ifindex = tvb_get_ntohl(tvb, offset + 4);
792                         ti = proto_tree_add_text(tree, tvb, offset, 8, "Contents: IPv4 address %s, ifIndex: %u",
793                                                  ip_to_str((guint8 *)&ipv4addr), ifindex);
794                         itf_tree = proto_item_add_subtree(ti, ett_cops_itf);
795                         proto_tree_add_ipv4(itf_tree,
796                                             (c_num == COPS_OBJ_IN_INT) ? hf_cops_in_int_ipv4 : hf_cops_out_int_ipv4,
797                                             tvb, offset, 4, ipv4addr);
798                         offset += 4;
799                 } else if (c_type == 2) {   /* IPv6 */
800                         tvb_memcpy(tvb, (guint8 *)&ipv6addr, offset, sizeof ipv6addr);
801                         ifindex = tvb_get_ntohl(tvb, offset + sizeof ipv6addr);
802                         ti = proto_tree_add_text(tree, tvb, offset, 20, "Contents: IPv6 address %s, ifIndex: %u",
803                                                  ip6_to_str(&ipv6addr), ifindex);
804                         itf_tree = proto_item_add_subtree(ti, ett_cops_itf);
805                         proto_tree_add_ipv6(itf_tree,
806                                             (c_num == COPS_OBJ_IN_INT) ? hf_cops_in_int_ipv6 : hf_cops_out_int_ipv6,
807                                             tvb, offset, 16, (guint8 *)&ipv6addr);
808                         offset += 16;
809                 } else {
810                         break;
811                 }
812                 proto_tree_add_uint(itf_tree, hf_cops_int_ifindex, tvb, offset, 4, ifindex);
813
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                 break;
831         case COPS_OBJ_DECISION:
832         case COPS_OBJ_LPDPDECISION:
833                 if (c_type == 1) {
834                         cmd_code = tvb_get_ntohs(tvb, offset);
835                         cmd_flags = tvb_get_ntohs(tvb, offset + 2);
836                         ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Command-Code: %s, Flags: %s",
837                                          val_to_str(cmd_code, cops_dec_cmd_code_vals, "<Unknown value>"),
838                                          val_to_str(cmd_flags, cops_dec_cmd_flag_vals, "<Unknown flag>"));
839                         dec_tree = proto_item_add_subtree(ti, ett_cops_decision);
840                         proto_tree_add_uint(dec_tree, hf_cops_dec_cmd_code, tvb, offset, 2, cmd_code);
841                         offset += 2;
842                         proto_tree_add_uint(dec_tree, hf_cops_dec_flags, tvb, offset, 2, cmd_flags);
843                 } else if (c_type == 5) { /*COPS-PR Data*/
844                         ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: %u bytes", len);
845                         dec_tree = proto_item_add_subtree(ti, ett_cops_decision);
846                         dissect_cops_pr_objects(tvb, offset, dec_tree, len);
847                 }
848
849                 break;
850         case COPS_OBJ_ERROR:
851                 if (c_type != 1)
852                         break;
853                 
854                 error = tvb_get_ntohs(tvb, offset);
855                 error_sub = tvb_get_ntohs(tvb, offset + 2);
856                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Error-Code: %s, Error Sub-code: 0x%04x",
857                                          val_to_str(error, cops_error_vals, "<Unknown value>"), error_sub);
858                 error_tree = proto_item_add_subtree(ti, ett_cops_error);
859                 proto_tree_add_uint(error_tree, hf_cops_error, tvb, offset, 2, error);
860                 offset += 2;
861                 if (error == 13) {
862                         proto_tree_add_text(error_tree, tvb, offset, 2, "Error Sub-code: "
863                                             "Unknown object's C-Num %u, C-Type %u",
864                                             tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset + 1));
865                 } else 
866                         proto_tree_add_uint(error_tree, hf_cops_error_sub, tvb, offset, 2, error_sub);
867
868                 break;
869         case COPS_OBJ_CLIENTSI:
870           
871                 if (c_type != 2) /*Not COPS-PR data*/
872                       break;
873
874                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: %u bytes", len);
875                 clientsi_tree = proto_item_add_subtree(ti, ett_cops_clientsi);
876
877                 dissect_cops_pr_objects(tvb, offset, clientsi_tree, len);
878
879                 break;
880         case COPS_OBJ_KATIMER:
881                 if (c_type != 1)
882                         break;
883
884                 proto_tree_add_item(tree, hf_cops_katimer, tvb, offset + 2, 2, FALSE);
885                 if (tvb_get_ntohs(tvb, offset + 2) == 0)
886                         proto_tree_add_text(tree, tvb, offset, 0, "Value of zero implies infinity.");
887                 
888                 break;
889         case COPS_OBJ_PEPID:
890                 if (c_type != 1)
891                         break;
892
893                 if (tvb_strnlen(tvb, offset, len) == -1)
894                         proto_tree_add_text(tree, tvb, offset, len, "<PEP Id is not a NUL terminated ASCII string>");
895                 else
896                         proto_tree_add_item(tree, hf_cops_pepid, tvb, offset,
897                                             tvb_strnlen(tvb, offset, len) + 1, FALSE);
898
899                 break;
900         case COPS_OBJ_REPORT_TYPE:
901                 if (c_type != 1)
902                         break;
903
904                 proto_tree_add_item(tree, hf_cops_report_type, tvb, offset, 2, FALSE);
905
906                 break;
907         case COPS_OBJ_PDPREDIRADDR:
908         case COPS_OBJ_LASTPDPADDR:
909                 if (c_type == 1) {          /* IPv4 */
910                         tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset, 4);
911                         tcp_port = tvb_get_ntohs(tvb, offset + 4 + 2);
912                         ti = proto_tree_add_text(tree, tvb, offset, 8, "Contents: IPv4 address %s, TCP Port Number: %u",
913                                                  ip_to_str((guint8 *)&ipv4addr), tcp_port);
914                         pdp_tree = proto_item_add_subtree(ti, ett_cops_pdp);
915                         proto_tree_add_ipv4(pdp_tree,
916                                             (c_num == COPS_OBJ_PDPREDIRADDR) ? hf_cops_pdprediraddr_ipv4 : hf_cops_lastpdpaddr_ipv4,
917                                             tvb, offset, 4, ipv4addr);
918                         offset += 4;
919                 } else if (c_type == 2) {   /* IPv6 */
920                         tvb_memcpy(tvb, (guint8 *)&ipv6addr, offset, sizeof ipv6addr);
921                         tcp_port = tvb_get_ntohs(tvb, offset + sizeof ipv6addr + 2);
922                         ti = proto_tree_add_text(tree, tvb, offset, 20, "Contents: IPv6 address %s, TCP Port Number: %u",
923                                                  ip6_to_str(&ipv6addr), tcp_port);
924                         pdp_tree = proto_item_add_subtree(ti, ett_cops_pdp);
925                         proto_tree_add_ipv6(pdp_tree,
926                                             (c_num == COPS_OBJ_PDPREDIRADDR) ? hf_cops_pdprediraddr_ipv6 : hf_cops_lastpdpaddr_ipv6,
927                                             tvb, offset, 16, (guint8 *)&ipv6addr);
928                         offset += 16;
929                 } else {
930                         break;
931                 }
932                 offset += 2;
933                 proto_tree_add_uint(pdp_tree, hf_cops_pdp_tcp_port, tvb, offset, 2, tcp_port);
934
935                 break;
936         case COPS_OBJ_ACCTTIMER:
937                 if (c_type != 1)
938                         break;
939
940                 proto_tree_add_item(tree, hf_cops_accttimer, tvb, offset + 2, 2, FALSE);
941                 if (tvb_get_ntohs(tvb, offset + 2) == 0)
942                         proto_tree_add_text(tree, tvb, offset, 0, "Value of zero means "
943                                             "there SHOULD be no unsolicited accounting updates.");
944
945                 break;
946         case COPS_OBJ_INTEGRITY:
947                 if (c_type != 1)
948                         break;      /* Not HMAC digest */
949
950                 proto_tree_add_item(tree, hf_cops_key_id, tvb, offset, 4, FALSE);
951                 proto_tree_add_item(tree, hf_cops_seq_num, tvb, offset + 4, 4, FALSE);
952                 proto_tree_add_text(tree, tvb, offset + 8 , len - 8, "Contents: Keyed Message Digest");
953
954                 break;
955         default:
956                 proto_tree_add_text(tree, tvb, offset, len, "Contents: %u bytes", len);
957
958                 break;
959         }
960
961         return 0;
962 }
963
964 static int decode_cops_pr_asn1_data(tvbuff_t *tvb, guint32 offset, proto_tree *tree, guint epdlen)
965 {
966         ASN1_SCK asn1; 
967         int start;
968         gboolean def;
969         guint length;
970
971         guint vb_length;
972         gushort vb_type;
973         gchar *vb_type_name;
974
975         int ret;
976         guint cls, con, tag;
977
978 #ifdef OLD
979         subid_t *variable_oid;
980         guint variable_oid_length;
981 #endif
982
983         gint32 vb_integer_value;
984         guint32 vb_uinteger_value;
985
986         guint8 *vb_octet_string;
987
988         subid_t *vb_oid;
989         guint vb_oid_length;
990
991         gchar *vb_display_string;
992
993         unsigned int i;
994         gchar *buf;
995         int len;
996
997         while (epdlen > 0){ /*while there is stuff to be decoded*/
998
999         asn1_open(&asn1, tvb, offset);
1000
1001
1002         /* parse the type of the object */
1003
1004         start = asn1.offset;
1005
1006         ret = asn1_header_decode (&asn1, &cls, &con, &tag, &def, &vb_length);
1007         if (ret != ASN1_ERR_NOERROR)
1008           return 0;
1009         if (!def)
1010           return ASN1_ERR_LENGTH_NOT_DEFINITE;
1011
1012         /* Convert the class, constructed flag, and tag to a type. */
1013         vb_type_name = cops_tag_cls2syntax(tag, cls, &vb_type);
1014         if (vb_type_name == NULL) {
1015                 /*
1016                  * Unsupported type.
1017                  * Dissect the value as an opaque string of octets.
1018                  */
1019                 vb_type_name = "unsupported type";
1020                 vb_type = COPS_OPAQUE;
1021         }
1022
1023         /* parse the value */
1024
1025         switch (vb_type) {
1026
1027         case  COPS_INTEGER : 
1028                 ret = asn1_int32_value_decode(&asn1, vb_length,
1029                     &vb_integer_value);
1030                 if (ret != ASN1_ERR_NOERROR)
1031                         return ret;
1032                 length = asn1.offset - start;
1033                 if (tree) {
1034                         proto_tree_add_text(tree, asn1.tvb, offset, length,
1035                             "Value: %s: %d (%#x)", vb_type_name,
1036                             vb_integer_value, vb_integer_value);
1037                 }
1038                 break;
1039
1040
1041         case COPS_UNSIGNED32:
1042         case COPS_TIMETICKS:
1043                 ret = asn1_uint32_value_decode(&asn1, vb_length,
1044                     &vb_uinteger_value);
1045                 if (ret != ASN1_ERR_NOERROR)
1046                         return ret;
1047                 length = asn1.offset - start;
1048                 if (tree) {
1049                         proto_tree_add_text(tree, asn1.tvb, offset, length,
1050                             "Value: %s: %u (%#x)", vb_type_name,
1051                             vb_uinteger_value, vb_uinteger_value);
1052                 }
1053                 break;
1054
1055         case COPS_OCTETSTR:
1056         case COPS_IPADDR:
1057         case COPS_OPAQUE:
1058         case COPS_UNSIGNED64:
1059         case COPS_INTEGER64:
1060                 ret = asn1_string_value_decode (&asn1, vb_length,
1061                     &vb_octet_string);
1062                 if (ret != ASN1_ERR_NOERROR)
1063                         return ret;
1064                 length = asn1.offset - start;
1065                 if (tree) {
1066                         /*
1067                          * If some characters are not printable, display
1068                          * the string as bytes.
1069                          */
1070                         for (i = 0; i < vb_length; i++) {
1071                                 if (!(isprint(vb_octet_string[i])
1072                                     || isspace(vb_octet_string[i])))
1073                                         break;
1074                         }
1075                         if (i < vb_length) {
1076                                 /*
1077                                  * We stopped, due to a non-printable
1078                                  * character, before we got to the end
1079                                  * of the string.
1080                                  */
1081                                 vb_display_string = g_malloc(4*vb_length);
1082                                 buf = &vb_display_string[0];
1083                                 len = sprintf(buf, "%03u", vb_octet_string[0]);
1084                                 buf += len;
1085                                 for (i = 1; i < vb_length; i++) {
1086                                         len = sprintf(buf, ".%03u",
1087                                             vb_octet_string[i]);
1088                                         buf += len;
1089                                 }
1090                                 proto_tree_add_text(tree, asn1.tvb, offset, length,
1091                                     "Value: %s: %s", vb_type_name,
1092                                     vb_display_string);
1093                                 g_free(vb_display_string);
1094                         } else {
1095                                 proto_tree_add_text(tree, asn1.tvb, offset, length,
1096                                     "Value: %s: %.*s", vb_type_name,
1097                                     (int)vb_length,
1098                                     SAFE_STRING(vb_octet_string));
1099                         }
1100                 }
1101                 g_free(vb_octet_string);
1102                 break;
1103
1104         case COPS_NULL:
1105                 ret = asn1_null_decode (&asn1, vb_length);
1106                 if (ret != ASN1_ERR_NOERROR)
1107                         return ret;
1108                 length = asn1.offset - start;
1109                 if (tree) {
1110                         proto_tree_add_text(tree, asn1.tvb, offset, length,
1111                             "Value: %s", vb_type_name);
1112                 }
1113                 break;
1114
1115         case COPS_OBJECTID:
1116                 ret = asn1_oid_value_decode (&asn1, vb_length, &vb_oid,
1117                     &vb_oid_length);
1118                 if (ret != ASN1_ERR_NOERROR)
1119                         return ret;
1120                 length = asn1.offset - start;
1121
1122                 if (tree) {
1123                         vb_display_string = format_oid(vb_oid, vb_oid_length);
1124                         proto_tree_add_text(tree, asn1.tvb, offset, length,
1125                             "Value: %s: %s", vb_type_name, vb_display_string);
1126                         g_free(vb_display_string);
1127                 }
1128
1129 #ifdef OLD
1130
1131                 if (tree) {
1132 #ifdef HAVE_SPRINT_VALUE
1133                         if (!unsafe) {
1134
1135                    
1136                                 variable.val.objid = vb_oid;
1137                                 vb_display_string = format_var(&variable,
1138                                     variable_oid, variable_oid_length, vb_type,
1139                                     vb_length);
1140                                 proto_tree_add_text(tree, asn1.tvb, offset,
1141                                     length,
1142                                     "Value: %s", vb_display_string);
1143                                 break;  /* we added formatted version to the tree */
1144                         }
1145 #endif /* HAVE_SPRINT_VALUE */
1146
1147                         vb_display_string = format_oid(vb_oid, vb_oid_length);
1148                         proto_tree_add_text(tree, asn1.tvb, offset, length,
1149                             "Value: %s: %s", vb_type_name, vb_display_string);
1150                         g_free(vb_display_string);
1151                 }
1152 #endif
1153
1154
1155                 g_free(vb_oid);
1156                 break;
1157
1158         default:
1159                 g_assert_not_reached();
1160                 return ASN1_ERR_WRONG_TYPE;
1161         }
1162   
1163         asn1_close(&asn1,&offset);
1164  
1165         epdlen -= length;
1166         }
1167         return 0;
1168 }
1169
1170 static int dissect_cops_pr_object_data(tvbuff_t *tvb, guint32 offset, proto_tree *tree,
1171                                     guint8 s_num, guint8 s_type, guint16 len)
1172 {
1173         proto_item *ti;
1174         proto_tree *asn1_object_tree, *gperror_tree, *cperror_tree;
1175         guint16 gperror=0, gperror_sub=0, cperror=0, cperror_sub=0;
1176
1177         switch (s_num){
1178         case COPS_OBJ_PRID:
1179                if (s_type != 1) /* Not Provisioning Instance Identifier (PRID) */
1180                         break; 
1181
1182                 ti=proto_tree_add_text(tree, tvb, offset, len, "Contents:");
1183                 asn1_object_tree = proto_item_add_subtree(ti, ett_cops_asn1);
1184
1185                 decode_cops_pr_asn1_data(tvb, offset, asn1_object_tree, len);
1186
1187                 break;
1188         case COPS_OBJ_PPRID: 
1189                 if (s_type != 1) /* Not Prefix Provisioning Instance Identifier (PPRID) */
1190                         break; 
1191
1192                 ti = proto_tree_add_text(tree, tvb, offset, len, "Contents:");
1193                 asn1_object_tree = proto_item_add_subtree(ti, ett_cops_asn1);
1194
1195                 decode_cops_pr_asn1_data(tvb, offset, asn1_object_tree, len);
1196
1197                 break;
1198         case COPS_OBJ_EPD:
1199                 if (s_type != 1) /* Not  Encoded Provisioning Instance Data (EPD) */
1200                         break; 
1201
1202                 ti = proto_tree_add_text(tree, tvb, offset, len, "Contents:");
1203                 asn1_object_tree = proto_item_add_subtree(ti, ett_cops_asn1);
1204
1205                 decode_cops_pr_asn1_data(tvb, offset, asn1_object_tree, len);
1206                         
1207                 break;
1208         case COPS_OBJ_GPERR:
1209                 if (s_type != 1) /* Not Global Provisioning Error Object (GPERR) */
1210                         break;
1211                 
1212                 gperror = tvb_get_ntohs(tvb, offset);
1213                 gperror_sub = tvb_get_ntohs(tvb, offset + 2);
1214                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Error-Code: %s, Error Sub-code: 0x%04x",
1215                                          val_to_str(gperror, cops_gperror_vals, "<Unknown value>"), gperror_sub);
1216                 gperror_tree = proto_item_add_subtree(ti, ett_cops_gperror);
1217                 proto_tree_add_uint(gperror_tree, hf_cops_gperror, tvb, offset, 2, gperror);
1218                 offset += 2;
1219                 if (cperror == 13) {
1220                         proto_tree_add_text(gperror_tree, tvb, offset, 2, "Error Sub-code: "
1221                                             "Unknown object's C-Num %u, C-Type %u",
1222                                             tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset + 1));
1223                 } else 
1224                         proto_tree_add_uint(gperror_tree, hf_cops_gperror_sub, tvb, offset, 2, gperror_sub);
1225
1226                 break;
1227         case COPS_OBJ_CPERR:
1228                 if (s_type != 1) /*Not PRC Class Provisioning Error Object (CPERR) */
1229                         break;
1230                 
1231                 break;
1232
1233                 cperror = tvb_get_ntohs(tvb, offset);
1234                 cperror_sub = tvb_get_ntohs(tvb, offset + 2);
1235                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Error-Code: %s, Error Sub-code: 0x%04x",
1236                                          val_to_str(cperror, cops_cperror_vals, "<Unknown value>"), cperror_sub);
1237                 cperror_tree = proto_item_add_subtree(ti, ett_cops_cperror);
1238                 proto_tree_add_uint(cperror_tree, hf_cops_cperror, tvb, offset, 2, cperror);
1239                 offset += 2;
1240                 if (cperror == 13) {
1241                         proto_tree_add_text(cperror_tree, tvb, offset, 2, "Error Sub-code: "
1242                                             "Unknown object's S-Num %u, C-Type %u",
1243                                             tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset + 1));
1244                 } else 
1245                         proto_tree_add_uint(cperror_tree, hf_cops_cperror_sub, tvb, offset, 2, cperror_sub);
1246
1247                 break;
1248         case COPS_OBJ_ERRPRID:
1249                 if (s_type != 1) /*Not  Error Provisioning Instance Identifier (ErrorPRID)*/
1250                         break;
1251
1252                 ti = proto_tree_add_text(tree, tvb, offset, len, "Contents:");
1253                 asn1_object_tree = proto_item_add_subtree(ti, ett_cops_asn1);
1254
1255                 decode_cops_pr_asn1_data(tvb, offset, asn1_object_tree, len);
1256
1257                 break;
1258         default:
1259                 proto_tree_add_text(tree, tvb, offset, len, "Contents: %u bytes", len);
1260                 break;
1261         }
1262
1263         return 0;
1264 }
1265
1266
1267 /* Register the protocol with Ethereal */
1268 void proto_register_cops(void)
1269 {                 
1270
1271         /* Setup list of header fields */
1272         static hf_register_info hf[] = {
1273                 { &hf_cops_ver_flags,
1274                         { "Version and Flags",           "cops.ver_flags",
1275                         FT_UINT8, BASE_HEX, NULL, 0x0,
1276                         "Version and Flags in COPS Common Header", HFILL }
1277                 },
1278                 { &hf_cops_version,
1279                         { "Version",           "cops.version",
1280                         FT_UINT8, BASE_DEC, NULL, 0xF0,
1281                         "Version in COPS Common Header", HFILL }
1282                 },
1283                 { &hf_cops_flags,
1284                         { "Flags",           "cops.flags",
1285                         FT_UINT8, BASE_HEX, VALS(cops_flags_vals), 0x0F,
1286                         "Flags in COPS Common Header", HFILL }
1287                 },
1288                 { &hf_cops_op_code,
1289                         { "Op Code",           "cops.op_code",
1290                         FT_UINT8, BASE_DEC, VALS(cops_op_code_vals), 0x0,
1291                         "Op Code in COPS Common Header", HFILL }
1292                 },
1293                 { &hf_cops_client_type,
1294                         { "Client Type",           "cops.client_type",
1295                         FT_UINT16, BASE_DEC, NULL, 0x0,
1296                         "Client Type in COPS Common Header", HFILL }
1297                 },
1298                 { &hf_cops_msg_len,
1299                         { "Message Length",           "cops.msg_len",
1300                         FT_UINT32, BASE_DEC, NULL, 0x0,
1301                         "Message Length in COPS Common Header", HFILL }
1302                 },
1303                 { &hf_cops_obj_len,
1304                         { "Object Length",           "cops.obj.len",
1305                         FT_UINT32, BASE_DEC, NULL, 0x0,
1306                         "Object Length in COPS Object Header", HFILL }
1307                 },
1308                 { &hf_cops_obj_c_num,
1309                         { "C-Num",           "cops.c_num",
1310                         FT_UINT8, BASE_DEC, VALS(cops_c_num_vals), 0x0,
1311                         "C-Num in COPS Object Header", HFILL }
1312                 },
1313                 { &hf_cops_obj_c_type,
1314                         { "C-Type",           "cops.c_type",
1315                         FT_UINT8, BASE_DEC, NULL, 0x0,
1316                         "C-Type in COPS Object Header", HFILL }
1317                 },
1318
1319                 { &hf_cops_obj_s_num,
1320                         { "S-Num",           "cops.s_num",
1321                         FT_UINT8, BASE_DEC, VALS(cops_s_num_vals), 0x0,
1322                         "S-Num in COPS-PR Object Header", HFILL }
1323                 },
1324                 { &hf_cops_obj_s_type,
1325                         { "S-Type",           "cops.s_type",
1326                         FT_UINT8, BASE_DEC, NULL, 0x0,
1327                         "S-Type in COPS-PR Object Header", HFILL }
1328                 },
1329
1330                 { &hf_cops_r_type_flags,
1331                         { "R-Type",           "cops.context.r_type",
1332                         FT_UINT16, BASE_HEX, VALS(cops_r_type_vals), 0xFFFF,
1333                         "R-Type in COPS Context Object", HFILL }
1334                 },
1335                 { &hf_cops_m_type_flags,
1336                         { "M-Type",           "cops.context.m_type",
1337                         FT_UINT16, BASE_HEX, NULL, 0xFFFF,
1338                         "M-Type in COPS Context Object", HFILL }
1339                 },
1340                 { &hf_cops_in_int_ipv4,
1341                         { "IPv4 address",           "cops.in-int.ipv4",
1342                         FT_IPv4, 0, NULL, 0xFFFF,
1343                         "IPv4 address in COPS IN-Int object", HFILL }
1344                 },
1345                 { &hf_cops_in_int_ipv6,
1346                         { "IPv6 address",           "cops.in-int.ipv6",
1347                         FT_IPv6, 0, NULL, 0xFFFF,
1348                         "IPv6 address in COPS IN-Int object", HFILL }
1349                 },
1350                 { &hf_cops_out_int_ipv4,
1351                         { "IPv4 address",           "cops.out-int.ipv4",
1352                         FT_IPv4, 0, NULL, 0xFFFF,
1353                         "IPv4 address in COPS OUT-Int object", HFILL }
1354                 },
1355                 { &hf_cops_out_int_ipv6,
1356                         { "IPv6 address",           "cops.out-int.ipv6",
1357                         FT_IPv6, 0, NULL, 0xFFFF,
1358                         "IPv6 address in COPS OUT-Int", HFILL }
1359                 },
1360                 { &hf_cops_int_ifindex,
1361                         { "ifIndex",           "cops.in-out-int.ifindex",
1362                         FT_UINT32, BASE_DEC, NULL, 0x0,
1363                         "If SNMP is supported, corresponds to MIB-II ifIndex", HFILL }
1364                 },
1365                 { &hf_cops_reason,
1366                         { "Reason",           "cops.reason",
1367                         FT_UINT16, BASE_DEC, VALS(cops_reason_vals), 0,
1368                         "Reason in Reason object", HFILL }
1369                 },
1370                 { &hf_cops_reason_sub,
1371                         { "Reason Sub-code",           "cops.reason_sub",
1372                         FT_UINT16, BASE_HEX, NULL, 0,
1373                         "Reason Sub-code in Reason object", HFILL }
1374                 },
1375                 { &hf_cops_dec_cmd_code,
1376                         { "Command-Code",           "cops.decision.cmd",
1377                         FT_UINT16, BASE_DEC, VALS(cops_dec_cmd_code_vals), 0,
1378                         "Command-Code in Decision/LPDP Decision object", HFILL }
1379                 },
1380                 { &hf_cops_dec_flags,
1381                         { "Flags",           "cops.decision.flags",
1382                         FT_UINT16, BASE_HEX, VALS(cops_dec_cmd_flag_vals), 0xffff,
1383                         "Flags in Decision/LPDP Decision object", HFILL }
1384                 },
1385                 { &hf_cops_error,
1386                         { "Error",           "cops.error",
1387                         FT_UINT16, BASE_DEC, VALS(cops_error_vals), 0,
1388                         "Error in Error object", HFILL }
1389                 },
1390                 { &hf_cops_error_sub,
1391                         { "Error Sub-code",           "cops.error_sub",
1392                         FT_UINT16, BASE_HEX, NULL, 0,
1393                         "Error Sub-code in Error object", HFILL }
1394                 },
1395                 { &hf_cops_katimer,
1396                         { "Contents: KA Timer Value",           "cops.katimer.value",
1397                         FT_UINT16, BASE_DEC, NULL, 0,
1398                         "Keep-Alive Timer Value in KATimer object", HFILL }
1399                 },
1400                 { &hf_cops_pepid,
1401                         { "Contents: PEP Id",           "cops.pepid.id",
1402                         FT_STRING, BASE_NONE, NULL, 0,
1403                         "PEP Id in PEPID object", HFILL }
1404                 },
1405                 { &hf_cops_report_type,
1406                         { "Contents: Report-Type",           "cops.report_type",
1407                         FT_UINT16, BASE_DEC, VALS(cops_report_type_vals), 0,
1408                         "Report-Type in Report-Type object", HFILL }
1409                 },
1410                 { &hf_cops_pdprediraddr_ipv4,
1411                         { "IPv4 address",           "cops.pdprediraddr.ipv4",
1412                         FT_IPv4, 0, NULL, 0xFFFF,
1413                         "IPv4 address in COPS PDPRedirAddr object", HFILL }
1414                 },
1415                 { &hf_cops_pdprediraddr_ipv6,
1416                         { "IPv6 address",           "cops.pdprediraddr.ipv6",
1417                         FT_IPv6, 0, NULL, 0xFFFF,
1418                         "IPv6 address in COPS PDPRedirAddr object", HFILL }
1419                 },
1420                 { &hf_cops_lastpdpaddr_ipv4,
1421                         { "IPv4 address",           "cops.lastpdpaddr.ipv4",
1422                         FT_IPv4, 0, NULL, 0xFFFF,
1423                         "IPv4 address in COPS LastPDPAddr object", HFILL }
1424                 },
1425                 { &hf_cops_lastpdpaddr_ipv6,
1426                         { "IPv6 address",           "cops.lastpdpaddr.ipv6",
1427                         FT_IPv6, 0, NULL, 0xFFFF,
1428                         "IPv6 address in COPS LastPDPAddr object", HFILL }
1429                 },
1430                 { &hf_cops_pdp_tcp_port,
1431                         { "TCP Port Number",           "cops.pdp.tcp_port",
1432                         FT_UINT32, BASE_DEC, NULL, 0x0,
1433                          "TCP Port Number of PDP in PDPRedirAddr/LastPDPAddr object", HFILL }
1434                 },
1435                 { &hf_cops_accttimer,
1436                         { "Contents: ACCT Timer Value",           "cops.accttimer.value",
1437                         FT_UINT16, BASE_DEC, NULL, 0,
1438                         "Accounting Timer Value in AcctTimer object", HFILL }
1439                 },
1440                 { &hf_cops_key_id,
1441                         { "Contents: Key ID",           "cops.integrity.key_id",
1442                         FT_UINT32, BASE_DEC, NULL, 0,
1443                         "Key ID in Integrity object", HFILL }
1444                 },
1445                 { &hf_cops_seq_num,
1446                         { "Contents: Sequence Number",           "cops.integrity.seq_num",
1447                         FT_UINT32, BASE_DEC, NULL, 0,
1448                         "Sequence Number in Integrity object", HFILL }
1449                 },
1450                 { &hf_cops_gperror,
1451                         { "Error",           "cops.gperror",
1452                         FT_UINT16, BASE_DEC, VALS(cops_gperror_vals), 0,
1453                         "Error in Error object", HFILL }
1454                 },
1455                 { &hf_cops_gperror_sub,
1456                         { "Error Sub-code",           "cops.gperror_sub",
1457                         FT_UINT16, BASE_HEX, NULL, 0,
1458                         "Error Sub-code in Error object", HFILL }
1459                 },
1460                 { &hf_cops_cperror,
1461                         { "Error",           "cops.cperror",
1462                         FT_UINT16, BASE_DEC, VALS(cops_cperror_vals), 0,
1463                         "Error in Error object", HFILL }
1464                 },
1465                 { &hf_cops_cperror_sub,
1466                         { "Error Sub-code",           "cops.cperror_sub",
1467                         FT_UINT16, BASE_HEX, NULL, 0,
1468                         "Error Sub-code in Error object", HFILL }
1469                 },
1470
1471         };
1472
1473         /* Setup protocol subtree array */
1474         static gint *ett[] = {
1475                 &ett_cops,
1476                 &ett_cops_ver_flags,
1477                 &ett_cops_obj,
1478                 &ett_cops_pr_obj,
1479                 &ett_cops_obj_data,
1480                 &ett_cops_r_type_flags,
1481                 &ett_cops_itf,
1482                 &ett_cops_reason,
1483                 &ett_cops_decision,
1484                 &ett_cops_error,
1485                 &ett_cops_clientsi,
1486                 &ett_cops_asn1,
1487                 &ett_cops_gperror,
1488                 &ett_cops_cperror,
1489                 &ett_cops_pdp,
1490         };
1491
1492         module_t* cops_module;
1493
1494         /* Register the protocol name and description */
1495         proto_cops = proto_register_protocol("Common Open Policy Service",
1496             "COPS", "cops");
1497
1498         /* Required function calls to register the header fields and subtrees used */
1499         proto_register_field_array(proto_cops, hf, array_length(hf));
1500         proto_register_subtree_array(ett, array_length(ett));
1501         
1502         /* Register our configuration options for cops, 
1503          * particularly our ports
1504          */
1505         cops_module = prefs_register_protocol(proto_cops,
1506                                               proto_reg_handoff_cops);
1507         prefs_register_uint_preference(cops_module,"tcp.cops_port",
1508                                        "COPS TCP Port",
1509                                        "Set the TCP port for COPS messages",
1510                                        10,&global_cops_tcp_port);
1511         prefs_register_bool_preference(cops_module, "desegment",
1512             "Desegment all COPS messages spanning multiple TCP segments",
1513             "Whether the COPS dissector should desegment all messages spanning multiple TCP segments",
1514             &cops_desegment);
1515 };
1516
1517 void
1518 proto_reg_handoff_cops(void)
1519 {
1520         static int cops_prefs_initialized = FALSE;
1521         static dissector_handle_t cops_handle;
1522
1523         if(!cops_prefs_initialized){
1524           cops_handle = create_dissector_handle(dissect_cops, proto_cops);
1525           cops_prefs_initialized = TRUE;
1526         }
1527         else {
1528           dissector_delete("tcp.port",cops_tcp_port,cops_handle);
1529         }
1530         
1531         /* Set our port numbers for future use */
1532         cops_tcp_port = global_cops_tcp_port;
1533
1534         dissector_add("tcp.port", cops_tcp_port, cops_handle);
1535 }