Apparently bit 0x80 of a SAMR domain handle is the permission to look
[metze/wireshark/wip.git] / packet-ldp.c
index 70a4add32dbc0f7f7ce09ab098e26949c145acc1..e3c665cb2067b5a8d2b5a46c66c6ea7fcab31116 100644 (file)
@@ -1,13 +1,17 @@
 /* packet-ldp.c
- * Routines for ldp packet disassembly
+ * Routines for LDP (RFC 3036) packet disassembly
+ *
+ * $Id: packet-ldp.c,v 1.43 2002/08/28 21:00:20 jmayer Exp $
  *
- * $Id: packet-ldp.c,v 1.9 2000/12/04 06:05:49 guy Exp $
- * 
  * Copyright (c) November 2000 by Richard Sharpe <rsharpe@ns.aus.com>
  *
+ * CRLDP (RFC3212) is now supported
+ *   - (c) 2002 Michael Rozhavsky <mike[AT]tochna.technion.ac.il>
+ *
  * Ethereal - Network traffic analyzer
- * By Gerald Combs
+ * By Gerald Combs <gerald@ethereal.com>
  * Copyright 1999 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
 #include "config.h"
 #endif
 
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
 #include <time.h>
 #include <glib.h>
 #include <string.h>
-#include "packet.h"
-#include "resolv.h"
+#include <epan/packet.h>
+#include <epan/resolv.h>
 #include "prefs.h"
+#include "afn.h"
+
+#include "packet-frame.h"
 
 #define TCP_PORT_LDP 646
 #define UDP_PORT_LDP 646
@@ -59,22 +58,118 @@ static int hf_ldp_version = -1;
 static int hf_ldp_pdu_len = -1;
 static int hf_ldp_lsr = -1;
 static int hf_ldp_ls_id = -1;
+static int hf_ldp_msg_ubit = -1;
 static int hf_ldp_msg_type = -1;
 static int hf_ldp_msg_len = -1;
 static int hf_ldp_msg_id = -1;
+static int hf_ldp_msg_vendor_id = -1;
+static int hf_ldp_msg_experiment_id = -1;
 static int hf_ldp_tlv_value = -1;
 static int hf_ldp_tlv_type = -1;
+static int hf_ldp_tlv_unknown = -1;
 static int hf_ldp_tlv_len = -1;
 static int hf_ldp_tlv_val_hold = -1;
 static int hf_ldp_tlv_val_target = -1;
 static int hf_ldp_tlv_val_request = -1;
 static int hf_ldp_tlv_val_res = -1;
+static int hf_ldp_tlv_ipv4_taddr = -1;
 static int hf_ldp_tlv_config_seqno = -1;
+static int hf_ldp_tlv_ipv6_taddr = -1;
 static int hf_ldp_tlv_fec_wc = -1;
 static int hf_ldp_tlv_fec_af = -1;
 static int hf_ldp_tlv_fec_len = -1;
 static int hf_ldp_tlv_fec_pfval = -1;
-
+static int hf_ldp_tlv_fec_hoval = -1;
+static int hf_ldp_tlv_addrl_addr_family = -1;
+static int hf_ldp_tlv_addrl_addr = -1;
+static int hf_ldp_tlv_hc_value = -1;
+static int hf_ldp_tlv_pv_lsrid = -1;
+static int hf_ldp_tlv_generic_label = -1;
+static int hf_ldp_tlv_atm_label_vbits = -1;
+static int hf_ldp_tlv_atm_label_vpi = -1;
+static int hf_ldp_tlv_atm_label_vci = -1;
+static int hf_ldp_tlv_fr_label_len = -1;
+static int hf_ldp_tlv_fr_label_dlci = -1;
+static int hf_ldp_tlv_status_ebit = -1;
+static int hf_ldp_tlv_status_fbit = -1;
+static int hf_ldp_tlv_status_data = -1;
+static int hf_ldp_tlv_status_msg_id = -1;
+static int hf_ldp_tlv_status_msg_type = -1;
+static int hf_ldp_tlv_extstatus_data = -1;
+static int hf_ldp_tlv_returned_version = -1;
+static int hf_ldp_tlv_returned_pdu_len = -1;
+static int hf_ldp_tlv_returned_lsr = -1;
+static int hf_ldp_tlv_returned_ls_id = -1;
+static int hf_ldp_tlv_returned_msg_ubit = -1;
+static int hf_ldp_tlv_returned_msg_type = -1;
+static int hf_ldp_tlv_returned_msg_len = -1;
+static int hf_ldp_tlv_returned_msg_id = -1;
+static int hf_ldp_tlv_mac = -1;
+static int hf_ldp_tlv_sess_ver = -1;
+static int hf_ldp_tlv_sess_ka = -1;
+static int hf_ldp_tlv_sess_advbit = -1;
+static int hf_ldp_tlv_sess_ldetbit = -1;
+static int hf_ldp_tlv_sess_pvlim = -1;
+static int hf_ldp_tlv_sess_mxpdu = -1;
+static int hf_ldp_tlv_sess_rxlsr = -1;
+static int hf_ldp_tlv_sess_rxls = -1;
+static int hf_ldp_tlv_sess_atm_merge = -1;
+static int hf_ldp_tlv_sess_atm_lr = -1;
+static int hf_ldp_tlv_sess_atm_dir = -1;
+static int hf_ldp_tlv_sess_atm_minvpi = -1;
+static int hf_ldp_tlv_sess_atm_maxvpi = -1;
+static int hf_ldp_tlv_sess_atm_minvci = -1;
+static int hf_ldp_tlv_sess_atm_maxvci = -1;
+static int hf_ldp_tlv_sess_fr_merge = -1;
+static int hf_ldp_tlv_sess_fr_lr = -1;
+static int hf_ldp_tlv_sess_fr_dir = -1;
+static int hf_ldp_tlv_sess_fr_len = -1;
+static int hf_ldp_tlv_sess_fr_mindlci = -1;
+static int hf_ldp_tlv_sess_fr_maxdlci = -1;
+static int hf_ldp_tlv_lbl_req_msg_id = -1;
+static int hf_ldp_tlv_vendor_id = -1;
+static int hf_ldp_tlv_experiment_id = -1;
+static int hf_ldp_tlv_fec_vc_controlword = -1;
+static int hf_ldp_tlv_fec_vc_vctype = -1;
+static int hf_ldp_tlv_fec_vc_infolength = -1;
+static int hf_ldp_tlv_fec_vc_groupid = -1;
+static int hf_ldp_tlv_fec_vc_vcid = -1;
+static int hf_ldp_tlv_fec_vc_intparam_length = -1;
+static int hf_ldp_tlv_fec_vc_intparam_mtu = -1;
+static int hf_ldp_tlv_fec_vc_intparam_id = -1;
+static int hf_ldp_tlv_fec_vc_intparam_maxcatmcells = -1;
+static int hf_ldp_tlv_fec_vc_intparam_desc = -1;
+static int hf_ldp_tlv_fec_vc_intparam_cembytes = -1;
+static int hf_ldp_tlv_fec_vc_intparam_vpnid_oui = -1;
+static int hf_ldp_tlv_fec_vc_intparam_vpnid_index = -1;
+static int hf_ldp_tlv_lspid_act_flg = -1;
+static int hf_ldp_tlv_lspid_cr_lsp = -1;
+static int hf_ldp_tlv_lspid_ldpid = -1;
+static int hf_ldp_tlv_er_hop_loose = -1;
+static int hf_ldp_tlv_er_hop_prelen = -1;
+static int hf_ldp_tlv_er_hop_prefix4 = -1;
+static int hf_ldp_tlv_er_hop_prefix6 = -1;
+static int hf_ldp_tlv_er_hop_as = -1;
+static int hf_ldp_tlv_er_hop_cr_lsp = -1;
+static int hf_ldp_tlv_er_hop_ldpid = -1;
+static int hf_ldp_tlv_flags_reserv = -1;
+static int hf_ldp_tlv_flags_weight = -1;
+static int hf_ldp_tlv_flags_ebs = -1;
+static int hf_ldp_tlv_flags_cbs = -1;
+static int hf_ldp_tlv_flags_cdr = -1;
+static int hf_ldp_tlv_flags_pbs = -1;
+static int hf_ldp_tlv_flags_pdr = -1;
+static int hf_ldp_tlv_frequency = -1;
+static int hf_ldp_tlv_pdr = -1;
+static int hf_ldp_tlv_pbs = -1;
+static int hf_ldp_tlv_cdr = -1;
+static int hf_ldp_tlv_cbs = -1;
+static int hf_ldp_tlv_ebs = -1;
+static int hf_ldp_tlv_weight = -1;
+static int hf_ldp_tlv_set_prio = -1;
+static int hf_ldp_tlv_hold_prio = -1;
+static int hf_ldp_tlv_route_pinning = -1;
+static int hf_ldp_tlv_resource_class = -1;
 static int ett_ldp = -1;
 static int ett_ldp_header = -1;
 static int ett_ldp_ldpid = -1;
@@ -82,10 +177,14 @@ static int ett_ldp_message = -1;
 static int ett_ldp_tlv = -1;
 static int ett_ldp_tlv_val = -1;
 static int ett_ldp_fec = -1;
+static int ett_ldp_fec_vc_interfaceparam = -1;
 
 static int tcp_port = 0;
 static int udp_port = 0;
 
+/* desegmentation of LDP over TCP */
+static gboolean ldp_desegment = FALSE;
+
 /* Add your functions here */
 
 static int global_ldp_tcp_port = TCP_PORT_LDP;
@@ -110,35 +209,60 @@ static int global_ldp_udp_port = UDP_PORT_LDP;
 #define TLV_IPV4_TRANSPORT_ADDRESS 0x0401
 #define TLV_CONFIGURATION_SEQNO    0x0402
 #define TLV_IPV6_TRANSPORT_ADDRESS 0x0403
+#define TLV_MAC                    0x0404
 #define TLV_COMMON_SESSION_PARMS   0x0500
 #define TLV_ATM_SESSION_PARMS      0x0501
 #define TLV_FRAME_RELAY_SESSION_PARMS 0x0502
 #define TLV_LABEL_REQUEST_MESSAGE_ID 0x0600
+#define TLV_ER                     0x0800
+#define TLV_ER_HOP_IPV4            0x0801
+#define TLV_ER_HOP_IPV6            0x0802
+#define TLV_ER_HOP_AS              0x0803
+#define TLV_ER_HOP_LSPID           0x0804
+#define TLV_TRAFFIC_PARAM          0x0810
+#define TLV_PREEMPTION             0x0820
+#define TLV_LSPID                  0x0821
+#define TLV_RESOURCE_CLASS         0x0822
+#define TLV_ROUTE_PINNING          0x0823
 
 #define TLV_VENDOR_PRIVATE_START   0x3E00
-#define TLV_VENDOR_PROVATE_END     0x3EFF
+#define TLV_VENDOR_PRIVATE_END     0x3EFF
 #define TLV_EXPERIMENTAL_START     0x3F00
 #define TLV_EXPERIMENTAL_END       0x3FFF
 
-static const value_string tlv_type_names[] = { 
-  { TLV_FEC,                       "Forwarding Equivalence Classes" },
-  { TLV_ADDRESS_LIST,              "Address List"},
-  { TLV_HOP_COUNT,                 "Hop Count"},
-  { TLV_PATH_VECTOR,               "Path Vector"},
-  { TLV_GENERIC_LABEL,             "Generic Label"},
-  { TLV_ATM_LABEL,                 "Frame Label"},
-  { TLV_STATUS,                    "Status"},
-  { TLV_EXTENDED_STATUS,           "Extended Status"},
-  { TLV_RETURNED_PDU,              "Returned PDU"},
-  { TLV_RETURNED_MESSAGE,          "Returned Message"},
-  { TLV_COMMON_HELLO_PARMS,        "Common Hello Parameters"},
-  { TLV_IPV4_TRANSPORT_ADDRESS,    "IPv4 Transport Address"},
-  { TLV_CONFIGURATION_SEQNO,       "Configuration Sequence Number"},
-  { TLV_IPV6_TRANSPORT_ADDRESS,    "IPv6 Transport Address"},
-  { TLV_COMMON_SESSION_PARMS,      "Common Session Parameters"},
-  { TLV_ATM_SESSION_PARMS,         "ATM Session Parameters"},
-  { TLV_FRAME_RELAY_SESSION_PARMS, "Frame Relay Session Parameters"},
-  { TLV_LABEL_REQUEST_MESSAGE_ID,  "Label Request Message ID"},
+static const value_string tlv_type_names[] = {
+  { TLV_FEC,                       "Forwarding Equivalence Classes TLV" },
+  { TLV_ADDRESS_LIST,              "Address List TLV"},
+  { TLV_HOP_COUNT,                 "Hop Count TLV"},
+  { TLV_PATH_VECTOR,               "Path Vector TLV"},
+  { TLV_GENERIC_LABEL,             "Generic Label TLV"},
+  { TLV_ATM_LABEL,                 "ATM Label TLV"},
+  { TLV_FRAME_LABEL,               "Frame Label TLV"},
+  { TLV_STATUS,                    "Status TLV"},
+  { TLV_EXTENDED_STATUS,           "Extended Status TLV"},
+  { TLV_RETURNED_PDU,              "Returned PDU TLV"},
+  { TLV_RETURNED_MESSAGE,          "Returned Message TLV"},
+  { TLV_COMMON_HELLO_PARMS,        "Common Hello Parameters TLV"},
+  { TLV_IPV4_TRANSPORT_ADDRESS,    "IPv4 Transport Address TLV"},
+  { TLV_CONFIGURATION_SEQNO,       "Configuration Sequence Number TLV"},
+  { TLV_IPV6_TRANSPORT_ADDRESS,    "IPv6 Transport Address TLV"},
+  { TLV_MAC,                       "MAC TLV"},
+  { TLV_COMMON_SESSION_PARMS,      "Common Session Parameters TLV"},
+  { TLV_ATM_SESSION_PARMS,         "ATM Session Parameters TLV"},
+  { TLV_FRAME_RELAY_SESSION_PARMS, "Frame Relay Session Parameters TLV"},
+  { TLV_LABEL_REQUEST_MESSAGE_ID,  "Label Request Message ID TLV"},
+  { TLV_LSPID,                     "LSP ID TLV"},
+  { TLV_ER,                        "Explicit route TLV"},
+  { TLV_ER_HOP_IPV4,               "ER hop IPv4 prefix TLV"},
+  { TLV_ER_HOP_IPV6,               "ER hop IPv6 prefix TLV"},
+  { TLV_ER_HOP_AS,                 "ER hop Autonomous system number prefix TLV"},
+  { TLV_TRAFFIC_PARAM,             "Traffic parameters TLV"},
+  { TLV_PREEMPTION,                "Preemption TLV"},
+  { TLV_ER_HOP_LSPID,              "ER hop LSPID prefix TLV"},
+  { TLV_RESOURCE_CLASS,            "Resource Class (Color) TLV"},
+  { TLV_ROUTE_PINNING,             "Route Pinning TLV"},
+  { TLV_VENDOR_PRIVATE_START,  "Vendor Private TLV"},
+  { TLV_EXPERIMENTAL_START,    "Experimental TLV"},
   { 0, NULL}
 };
 
@@ -163,606 +287,2256 @@ static const value_string tlv_type_names[] = {
 #define LDP_EXPERIMENTAL_MESSAGE_END   0x3FFF
 
 static const value_string ldp_message_types[] = {
-  {LDP_NOTIFICATION,             "Notification"},
-  {LDP_HELLO,                    "Hello"},
-  {LDP_INITIALIZATION,           "Initialization"},
-  {LDP_KEEPALIVE,                "Keep Alive"},
-  {LDP_ADDRESS,                  "Address"},
-  {LDP_ADDRESS_WITHDRAWAL,       "Address Withdrawal"},
-  {LDP_LABEL_MAPPING,            "Label Mapping"},
-  {LDP_LABEL_REQUEST,            "Label Request"},
-  {LDP_LABEL_WITHDRAWAL,         "Label Withdrawal"},
-  {LDP_LABEL_RELEASE,            "Label Release"},
-  {LDP_LABEL_ABORT_REQUEST,      "Label Abort Request"},
+  {LDP_NOTIFICATION,             "Notification Message"},
+  {LDP_HELLO,                    "Hello Message"},
+  {LDP_INITIALIZATION,           "Initialization Message"},
+  {LDP_KEEPALIVE,                "Keep Alive Message"},
+  {LDP_ADDRESS,                  "Address Message"},
+  {LDP_ADDRESS_WITHDRAWAL,       "Address Withdrawal Message"},
+  {LDP_LABEL_MAPPING,            "Label Mapping Message"},
+  {LDP_LABEL_REQUEST,            "Label Request Message"},
+  {LDP_LABEL_WITHDRAWAL,         "Label Withdrawal Message"},
+  {LDP_LABEL_RELEASE,            "Label Release Message"},
+  {LDP_LABEL_ABORT_REQUEST,      "Label Abort Request Message"},
+  {LDP_VENDOR_PRIVATE_START,     "Vendor-Private Message"},
+  {LDP_EXPERIMENTAL_MESSAGE_START,     "Experimental Message"},
   {0, NULL}
 };
 
+static const true_false_string ldp_message_ubit = {
+  "Unknown bit set",
+  "Unknown bit not set"
+};
+
 static const true_false_string hello_targeted_vals = {
   "Targeted Hello",
   "Link Hello"
 };
 
-static const value_string fec_types[] = {
-  {1, "Wildcard FEC"},
-  {2, "Prefix FEC"},
-  {3, "Host Address FEC"},
+static const value_string tlv_unknown_vals[] = {
+  {0, "Known TLV, do not Forward"},
+  {1, "Known TLV, do Forward"},
+  {2, "Unknown TLV, do not Forward"},
+  {3, "Unknown TLV, do Forward"},
   {0, NULL}
 };
 
-static const value_string fec_af_types[] = {
-  {1, "UNIX"},
-  {2, "Internet (IPv4)"},
-  {3, "X25"},
-  {4, "IPX"},
-  {5, "Appletalk"},
-  {10, "IPv6"},
+#define        WILDCARD_FEC    1
+#define        PREFIX_FEC      2
+#define        HOST_FEC        3
+#define CRLSP_FEC       4
+#define VC_FEC          0x80   /* draft-martini-l2circuit-trans-mpls */
+
+static const value_string fec_types[] = {
+  {WILDCARD_FEC, "Wildcard FEC"},
+  {PREFIX_FEC, "Prefix FEC"},
+  {HOST_FEC, "Host Address FEC"},
+  {CRLSP_FEC, "CR LSP FEC"},
+  {VC_FEC, "Virtual Circuit FEC"},
   {0, NULL}
 };
 
-static const true_false_string hello_requested_vals = {
-  "Source requests periodic hellos",
-  "Source does not request periodic hellos"
-};
 
-/* Dissect the common hello params */
+static const value_string fec_vc_types_vals[] = {
+  {0x0001, "Frame Relay DLCI"},
+  {0x0002, "ATM VCC transport"},
+  {0x0003, "ATM VPC transport"},
+  {0x0004, "Ethernet VLAN"},
+  {0x0005, "Ethernet"},
+  {0x0006, "HDLC"},
+  {0x0007, "PPP"},
+  {0x0009, "ATM VCC cell transport"},
+  {0x8008, "CEM"},
+  {0x000A, "ATM VPC cell transport"},
+  {0x000B, "Ethernet VPLS"},
+  {0, NULL}
+};
 
-void dissect_tlv_common_hello_parms(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
-{
-  proto_tree *ti = NULL, *val_tree = NULL;
 
-  if (tree) {
+#define FEC_VC_INTERFACEPARAM_MTU          0x01
+#define FEC_VC_INTERFACEPARAM_MAXCATMCELLS 0x02
+#define FEC_VC_INTERFACEPARAM_DESCRIPTION  0x03
+#define FEC_VC_INTERFACEPARAM_CEMBYTES     0x04
+#define FEC_VC_INTERFACEPARAM_CEMOPTIONS   0x05
+#define FEC_VC_INTERFACEPARAM_VPNID        0x06
 
-    ti = proto_tree_add_bytes(tree, hf_ldp_tlv_value, tvb, offset, rem,
-                             tvb_get_ptr(tvb, offset, rem));
 
-    val_tree = proto_item_add_subtree(ti, ett_ldp_tlv_val);
+static const value_string fec_vc_interfaceparm[] = {
+  {FEC_VC_INTERFACEPARAM_MTU, "MTU"},
+  {FEC_VC_INTERFACEPARAM_MAXCATMCELLS, "Max Concatenated ATM cells"},
+  {FEC_VC_INTERFACEPARAM_DESCRIPTION, "Interface Description"},
+  {FEC_VC_INTERFACEPARAM_CEMBYTES, "CEM Payload Bytes"},
+  {FEC_VC_INTERFACEPARAM_CEMOPTIONS, "CEM options"},
+  {FEC_VC_INTERFACEPARAM_VPNID, "VPN Id"}
+};
 
-    proto_tree_add_item(val_tree, hf_ldp_tlv_val_hold, tvb, offset, 2, FALSE);
+static const true_false_string fec_vc_cbit = {
+  "Contorl Word Present",
+  "Control Word NOT Present"
+};
 
-    proto_tree_add_boolean(val_tree, hf_ldp_tlv_val_target, tvb, offset + 2, 2, FALSE);
-    proto_tree_add_boolean(val_tree, hf_ldp_tlv_val_request, tvb, offset + 2, 2, FALSE);
-    proto_tree_add_item(val_tree, hf_ldp_tlv_val_res, tvb, offset + 2, 2, FALSE);
-  }
 
-}
 
-/* Dissect a TLV and return the number of bytes consumed ... */
+static const value_string tlv_atm_merge_vals[] = {
+  {0, "Merge not supported"},
+  {1, "VP merge supported"},
+  {2, "VC merge supported"},
+  {3, "VP & VC merge supported"},
+  {0, NULL}
+};
 
-int dissect_tlv(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
-{
-  guint16 message = tvb_get_ntohs(tvb, offset),
-          length = tvb_get_ntohs(tvb, offset + 2),
-          pad = 0, fec_len = 0;
-  proto_tree *ti = NULL, *tlv_tree = NULL;
+static const value_string tlv_atm_vbits_vals[] = {
+  {0, "VPI & VCI Significant"},
+  {1, "Only VPI Significant"},
+  {2, "Only VCI Significant"},
+  {3, "VPI & VCI not Significant, nonsense"},
+  {0, NULL}
+};
 
-  /* Hmmm, check for illegal alignment padding */
+static const value_string tlv_fr_merge_vals[] = {
+  {0, "Merge not supported"},
+  {1, "Merge supported"},
+  {2, "Unspecified"},
+  {3, "Unspecified"},
+  {0, NULL}
+};
 
-  if (message == 0x00) {
+static const value_string tlv_fr_len_vals[] = {
+  {0, "10 bits"},
+  {1, "Reserved"},
+  {2, "23 bits"},
+  {3, "Reserved"},
+  {0, NULL}
+};
 
-    proto_tree_add_text(tree, tvb, offset, 2, "Illegal Padding: %04X", message);
-    offset += 2; pad = 2;
-    message = tvb_get_ntohs(tvb, offset);
-    length = tvb_get_ntohs(tvb, offset + 2);
+static const value_string ldp_act_flg_vals[] = {
+  {0, "indicates initial LSP setup"},
+  {1, "indicates modify LSP"},
+  {0, NULL}
+};
 
-  }
+static const value_string route_pinning_vals[] = {
+  {0, "route pinning is not requested"},
+  {1, "route pinning is requested"},
+  {0, NULL}
+};
 
-  length = MIN(length, rem);  /* Don't go haywire if a problem ... */
+static const value_string ldp_loose_vals[] = {
+  {0, "strict hop"},
+  {1, "loose hop"},
+  {0, NULL}
+};
 
-  if (tree) {
+static const true_false_string tlv_negotiable = {
+  "Negotiable",
+  "Not negotiable"
+};
 
-    /* FIXME: Account for vendor and special messages */
+static const value_string freq_values[] = {
+  {0, "Unspecified"},
+  {1, "Frequent"},
+  {2, "VeryFrequent"},
+  {0, NULL}
+};
 
-    ti = proto_tree_add_text(tree, tvb, offset, length + 4, "%s",
-                            val_to_str(message, tlv_type_names, "Unknown TLV type (0x%04X)"));
+static const true_false_string tlv_atm_dirbit = {
+  "Bidirectional capability",
+  "Unidirectional capability"
+};
 
-    tlv_tree = proto_item_add_subtree(ti, ett_ldp_tlv);
+static const true_false_string hello_requested_vals = {
+  "Source requests periodic hellos",
+  "Source does not request periodic hellos"
+};
 
-    proto_tree_add_item(tlv_tree, hf_ldp_tlv_type, tvb, offset, 2, FALSE);
+static const true_false_string tlv_sess_advbit_vals = {
+  "Downstream On Demand proposed",
+  "Downstream Unsolicited proposed"
+};
 
-    proto_tree_add_item(tlv_tree, hf_ldp_tlv_len, tvb, offset + 2, 2, FALSE);
+static const true_false_string tlv_sess_ldetbit_vals = {
+  "Loop Detection Enabled",
+  "Loop Detection Disabled"
+};
 
-    switch (message) {
+static const true_false_string tlv_status_ebit = {
+  "Fatal Error Notification",
+  "Advisory Notification"
+};
 
-    case TLV_FEC:  /* Process an FEC */
+static const true_false_string tlv_status_fbit = {
+  "Notification should be Forwarded",
+  "Notification should NOT be Forwarded"
+};
 
-      offset += 4;  /* Skip the TLV header */
+static const value_string tlv_status_data[] = {
+  {0, "Success"},
+  {1, "Bad LDP Identifier"},
+  {2, "Bad Protocol Version"},
+  {3, "Bad PDU Length"},
+  {4, "Unknown Message Type"},
+  {5, "Bad Message Length"},
+  {6, "Unknown TLV"},
+  {7, "Bad TLV Length"},
+  {8, "Malformed TLV Value"},
+  {9, "Hold Timer Expired"},
+  {10, "Shutdown"},
+  {11, "Loop Detected"},
+  {12, "Unknown FEC"},
+  {13, "No Route"},
+  {14, "No Label Resources"},
+  {15, "Label Resources / Available"},
+  {16, "Session Rejected / No Hello"},
+  {17, "Session Rejected / Parameters Advertisement Mode"},
+  {18, "Session Rejected / Parameters Max PDU Length"},
+  {19, "Session Rejected / Parameters Label Range"},
+  {20, "KeepAlive Timer Expired"},
+  {21, "Label Request Aborted"},
+  {22, "Missing Message Parameters"},
+  {23, "Unsoported Address Family"},
+  {24, "Session Rejected / Bad KeepAlive Time"},
+  {25, "Internal Error"},
+  {0x01000001,"Unexpected Diff-Serv TLV"},
+  {0x01000002,"Unsupported PHB"},
+  {0x01000003,"Invalid EXP<->PHB Mapping"},
+  {0x01000004,"Unsupported PSC"},
+  {0x01000005,"Per-LSP context allocation failure"},
+  {0x04000001,"Bad Explicit Routing TLV Error"},
+  {0x04000002,"Bad Strict Node Error"},
+  {0x04000003,"Bad Strict Node Error"},
+  {0x04000004,"Bad Initial ER-Hop Error"},
+  {0x04000005,"Resource Unavailable"},
+  {0x04000006,"Traffic Parameters Unavailable"},
+  {0x04000007,"LSP Preempted"},
+  {0x04000008,"Modify Request Not Supported"},
+  {0x20000001,"Illegal C-Bit"},
+  {0x20000002,"Wrong C-Bit"},
+  {0, NULL}
+};
 
-      fec_len = length;
+/* Dissect FEC TLV */
 
-      while (fec_len > 0) {
-       proto_tree *fec_tree = NULL;
+static void
+dissect_tlv_fec(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti=NULL, *val_tree=NULL, *fec_tree=NULL, *vcintparam_tree=NULL;
+       guint16 family, ix=1, ax;
+       guint8  addr_size=0, *addr, implemented, prefix_len_octets, prefix_len, host_len, vc_len;
+       guint8  intparam_len;
+       void *str_handler=NULL;
+       char *str;
+
+       if (tree) {
+               ti=proto_tree_add_text(tree, tvb, offset, rem, "FEC Elements");
+               val_tree=proto_item_add_subtree(ti, ett_ldp_tlv_val);
+               if(val_tree == NULL) return;
+
+               while (rem > 0){
+                       switch (tvb_get_guint8(tvb, offset)) {
+                       case WILDCARD_FEC:
+                       case CRLSP_FEC:
+                               ti = proto_tree_add_text(val_tree, tvb, offset, 1, "FEC Element %u", ix);
+                               fec_tree = proto_item_add_subtree(ti, ett_ldp_fec);
+                               if(fec_tree == NULL) return;
+                               proto_tree_add_item(fec_tree, hf_ldp_tlv_fec_wc,tvb, offset, 1, FALSE);
+                               rem -= 1;
+                               offset += 1;
+                               break;
+
+                       case PREFIX_FEC:
+                               if( rem < 4 ){/*not enough*/
+                                       proto_tree_add_text(val_tree, tvb, offset, rem, "Error in FEC Element %u", ix);
+                                       return;
+                               }
+                               family=tvb_get_ntohs(tvb, offset+1);
+                               prefix_len=tvb_get_guint8(tvb, offset+3);
+                               prefix_len_octets=(prefix_len+7)/8;
+
+                               implemented=1;
+                               switch(family) {
+                                       case AFNUM_INET: /*IPv4*/
+                                               addr_size=4;
+                                               str_handler=ip_to_str;
+                                               break;
+                                       case AFNUM_INET6: /*IPv6*/
+                                               addr_size=16;
+                                               str_handler=ip6_to_str;
+                                               break;
+                                       default:
+                                               implemented=0;
+                                               break;
+                               }
+
+                               if( !implemented ) {
+                                       guint16 noctets;
+
+                                       noctets= rem>4+prefix_len_octets?4+prefix_len_octets:rem;
+                                       proto_tree_add_text(val_tree, tvb, offset, noctets,"Support for Address Family not implemented");
+                                       offset+=noctets;
+                                       rem-=noctets;
+                                       break;
+                               }
+
+                               if( rem < 4+MIN(addr_size, prefix_len_octets) ){
+                                       proto_tree_add_text(val_tree, tvb, offset, rem, "Error in FEC Element %u", ix);
+                                       return;
+                               }
+
+                               /*Add a subtree for this*/
+                               ti = proto_tree_add_text(val_tree, tvb, offset, 4+MIN(addr_size, prefix_len_octets), "FEC Element %u", ix);
+                               fec_tree = proto_item_add_subtree(ti, ett_ldp_fec);
+                               if(fec_tree == NULL) return;
+                               proto_tree_add_item(fec_tree, hf_ldp_tlv_fec_wc, tvb, offset, 1, FALSE);
+                               offset += 1;
+
+                               proto_tree_add_item(fec_tree, hf_ldp_tlv_fec_af, tvb, offset, 2, FALSE);
+                               offset += 2;
+
+                               proto_tree_add_item(fec_tree, hf_ldp_tlv_fec_len, tvb, offset, 1, FALSE);
+                               offset += 1;
+
+
+                               if( addr_size < prefix_len_octets) {
+                                       offset+=addr_size;
+                                       rem-=addr_size;
+                                       proto_tree_add_text(fec_tree, tvb, offset-1, 1, "Invalid prefix %u length for family %s", prefix_len, val_to_str(family, afn_vals, "Unknown Family"));
+                                       break;
+                               }
+
+                               if( (addr=g_malloc0(addr_size)) == NULL ){
+                                       /*big big trouble, no mem or bad addr_size*/
+                                       fprintf(stderr, "packet-ldp: dissect_tlv_fec() malloc failed\n");
+                                       return;
+                               }
+
+                               for(ax=0; ax+1 <= prefix_len_octets; ax++)
+                                       addr[ax]=tvb_get_guint8(tvb, offset+ax);
+                               if( prefix_len % 8 )
+                                       addr[ax-1] = addr[ax-1]&(0xFF<<(8-prefix_len%8));
+
+                               str = (* (char* (*)(guint8 *))str_handler)(addr);
+                               proto_tree_add_string_format(fec_tree, hf_ldp_tlv_fec_pfval, tvb, offset, prefix_len_octets, str, "Prefix: %s", str);
+
+                               offset += prefix_len_octets;
+                               rem -= 4+prefix_len_octets;
+                               g_free(addr);
+                               break;
+
+                       case HOST_FEC:
+                               if( rem < 4 ){/*not enough*/
+                                       proto_tree_add_text(val_tree, tvb, offset, rem, "Error in FEC Element %u", ix);
+                                       return;
+                               }
+                               family=tvb_get_ntohs(tvb, offset+1);
+                               host_len=tvb_get_guint8(tvb, offset+3);
+
+                               implemented=1;
+                               switch(family) {
+                                       case AFNUM_INET: /*IPv4*/
+                                               addr_size=4;
+                                               str_handler=ip_to_str;
+                                               break;
+                                       case AFNUM_INET6: /*IPv6*/
+                                               addr_size=16;
+                                               str_handler=ip6_to_str;
+                                               break;
+                                       default:
+                                               implemented=0;
+                                               break;
+                               }
+
+                               if( !implemented ) {
+                                       guint16 noctets;
+
+                                       noctets= rem>4+host_len?4+host_len:rem;
+                                       proto_tree_add_text(val_tree, tvb, offset, noctets,"Support for Address Family not implemented");
+                                       offset+=noctets;
+                                       rem-=noctets;
+                                       break;
+                               }
+
+                               if( rem < 4+addr_size ){
+                                       proto_tree_add_text(val_tree, tvb, offset, rem, "Error in FEC Element %u", ix);
+                                       return;
+                               }
+
+                               /*Add a subtree for this*/
+                               ti = proto_tree_add_text(val_tree, tvb, offset, 4+addr_size, "FEC Element %u", ix);
+                               fec_tree = proto_item_add_subtree(ti, ett_ldp_fec);
+                               if(fec_tree == NULL) return;
+                               proto_tree_add_item(fec_tree, hf_ldp_tlv_fec_wc, tvb, offset, 1, FALSE);
+                               offset += 1;
+
+                               proto_tree_add_item(fec_tree, hf_ldp_tlv_fec_af, tvb, offset, 2, FALSE);
+                               offset += 2;
+
+                               proto_tree_add_item(fec_tree, hf_ldp_tlv_fec_len, tvb, offset, 1, FALSE);
+                               offset += 1;
+
+
+                               if( addr_size != host_len) {
+                                       offset+=addr_size;
+                                       rem-=addr_size;
+                                       proto_tree_add_text(fec_tree, tvb, offset-1, 1, "Invalid address length %u length for family %s", host_len, val_to_str(family, afn_vals, "Unknown Family"));
+                                       break;
+                               }
+
+                               if( (addr=g_malloc0(addr_size)) == NULL ){
+                                       /*big big xtrouble, no mem or bad addr_size*/
+                                       fprintf(stderr, "packet-ldp: dissect_tlv_fec() malloc failed\n");
+                                       return;
+                               }
+
+                               for(ax=0; ax+1 <= host_len; ax++)
+                                       addr[ax]=tvb_get_guint8(tvb, offset+ax);
+
+                               str = (* (char* (*)(guint8 *))str_handler)(addr);
+                               proto_tree_add_string_format(fec_tree, hf_ldp_tlv_fec_hoval, tvb, offset, host_len, str, "Address: %s", str);
+
+                               offset += host_len;
+                               rem -= 4+host_len;
+                               g_free(addr);
+                               break;
+
+                       case VC_FEC:
+                         if( rem < 8 ){/*not enough bytes for a minimal VC_FEC*/
+                           proto_tree_add_text(val_tree, tvb, offset, rem, "Error in FEC Element %u", ix);
+                           return;
+                         }
+                         vc_len = tvb_get_guint8 (tvb, offset+3);
+
+
+                         ti = proto_tree_add_text(val_tree, tvb, offset, 8+vc_len, "FEC Element %u", ix);
+                         fec_tree = proto_item_add_subtree(ti, ett_ldp_fec);
+                         if(fec_tree == NULL) return;
+                         proto_tree_add_item(fec_tree, hf_ldp_tlv_fec_wc, tvb, offset, 1, FALSE);
+                         proto_tree_add_item(fec_tree, hf_ldp_tlv_fec_vc_controlword, tvb, offset+1, 1, FALSE);
+                         proto_tree_add_item(fec_tree, hf_ldp_tlv_fec_vc_vctype, tvb, offset+1, 2, FALSE);
+                         proto_tree_add_item(fec_tree, hf_ldp_tlv_fec_vc_infolength, tvb, offset+3,1,FALSE);
+                         proto_tree_add_item(fec_tree, hf_ldp_tlv_fec_vc_groupid,tvb, offset +4, 4, FALSE);
+                         rem -=8;
+                         offset +=8;
+
+                         if ( (vc_len > 3) && ( rem > 3 ) ) { /* there is enough room for vcid */
+                           proto_tree_add_item(fec_tree, hf_ldp_tlv_fec_vc_vcid,tvb, offset, 4, FALSE);
+                           proto_item_append_text (ti," VCID: %u",tvb_get_ntohl(tvb,offset));
+
+                         } else {
+                           proto_tree_add_text(val_tree,tvb,offset +4, 8 +vc_len, "VC FEC size format error");
+                           return;
+                         }
+                         rem -= 4;
+                         vc_len -= 4;
+                         offset += 4;
+
+                         while ( (vc_len > 1) && (rem > 1) ) { /* enough to include id and length */
+                           intparam_len = tvb_get_guint8(tvb, offset+1);
+                           ti = proto_tree_add_text(fec_tree, tvb, offset, intparam_len, "Interface Paramameter");
+                           vcintparam_tree = proto_item_add_subtree(ti, ett_ldp_fec_vc_interfaceparam);
+                           if(vcintparam_tree == NULL) return;
+                           proto_tree_add_item(vcintparam_tree,hf_ldp_tlv_fec_vc_intparam_id,tvb,offset,1,FALSE);
+                           proto_tree_add_item(vcintparam_tree,hf_ldp_tlv_fec_vc_intparam_length,tvb, offset+1, 1, FALSE);
+                           if ( (vc_len -intparam_len) <0 && (rem -intparam_len) <0 ) { /* error condition */
+                             proto_tree_add_text(vcintparam_tree, tvb, offset +2, MIN(vc_len,rem), "malformed data");
+                             return;
+                           }
+                           switch (tvb_get_guint8(tvb, offset)) {
+                           case FEC_VC_INTERFACEPARAM_MTU:
+                             proto_item_append_text(ti,": MTU %u", tvb_get_ntohs(tvb,offset+2));
+                             proto_tree_add_item(vcintparam_tree,hf_ldp_tlv_fec_vc_intparam_mtu,tvb, offset+2, 2, FALSE);
+                             break;
+                           case FEC_VC_INTERFACEPARAM_MAXCATMCELLS:
+                             proto_item_append_text(ti,": Max ATM Concat Cells %u", tvb_get_ntohs(tvb,offset+2));
+                             proto_tree_add_item(vcintparam_tree,hf_ldp_tlv_fec_vc_intparam_maxcatmcells,tvb, offset+2, 2, FALSE);
+                             break;
+                           case FEC_VC_INTERFACEPARAM_DESCRIPTION:
+                             proto_item_append_text(ti,": Description");
+                             proto_tree_add_item(vcintparam_tree,hf_ldp_tlv_fec_vc_intparam_desc,tvb, offset+2, (intparam_len -2), FALSE);
+                             break;
+                           case FEC_VC_INTERFACEPARAM_CEMBYTES:
+                             proto_item_append_text(ti,": CEM Payload Bytes %u", tvb_get_ntohs(tvb,offset+2));
+                             proto_tree_add_item(vcintparam_tree,hf_ldp_tlv_fec_vc_intparam_cembytes,tvb, offset+2, 2, FALSE);
+                             break;
+                           case FEC_VC_INTERFACEPARAM_VPNID:
+                             /* draft-lasserre-tls-mpls-00.txt */
+                             proto_item_append_text(ti,": VPN Id");
+                             proto_tree_add_item(vcintparam_tree,hf_ldp_tlv_fec_vc_intparam_vpnid_oui, tvb, offset+2, 3, FALSE);
+                             proto_tree_add_item(vcintparam_tree,hf_ldp_tlv_fec_vc_intparam_vpnid_index, tvb, offset+5, 4, FALSE);
+                             break;
+                           case FEC_VC_INTERFACEPARAM_CEMOPTIONS:
+                               /* draft-malis-sonet-ces-mpls CEM options still undefined */
+                           default: /* unknown */
+                             proto_item_append_text(ti," unknown");
+                             proto_tree_add_text(vcintparam_tree,tvb, offset+2, (intparam_len -2), "Unknown data");
+
+                             return;
+                           }
+                           rem -= intparam_len;
+                           vc_len -= intparam_len;
+                           offset += intparam_len;
+                         }
+                         break;
+
+                       default:  /* Unknown */
+                       /* XXX - do all FEC's have a length that's a multiple of 4? */
+                       /* Hmmm, don't think so. Will check. RJS. */
+                       /* If we don't know its structure, we have to exit */
+                               ti = proto_tree_add_text(val_tree, tvb, offset, 4, "FEC Element %u", ix);
+                               fec_tree = proto_item_add_subtree(ti, ett_ldp_fec);
+                               if(fec_tree == NULL) return;
+                               proto_tree_add_text(fec_tree, tvb, offset, rem, "Unknown FEC TLV type");
+                               return;
+                       }
+                       ix++;
+               }
+       }
+}
 
+/* Dissect Address List TLV */
 
-       switch (tvb_get_guint8(tvb, offset)) {
-       case 1:   /* Wild Card */
+static void
+dissect_tlv_address_list(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL;
+       guint16 family, ix;
+       guint8  addr_size, *addr;
+       void *str_handler;
+       char *str;
+
+       if (tree) {
+               if( rem < 2 ) {
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing Address List TLV: length is %d, should be >= 2",
+                           rem);
+                       return;
+               }
+
+               family=tvb_get_ntohs(tvb, offset);
+               proto_tree_add_item(tree, hf_ldp_tlv_addrl_addr_family, tvb,
+                                        offset, 2, FALSE);
+               switch(family) {
+                       case AFNUM_INET: /*IPv4*/
+                               addr_size=4;
+                               str_handler=ip_to_str;
+                               break;
+                       case AFNUM_INET6: /*IPv6*/
+                               addr_size=16;
+                               str_handler=ip6_to_str;
+                               break;
+                       default:
+                               proto_tree_add_text(tree, tvb, offset+2, rem-2,
+                                "Support for Address Family not implemented");
+                               return;
+               }
+
+               offset+=2; rem-=2;
+               ti=proto_tree_add_text(tree, tvb, offset, rem, "Addresses");
+               val_tree=proto_item_add_subtree(ti, ett_ldp_tlv_val);
+
+               if(val_tree == NULL) return;
+               if( (addr=g_malloc(addr_size)) == NULL ){
+                       /*big big trouble*/
+                       fprintf(stderr, "packet-ldp: dissect_tlv_address_list() malloc failed\n");
+                       return;
+               }
+
+               for(ix=1; rem >= addr_size; ix++, offset += addr_size,
+                                                        rem -= addr_size) {
+                       if( (tvb_memcpy(tvb, addr, offset, addr_size))
+                                                        == NULL)
+                               break;
+
+                       str = (* (char* (*)(guint8 *))str_handler)(addr);
+                       proto_tree_add_string_format(val_tree,
+                           hf_ldp_tlv_addrl_addr, tvb, offset, addr_size, str,
+                           "Address %u: %s", ix, str);
+               }
+               if(rem)
+                       proto_tree_add_text(val_tree, tvb, offset, rem,
+                           "Error processing TLV: Extra data at end of address list");
+               g_free(addr);
+       }
+}
 
-         proto_tree_add_item(tlv_tree, hf_ldp_tlv_fec_wc, tvb, offset, 4, FALSE);
-         fec_len -= 4;
+/* Dissect Path Vector TLV */
 
-         offset += 4;
+static void
+dissect_tlv_path_vector(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL;
+       guint8  ix;
+       guint32 addr;
+
+       if (tree) {
+               ti=proto_tree_add_text(tree, tvb, offset, rem, "LSR IDs");
+               val_tree=proto_item_add_subtree(ti, ett_ldp_tlv_val);
+
+               if(val_tree == NULL) return;
+
+               for(ix=1; rem >= 4; ix++, offset += 4, rem -= 4) {
+                       tvb_memcpy(tvb, (guint8 *)&addr, offset, 4);
+                       proto_tree_add_ipv4_format(val_tree,
+                           hf_ldp_tlv_pv_lsrid, tvb, offset, 4,
+                           addr, "LSR Id %u: %s", ix,
+                           ip_to_str((guint8 *)&addr));
+               }
+               if(rem)
+                       proto_tree_add_text(val_tree, tvb, offset, rem,
+                           "Error processing TLV: Extra data at end of path vector");
+       }
+}
 
-         break;
+/* Dissect ATM Label TLV */
 
-       case 2:   /* Prefix    */
+static void
+dissect_tlv_atm_label(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL;
+       guint16 id;
+
+       if(tree) {
+               if(rem != 4){
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing ATM Label TLV: length is %d, should be 4",
+                           rem);
+                       return;
+               }
+               ti=proto_tree_add_text(tree, tvb, offset, rem, "ATM Label");
+               val_tree=proto_item_add_subtree(ti, ett_ldp_tlv_val);
+               if(val_tree == NULL) return;
+
+               proto_tree_add_item(val_tree, hf_ldp_tlv_atm_label_vbits, tvb, offset, 1, FALSE);
+
+               id=tvb_get_ntohs(tvb, offset)&0x0FFF;
+               proto_tree_add_uint_format(val_tree, hf_ldp_tlv_atm_label_vpi, tvb, offset, 2, id, "VPI: %u", id);
+
+               id=tvb_get_ntohs(tvb, offset+2);
+               proto_tree_add_uint_format(val_tree, hf_ldp_tlv_atm_label_vci, tvb, offset+2, 2, id, "VCI: %u", id);
+       }
+}
 
-         /* Add a subtree for this ... */
+/* Dissect FRAME RELAY Label TLV */
 
-         ti = proto_tree_add_text(tlv_tree, tvb, offset, 8, "Prefix FEC Element");
+static void
+dissect_tlv_frame_label(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL;
+       guint8  len;
+       guint32 id;
+
+       if(tree) {
+               if(rem != 4){
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing Frame Relay Label TLV: length is %d, should be 4",
+                           rem);
+                       return;
+               }
+               ti=proto_tree_add_text(tree, tvb, offset, rem, "Frame Relay Label");
+               val_tree=proto_item_add_subtree(ti, ett_ldp_tlv_val);
+               if(val_tree == NULL) return;
+
+               len=(guint8)(tvb_get_ntohs(tvb, offset)>>7) & 0x03;
+               proto_tree_add_uint_format(val_tree, hf_ldp_tlv_fr_label_len, tvb, offset, 2, len, "Number of DLCI bits: %s (%u)", val_to_str(len, tlv_fr_len_vals, "Unknown Length"), len);
+
+               id=tvb_get_ntoh24(tvb, offset+1)&0x7FFFFF;
+               proto_tree_add_uint_format(val_tree,
+               hf_ldp_tlv_fr_label_dlci, tvb, offset+1, 3, id, "DLCI: %u", id);
+       }
+}
 
-         fec_tree = proto_item_add_subtree(ti, ett_ldp_fec);
+/* Dissect STATUS TLV */
 
-         proto_tree_add_item(fec_tree, hf_ldp_tlv_fec_wc, tvb, offset, 1, FALSE);
+static void
+dissect_tlv_status(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL;
+       guint32 data;
 
-         offset += 1;
+       if(tree) {
+               if(rem != 10){
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing Status TLV: length is %d, should be 10",
+                           rem);
+                       return;
+               }
 
-         /* XXX - the address family length should be extracted and used to
-            dissect the prefix field. */
-         proto_tree_add_item(fec_tree, hf_ldp_tlv_fec_af, tvb, offset, 2, FALSE);
-         offset += 2;
+               ti=proto_tree_add_text(tree, tvb, offset, rem, "Status");
+               val_tree=proto_item_add_subtree(ti, ett_ldp_tlv_val);
+               if(val_tree == NULL) return;
 
+               proto_tree_add_item(val_tree, hf_ldp_tlv_status_ebit, tvb, offset, 1, FALSE);
+               proto_tree_add_item(val_tree, hf_ldp_tlv_status_fbit, tvb, offset, 1, FALSE);
 
-         proto_tree_add_item(fec_tree, hf_ldp_tlv_fec_len, tvb, offset, 1, FALSE);
+               data=tvb_get_ntohl(tvb, offset)&0x3FFFFFFF;
+               proto_tree_add_uint_format(val_tree, hf_ldp_tlv_status_data, tvb, offset, 4, data, "Status Data: %s (0x%X)", val_to_str(data, tlv_status_data, "Unknown Status Data"), data);
 
-         offset += 1;
+               proto_tree_add_item(val_tree, hf_ldp_tlv_status_msg_id, tvb, offset+4, 4, FALSE);
+               proto_tree_add_item(val_tree, hf_ldp_tlv_status_msg_type, tvb, offset+8, 2, FALSE);
+       }
+}
 
-         proto_tree_add_item(fec_tree, hf_ldp_tlv_fec_pfval, tvb, offset, 4, FALSE);
+/* Dissect Returned PDU TLV */
 
-         fec_len -= 8;
+static void
+dissect_tlv_returned_pdu(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL;
+
+       if(tree) {
+               if(rem < 10){
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing Returned PDU TLV: length is %d, should be >= 10",
+                           rem);
+                       return;
+               }
+               ti=proto_tree_add_text(tree, tvb, offset, rem, "Returned PDU");
+               val_tree=proto_item_add_subtree(ti, ett_ldp_tlv_val);
+               if(val_tree == NULL) return;
+
+               proto_tree_add_item(val_tree, hf_ldp_tlv_returned_version, tvb, offset, 2, FALSE);
+               proto_tree_add_item(val_tree, hf_ldp_tlv_returned_pdu_len, tvb, offset+2, 2, FALSE);
+               proto_tree_add_item(val_tree, hf_ldp_tlv_returned_lsr, tvb, offset+4, 4, FALSE);
+               proto_tree_add_item(val_tree, hf_ldp_tlv_returned_ls_id, tvb, offset+8, 2, FALSE);
+               offset += 10;
+               rem -= 10;
+
+               if( rem > 0 ) {
+               /*XXX - dissect returned pdu data*/
+                       proto_tree_add_text(val_tree, tvb, offset, rem, "Returned PDU Data");
+               }
+       }
+}
 
-         break;
+/* Dissect Returned MESSAGE TLV */
 
-       case 3:   /* Host address */
+static void
+dissect_tlv_returned_message(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL;
+       guint16 type;
+
+       if(tree) {
+               if(rem < 4){
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing Returned Message TLV: length is %d, should be >= 4",
+                           rem);
+                       return;
+               }
+               ti=proto_tree_add_text(tree, tvb, offset, rem, "Returned Message");
+               val_tree=proto_item_add_subtree(ti, ett_ldp_tlv_val);
+               if(val_tree == NULL) return;
+
+               proto_tree_add_item(val_tree, hf_ldp_tlv_returned_msg_ubit, tvb, offset, 1, FALSE);
+
+               type=tvb_get_ntohs(tvb, offset)&0x7FFF;
+               proto_tree_add_uint_format(val_tree, hf_ldp_tlv_returned_msg_type, tvb, offset, 2, type, "Message Type: %s (0x%X)", val_to_str(type, ldp_message_types,"Unknown Message Type"), type);
+
+               proto_tree_add_item(val_tree, hf_ldp_tlv_returned_msg_len, tvb, offset+2, 2, FALSE);
+               offset += 4;
+               rem -= 4;
+
+               if( rem >= 4  ) { /*have msg_id*/
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_returned_msg_id, tvb, offset, 4, FALSE);
+                       offset += 4;
+                       rem -= 4;
+               }
+
+               if( rem > 0 ) {
+               /*XXX - dissect returned msg parameters*/
+                       proto_tree_add_text(val_tree, tvb, offset, rem, "Returned Message Parameters");
+               }
+       }
+}
 
-         /* XXX - write me. */
+/* Dissect the common hello params */
 
-         fec_len -= 8;
+static void
+#if 0
+dissect_tlv_common_hello_parms(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+#else
+dissect_tlv_common_hello_parms(tvbuff_t *tvb, guint offset, proto_tree *tree)
+#endif
+{
+#if 0
+       proto_tree *ti = NULL;
+#endif
+       proto_tree *val_tree = NULL;
+
+       if (tree) {
+#if 0
+               ti = proto_tree_add_item(tree, hf_ldp_tlv_value, tvb, offset, rem, FALSE);
+               val_tree = proto_item_add_subtree(ti, ett_ldp_tlv_val);
+               if(val_tree == NULL) return;
+#else
+               val_tree=tree;
+#endif
+               proto_tree_add_item(val_tree, hf_ldp_tlv_val_hold, tvb, offset, 2, FALSE);
+               proto_tree_add_item(val_tree, hf_ldp_tlv_val_target, tvb, offset + 2, 2, FALSE);
+               proto_tree_add_item(val_tree, hf_ldp_tlv_val_request, tvb, offset + 2, 2, FALSE);
+               proto_tree_add_item(val_tree, hf_ldp_tlv_val_res, tvb, offset + 2, 2, FALSE);
+       }
+}
 
-         offset += 8;
+/* Dissect MAC TLV */
 
-         break;
+static void
+dissect_tlv_mac(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL;
+       guint8  ix;
+       const guint8 *mac;
+
+       if (tree) {
+               ti=proto_tree_add_text(tree, tvb, offset, rem, "MAC addresses");
+               val_tree=proto_item_add_subtree(ti, ett_ldp_tlv_val);
+
+               if(val_tree == NULL) return;
+
+               for(ix=1; rem >= 6; ix++, offset += 6, rem -= 6) {
+                       mac = tvb_get_ptr(tvb, offset, 6);
+                       proto_tree_add_ether(val_tree,
+                            hf_ldp_tlv_mac, tvb, offset, 6, mac);
+               }
+               if(rem)
+                       proto_tree_add_text(val_tree, tvb, offset, rem,
+                           "Error processing TLV: Extra data at end of path vector");
+       }
+}
 
-       default:  /* Unknown */
 
-          /* XXX - do all FEC's have a length that's a multiple of 4? */
 
-         fec_len -= 4;
+/* Dissect the common session params */
 
-         offset += 4;
+static void
+dissect_tlv_common_session_parms(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL;
 
-         break;
+       if (tree != NULL) {
+               if( rem != 14) { /*length of Comm Sess Parms tlv*/
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing Common Session Parameters TLV: length is %d, should be 14",
+                           rem);
+                       return;
+               }
+               ti = proto_tree_add_text(tree, tvb, offset, rem, "Parameters");
+               val_tree = proto_item_add_subtree(ti, ett_ldp_tlv_val);
 
-       }
+               if(val_tree != NULL) {
+                       /*Protocol Version*/
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_sess_ver, tvb,offset, 2, FALSE);
 
-      }
+                       /*KeepAlive Time*/
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_sess_ka, tvb,offset + 2, 2, FALSE);
 
-      break;;
+                       /*A bit*/
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_sess_advbit,tvb, offset + 4, 1, FALSE);
 
-    case TLV_COMMON_HELLO_PARMS:
+                       /*D bit*/
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_sess_ldetbit,tvb, offset + 4, 1, FALSE);
 
-      dissect_tlv_common_hello_parms(tvb, offset + 4, tlv_tree, length);
-      break;
+                       /*Path Vector Limit*/
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_sess_pvlim,tvb, offset + 5, 1, FALSE);
 
-    case TLV_CONFIGURATION_SEQNO:
+                       /*Max PDU Length*/
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_sess_mxpdu,tvb, offset + 6, 2, FALSE);
 
-      proto_tree_add_item(tlv_tree, hf_ldp_tlv_config_seqno, tvb, offset + 4, 4, FALSE);
-      break;
+                       /*Rx LSR*/
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_sess_rxlsr,tvb, offset + 8, 4, FALSE);
 
-    default:
-      proto_tree_add_bytes(tlv_tree, hf_ldp_tlv_value, tvb, offset + 4, 
-                          length, tvb_get_ptr(tvb, offset + 4, length));
+                       /*Rx LS*/
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_sess_rxls,tvb, offset + 12, 2, FALSE);
+               }
+       }
+}
 
-      break;
-    }
+/* Dissect the atm session params */
 
-  }
+static void
+dissect_tlv_atm_session_parms(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL, *lbl_tree = NULL;
+       guint8 numlr, ix;
+       guint16 id;
+
+       if (tree != NULL) {
+               if(rem < 4) {
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing ATM Parameters TLV: length is %d, should be >= 4",
+                           rem);
+                       return;
+               }
+
+               ti = proto_tree_add_text(tree, tvb, offset, rem,"ATM Parameters");
+               val_tree = proto_item_add_subtree(ti, ett_ldp_tlv_val);
+
+               if(val_tree != NULL) {
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_sess_atm_merge,tvb, offset, 1, FALSE);
+
+                       /*get the number of label ranges*/
+                       numlr=(tvb_get_guint8(tvb, offset)>>2) & 0x0F;
+                       proto_tree_add_uint_format(val_tree, hf_ldp_tlv_sess_atm_lr,
+                       tvb, offset, 1, numlr, "Number of Label Range components: %u",
+                       numlr);
+
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_sess_atm_dir,tvb, offset, 1, FALSE);
+
+                       /*move into range components*/
+                       offset += 4;
+                       rem -= 4;
+                       ti = proto_tree_add_text(val_tree, tvb, offset, rem,"ATM Label Range Components");
+
+                       if(numlr) {
+                               val_tree=proto_item_add_subtree(ti,ett_ldp_tlv_val);
+                               if( ! val_tree ) return;
+                       }
+                       /*now dissect ranges*/
+                       for(ix=1; numlr > 0 && rem >= 8; ix++, rem-=8, numlr--) {
+                               ti=proto_tree_add_text(val_tree, tvb, offset, 8,
+                                "ATM Label Range Component %u", ix);
+                               lbl_tree=proto_item_add_subtree(ti, ett_ldp_tlv_val);
+
+                               if( lbl_tree == NULL ) break;
+
+                               id=tvb_get_ntohs(tvb, offset)&0x0FFF;
+                               proto_tree_add_uint_format(lbl_tree,
+                                   hf_ldp_tlv_sess_atm_minvpi,
+                                   tvb, offset, 2,
+                                   id, "Minimum VPI: %u", id);
+                               id=tvb_get_ntohs(tvb, offset+4)&0x0FFF;
+                               proto_tree_add_uint_format(lbl_tree,
+                                   hf_ldp_tlv_sess_atm_maxvpi,
+                                   tvb, (offset+4), 2, id,
+                                   "Maximum VPI: %u", id);
+
+                               id=tvb_get_ntohs(tvb, offset+2);
+                               proto_tree_add_uint_format(lbl_tree,
+                                   hf_ldp_tlv_sess_atm_minvci,
+                                   tvb, offset+2, 2,
+                                   id, "Minimum VCI: %u", id);
+                               id=tvb_get_ntohs(tvb, offset+6);
+                               proto_tree_add_uint_format(lbl_tree,
+                                   hf_ldp_tlv_sess_atm_maxvci,
+                                   tvb, offset+6, 2,
+                                   id, "Maximum VCI: %u", id);
+
+                               offset += 8;
+                       }
+                       if( rem || numlr)
+                               proto_tree_add_text(val_tree, tvb, offset, rem,
+                                   "Error processing TLV: Extra data at end of TLV");
+               }
+       }
+}
 
-  return length + pad + 4;  /* Length of the value field + header */
+/* Dissect the frame relay session params */
 
+static void
+dissect_tlv_frame_relay_session_parms(tvbuff_t *tvb, guint offset,proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL, *lbl_tree = NULL;
+       guint8 numlr, ix, len;
+       guint32 id;
+
+       if (tree != NULL) {
+               if(rem < 4) {
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing Frame Relay Parameters TLV: length is %d, should be >= 4",
+                           rem);
+                       return;
+               }
+
+               ti = proto_tree_add_text(tree, tvb, offset, rem,
+                                                "Frame Relay Parameters");
+               val_tree = proto_item_add_subtree(ti, ett_ldp_tlv_val);
+
+               if(val_tree != NULL) {
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_sess_fr_merge,
+                               tvb, offset, 1, FALSE);
+
+                       /*get the number of label ranges*/
+                       numlr=(tvb_get_guint8(tvb, offset)>>2) & 0x0F;
+                       proto_tree_add_uint_format(val_tree, hf_ldp_tlv_sess_fr_lr,
+                       tvb, offset, 1, numlr, "Number of Label Range components: %u",
+                       numlr);
+
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_sess_fr_dir,
+                                tvb, offset, 1, FALSE);
+
+                       /*move into range components*/
+                       offset += 4;
+                       rem -= 4;
+                       ti = proto_tree_add_text(val_tree, tvb, offset, rem,
+                                "Frame Relay Label Range Components");
+
+                       if(numlr) {
+                               val_tree=proto_item_add_subtree(ti,
+                                                        ett_ldp_tlv_val);
+                               if( ! val_tree ) return;
+                       }
+
+                       /*now dissect ranges*/
+                       for(ix=1; numlr > 0 && rem >= 8; ix++, rem-=8, numlr--) {
+                               ti=proto_tree_add_text(val_tree, tvb, offset, 8,
+                               "Frame Relay Label Range Component %u", ix);
+                               lbl_tree=proto_item_add_subtree(ti, ett_ldp_tlv_val);
+
+                               if( lbl_tree == NULL ) break;
+
+                               len=(guint8)(tvb_get_ntohs(tvb, offset)>>7) & 0x03;
+                               proto_tree_add_uint_format(lbl_tree, hf_ldp_tlv_sess_fr_len, tvb, offset, 2, len, "Number of DLCI bits: %s (%u)", val_to_str(len, tlv_fr_len_vals, "Unknown Length"), len);
+
+                               id=tvb_get_ntoh24(tvb, offset+1)&0x7FFFFF;
+                               proto_tree_add_uint_format(lbl_tree,
+                       hf_ldp_tlv_sess_fr_mindlci, tvb, offset+1, 3, id, "Minimum DLCI %u", id);
+                               id=tvb_get_ntoh24(tvb, offset+5)&0x7FFFFF;
+                               proto_tree_add_uint_format(lbl_tree,
+                       hf_ldp_tlv_sess_fr_maxdlci, tvb, offset+5, 3, id, "Maximum DLCI %u", id);
+
+                               offset += 8;
+                       }
+
+                       if( rem || numlr)
+                               proto_tree_add_text(val_tree, tvb, offset, rem,
+                                "Error processing TLV: Extra data at end of TLV");
+               }
+       }
 }
 
-/* 
- * Each of these routines dissect the relevant messages, but the msg header 
- * has already been dissected.
- */
 
-void
-dissect_ldp_notification(tvbuff_t *tvb, guint offset, packet_info *pinfo, proto_tree *tree, guint length)
+static void
+dissect_tlv_lspid(tvbuff_t *tvb, guint offset,proto_tree *tree, int rem)
 {
-  guint rem = length, cc = 0;
+       proto_tree *ti = NULL, *val_tree = NULL;
+
+       if (tree != NULL) {
+               if(rem != 8) {
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing LSP ID TLV: length is %d, should be 8",
+                           rem);
+                       return;
+               }
+
+               ti = proto_tree_add_text(tree, tvb, offset, rem,
+                                               "LSP ID");
+               val_tree = proto_item_add_subtree(ti, ett_ldp_tlv_val);
+
+               if(val_tree != NULL) {
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_lspid_act_flg,
+                                          tvb, offset, 2, FALSE);
+                       offset += 2;
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_lspid_cr_lsp,
+                                          tvb, offset, 2, FALSE);
+                       offset += 2;
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_lspid_ldpid,
+                                          tvb, offset, 4, FALSE);
+               }
+       }
+}
 
-  while (rem > 0) {
+static void
+dissect_tlv_er_hop_ipv4(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL;
+
+
+       if (tree != NULL) {
+               if(rem != 8) {
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing ER HOP IPv4 TLV: length is %d, should be 8",
+                           rem);
+                       return;
+               }
+               ti = proto_tree_add_text(tree, tvb, offset, rem, "ER HOP IPv4");
+               val_tree = proto_item_add_subtree(ti, ett_ldp_tlv_val);
+
+               if(val_tree != NULL) {
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_er_hop_loose,
+                                          tvb, offset, 3, FALSE);
+                       offset += 3;
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_er_hop_prelen,
+                                          tvb, offset, 1, FALSE);
+                       offset ++;
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_er_hop_prefix4,
+                                          tvb, offset, 4, FALSE);
+               }
+       }
+}
 
-    rem -= (cc = dissect_tlv(tvb, offset, tree, rem));
-    offset += cc;
+static void
+dissect_tlv_er_hop_ipv6(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL;
+
+       if (tree != NULL) {
+               if(rem != 20) {
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing ER HOP IPv6 TLV: length is %d, should be 20",
+                           rem);
+                       return;
+               }
+               ti = proto_tree_add_text(tree, tvb, offset, rem, "ER HOP IPv6");
+               val_tree = proto_item_add_subtree(ti, ett_ldp_tlv_val);
+
+               if(val_tree != NULL) {
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_er_hop_loose,
+                                          tvb, offset, 3, FALSE);
+                       offset += 3;
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_er_hop_prelen,
+                                         tvb, offset, 1, FALSE);
+                       offset ++;
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_er_hop_prefix6,
+                                          tvb, offset, 16, FALSE);
+               }
+       }
+}
 
-  }
+static void
+dissect_tlv_er_hop_as(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL;
+
+       if (tree != NULL) {
+               if(rem != 4) {
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing ER HOP AS TLV: length is %d, should be 4",
+                           rem);
+                       return;
+               }
+               ti = proto_tree_add_text(tree, tvb, offset, rem, "ER HOP AS");
+               val_tree = proto_item_add_subtree(ti, ett_ldp_tlv_val);
+
+               if(val_tree != NULL) {
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_er_hop_loose,
+                                          tvb, offset, 2, FALSE);
+                       offset += 2;
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_er_hop_as,
+                                          tvb, offset, 2, FALSE);
+               }
+       }
+}
 
+static void
+dissect_tlv_er_hop_lspid(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL;
+
+       if (tree != NULL) {
+               if(rem != 8) {
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing ER HOP LSPID TLV: length is %d, should be 8",
+                           rem);
+                       return;
+               }
+               ti = proto_tree_add_text(tree, tvb, offset, rem, "ER HOP LSPID");
+               val_tree = proto_item_add_subtree(ti, ett_ldp_tlv_val);
+
+               if(val_tree != NULL) {
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_er_hop_loose,
+                                          tvb, offset, 2, FALSE);
+                       offset += 2;
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_er_hop_cr_lsp,
+                                          tvb, offset, 2, FALSE);
+                       offset += 2;
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_er_hop_ldpid,
+                                          tvb, offset, 4, FALSE);
+               }
+       }
 }
 
-/* Dissect a Hello Message ... */
-void
-dissect_ldp_hello(tvbuff_t *tvb, guint offset, packet_info *pinfo, proto_tree *tree, guint length)
+static void
+dissect_tlv_traffic(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL;
+       guint8  val_8;
+       float   val_f;
+       proto_item *pi;
+
+       if (tree != NULL) {
+               if(rem != 24) {
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing Traffic Parameters TLV: length is %d, should be 24",
+                           rem);
+                       return;
+               }
+               ti = proto_tree_add_text(tree, tvb, offset, rem, "Traffic parameters");
+               val_tree = proto_item_add_subtree(ti, ett_ldp_tlv_val);
+
+               if(val_tree != NULL) {
+                       /* flags */
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_flags_reserv, tvb, offset, 1, FALSE);
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_flags_weight, tvb, offset, 1, FALSE);
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_flags_ebs, tvb, offset, 1, FALSE);
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_flags_cbs, tvb, offset, 1, FALSE);
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_flags_cdr, tvb, offset, 1, FALSE);
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_flags_pbs, tvb, offset, 1, FALSE);
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_flags_pdr, tvb, offset, 1, FALSE);
+
+                       offset ++;
+                       /* frequency */
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_frequency, tvb, offset, 1, FALSE);
+                       offset ++;
+
+                       /* reserver byte */
+                       offset ++;
+
+                       /* weight */
+                       pi = proto_tree_add_item(val_tree, hf_ldp_tlv_weight, tvb, offset, 1, FALSE);
+                       val_8 = tvb_get_guint8(tvb, offset);
+                       if (val_8 == 0)
+                               proto_item_set_text(pi, "Weight: Not applicable");
+                       offset ++;
+
+                       /* PDR */
+                       val_f = tvb_get_ntohieee_float (tvb, offset);
+                       proto_tree_add_double_format(val_tree, hf_ldp_tlv_pdr, tvb, offset,
+                                                   4, val_f, "PDR: %.10g Bps", val_f);
+                       offset += 4;
+                       /* PBS */
+                       val_f = tvb_get_ntohieee_float (tvb, offset);
+                       proto_tree_add_double_format(val_tree, hf_ldp_tlv_pbs, tvb, offset,
+                                                   4, val_f, "PBS: %.10g Bytes", val_f);
+                       offset += 4;
+
+                       /* CDR */
+                       val_f = tvb_get_ntohieee_float (tvb, offset);
+                       proto_tree_add_double_format(val_tree, hf_ldp_tlv_cdr, tvb, offset,
+                                                   4, val_f, "CDR: %.10g Bps", val_f);
+                       offset += 4;
+
+                       /* CBS */
+                       val_f = tvb_get_ntohieee_float (tvb, offset);
+                       proto_tree_add_double_format(val_tree, hf_ldp_tlv_cbs, tvb, offset,
+                                                   4, val_f, "CBS: %.10g Bytes", val_f);
+                       offset += 4;
+
+                       /* EBS */
+                       val_f = tvb_get_ntohieee_float (tvb, offset);
+                       proto_tree_add_double_format(val_tree, hf_ldp_tlv_ebs, tvb, offset,
+                                                   4, val_f, "EBS: %.10g Bytes", val_f);
+
+               }
+       }
+}
+
+static void
+dissect_tlv_route_pinning(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
 {
-  guint rem = length, cc = 0;
+       proto_tree *ti = NULL, *val_tree = NULL;
+
+       if (tree != NULL) {
+               if(rem != 4) {
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing Route Pinning TLV: length is %d, should be 4",
+                           rem);
+                       return;
+               }
+               ti = proto_tree_add_text(tree, tvb, offset, rem, "Route Pinning");
+               val_tree = proto_item_add_subtree(ti, ett_ldp_tlv_val);
+
+               if(val_tree != NULL) {
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_route_pinning,
+                                          tvb, offset, 4, FALSE);
+               }
+       }
+}
 
-  while (rem > 0) {
 
-    rem -= (cc = dissect_tlv(tvb, offset, tree, rem));
-    offset += cc;
+static void
+dissect_tlv_resource_class(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL;
+
+       if (tree != NULL) {
+               if(rem != 4) {
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing Resource Class TLV: length is %d, should be 4",
+                           rem);
+                       return;
+               }
+               ti = proto_tree_add_text(tree, tvb, offset, rem, "Resource Class");
+               val_tree = proto_item_add_subtree(ti, ett_ldp_tlv_val);
+
+               if(val_tree != NULL) {
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_resource_class,
+                                          tvb, offset, 4, FALSE);
+               }
+       }
+}
 
-  }
 
+static void
+dissect_tlv_preemption(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       proto_tree *ti = NULL, *val_tree = NULL;
+
+       if (tree != NULL) {
+               if(rem != 4) {
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing Preemption TLV: length is %d, should be 4",
+                           rem);
+                       return;
+               }
+               ti = proto_tree_add_text(tree, tvb, offset, rem, "Preemption");
+               val_tree = proto_item_add_subtree(ti, ett_ldp_tlv_val);
+
+               if(val_tree != NULL) {
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_set_prio,
+                                          tvb, offset, 1, FALSE);
+                       offset += 1;
+                       proto_tree_add_item(val_tree, hf_ldp_tlv_hold_prio,
+                                          tvb, offset, 1, FALSE);
+               }
+       }
 }
 
-void
-dissect_ldp_initialization(tvbuff_t *tvb, guint offset, packet_info *pinfo, proto_tree *tree, guint length)
+static int
+dissect_tlv(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem);
+
+static void
+dissect_tlv_er(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
 {
-  guint rem = length, cc = 0;
+       proto_tree *ti = NULL, *val_tree = NULL;
+       int len;
+
+       if (tree != NULL) {
+               ti = proto_tree_add_text(tree, tvb, offset, rem, "Explicit route");
+               val_tree = proto_item_add_subtree(ti, ett_ldp_tlv_val);
+
+               if(val_tree != NULL) {
+                       len = 0;
+                       while (rem > 0) {
+                               len = dissect_tlv (tvb, offset, val_tree, rem);
+                               offset += len;
+                               rem -= len;
+                       }
+               }
+       }
+}
 
-  while (rem > 0) {
 
-    rem -= (cc = dissect_tlv(tvb, offset, tree, rem));
-    offset += cc;
+/* Dissect a TLV and return the number of bytes consumed ... */
 
-  }
+static int
+dissect_tlv(tvbuff_t *tvb, guint offset, proto_tree *tree, int rem)
+{
+       guint16 type, typebak;
+       int length;
+       proto_tree *ti = NULL, *tlv_tree = NULL;
+
+       length=tvb_reported_length_remaining(tvb, offset);
+       rem=MIN(rem, length);
+
+       if( rem < 4 ) {/*chk for minimum header*/
+               if(tree)
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing TLV: length is %d, should be >= 4",
+                           rem);
+               return rem;
+       }
+       type = tvb_get_ntohs(tvb, offset) & 0x3FFF;
+
+       length = tvb_get_ntohs(tvb, offset + 2),
+       rem -= 4; /*do not count header*/
+       length = MIN(length, rem);  /* Don't go haywire if a problem ... */
+
+       if (tree != NULL) {
+               /*chk for vendor-private*/
+               if(type>=TLV_VENDOR_PRIVATE_START && type<=TLV_VENDOR_PRIVATE_END){
+                       typebak=type;           /*keep type*/
+                       type=TLV_VENDOR_PRIVATE_START;
+
+               /*chk for experimental*/
+               } else if(type>=TLV_EXPERIMENTAL_START && type<=TLV_EXPERIMENTAL_END){
+                       typebak=type;           /*keep type*/
+                       type=TLV_EXPERIMENTAL_START;
+               }
+
+               ti = proto_tree_add_text(tree, tvb, offset, length + 4, "%s",
+                       val_to_str(type, tlv_type_names, "Unknown TLV type (0x%04X)"));
+               tlv_tree = proto_item_add_subtree(ti, ett_ldp_tlv);
+               if(tlv_tree == NULL) return length+4;
+
+               proto_tree_add_item(tlv_tree, hf_ldp_tlv_unknown, tvb, offset, 1, FALSE);
+
+               proto_tree_add_uint_format(tlv_tree, hf_ldp_tlv_type, tvb, offset, 2, type, "TLV Type: %s (0x%X)", val_to_str(type, tlv_type_names, "Unknown TLV type"), type );
+
+               proto_tree_add_item(tlv_tree, hf_ldp_tlv_len, tvb, offset + 2, 2, FALSE);
+
+               switch (type) {
+
+               case TLV_FEC:
+                       dissect_tlv_fec(tvb, offset + 4, tlv_tree, length);
+                       break;
+
+               case TLV_ADDRESS_LIST:
+                       dissect_tlv_address_list(tvb, offset + 4, tlv_tree, length);
+                       break;
+
+               case TLV_HOP_COUNT:
+                       if( length != 1 ) /*error, only one byte*/
+                               proto_tree_add_text(tlv_tree, tvb, offset + 4,length,
+                                   "Error processing Hop Count TLV: length is %d, should be 1",
+                                   length);
+                       else
+                               proto_tree_add_item(tlv_tree, hf_ldp_tlv_hc_value, tvb,offset + 4, length, FALSE);
+                       break;
+
+               case TLV_PATH_VECTOR:
+                       dissect_tlv_path_vector(tvb, offset + 4, tlv_tree, length);
+                       break;
+
+               case TLV_GENERIC_LABEL:
+                       if( length != 4 ) /*error, need only label*/
+                               proto_tree_add_text(tlv_tree, tvb, offset + 4, length,
+                                   "Error processing Generic Label TLV: length is %d, should be 4",
+                                   length);
+                       else {
+                               guint32 label=tvb_get_ntohl(tvb, offset+4) & 0x000FFFFF;
+
+                               proto_tree_add_uint_format(tlv_tree, hf_ldp_tlv_generic_label,
+                                       tvb, offset+4, length, label, "Generic Label: %u", label);
+                       }
+                       break;
+
+               case TLV_ATM_LABEL:
+                       dissect_tlv_atm_label(tvb, offset + 4, tlv_tree, length);
+                       break;
+
+               case TLV_FRAME_LABEL:
+                       dissect_tlv_frame_label(tvb, offset + 4, tlv_tree, length);
+                       break;
+
+               case TLV_STATUS:
+                       dissect_tlv_status(tvb, offset + 4, tlv_tree, length);
+                       break;
+
+               case TLV_EXTENDED_STATUS:
+                       if( length != 4 ) /*error, need only status_code(guint32)*/
+                               proto_tree_add_text(tlv_tree, tvb, offset + 4, length,
+                                   "Error processing Extended Status TLV: length is %d, should be 4",
+                                   length);
+                       else {
+                               proto_tree_add_item(tlv_tree, hf_ldp_tlv_extstatus_data, tvb, offset + 4, length, FALSE);
+                       }
+                       break;
+
+               case TLV_RETURNED_PDU:
+                       dissect_tlv_returned_pdu(tvb, offset + 4, tlv_tree, length);
+                       break;
+
+               case TLV_RETURNED_MESSAGE:
+                       dissect_tlv_returned_message(tvb, offset + 4, tlv_tree, length);
+                       break;
+
+               case TLV_COMMON_HELLO_PARMS:
+#if 0
+                       dissect_tlv_common_hello_parms(tvb, offset + 4, tlv_tree, length);
+#else
+                       dissect_tlv_common_hello_parms(tvb, offset + 4, tlv_tree);
+#endif
+                       break;
+
+               case TLV_IPV4_TRANSPORT_ADDRESS:
+                       if( length != 4 ) /*error, need only ipv4*/
+                               proto_tree_add_text(tlv_tree, tvb, offset + 4, length,
+                                   "Error processing IPv4 Transport Address TLV: length is %d, should be 4",
+                                   length);
+                       else {
+                               proto_tree_add_item(tlv_tree, hf_ldp_tlv_ipv4_taddr, tvb, offset + 4, 4, FALSE);
+                       }
+                       break;
+
+               case TLV_CONFIGURATION_SEQNO:
+                       if( length != 4 ) /*error, need only seq_num(guint32)*/
+                               proto_tree_add_text(tlv_tree, tvb, offset + 4, length,
+                                   "Error processing Configuration Sequence Number TLV: length is %d, should be 4",
+                                   length);
+                       else {
+                               proto_tree_add_item(tlv_tree, hf_ldp_tlv_config_seqno, tvb, offset + 4, 4, FALSE);
+                       }
+                       break;
+
+               case TLV_IPV6_TRANSPORT_ADDRESS:
+                       if( length != 16 ) /*error, need only ipv6*/
+                               proto_tree_add_text(tlv_tree, tvb, offset + 4, length,
+                                   "Error processing IPv6 Transport Address TLV: length is %d, should be 16",
+                                   length);
+                       else {
+                               proto_tree_add_item(tlv_tree, hf_ldp_tlv_ipv6_taddr, tvb, offset + 4, 16, FALSE);
+                       }
+                       break;
+
+               case TLV_MAC: /* draft-lasserre-vkompella-ppvpn-vpls-02.txt */
+                       dissect_tlv_mac(tvb, offset + 4, tlv_tree, length);
+                       break;
+
+               case TLV_COMMON_SESSION_PARMS:
+                       dissect_tlv_common_session_parms(tvb, offset + 4, tlv_tree, length);
+                       break;
+
+               case TLV_ATM_SESSION_PARMS:
+                       dissect_tlv_atm_session_parms(tvb, offset + 4, tlv_tree, length);
+                       break;
+
+               case TLV_FRAME_RELAY_SESSION_PARMS:
+                       dissect_tlv_frame_relay_session_parms(tvb, offset + 4, tlv_tree, length);
+                       break;
+
+               case TLV_LABEL_REQUEST_MESSAGE_ID:
+                       if( length != 4 ) /*error, need only one msgid*/
+                               proto_tree_add_text(tlv_tree, tvb, offset + 4, length,
+                                   "Error processing Label Request Message ID TLV: length is %d, should be 4",
+                                   length);
+                       else
+                               proto_tree_add_item(tlv_tree, hf_ldp_tlv_lbl_req_msg_id, tvb,offset + 4,length, FALSE);
+                       break;
+
+               case TLV_LSPID:
+                       dissect_tlv_lspid(tvb, offset + 4, tlv_tree, length);
+                       break;
+
+               case TLV_ER:
+                       dissect_tlv_er(tvb, offset + 4, tlv_tree, length);
+                       break;
+
+               case TLV_ER_HOP_IPV4:
+                       dissect_tlv_er_hop_ipv4(tvb, offset + 4, tlv_tree, length);
+                       break;
+
+               case TLV_ER_HOP_IPV6:
+                       dissect_tlv_er_hop_ipv6(tvb, offset +4, tlv_tree, length);
+                       break;
+
+               case TLV_ER_HOP_AS:
+                       dissect_tlv_er_hop_as(tvb, offset + 4, tlv_tree, length);
+                       break;
+
+               case TLV_ER_HOP_LSPID:
+                       dissect_tlv_er_hop_lspid(tvb, offset +4, tlv_tree, length);
+                       break;
+
+               case TLV_TRAFFIC_PARAM:
+                       dissect_tlv_traffic(tvb, offset +4, tlv_tree, length);
+                       break;
+
+               case TLV_PREEMPTION:
+                       dissect_tlv_preemption(tvb, offset +4, tlv_tree, length);
+                       break;
+
+               case TLV_RESOURCE_CLASS:
+                       dissect_tlv_resource_class(tvb, offset +4, tlv_tree, length);
+                       break;
+
+               case TLV_ROUTE_PINNING:
+                       dissect_tlv_route_pinning(tvb, offset +4, tlv_tree, length);
+                       break;
+
+               case TLV_VENDOR_PRIVATE_START:
+                       if( length < 4 ) /*error, at least Vendor ID*/
+                               proto_tree_add_text(tlv_tree, tvb, offset + 4, length,
+                                   "Error processing Vendor Private Start TLV: length is %d, should be >= 4",
+                                   length);
+                       else {
+                               proto_tree_add_item(tlv_tree, hf_ldp_tlv_vendor_id, tvb,offset + 4, 4, FALSE);
+                               if( length > 4 )  /*have data*/
+                                       proto_tree_add_text(tlv_tree, tvb, offset + 8, length-4,"Data");
+                       }
+                       break;
+
+               case TLV_EXPERIMENTAL_START:
+                       if( length < 4 ) /*error, at least Experiment ID*/
+                               proto_tree_add_text(tlv_tree, tvb, offset + 4, length,
+                                   "Error processing Experimental Start TLV: length is %d, should be >= 4",
+                                   length);
+                       else {
+                               proto_tree_add_item(tlv_tree, hf_ldp_tlv_experiment_id, tvb,offset + 4, 4, FALSE);
+                               if( length > 4 )  /*have data*/
+                                       proto_tree_add_text(tlv_tree, tvb, offset + 8, length-4,"Data");
+                       }
+                       break;
+
+               default:
+                       proto_tree_add_item(tlv_tree, hf_ldp_tlv_value, tvb, offset + 4, length, FALSE);
+                       break;
+               }
+       }
 
+       return length + 4;  /* Length of the value field + header */
 }
 
-void
-dissect_ldp_keepalive(tvbuff_t *tvb, guint offset, packet_info *pinfo, proto_tree *tree, guint length)
+
+/* Dissect a Message and return the number of bytes consumed ... */
+
+static int
+dissect_msg(tvbuff_t *tvb, guint offset, packet_info *pinfo, proto_tree *tree)
 {
-  guint rem = length, cc = 0;
+       guint16 type, typebak;
+       guint8  extra=0;
+       int length, rem, ao=0, co;
+       proto_tree *ti = NULL, *msg_tree = NULL;
+
+       rem=tvb_reported_length_remaining(tvb, offset);
+
+       if( rem < 8 ) {/*chk for minimum header = type + length + msg_id*/
+               if( check_col(pinfo->cinfo, COL_INFO) )
+                       col_append_fstr(pinfo->cinfo, COL_INFO, "Bad Message");
+               if(tree)
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing Message: length is %d, should be >= 8",
+                           rem);
+               return rem;
+       }
+       type = tvb_get_ntohs(tvb, offset) & 0x7FFF;
+
+       /*chk for vendor-private*/
+       if(type>=LDP_VENDOR_PRIVATE_START && type<=LDP_VENDOR_PRIVATE_END){
+               typebak=type;           /*keep type*/
+               type=LDP_VENDOR_PRIVATE_START;
+               extra=4;
+       /*chk for experimental*/
+       } else if(type>=LDP_EXPERIMENTAL_MESSAGE_START && type<=LDP_EXPERIMENTAL_MESSAGE_END){
+               typebak=type;           /*keep type*/
+               type=LDP_EXPERIMENTAL_MESSAGE_START;
+               extra=4;
+       }
+
+       if( (length = tvb_get_ntohs(tvb, offset + 2)) < (4+extra) ) {/*not enough data for type*/
+               if( check_col(pinfo->cinfo, COL_INFO) )
+                       col_append_fstr(pinfo->cinfo, COL_INFO, "Bad Message Length ");
+               if(tree)
+                       proto_tree_add_text(tree, tvb, offset, rem,
+                           "Error processing Message Length: length is %d, should be >= %u",
+                           length, 4+extra);
+               return rem;
+       }
+       rem -= 4;
+       length = MIN(length, rem);  /* Don't go haywire if a problem ... */
 
-  while (rem > 0) {
+       if( check_col(pinfo->cinfo, COL_INFO) ){
+               col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str(type, ldp_message_types, "Unknown Message (0x%04X)"));
+       }
 
-    rem -= (cc = dissect_tlv(tvb, offset, tree, rem));
-    offset += cc;
+       if( tree ){
+               ti = proto_tree_add_text(tree, tvb, offset, length + 4, "%s",
+                       val_to_str(type, ldp_message_types, "Unknown Message type (0x%04X)"));
+               msg_tree = proto_item_add_subtree(ti, ett_ldp_message);
+               if(msg_tree == NULL) return length+4;
+
+               proto_tree_add_item(msg_tree, hf_ldp_msg_ubit, tvb, offset, 1, FALSE);
+
+               type=tvb_get_ntohs(tvb, offset)&0x7FFF;
+               proto_tree_add_uint_format(msg_tree, hf_ldp_msg_type, tvb, offset, 2, type, "Message Type: %s (0x%X)", val_to_str(type, ldp_message_types,"Unknown Message Type"), type);
+
+               proto_tree_add_item(msg_tree, hf_ldp_msg_len, tvb, offset+2, 2, FALSE);
+               proto_tree_add_item(msg_tree, hf_ldp_msg_id, tvb, offset+4, 4, FALSE);
+               if(extra){
+                       int hf_tmp=0;
+
+                       switch(type){
+                               case LDP_VENDOR_PRIVATE_START:
+                                       hf_tmp=hf_ldp_msg_vendor_id;
+                                       break;
+                               case LDP_EXPERIMENTAL_MESSAGE_START:
+                                       hf_tmp=hf_ldp_msg_experiment_id;
+                                       break;
+                       }
+                       proto_tree_add_item(msg_tree, hf_tmp, tvb, offset+8, extra, FALSE);
+               }
+       }
 
-  }
+       offset += (8+extra);
+       length -= (4+extra);
 
+       if( tree )
+               while( (length-ao) > 0 ) {
+                       co=dissect_tlv(tvb, offset, msg_tree, length-ao);
+                       offset += co;
+                       ao += co;
+               }
+
+       return length+8+extra;
 }
 
-void
-dissect_ldp_address(tvbuff_t *tvb, guint offset, packet_info *pinfo, proto_tree *tree, guint length)
+/* Dissect a PDU */
+static void
+dissect_ldp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
-  guint rem = length, cc = 0;
+       int offset = 0, co;
+       int rem, length;
+       proto_tree *ti=NULL, *pdu_tree = NULL;
 
-  while (rem > 0) {
+       if (check_col(pinfo->cinfo, COL_PROTOCOL))
+               col_set_str(pinfo->cinfo, COL_PROTOCOL, "LDP");
 
-    rem -= (cc = dissect_tlv(tvb, offset, tree, rem));
-    offset += cc;
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_clear(pinfo->cinfo, COL_INFO);
 
-  }
+       if( tree ){
+               ti=proto_tree_add_item(tree, proto_ldp, tvb, 0, -1, FALSE);
+               pdu_tree = proto_item_add_subtree(ti, ett_ldp);
 
-}
-
-void
-dissect_ldp_address_withdrawal(tvbuff_t *tvb, guint offset, packet_info *pinfo, proto_tree *tree, guint length)
-{
-  guint rem = length, cc = 0;
+               proto_tree_add_item(pdu_tree, hf_ldp_version, tvb, offset, 2, FALSE);
+       }
 
-  while (rem > 0) {
+       length = tvb_get_ntohs(tvb, offset+2);
+       if( tree )
+               proto_tree_add_uint(pdu_tree, hf_ldp_pdu_len, tvb, offset+2, 2, length);
 
-    rem -= (cc = dissect_tlv(tvb, offset, tree, rem));
-    offset += cc;
+       length += 4;    /* add the version and type sizes */
+       rem = tvb_reported_length_remaining(tvb, offset);
+       if (length < rem)
+               tvb_set_reported_length(tvb, length);
 
-  }
+       if( tree ){
+               proto_tree_add_item(pdu_tree, hf_ldp_lsr, tvb, offset+4, 4, FALSE);
+               proto_tree_add_item(pdu_tree, hf_ldp_ls_id, tvb, offset+8, 2, FALSE);
+       }
+       offset += 10;
 
+       while( tvb_reported_length_remaining(tvb, offset) > 0 ) {
+               co=dissect_msg(tvb, offset, pinfo, pdu_tree);
+               offset += co;
+       }
 }
 
-void
-dissect_ldp_label_mapping(tvbuff_t *tvb, guint offset, packet_info *pinfo, proto_tree *tree, guint length)
+static int
+dissect_ldp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
-  guint rem = length, cc = 0;
-
-  while (rem > 0) {
+       /*
+        * Make sure the first PDU has a version number of 1;
+        * if not, reject this, so we don't get confused by
+        * packets that happen to be going to or from the
+        * LDP port but that aren't LDP packets.
+        */
+       if (!tvb_bytes_exist(tvb, 0, 2)) {
+               /*
+                * Not enough information to tell.
+                */
+               return 0;
+       }
+       if (tvb_get_ntohs(tvb, 0) != 1) {
+               /*
+                * Not version 1.
+                */
+               return 0;
+       }
 
-    rem -= (cc = dissect_tlv(tvb, offset, tree, rem));
-    offset += cc;
+       dissect_ldp_pdu(tvb, pinfo, tree);
 
-  }
+       /*
+        * XXX - return minimum of this and the length of the PDU?
+        */
+       return tvb_length(tvb);
+}
 
+static int
+dissect_ldp_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+       volatile gboolean first = TRUE;
+       volatile int offset = 0;
+       int length_remaining;
+       guint16 plen;
+       int length;
+       tvbuff_t *next_tvb;
+
+       while (tvb_reported_length_remaining(tvb, offset) != 0) {
+               length_remaining = tvb_length_remaining(tvb, offset);
+
+               /*
+                * Make sure the first PDU has a version number of 1;
+                * if not, reject this, so we don't get confused by
+                * packets that happen to be going to or from the
+                * LDP port but that aren't LDP packets.
+                *
+                * XXX - this means we can't handle an LDP PDU of which
+                * only one byte appears in a TCP segment.  If that's
+                * a problem, we'll either have to completely punt on
+                * rejecting non-LDP packets, or will have to assume
+                * that if we have only one byte, it's an LDP packet.
+                */
+               if (first) {
+                       if (length_remaining < 2) {
+                               /*
+                                * Not enough information to tell.
+                                */
+                               return 0;
+                       }
+                       if (tvb_get_ntohs(tvb, offset) != 1) {
+                               /*
+                                * Not version 1.
+                                */
+                               return 0;
+                       }
+                       first = FALSE;
+               }
+
+               /*
+                * Can we do reassembly?
+                */
+               if (ldp_desegment && pinfo->can_desegment) {
+                       /*
+                        * Yes - is the LDP header split across segment
+                        * boundaries?
+                        */
+                       if (length_remaining < 4) {
+                               /*
+                                * Yes.  Tell the TCP dissector where
+                                * the data for this message starts in
+                                * the data it handed us, and how many
+                                * more bytes we need, and return.
+                                */
+                               pinfo->desegment_offset = offset;
+                               pinfo->desegment_len = 4 - length_remaining;
+                               return -pinfo->desegment_len;
+                       }
+               }
+
+               /*
+                * Get the length of the rest of the LDP packet.
+                * XXX - check for a version of 1 first?
+                */
+               plen = tvb_get_ntohs(tvb, offset + 2);
+
+               /*
+                * Can we do reassembly?
+                */
+               if (ldp_desegment && pinfo->can_desegment) {
+                       /*
+                        * Yes - is the LDP packet split across segment
+                        * boundaries?
+                        */
+                       if (length_remaining < plen + 4) {
+                               /*
+                                * Yes.  Tell the TCP dissector where the
+                                * data for this message starts in the data
+                                * it handed us, and how many more bytes we
+                                * need, and return.
+                                */
+                               pinfo->desegment_offset = offset;
+                               pinfo->desegment_len =
+                                   (plen + 4) - length_remaining;
+                               return -pinfo->desegment_len;
+                       }
+               }
+
+               /*
+                * Construct a tvbuff containing the amount of the payload
+                * we have available.  Make its reported length the
+                * amount of data in the DNS-over-TCP packet.
+                *
+                * XXX - if reassembly isn't enabled. the subdissector
+                * will throw a BoundsError exception, rather than a
+                * ReportedBoundsError exception.  We really want
+                * a tvbuff where the length is "length", the reported
+                * length is "plen + 4", and the "if the snapshot length
+                * were infinite" length is the minimum of the
+                * reported length of the tvbuff handed to us and "plen+4",
+                * with a new type of exception thrown if the offset is
+                * within the reported length but beyond that third length,
+                * with that exception getting the "Unreassembled Packet"
+                * error.
+                */
+               length = length_remaining;
+               if (length > plen + 4)
+                       length = plen + 4;
+               next_tvb = tvb_new_subset(tvb, offset, length, plen + 4);
+
+               /*
+                * Dissect the LDP packet.
+                *
+                * Catch the ReportedBoundsError exception; if this
+                * particular message happens to get a ReportedBoundsError
+                * exception, that doesn't mean that we should stop
+                * dissecting LDP messages within this frame or chunk of
+                * reassembled data.
+                *
+                * If it gets a BoundsError, we can stop, as there's nothing
+                * more to see, so we just re-throw it.
+                */
+               TRY {
+                       dissect_ldp_pdu(next_tvb, pinfo, tree);
+               }
+               CATCH(BoundsError) {
+                       RETHROW;
+               }
+               CATCH(ReportedBoundsError) {
+                       show_reported_bounds_error(tvb, pinfo, tree);
+               }
+               ENDTRY;
+
+               /*
+                * Skip the LDP header and the payload.
+                */
+               offset += plen + 4;
+       }
+       return tvb_length(tvb);
 }
 
+/* Register all the bits needed with the filtering engine */
+
 void
-dissect_ldp_label_request(tvbuff_t *tvb, guint offset, packet_info *pinfo, proto_tree *tree, guint length)
+proto_register_ldp(void)
 {
-  guint rem = length, cc = 0;
-
-  while (rem > 0) {
+  static hf_register_info hf[] = {
+    { &hf_ldp_req,
+      /* Change the following to the type you need */
+      { "Request", "ldp.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
 
-    rem -= (cc = dissect_tlv(tvb, offset, tree, rem));
-    offset += cc;
+    { &hf_ldp_rsp,
+      { "Response", "ldp.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "", HFILL }},
 
-  }
+    { &hf_ldp_version,
+      { "Version", "ldp.hdr.version", FT_UINT16, BASE_DEC, NULL, 0x0, "LDP Version Number", HFILL }},
 
-}
+    { &hf_ldp_pdu_len,
+      { "PDU Length", "ldp.hdr.pdu_len", FT_UINT16, BASE_DEC, NULL, 0x0, "LDP PDU Length", HFILL }},
 
-void
-dissect_ldp_label_withdrawal(tvbuff_t *tvb, guint offset, packet_info *pinfo, proto_tree *tree, guint length)
-{
-  guint rem = length, cc = 0;
+    { &hf_ldp_lsr,
+      { "LSR ID", "ldp.hdr.ldpid.lsr", FT_IPv4, BASE_HEX, NULL, 0x0, "LDP Label Space Router ID", HFILL }},
 
-  while (rem > 0) {
+    { &hf_ldp_ls_id,
+      { "Label Space ID", "ldp.hdr.ldpid.lsid", FT_UINT16, BASE_DEC, NULL, 0, "LDP Label Space ID", HFILL }},
 
-    rem -= (cc = dissect_tlv(tvb, offset, tree, rem));
-    offset += cc;
+    { &hf_ldp_msg_ubit,
+      { "U bit", "ldp.msg.ubit", FT_BOOLEAN, 8, TFS(&ldp_message_ubit), 0x80, "Unknown Message Bit", HFILL }},
 
-  }
+    { &hf_ldp_msg_type,
+      { "Message Type", "ldp.msg.type", FT_UINT16, BASE_HEX, VALS(ldp_message_types), 0x7FFF, "LDP message type", HFILL }},
 
-}
+    { &hf_ldp_msg_len,
+      { "Message Length", "ldp.msg.len", FT_UINT16, BASE_DEC, NULL, 0x0, "LDP Message Length (excluding message type and len)", HFILL }},
 
-void
-dissect_ldp_label_release(tvbuff_t *tvb, guint offset, packet_info *pinfo, proto_tree *tree, guint length)
-{
-  guint rem = length, cc = 0;
+    { &hf_ldp_msg_id,
+      { "Message ID", "ldp.msg.id", FT_UINT32, BASE_HEX, NULL, 0x0, "LDP Message ID", HFILL }},
 
-  while (rem > 0) {
+    { &hf_ldp_msg_vendor_id,
+      { "Vendor ID", "ldp.msg.vendor.id", FT_UINT32, BASE_HEX, NULL, 0x0, "LDP Vendor-private Message ID", HFILL }},
 
-    rem -= (cc = dissect_tlv(tvb, offset, tree, rem));
-    offset += cc;
+    { &hf_ldp_msg_experiment_id,
+      { "Experiment ID", "ldp.msg.experiment.id", FT_UINT32, BASE_HEX, NULL, 0x0, "LDP Experimental Message ID", HFILL }},
 
-  }
+    { &hf_ldp_tlv_unknown,
+      { "TLV Unknown bits", "ldp.msg.tlv.unknown", FT_UINT8, BASE_HEX, VALS(tlv_unknown_vals), 0xC0, "TLV Unknown bits Field", HFILL }},
 
-}
+    { &hf_ldp_tlv_type,
+      { "TLV Type", "ldp.msg.tlv.type", FT_UINT16, BASE_HEX, VALS(tlv_type_names), 0x3FFF, "TLV Type Field", HFILL }},
 
-void
-dissect_ldp_label_abort_request(tvbuff_t *tvb, guint offset, packet_info *pinfo, proto_tree *tree, guint length)
-{
-  guint rem = length, cc = 0;
+    { &hf_ldp_tlv_len,
+      {"TLV Length", "ldp.msg.tlv.len", FT_UINT16, BASE_DEC, NULL, 0x0, "TLV Length Field", HFILL }},
 
-  while (rem > 0) {
+    { &hf_ldp_tlv_value,
+      { "TLV Value", "ldp.msg.tlv.value", FT_BYTES, BASE_NONE, NULL, 0x0, "TLV Value Bytes", HFILL }},
 
-    rem -= (cc = dissect_tlv(tvb, offset, tree, rem));
-    offset += cc;
+    { &hf_ldp_tlv_val_hold,
+      { "Hold Time", "ldp.msg.tlv.hello.hold", FT_UINT16, BASE_DEC, NULL, 0x0, "Hello Common Parameters Hold Time", HFILL }},
 
-  }
+    { &hf_ldp_tlv_val_target,
+      { "Targeted Hello", "ldp.msg.tlv.hello.targeted", FT_BOOLEAN, 16, TFS(&hello_targeted_vals), 0x8000, "Hello Common Parameters Targeted Bit", HFILL }},
 
-}
+    { &hf_ldp_tlv_val_request,
+      { "Hello Requested", "ldp,msg.tlv.hello.requested", FT_BOOLEAN, 16, TFS(&hello_requested_vals), 0x4000, "Hello Common Parameters Hello Requested Bit", HFILL }},
 
-static void
-dissect_ldp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
-{
-  proto_tree     *ldp_tree = NULL, 
-                 *ti = NULL,
-                 *hdr_tree = NULL, *ldpid_tree = NULL;
-  int           offset = 0, msg_cnt = 0;
-  guint16        ldp_message = 0;
+    { &hf_ldp_tlv_val_res,
+      { "Reserved", "ldp.msg.tlv.hello.res", FT_UINT16, BASE_HEX, NULL, 0x3FFF, "Hello Common Parameters Reserved Field", HFILL }},
 
-  CHECK_DISPLAY_AS_DATA(proto_ldp, tvb, pinfo, tree);
+    { &hf_ldp_tlv_ipv4_taddr,
+      { "IPv4 Transport Address", "ldp.msg.tlv.ipv4.taddr", FT_IPv4, BASE_DEC, NULL, 0x0, "IPv4 Transport Address", HFILL }},
 
-  pinfo->current_proto = "LDP";
+    { &hf_ldp_tlv_config_seqno,
+      { "Configuration Sequence Number", "ldp.msg.tlv.hello.cnf_seqno", FT_UINT32, BASE_DEC, NULL, 0x0, "Hello Configuration Sequence Number", HFILL }},
 
-  if (check_col(pinfo->fd, COL_PROTOCOL))
+    { &hf_ldp_tlv_ipv6_taddr,
+      { "IPv6 Transport Address", "ldp.msg.tlv.ipv6.taddr", FT_IPv6, BASE_DEC, NULL, 0x0, "IPv6 Transport Address", HFILL }},
 
-    col_add_str(pinfo->fd, COL_PROTOCOL, "LDP");
+    { &hf_ldp_tlv_fec_wc,
+      { "FEC Element Type", "ldp.msg.tlv.fec.type", FT_UINT8, BASE_DEC, VALS(fec_types), 0x0, "Forwarding Equivalence Class Element Types", HFILL }},
 
-  if (tree) {  /* Build the tree info ..., this is wrong! FIXME */
+    { &hf_ldp_tlv_fec_af,
+      { "FEC Element Address Type", "ldp.msg.tlv.fec.af", FT_UINT16, BASE_DEC, VALS(afn_vals), 0x0, "Forwarding Equivalence Class Element Address Family", HFILL }},
 
-    ti = proto_tree_add_item(tree, proto_ldp, tvb, offset,
-                            tvb_length_remaining(tvb, offset), FALSE);
-    ldp_tree = proto_item_add_subtree(ti, ett_ldp);
+    { &hf_ldp_tlv_fec_len,
+      { "FEC Element Length", "ldp.msg.tlv.fec.len", FT_UINT8, BASE_DEC, NULL, 0x0, "Forwarding Equivalence Class Element Length", HFILL }},
 
-    ti = proto_tree_add_text(ldp_tree, tvb, 0, 10, "Header");
+    { &hf_ldp_tlv_fec_pfval,
+      { "FEC Element Prefix Value", "ldp.msg.tlv.fec.pfval", FT_STRING, BASE_NONE, NULL, 0x0, "Forwarding Equivalence Class Element Prefix", HFILL }},
 
-    hdr_tree = proto_item_add_subtree(ti, ett_ldp_header);
+    { &hf_ldp_tlv_fec_hoval,
+      { "FEC Element Host Address Value", "ldp.msg.tlv.fec.hoval", FT_STRING, BASE_NONE, NULL, 0x0, "Forwarding Equivalence Class Element Address", HFILL }},
 
-    proto_tree_add_item(hdr_tree, hf_ldp_version, tvb, offset, 2, FALSE);
+    { &hf_ldp_tlv_addrl_addr_family,
+      { "Address Family", "ldp.msg.tlv.addrl.addr_family", FT_UINT16, BASE_DEC, VALS(afn_vals), 0x0, "Address Family List", HFILL }},
 
-    offset += 2;
+    { &hf_ldp_tlv_addrl_addr,
+      { "Address", "ldp.msg.tlv.addrl.addr", FT_STRING, BASE_NONE, NULL, 0x0, "Address", HFILL }},
 
-    proto_tree_add_item(hdr_tree, hf_ldp_pdu_len, tvb, offset, 2, FALSE);
+    { &hf_ldp_tlv_hc_value,
+      { "Hop Count Value", "ldp.msg.tlv.hc.value", FT_UINT8, BASE_DEC, NULL, 0x0, "Hop Count", HFILL }},
 
-    offset += 2;
+    { &hf_ldp_tlv_pv_lsrid,
+      { "LSR Id", "ldp.msg.tlv.pv.lsrid", FT_IPv4, BASE_DEC, NULL, 0x0, "Path Vector LSR Id", HFILL }},
 
-    ti = proto_tree_add_text(hdr_tree, tvb, offset, 6, "LDP Identifier");
+    { &hf_ldp_tlv_sess_ver,
+      { "Session Protocol Version", "ldp.msg.tlv.sess.ver", FT_UINT16, BASE_DEC, NULL, 0x0, "Common Session Parameters Protocol Version", HFILL }},
 
-    ldpid_tree = proto_item_add_subtree(ti, ett_ldp_ldpid);
+    { &hf_ldp_tlv_sess_ka,
+      { "Session KeepAlive Time", "ldp.msg.tlv.sess.ka", FT_UINT16, BASE_DEC, NULL, 0x0, "Common Session Parameters KeepAlive Time", HFILL }},
 
-    proto_tree_add_item(ldpid_tree, hf_ldp_lsr, tvb, offset, 4, FALSE);
+    { &hf_ldp_tlv_sess_advbit,
+      { "Session Label Advertisement Discipline", "ldp.msg.tlv.sess.advbit",
+ FT_BOOLEAN, 8, TFS(&tlv_sess_advbit_vals), 0x80,
+       "Common Session Parameters Label Advertisement Discipline", HFILL }},
 
-    offset += 4;
+    { &hf_ldp_tlv_sess_ldetbit,
+      { "Session Loop Detection", "ldp.msg.tlv.sess.ldetbit", FT_BOOLEAN, 8, TFS(&tlv_sess_ldetbit_vals), 0x40, "Common Session Parameters Loop Detection", HFILL }},
 
-    proto_tree_add_item(ldpid_tree, hf_ldp_ls_id, tvb, offset, 2, FALSE);
+    { &hf_ldp_tlv_sess_pvlim,
+      { "Session Path Vector Limit", "ldp.msg.tlv.sess.pvlim", FT_UINT8, BASE_DEC, NULL, 0x0, "Common Session Parameters Path Vector Limit", HFILL }},
 
-    offset += 2;
+    { &hf_ldp_tlv_sess_mxpdu,
+      { "Session Max PDU Length", "ldp.msg.tlv.sess.mxpdu", FT_UINT16, BASE_DEC, NULL, 0x0, "Common Session Parameters Max PDU Length", HFILL }},
 
-  }
+    { &hf_ldp_tlv_sess_rxlsr,
+      { "Session Receiver LSR Identifier", "ldp.msg.tlv.sess.rxlsr", FT_IPv4, BASE_DEC, NULL, 0x0, "Common Session Parameters LSR Identifier", HFILL }},
 
-  offset = 10;
+    { &hf_ldp_tlv_sess_rxls,
+      { "Session Receiver Label Space Identifier", "ldp.msg.tlv.sess.rxlsr", FT_UINT16, BASE_DEC, NULL, 0x0, "Common Session Parameters Receiver Label Space Identifier", HFILL }},
 
-  while (tvb_length_remaining(tvb, offset) > 0) { /* Dissect a message */
+    { &hf_ldp_tlv_sess_atm_merge,
+      { "Session ATM Merge Parameter", "ldp.msg.tlv.sess.atm.merge", FT_UINT8, BASE_DEC, VALS(tlv_atm_merge_vals), 0xC0, "Merge ATM Session Parameters", HFILL }},
 
-    guint msg_len;
+    { &hf_ldp_tlv_sess_atm_lr,
+      { "Number of ATM Label Ranges", "ldp.msg.tlv.sess.atm.lr", FT_UINT8, BASE_DEC, NULL, 0x3C, "Number of Label Ranges", HFILL }},
 
-    ldp_message = tvb_get_ntohs(tvb, offset) & 0x7FFF; /* Get the message type */
+    { &hf_ldp_tlv_sess_atm_dir,
+      { "Directionality", "ldp.msg.tlv.sess.atm.dir", FT_BOOLEAN, 8, TFS(&tlv_atm_dirbit), 0x02, "Lablel Directionality", HFILL }},
 
-    msg_len = tvb_get_ntohs(tvb, offset + 2);
+    { &hf_ldp_tlv_sess_atm_minvpi,
+      { "Minimum VPI", "ldp.msg.tlv.sess.atm.minvpi", FT_UINT16, BASE_DEC, NULL, 0x0FFF, "Minimum VPI", HFILL }},
 
-    if (check_col(pinfo->fd, COL_INFO)) {  /* Check the type ... */
+    { &hf_ldp_tlv_sess_atm_minvci,
+      { "Minimum VCI", "ldp.msg.tlv.sess.atm.minvci", FT_UINT16, BASE_DEC, NULL, 0x0, "Minimum VCI", HFILL }},
 
-      if (msg_cnt > 0) 
-       col_append_fstr(pinfo->fd, COL_INFO, ", %s",
-                       val_to_str(ldp_message, ldp_message_types, "Unknown Message (0x%04X)"));
-      else
-       col_add_fstr(pinfo->fd, COL_INFO, "%s", 
-                    val_to_str(ldp_message, ldp_message_types, "Unknown Message (0x%04X)"));
+    { &hf_ldp_tlv_sess_atm_maxvpi,
+      { "Maximum VPI", "ldp.msg.tlv.sess.atm.maxvpi", FT_UINT16, BASE_DEC, NULL, 0x0FFF, "Maximum VPI", HFILL }},
 
-    }
+    { &hf_ldp_tlv_sess_atm_maxvci,
+      { "Maximum VCI", "ldp.msg.tlv.sess.atm.maxvci", FT_UINT16, BASE_DEC, NULL, 0x0, "Maximum VCI", HFILL }},
 
-    msg_cnt++;
+    { &hf_ldp_tlv_sess_fr_merge,
+      { "Session Frame Relay Merge Parameter", "ldp.msg.tlv.sess.fr.merge", FT_UINT8, BASE_DEC, VALS(tlv_fr_merge_vals), 0xC0, "Merge Frame Relay Session Parameters", HFILL }},
 
-    if (tree) {
+    { &hf_ldp_tlv_sess_fr_lr,
+      { "Number of Frame Relay Label Ranges", "ldp.msg.tlv.sess.fr.lr", FT_UINT8, BASE_DEC, NULL, 0x3C, "Number of Label Ranges", HFILL }},
 
-      proto_tree *ti = NULL, *msg_tree = NULL;
+    { &hf_ldp_tlv_sess_fr_dir,
+      { "Directionality", "ldp.msg.tlv.sess.fr.dir", FT_BOOLEAN, 8, TFS(&tlv_atm_dirbit), 0x02, "Lablel Directionality", HFILL }},
 
-      /* FIXME: Account for vendor and experimental messages */
+    { &hf_ldp_tlv_sess_fr_len,
+      { "Number of DLCI bits", "ldp.msg.tlv.sess.fr.len", FT_UINT16, BASE_DEC, VALS(tlv_fr_len_vals), 0x0180, "DLCI Number of bits", HFILL }},
 
-      ti = proto_tree_add_text(ldp_tree, tvb, offset, msg_len + 4, "%s",
-                              val_to_str(ldp_message, ldp_message_types, "Unknown Message (0x%04X)"));
+    { &hf_ldp_tlv_sess_fr_mindlci,
+      { "Minimum DLCI", "ldp.msg.tlv.sess.fr.mindlci", FT_UINT24, BASE_DEC, NULL, 0x7FFFFF, "Minimum DLCI", HFILL }},
 
-      msg_tree = proto_item_add_subtree(ti, ett_ldp_message);
+    { &hf_ldp_tlv_sess_fr_maxdlci,
+      { "Maximum DLCI", "ldp.msg.tlv.sess.fr.maxdlci", FT_UINT24, BASE_DEC, NULL, 0x7FFFFF, "Maximum DLCI", HFILL }},
 
-      proto_tree_add_item(msg_tree, hf_ldp_msg_type, tvb, offset, 2, FALSE);
+    { &hf_ldp_tlv_lbl_req_msg_id,
+      { "Label Request Message ID", "ldp.tlv.lbl_req_msg_id", FT_UINT32, BASE_HEX, NULL, 0x0, "Label Request Message to be aborted", HFILL }},
 
-      proto_tree_add_item(msg_tree, hf_ldp_msg_len, tvb, offset + 2, 2, FALSE);
+    { &hf_ldp_tlv_vendor_id,
+      { "Vendor ID", "ldp.msg.tlv.vendor_id", FT_UINT32, BASE_HEX, NULL, 0, "IEEE 802 Assigned Vendor ID", HFILL }},
 
-      proto_tree_add_item(msg_tree, hf_ldp_msg_id, tvb, offset + 4, 4, FALSE);
+    { &hf_ldp_tlv_experiment_id,
+      { "Experiment ID", "ldp.msg.tlv.experiment_id", FT_UINT32, BASE_HEX, NULL, 0, "Experiment ID", HFILL }},
 
-      switch (ldp_message) {
+    { &hf_ldp_tlv_generic_label,
+      { "Generic Label", "ldp.msg.tlv.generic.label", FT_UINT32, BASE_HEX, NULL, 0x000FFFFF, "Generic Label", HFILL }},
 
-      case LDP_NOTIFICATION:
+    { &hf_ldp_tlv_atm_label_vbits,
+      { "V-bits", "ldp.msg.tlv.atm.label.vbits", FT_UINT8, BASE_HEX, VALS(tlv_atm_vbits_vals), 0x30, "ATM Label V Bits", HFILL }},
 
-       dissect_ldp_notification(tvb, offset + 8, pinfo, msg_tree, msg_len - 4); 
+    { &hf_ldp_tlv_atm_label_vpi,
+      { "VPI", "ldp.msg.tlv.atm.label.vpi", FT_UINT16, BASE_DEC, NULL, 0x0FFF, "ATM Label VPI", HFILL }},
 
-       break;
+    { &hf_ldp_tlv_atm_label_vci,
+      { "VCI", "ldp.msg.tlv.atm.label.vci", FT_UINT16, BASE_DEC, NULL, 0, "ATM Label VCI", HFILL }},
 
-      case LDP_HELLO:
+    { &hf_ldp_tlv_fr_label_len,
+      { "Number of DLCI bits", "ldp.msg.tlv.fr.label.len", FT_UINT16, BASE_DEC, VALS(tlv_fr_len_vals), 0x0180, "DLCI Number of bits", HFILL }},
 
-       dissect_ldp_hello(tvb, offset + 8, pinfo, msg_tree, msg_len - 4);
+    { &hf_ldp_tlv_fr_label_dlci,
+      { "DLCI", "ldp.msg.tlv.fr.label.dlci", FT_UINT24, BASE_DEC, NULL, 0x7FFFFF, "FRAME RELAY Label DLCI", HFILL }},
 
-       break;
+    { &hf_ldp_tlv_status_ebit,
+      { "E Bit", "ldp.msg.tlv.status.ebit", FT_BOOLEAN, 8, TFS(&tlv_status_ebit), 0x80, "Fatal Error Bit", HFILL }},
 
-      case LDP_INITIALIZATION:
+    { &hf_ldp_tlv_status_fbit,
+      { "F Bit", "ldp.msg.tlv.status.fbit", FT_BOOLEAN, 8, TFS(&tlv_status_fbit), 0x40, "Forward Bit", HFILL }},
 
-       dissect_ldp_initialization(tvb, offset + 8, pinfo, msg_tree, msg_len - 4);
+    { &hf_ldp_tlv_status_data,
+      { "Status Data", "ldp.msg.tlv.status.data", FT_UINT32, BASE_HEX, VALS(tlv_status_data), 0x3FFFFFFF, "Status Data", HFILL }},
 
-       break;
+    { &hf_ldp_tlv_status_msg_id,
+      { "Message ID", "ldp.msg.tlv.status.msg.id", FT_UINT32, BASE_HEX, NULL, 0x0, "Identifies peer message to which Status TLV refers", HFILL }},
 
-      case LDP_KEEPALIVE:
+    { &hf_ldp_tlv_status_msg_type,
+      { "Message Type", "ldp.msg.tlv.status.msg.type", FT_UINT16, BASE_HEX, VALS(ldp_message_types), 0x0, "Type of peer message to which Status TLV refers", HFILL }},
 
-       dissect_ldp_keepalive(tvb, offset + 8, pinfo, msg_tree, msg_len - 4);
+    { &hf_ldp_tlv_extstatus_data,
+      { "Extended Status Data", "ldp.msg.tlv.extstatus.data", FT_UINT32, BASE_HEX, NULL, 0x0, "Extended Status Data", HFILL }},
 
-       break;
+    { &hf_ldp_tlv_returned_version,
+      { "Returned PDU Version", "ldp.msg.tlv.returned.version", FT_UINT16, BASE_DEC, NULL, 0x0, "LDP Version Number", HFILL }},
 
-      case LDP_ADDRESS:
+    { &hf_ldp_tlv_returned_pdu_len,
+      { "Returned PDU Length", "ldp.msg.tlv.returned.pdu_len", FT_UINT16, BASE_DEC, NULL, 0x0, "LDP PDU Length", HFILL }},
 
-       dissect_ldp_address(tvb, offset + 8, pinfo, msg_tree, msg_len - 4);
+    { &hf_ldp_tlv_returned_lsr,
+      { "Returned PDU LSR ID", "ldp.msg.tlv.returned.ldpid.lsr", FT_IPv4, BASE_DEC, NULL, 0x0, "LDP Label Space Router ID", HFILL }},
 
-       break;
+    { &hf_ldp_tlv_returned_ls_id,
+      { "Returned PDU Label Space ID", "ldp.msg.tlv.returned.ldpid.lsid", FT_UINT16, BASE_HEX, NULL, 0x0, "LDP Label Space ID", HFILL }},
 
-      case LDP_ADDRESS_WITHDRAWAL:
+    { &hf_ldp_tlv_returned_msg_ubit,
+      { "Returned Message Unknown bit", "ldp.msg.tlv.returned.msg.ubit", FT_UINT8, BASE_HEX, TFS(&ldp_message_ubit), 0x80, "Message Unknown bit", HFILL }},
 
-       dissect_ldp_address_withdrawal(tvb, offset + 8, pinfo, msg_tree, msg_len - 4);
+    { &hf_ldp_tlv_returned_msg_type,
+      { "Returned Message Type", "ldp.msg.tlv.returned.msg.type", FT_UINT16, BASE_HEX, VALS(ldp_message_types), 0x7FFF, "LDP message type", HFILL }},
 
-       break;
+    { &hf_ldp_tlv_returned_msg_len,
+      { "Returned Message Length", "ldp.msg.tlv.returned.msg.len", FT_UINT16, BASE_DEC, NULL, 0x0, "LDP Message Length (excluding message type and len)", HFILL }},
 
-      case LDP_LABEL_MAPPING:
+    { &hf_ldp_tlv_returned_msg_id,
+      { "Returned Message ID", "ldp.msg.tlv.returned.msg.id", FT_UINT32, BASE_HEX, NULL, 0x0, "LDP Message ID", HFILL }},
 
-       dissect_ldp_label_mapping(tvb, offset + 8, pinfo, msg_tree, msg_len - 4);
+    { &hf_ldp_tlv_mac,
+      { "MAC address", "ldp.msg.tlv.mac", FT_ETHER, BASE_NONE, NULL, 0x0, "MAC address", HFILL}},
 
-       break;
+    {&hf_ldp_tlv_fec_vc_controlword,
+     {"C-bit", "ldp.msg.tlv.fec.vc.controlword", FT_BOOLEAN, 8, TFS(&fec_vc_cbit), 0x80, "Control Word Present", HFILL }},
 
-      case LDP_LABEL_REQUEST:
+    {&hf_ldp_tlv_fec_vc_vctype,
+     {"VC Type", "ldp.msg.tlv.fec.vc.vctype", FT_UINT16, BASE_HEX, VALS(fec_vc_types_vals), 0x7FFF, "Virtual Circuit Type", HFILL }},
 
-       dissect_ldp_label_request(tvb, offset + 8, pinfo, msg_tree, msg_len - 4);
+    {&hf_ldp_tlv_fec_vc_infolength,
+     {"VC Info Length", "ldp.msg.tlv.fec.vc.infolength", FT_UINT8, BASE_DEC, NULL, 0x0, "VC FEC Info Length", HFILL }},
 
-       break;
+    {&hf_ldp_tlv_fec_vc_groupid,
+     {"Group ID", "ldp.msg.tlv.fec.vc.groupid", FT_UINT32, BASE_DEC, NULL, 0x0, "VC FEC Group ID", HFILL }},
 
-      case LDP_LABEL_WITHDRAWAL:
+    {&hf_ldp_tlv_fec_vc_vcid,
+     {"VC ID", "ldp.msg.tlv.fec.vc.vcid", FT_UINT32, BASE_DEC, NULL, 0x0, "VC FEC VCID", HFILL }},
 
-       dissect_ldp_label_withdrawal(tvb, offset + 8, pinfo, msg_tree, msg_len - 4);
+    {&hf_ldp_tlv_fec_vc_intparam_length,
+     {"Length", "ldp.msg.tlv.fec.vc.intparam.length", FT_UINT8, BASE_DEC, NULL, 0x0, "VC FEC Interface Paramater Length", HFILL }},
 
-       break;
+    {&hf_ldp_tlv_fec_vc_intparam_mtu,
+     {"MTU", "ldp.msg.tlv.fec.vc.intparam.mtu", FT_UINT16, BASE_DEC, NULL, 0x0, "VC FEC Interface Paramater MTU", HFILL }},
 
-      case LDP_LABEL_RELEASE:
+    {&hf_ldp_tlv_fec_vc_intparam_id,
+     {"ID", "ldp.msg.tlv.fec.vc.intparam.id", FT_UINT8, BASE_HEX, VALS(fec_vc_interfaceparm), 0x0, "VC FEC Interface Paramater ID", HFILL }},
 
-       dissect_ldp_label_release(tvb, offset + 8, pinfo, msg_tree, msg_len - 4);
+    {&hf_ldp_tlv_fec_vc_intparam_maxcatmcells,
+     {"Number of Cells", "ldp.msg.tlv.fec.vc.intparam.maxatm", FT_UINT16, BASE_DEC, NULL, 0x0, "VC FEC Interface Param Max ATM Concat Cells", HFILL }},
 
-       break;
+    { &hf_ldp_tlv_fec_vc_intparam_desc,
+      { "Description", "ldp.msg.tlv.fec.vc.intparam.desc", FT_STRING, BASE_DEC, NULL, 0, "VC FEC Interface Description", HFILL }},
 
-      case LDP_LABEL_ABORT_REQUEST:
+    { &hf_ldp_tlv_fec_vc_intparam_cembytes,
+     {"Payload Bytes", "ldp.msg.tlv.fec.vc.intparam.cembytes", FT_UINT16, BASE_DEC, NULL, 0x0, "VC FEC Interface Param CEM Payload Bytes", HFILL }},
 
-       dissect_ldp_label_abort_request(tvb, offset + 8, pinfo, msg_tree, msg_len - 4);
+    { &hf_ldp_tlv_fec_vc_intparam_vpnid_oui,
+      { "VPN OUI", "ldp.msg.tlv.fec.vc.intparam.vpnid.oui", FT_UINT24, BASE_HEX, NULL, 0x0, "VC FEC Interface Param VPN OUI", HFILL }},
 
-       break;
+    { &hf_ldp_tlv_fec_vc_intparam_vpnid_index,
+      { "VPN Index", "ldp.msg.tlv.fec.vc.intparam.vpnid.index", FT_UINT32, BASE_HEX, NULL, 0x0, "VC FEC Interface Param VPN Index", HFILL }},
 
-      default:
+    { &hf_ldp_tlv_lspid_act_flg,
+      { "Action Indicator Flag", "ldp.msg.tlv.lspid.actflg", FT_UINT16, BASE_HEX, VALS(ldp_act_flg_vals), 0x000F, "Action Indicator Flag", HFILL}},
 
-       /* Some sort of unknown message, treat as undissected data */
+    { &hf_ldp_tlv_lspid_cr_lsp,
+      { "Local CR-LSP ID", "ldp.msg.tlv.lspid.locallspid", FT_UINT16, BASE_HEX, NULL, 0x0, "Local CR-LSP ID", HFILL}},
 
-       break;
+    { &hf_ldp_tlv_lspid_ldpid,
+      { "Ingress LSR Router ID", "ldp.msg.tlv.lspid.lsrid", FT_IPv4, BASE_DEC, NULL, 0x0, "Ingress LSR Router ID", HFILL}},
 
-      }
-    
-    }
+    { &hf_ldp_tlv_er_hop_loose,
+      { "Loose route bit", "ldp.msg.tlv.er_hop.loose", FT_UINT24, BASE_HEX, VALS(ldp_loose_vals), 0x800000, "Loose route bit", HFILL}},
 
-    offset += msg_len + 4;
+    { &hf_ldp_tlv_er_hop_prelen,
+      { "Prefix length", "ldp.msg.tlv.er_hop.prefixlen", FT_UINT8, BASE_DEC, NULL, 0x0, "Prefix len", HFILL}},
 
-  }
-}
+    { &hf_ldp_tlv_er_hop_prefix4,
+      { "IPv4 Address", "ldp.msg.tlv.er_hop.prefix4", FT_IPv4, BASE_DEC, NULL, 0x0, "IPv4 Address", HFILL}},
+   { &hf_ldp_tlv_er_hop_prefix6,
+     { "IPv6 Address", "ldp.msg.tlv.er_hop.prefix6", FT_IPv6, BASE_DEC, NULL, 0x0, "IPv6 Address", HFILL}},
 
-/* Register all the bits needed with the filtering engine */
+    { &hf_ldp_tlv_er_hop_as,
+      { "AS Number", "ldp.msg.tlv.er_hop.as", FT_UINT16, BASE_DEC, NULL, 0x0, "AS Number", HFILL}},
 
-void 
-proto_register_ldp(void)
-{
-  static hf_register_info hf[] = {
-    { &hf_ldp_req,
-      /* Change the following to the type you need */
-      { "Request", "ldp.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "" }},
+    { &hf_ldp_tlv_er_hop_cr_lsp,
+      { "Local CR-LSP ID", "ldp.msg.tlv.er_hop.locallspid", FT_UINT16, BASE_DEC, NULL, 0x0, "Local CR-LSP ID", HFILL}},
 
-    { &hf_ldp_rsp,
-      { "Response", "ldp.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "" }},
+    { &hf_ldp_tlv_er_hop_ldpid,
+      { "Local CR-LSP ID", "ldp.msg.tlv.er_hop.lsrid", FT_IPv4, BASE_DEC, NULL, 0x0, "Local CR-LSP ID", HFILL}},
 
-    { &hf_ldp_version,
-      { "Version", "ldp.hdr.version", FT_UINT16, BASE_DEC, NULL, 0x0, "LDP Version Number" }},
+    { &hf_ldp_tlv_flags_reserv,
+      { "Reserved", "ldp.msg.tlv.flags_reserv", FT_UINT8, BASE_HEX, NULL, 0xC0, "Reserved", HFILL}},
 
-    { &hf_ldp_pdu_len,
-      { "PDU Length", "ldp.hdr.pdu_len", FT_UINT16, BASE_DEC, NULL, 0x0, "LDP PDU Length"}},
+    { &hf_ldp_tlv_flags_pdr,
+      { "PDR", "ldp.msg.tlv.flags_pdr", FT_BOOLEAN, 8, TFS(&tlv_negotiable), 0x1, "PDR negotiability flag", HFILL}},
 
-    { &hf_ldp_lsr,
-      { "LSR ID", "ldp.hdr.ldpid.lsr", FT_UINT32, BASE_HEX, NULL, 0x0, "LDP Label Space Router ID"}},
+    { &hf_ldp_tlv_flags_pbs,
+      { "PBS", "ldp.msg.tlv.flags_pbs", FT_BOOLEAN, 8, TFS(&tlv_negotiable), 0x2, "PBS negotiability flag", HFILL}},
 
-    { &hf_ldp_ls_id,
-      { "Label Space ID", "ldp.hdr.ldpid.lsid", FT_UINT16, BASE_HEX, NULL, 0x0, "LDP Label Space ID"}},
+    { &hf_ldp_tlv_flags_cdr,
+      { "CDR", "ldp.msg.tlv.flags_cdr", FT_BOOLEAN, 8, TFS(&tlv_negotiable), 0x4, "CDR negotiability flag", HFILL}},
 
-    { &hf_ldp_msg_type,
-      { "Message Type", "ldp.msg.type", FT_UINT16, BASE_HEX, VALS(ldp_message_types), 0x0, "LDP message type"}},
+    { &hf_ldp_tlv_flags_cbs,
+      { "CBS", "ldp.msg.tlv.flags_cbs", FT_BOOLEAN, 8, TFS(&tlv_negotiable), 0x8, "CBS negotiability flag", HFILL}},
 
-    { &hf_ldp_msg_len,
-      { "Message Length", "ldp.msg.len", FT_UINT16, BASE_DEC, NULL, 0x0, "LDP Message Length (excluding message type and len)"}},
+    { &hf_ldp_tlv_flags_ebs,
+      { "EBS", "ldp.msg.tlv.flags_ebs", FT_BOOLEAN, 8, TFS(&tlv_negotiable), 0x10, "EBS negotiability flag", HFILL}},
 
-    { &hf_ldp_msg_id, 
-      { "Message ID", "ldp.msg.id", FT_UINT32, BASE_HEX, NULL, 0x0, "LDP Message ID"}},
+    { &hf_ldp_tlv_flags_weight,
+      { "Weight", "ldp.msg.tlv.flags_weight", FT_BOOLEAN, 8, TFS(&tlv_negotiable), 0x20, "Weight negotiability flag", HFILL}},
 
-    { &hf_ldp_tlv_type, 
-      { "TLV Type", "ldp.msg.tlv.type", FT_UINT16, BASE_HEX, VALS(tlv_type_names), 0x0, "TLV Type Field"}},
+    { &hf_ldp_tlv_frequency,
+      { "Frequency", "ldp.msg.tlv.frequency", FT_UINT8, BASE_DEC, VALS(freq_values), 0, "Frequency", HFILL}},
 
-    { &hf_ldp_tlv_len,
-      {"TLV Length", "ldp.msg.tlv.len", FT_UINT16, BASE_DEC, NULL, 0x0, "TLV Length Field"}},
+    { &hf_ldp_tlv_weight,
+      { "Weight", "ldp.msg.tlv.weight", FT_UINT8, BASE_DEC, NULL, 0, "Weight of the CR-LSP", HFILL}},
 
-    { &hf_ldp_tlv_value,
-      { "TLV Value", "ldp.msg.tlv.value", FT_BYTES, BASE_NONE, NULL, 0x0, "TLV Value Bytes"}},
+    { &hf_ldp_tlv_pdr,
+      { "PDR", "ldp.msg.tlv.pdr", FT_DOUBLE, BASE_NONE, NULL, 0, "Peak Data Rate", HFILL}},
 
-    { &hf_ldp_tlv_val_hold,
-      { "Hold Time", "ldp.msg.tlv.hello.hold", FT_UINT16, BASE_DEC, NULL, 0x0, "Hello Common Parameters Hold Time"}},
+    { &hf_ldp_tlv_pbs,
+      { "PBS", "ldp.msg.tlv.pbs", FT_DOUBLE, BASE_NONE, NULL, 0, "Peak Burst Size", HFILL}},
 
-    { &hf_ldp_tlv_val_target,
-      { "Targeted Hello", "ldp.msg.tlv.hello.targeted", FT_BOOLEAN, 8, TFS(&hello_targeted_vals), 0x80, "Hello Common Parameters Targeted Bit"}},
+    { &hf_ldp_tlv_cdr,
+      { "CDR", "ldp.msg.tlv.cdr", FT_DOUBLE, BASE_NONE, NULL, 0, "Committed Data Rate", HFILL}},
 
-    { &hf_ldp_tlv_val_request,
-      { "Hello Requested", "ldp,msg.tlv.hello.requested", FT_BOOLEAN, 8, TFS(&hello_requested_vals), 0x40, "Hello Common Parameters Hello Requested Bit" }},
-    { &hf_ldp_tlv_val_res,
-      { "Reserved", "ldp.msg.tlv.hello.res", FT_UINT16, BASE_HEX, NULL, 0x3FFF, "Hello Common Parameters Reserved Field"}},
+    { &hf_ldp_tlv_cbs,
+      { "CBS", "ldp.msg.tlv.cbs", FT_DOUBLE, BASE_NONE, NULL, 0, "Committed Burst Size", HFILL}},
 
-    { &hf_ldp_tlv_config_seqno,
-      { "Configuration Sequence Number", "ldp.msg.tlv.hello.cnf_seqno", FT_UINT32, BASE_HEX, NULL, 0x0, "Hello COnfiguration Sequence Number"}},
+    { &hf_ldp_tlv_ebs,
+      { "EBS", "ldp.msg.tlv.ebs", FT_DOUBLE, BASE_NONE, NULL, 0, "Excess Burst Size", HFILL}},
 
-    { &hf_ldp_tlv_fec_wc,
-      { "FEC Element Type", "ldp.msg.tlv.fec.type", FT_UINT8, BASE_DEC, VALS(fec_types), 0x0, "Forwarding Equivalence Class Element Types"}},
+    { &hf_ldp_tlv_set_prio,
+      { "Set Prio", "ldp.msg.tlv.set_prio", FT_UINT8, BASE_DEC, NULL, 0, "LSP setup priority", HFILL}},
 
-    { &hf_ldp_tlv_fec_af,
-      { "FEC Element Address Type", "ldp.msg.tlv.fec.af", FT_UINT16, BASE_DEC, VALS(fec_af_types), 0x0, "Forwarding Equivalence Class Element Address Family"}},
+    { &hf_ldp_tlv_hold_prio,
+      { "Hold Prio", "ldp.msg.tlv.hold_prio", FT_UINT8, BASE_DEC, NULL, 0, "LSP hold priority", HFILL}},
 
-    { &hf_ldp_tlv_fec_len,
-      { "FEC Element Length", "ldp.msg.tlv.fec.len", FT_UINT8, BASE_DEC, NULL, 0x0, "Forwarding Equivalence Class Element Length"}},
+    { &hf_ldp_tlv_route_pinning,
+      { "Route Pinning", "ldp.msg.tlv.route_pinning", FT_UINT32, BASE_DEC, VALS(route_pinning_vals), 0x80000000, "Route Pinning", HFILL}},
 
-    { &hf_ldp_tlv_fec_pfval,
-      { "FEC Element Prefix Value", "ldp.msg.tlv.fec.pfval", FT_UINT32, BASE_HEX, NULL, 0x0, "Forwarding Equivalence Class Element Prefix"}},
+    { &hf_ldp_tlv_resource_class,
+      { "Resource Class", "ldp.msg.tlv.resource_class", FT_UINT32, BASE_HEX, NULL, 0, "Resource Class (Color)", HFILL}},
 
   };
+
   static gint *ett[] = {
     &ett_ldp,
     &ett_ldp_header,
@@ -771,46 +2545,55 @@ proto_register_ldp(void)
     &ett_ldp_tlv,
     &ett_ldp_tlv_val,
     &ett_ldp_fec,
+    &ett_ldp_fec_vc_interfaceparam
   };
-  module_t *ldp_module; 
+  module_t *ldp_module;
+
+  proto_ldp = proto_register_protocol("Label Distribution Protocol",
+                                      "LDP", "ldp");
+
+  proto_register_field_array(proto_ldp, hf, array_length(hf));
+  proto_register_subtree_array(ett, array_length(ett));
 
   /* Register our configuration options for , particularly our port */
 
-  ldp_module = prefs_register_module("ldp", "LDP", proto_reg_handoff_ldp);
+  ldp_module = prefs_register_protocol(proto_ldp, proto_reg_handoff_ldp);
 
   prefs_register_uint_preference(ldp_module, "tcp.port", "LDP TCP Port",
-                                "Set the port for  messages (if other"
+                                "Set the TCP port for messages (if other"
                                 " than the default of 646)",
                                 10, &global_ldp_tcp_port);
 
   prefs_register_uint_preference(ldp_module, "udp.port", "LDP UDP Port",
-                                "Set the port for  messages (if other"
+                                "Set the UDP port for messages (if other"
                                 " than the default of 646)",
                                 10, &global_ldp_udp_port);
 
-  proto_ldp = proto_register_protocol("Label Distribution Protocol",
-                                      "ldp");
-
-  proto_register_field_array(proto_ldp, hf, array_length(hf));
-  proto_register_subtree_array(ett, array_length(ett));
-
+  prefs_register_bool_preference(ldp_module, "desegment_ldp_messages",
+    "Desegment all LDP messages spanning multiple TCP segments",
+    "Whether the LDP dissector should desegment all messages spanning multiple TCP segments",
+    &ldp_desegment);
 }
 
 /* The registration hand-off routine */
 void
 proto_reg_handoff_ldp(void)
 {
-  static int ldp_prefs_initialized = FALSE;
+  static gboolean ldp_prefs_initialized = FALSE;
+  static dissector_handle_t ldp_tcp_handle, ldp_handle;
+
+  if (!ldp_prefs_initialized) {
 
-  if (ldp_prefs_initialized) {
+    ldp_tcp_handle = new_create_dissector_handle(dissect_ldp_tcp, proto_ldp);
+    ldp_handle = new_create_dissector_handle(dissect_ldp, proto_ldp);
 
-    dissector_delete("tcp.port", tcp_port, dissect_ldp);
-    dissector_delete("udp.port", udp_port, dissect_ldp);
+    ldp_prefs_initialized = TRUE;
 
   }
   else {
 
-    ldp_prefs_initialized = TRUE;
+    dissector_delete("tcp.port", tcp_port, ldp_tcp_handle);
+    dissector_delete("udp.port", udp_port, ldp_handle);
 
   }
 
@@ -819,7 +2602,7 @@ proto_reg_handoff_ldp(void)
   tcp_port = global_ldp_tcp_port;
   udp_port = global_ldp_udp_port;
 
-  dissector_add("tcp.port", global_ldp_tcp_port, dissect_ldp);
-  dissector_add("udp.port", global_ldp_udp_port, dissect_ldp);
+  dissector_add("tcp.port", global_ldp_tcp_port, ldp_tcp_handle);
+  dissector_add("udp.port", global_ldp_udp_port, ldp_handle);
 
 }