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