Added a preference to allow for setting the COPS tcp port.
[obnox/wireshark/wip.git] / packet-cops.c
1 /* packet-cops.c
2  * Routines for the COPS (Common Open Policy Service) protocol dissection
3  * RFC2748
4  *
5  * Copyright 2000, Heikki Vatiainen <hessu@cs.tut.fi>
6  *
7  * $Id: packet-cops.c,v 1.18 2002/02/22 02:56:58 hagbard 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
35 #include <string.h>
36 #include <glib.h>
37 #include <epan/packet.h>
38 #include "packet-ipv6.h"
39 #include "prefs.h"
40
41 #define TCP_PORT_COPS 3288
42
43 /* Variable to hold the tcp port preference */
44 static guint global_cops_tcp_port = TCP_PORT_COPS;
45
46 /* Variable to allow for proper deletion of dissector registration 
47  * when the user changes port from the gui
48  */
49
50 static guint cops_tcp_port = 0;
51
52 #define COPS_OBJECT_HDR_SIZE 4
53
54 static const value_string cops_flags_vals[] = {
55         { 0x00,          "None" },
56         { 0x01,          "Solicited Message Flag Bit" },
57         { 0, NULL },
58 };
59
60 /* The different COPS message types */
61 enum cops_op_code {
62         COPS_NO_MSG,          /* Not a COPS Message type     */ 
63
64         COPS_MSG_REQ,         /* Request (REQ)               */
65         COPS_MSG_DEC,         /* Decision (DEC)              */
66         COPS_MSG_RPT,         /* Report State (RPT)          */
67         COPS_MSG_DRQ,         /* Delete Request State (DRQ)  */
68         COPS_MSG_SSQ,         /* Synchronize State Req (SSQ) */
69         COPS_MSG_OPN,         /* Client-Open (OPN)           */
70         COPS_MSG_CAT,         /* Client-Accept (CAT)         */
71         COPS_MSG_CC,          /* Client-Close (CC)           */
72         COPS_MSG_KA,          /* Keep-Alive (KA)             */
73         COPS_MSG_SSC,         /* Synchronize Complete (SSC)  */
74
75         COPS_LAST_OP_CODE     /* For error checking          */
76 };
77
78 static const value_string cops_op_code_vals[] = {
79         { COPS_MSG_REQ,          "Request (REQ)" },
80         { COPS_MSG_DEC,          "Decision (DEC)" },
81         { COPS_MSG_RPT,          "Report State (RPT)" },
82         { COPS_MSG_DRQ,          "Delete Request State (DRQ)" },
83         { COPS_MSG_SSQ,          "Synchronize State Req (SSQ)" },
84         { COPS_MSG_OPN,          "Client-Open (OPN)" },
85         { COPS_MSG_CAT,          "Client-Accept (CAT)" },
86         { COPS_MSG_CC,           "Client-Close (CC)" },
87         { COPS_MSG_KA,           "Keep-Alive (KA)" },
88         { COPS_MSG_SSC,          "Synchronize Complete (SSC)" },
89         { 0, NULL },
90 };
91
92
93 /* The different objects in COPS messages */
94 enum cops_c_num {
95         COPS_NO_OBJECT,        /* Not a COPS Object type               */
96
97         COPS_OBJ_HANDLE,       /* Handle Object (Handle)               */
98         COPS_OBJ_CONTEXT,      /* Context Object (Context)             */
99         COPS_OBJ_IN_INT,       /* In-Interface Object (IN-Int)         */
100         COPS_OBJ_OUT_INT,      /* Out-Interface Object (OUT-Int)       */
101         COPS_OBJ_REASON,       /* Reason Object (Reason)               */
102         COPS_OBJ_DECISION,     /* Decision Object (Decision)           */
103         COPS_OBJ_LPDPDECISION, /* LPDP Decision Object (LPDPDecision)  */
104         COPS_OBJ_ERROR,        /* Error Object (Error)                 */
105         COPS_OBJ_CLIENTSI,     /* Client Specific Information Object (ClientSI) */
106         COPS_OBJ_KATIMER,      /* Keep-Alive Timer Object (KATimer)    */
107         COPS_OBJ_PEPID,        /* PEP Identification Object (PEPID)    */
108         COPS_OBJ_REPORT_TYPE,  /* Report-Type Object (Report-Type)     */
109         COPS_OBJ_PDPREDIRADDR, /* PDP Redirect Address Object (PDPRedirAddr) */
110         COPS_OBJ_LASTPDPADDR,  /* Last PDP Address (LastPDPaddr)       */
111         COPS_OBJ_ACCTTIMER,    /* Accounting Timer Object (AcctTimer)  */
112         COPS_OBJ_INTEGRITY,    /* Message Integrity Object (Integrity) */
113
114         COPS_LAST_C_NUM        /* For error checking                   */
115 };
116
117 static const value_string cops_c_num_vals[] = {
118         { COPS_OBJ_HANDLE,       "Handle Object (Handle)" },
119         { COPS_OBJ_CONTEXT,      "Context Object (Context)" },
120         { COPS_OBJ_IN_INT,       "In-Interface Object (IN-Int)" },
121         { COPS_OBJ_OUT_INT,      "Out-Interface Object (OUT-Int)" },
122         { COPS_OBJ_REASON,       "Reason Object (Reason)" },
123         { COPS_OBJ_DECISION,     "Decision Object (Decision)" },
124         { COPS_OBJ_LPDPDECISION, "LPDP Decision Object (LPDPDecision)" },
125         { COPS_OBJ_ERROR,        "Error Object (Error)" },
126         { COPS_OBJ_CLIENTSI,     "Client Specific Information Object (ClientSI)" },
127         { COPS_OBJ_KATIMER,      "Keep-Alive Timer Object (KATimer)" },
128         { COPS_OBJ_PEPID,        "PEP Identification Object (PEPID)" },
129         { COPS_OBJ_REPORT_TYPE,  "Report-Type Object (Report-Type)" },
130         { COPS_OBJ_PDPREDIRADDR, "PDP Redirect Address Object (PDPRedirAddr)" },
131         { COPS_OBJ_LASTPDPADDR,  "Last PDP Address (LastPDPaddr)" },
132         { COPS_OBJ_ACCTTIMER,    "Accounting Timer Object (AcctTimer)" },
133         { COPS_OBJ_INTEGRITY,    "Message Integrity Object (Integrity)" },
134         { 0, NULL },
135
136 };
137
138 /* R-Type is carried within the Context Object */
139 static const value_string cops_r_type_vals[] = {
140         { 0x01, "Incoming-Message/Admission Control request" },
141         { 0x02, "Resource-Allocation request" },
142         { 0x04, "Outgoing-Message request" },
143         { 0x08, "Configuration request" },
144         { 0, NULL },
145 };
146
147 /* Reason-Code is carried within the Reason object */
148 static const value_string cops_reason_vals[] = {
149         { 1,  "Unspecified" },
150         { 2,  "Management" },
151         { 3,  "Preempted (Another request state takes precedence)" },
152         { 4,  "Tear (Used to communicate a signaled state removal)" },
153         { 5,  "Timeout (Local state has timed-out)" },
154         { 6,  "Route Change (Change invalidates request state)" },
155         { 7,  "Insufficient Resources (No local resource available)" },
156         { 8,  "PDP's Directive (PDP decision caused the delete)" },
157         { 9,  "Unsupported decision (PDP decision not supported)" },
158         { 10, "Synchronize Handle Unknown" },
159         { 11, "Transient Handle (stateless event)" },
160         { 12, "Malformed Decision (could not recover)" },
161         { 13, "Unknown COPS Object from PDP" },
162         { 0, NULL },
163 };
164
165 /* Command-Code is carried within the Decision object if C-Type is 1 */
166 static const value_string cops_dec_cmd_code_vals[] = {
167         { 0, "NULL Decision (No configuration data available)" },
168         { 1, "Install (Admit request/Install configuration)" },
169         { 2, "Remove (Remove request/Remove configuration)" },
170         { 0, NULL },
171 };
172
173 /* Decision flags are also carried with the Decision object if C-Type is 1 */
174 static const value_string cops_dec_cmd_flag_vals[] = {
175         { 0x00, "<None set>" },
176         { 0x01, "Trigger Error (Trigger error message if set)" },
177         { 0, NULL },
178 };
179
180 /* Error-Code from Error object */
181 static const value_string cops_error_vals[] = {
182         {1,  "Bad handle" },
183         {2,  "Invalid handle reference" },
184         {3,  "Bad message format (Malformed Message)" },
185         {4,  "Unable to process (server gives up on query)" },
186         {5,  "Mandatory client-specific info missing" },
187         {6,  "Unsupported client" },
188         {7,  "Mandatory COPS object missing" },
189         {8,  "Client Failure" },
190         {9,  "Communication Failure" },
191         {10, "Unspecified" },
192         {11, "Shutting down" },
193         {12, "Redirect to Preferred Server" },
194         {13, "Unknown COPS Object" },
195         {14, "Authentication Failure" },
196         {15, "Authentication Required" },
197         {0,  NULL },
198 };
199
200 /* Report-Type from Report-Type object */
201 static const value_string cops_report_type_vals[] = {
202         {1, " Success   : Decision was successful at the PEP" },
203         {2, " Failure   : Decision could not be completed by PEP" },
204         {3, " Accounting: Accounting update for an installed state" },
205         {0, NULL },
206 };
207
208 /* Initialize the protocol and registered fields */
209 static gint proto_cops = -1;
210 static gint hf_cops_ver_flags = -1;
211 static gint hf_cops_version = -1;
212 static gint hf_cops_flags = -1;
213
214 static gint hf_cops_op_code = -1;
215 static gint hf_cops_client_type = -1;
216 static gint hf_cops_msg_len = -1;
217
218 static gint hf_cops_obj_len = -1;
219 static gint hf_cops_obj_c_num = -1;
220 static gint hf_cops_obj_c_type = -1;
221
222 static gint hf_cops_r_type_flags = -1;
223 static gint hf_cops_m_type_flags = -1;
224
225 static gint hf_cops_in_int_ipv4 = -1;
226 static gint hf_cops_in_int_ipv6 = -1;
227 static gint hf_cops_out_int_ipv4 = -1;
228 static gint hf_cops_out_int_ipv6 = -1;
229 static gint hf_cops_int_ifindex = -1;
230
231 static gint hf_cops_reason = -1;
232 static gint hf_cops_reason_sub = -1;
233
234 static gint hf_cops_dec_cmd_code = -1;
235 static gint hf_cops_dec_flags = -1;
236
237 static gint hf_cops_error = -1;
238 static gint hf_cops_error_sub = -1;
239
240 static gint hf_cops_katimer = -1;
241
242 static gint hf_cops_pepid = -1;
243
244 static gint hf_cops_report_type = -1;
245
246 static gint hf_cops_pdprediraddr_ipv4 = -1;
247 static gint hf_cops_pdprediraddr_ipv6 = -1;
248 static gint hf_cops_lastpdpaddr_ipv4 = -1;
249 static gint hf_cops_lastpdpaddr_ipv6 = -1;
250 static gint hf_cops_pdp_tcp_port = -1;
251
252 static gint hf_cops_accttimer = -1;
253
254 static gint hf_cops_key_id = -1;
255 static gint hf_cops_seq_num = -1;
256
257 /* Initialize the subtree pointers */
258 static gint ett_cops = -1;
259 static gint ett_cops_ver_flags = -1;
260 static gint ett_cops_obj = -1;
261 static gint ett_cops_obj_data = -1;
262 static gint ett_cops_r_type_flags = -1;
263 static gint ett_cops_itf = -1;
264 static gint ett_cops_reason = -1;
265 static gint ett_cops_decision = -1;
266 static gint ett_cops_error = -1;
267 static gint ett_cops_pdp = -1;
268
269 void proto_reg_handoff_cops(void);
270
271 static int dissect_cops_object(tvbuff_t *tvb, guint32 offset, proto_tree *tree);
272 static int dissect_cops_object_data(tvbuff_t *tvb, guint32 offset, proto_tree *tree,
273                                     guint8 c_num, guint8 c_type, guint16 len);
274
275 /* Code to actually dissect the packets */
276 static void dissect_cops(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
277 {
278         guint8 op_code;
279
280         if (check_col(pinfo->cinfo, COL_PROTOCOL)) 
281                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "COPS");
282         if (check_col(pinfo->cinfo, COL_INFO)) 
283                 col_clear(pinfo->cinfo, COL_INFO);
284     
285         op_code = tvb_get_guint8(tvb, 1);
286         if (check_col(pinfo->cinfo, COL_INFO))
287                 col_add_fstr(pinfo->cinfo, COL_INFO, "COPS %s",
288                              val_to_str(op_code, cops_op_code_vals, "Unknown Op Code"));
289
290         if (tree) {
291                 proto_item *ti, *tv;
292                 proto_tree *cops_tree, *ver_flags_tree;
293                 guint32 offset, msg_len;
294                 guint8 ver_flags;
295                 gint garbage;
296
297                 offset = 0;
298                 ti = proto_tree_add_item(tree, proto_cops, tvb, offset, -1, FALSE);
299                 cops_tree = proto_item_add_subtree(ti, ett_cops);
300
301                 /* Version and flags share the same byte, put them in a subtree */
302                 ver_flags = tvb_get_guint8(tvb, offset);
303                 tv = proto_tree_add_uint_format(cops_tree, hf_cops_ver_flags, tvb, offset, 1,
304                                                   ver_flags, "Version: %u, Flags: %s",
305                                                   hi_nibble(ver_flags),
306                                                   val_to_str(lo_nibble(ver_flags), cops_flags_vals, "Unknown"));
307                 ver_flags_tree = proto_item_add_subtree(tv, ett_cops_ver_flags);
308                 proto_tree_add_uint(ver_flags_tree, hf_cops_version, tvb, offset, 1, ver_flags);
309                 proto_tree_add_uint(ver_flags_tree, hf_cops_flags, tvb, offset, 1, ver_flags);
310                 offset++;
311
312                 proto_tree_add_uint(cops_tree, hf_cops_op_code, tvb, offset, 1, tvb_get_guint8(tvb, offset));
313                 offset ++;
314                 proto_tree_add_uint(cops_tree, hf_cops_client_type, tvb, offset, 2, tvb_get_ntohs(tvb, offset));
315                 offset += 2;
316
317                 msg_len = tvb_get_ntohl(tvb, offset);
318                 proto_tree_add_uint(cops_tree, hf_cops_msg_len, tvb, offset, 4, tvb_get_ntohl(tvb, offset));
319                 offset += 4;
320
321                 while (msg_len >= COPS_OBJECT_HDR_SIZE) {
322                         int consumed;
323
324                         consumed = dissect_cops_object(tvb, offset, cops_tree);
325                         if (consumed == 0)
326                                 break;
327                         msg_len -= consumed;
328                         offset += consumed;
329                 }
330
331                 garbage = tvb_length_remaining(tvb, offset);
332                 if (garbage > 0)
333                         proto_tree_add_text(cops_tree, tvb, offset, garbage,
334                                             "Trailing garbage: %d byte%s", garbage,
335                                             plurality(garbage, "", "s"));
336         }
337
338         return;
339 }
340
341 static char *cops_c_type_to_str(guint8 c_num, guint8 c_type)
342 {
343         switch (c_num) {
344         case COPS_OBJ_HANDLE:
345                 if (c_type == 1)
346                         return "Client Handle";
347                 break;
348         case COPS_OBJ_IN_INT:
349         case COPS_OBJ_OUT_INT:
350                 if (c_type == 1)
351                         return "IPv4 Address + Interface";
352                 else if (c_type == 2)
353                         return "IPv6 Address + Interface";
354                 break;
355         case COPS_OBJ_DECISION:
356         case COPS_OBJ_LPDPDECISION:
357                 if (c_type == 1)
358                         return "Decision Flags (Mandatory)";
359                 else if (c_type == 2)
360                         return "Stateless Data";
361                 else if (c_type == 3)
362                         return "Replacement Data";
363                 else if (c_type == 4)
364                         return "Client Specific Decision Data";
365                 else if (c_type == 5)
366                         return "Named Decision Data";
367                 break;
368         case COPS_OBJ_CLIENTSI:
369                 if (c_type == 1)
370                         return "Signaled ClientSI";
371                 else if (c_type == 2)
372                         return "Named ClientSI";
373                 break;
374         case COPS_OBJ_KATIMER:
375                 if (c_type == 1)
376                         return "Keep-alive timer value";
377                 break;
378         case COPS_OBJ_PDPREDIRADDR:
379         case COPS_OBJ_LASTPDPADDR:
380                 if (c_type == 1)
381                         return "IPv4 Address + TCP Port";
382                 else if (c_type == 2)
383                         return "IPv6 Address + TCP Port";
384                 break;
385         case COPS_OBJ_ACCTTIMER:
386                 if (c_type == 1)
387                         return "Accounting timer value";
388                 break;
389         case COPS_OBJ_INTEGRITY:
390                 if (c_type == 1)
391                         return "HMAC digest";
392                 break;
393         }
394
395         return "";
396 }
397
398 static int dissect_cops_object(tvbuff_t *tvb, guint32 offset, proto_tree *tree)
399 {
400         guint16 object_len, contents_len;
401         guint8 c_num, c_type;
402         proto_item *ti;
403         proto_tree *obj_tree;
404         char *type_str;
405         int ret;
406
407         if (tvb_reported_length_remaining(tvb, offset) < COPS_OBJECT_HDR_SIZE)
408                 return 0;
409
410         object_len = tvb_get_ntohs(tvb, offset);
411         c_num = tvb_get_guint8(tvb, offset + 2);
412         c_type = tvb_get_guint8(tvb, offset + 3);
413
414         ti = proto_tree_add_uint_format(tree, hf_cops_obj_c_num, tvb, offset, object_len, c_num,
415                                         "%s: %s", val_to_str(c_num, cops_c_num_vals, "Unknown"),
416                                         cops_c_type_to_str(c_num, c_type));
417         obj_tree = proto_item_add_subtree(ti, ett_cops_obj);
418
419         proto_tree_add_uint(obj_tree, hf_cops_obj_len, tvb, offset, 2, tvb_get_ntohs(tvb, offset));
420         offset += 2;
421
422         proto_tree_add_uint(obj_tree, hf_cops_obj_c_num, tvb, offset, 1, c_num);
423         offset++;
424
425         type_str = cops_c_type_to_str(c_num, c_type);
426         proto_tree_add_text(obj_tree, tvb, offset, 1, "C-Type: %s%s%u%s",
427                             type_str,
428                             strlen(type_str) ? " (" : "",
429                             c_type,
430                             strlen(type_str) ? ")" : "");
431         offset++;
432
433         contents_len = object_len - COPS_OBJECT_HDR_SIZE;
434         ret = dissect_cops_object_data(tvb, offset, obj_tree, c_num, c_type, contents_len);
435         if (ret < 0) return 0;
436
437         /* Pad to 32bit boundary */
438         if (object_len % sizeof (guint32))
439                 object_len += (sizeof (guint32) - object_len % sizeof (guint32));
440
441         return object_len;
442         
443 }
444
445 static int dissect_cops_object_data(tvbuff_t *tvb, guint32 offset, proto_tree *tree,
446                                     guint8 c_num, guint8 c_type, guint16 len)
447 {
448         proto_item *ti;
449         proto_tree *r_type_tree, *itf_tree, *reason_tree, *dec_tree, *error_tree, *pdp_tree;
450         guint16 r_type, m_type, reason, reason_sub, cmd_code, cmd_flags, error, error_sub, tcp_port;
451         guint32 ipv4addr, ifindex;
452         struct e_in6_addr ipv6addr;
453
454         switch (c_num) {
455         case COPS_OBJ_CONTEXT:
456                 r_type = tvb_get_ntohs(tvb, offset);
457                 m_type = tvb_get_ntohs(tvb, offset + 2);
458                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: R-Type: %s, M-Type: %u",
459                                          val_to_str(r_type, cops_r_type_vals, "Unknown"),
460                                          m_type);
461
462                 r_type_tree = proto_item_add_subtree(ti, ett_cops_r_type_flags);
463                 proto_tree_add_uint(r_type_tree, hf_cops_r_type_flags, tvb, offset, 2, r_type);
464                 offset += 2;
465                 proto_tree_add_uint(r_type_tree, hf_cops_m_type_flags, tvb, offset, 2, m_type);
466
467                 return 0;
468                 break;
469         case COPS_OBJ_IN_INT:
470         case COPS_OBJ_OUT_INT:
471                 if (c_type == 1) {          /* IPv4 */
472                         tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset, 4);
473                         ifindex = tvb_get_ntohl(tvb, offset + 4);
474                         ti = proto_tree_add_text(tree, tvb, offset, 8, "Contents: IPv4 address %s, ifIndex: %u",
475                                                  ip_to_str((guint8 *)&ipv4addr), ifindex);
476                         itf_tree = proto_item_add_subtree(ti, ett_cops_itf);
477                         proto_tree_add_ipv4(itf_tree,
478                                             (c_num == COPS_OBJ_IN_INT) ? hf_cops_in_int_ipv4 : hf_cops_out_int_ipv4,
479                                             tvb, offset, 4, ipv4addr);
480                         offset += 4;
481                 } else if (c_type == 2) {   /* IPv6 */
482                         tvb_memcpy(tvb, (guint8 *)&ipv6addr, offset, sizeof ipv6addr);
483                         ifindex = tvb_get_ntohl(tvb, offset + sizeof ipv6addr);
484                         ti = proto_tree_add_text(tree, tvb, offset, 20, "Contents: IPv6 address %s, ifIndex: %u",
485                                                  ip6_to_str(&ipv6addr), ifindex);
486                         itf_tree = proto_item_add_subtree(ti, ett_cops_itf);
487                         proto_tree_add_ipv6(itf_tree,
488                                             (c_num == COPS_OBJ_IN_INT) ? hf_cops_in_int_ipv6 : hf_cops_out_int_ipv6,
489                                             tvb, offset, 16, (guint8 *)&ipv6addr);
490                         offset += 16;
491                 } else {
492                         break;
493                 }
494                 proto_tree_add_uint(itf_tree, hf_cops_int_ifindex, tvb, offset, 4, ifindex);
495
496                 return 0;
497                 break;
498         case COPS_OBJ_REASON:
499                 reason = tvb_get_ntohs(tvb, offset);
500                 reason_sub = tvb_get_ntohs(tvb, offset + 2);
501                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Reason-Code: %s, Reason Sub-code: 0x%04x",
502                                          val_to_str(reason, cops_reason_vals, "<Unknown value>"), reason_sub);
503                 reason_tree = proto_item_add_subtree(ti, ett_cops_reason);
504                 proto_tree_add_uint(reason_tree, hf_cops_reason, tvb, offset, 2, reason);
505                 offset += 2;
506                 if (reason == 13) {
507                         proto_tree_add_text(reason_tree, tvb, offset, 2, "Reason Sub-code: "
508                                             "Unknown object's C-Num %u, C-Type %u",
509                                             tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset + 1));
510                 } else 
511                         proto_tree_add_uint(reason_tree, hf_cops_reason_sub, tvb, offset, 2, reason_sub);
512
513                 return 0;
514                 break;
515         case COPS_OBJ_DECISION:
516         case COPS_OBJ_LPDPDECISION:
517                 if (c_type != 1)
518                         break;
519
520                 cmd_code = tvb_get_ntohs(tvb, offset);
521                 cmd_flags = tvb_get_ntohs(tvb, offset + 2);
522                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Command-Code: %s, Flags: %s",
523                                          val_to_str(cmd_code, cops_dec_cmd_code_vals, "<Unknown value>"),
524                                          val_to_str(cmd_flags, cops_dec_cmd_flag_vals, "<Unknown flag>"));
525                 dec_tree = proto_item_add_subtree(ti, ett_cops_decision);
526                 proto_tree_add_uint(dec_tree, hf_cops_dec_cmd_code, tvb, offset, 2, cmd_code);
527                 offset += 2;
528                 proto_tree_add_uint(dec_tree, hf_cops_dec_flags, tvb, offset, 2, cmd_flags);
529                 
530                 return 0;
531                 break;
532         case COPS_OBJ_ERROR:
533                 if (c_type != 1)
534                         break;
535                 
536                 error = tvb_get_ntohs(tvb, offset);
537                 error_sub = tvb_get_ntohs(tvb, offset + 2);
538                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Error-Code: %s, Error Sub-code: 0x%04x",
539                                          val_to_str(error, cops_error_vals, "<Unknown value>"), error_sub);
540                 error_tree = proto_item_add_subtree(ti, ett_cops_error);
541                 proto_tree_add_uint(error_tree, hf_cops_error, tvb, offset, 2, error);
542                 offset += 2;
543                 if (error == 13) {
544                         proto_tree_add_text(error_tree, tvb, offset, 2, "Error Sub-code: "
545                                             "Unknown object's C-Num %u, C-Type %u",
546                                             tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset + 1));
547                 } else 
548                         proto_tree_add_uint(error_tree, hf_cops_error_sub, tvb, offset, 2, error_sub);
549
550                 return 0;
551                 break;
552         case COPS_OBJ_KATIMER:
553                 if (c_type != 1)
554                         break;
555
556                 proto_tree_add_item(tree, hf_cops_katimer, tvb, offset + 2, 2, FALSE);
557                 if (tvb_get_ntohs(tvb, offset + 2) == 0)
558                         proto_tree_add_text(tree, tvb, offset, 0, "Value of zero implies infinity.");
559                 
560                 return 0;
561                 break;
562         case COPS_OBJ_PEPID:
563                 if (c_type != 1)
564                         break;
565
566                 if (tvb_strnlen(tvb, offset, len) == -1)
567                         proto_tree_add_text(tree, tvb, offset, len, "<PEP Id is not a NUL terminated ASCII string>");
568                 else
569                         proto_tree_add_item(tree, hf_cops_pepid, tvb, offset,
570                                             tvb_strnlen(tvb, offset, len) + 1, FALSE);
571
572                 return 0;
573                 break;
574         case COPS_OBJ_REPORT_TYPE:
575                 if (c_type != 1)
576                         break;
577
578                 proto_tree_add_item(tree, hf_cops_report_type, tvb, offset, 2, FALSE);
579
580                 return 0;
581                 break;
582         case COPS_OBJ_PDPREDIRADDR:
583         case COPS_OBJ_LASTPDPADDR:
584                 if (c_type == 1) {          /* IPv4 */
585                         tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset, 4);
586                         tcp_port = tvb_get_ntohs(tvb, offset + 4 + 2);
587                         ti = proto_tree_add_text(tree, tvb, offset, 8, "Contents: IPv4 address %s, TCP Port Number: %u",
588                                                  ip_to_str((guint8 *)&ipv4addr), tcp_port);
589                         pdp_tree = proto_item_add_subtree(ti, ett_cops_pdp);
590                         proto_tree_add_ipv4(pdp_tree,
591                                             (c_num == COPS_OBJ_PDPREDIRADDR) ? hf_cops_pdprediraddr_ipv4 : hf_cops_lastpdpaddr_ipv4,
592                                             tvb, offset, 4, ipv4addr);
593                         offset += 4;
594                 } else if (c_type == 2) {   /* IPv6 */
595                         tvb_memcpy(tvb, (guint8 *)&ipv6addr, offset, sizeof ipv6addr);
596                         tcp_port = tvb_get_ntohs(tvb, offset + sizeof ipv6addr + 2);
597                         ti = proto_tree_add_text(tree, tvb, offset, 20, "Contents: IPv6 address %s, TCP Port Number: %u",
598                                                  ip6_to_str(&ipv6addr), tcp_port);
599                         pdp_tree = proto_item_add_subtree(ti, ett_cops_pdp);
600                         proto_tree_add_ipv6(pdp_tree,
601                                             (c_num == COPS_OBJ_PDPREDIRADDR) ? hf_cops_pdprediraddr_ipv6 : hf_cops_lastpdpaddr_ipv6,
602                                             tvb, offset, 16, (guint8 *)&ipv6addr);
603                         offset += 16;
604                 } else {
605                         break;
606                 }
607                 offset += 2;
608                 proto_tree_add_uint(pdp_tree, hf_cops_pdp_tcp_port, tvb, offset, 2, tcp_port);
609
610                 return 0;
611                 break;
612         case COPS_OBJ_ACCTTIMER:
613                 if (c_type != 1)
614                         break;
615
616                 proto_tree_add_item(tree, hf_cops_accttimer, tvb, offset + 2, 2, FALSE);
617                 if (tvb_get_ntohs(tvb, offset + 2) == 0)
618                         proto_tree_add_text(tree, tvb, offset, 0, "Value of zero means "
619                                             "there SHOULD be no unsolicited accounting updates.");
620
621                 return 0;
622                 break;
623         case COPS_OBJ_INTEGRITY:
624                 if (c_type != 1)
625                         break;      /* Not HMAC digest */
626
627                 proto_tree_add_item(tree, hf_cops_key_id, tvb, offset, 4, FALSE);
628                 proto_tree_add_item(tree, hf_cops_seq_num, tvb, offset + 4, 4, FALSE);
629                 proto_tree_add_text(tree, tvb, offset + 8 , len - 8, "Contents: Keyed Message Digest");
630
631                 return 0;
632                 break;
633         default:
634                 break;
635         }
636
637         ti = proto_tree_add_text(tree, tvb, offset, len, "Contents: %u bytes", len);
638
639         return 0;
640 }
641
642 /* Register the protocol with Ethereal */
643 void proto_register_cops(void)
644 {                 
645
646         /* Setup list of header fields */
647         static hf_register_info hf[] = {
648                 { &hf_cops_ver_flags,
649                         { "Version and Flags",           "cops.ver_flags",
650                         FT_UINT8, BASE_HEX, NULL, 0x0,
651                         "Version and Flags in COPS Common Header", HFILL }
652                 },
653                 { &hf_cops_version,
654                         { "Version",           "cops.version",
655                         FT_UINT8, BASE_DEC, NULL, 0xF0,
656                         "Version in COPS Common Header", HFILL }
657                 },
658                 { &hf_cops_flags,
659                         { "Flags",           "cops.flags",
660                         FT_UINT8, BASE_HEX, VALS(cops_flags_vals), 0x0F,
661                         "Flags in COPS Common Header", HFILL }
662                 },
663                 { &hf_cops_op_code,
664                         { "Op Code",           "cops.op_code",
665                         FT_UINT8, BASE_DEC, VALS(cops_op_code_vals), 0x0,
666                         "Op Code in COPS Common Header", HFILL }
667                 },
668                 { &hf_cops_client_type,
669                         { "Client Type",           "cops.client_type",
670                         FT_UINT16, BASE_DEC, NULL, 0x0,
671                         "Client Type in COPS Common Header", HFILL }
672                 },
673                 { &hf_cops_msg_len,
674                         { "Message Length",           "cops.msg_len",
675                         FT_UINT32, BASE_DEC, NULL, 0x0,
676                         "Message Length in COPS Common Header", HFILL }
677                 },
678                 { &hf_cops_obj_len,
679                         { "Object Length",           "cops.obj.len",
680                         FT_UINT32, BASE_DEC, NULL, 0x0,
681                         "Object Length in COPS Object Header", HFILL }
682                 },
683                 { &hf_cops_obj_c_num,
684                         { "C-Num",           "cops.c_num",
685                         FT_UINT8, BASE_DEC, VALS(cops_c_num_vals), 0x0,
686                         "C-Num in COPS Object Header", HFILL }
687                 },
688                 { &hf_cops_obj_c_type,
689                         { "C-Type",           "cops.c_type",
690                         FT_UINT8, BASE_DEC, NULL, 0x0,
691                         "C-Type in COPS Object Header", HFILL }
692                 },
693                 { &hf_cops_r_type_flags,
694                         { "R-Type",           "cops.context.r_type",
695                         FT_UINT16, BASE_HEX, VALS(cops_r_type_vals), 0xFFFF,
696                         "R-Type in COPS Context Object", HFILL }
697                 },
698                 { &hf_cops_m_type_flags,
699                         { "M-Type",           "cops.context.m_type",
700                         FT_UINT16, BASE_HEX, NULL, 0xFFFF,
701                         "M-Type in COPS Context Object", HFILL }
702                 },
703                 { &hf_cops_in_int_ipv4,
704                         { "IPv4 address",           "cops.in-int.ipv4",
705                         FT_IPv4, 0, NULL, 0xFFFF,
706                         "IPv4 address in COPS IN-Int object", HFILL }
707                 },
708                 { &hf_cops_in_int_ipv6,
709                         { "IPv6 address",           "cops.in-int.ipv6",
710                         FT_IPv6, 0, NULL, 0xFFFF,
711                         "IPv6 address in COPS IN-Int object", HFILL }
712                 },
713                 { &hf_cops_out_int_ipv4,
714                         { "IPv4 address",           "cops.out-int.ipv4",
715                         FT_IPv4, 0, NULL, 0xFFFF,
716                         "IPv4 address in COPS OUT-Int object", HFILL }
717                 },
718                 { &hf_cops_out_int_ipv6,
719                         { "IPv6 address",           "cops.out-int.ipv6",
720                         FT_IPv6, 0, NULL, 0xFFFF,
721                         "IPv6 address in COPS OUT-Int", HFILL }
722                 },
723                 { &hf_cops_int_ifindex,
724                         { "ifIndex",           "cops.in-out-int.ifindex",
725                         FT_UINT32, BASE_DEC, NULL, 0x0,
726                         "If SNMP is supported, corresponds to MIB-II ifIndex", HFILL }
727                 },
728                 { &hf_cops_reason,
729                         { "Reason",           "cops.reason",
730                         FT_UINT16, BASE_DEC, VALS(cops_reason_vals), 0,
731                         "Reason in Reason object", HFILL }
732                 },
733                 { &hf_cops_reason_sub,
734                         { "Reason Sub-code",           "cops.reason_sub",
735                         FT_UINT16, BASE_HEX, NULL, 0,
736                         "Reason Sub-code in Reason object", HFILL }
737                 },
738                 { &hf_cops_dec_cmd_code,
739                         { "Command-Code",           "cops.decision.cmd",
740                         FT_UINT16, BASE_DEC, VALS(cops_dec_cmd_code_vals), 0,
741                         "Command-Code in Decision/LPDP Decision object", HFILL }
742                 },
743                 { &hf_cops_dec_flags,
744                         { "Flags",           "cops.decision.flags",
745                         FT_UINT16, BASE_HEX, VALS(cops_dec_cmd_flag_vals), 0xffff,
746                         "Flags in Decision/LPDP Decision object", HFILL }
747                 },
748                 { &hf_cops_error,
749                         { "Error",           "cops.error",
750                         FT_UINT16, BASE_DEC, VALS(cops_error_vals), 0,
751                         "Error in Error object", HFILL }
752                 },
753                 { &hf_cops_error_sub,
754                         { "Error Sub-code",           "cops.error_sub",
755                         FT_UINT16, BASE_HEX, NULL, 0,
756                         "Error Sub-code in Error object", HFILL }
757                 },
758                 { &hf_cops_katimer,
759                         { "Contents: KA Timer Value",           "cops.katimer.value",
760                         FT_UINT16, BASE_DEC, NULL, 0,
761                         "Keep-Alive Timer Value in KATimer object", HFILL }
762                 },
763                 { &hf_cops_pepid,
764                         { "Contents: PEP Id",           "cops.pepid.id",
765                         FT_STRING, BASE_NONE, NULL, 0,
766                         "PEP Id in PEPID object", HFILL }
767                 },
768                 { &hf_cops_report_type,
769                         { "Contents: Report-Type",           "cops.report_type",
770                         FT_UINT16, BASE_DEC, VALS(cops_report_type_vals), 0,
771                         "Report-Type in Report-Type object", HFILL }
772                 },
773                 { &hf_cops_pdprediraddr_ipv4,
774                         { "IPv4 address",           "cops.pdprediraddr.ipv4",
775                         FT_IPv4, 0, NULL, 0xFFFF,
776                         "IPv4 address in COPS PDPRedirAddr object", HFILL }
777                 },
778                 { &hf_cops_pdprediraddr_ipv6,
779                         { "IPv6 address",           "cops.pdprediraddr.ipv6",
780                         FT_IPv6, 0, NULL, 0xFFFF,
781                         "IPv6 address in COPS PDPRedirAddr object", HFILL }
782                 },
783                 { &hf_cops_lastpdpaddr_ipv4,
784                         { "IPv4 address",           "cops.lastpdpaddr.ipv4",
785                         FT_IPv4, 0, NULL, 0xFFFF,
786                         "IPv4 address in COPS LastPDPAddr object", HFILL }
787                 },
788                 { &hf_cops_lastpdpaddr_ipv6,
789                         { "IPv6 address",           "cops.lastpdpaddr.ipv6",
790                         FT_IPv6, 0, NULL, 0xFFFF,
791                         "IPv6 address in COPS LastPDPAddr object", HFILL }
792                 },
793                 { &hf_cops_pdp_tcp_port,
794                         { "TCP Port Number",           "cops.pdp.tcp_port",
795                         FT_UINT32, BASE_DEC, NULL, 0x0,
796                          "TCP Port Number of PDP in PDPRedirAddr/LastPDPAddr object", HFILL }
797                 },
798                 { &hf_cops_accttimer,
799                         { "Contents: ACCT Timer Value",           "cops.accttimer.value",
800                         FT_UINT16, BASE_DEC, NULL, 0,
801                         "Accounting Timer Value in AcctTimer object", HFILL }
802                 },
803                 { &hf_cops_key_id,
804                         { "Contents: Key ID",           "cops.integrity.key_id",
805                         FT_UINT32, BASE_DEC, NULL, 0,
806                         "Key ID in Integrity object", HFILL }
807                 },
808                 { &hf_cops_seq_num,
809                         { "Contents: Sequence Number",           "cops.integrity.seq_num",
810                         FT_UINT32, BASE_DEC, NULL, 0,
811                         "Sequence Number in Integrity object", HFILL }
812                 },
813         };
814
815         /* Setup protocol subtree array */
816         static gint *ett[] = {
817                 &ett_cops,
818                 &ett_cops_ver_flags,
819                 &ett_cops_obj,
820                 &ett_cops_obj_data,
821                 &ett_cops_r_type_flags,
822                 &ett_cops_itf,
823                 &ett_cops_reason,
824                 &ett_cops_decision,
825                 &ett_cops_error,
826                 &ett_cops_pdp,
827         };
828
829         module_t* cops_module;
830
831         /* Register the protocol name and description */
832         proto_cops = proto_register_protocol("Common Open Policy Service",
833             "COPS", "cops");
834
835         /* Required function calls to register the header fields and subtrees used */
836         proto_register_field_array(proto_cops, hf, array_length(hf));
837         proto_register_subtree_array(ett, array_length(ett));
838         
839         /* Register our configuration options for cops, 
840          * particularly our ports
841          */
842         cops_module = prefs_register_protocol(proto_cops,
843                                               proto_reg_handoff_cops);
844         prefs_register_uint_preference(cops_module,"tcp.cops_port",
845                                        "COPS TCP Port",
846                                        "Set the TCP port for COPS messages",
847                                        10,&global_cops_tcp_port);
848 };
849
850 void
851 proto_reg_handoff_cops(void)
852 {
853         static int cops_prefs_initialized = FALSE;
854         static dissector_handle_t cops_handle;
855
856         if(!cops_prefs_initialized){
857           cops_handle = create_dissector_handle(dissect_cops, proto_cops);
858           cops_prefs_initialized = TRUE;
859         }
860         else {
861           dissector_delete("tcp.port",cops_tcp_port,cops_handle);
862         }
863         
864         /* Set our port numbers for future use */
865         cops_tcp_port = global_cops_tcp_port;
866
867         dissector_add("tcp.port", cops_tcp_port, cops_handle);
868 }