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