/* packet-rsvp.c
 * Routines for RSVP packet disassembly
 *
 * (c) Copyright Ashok Narayanan <ashokn@cisco.com>
 *
 * $Id: packet-rsvp.c,v 1.46 2001/06/26 20:50:30 guy Exp $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@ethereal.com>
 * Copyright 1998 Gerald Combs
 * 
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

/*
 * NOTES
 *
 * This module defines routines to disassemble RSVP packets, as defined in
 * RFC 2205. All objects from RC2205 are supported, in IPv4 and IPv6 mode.
 * In addition, the Integrated Services traffic specification objects
 * defined in RFC2210 are also supported. 
 *
 * IPv6 support is not completely tested
 *
 * Mar 3, 2000: Added support for MPLS/TE objects, as defined in 
 * <draft-ietf-mpls-rsvp-lsp-tunnel-04.txt>
 */

 
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <stdlib.h>
#include <string.h>

#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif

#include <glib.h>

#ifdef NEED_SNPRINTF_H
# include "snprintf.h"
#endif

#include "packet.h"
#include "in_cksum.h"
#include "ieee-float.h"
#include "etypes.h"
#include "ipproto.h"

static int proto_rsvp = -1;

static gint ett_rsvp = -1;
static gint ett_rsvp_hdr = -1;
static gint ett_rsvp_session = -1;
static gint ett_rsvp_hop = -1;
static gint ett_rsvp_time_values = -1;
static gint ett_rsvp_error = -1;
static gint ett_rsvp_scope = -1;
static gint ett_rsvp_style = -1;
static gint ett_rsvp_confirm = -1;
static gint ett_rsvp_sender_template = -1;
static gint ett_rsvp_filter_spec = -1;
static gint ett_rsvp_sender_tspec = -1;
static gint ett_rsvp_flowspec = -1;
static gint ett_rsvp_adspec = -1;
static gint ett_rsvp_adspec_subtree = -1;
static gint ett_rsvp_integrity = -1;
static gint ett_rsvp_policy = -1;
static gint ett_rsvp_label = -1;
static gint ett_rsvp_label_request = -1;
static gint ett_rsvp_session_attribute = -1;
static gint ett_rsvp_session_attribute_flags = -1;
static gint ett_rsvp_hello_obj = -1;
static gint ett_rsvp_explicit_route = -1;
static gint ett_rsvp_explicit_route_subobj = -1;
static gint ett_rsvp_record_route = -1;
static gint ett_rsvp_record_route_subobj = -1;
static gint ett_rsvp_unknown_class = -1;


/*
 * RSVP message types
 */
typedef enum {
    RSVP_MSG_PATH=1, 
    RSVP_MSG_RESV, 
    RSVP_MSG_PERR, 
    RSVP_MSG_RERR,
    RSVP_MSG_PTEAR, 
    RSVP_MSG_RTEAR, 
    RSVP_MSG_CONFIRM, 
    RSVP_MSG_RTEAR_CONFIRM=10,
    RSVP_MSG_BUNDLE = 12,
    RSVP_MSG_ACK,
    RSVP_MSG_SREFRESH = 15,
    RSVP_MSG_HELLO = 20
} rsvp_message_types;

static value_string message_type_vals[] = { 
    {RSVP_MSG_PATH, "PATH Message"},
    {RSVP_MSG_RESV, "RESV Message"},
    {RSVP_MSG_PERR, "PATH ERROR Message"},
    {RSVP_MSG_RERR, "RESV ERROR Message"},
    {RSVP_MSG_PTEAR, "PATH TEAR Message"},
    {RSVP_MSG_RTEAR, "RESV TEAR Message"},
    {RSVP_MSG_CONFIRM, "CONFIRM Message"},
    {RSVP_MSG_RTEAR_CONFIRM, "RESV TEAR CONFIRM Message"},
    {RSVP_MSG_BUNDLE, "BUNDLE Message"},
    {RSVP_MSG_ACK, "ACK Message"},
    {RSVP_MSG_SREFRESH, "SREFRESH Message"},
    {RSVP_MSG_HELLO, "HELLO Message"},
    {0, NULL}
};

/* 
 * RSVP classes
 */
#define MAX_RSVP_CLASS 15

enum rsvp_classes {
    RSVP_CLASS_NULL=0,
    RSVP_CLASS_SESSION,

    RSVP_CLASS_HOP=3,
    RSVP_CLASS_INTEGRITY,
    RSVP_CLASS_TIME_VALUES,
    RSVP_CLASS_ERROR,
    RSVP_CLASS_SCOPE,
    RSVP_CLASS_STYLE,
    RSVP_CLASS_FLOWSPEC,
    RSVP_CLASS_FILTER_SPEC,
    RSVP_CLASS_SENDER_TEMPLATE,
    RSVP_CLASS_SENDER_TSPEC,
    RSVP_CLASS_ADSPEC,
    RSVP_CLASS_POLICY,
    RSVP_CLASS_CONFIRM,
    RSVP_CLASS_LABEL,

    RSVP_CLASS_LABEL_REQUEST=19,
    RSVP_CLASS_EXPLICIT_ROUTE,
    RSVP_CLASS_RECORD_ROUTE,

    RSVP_CLASS_MESSAGE_ID = 23,
    RSVP_CLASS_MESSAGE_ID_ACK,
    RSVP_CLASS_MESSAGE_ID_LIST,

    RSVP_CLASS_SESSION_ATTRIBUTE=207,
    RSVP_CLASS_HELLO = 22
};

static value_string rsvp_class_vals[] = { 
    {RSVP_CLASS_NULL, "NULL object"},
    {RSVP_CLASS_SESSION, "SESSION object"},
    {RSVP_CLASS_HOP, "HOP object"},
    {RSVP_CLASS_INTEGRITY, "INTEGRITY object"},
    {RSVP_CLASS_TIME_VALUES, "TIME VALUES object"},
    {RSVP_CLASS_ERROR, "ERROR object"},
    {RSVP_CLASS_SCOPE, "SCOPE object"},
    {RSVP_CLASS_STYLE, "STYLE object"},
    {RSVP_CLASS_FLOWSPEC, "FLOWSPEC object"},
    {RSVP_CLASS_FILTER_SPEC, "FILTER SPEC object"},
    {RSVP_CLASS_SENDER_TEMPLATE, "SENDER TEMPLATE object"},
    {RSVP_CLASS_SENDER_TSPEC, "SENDER TSPEC object"},
    {RSVP_CLASS_ADSPEC, "ADSPEC object"},
    {RSVP_CLASS_POLICY, "POLICY object"},
    {RSVP_CLASS_CONFIRM, "CONFIRM object"},
    {RSVP_CLASS_LABEL, "LABEL object"},
    {RSVP_CLASS_LABEL_REQUEST, "LABEL REQUEST object"},
    {RSVP_CLASS_EXPLICIT_ROUTE, "EXPLICIT ROUTE object"},
    {RSVP_CLASS_RECORD_ROUTE, "RECORD ROUTE object"},
    {RSVP_CLASS_SESSION_ATTRIBUTE, "SESSION ATTRIBUTE object"},
    {RSVP_CLASS_MESSAGE_ID, "MESSAGE-ID object"},
    {RSVP_CLASS_MESSAGE_ID_ACK, "MESSAGE-ID ACK/NACK object"},
    {RSVP_CLASS_MESSAGE_ID_LIST, "MESSAGE-ID LIST object"},
    {RSVP_CLASS_HELLO, "HELLO object"},
    {0, NULL}
};

/*
 * RSVP error values
 */
enum rsvp_error_types {
    RSVP_ERROR_CONFIRM = 0,
    RSVP_ERROR_ADMISSION,
    RSVP_ERROR_POLICY,
    RSVP_ERROR_NO_PATH,
    RSVP_ERROR_NO_SENDER,
    RSVP_ERROR_CONFLICT_RESV_STYLE,
    RSVP_ERROR_UNKNOWN_RESV_STYLE,
    RSVP_ERROR_CONFLICT_DEST_PORTS,
    RSVP_ERROR_CONFLICT_SRC_PORTS,
    RSVP_ERROR_PREEMPTED=12,
    RSVP_ERROR_UNKNOWN_CLASS,
    RSVP_ERROR_UNKNOWN_C_TYPE,
    RSVP_ERROR_TRAFFIC = 21,
    RSVP_ERROR_TRAFFIC_SYSTEM,
    RSVP_ERROR_SYSTEM,
    RSVP_ERROR_ROUTING,
    RSVP_ERROR_NOTIFY
};

static value_string rsvp_error_vals[] = {
    {RSVP_ERROR_CONFIRM, "Confirmation"},
    {RSVP_ERROR_ADMISSION, "Admission Control Failure "},
    {RSVP_ERROR_POLICY, "Policy Control Failure"},
    {RSVP_ERROR_NO_PATH, "No PATH information for this RESV message"},
    {RSVP_ERROR_NO_SENDER, "No sender information for this RESV message"},
    {RSVP_ERROR_CONFLICT_RESV_STYLE, "Conflicting reservation styles"},
    {RSVP_ERROR_UNKNOWN_RESV_STYLE, "Unknown reservation style"},
    {RSVP_ERROR_CONFLICT_DEST_PORTS, "Conflicting destination ports"},
    {RSVP_ERROR_CONFLICT_SRC_PORTS, "Conflicting source ports"},
    {RSVP_ERROR_PREEMPTED, "Service preempted"},
    {RSVP_ERROR_UNKNOWN_CLASS, "Unknown object class"},
    {RSVP_ERROR_UNKNOWN_C_TYPE, "Unknown object C-type"},
    {RSVP_ERROR_TRAFFIC, "Traffic Control Error"},
    {RSVP_ERROR_TRAFFIC_SYSTEM, "Traffic Control System Error"},
    {RSVP_ERROR_SYSTEM, "RSVP System Error"},
    {RSVP_ERROR_ROUTING, "Routing Error"},
    {RSVP_ERROR_NOTIFY, "RSVP Notify Error"},
    {0, NULL}
};

/*
 * Defines the reservation style plus style-specific information that
 * is not a FLOWSPEC or FILTER_SPEC object, in a RESV message.
 */
#define RSVP_DISTINCT (1 << 3)
#define RSVP_SHARED (2 << 3)
#define RSVP_SHARING_MASK (RSVP_DISTINCT | RSVP_SHARED)

#define RSVP_SCOPE_WILD 1
#define RSVP_SCOPE_EXPLICIT 2
#define RSVP_SCOPE_MASK 0x07

#define RSVP_WF (RSVP_SHARED | RSVP_SCOPE_WILD)
#define RSVP_FF (RSVP_DISTINCT | RSVP_SCOPE_EXPLICIT)
#define RSVP_SE (RSVP_SHARED | RSVP_SCOPE_EXPLICIT)

static value_string style_vals[] = {
    { RSVP_WF, "Wildcard Filter" },
    { RSVP_FF, "Fixed Filter" },
    { RSVP_SE, "Shared-Explicit" },
    { 0,       NULL }
};

/*
 * Defines a desired QoS, in a RESV message.
 */
enum    qos_service_type {
    QOS_QUALITATIVE =     128,          /* Qualitative service */
    QOS_CONTROLLED_LOAD=    5,		/* Controlled Load Service */
    QOS_GUARANTEED =        2,		/* Guaranteed service */
    QOS_TSPEC =             1		/* Traffic specification */
    };

static value_string qos_vals[] = {
    { QOS_QUALITATIVE, "Qualitative QoS" },
    { QOS_CONTROLLED_LOAD, "Controlled-load QoS" },
    { QOS_GUARANTEED, "Guaranteed rate QoS" },
    { QOS_TSPEC, "Traffic specification" },
    { 0, NULL }
};

static value_string svc_vals[] = {
    { 127, "Token bucket TSpec" },
    { 128, "Qualitative TSpec" },
    { 130, "Guaranteed-rate RSpec" },
    { 0, NULL }
};

enum rsvp_spec_types { INTSRV = 2 };

enum intsrv_services {
	INTSRV_GENERAL = 1,
	INTSRV_GTD = 2,
	INTSRV_CLOAD = 5,
	INTSRV_QUALITATIVE = 128
};

static value_string intsrv_services_str[] = { 
    {INTSRV_GENERAL, "Default General Parameters"},
    {INTSRV_GTD, "Guaranteed"},
    {INTSRV_CLOAD, "Controlled Load"},
    {INTSRV_QUALITATIVE, "Qualitative"},
    { 0, NULL }
};

enum intsrv_field_name {
	INTSRV_NON_IS_HOPS = 1, INTSRV_COMPOSED_NON_IS_HOPS,
	INTSRV_IS_HOPS, INTSRV_COMPOSED_IS_HOPS,
	INTSRV_PATH_BANDWIDTH, INTSRV_MIN_PATH_BANDWIDTH,
	INTSRV_IF_LATENCY, INTSRV_PATH_LATENCY,
	INTSRV_MTU, INTSRV_COMPOSED_MTU,

	INTSRV_TOKEN_BUCKET_TSPEC = 127,
	INTSRV_QUALITATIVE_TSPEC = 128,
	INTSRV_GTD_RSPEC = 130,

    	INTSRV_DELAY = 131,	/* Gtd Parameter C - Max Delay Bound - bytes */
	INTSRV_MAX_JITTER,	/* Gtd Parameter D - Max Jitter */
    	INTSRV_E2E_DELAY,	/* Gtd Parameter Ctot */
	INTSRV_E2E_MAX_JITTER,	/* Gtd Parameter Dtot */
    	INTSRV_SHP_DELAY,	/* Gtd Parameter Csum */
	INTSRV_SHP_MAX_JITTER	/* Gtd Parameter Dsum */
};

static value_string adspec_params[] = { 
    {4, "IS Hop Count"},
    {6, "Path b/w estimate"},
    {8, "Minimum path latency"},
    {10, "Composed MTU"},
    {133, "End-to-end composed value for C"},
    {134, "End-to-end composed value for D"},
    {135, "Since-last-reshaping point composed C"},
    {136, "Since-last-reshaping point composed D"},
    { 0, NULL }
};

/* -------------------- Stuff for MPLS/TE objects -------------------- */

static const value_string proto_vals[] = { {IP_PROTO_ICMP, "ICMP"},
                                           {IP_PROTO_IGMP, "IGMP"},
                                           {IP_PROTO_TCP,  "TCP" },
                                           {IP_PROTO_UDP,  "UDP" },
                                           {IP_PROTO_OSPF, "OSPF"},
                                           {0,             NULL  } };

/* Filter keys */
enum rsvp_filter_keys {

    /* Message types */
    RSVPF_MSG,          /* Message type */
    /* Shorthand for message types */
    RSVPF_PATH,
    RSVPF_RESV,
    RSVPF_PATHERR,
    RSVPF_RESVERR,
    RSVPF_PATHTEAR,
    RSVPF_RESVTEAR,
    RSVPF_RCONFIRM,
    RSVPF_JUNK_MSG8,
    RSVPF_JUNK_MSG9,
    RSVPF_RTEARCONFIRM,
    RSVPF_JUNK11,
    RSVPF_BUNDLE,
    RSVPF_ACK,
    RSVPF_JUNK14,
    RSVPF_SREFRESH,
    RSVPF_HELLO,
    /* Does the message contain an object of this type? */
    RSVPF_OBJECT,
    /* Object present shorthands */
    RSVPF_SESSION,
    RSVPF_DUMMY_1,
    RSVPF_HOP,
    RSVPF_INTEGRITY,
    RSVPF_TIME_VALUES,
    RSVPF_ERROR,
    RSVPF_SCOPE,
    RSVPF_STYLE,
    RSVPF_FLOWSPEC,
    RSVPF_FILTER_SPEC,
    RSVPF_SENDER,
    RSVPF_TSPEC,
    RSVPF_ADSPEC,
    RSVPF_POLICY,
    RSVPF_CONFIRM,
    RSVPF_LABEL,
    RSVPF_DUMMY_2,
    RSVPF_DUMMY_3,
    RSVPF_LABEL_REQUEST,
    RSVPF_EXPLICIT_ROUTE,
    RSVPF_RECORD_ROUTE,
    RSVPF_DUMMY_4,
    RSVPF_MESSAGE_ID,
    RSVPF_MESSAGE_ID_ACK,
    RSVPF_MESSAGE_ID_LIST,
    RSVPF_HELLO_OBJ,
    RSVPF_SESSION_ATTRIBUTE,
    RSVPF_UNKNOWN_OBJ, 

    /* Session object */
    RSVPF_SESSION_IP,
    RSVPF_SESSION_PROTO,
    RSVPF_SESSION_PORT,
    RSVPF_SESSION_TUNNEL_ID,
    RSVPF_SESSION_EXT_TUNNEL_ID,

    /* Sender template */
    RSVPF_SENDER_IP,
    RSVPF_SENDER_PORT,
    RSVPF_SENDER_LSP_ID,

    /* Sentinel */
    RSVPF_MAX
};

static int rsvp_filter[RSVPF_MAX];

static hf_register_info rsvpf_info[] = {

    /* Message type number */
    {&rsvp_filter[RSVPF_MSG], 
     { "Message Type", "rsvp.msg", FT_UINT8, BASE_DEC, VALS(message_type_vals), 0x0,
     	"", HFILL }},

    /* Message type shorthands */
    {&rsvp_filter[RSVPF_PATH], 
     { "Path Message", "rsvp.path", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_HELLO], 
     { "HELLO Message", "rsvp.hello", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_RESV], 
     { "Resv Message", "rsvp.resv", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_PATHERR], 
     { "Path Error Message", "rsvp.perr", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_RESVERR], 
     { "Resv Error Message", "rsvp.rerr", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_PATHTEAR], 
     { "Path Tear Message", "rsvp.ptear", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_RESVTEAR], 
     { "Resv Tear Message", "rsvp.rtear", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_RCONFIRM], 
     { "Resv Confirm Message", "rsvp.resvconf", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_RTEARCONFIRM], 
     { "Resv Tear Confirm Message", "rsvp.rtearconf", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    /* Object class */
    {&rsvp_filter[RSVPF_OBJECT], 
     { "Object class", "rsvp.object", FT_UINT8, BASE_DEC, VALS(rsvp_class_vals), 0x0,
     	"", HFILL }},

    /* Object present shorthands */
    {&rsvp_filter[RSVPF_SESSION], 
     { "SESSION", "rsvp.session", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_HOP], 
     { "HOP", "rsvp.hop", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_HELLO_OBJ], 
     { "HELLO Request/Ack", "rsvp.hello_obj", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_INTEGRITY], 
     { "INTEGRITY", "rsvp.integrity", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_TIME_VALUES], 
     { "TIME VALUES", "rsvp.time", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_ERROR], 
     { "ERROR", "rsvp.error", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_SCOPE], 
     { "SCOPE", "rsvp.scope", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_STYLE], 
     { "STYLE", "rsvp.style", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_FLOWSPEC], 
     { "FLOWSPEC", "rsvp.flowspec", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_FILTER_SPEC], 
     { "FILTERSPEC", "rsvp.filter", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_SENDER], 
     { "SENDER TEMPLATE", "rsvp.sender", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_TSPEC], 
     { "SENDER TSPEC", "rsvp.tspec", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_ADSPEC], 
     { "ADSPEC", "rsvp.adspec", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_POLICY], 
     { "POLICY", "rsvp.policy", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_CONFIRM], 
     { "CONFIRM", "rsvp.confirm", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_LABEL], 
     { "LABEL", "rsvp.label", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_LABEL_REQUEST], 
     { "LABEL REQUEST", "rsvp.label_request", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_SESSION_ATTRIBUTE], 
     { "SESSION ATTRIBUTE", "rsvp.session_attribute", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_EXPLICIT_ROUTE], 
     { "EXPLICIT ROUTE", "rsvp.explicit_route", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_RECORD_ROUTE], 
     { "RECORD ROUTE", "rsvp.record_route", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_MESSAGE_ID], 
     { "MESSAGE-ID", "rsvp.msgid", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_MESSAGE_ID_ACK], 
     { "MESSAGE-ID ACK", "rsvp.ack", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_MESSAGE_ID_LIST], 
     { "MESSAGE-ID LIST", "rsvp.msgid_list", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_HELLO_OBJ], 
     { "HELLO Message", "rsvp.hello", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_UNKNOWN_OBJ], 
     { "Unknown object", "rsvp.obj_unknown", FT_NONE, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    /* Session fields */
    {&rsvp_filter[RSVPF_SESSION_IP], 
     { "Destination address", "rsvp.session.ip", FT_IPv4, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_SESSION_PORT], 
     { "Port number", "rsvp.session.port", FT_UINT16, BASE_DEC, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_SESSION_PROTO], 
     { "Protocol", "rsvp.session.proto", FT_UINT8, BASE_DEC, VALS(proto_vals), 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_SESSION_TUNNEL_ID], 
     { "Tunnel ID", "rsvp.session.tunnel_id", FT_UINT16, BASE_DEC, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_SESSION_EXT_TUNNEL_ID], 
     { "Extended tunnel ID", "rsvp.session.ext_tunnel_id", FT_UINT32, BASE_DEC, NULL, 0x0,
     	"", HFILL }},

    /* Sender template/Filterspec fields */
    {&rsvp_filter[RSVPF_SENDER_IP], 
     { "Sender IPv4 address", "rsvp.sender.ip", FT_IPv4, BASE_NONE, NULL, 0x0,
     	"", HFILL }},

    {&rsvp_filter[RSVPF_SENDER_PORT], 
     { "Sender port number", "rsvp.sender.port", FT_UINT16, BASE_DEC, NULL, 0x0,
       "", HFILL }},

    {&rsvp_filter[RSVPF_SENDER_LSP_ID], 
     { "Sender LSP ID", "rsvp.sender.lsp_id", FT_UINT16, BASE_DEC, NULL, 0x0,
     	"", HFILL }}
};

static inline int rsvp_class_to_filter_num(int classnum)
{
    switch(classnum) {
    case RSVP_CLASS_SESSION :
    case RSVP_CLASS_HOP :
    case RSVP_CLASS_INTEGRITY :
    case RSVP_CLASS_TIME_VALUES :
    case RSVP_CLASS_ERROR :
    case RSVP_CLASS_SCOPE :
    case RSVP_CLASS_STYLE :
    case RSVP_CLASS_FLOWSPEC :
    case RSVP_CLASS_FILTER_SPEC :
    case RSVP_CLASS_SENDER_TEMPLATE :
    case RSVP_CLASS_SENDER_TSPEC :
    case RSVP_CLASS_ADSPEC :
    case RSVP_CLASS_POLICY :
    case RSVP_CLASS_CONFIRM :
    case RSVP_CLASS_LABEL :
    case RSVP_CLASS_LABEL_REQUEST :
    case RSVP_CLASS_EXPLICIT_ROUTE :
    case RSVP_CLASS_RECORD_ROUTE :
    case RSVP_CLASS_MESSAGE_ID :
    case RSVP_CLASS_MESSAGE_ID_ACK :
    case RSVP_CLASS_MESSAGE_ID_LIST :    
	return classnum + RSVPF_OBJECT;
	break;
    case RSVP_CLASS_HELLO :
        return RSVPF_HELLO_OBJ;
        break;

    case RSVP_CLASS_SESSION_ATTRIBUTE :
	return RSVPF_SESSION_ATTRIBUTE;
	
    default:
	return RSVPF_UNKNOWN_OBJ;
    }
}

static void 
dissect_rsvp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) 
{
    int offset = 0;
    proto_tree *rsvp_tree = NULL, *ti, *ti2; 
    proto_tree *rsvp_header_tree;
    proto_tree *rsvp_object_tree;
    proto_tree *rsvp_sa_flags_tree;
    proto_tree *rsvp_ero_subtree;
    guint8 ver_flags;
    guint8 message_type;
    guint16 cksum, computed_cksum;
    vec_t cksum_vec[1];
    int i, j, k, l, len;
    int msg_length;
    int obj_length;
    int mylen;
    int offset2;
    char *objtype;

    if (check_col(pinfo->fd, COL_PROTOCOL))
        col_set_str(pinfo->fd, COL_PROTOCOL, "RSVP");
    if (check_col(pinfo->fd, COL_INFO))
        col_clear(pinfo->fd, COL_INFO);

    ver_flags = tvb_get_guint8(tvb, offset+0);
    message_type = tvb_get_guint8(tvb, offset+1);
    if (check_col(pinfo->fd, COL_INFO)) {
        col_add_str(pinfo->fd, COL_INFO,
            val_to_str(message_type, message_type_vals, "Unknown (%u)")); 
    }

    if (tree) {
	msg_length = tvb_get_ntohs(tvb, offset+6);
	ti = proto_tree_add_item(tree, proto_rsvp, tvb, offset, msg_length,
	    FALSE);
	rsvp_tree = proto_item_add_subtree(ti, ett_rsvp);

	ti = proto_tree_add_text(rsvp_tree, tvb, offset, 8, "RSVP Header"); 
	rsvp_header_tree = proto_item_add_subtree(ti, ett_rsvp_hdr);

        proto_tree_add_text(rsvp_header_tree, tvb, offset, 1, "RSVP Version: %u", 
			    (ver_flags & 0xf0)>>4);  
	proto_tree_add_text(rsvp_header_tree, tvb, offset, 1, "Flags: %02x",
			    ver_flags & 0xf);
	proto_tree_add_uint(rsvp_header_tree, rsvp_filter[RSVPF_MSG], tvb, 
			    offset+1, 1, message_type);
	if (message_type <= RSVPF_RTEARCONFIRM &&
			message_type != RSVPF_JUNK_MSG8 &&
			message_type != RSVPF_JUNK_MSG9 ) {
	       proto_tree_add_boolean_hidden(rsvp_header_tree, rsvp_filter[RSVPF_MSG + message_type], tvb, 
				   offset+1, 1, 1);
	}
	cksum = tvb_get_ntohs(tvb, offset+2);
	if (!pinfo->fragmented && tvb_length(tvb) >= msg_length) {
	    /* The packet isn't part of a fragmented datagram and isn't
	       truncated, so we can checksum it. */
	    cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, msg_length);
	    cksum_vec[0].len = msg_length;
	    computed_cksum = in_cksum(&cksum_vec[0], 1);
	    if (computed_cksum == 0) {
		proto_tree_add_text(rsvp_header_tree, tvb, offset+2, 2,
				    "Message Checksum: 0x%04x (correct)",
				    cksum);
	    } else {
		proto_tree_add_text(rsvp_header_tree, tvb, offset+2, 2,
				    "Message Checksum: 0x%04x (incorrect, should be 0x%04x)",
				    cksum,
				    in_cksum_shouldbe(cksum, computed_cksum));
	    }
	} else {
	    proto_tree_add_text(rsvp_header_tree, tvb, offset+2, 2,
				"Message Checksum: 0x%04x",
				cksum);
	}
	proto_tree_add_text(rsvp_header_tree, tvb, offset+4, 1,
			    "Sending TTL: %u",
			    tvb_get_guint8(tvb, offset+4));
	proto_tree_add_text(rsvp_header_tree, tvb, offset+6, 2,
			    "Message length: %u", msg_length);

	offset += 8;
	len = 8;
	while (len < msg_length) {
	    guint8 class;	
	    guint8 type;
	    char *object_type;

	    obj_length = tvb_get_ntohs(tvb, offset);
	    class = tvb_get_guint8(tvb, offset+2);
	    type = tvb_get_guint8(tvb, offset+3);
	    object_type = val_to_str(class, rsvp_class_vals, "Unknown");
	    proto_tree_add_uint_hidden(rsvp_tree, rsvp_filter[RSVPF_OBJECT], tvb, 
					    offset, obj_length, class);
	    ti = proto_tree_add_item(rsvp_tree, rsvp_filter[rsvp_class_to_filter_num(class)],
	    			     tvb, offset, obj_length, FALSE);

	    offset2 = offset+4;

	    switch(class) {

	    case RSVP_CLASS_SESSION : 		
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_session);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
		mylen = obj_length - 4;
		switch(type) {
		case 1: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1,
					"C-type: 1 - IPv4");
		    proto_tree_add_item(rsvp_object_tree,
		    			rsvp_filter[RSVPF_SESSION_IP],
		    			tvb, offset2, 4, FALSE);

		    proto_tree_add_item(rsvp_object_tree,
					rsvp_filter[RSVPF_SESSION_PROTO], tvb, 
					offset2+4, 1, FALSE);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+5, 1,
					"Flags: %x",
					tvb_get_guint8(tvb, offset2+5));
		    proto_tree_add_item(rsvp_object_tree,
					rsvp_filter[RSVPF_SESSION_PORT], tvb, 
					offset2+6, 2, FALSE);
                    proto_item_set_text(ti, "SESSION: IPv4, %s, %d, %d", 
                                        ip_to_str(tvb_get_ptr(tvb, offset2, 4)), 
                                        tvb_get_guint8(tvb, offset2+4),
                                        tvb_get_ntohs(tvb, offset2+6));
		    break;
		}

		case 2: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 2 - IPv6");
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, 16,
					"Destination address: %s", 
					ip6_to_str((struct e_in6_addr *)tvb_get_ptr(tvb, offset2, 16)));
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+16, 1,
					"Protocol: %u",
					tvb_get_guint8(tvb, offset2+16));
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+17, 1,
					"Flags: %x",
					tvb_get_guint8(tvb, offset2+17));
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+18, 2,
					"Destination port: %u", 
					tvb_get_ntohs(tvb, offset2+18));
		    break;
		}
		
		case 7: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 7 - IPv4 LSP");
		    proto_tree_add_item(rsvp_object_tree,
					rsvp_filter[RSVPF_SESSION_IP],
					tvb, offset2, 4, FALSE);

		    proto_tree_add_item(rsvp_object_tree,
					rsvp_filter[RSVPF_SESSION_TUNNEL_ID],
					tvb, offset2+6, 2, FALSE);

		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+8, 4, 
					"Extended Tunnel ID: %u (%s)",
					tvb_get_ntohl(tvb, offset2+8),
					ip_to_str(tvb_get_ptr(tvb, offset2+8, 4)));
		    proto_tree_add_item_hidden(rsvp_object_tree,
		    			rsvp_filter[RSVPF_SESSION_EXT_TUNNEL_ID],
		    			tvb, offset2+8, 4, FALSE);
                    proto_item_set_text(ti, "SESSION: IPv4-LSP, %s, %d, %0x", 
                                        ip_to_str(tvb_get_ptr(tvb, offset2, 4)), 
                                        tvb_get_ntohs(tvb, offset2+6),
                                        tvb_get_ntohl(tvb, offset2+8));
		    break;
		}

		default: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: Unknown (%u)",
					type);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, mylen,
					"Data (%d bytes)", mylen);
		}
		}
		break;
		
	    case RSVP_CLASS_HOP :		
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_hop);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
		mylen = obj_length - 4;
		switch(type) {
		case 1: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 1 - IPv4");
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, 4, 
					"Neighbor address: %s", 
					ip_to_str(tvb_get_ptr(tvb, offset2, 4)));
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+4, 4,
					"Logical interface: %u", 
					tvb_get_ntohl(tvb, offset2+4));
                    proto_item_set_text(ti, "HOP: IPv4, %s", 
                                        ip_to_str(tvb_get_ptr(tvb, offset2, 4)));
		    break;
		}

		case 2: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 2 - IPv6");
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, 16,
					"Neighbor address: %s", 
					ip6_to_str((struct e_in6_addr *)tvb_get_ptr(tvb, offset2, 16)));
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+16, 4,
					"Logical interface: 0x%08x", 
					tvb_get_ntohl(tvb, offset2+16));
		    break;
		}
		
		default: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: Unknown (%u)",
					type);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, mylen,
					"Data (%d bytes)", mylen);
		}
		}
		break;
		
	    case RSVP_CLASS_TIME_VALUES : 
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_time_values);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
		mylen = obj_length - 4;
		switch(type) {
		case 1: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 1");
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, 4, 
					"Refresh interval: %u ms (%u seconds)",
					tvb_get_ntohl(tvb, offset2),
					tvb_get_ntohl(tvb, offset2)/1000);
                    proto_item_set_text(ti, "TIME VALUES: %d ms", 
                                        tvb_get_ntohl(tvb, offset2));
		    break;
		}

		default: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: Unknown (%u)",
					type);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, mylen,
					"Data (%d bytes)", mylen);
		    break;
		}
		}
		break;

	    case RSVP_CLASS_ERROR :
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_error);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
		mylen = obj_length - 4;
		switch(type) {
		case 1: {
		    guint8 error_code;

		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 1 - IPv4");
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, 4, 
					"Error node: %s",
					ip_to_str(tvb_get_ptr(tvb, offset2, 4)));
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+4, 1,
					"Flags: 0x%02x",
					tvb_get_guint8(tvb, offset2+4));
		    error_code = tvb_get_guint8(tvb, offset2+5);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+5, 1,
					"Error code: %u - %s", error_code,
					val_to_str(error_code, rsvp_error_vals, "Unknown"));
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+6, 2,
					"Error value: %u",
					tvb_get_ntohs(tvb, offset2+6));
                    proto_item_set_text(ti, "ERROR: IPv4, %d, %d, %s", 
                                        tvb_get_guint8(tvb, offset2+5),
                                        tvb_get_ntohs(tvb, offset2+6),
                                        ip_to_str(tvb_get_ptr(tvb, offset2, 4)));
		    break;
		}

		case 2: {
		    guint8 error_code;

		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 2 - IPv6");
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, 16,
					"Error node: %s",
					ip6_to_str((struct e_in6_addr *)tvb_get_ptr(tvb, offset2, 16)));
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+16, 1,
					"Flags: 0x%02x",
					tvb_get_guint8(tvb, offset2+16));
		    error_code = tvb_get_guint8(tvb, offset2+17);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+17, 1,
					"Error code: %u - %s", error_code,
					val_to_str(error_code, rsvp_error_vals, "Unknown"));
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+18, 2,
					"Error value: %u",
					tvb_get_ntohs(tvb, offset2+18));
		    break;
		}
		
		default: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: Unknown (%u)",
					type);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, mylen,
					"Data (%d bytes)", mylen);
		}
		}
		break;
		

	    case RSVP_CLASS_SCOPE : 
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_scope);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
		mylen = obj_length - 4;
		switch(type) {
		case 1: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 1 - IPv4");
		    while (mylen > 0) {
			proto_tree_add_text(rsvp_object_tree, tvb, offset2, 4, 
					    "IPv4 Address: %s",
					    ip_to_str(tvb_get_ptr(tvb, offset2, 4)));
			offset2 += 4;
			mylen -= 4;
		    }
		    break;
		}

		case 2: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 2 - IPv6");
		    while (mylen > 0) {
			proto_tree_add_text(rsvp_object_tree, tvb, offset2, 16, 
					    "IPv6 Address: %s",
					    ip6_to_str((struct e_in6_addr *)tvb_get_ptr(tvb, offset2, 16)));
			offset2 += 16;
			mylen -= 16;
		    }
		    break;
		}
		
		default: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: Unknown (%u)",
					type);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, mylen,
					"Data (%d bytes)", mylen);
		}
		}
		break;
		
	    case RSVP_CLASS_STYLE : 
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_style);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
		mylen = obj_length - 4;
		switch(type) {
		case 1: {
		    guint32 style;

		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 1");
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, 1,
					"Flags: 0x%02x",
					tvb_get_guint8(tvb, offset2));
		    style = tvb_get_ntoh24(tvb, offset2+1);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+1,
                                        3, "Style: 0x%06X - %s", style,
                                        val_to_str(style, style_vals, "Unknown"));
                    proto_item_set_text(ti, "STYLE: %s (%d)", 
                                        val_to_str(style, style_vals, "Unknown"),
                                        style);
		    break;
		}

		default: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: Unknown (%u)",
					type);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, mylen,
					"Data (%d bytes)", mylen);
		    break;
		}
		}
		break;
	    
	    case RSVP_CLASS_CONFIRM :		
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_confirm);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
		mylen = obj_length - 4;
		switch(type) {
		case 1: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 1 - IPv4");
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, 4, 
					"Receiver address: %s", 
					ip_to_str(tvb_get_ptr(tvb, offset2, 4)));
		    proto_item_set_text(ti, "CONFIRM: %s", 
					ip_to_str(tvb_get_ptr(tvb, offset2, 4)));
		    break;
		}

		case 2: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 2 - IPv6");
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, 16, 
					"Receiver address: %s", 
					ip6_to_str((struct e_in6_addr *)tvb_get_ptr(tvb, offset2, 16)));
		    break;
		}

		default: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: Unknown (%u)",
					type);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, mylen,
					"Data (%d bytes)", mylen);
		}
		}
		break;

	    case RSVP_CLASS_SENDER_TEMPLATE :
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_sender_template);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
                objtype = "SENDER TEMPLATE";
		goto common_template;
	    case RSVP_CLASS_FILTER_SPEC :
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_filter_spec);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
                objtype = "FILTERSPEC";
	    common_template:
		mylen = obj_length - 4;
		switch(type) {
		case 1: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 1 - IPv4");
		    proto_tree_add_item(rsvp_object_tree,
					rsvp_filter[RSVPF_SENDER_IP],
					tvb, offset2, 4, FALSE);
		    proto_tree_add_item(rsvp_object_tree,
					rsvp_filter[RSVPF_SENDER_PORT],
					tvb, offset2+6, 2, FALSE);
                    proto_item_set_text(ti, "%s: IPv4, %s, %d", objtype,
                                        ip_to_str(tvb_get_ptr(tvb, offset2, 4)), 
                                        tvb_get_ntohs(tvb, offset2+6));
		    break;
		}

		case 2: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 2 - IPv6");
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, 16, 
					"Source address: %s", 
					ip6_to_str((struct e_in6_addr *)tvb_get_ptr(tvb, offset2, 16)));
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+18, 2,
					"Source port: %u",
					tvb_get_ntohs(tvb, offset2+18));
		    break;
		}
		
		case 7: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 7 - IPv4 LSP");
		    proto_tree_add_item(rsvp_object_tree,
					rsvp_filter[RSVPF_SENDER_IP],
					tvb, offset2, 4, FALSE);
		    proto_tree_add_item(rsvp_object_tree,
					rsvp_filter[RSVPF_SENDER_LSP_ID],
		    			tvb, offset2+6, 2, FALSE);
                    proto_item_set_text(ti, "%s: IPv4-LSP, %s, %d", objtype,
                                        ip_to_str(tvb_get_ptr(tvb, offset2, 4)), 
                                        tvb_get_ntohs(tvb, offset2+6));
		    break;
		}

		default: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: Unknown (%u)",
					type);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, mylen,
					"Data (%d bytes)", mylen);
		}
		}
		break;

	    case RSVP_CLASS_SENDER_TSPEC : {
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_sender_tspec);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
		mylen = obj_length - 4;

		proto_tree_add_text(rsvp_object_tree, tvb, offset2, 1, 
				    "Message format version: %u", 
				    tvb_get_guint8(tvb, offset2)>>4);
		proto_tree_add_text(rsvp_object_tree, tvb, offset2+2, 2, 
				    "Data length: %u words, not including header", 
				    tvb_get_ntohs(tvb, offset2+2));

		mylen -= 4;
		offset2 += 4;
		while (mylen > 0) {
		    guint8 service_num;
		    guint8 param_id;
		    guint16 length;

		    service_num = tvb_get_guint8(tvb, offset2);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, 1, 
					"Service header: %u - %s", 
					service_num,
					val_to_str(service_num, qos_vals, "Unknown"));
		    length = tvb_get_ntohs(tvb, offset2+2);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+2, 2, 
					"Length of service %u data: %u words, " 
					"not including header", 
					service_num, length);

		    mylen -= 4;
		    offset2 += 4;

		    switch(service_num) {
			
		    case QOS_TSPEC :
			/* Token bucket TSPEC */
			param_id = tvb_get_guint8(tvb, offset2);
			proto_tree_add_text(rsvp_object_tree, tvb, offset2, 1, 
					    "Parameter %u - %s", 
					    param_id,
					    val_to_str(param_id, svc_vals, "Unknown"));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+1, 1,
					    "Parameter %u flags: 0x%02x",
					    param_id,
					    tvb_get_guint8(tvb, offset2+1));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+2, 2,
					    "Parameter %u data length: %u words, " 
					    "not including header",
					    param_id,
					    tvb_get_ntohs(tvb, offset2+2));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+4, 4,
					    "Token bucket rate: %ld", 
					    tvb_ieee_to_long(tvb, offset2+4));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+8, 4,
					    "Token bucket size: %ld", 
					    tvb_ieee_to_long(tvb, offset2+8));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+12, 4,
					    "Peak data rate: %ld", 
					    tvb_ieee_to_long(tvb, offset2+12));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+16, 4,
					    "Minimum policed unit [m]: %u", 
					    tvb_get_ntohl(tvb, offset2+16));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+20, 4,
					    "Maximum packet size [M]: %u", 
					    tvb_get_ntohl(tvb, offset2+20));
                        proto_item_set_text(ti, "SENDER TSPEC: IntServ, %lu bytes/sec", 
                                            tvb_ieee_to_long(tvb, offset2+4));
			break;

		    case QOS_QUALITATIVE :
			/* Token bucket TSPEC */
			param_id = tvb_get_guint8(tvb, offset2);
			proto_tree_add_text(rsvp_object_tree, tvb, offset2, 1, 
					    "Parameter %u - %s", 
					    param_id,
					    val_to_str(param_id, svc_vals, "Unknown"));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+1, 1,
					    "Parameter %u flags: %x", 
					    param_id,
					    tvb_get_guint8(tvb, offset2+1));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+2, 2,
					    "Parameter %u data length: %u words, " 
					    "not including header",
					    param_id,
					    tvb_get_ntohs(tvb, offset2+2));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+4, 4,
					    "Maximum packet size [M]: %u", 
					    tvb_get_ntohl(tvb, offset2+4));
                        proto_item_set_text(ti, "SENDER TSPEC: Qualitative");
			break;

		    }
		    offset2 += length*4; 
		    mylen -= length*4;
		}
		break;
	    }

	    case RSVP_CLASS_FLOWSPEC : {
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_flowspec);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
		mylen = obj_length - 4;

		proto_tree_add_text(rsvp_object_tree, tvb, offset2, 1, 
				    "Message format version: %u", 
				    tvb_get_guint8(tvb, offset2)>>4);
		proto_tree_add_text(rsvp_object_tree, tvb, offset2+2, 2, 
				    "Data length: %u words, not including header", 
				    tvb_get_ntohs(tvb, offset2+2));

		mylen -= 4;
		offset2+= 4;
		while (mylen > 0) {
		    guint8 service_num;
		    guint16 length;
		    guint8 param_id;

		    service_num = tvb_get_guint8(tvb, offset2);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, 1, 
					"Service header: %u - %s", 
					service_num,
					val_to_str(service_num, intsrv_services_str, "Unknown"));
		    length = tvb_get_ntohs(tvb, offset2+2);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+2, 2, 
					"Length of service %u data: %u words, " 
					"not including header", 
					service_num,
					length);

		    mylen -= 4;
		    offset2 += 4;

		    switch(service_num) {

		    case QOS_CONTROLLED_LOAD :
		    case QOS_GUARANTEED :
			/* Treat both these the same for now */
			param_id = tvb_get_guint8(tvb, offset2);
			proto_tree_add_text(rsvp_object_tree, tvb, offset2, 1, 
					    "Parameter %u - %s", 
					    param_id,
					    val_to_str(param_id, svc_vals, "Unknown"));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+1, 1,
					    "Parameter %u flags: %x", 
					    param_id,
					    tvb_get_guint8(tvb, offset2+1));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+2, 2,
					    "Parameter %u data length: %u words, " 
					    "not including header",
					    param_id,
					    tvb_get_ntohs(tvb, offset2+2));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+4, 4,
					    "Token bucket rate: %ld", 
					    tvb_ieee_to_long(tvb, offset2+4));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+8, 4,
					    "Token bucket size: %ld", 
					    tvb_ieee_to_long(tvb, offset2+8));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+12, 4,
					    "Peak data rate: %ld", 
					    tvb_ieee_to_long(tvb, offset2+12));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+16, 4,
					    "Minimum policed unit [m]: %u", 
					    tvb_get_ntohl(tvb, offset2+16));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+20, 4,
					    "Maximum packet size [M]: %u", 
					    tvb_get_ntohl(tvb, offset2+20));

			if (service_num != QOS_GUARANTEED) {
                            proto_item_set_text(ti, "FLOWSPEC: Controlled-Load, %lu bytes/sec", 
                                                tvb_ieee_to_long(tvb, offset2+4));
			    break;
                        }
			
			/* Guaranteed-rate RSpec */
			param_id = tvb_get_guint8(tvb, offset2+24);
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+24, 1,
					    "Parameter %u - %s", 
					    param_id,
					    val_to_str(param_id, svc_vals, "Unknown"));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+25, 1, 
					    "Parameter %u flags: %x", 
					    param_id,
					    tvb_get_guint8(tvb, offset2+25));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+26, 2,
					    "Parameter %u data length: %u words, " 
					    "not including header",
					    param_id,
					    tvb_get_ntohs(tvb, offset2+26));

			proto_tree_add_text(rsvp_object_tree, tvb, offset2+28, 4,
					    "Rate: %ld", 
					    tvb_ieee_to_long(tvb, offset2+28));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+32, 4,
					    "Slack term: %u", 
					    tvb_get_ntohl(tvb, offset2+32));
                        proto_item_set_text(ti, "FLOWSPEC: Guaranteed-Rate, %lu bytes/sec", 
                                            tvb_ieee_to_long(tvb, offset2+4));
			break;

		    case QOS_QUALITATIVE :
			param_id = tvb_get_guint8(tvb, offset2);
			proto_tree_add_text(rsvp_object_tree, tvb, offset2, 1, 
					    "Parameter %u - %s", 
					    param_id,
					    val_to_str(param_id, svc_vals, "Unknown"));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+1, 1,
					    "Parameter %u flags: %x", 
					    param_id,
					    tvb_get_guint8(tvb, offset2+1));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+2, 2,
					    "Parameter %u data length: %u words, " 
					    "not including header",
					    param_id,
					    tvb_get_ntohs(tvb, offset2+2));
			proto_tree_add_text(rsvp_object_tree, tvb, offset2+4, 4,
					    "Maximum packet size [M]: %u", 
					    tvb_get_ntohl(tvb, offset2+4));
			
                        proto_item_set_text(ti, "FLOWSPEC: Qualitative");
			break;
		    }
		    offset2 += length*4;
		    mylen -= length*4;
		}
		break;
	    }

	    case RSVP_CLASS_ADSPEC : {
		proto_tree *adspec_tree;

		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_adspec);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
		mylen = obj_length - 4;

		proto_tree_add_text(rsvp_object_tree, tvb, offset2, 1,
				    "Message format version: %u", 
				    tvb_get_guint8(tvb, offset2)>>4);
		proto_tree_add_text(rsvp_object_tree, tvb, offset2+2, 2,
				    "Data length: %u words, not including header", 
				    tvb_get_ntohs(tvb, offset2+2));
		mylen -= 4;
		offset2 += 4;
		while (mylen > 0) {
		    guint8 service_num;
		    guint8 break_bit;
		    guint16 length;
		    char *str;

		    service_num = tvb_get_guint8(tvb, offset2);
		    str = val_to_str(service_num, intsrv_services_str, "Unknown");
		    break_bit = tvb_get_guint8(tvb, offset2+1);
		    length = tvb_get_ntohs(tvb, offset2+2);
		    ti = proto_tree_add_text(rsvp_object_tree, tvb, offset2, 
					     (length+1)*4,
					     str);
		    adspec_tree = proto_item_add_subtree(ti,
							 ett_rsvp_adspec_subtree);
		    proto_tree_add_text(adspec_tree, tvb, offset2, 1,
					"Service header %u - %s",
					service_num, str);
		    proto_tree_add_text(adspec_tree, tvb, offset2+1, 1,
					(break_bit&0x80)?
					"Break bit set":"Break bit not set");
		    proto_tree_add_text(adspec_tree, tvb, offset2+2, 2, 
					"Data length: %u words, not including header", 
					length);
		    mylen -= 4;
		    offset2 += 4;
		    i = length*4;
		    while (i > 0) {
		    	guint8 id;
		    	guint16 phdr_length;

			id = tvb_get_guint8(tvb, offset2);
			phdr_length = tvb_get_ntohs(tvb, offset2+2);
			str = match_strval(id, adspec_params);
			if (str) {
			    switch(id) {
			    case 4:
			    case 8:
			    case 10:
			    case 133:
			    case 134:
			    case 135:
			    case 136:
				/* 32-bit unsigned integer */
				proto_tree_add_text(adspec_tree, tvb, offset2,
						    (phdr_length+1)<<2,
						    "%s - %u (type %u, length %u)",
						    str,
						    tvb_get_ntohl(tvb, offset2+4),
						    id, phdr_length);
				break;
				
			    case 6:
				/* IEEE float */
				proto_tree_add_text(adspec_tree, tvb, offset2,
						    (phdr_length+1)<<2,
						    "%s - %lu (type %u, length %u)",
						    str,
						    tvb_ieee_to_long(tvb, offset2+4),
						    id, phdr_length);
				break;
			    default: 
				proto_tree_add_text(adspec_tree, tvb, offset2, 
						    (phdr_length+1)<<2,
						    "%s (type %u, length %u)",
						    str,
						    id, phdr_length);
			    }
			} else {
			    proto_tree_add_text(adspec_tree, tvb, offset2, 
						(phdr_length+1)<<2,
						"Unknown (type %u, length %u)",
						id, phdr_length);
			}
			offset2 += (phdr_length+1)<<2;
			i -= (phdr_length+1)<<2;
			mylen -= (phdr_length+1)<<2;
		    }
		}
		break;
	    }

	    case RSVP_CLASS_INTEGRITY :
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_integrity);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
		goto default_class;

	    case RSVP_CLASS_POLICY :
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_policy);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
		goto default_class;

	    case RSVP_CLASS_LABEL_REQUEST : 
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_label_request);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
		mylen = obj_length - 4;
		switch(type) {
		case 1: {
		    unsigned short l3pid = tvb_get_ntohs(tvb, offset2+2);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 1");
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+2, 2,
					"L3PID: %s (0x%04x)",
					val_to_str(l3pid, etype_vals, "Unknown"),
					l3pid);
                    proto_item_set_text(ti, "LABEL REQUEST: %s (0x%04x)",
					val_to_str(l3pid, etype_vals, "Unknown"),
					l3pid);
		    break;
		}

		default: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: Unknown (%u)",
					type);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, mylen,
					"Data (%d bytes)", mylen);
		    break;
		}
		}
		break;
	    
	    case RSVP_CLASS_LABEL : 
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_label);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
		mylen = obj_length - 4;
		switch(type) {
		case 1: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 1");
                    proto_tree_add_text(rsvp_object_tree, tvb, offset2, 4,
                                        "Label: %u", 
                                        tvb_get_ntohl(tvb, offset2));
                    proto_item_set_text(ti, "LABEL: %d", 
                                        tvb_get_ntohl(tvb, offset2));
		    break;
		}

		default: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: Unknown (%u)",
					type);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, mylen,
					"Data (%d bytes)", mylen);
		    break;
		}
		}
		break;
	    
	    case RSVP_CLASS_SESSION_ATTRIBUTE : 
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_session_attribute);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
		mylen = obj_length - 4;
		switch(type) {
		case 7: {
		    guint8 flags;
		    guint8 name_len;

		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 7 - IPv4 LSP");
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, 1,
					"Setup priority: %u",
					tvb_get_guint8(tvb, offset2));
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+1, 1,
					"Hold priority: %u",
					tvb_get_guint8(tvb, offset2+1));
		    flags = tvb_get_guint8(tvb, offset2+2);
		    ti2 = proto_tree_add_text(rsvp_object_tree, tvb, offset2+2, 1,
					      "Flags: 0x%02x", flags);
		    rsvp_sa_flags_tree = proto_item_add_subtree(ti2, 
								ett_rsvp_session_attribute_flags);
		    proto_tree_add_text(rsvp_sa_flags_tree, tvb, offset2+2, 1,
					decode_boolean_bitfield(flags, 0x01, 8,
					    "Local protection desired",
					    "Local protection not desired"));
		    proto_tree_add_text(rsvp_sa_flags_tree, tvb, offset2+2, 1,
					decode_boolean_bitfield(flags, 0x02, 8,
					    "Merging permitted",
					    "Merging not permitted"));
		    proto_tree_add_text(rsvp_sa_flags_tree, tvb, offset2+2, 1,
					decode_boolean_bitfield(flags, 0x04, 8,
					    "Ingress node may reroute",
					    "Ingress node may not reroute"));
		    
		    name_len = tvb_get_guint8(tvb, offset2+3);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+3, 1,
					"Name length: %u", name_len);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2+4, name_len,
					"Name: %.*s",
					name_len,
					tvb_get_ptr(tvb, offset2+4, name_len));
		    break;
		}

		default: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: Unknown (%u)",
					type);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, mylen,
					"Data (%d bytes)", mylen);
		    break;
		}
		}
		break;

	    case RSVP_CLASS_EXPLICIT_ROUTE : 
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_explicit_route);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
		mylen = obj_length - 4;
		switch(type) {
		case 1: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 1");
		    for (i=1, l = 0; l < mylen; i++) {
			j = tvb_get_guint8(tvb, offset2+l) & 0x7f;
			switch(j) {
			case 1: /* IPv4 */
			    k = tvb_get_guint8(tvb, offset2+l) & 0x80;
			    ti2 = proto_tree_add_text(rsvp_object_tree, tvb, 
						      offset2+l, 8,
						      "IPv4 Subobject - %s, %s",
						      ip_to_str(tvb_get_ptr(tvb, offset2+l+2, 4)),
						      k ? "Loose" : "Strict");
			    rsvp_ero_subtree = 
				proto_item_add_subtree(ti2, ett_rsvp_explicit_route_subobj); 
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l, 1,
						k ? "Loose Hop " : "Strict Hop");
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l, 1,
						"Type: 1 (IPv4)");
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l+1, 1,
						"Length: %u",
						tvb_get_guint8(tvb, offset2+l+1));
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l+2, 4,
						"IPv4 hop: %s",
						ip_to_str(tvb_get_ptr(tvb, offset2+l+2, 4)));
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l+6, 1, 
						"Prefix length: %u",
						tvb_get_guint8(tvb, offset2+l+6));
			    break;

			case 2: /* IPv6 */
			    ti2 = proto_tree_add_text(rsvp_object_tree, tvb, 
						      offset2+l, 20,
						      "IPv6 Subobject");
			    rsvp_ero_subtree = 
				proto_item_add_subtree(ti2, ett_rsvp_explicit_route_subobj); 
			    k = tvb_get_guint8(tvb, offset2+l) & 0x80;
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l, 1,
						k ? "Loose Hop " : "Strict Hop");
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l, 1,
						"Type: 2 (IPv6)");
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l+1, 1,
						"Length: %u",
						tvb_get_guint8(tvb, offset2+l+1));
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l+2, 16,
						"IPv6 hop: %s",
						ip6_to_str((struct e_in6_addr *)tvb_get_ptr(tvb, offset2+l+2, 16)));
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l+18, 1,
						"Prefix length: %u",
						tvb_get_guint8(tvb, offset2+l+6));
			    break;

			case 32: /* AS */
			    k = tvb_get_ntohs(tvb, offset2+l+2);
			    ti2 = proto_tree_add_text(rsvp_object_tree, tvb, 
						      offset2+l, 4,
						      "Autonomous System %u",
						      k);
			    rsvp_ero_subtree = 
				proto_item_add_subtree(ti2, ett_rsvp_explicit_route_subobj); 
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l, 1,
						k ? "Loose Hop " : "Strict Hop");
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l, 1,
						"Type: 32 (Autonomous System Number)");
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l+1, 1,
						"Length: %u",
						tvb_get_guint8(tvb, offset2+l+1));
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l+2, 2,
						"Autonomous System %u", k);
			    break;

			case 64: /* Path Term */
			    k = tvb_get_guint8(tvb, offset2+l) & 0x80;
			    ti2 = proto_tree_add_text(rsvp_object_tree, tvb,
						      offset2+l, 4,
						      "LSP Path Termination");
			    rsvp_ero_subtree = 
				proto_item_add_subtree(ti2, ett_rsvp_explicit_route_subobj); 
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l, 1,
						k ? "Loose Hop " : "Strict Hop");
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l, 1,
						"Type: 64 (MPLS LSP Path Termination)");
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l+1, 1,
						"Length: %u",
						tvb_get_guint8(tvb, offset2+l+1));
			    break;

			default: /* Unknown subobject */
			    k = tvb_get_guint8(tvb, offset2+l) & 0x80;
			    ti2 = proto_tree_add_text(rsvp_object_tree, tvb, 
						      offset2+l,
						      tvb_get_guint8(tvb, offset2+l+1),
						      "Unknown subobject: %d", j);
			    rsvp_ero_subtree = 
				proto_item_add_subtree(ti2, ett_rsvp_explicit_route_subobj); 
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l, 1,
						k ? "Loose Hop " : "Strict Hop");
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l, 1,
						"Type: %u (Unknown)", j);
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l+1, 1,
						"Length: %u",
						tvb_get_guint8(tvb, offset2+l+1));

			}

			l += tvb_get_guint8(tvb, offset2+l+1);
		    }
		    break;
		}
		default: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: Unknown (%u)",
					type);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, mylen,
					"Data (%d bytes)", mylen);
		    break;
		}
		}
		break;
	    

	    case RSVP_CLASS_RECORD_ROUTE : 
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_record_route);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
		mylen = obj_length - 4;
		switch(type) {
		case 1: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 1");
		    for (i=1, l = 0; l < mylen; i++) {
			j = tvb_get_guint8(tvb, offset2+l);
			switch(j) {
			case 1: /* IPv4 */
			    ti2 = proto_tree_add_text(rsvp_object_tree, tvb, 
						      offset2+l, 8,
						      "IPv4 Subobject - %s",
						      ip_to_str(tvb_get_ptr(tvb, offset2+l+2, 4)));
			    rsvp_ero_subtree = 
				proto_item_add_subtree(ti2, ett_rsvp_record_route_subobj); 
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l, 1,
						"Type: 1 (IPv4)");
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l+1, 1,
						"Length: %u",
						tvb_get_guint8(tvb, offset2+l+1));
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l+2, 4,
						"IPv4 hop: %s",
						ip_to_str(tvb_get_ptr(tvb, offset2+l+2, 4)));
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l+6, 1,
						"Prefix length: %u",
						tvb_get_guint8(tvb, offset2+l+6));
			    break;

			case 2: /* IPv6 */
			    ti2 = proto_tree_add_text(rsvp_object_tree, tvb, 
						      offset2+l, 20,
						      "IPv6 Subobject");
			    rsvp_ero_subtree = 
				proto_item_add_subtree(ti2, ett_rsvp_record_route_subobj); 
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l, 1,
						"Type: 2 (IPv6)");
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l+1, 1,
						"Length: %u",
						tvb_get_guint8(tvb, offset2+l+1));
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l+2, 16,
						"IPv6 hop: %s",
						ip6_to_str((struct e_in6_addr *)tvb_get_ptr(tvb, offset2+l+2, 16)));
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l+18, 1,
						"Prefix length: %u",
						tvb_get_guint8(tvb, offset2+l+6));
			    break;

			default: /* Unknown subobject */
			    k = tvb_get_guint8(tvb, offset2+l) & 0x80;
			    ti2 = proto_tree_add_text(rsvp_object_tree, tvb, 
						      offset2+l,
						      tvb_get_guint8(tvb, offset2+l+1),
						      "Unknown subobject: %u",
						      j);
			    rsvp_ero_subtree = 
				proto_item_add_subtree(ti2, ett_rsvp_record_route_subobj); 
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l, 1,
						k ? "Loose Hop " : "Strict Hop");
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l, 1,
						"Type: %u (Unknown)", j);
			    proto_tree_add_text(rsvp_ero_subtree, tvb, offset2+l+1, 1,
						"Length: %u",
						tvb_get_guint8(tvb, offset2+l+1));

			}

			l += tvb_get_guint8(tvb, offset2+l+1);
		    }
		    break;
		}
		
		default: {
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: Unknown (%u)",
					type);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, mylen,
					"Data (%d bytes)", mylen);
		    break;
		}
		}
		break;
	    
	    case RSVP_CLASS_MESSAGE_ID :
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_policy);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
                switch(type) {
                case 1: 
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 1");
                    proto_tree_add_text(rsvp_object_tree, tvb, offset+4, 1,
                                        "Flags: %d", tvb_get_guint8(tvb, offset+4));
                    proto_tree_add_text(rsvp_object_tree, tvb, offset+5, 3,
                                        "Epoch: %d", tvb_get_ntoh24(tvb, offset+5));
                    proto_tree_add_text(rsvp_object_tree, tvb, offset+8, 4,
                                        "Message-ID: %d", tvb_get_ntohl(tvb, offset+8));
                    proto_item_set_text(ti, "MESSAGE-ID: %d %s", 
                                        tvb_get_ntohl(tvb, offset+8), 
                                        tvb_get_guint8(tvb, offset+4) & 1 ? "(Ack Desired)" : "");
                    break;

                default:
                    mylen = obj_length - 4;
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: Unknown (%u)",
					type);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, mylen,
					"Data (%d bytes)", mylen);
		    break;
                }
                break;

	    case RSVP_CLASS_MESSAGE_ID_ACK :
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_policy);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
                switch(type) {
                case 1: 
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 1");
                    proto_tree_add_text(rsvp_object_tree, tvb, offset+4, 1,
                                        "Flags: %d", tvb_get_guint8(tvb, offset+4));
                    proto_tree_add_text(rsvp_object_tree, tvb, offset+5, 3,
                                        "Epoch: %d", tvb_get_ntoh24(tvb, offset+5));
                    proto_tree_add_text(rsvp_object_tree, tvb, offset+8, 4,
                                        "Message-ID: %d", tvb_get_ntohl(tvb, offset+8));
                    proto_item_set_text(ti, "MESSAGE-ID ACK: %d", tvb_get_ntohl(tvb, offset+8));
                    break;

                case 2: 
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 2");
                    proto_tree_add_text(rsvp_object_tree, tvb, offset+4, 1,
                                        "Flags: %d", tvb_get_guint8(tvb, offset+4));
                    proto_tree_add_text(rsvp_object_tree, tvb, offset+5, 3,
                                        "Epoch: %d", tvb_get_ntoh24(tvb, offset+5));
                    proto_tree_add_text(rsvp_object_tree, tvb, offset+8, 4,
                                        "Message-ID: %d", tvb_get_ntohl(tvb, offset+8));
                    proto_item_set_text(ti, "MESSAGE-ID NACK: %d", tvb_get_ntohl(tvb, offset+8));
                    break;

                default:
                    mylen = obj_length - 4;
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: Unknown (%u)",
					type);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, mylen,
					"Data (%d bytes)", mylen);
		    break;
                }
                break;

	    case RSVP_CLASS_MESSAGE_ID_LIST :
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_policy);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
                switch(type) {
                case 1: 
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: 1");
                    proto_tree_add_text(rsvp_object_tree, tvb, offset+4, 1,
                                        "Flags: %d", tvb_get_guint8(tvb, offset+4));
                    proto_tree_add_text(rsvp_object_tree, tvb, offset+5, 3,
                                        "Epoch: %d", tvb_get_ntoh24(tvb, offset+5));
                    for (mylen = 8; mylen < obj_length; mylen += 4)
                        proto_tree_add_text(rsvp_object_tree, tvb, offset+mylen, 4,
                                            "Message-ID: %d", tvb_get_ntohl(tvb, offset+mylen));
                    proto_item_set_text(ti, "MESSAGE-ID LIST: %d IDs", 
                                        (obj_length - 8)/4);
                    break;

                default:
                    mylen = obj_length - 4;
		    proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-type: Unknown (%u)",
					type);
		    proto_tree_add_text(rsvp_object_tree, tvb, offset2, mylen,
					"Data (%d bytes)", mylen);
		    break;
                }
                break;

	    case RSVP_CLASS_HELLO:
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_hello_obj);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);        
		switch(type) {
		    case 1:
		      proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-Type: 1 - HELLO REQUEST object");
		      break;
		    case 2:
		      proto_tree_add_text(rsvp_object_tree, tvb, offset+3, 1, 
					"C-Type: 2 - HELLO ACK object");
		      break;
		};

		proto_tree_add_text(rsvp_object_tree, tvb, offset+4, 4,
				    "Source Instance: 0x%x",tvb_get_ntohl(tvb, offset+4));
   
		proto_tree_add_text(rsvp_object_tree, tvb, offset+8, 4,
				    "Destination Instance: 0x%x",tvb_get_ntohl(tvb, offset+8));
   
		break;

	    default :
		rsvp_object_tree = proto_item_add_subtree(ti, ett_rsvp_unknown_class);
		proto_tree_add_text(rsvp_object_tree, tvb, offset, 2,
				    "Length: %u", obj_length);
		proto_tree_add_text(rsvp_object_tree, tvb, offset+2, 1, 
				    "Class number: %u - %s", 
				    class, object_type);
	    default_class:
		mylen = obj_length - 4;
		proto_tree_add_text(rsvp_object_tree, tvb, offset2, mylen,
				    "Data (%d bytes)", mylen);
		break;

	    case RSVP_CLASS_NULL :
		break;

	    }  
	    
	    offset += obj_length;
	    len += obj_length;
	}
    }
}

void
proto_register_rsvp(void)
{
	static gint *ett[] = {
		&ett_rsvp,
		&ett_rsvp_hdr,
		&ett_rsvp_session,
		&ett_rsvp_hop,
		&ett_rsvp_time_values,
		&ett_rsvp_error,
		&ett_rsvp_scope,
		&ett_rsvp_style,
		&ett_rsvp_confirm,
		&ett_rsvp_sender_template,
		&ett_rsvp_filter_spec,
		&ett_rsvp_sender_tspec,
		&ett_rsvp_flowspec,
		&ett_rsvp_adspec,
		&ett_rsvp_adspec_subtree,
		&ett_rsvp_integrity,
		&ett_rsvp_policy,
		&ett_rsvp_label,
		&ett_rsvp_label_request,
		&ett_rsvp_session_attribute,
		&ett_rsvp_session_attribute_flags,
		&ett_rsvp_explicit_route,
		&ett_rsvp_explicit_route_subobj,
		&ett_rsvp_record_route,
		&ett_rsvp_record_route_subobj,
		&ett_rsvp_hello_obj,
		&ett_rsvp_unknown_class,
	};

        proto_rsvp = proto_register_protocol("Resource ReserVation Protocol (RSVP)",
	    "RSVP", "rsvp");
        proto_register_field_array(proto_rsvp, rsvpf_info, array_length(rsvpf_info));
	proto_register_subtree_array(ett, array_length(ett));
}

void
proto_reg_handoff_rsvp(void)
{
	dissector_add("ip.proto", IP_PROTO_RSVP, dissect_rsvp, proto_rsvp);
}
