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