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