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