Remove "text2pcap-scanner.obj" and "tools\lemon\lemon.obj" when a "nmake
[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.13 2001/06/18 02:17:45 guy Exp $
8  *
9  * Ethereal - Network traffic analyzer
10  * By Gerald Combs <gerald@ethereal.com>
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         if (check_col(pinfo->fd, COL_PROTOCOL)) 
270                 col_set_str(pinfo->fd, COL_PROTOCOL, "COPS");
271         if (check_col(pinfo->fd, COL_INFO)) 
272                 col_clear(pinfo->fd, COL_INFO);
273     
274         op_code = tvb_get_guint8(tvb, 1);
275         if (check_col(pinfo->fd, COL_INFO))
276                 col_add_fstr(pinfo->fd, COL_INFO, "COPS %s",
277                              val_to_str(op_code, cops_op_code_vals, "Unknown Op Code"));
278
279         if (tree) {
280                 proto_item *ti, *tv;
281                 proto_tree *cops_tree, *ver_flags_tree;
282                 guint32 offset, msg_len;
283                 guint8 ver_flags;
284                 gint garbage;
285
286                 offset = 0;
287                 ti = proto_tree_add_item(tree, proto_cops, tvb, offset, tvb_length(tvb), FALSE);
288                 cops_tree = proto_item_add_subtree(ti, ett_cops);
289
290                 /* Version and flags share the same byte, put them in a subtree */
291                 ver_flags = tvb_get_guint8(tvb, offset);
292                 tv = proto_tree_add_uint_format(cops_tree, hf_cops_ver_flags, tvb, offset, 1,
293                                                   ver_flags, "Version: %u, Flags: %s",
294                                                   hi_nibble(ver_flags),
295                                                   val_to_str(lo_nibble(ver_flags), cops_flags_vals, "Unknown"));
296                 ver_flags_tree = proto_item_add_subtree(tv, ett_cops_ver_flags);
297                 proto_tree_add_uint(ver_flags_tree, hf_cops_version, tvb, offset, 1, ver_flags);
298                 proto_tree_add_uint(ver_flags_tree, hf_cops_flags, tvb, offset, 1, ver_flags);
299                 offset++;
300
301                 proto_tree_add_uint(cops_tree, hf_cops_op_code, tvb, offset, 1, tvb_get_guint8(tvb, offset));
302                 offset ++;
303                 proto_tree_add_uint(cops_tree, hf_cops_client_type, tvb, offset, 2, tvb_get_ntohs(tvb, offset));
304                 offset += 2;
305
306                 msg_len = tvb_get_ntohl(tvb, offset);
307                 proto_tree_add_uint(cops_tree, hf_cops_msg_len, tvb, offset, 4, tvb_get_ntohl(tvb, offset));
308                 offset += 4;
309
310                 while (msg_len >= COPS_OBJECT_HDR_SIZE) {
311                         int consumed;
312
313                         consumed = dissect_cops_object(tvb, offset, cops_tree);
314                         if (consumed == 0)
315                                 break;
316                         msg_len -= consumed;
317                         offset += consumed;
318                 }
319
320                 garbage = tvb_length_remaining(tvb, offset);
321                 if (garbage > 0)
322                         proto_tree_add_text(cops_tree, tvb, offset, garbage,
323                                             "Trailing garbage: %d byte%s", garbage,
324                                             plurality(garbage, "", "s"));
325         }
326
327         return;
328 }
329
330 static char *cops_c_type_to_str(guint8 c_num, guint8 c_type)
331 {
332         switch (c_num) {
333         case COPS_OBJ_HANDLE:
334                 if (c_type == 1)
335                         return "Client Handle";
336                 break;
337         case COPS_OBJ_IN_INT:
338         case COPS_OBJ_OUT_INT:
339                 if (c_type == 1)
340                         return "IPv4 Address + Interface";
341                 else if (c_type == 2)
342                         return "IPv6 Address + Interface";
343                 break;
344         case COPS_OBJ_DECISION:
345         case COPS_OBJ_LPDPDECISION:
346                 if (c_type == 1)
347                         return "Decision Flags (Mandatory)";
348                 else if (c_type == 2)
349                         return "Stateless Data";
350                 else if (c_type == 3)
351                         return "Replacement Data";
352                 else if (c_type == 4)
353                         return "Client Specific Decision Data";
354                 else if (c_type == 5)
355                         return "Named Decision Data";
356                 break;
357         case COPS_OBJ_CLIENTSI:
358                 if (c_type == 1)
359                         return "Signaled ClientSI";
360                 else if (c_type == 2)
361                         return "Named ClientSI";
362                 break;
363         case COPS_OBJ_KATIMER:
364                 if (c_type == 1)
365                         return "Keep-alive timer value";
366                 break;
367         case COPS_OBJ_PDPREDIRADDR:
368         case COPS_OBJ_LASTPDPADDR:
369                 if (c_type == 1)
370                         return "IPv4 Address + TCP Port";
371                 else if (c_type == 2)
372                         return "IPv6 Address + TCP Port";
373                 break;
374         case COPS_OBJ_ACCTTIMER:
375                 if (c_type == 1)
376                         return "Accounting timer value";
377                 break;
378         case COPS_OBJ_INTEGRITY:
379                 if (c_type == 1)
380                         return "HMAC digest";
381                 break;
382         }
383
384         return "";
385 }
386
387 static int dissect_cops_object(tvbuff_t *tvb, guint32 offset, proto_tree *tree)
388 {
389         guint16 object_len, contents_len;
390         guint8 c_num, c_type;
391         proto_item *ti;
392         proto_tree *obj_tree;
393         char *type_str;
394         int ret;
395
396         if (tvb_length_remaining(tvb, offset) < COPS_OBJECT_HDR_SIZE)
397                 return 0;
398
399         object_len = tvb_get_ntohs(tvb, offset);
400         c_num = tvb_get_guint8(tvb, offset + 2);
401         c_type = tvb_get_guint8(tvb, offset + 3);
402
403         ti = proto_tree_add_uint_format(tree, hf_cops_obj_c_num, tvb, offset, object_len, c_num,
404                                         "%s: %s", val_to_str(c_num, cops_c_num_vals, "Unknown"),
405                                         cops_c_type_to_str(c_num, c_type));
406         obj_tree = proto_item_add_subtree(ti, ett_cops_obj);
407
408         proto_tree_add_uint(obj_tree, hf_cops_obj_len, tvb, offset, 2, tvb_get_ntohs(tvb, offset));
409         offset += 2;
410
411         proto_tree_add_uint(obj_tree, hf_cops_obj_c_num, tvb, offset, 1, c_num);
412         offset++;
413
414         type_str = cops_c_type_to_str(c_num, c_type);
415         proto_tree_add_text(obj_tree, tvb, offset, 1, "C-Type: %s%s%u%s",
416                             type_str,
417                             strlen(type_str) ? " (" : "",
418                             c_type,
419                             strlen(type_str) ? ")" : "");
420         offset++;
421
422         contents_len = object_len - COPS_OBJECT_HDR_SIZE;
423         ret = dissect_cops_object_data(tvb, offset, obj_tree, c_num, c_type, contents_len);
424         if (ret < 0) return 0;
425
426         /* Pad to 32bit boundary */
427         if (object_len % sizeof (guint32))
428                 object_len += (sizeof (guint32) - object_len % sizeof (guint32));
429
430         return object_len;
431         
432 }
433
434 static int dissect_cops_object_data(tvbuff_t *tvb, guint32 offset, proto_tree *tree,
435                                     guint8 c_num, guint8 c_type, guint16 len)
436 {
437         proto_item *ti;
438         proto_tree *r_type_tree, *itf_tree, *reason_tree, *dec_tree, *error_tree, *pdp_tree;
439         guint16 r_type, m_type, reason, reason_sub, cmd_code, cmd_flags, error, error_sub, tcp_port;
440         guint32 ipv4addr, ifindex;
441         struct e_in6_addr ipv6addr;
442
443         switch (c_num) {
444         case COPS_OBJ_CONTEXT:
445                 r_type = tvb_get_ntohs(tvb, offset);
446                 m_type = tvb_get_ntohs(tvb, offset + 2);
447                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: R-Type: %s, M-Type: %u",
448                                          val_to_str(r_type, cops_r_type_vals, "Unknown"),
449                                          m_type);
450
451                 r_type_tree = proto_item_add_subtree(ti, ett_cops_r_type_flags);
452                 proto_tree_add_uint(r_type_tree, hf_cops_r_type_flags, tvb, offset, 2, r_type);
453                 offset += 2;
454                 proto_tree_add_uint(r_type_tree, hf_cops_m_type_flags, tvb, offset, 2, m_type);
455
456                 return 0;
457                 break;
458         case COPS_OBJ_IN_INT:
459         case COPS_OBJ_OUT_INT:
460                 if (c_type == 1) {          /* IPv4 */
461                         tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset, 4);
462                         ifindex = tvb_get_ntohl(tvb, offset + 4);
463                         ti = proto_tree_add_text(tree, tvb, offset, 8, "Contents: IPv4 address %s, ifIndex: %u",
464                                                  ip_to_str((guint8 *)&ipv4addr), ifindex);
465                         itf_tree = proto_item_add_subtree(ti, ett_cops_itf);
466                         proto_tree_add_ipv4(itf_tree,
467                                             (c_num == COPS_OBJ_IN_INT) ? hf_cops_in_int_ipv4 : hf_cops_out_int_ipv4,
468                                             tvb, offset, 4, ipv4addr);
469                         offset += 4;
470                 } else if (c_type == 2) {   /* IPv6 */
471                         tvb_memcpy(tvb, (guint8 *)&ipv6addr, offset, sizeof ipv6addr);
472                         ifindex = tvb_get_ntohl(tvb, offset + sizeof ipv6addr);
473                         ti = proto_tree_add_text(tree, tvb, offset, 20, "Contents: IPv6 address %s, ifIndex: %u",
474                                                  ip6_to_str(&ipv6addr), ifindex);
475                         itf_tree = proto_item_add_subtree(ti, ett_cops_itf);
476                         proto_tree_add_ipv6(itf_tree,
477                                             (c_num == COPS_OBJ_IN_INT) ? hf_cops_in_int_ipv6 : hf_cops_out_int_ipv6,
478                                             tvb, offset, 16, (guint8 *)&ipv6addr);
479                         offset += 16;
480                 } else {
481                         break;
482                 }
483                 proto_tree_add_uint(itf_tree, hf_cops_int_ifindex, tvb, offset, 4, ifindex);
484
485                 return 0;
486                 break;
487         case COPS_OBJ_REASON:
488                 reason = tvb_get_ntohs(tvb, offset);
489                 reason_sub = tvb_get_ntohs(tvb, offset + 2);
490                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Reason-Code: %s, Reason Sub-code: 0x%04x",
491                                          val_to_str(reason, cops_reason_vals, "<Unknown value>"), reason_sub);
492                 reason_tree = proto_item_add_subtree(ti, ett_cops_reason);
493                 proto_tree_add_uint(reason_tree, hf_cops_reason, tvb, offset, 2, reason);
494                 offset += 2;
495                 if (reason == 13) {
496                         proto_tree_add_text(reason_tree, tvb, offset, 2, "Reason Sub-code: "
497                                             "Unknown object's C-Num %u, C-Type %u",
498                                             tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset + 1));
499                 } else 
500                         proto_tree_add_uint(reason_tree, hf_cops_reason_sub, tvb, offset, 2, reason_sub);
501
502                 return 0;
503                 break;
504         case COPS_OBJ_DECISION:
505         case COPS_OBJ_LPDPDECISION:
506                 if (c_type != 1)
507                         break;
508
509                 cmd_code = tvb_get_ntohs(tvb, offset);
510                 cmd_flags = tvb_get_ntohs(tvb, offset + 2);
511                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Command-Code: %s, Flags: %s",
512                                          val_to_str(cmd_code, cops_dec_cmd_code_vals, "<Unknown value>"),
513                                          val_to_str(cmd_flags, cops_dec_cmd_flag_vals, "<Unknown flag>"));
514                 dec_tree = proto_item_add_subtree(ti, ett_cops_decision);
515                 proto_tree_add_uint(dec_tree, hf_cops_dec_cmd_code, tvb, offset, 2, cmd_code);
516                 offset += 2;
517                 proto_tree_add_uint(dec_tree, hf_cops_dec_flags, tvb, offset, 2, cmd_flags);
518                 
519                 return 0;
520                 break;
521         case COPS_OBJ_ERROR:
522                 if (c_type != 1)
523                         break;
524                 
525                 error = tvb_get_ntohs(tvb, offset);
526                 error_sub = tvb_get_ntohs(tvb, offset + 2);
527                 ti = proto_tree_add_text(tree, tvb, offset, 4, "Contents: Error-Code: %s, Error Sub-code: 0x%04x",
528                                          val_to_str(error, cops_error_vals, "<Unknown value>"), error_sub);
529                 error_tree = proto_item_add_subtree(ti, ett_cops_error);
530                 proto_tree_add_uint(error_tree, hf_cops_error, tvb, offset, 2, error);
531                 offset += 2;
532                 if (error == 13) {
533                         proto_tree_add_text(error_tree, tvb, offset, 2, "Error Sub-code: "
534                                             "Unknown object's C-Num %u, C-Type %u",
535                                             tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset + 1));
536                 } else 
537                         proto_tree_add_uint(error_tree, hf_cops_error_sub, tvb, offset, 2, error_sub);
538
539                 return 0;
540                 break;
541         case COPS_OBJ_KATIMER:
542                 if (c_type != 1)
543                         break;
544
545                 proto_tree_add_item(tree, hf_cops_katimer, tvb, offset + 2, 2, FALSE);
546                 if (tvb_get_ntohs(tvb, offset + 2) == 0)
547                         proto_tree_add_text(tree, tvb, offset, 0, "Value of zero implies infinity.");
548                 
549                 return 0;
550                 break;
551         case COPS_OBJ_PEPID:
552                 if (c_type != 1)
553                         break;
554
555                 if (tvb_strnlen(tvb, offset, len) == -1)
556                         proto_tree_add_text(tree, tvb, offset, len, "<PEP Id is not a NUL terminated ASCII string>");
557                 else
558                         proto_tree_add_item(tree, hf_cops_pepid, tvb, offset,
559                                             tvb_strnlen(tvb, offset, len) + 1, FALSE);
560
561                 return 0;
562                 break;
563         case COPS_OBJ_REPORT_TYPE:
564                 if (c_type != 1)
565                         break;
566
567                 proto_tree_add_item(tree, hf_cops_report_type, tvb, offset, 2, FALSE);
568
569                 return 0;
570                 break;
571         case COPS_OBJ_PDPREDIRADDR:
572         case COPS_OBJ_LASTPDPADDR:
573                 if (c_type == 1) {          /* IPv4 */
574                         tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset, 4);
575                         tcp_port = tvb_get_ntohs(tvb, offset + 4 + 2);
576                         ti = proto_tree_add_text(tree, tvb, offset, 8, "Contents: IPv4 address %s, TCP Port Number: %u",
577                                                  ip_to_str((guint8 *)&ipv4addr), tcp_port);
578                         pdp_tree = proto_item_add_subtree(ti, ett_cops_pdp);
579                         proto_tree_add_ipv4(pdp_tree,
580                                             (c_num == COPS_OBJ_PDPREDIRADDR) ? hf_cops_pdprediraddr_ipv4 : hf_cops_lastpdpaddr_ipv4,
581                                             tvb, offset, 4, ipv4addr);
582                         offset += 4;
583                 } else if (c_type == 2) {   /* IPv6 */
584                         tvb_memcpy(tvb, (guint8 *)&ipv6addr, offset, sizeof ipv6addr);
585                         tcp_port = tvb_get_ntohs(tvb, offset + sizeof ipv6addr + 2);
586                         ti = proto_tree_add_text(tree, tvb, offset, 20, "Contents: IPv6 address %s, TCP Port Number: %u",
587                                                  ip6_to_str(&ipv6addr), tcp_port);
588                         pdp_tree = proto_item_add_subtree(ti, ett_cops_pdp);
589                         proto_tree_add_ipv6(pdp_tree,
590                                             (c_num == COPS_OBJ_PDPREDIRADDR) ? hf_cops_pdprediraddr_ipv6 : hf_cops_lastpdpaddr_ipv6,
591                                             tvb, offset, 16, (guint8 *)&ipv6addr);
592                         offset += 16;
593                 } else {
594                         break;
595                 }
596                 offset += 2;
597                 proto_tree_add_uint(pdp_tree, hf_cops_pdp_tcp_port, tvb, offset, 2, tcp_port);
598
599                 return 0;
600                 break;
601         case COPS_OBJ_ACCTTIMER:
602                 if (c_type != 1)
603                         break;
604
605                 proto_tree_add_item(tree, hf_cops_accttimer, tvb, offset + 2, 2, FALSE);
606                 if (tvb_get_ntohs(tvb, offset + 2) == 0)
607                         proto_tree_add_text(tree, tvb, offset, 0, "Value of zero means "
608                                             "there SHOULD be no unsolicited accounting updates.");
609
610                 return 0;
611                 break;
612         case COPS_OBJ_INTEGRITY:
613                 if (c_type != 1)
614                         break;      /* Not HMAC digest */
615
616                 proto_tree_add_item(tree, hf_cops_key_id, tvb, offset, 4, FALSE);
617                 proto_tree_add_item(tree, hf_cops_seq_num, tvb, offset + 4, 4, FALSE);
618                 proto_tree_add_text(tree, tvb, offset + 8 , len - 8, "Contents: Keyed Message Digest");
619
620                 return 0;
621                 break;
622         default:
623                 break;
624         }
625
626         ti = proto_tree_add_text(tree, tvb, offset, len, "Contents: %u bytes", len);
627
628         return 0;
629 }
630
631 /* Register the protocol with Ethereal */
632 void proto_register_cops(void)
633 {                 
634
635         /* Setup list of header fields */
636         static hf_register_info hf[] = {
637                 { &hf_cops_ver_flags,
638                         { "Version and Flags",           "cops.ver_flags",
639                         FT_UINT8, BASE_HEX, NULL, 0x0,
640                         "Version and Flags in COPS Common Header", HFILL }
641                 },
642                 { &hf_cops_version,
643                         { "Version",           "cops.version",
644                         FT_UINT8, BASE_DEC, NULL, 0xF0,
645                         "Version in COPS Common Header", HFILL }
646                 },
647                 { &hf_cops_flags,
648                         { "Flags",           "cops.flags",
649                         FT_UINT8, BASE_HEX, VALS(cops_flags_vals), 0x0F,
650                         "Flags in COPS Common Header", HFILL }
651                 },
652                 { &hf_cops_op_code,
653                         { "Op Code",           "cops.op_code",
654                         FT_UINT8, BASE_DEC, VALS(cops_op_code_vals), 0x0,
655                         "Op Code in COPS Common Header", HFILL }
656                 },
657                 { &hf_cops_client_type,
658                         { "Client Type",           "cops.client_type",
659                         FT_UINT16, BASE_DEC, NULL, 0x0,
660                         "Client Type in COPS Common Header", HFILL }
661                 },
662                 { &hf_cops_msg_len,
663                         { "Message Length",           "cops.msg_len",
664                         FT_UINT32, BASE_DEC, NULL, 0x0,
665                         "Message Length in COPS Common Header", HFILL }
666                 },
667                 { &hf_cops_obj_len,
668                         { "Object Length",           "cops.obj.len",
669                         FT_UINT32, BASE_DEC, NULL, 0x0,
670                         "Object Length in COPS Object Header", HFILL }
671                 },
672                 { &hf_cops_obj_c_num,
673                         { "C-Num",           "cops.c_num",
674                         FT_UINT8, BASE_DEC, VALS(cops_c_num_vals), 0x0,
675                         "C-Num in COPS Object Header", HFILL }
676                 },
677                 { &hf_cops_obj_c_type,
678                         { "C-Type",           "cops.c_type",
679                         FT_UINT8, BASE_DEC, NULL, 0x0,
680                         "C-Type in COPS Object Header", HFILL }
681                 },
682                 { &hf_cops_r_type_flags,
683                         { "R-Type",           "cops.context.r_type",
684                         FT_UINT16, BASE_HEX, VALS(cops_r_type_vals), 0xFFFF,
685                         "R-Type in COPS Context Object", HFILL }
686                 },
687                 { &hf_cops_m_type_flags,
688                         { "M-Type",           "cops.context.m_type",
689                         FT_UINT16, BASE_HEX, NULL, 0xFFFF,
690                         "M-Type in COPS Context Object", HFILL }
691                 },
692                 { &hf_cops_in_int_ipv4,
693                         { "IPv4 address",           "cops.in-int.ipv4",
694                         FT_IPv4, 0, NULL, 0xFFFF,
695                         "IPv4 address in COPS IN-Int object", HFILL }
696                 },
697                 { &hf_cops_in_int_ipv6,
698                         { "IPv6 address",           "cops.in-int.ipv6",
699                         FT_IPv6, 0, NULL, 0xFFFF,
700                         "IPv6 address in COPS IN-Int object", HFILL }
701                 },
702                 { &hf_cops_out_int_ipv4,
703                         { "IPv4 address",           "cops.out-int.ipv4",
704                         FT_IPv4, 0, NULL, 0xFFFF,
705                         "IPv4 address in COPS OUT-Int object", HFILL }
706                 },
707                 { &hf_cops_out_int_ipv6,
708                         { "IPv6 address",           "cops.out-int.ipv6",
709                         FT_IPv6, 0, NULL, 0xFFFF,
710                         "IPv6 address in COPS OUT-Int", HFILL }
711                 },
712                 { &hf_cops_int_ifindex,
713                         { "ifIndex",           "cops.in-out-int.ifindex",
714                         FT_UINT32, BASE_DEC, NULL, 0x0,
715                         "If SNMP is supported, corresponds to MIB-II ifIndex", HFILL }
716                 },
717                 { &hf_cops_reason,
718                         { "Reason",           "cops.reason",
719                         FT_UINT16, BASE_DEC, VALS(cops_reason_vals), 0,
720                         "Reason in Reason object", HFILL }
721                 },
722                 { &hf_cops_reason_sub,
723                         { "Reason Sub-code",           "cops.reason_sub",
724                         FT_UINT16, BASE_HEX, NULL, 0,
725                         "Reason Sub-code in Reason object", HFILL }
726                 },
727                 { &hf_cops_dec_cmd_code,
728                         { "Command-Code",           "cops.decision.cmd",
729                         FT_UINT16, BASE_DEC, VALS(cops_dec_cmd_code_vals), 0,
730                         "Command-Code in Decision/LPDP Decision object", HFILL }
731                 },
732                 { &hf_cops_dec_flags,
733                         { "Flags",           "cops.decision.flags",
734                         FT_UINT16, BASE_HEX, VALS(cops_dec_cmd_flag_vals), 0xffff,
735                         "Flags in Decision/LPDP Decision object", HFILL }
736                 },
737                 { &hf_cops_error,
738                         { "Error",           "cops.error",
739                         FT_UINT16, BASE_DEC, VALS(cops_error_vals), 0,
740                         "Error in Error object", HFILL }
741                 },
742                 { &hf_cops_error_sub,
743                         { "Error Sub-code",           "cops.error_sub",
744                         FT_UINT16, BASE_HEX, NULL, 0,
745                         "Error Sub-code in Error object", HFILL }
746                 },
747                 { &hf_cops_katimer,
748                         { "Contents: KA Timer Value",           "cops.katimer.value",
749                         FT_UINT16, BASE_DEC, NULL, 0,
750                         "Keep-Alive Timer Value in KATimer object", HFILL }
751                 },
752                 { &hf_cops_pepid,
753                         { "Contents: PEP Id",           "cops.pepid.id",
754                         FT_STRING, BASE_NONE, NULL, 0,
755                         "PEP Id in PEPID object", HFILL }
756                 },
757                 { &hf_cops_report_type,
758                         { "Contents: Report-Type",           "cops.report_type",
759                         FT_UINT16, BASE_DEC, VALS(cops_report_type_vals), 0,
760                         "Report-Type in Report-Type object", HFILL }
761                 },
762                 { &hf_cops_pdprediraddr_ipv4,
763                         { "IPv4 address",           "cops.pdprediraddr.ipv4",
764                         FT_IPv4, 0, NULL, 0xFFFF,
765                         "IPv4 address in COPS PDPRedirAddr object", HFILL }
766                 },
767                 { &hf_cops_pdprediraddr_ipv6,
768                         { "IPv6 address",           "cops.pdprediraddr.ipv6",
769                         FT_IPv6, 0, NULL, 0xFFFF,
770                         "IPv6 address in COPS PDPRedirAddr object", HFILL }
771                 },
772                 { &hf_cops_lastpdpaddr_ipv4,
773                         { "IPv4 address",           "cops.lastpdpaddr.ipv4",
774                         FT_IPv4, 0, NULL, 0xFFFF,
775                         "IPv4 address in COPS LastPDPAddr object", HFILL }
776                 },
777                 { &hf_cops_lastpdpaddr_ipv6,
778                         { "IPv6 address",           "cops.lastpdpaddr.ipv6",
779                         FT_IPv6, 0, NULL, 0xFFFF,
780                         "IPv6 address in COPS LastPDPAddr object", HFILL }
781                 },
782                 { &hf_cops_pdp_tcp_port,
783                         { "TCP Port Number",           "cops.pdp.tcp_port",
784                         FT_UINT32, BASE_DEC, NULL, 0x0,
785                          "TCP Port Number of PDP in PDPRedirAddr/LastPDPAddr object", HFILL }
786                 },
787                 { &hf_cops_accttimer,
788                         { "Contents: ACCT Timer Value",           "cops.accttimer.value",
789                         FT_UINT16, BASE_DEC, NULL, 0,
790                         "Accounting Timer Value in AcctTimer object", HFILL }
791                 },
792                 { &hf_cops_key_id,
793                         { "Contents: Key ID",           "cops.integrity.key_id",
794                         FT_UINT32, BASE_DEC, NULL, 0,
795                         "Key ID in Integrity object", HFILL }
796                 },
797                 { &hf_cops_seq_num,
798                         { "Contents: Sequence Number",           "cops.integrity.seq_num",
799                         FT_UINT32, BASE_DEC, NULL, 0,
800                         "Sequence Number in Integrity object", HFILL }
801                 },
802         };
803
804         /* Setup protocol subtree array */
805         static gint *ett[] = {
806                 &ett_cops,
807                 &ett_cops_ver_flags,
808                 &ett_cops_obj,
809                 &ett_cops_obj_data,
810                 &ett_cops_r_type_flags,
811                 &ett_cops_itf,
812                 &ett_cops_reason,
813                 &ett_cops_decision,
814                 &ett_cops_error,
815                 &ett_cops_pdp,
816         };
817
818         /* Register the protocol name and description */
819         proto_cops = proto_register_protocol("Common Open Policy Service",
820             "COPS", "cops");
821
822         /* Required function calls to register the header fields and subtrees used */
823         proto_register_field_array(proto_cops, hf, array_length(hf));
824         proto_register_subtree_array(ett, array_length(ett));
825 };
826
827 void
828 proto_reg_handoff_cops(void)
829 {
830         dissector_add("tcp.port", TCP_PORT_COPS, dissect_cops, proto_cops);
831 }