Logo Search packages:      
Sourcecode: wireshark version File versions

packet-bootp.c

/* packet-bootp.c
 * Routines for BOOTP/DHCP packet disassembly
 * Copyright 1998, Gilbert Ramirez <gram@alumni.rice.edu>
 * Copyright 2004, Thomas Anders <thomas.anders [AT] blue-cable.de>
 *
 * $Id: packet-bootp.c 21990 2007-05-29 18:52:42Z guy $
 *
 * The information used comes from:
 * RFC  951: Bootstrap Protocol
 * RFC 1497: BOOTP extensions
 * RFC 1542: Clarifications and Extensions for the Bootstrap Protocol
 * RFC 2131: Dynamic Host Configuration Protocol
 * RFC 2132: DHCP Options and BOOTP Vendor Extensions
 * RFC 2241: DHCP Options for Novell Directory Services
 * RFC 2242: NetWare/IP Domain Name and Information
 * RFC 2489: Procedure for Defining New DHCP Options
 * RFC 2610: DHCP Options for Service Location Protocol
 * RFC 3046: DHCP Relay Agent Information Option
 * RFC 3118: Authentication for DHCP Messages
 * RFC 3203: DHCP reconfigure extension
 * RFC 3495: DHCP Option (122) for CableLabs Client Configuration
 * RFC 3594: PacketCable Security Ticket Control Sub-Option (122.9)
 * draft-ietf-dhc-fqdn-option-07.txt
 * BOOTP and DHCP Parameters
 *     http://www.iana.org/assignments/bootp-dhcp-parameters
 * DOCSIS(TM) 2.0 Radio Frequency Interface Specification
 *     http://www.cablemodem.com/downloads/specs/CM-SP-RFI2.0-I10-051209.pdf
 * PacketCable(TM) 1.0 MTA Device Provisioning Specification
 *     http://www.packetcable.com/downloads/specs/PKT-SP-PROV-I11-050812.pdf
 *     http://www.cablelabs.com/specifications/archives/PKT-SP-PROV-I05-021127.pdf (superseded by above)
 * PacketCable(TM) 1.5 MTA Device Provisioning Specification
 *     http://www.packetcable.com/downloads/specs/PKT-SP-PROV1.5-I02-050812.pdf
 * CableHome(TM) 1.1 Specification
 *     http://www.cablelabs.com/projects/cablehome/downloads/specs/CH-SP-CH1.1-I11-060407.pdf
 * DSL Forum TR-111
 *     http://www.dslforum.org/techwork/tr/TR-111.pdf
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

/*
 * Some of the development of the BOOTP/DHCP protocol decoder was sponsored by
 * Cable Television Laboratories, Inc. ("CableLabs") based upon proprietary
 * CableLabs' specifications. Your license and use of this protocol decoder
 * does not mean that you are licensed to use the CableLabs'
 * specifications.  If you have questions about this protocol, contact
 * jf.mule [AT] cablelabs.com or c.stuart [AT] cablelabs.com for additional
 * information.
 */


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

#include <string.h>
#include <glib.h>
#include <epan/packet.h>
#include "packet-arp.h"
#include "packet-dns.h"                   /* for get_dns_name() */
#include <epan/addr_resolv.h>
#include <epan/prefs.h>
#include <epan/tap.h>
#include <epan/strutil.h>
#include <epan/arptypes.h>
#include <epan/emem.h>
#include <epan/sminmpec.h>

static int bootp_dhcp_tap = -1;
static int proto_bootp = -1;
static int hf_bootp_type = -1;
static int hf_bootp_hw_type = -1;
static int hf_bootp_hw_len = -1;
static int hf_bootp_hops = -1;
static int hf_bootp_id = -1;
static int hf_bootp_secs = -1;
static int hf_bootp_flags = -1;
static int hf_bootp_flags_broadcast = -1;
static int hf_bootp_flags_reserved = -1;
static int hf_bootp_ip_client = -1;
static int hf_bootp_ip_your = -1;
static int hf_bootp_ip_server = -1;
static int hf_bootp_ip_relay = -1;
static int hf_bootp_hw_addr = -1;
static int hf_bootp_server = -1;
static int hf_bootp_file = -1;
static int hf_bootp_cookie = -1;
static int hf_bootp_vendor = -1;
static int hf_bootp_dhcp = -1;
static int hf_bootp_fqdn_s = -1;
static int hf_bootp_fqdn_o = -1;
static int hf_bootp_fqdn_e = -1;
static int hf_bootp_fqdn_n = -1;
static int hf_bootp_fqdn_mbz = -1;
static int hf_bootp_fqdn_rcode1 = -1;
static int hf_bootp_fqdn_rcode2 = -1;
static int hf_bootp_fqdn_name = -1;
static int hf_bootp_fqdn_asciiname = -1;
static int hf_bootp_pkt_mtacap_len = -1;
static int hf_bootp_docsis_cmcap_len = -1;
static int hf_bootp_hw_ether_addr = -1;
static int hf_bootp_alcatel_vid = -1;
static int hf_bootp_client_identifier_uuid = -1;
static int hf_bootp_client_network_id_major_ver = -1;
static int hf_bootp_client_network_id_minor_ver = -1;
static int hf_bootp_option_type = -1;
static int hf_bootp_option_length = -1;
static int hf_bootp_option_value = -1;

static gint ett_bootp = -1;
static gint ett_bootp_flags = -1;
static gint ett_bootp_option = -1;
static gint ett_bootp_fqdn = -1;

gboolean novell_string = FALSE;

#define UDP_PORT_BOOTPS  67
#define UDP_PORT_BOOTPC  68

#define BOOTP_BC  0x8000
#define BOOTP_MBZ 0x7FFF

/* FQDN stuff */
#define F_FQDN_S  0x01
#define F_FQDN_O  0x02
#define F_FQDN_E  0x04
#define F_FQDN_N  0x08
#define F_FQDN_MBZ      0xf0

static const true_false_string tfs_fqdn_s = {
  "Server",
  "Client"
};

static const true_false_string tfs_fqdn_o = {
  "Override",
  "No override"
};

static const true_false_string tfs_fqdn_e = {
  "Binary encoding",
  "ASCII encoding"
};

static const true_false_string tfs_fqdn_n = {
  "No server updates",
  "Some server updates"
};

enum field_type {
      special,
      none,
      presence,
      ipv4,             /* single IPv4 address */
      ipv4_list,        /* list of IPv4 addresses */
      string,
      bytes,
      opaque,
      val_boolean,
      val_u_byte,
      val_u_short,
      val_u_short_list,
      val_u_le_short,
      val_u_long,
      time_in_secs,
      fqdn,
      ipv4_or_fqdn
};

struct opt_info {
      const char  *text;
      enum field_type ftype;
      const void  *data;
};

static const true_false_string flag_set_broadcast = {
  "Broadcast",
  "Unicast"
};


/* PacketCable/DOCSIS definitions */
#define PACKETCABLE_MTA_CAP10 "pktc1.0:"
#define PACKETCABLE_MTA_CAP15 "pktc1.5:"
#define PACKETCABLE_CM_CAP11  "docsis1.1:"
#define PACKETCABLE_CM_CAP20  "docsis2.0:"

#define PACKETCABLE_CCC_I05      1
#define PACKETCABLE_CCC_DRAFT5   2
#define PACKETCABLE_CCC_RFC_3495 3

static enum_val_t pkt_ccc_protocol_versions[] = {
      { "ccc_i05",     "PKT-SP-PROV-I05-021127", PACKETCABLE_CCC_I05 },
      { "ccc_draft_5", "IETF Draft 5",           PACKETCABLE_CCC_DRAFT5 },
      { "rfc_3495",    "RFC 3495",               PACKETCABLE_CCC_RFC_3495 },
      { NULL, NULL, 0 }
};

static gint pkt_ccc_protocol_version = PACKETCABLE_CCC_RFC_3495;
static guint pkt_ccc_option = 122;


static int dissect_vendor_pxeclient_suboption(proto_tree *v_tree, tvbuff_t *tvb,
    int optoff, int optend);
static int dissect_vendor_cablelabs_suboption(proto_tree *v_tree, tvbuff_t *tvb,
    int optoff, int optend);
static int dissect_netware_ip_suboption(proto_tree *v_tree, tvbuff_t *tvb,
    int optoff, int optend);
static int dissect_vendor_tr111_suboption(proto_tree *v_tree, tvbuff_t *tvb,
    int optoff, int optend);
static int bootp_dhcp_decode_agent_info(proto_tree *v_tree, tvbuff_t *tvb,
    int optoff, int optend);
static void dissect_packetcable_mta_cap(proto_tree *v_tree, tvbuff_t *tvb,
       int voff, int len);
static void dissect_docsis_cm_cap(proto_tree *v_tree, tvbuff_t *tvb,
       int voff, int len);
static int dissect_packetcable_i05_ccc(proto_tree *v_tree, tvbuff_t *tvb,
    int optoff, int optend);
static int dissect_packetcable_ietf_ccc(proto_tree *v_tree, tvbuff_t *tvb,
    int optoff, int optend, int revision);

#define OPT53_DISCOVER "Discover"
static const value_string opt53_text[] = {
      { 1,  OPT53_DISCOVER },
      { 2,  "Offer" },
      { 3,  "Request" },
      { 4,  "Decline" },
      { 5,  "ACK" },
      { 6,  "NAK" },
      { 7,  "Release" },
      { 8,  "Inform" },
      { 9,  "Force Renew" },
      /* draft-ietf-dhc-leasequery-09.txt */
      { 13, "Lease query" },
      { 14, "Lease known" },
      { 15, "Lease unknown" },
      { 16, "Lease active" },
      { 17, "Unimplemented" },

      { 0,  NULL }
};

/* DHCP Authentication protocols */
#define AUTHEN_PROTO_CONFIG_TOKEN   0
#define AUTHEN_PROTO_DELAYED_AUTHEN 1

/* DHCP Authentication algorithms for delayed authentication */
#define AUTHEN_DELAYED_ALGO_HMAC_MD5      1

/* DHCP Authentication Replay Detection Methods */
#define AUTHEN_RDM_MONOTONIC_COUNTER      0x00

/* DHCP Option Overload (option code 52) */
#define OPT_OVERLOAD_FILE           1
#define OPT_OVERLOAD_SNAME          2
#define OPT_OVERLOAD_BOTH           3

/* Server name and boot file offsets and lengths */
#define SERVER_NAME_OFFSET          44
#define SERVER_NAME_LEN             64
#define FILE_NAME_OFFSET            108
#define FILE_NAME_LEN               128
#define VENDOR_INFO_OFFSET          236

static const true_false_string toggle_tfs = {
      "Enabled",
      "Disabled"
};

static const true_false_string yes_no_tfs = {
      "Yes",
      "No"
};

static const value_string bootp_nbnt_vals[] = {
    {0x1,   "B-node" },
    {0x2,   "P-node" },
    {0x4,   "M-node" },
    {0x8,   "H-node" },
    {0,     NULL     }
};

static const value_string bootp_client_arch[] = {
      { 0x0000, "IA x86 PC" },
      { 0x0001, "NEC/PC98" },
      { 0x0002, "IA64 PC" },
      { 0x0003, "DEC Alpha" },
      { 0x0004, "ArcX86" },
      { 0x0005, "Intel Lean Client" },
      { 0,      NULL }
};

static struct opt_info bootp_opt[] = {
/*   0 */ { "Padding",                          none, NULL },
/*   1 */ { "Subnet Mask",                      ipv4, NULL },
/*   2 */ { "Time Offset",                      time_in_secs, NULL },
/*   3 */ { "Router",                           ipv4_list, NULL },
/*   4 */ { "Time Server",                      ipv4_list, NULL },
/*   5 */ { "Name Server",                      ipv4_list, NULL },
/*   6 */ { "Domain Name Server",               ipv4_list, NULL },
/*   7 */ { "Log Server",                       ipv4_list, NULL },
/*   8 */ { "Cookie Server",                    ipv4_list, NULL },
/*   9 */ { "LPR Server",                       ipv4_list, NULL },
/*  10 */ { "Impress Server",                   ipv4_list, NULL },
/*  11 */ { "Resource Location Server",               ipv4_list, NULL },
/*  12 */ { "Host Name",                        string, NULL },
/*  13 */ { "Boot File Size",                   val_u_short, NULL },
/*  14 */ { "Merit Dump File",                        string, NULL },
/*  15 */ { "Domain Name",                      string, NULL },
/*  16 */ { "Swap Server",                      ipv4, NULL },
/*  17 */ { "Root Path",                        string, NULL },
/*  18 */ { "Extensions Path",                        string, NULL },
/*  19 */ { "IP Forwarding",                    val_boolean, TFS(&toggle_tfs) },
/*  20 */ { "Non-Local Source Routing",               val_boolean, TFS(&toggle_tfs) },
/*  21 */ { "Policy Filter",                    special, NULL },
/*  22 */ { "Maximum Datagram Reassembly Size",       val_u_short, NULL },
/*  23 */ { "Default IP Time-to-Live",                val_u_byte, NULL },
/*  24 */ { "Path MTU Aging Timeout",                 time_in_secs, NULL },
/*  25 */ { "Path MTU Plateau Table",                 val_u_short_list, NULL },
/*  26 */ { "Interface MTU",                    val_u_short, NULL },
/*  27 */ { "All Subnets are Local",                  val_boolean, TFS(&yes_no_tfs) },
/*  28 */ { "Broadcast Address",                ipv4, NULL },
/*  29 */ { "Perform Mask Discovery",                 val_boolean, TFS(&toggle_tfs) },
/*  30 */ { "Mask Supplier",                    val_boolean, TFS(&yes_no_tfs) },
/*  31 */ { "Perform Router Discover",                val_boolean, TFS(&toggle_tfs) },
/*  32 */ { "Router Solicitation Address",            ipv4, NULL },
/*  33 */ { "Static Route",                     special, NULL },
/*  34 */ { "Trailer Encapsulation",                  val_boolean, TFS(&toggle_tfs) },
/*  35 */ { "ARP Cache Timeout",                time_in_secs, NULL },
/*  36 */ { "Ethernet Encapsulation",                 val_boolean, TFS(&toggle_tfs) },
/*  37 */ { "TCP Default TTL",                        val_u_byte, NULL },
/*  38 */ { "TCP Keepalive Interval",                 time_in_secs, NULL },
/*  39 */ { "TCP Keepalive Garbage",                  val_boolean, TFS(&toggle_tfs) },
/*  40 */ { "Network Information Service Domain",     string, NULL },
/*  41 */ { "Network Information Service Servers",    ipv4_list, NULL },
/*  42 */ { "Network Time Protocol Servers",          ipv4_list, NULL },
/*  43 */ { "Vendor-Specific Information",            special, NULL },
/*  44 */ { "NetBIOS over TCP/IP Name Server",        ipv4_list, NULL },
/*  45 */ { "NetBIOS over TCP/IP Datagram Distribution Name Server", ipv4_list, NULL },
/*  46 */ { "NetBIOS over TCP/IP Node Type",          val_u_byte, VALS(bootp_nbnt_vals) },
/*  47 */ { "NetBIOS over TCP/IP Scope",        string, NULL },
/*  48 */ { "X Window System Font Server",            ipv4_list, NULL },
/*  49 */ { "X Window System Display Manager",        ipv4_list, NULL },
/*  50 */ { "Requested IP Address",             ipv4, NULL },
/*  51 */ { "IP Address Lease Time",                  time_in_secs, NULL },
/*  52 */ { "Option Overload",                        special, NULL },
/*  53 */ { "DHCP Message Type",                special, NULL },
/*  54 */ { "Server Identifier",                ipv4, NULL },
/*  55 */ { "Parameter Request List",                 special, NULL },
/*  56 */ { "Message",                          string, NULL },
/*  57 */ { "Maximum DHCP Message Size",        val_u_short, NULL },
/*  58 */ { "Renewal Time Value",               time_in_secs, NULL },
/*  59 */ { "Rebinding Time Value",             time_in_secs, NULL },
/*  60 */ { "Vendor class identifier",                special, NULL },
/*  61 */ { "Client identifier",                special, NULL },
/*  62 */ { "Novell/Netware IP domain",               string, NULL },
/*  63 */ { "Novell Options",                   special, NULL },
/*  64 */ { "Network Information Service+ Domain",    string, NULL },
/*  65 */ { "Network Information Service+ Servers",   ipv4_list, NULL },
/*  66 */ { "TFTP Server Name",                       string, NULL },
/*  67 */ { "Bootfile name",                    string, NULL },
/*  68 */ { "Mobile IP Home Agent",             ipv4_list, NULL },
/*  69 */ { "SMTP Server",                      ipv4_list, NULL },
/*  70 */ { "POP3 Server",                      ipv4_list, NULL },
/*  71 */ { "NNTP Server",                      ipv4_list, NULL },
/*  72 */ { "Default WWW Server",               ipv4_list, NULL },
/*  73 */ { "Default Finger Server",                  ipv4_list, NULL },
/*  74 */ { "Default IRC Server",               ipv4_list, NULL },
/*  75 */ { "StreetTalk Server",                ipv4_list, NULL },
/*  76 */ { "StreetTalk Directory Assistance Server", ipv4_list, NULL },
/*  77 */ { "User Class Information",                 opaque, NULL },
/*  78 */ { "Directory Agent Information",            special, NULL },
/*  79 */ { "Service Location Agent Scope",           special, NULL },
/*  80 */ { "Naming Authority",                       opaque, NULL },
/*  81 */ { "Client Fully Qualified Domain Name",     special, NULL },
/*  82 */ { "Agent Information Option",                 special, NULL },
/*  83 */ { "Unassigned",                       opaque, NULL },
/*  84 */ { "Unassigned",                       opaque, NULL },
/*  85 */ { "Novell Directory Services Servers",      special, NULL },
/*  86 */ { "Novell Directory Services Tree Name",    string, NULL },
/*  87 */ { "Novell Directory Services Context",      string, NULL },
/*  88 */ { "IEEE 1003.1 POSIX Timezone",       opaque, NULL },
/*  89 */ { "Fully Qualified Domain Name",            opaque, NULL },
/*  90 */ { "Authentication",                   special, NULL },
/*  91 */ { "Vines TCP/IP Server Option",       opaque, NULL },
/*  92 */ { "Server Selection Option",                opaque, NULL },
/*  93 */ { "Client System Architecture",       val_u_short, VALS(bootp_client_arch) },
/*  94 */ { "Client Network Device Interface",        special, NULL },
/*  95 */ { "Lightweight Directory Access Protocol",  opaque, NULL },
/*  96 */ { "IPv6 Transitions",                       opaque, NULL },
/*  97 */ { "UUID/GUID-based Client Identifier",      special, NULL },
/*  98 */ { "Open Group's User Authentication",       opaque, NULL },
/*  99 */ { "Unassigned",                       opaque, NULL },
/* 100 */ { "Printer Name",                     opaque, NULL },
/* 101 */ { "MDHCP multicast address",                opaque, NULL },
/* 102 */ { "Removed/unassigned",               opaque, NULL },
/* 103 */ { "Removed/unassigned",               opaque, NULL },
/* 104 */ { "Removed/unassigned",               opaque, NULL },
/* 105 */ { "Removed/unassigned",               opaque, NULL },
/* 106 */ { "Removed/unassigned",               opaque, NULL },
/* 107 */ { "Removed/unassigned",               opaque, NULL },
/* 108 */ { "Swap Path Option",                       opaque, NULL },
/* 109 */ { "Unassigned",                       opaque, NULL },
/* 110 */ { "IPX Compability",                        opaque, NULL },
/* 111 */ { "Unassigned",                       opaque, NULL },
/* 112 */ { "NetInfo Parent Server Address",          ipv4_list, NULL },
/* 113 */ { "NetInfo Parent Server Tag",        string, NULL },
/* 114 */ { "URL",                              opaque, NULL },
/* 115 */ { "DHCP Failover Protocol",                 opaque, NULL },
/* 116 */ { "DHCP Auto-Configuration",                opaque, NULL },
/* 117 */ { "Name Service Search",                    opaque, NULL },
/* 118 */ { "Subnet Selection Option",                      ipv4_list, NULL },
/* 119 */ { "Domain Search",                    opaque, NULL },
/* 120 */ { "SIP Servers",                      opaque, NULL },
/* 121 */ { "Classless Static Route",                       opaque, NULL },
/* 122 */ { "CableLabs Client Configuration",         opaque, NULL },
/* 123 */ { "Unassigned",                       opaque, NULL },
/* 124 */ { "V-I Vendor Class",                       opaque, NULL },
/* 125 */ { "V-I Vendor-specific Information",        opaque, NULL },
/* 126 */ { "Extension",                        opaque, NULL },
/* 127 */ { "Extension",                        opaque, NULL },
/* 128 */ { "Private",                          opaque, NULL },
/* 129 */ { "Private",                          opaque, NULL },
/* 130 */ { "Private",                          opaque, NULL },
/* 131 */ { "Private",                          opaque, NULL },
/* 132 */ { "Private",                          opaque, NULL },
/* 133 */ { "Private",                          opaque, NULL },
/* 134 */ { "Private",                          opaque, NULL },
/* 135 */ { "Private",                          opaque, NULL },
/* 136 */ { "Private",                          opaque, NULL },
/* 137 */ { "Private",                          opaque, NULL },
/* 138 */ { "Private",                          opaque, NULL },
/* 139 */ { "Private",                          opaque, NULL },
/* 140 */ { "Private",                          opaque, NULL },
/* 141 */ { "Private",                          opaque, NULL },
/* 142 */ { "Private",                          opaque, NULL },
/* 143 */ { "Private",                          opaque, NULL },
/* 144 */ { "Private",                          opaque, NULL },
/* 145 */ { "Private",                          opaque, NULL },
/* 146 */ { "Private",                          opaque, NULL },
/* 147 */ { "Private",                          opaque, NULL },
/* 148 */ { "Private",                          opaque, NULL },
/* 149 */ { "Private",                          opaque, NULL },
/* 150 */ { "Private",                          opaque, NULL },
/* 151 */ { "Private",                          opaque, NULL },
/* 152 */ { "Private",                          opaque, NULL },
/* 153 */ { "Private",                          opaque, NULL },
/* 154 */ { "Private",                          opaque, NULL },
/* 155 */ { "Private",                          opaque, NULL },
/* 156 */ { "Private",                          opaque, NULL },
/* 157 */ { "Private",                          opaque, NULL },
/* 158 */ { "Private",                          opaque, NULL },
/* 159 */ { "Private",                          opaque, NULL },
/* 160 */ { "Private",                          opaque, NULL },
/* 161 */ { "Private",                          opaque, NULL },
/* 162 */ { "Private",                          opaque, NULL },
/* 163 */ { "Private",                          opaque, NULL },
/* 164 */ { "Private",                          opaque, NULL },
/* 165 */ { "Private",                          opaque, NULL },
/* 166 */ { "Private",                          opaque, NULL },
/* 167 */ { "Private",                          opaque, NULL },
/* 168 */ { "Private",                          opaque, NULL },
/* 169 */ { "Private",                          opaque, NULL },
/* 170 */ { "Private",                          opaque, NULL },
/* 171 */ { "Private",                          opaque, NULL },
/* 172 */ { "Private",                          opaque, NULL },
/* 173 */ { "Private",                          opaque, NULL },
/* 174 */ { "Private",                          opaque, NULL },
/* 175 */ { "Private",                          opaque, NULL },
/* 176 */ { "Private",                          opaque, NULL },
/* 177 */ { "Private",                          opaque, NULL },
/* 178 */ { "Private",                          opaque, NULL },
/* 179 */ { "Private",                          opaque, NULL },
/* 180 */ { "Private",                          opaque, NULL },
/* 181 */ { "Private",                          opaque, NULL },
/* 182 */ { "Private",                          opaque, NULL },
/* 183 */ { "Private",                          opaque, NULL },
/* 184 */ { "Private",                          opaque, NULL },
/* 185 */ { "Private",                          opaque, NULL },
/* 186 */ { "Private",                          opaque, NULL },
/* 187 */ { "Private",                          opaque, NULL },
/* 188 */ { "Private",                          opaque, NULL },
/* 189 */ { "Private",                          opaque, NULL },
/* 190 */ { "Private",                          opaque, NULL },
/* 191 */ { "Private",                          opaque, NULL },
/* 192 */ { "Private",                          opaque, NULL },
/* 193 */ { "Private",                          opaque, NULL },
/* 194 */ { "Private",                          opaque, NULL },
/* 195 */ { "Private",                          opaque, NULL },
/* 196 */ { "Private",                          opaque, NULL },
/* 197 */ { "Private",                          opaque, NULL },
/* 198 */ { "Private",                          opaque, NULL },
/* 199 */ { "Private",                          opaque, NULL },
/* 200 */ { "Private",                          opaque, NULL },
/* 201 */ { "Private",                          opaque, NULL },
/* 202 */ { "Private",                          opaque, NULL },
/* 203 */ { "Private",                          opaque, NULL },
/* 204 */ { "Private",                          opaque, NULL },
/* 205 */ { "Private",                          opaque, NULL },
/* 206 */ { "Private",                          opaque, NULL },
/* 207 */ { "Private",                          opaque, NULL },
/* 208 */ { "Private",                          opaque, NULL },
/* 209 */ { "Private",                          opaque, NULL },
/* 210 */ { "Authentication",                   special, NULL },
/* 211 */ { "Private",                          opaque, NULL },
/* 212 */ { "Private",                          opaque, NULL },
/* 213 */ { "Private",                          opaque, NULL },
/* 214 */ { "Private",                          opaque, NULL },
/* 215 */ { "Private",                          opaque, NULL },
/* 216 */ { "Private",                          opaque, NULL },
/* 217 */ { "Private",                          opaque, NULL },
/* 218 */ { "Private",                          opaque, NULL },
/* 219 */ { "Private",                          opaque, NULL },
/* 220 */ { "Private",                          opaque, NULL },
/* 221 */ { "Private",                          opaque, NULL },
/* 222 */ { "Private",                          opaque, NULL },
/* 223 */ { "Private",                          opaque, NULL },
/* 224 */ { "Private",                          opaque, NULL },
/* 225 */ { "Private",                          opaque, NULL },
/* 226 */ { "Private",                          opaque, NULL },
/* 227 */ { "Private",                          opaque, NULL },
/* 228 */ { "Private",                          opaque, NULL },
/* 229 */ { "Private",                          opaque, NULL },
/* 230 */ { "Private",                          opaque, NULL },
/* 231 */ { "Private",                          opaque, NULL },
/* 232 */ { "Private",                          opaque, NULL },
/* 233 */ { "Private",                          opaque, NULL },
/* 234 */ { "Private",                          opaque, NULL },
/* 235 */ { "Private",                          opaque, NULL },
/* 236 */ { "Private",                          opaque, NULL },
/* 237 */ { "Private",                          opaque, NULL },
/* 238 */ { "Private",                          opaque, NULL },
/* 239 */ { "Private",                          opaque, NULL },
/* 240 */ { "Private",                          opaque, NULL },
/* 241 */ { "Private",                          opaque, NULL },
/* 242 */ { "Private",                          opaque, NULL },
/* 243 */ { "Private",                          opaque, NULL },
/* 244 */ { "Private",                          opaque, NULL },
/* 245 */ { "Private",                          opaque, NULL },
/* 246 */ { "Private",                          opaque, NULL },
/* 247 */ { "Private",                          opaque, NULL },
/* 248 */ { "Private",                          opaque, NULL },
/* 249 */ { "Classless static routes",                opaque, NULL },
/* 250 */ { "Private",                          opaque, NULL },
/* 251 */ { "Private",                          opaque, NULL },
/* 252 */ { "Proxy autodiscovery",              string, NULL },
/* 253 */ { "Private",                          opaque, NULL },
/* 254 */ { "Private",                          opaque, NULL },
/* 255 */ { "Private",                          opaque, NULL }
};

static const char *
bootp_get_opt_text(unsigned int index)
{
      if(index>=(sizeof(bootp_opt)/sizeof(struct opt_info)))
            return "unknown";
      return bootp_opt[index].text;
}

static const void *
bootp_get_opt_data(unsigned int index)
{
      if(index>=(sizeof(bootp_opt)/sizeof(struct opt_info)))
            return NULL;
      return bootp_opt[index].data;
}

static enum field_type
bootp_get_opt_ftype(unsigned int index)
{
      if(index>=(sizeof(bootp_opt)/sizeof(struct opt_info)))
            return none;
      return bootp_opt[index].ftype;
}


/* Returns the number of bytes consumed by this option. */
static int
bootp_option(tvbuff_t *tvb, proto_tree *bp_tree, int voff, int eoff,
    gboolean first_pass, gboolean *at_end, const char **dhcp_type_p,
    const guint8 **vendor_class_id_p)
{
      const char        *text;
      enum field_type         ftype;
      guchar                  code = tvb_get_guint8(tvb, voff);
      int               optlen;
      const struct true_false_string *tfs;
      const value_string      *vs;
      guchar                  byte;
      int               i, consumed;
      int               optoff, optleft, optend;
      gulong                  time_secs;
      proto_tree        *v_tree, *ft;
      proto_item        *vti;
      guint8                  protocol;
      guint8                  algorithm;
      guint8                  rdm;
      guint8                  fqdn_flags;
      int               o52voff, o52eoff;
      gboolean          o52at_end;
      gboolean          skip_opaque = FALSE;
      int               s_option;
      int               ava_vid;


      static const value_string slpda_vals[] = {
          {0x00,   "Dynamic Discovery" },
          {0x01,   "Static Discovery" },
          {0x80,   "Backwards compatibility" },
          {0,     NULL     } };

      static const value_string slp_scope_vals[] = {
          {0x00,   "Preferred Scope" },
          {0x01,   "Mandatory Scope" },
          {0,     NULL     } };

      static const value_string authen_protocol_vals[] = {
          {AUTHEN_PROTO_CONFIG_TOKEN,   "configuration token" },
          {AUTHEN_PROTO_DELAYED_AUTHEN, "delayed authentication" },
          {0,                           NULL     } };

      static const value_string authen_da_algo_vals[] = {
          {AUTHEN_DELAYED_ALGO_HMAC_MD5, "HMAC_MD5" },
          {0,                            NULL     } };

      static const value_string authen_rdm_vals[] = {
          {AUTHEN_RDM_MONOTONIC_COUNTER, "Monotonically-increasing counter" },
          {0,                            NULL     } };

      static const value_string opt_overload_vals[] = {
          { OPT_OVERLOAD_FILE,  "Boot file name holds options",                },
          { OPT_OVERLOAD_SNAME, "Server host name holds options",              },
          { OPT_OVERLOAD_BOTH,  "Boot file and server host names hold options" },
          { 0,                  NULL                                           } };

      /* Options whose length isn't "optlen + 2". */
      switch (code) {

      case 0:           /* Padding */
            /* check how much padding we have */
            for (i = voff + 1; i < eoff; i++ ) {
                  if (tvb_get_guint8(tvb, i) != 0) {
                        break;
                  }
            }
            i = i - voff;
            if (!first_pass) {
                  if (bp_tree != NULL) {
                        proto_tree_add_text(bp_tree, tvb, voff, i,
                            "Padding (%d byte%s)", i, (i>1)?"s":"");
                  }
            }
            consumed = i;
            return consumed;
            break;

      case 255:   /* End Option */
            if (!first_pass) {
                  if (bp_tree != NULL) {
                        proto_tree_add_text(bp_tree, tvb, voff, 1,
                            "End Option");
                  }
            }
            *at_end = TRUE;
            consumed = 1;
            return consumed;
      }

      /*
       * Get the length of the option, and the number of bytes it
       * consumes (the length doesn't include the option code or
       * length bytes).
       *
       * On the first pass, check first whether we have the length
       * byte, so that we don't throw an exception; if we throw an
       * exception in the first pass, which is only checking for options
       * whose values we need in order to properly dissect the packet
       * on the second pass, we won't actually dissect the options, so
       * you won't be able to see which option had the problem.
       */
      if (first_pass) {
            if (!tvb_bytes_exist(tvb, voff+1, 1)) {
                  /*
                   * We don't have the length byte; just return 1
                   * as the number of bytes we consumed, to count
                   * the code byte.
                   */
                  return 1;
            }
      }
      optlen = tvb_get_guint8(tvb, voff+1);
      consumed = optlen + 2;

      /*
       * In the first pass, we don't put anything into the protocol
       * tree; we just check for some options we have to look at
       * in order to properly process the packet:
       *
       *    53 (DHCP message type) - if this is present, this is DHCP
       *
       *    60 (Vendor class identifier) - we need this in order to
       *       interpret the vendor-specific info
       *
       * We also check, before fetching anything, to make sure we
       * have the entire item we're fetching, so that we don't throw
       * an exception.
       */
      if (first_pass) {
            if (tvb_bytes_exist(tvb, voff+2, consumed-2)) {
                  switch (code) {

                  case 53:
                        *dhcp_type_p =
                            val_to_str(tvb_get_guint8(tvb, voff+2),
                              opt53_text,
                              "Unknown Message Type (0x%02x)");
                        break;

                  case 60:
                        *vendor_class_id_p =
                            tvb_get_ptr(tvb, voff+2, consumed-2);
                        break;
                  }
            }

            /*
             * We don't do anything else here.
             */
            return consumed;
      }

      /*
       * This is the second pass - if there's a protocol tree to be
       * built, we put stuff into it, otherwise we just return.
       */
      if (bp_tree == NULL) {
            /* Don't put anything in the protocol tree. */
            return consumed;
      }

      /* Normal cases */
      text = bootp_get_opt_text(code);
      ftype = bootp_get_opt_ftype(code);

      optoff = voff+2;

      vti = proto_tree_add_text(bp_tree, tvb, voff, consumed,
          "Option: (t=%d,l=%d) %s", code, optlen, text);
      v_tree = proto_item_add_subtree(vti, ett_bootp_option);
      proto_tree_add_uint_format_value(v_tree, hf_bootp_option_type,
            tvb, voff, 1, code, "(%d) %s", code, text);
      proto_tree_add_item(v_tree, hf_bootp_option_length, tvb, voff+1, 1, FALSE);
      if (optlen > 0) {
            proto_tree_add_item(v_tree, hf_bootp_option_value, tvb, voff+2, optlen, FALSE);
      }

      /* Special cases */
      switch (code) {

      case 21:    /* Policy Filter */
            if (optlen == 8) {
                  /* one IP address pair */
                  proto_item_append_text(vti, " = %s/%s",
                        ip_to_str(tvb_get_ptr(tvb, optoff, 4)),
                        ip_to_str(tvb_get_ptr(tvb, optoff+4, 4)));
            } else {
                  /* > 1 IP address pair. Let's make a sub-tree */
                  for (i = optoff, optleft = optlen;
                      optleft > 0; i += 8, optleft -= 8) {
                        if (optleft < 8) {
                              proto_tree_add_text(v_tree, tvb, i, optleft,
                                  "Option length isn't a multiple of 8");
                              break;
                        }
                        proto_tree_add_text(v_tree, tvb, i, 8, "IP Address/Mask: %s/%s",
                              ip_to_str(tvb_get_ptr(tvb, i, 4)),
                              ip_to_str(tvb_get_ptr(tvb, i+4, 4)));
                  }
            }
            break;

      case 33:    /* Static Route */
            if (optlen == 8) {
                  /* one IP address pair */
                  proto_item_append_text(vti, " = %s/%s",
                        ip_to_str(tvb_get_ptr(tvb, optoff, 4)),
                        ip_to_str(tvb_get_ptr(tvb, optoff+4, 4)));
            } else {
                  /* > 1 IP address pair. Let's make a sub-tree */
                  for (i = optoff, optleft = optlen; optleft > 0;
                      i += 8, optleft -= 8) {
                        if (optleft < 8) {
                              proto_tree_add_text(v_tree, tvb, i, optleft,
                                  "Option length isn't a multiple of 8");
                              break;
                        }
                        proto_tree_add_text(v_tree, tvb, i, 8,
                              "Destination IP Address/Router: %s/%s",
                              ip_to_str(tvb_get_ptr(tvb, i, 4)),
                              ip_to_str(tvb_get_ptr(tvb, i+4, 4)));
                  }
            }
            break;

      case 43:    /* Vendor-Specific Info */
            s_option = tvb_get_guint8(tvb, optoff);

            if (optlen == 5 && s_option == 58)
            {
                        proto_item_append_text(vti, " (Alcatel AVA)");
                        ava_vid =  tvb_get_ntohs(tvb, optoff + 2);

                        proto_tree_add_uint (v_tree, hf_bootp_alcatel_vid, tvb, optoff + 2,
                              2, ava_vid);

                        if (ava_vid == 65535)
                        {
                              proto_tree_add_text (v_tree, tvb, optoff + 2,
                                    2, "Type: Request from TSC IP Phone");
                        } else {
                              proto_tree_add_text (v_tree, tvb, optoff + 2,
                                    2, "Type: Response from Server");
                        }

                        break;
            }

            /* PXE protocol 2.1 as described in the intel specs */
            if (*vendor_class_id_p != NULL &&
                strncmp((const gchar*)*vendor_class_id_p, "PXEClient", strlen("PXEClient")) == 0) {
                  proto_item_append_text(vti, " (PXEClient)");
                  v_tree = proto_item_add_subtree(vti, ett_bootp_option);

                  optend = optoff + optlen;
                  while (optoff < optend) {
                        optoff = dissect_vendor_pxeclient_suboption(v_tree,
                              tvb, optoff, optend);
                  }
            } else if (*vendor_class_id_p != NULL &&
                     ((strncmp((const gchar*)*vendor_class_id_p, "pktc", strlen("pktc")) == 0) ||
                            (strncmp((const gchar*)*vendor_class_id_p, "docsis", strlen("docsis")) == 0) ||
                            (strncmp((const gchar*)*vendor_class_id_p, "CableHome", strlen("CableHome")) == 0))) {
                    /* CableLabs standard - see www.cablelabs.com/projects */
                    proto_item_append_text(vti, " (CableLabs)");

                  optend = optoff + optlen;
                  while (optoff < optend) {
                          optoff = dissect_vendor_cablelabs_suboption(v_tree,
                              tvb, optoff, optend);
                  }
            }
            break;

      case 52:    /* Option Overload */
            if (optlen < 1) {
                  proto_item_append_text(vti, " length isn't >= 1");
                  break;
            }
            byte = tvb_get_guint8(tvb, optoff);
            proto_item_append_text(vti, " = %s",
                  val_to_str(byte, opt_overload_vals,
                      "Unknown (0x%02x)"));

            /* Just in case we find an option 52 in sname or file */
            if (voff > VENDOR_INFO_OFFSET && byte >= 1 && byte <= 3) {
                  v_tree = proto_item_add_subtree(vti, ett_bootp_option);
                  if (byte == 1 || byte == 3) { /* 'file' */
                        vti = proto_tree_add_text (v_tree, tvb,
                              FILE_NAME_OFFSET, FILE_NAME_LEN,
                              "Boot file name option overload");
                        v_tree = proto_item_add_subtree(vti, ett_bootp_option);
                        o52voff = FILE_NAME_OFFSET;
                        o52eoff = FILE_NAME_OFFSET + FILE_NAME_LEN;
                        o52at_end = FALSE;
                        while (o52voff < o52eoff && !o52at_end) {
                              o52voff += bootp_option(tvb, v_tree, o52voff,
                                    o52eoff, FALSE, &o52at_end,
                                    dhcp_type_p, vendor_class_id_p);
                        }
                  }
                  if (byte == 2 || byte == 3) { /* 'sname' */
                        vti = proto_tree_add_text (v_tree, tvb,
                              SERVER_NAME_OFFSET, SERVER_NAME_LEN,
                              "Server host name option overload");
                        v_tree = proto_item_add_subtree(vti, ett_bootp_option);
                        o52voff = SERVER_NAME_OFFSET;
                        o52eoff = SERVER_NAME_OFFSET + SERVER_NAME_LEN;
                        o52at_end = FALSE;
                        while (o52voff < o52eoff && !o52at_end) {
                              o52voff += bootp_option(tvb, v_tree, o52voff,
                                    o52eoff, FALSE, &o52at_end,
                                    dhcp_type_p, vendor_class_id_p);
                        }
                  }
            }

/*          protocol = tvb_get_guint8(tvb, optoff);
            proto_tree_add_text(v_tree, tvb, optoff, 1, "Protocol: %s (%u)",
                            val_to_str(protocol, authen_protocol_vals, "Unknown"),
                            protocol); */
            break;

      case 53:    /* DHCP Message Type */
            if (optlen != 1) {
                  proto_item_append_text(vti, " length isn't 1");
                  break;
            }
            proto_item_append_text(vti, " = DHCP %s",
                  val_to_str(tvb_get_guint8(tvb, optoff),
                        opt53_text,
                        "Unknown Message Type (0x%02x)"));
            break;

      case 55:    /* Parameter Request List */
            for (i = 0; i < optlen; i++) {
                  byte = tvb_get_guint8(tvb, optoff+i);
                  proto_tree_add_text(v_tree, tvb, optoff+i, 1, "%d = %s",
                              byte, bootp_get_opt_text(byte));
            }
            break;

      case 60:    /* Vendor class identifier */
            /*
             * XXX - RFC 2132 says this is a string of octets;
             * should we check for non-printables?
             */
            proto_item_append_text(vti, " = \"%s\"",
                  tvb_format_stringzpad(tvb, optoff, consumed-2));
            if ((tvb_memeql(tvb, optoff, (const guint8*)PACKETCABLE_MTA_CAP10,
                              strlen(PACKETCABLE_MTA_CAP10)) == 0)
                ||
                (tvb_memeql(tvb, optoff, (const guint8*)PACKETCABLE_MTA_CAP15,
                              strlen(PACKETCABLE_MTA_CAP10)) == 0))
            {
                  dissect_packetcable_mta_cap(v_tree, tvb, optoff, optlen);
            }
            else {
              if (tvb_memeql(tvb, optoff, (const guint8*)PACKETCABLE_CM_CAP11,
                              strlen(PACKETCABLE_CM_CAP11)) == 0
                  ||
                  tvb_memeql(tvb, optoff, (const guint8*)PACKETCABLE_CM_CAP20,
                              strlen(PACKETCABLE_CM_CAP20)) == 0 )
              {
                  dissect_docsis_cm_cap(v_tree, tvb, optoff, optlen);
              }
            }
            break;

      case 61:    /* Client Identifier */
      case 97:        /* Client Identifier (UUID) */
            if (optlen > 0)
                  byte = tvb_get_guint8(tvb, optoff);
            else
                  byte = 0;

            /* We *MAY* use hwtype/hwaddr. If we have 7 bytes, I'll
               guess that the first is the hwtype, and the last 6
               are the hw addr */
            /* See http://www.iana.org/assignments/arp-parameters */
            /* RFC2132 9.14 Client-identifier has the following to say:
               A hardware type of 0 (zero) should be used when the value
               field contains an identifier other than a hardware address
               (e.g. a fully qualified domain name). */

            if (optlen == 7 && byte > 0 && byte < 48) {
                  proto_tree_add_text(v_tree, tvb, optoff, 1,
                        "Hardware type: %s",
                        arphrdtype_to_str(byte,
                              "Unknown (0x%02x)"));
                  if (byte == ARPHRD_ETHER || byte == ARPHRD_IEEE802)
                        proto_tree_add_item(v_tree,
                            hf_bootp_hw_ether_addr, tvb, optoff+1, 6,
                            FALSE);
                  else
                        proto_tree_add_text(v_tree, tvb, optoff+1, 6,
                              "Client hardware address: %s",
                              arphrdaddr_to_str(tvb_get_ptr(tvb, optoff+1, 6),
                              6, byte));
            } else if (optlen == 17 && byte == 0) {
                  /* Identifier is a UUID */
                  proto_tree_add_item(v_tree, hf_bootp_client_identifier_uuid,
                                  tvb, optoff + 1, 16, TRUE);
            } else {
                  /* otherwise, it's opaque data */
            }
            break;

      case 63:    /* NetWare/IP options (RFC 2242) */

            optend = optoff + optlen;
            while (optoff < optend)
                  optoff = dissect_netware_ip_suboption(v_tree, tvb, optoff, optend);
            break;

      case 78:    /* SLP Directory Agent Option RFC2610 Added by Greg Morris (gmorris@novell.com)*/
            if (optlen < 1) {
                  proto_item_append_text(vti, " length isn't >= 1");
                  break;
            }
            optleft = optlen;
            byte = tvb_get_guint8(tvb, optoff);
            proto_item_append_text(vti, " = %s",
                        val_to_str(byte, slpda_vals,
                              "Unknown (0x%02x)"));
            optoff++;
            optleft--;
            if (byte == 0x80) {
                  if (optleft == 0)
                        break;
                  optoff++;
                  optleft--;
            }
            for (i = optoff; optleft > 0; i += 4, optleft -= 4) {
                  if (optleft < 4) {
                        proto_tree_add_text(v_tree, tvb, i, optleft,
                            "Option length isn't a multiple of 4");
                        break;
                  }
                  proto_tree_add_text(v_tree, tvb, i, 4, "SLPDA Address: %s",
                      ip_to_str(tvb_get_ptr(tvb, i, 4)));
            }
            break;

      case 79:    /* SLP Service Scope Option RFC2610 Added by Greg Morris (gmorris@novell.com)*/
            byte = tvb_get_guint8(tvb, optoff);
            proto_item_append_text(vti, " = %s",
                        val_to_str(byte, slp_scope_vals,
                            "Unknown (0x%02x)"));
            optoff++;
            optleft = optlen - 1;
            proto_tree_add_text(v_tree, tvb, optoff, optleft,
                "%s = \"%s\"", text,
                tvb_format_stringzpad(tvb, optoff, optleft));
            break;

      case 81:    /* Client Fully Qualified Domain Name */
            if (optlen < 3) {
                  proto_item_append_text(vti, " length isn't >= 3");
                  break;
            }
            fqdn_flags = tvb_get_guint8(tvb, optoff);
            ft = proto_tree_add_text(v_tree, tvb, optoff, 1, "Flags: 0x%02x", fqdn_flags);
            proto_tree_add_item(v_tree, hf_bootp_fqdn_mbz, tvb, optoff, 1, FALSE);
            proto_tree_add_item(v_tree, hf_bootp_fqdn_n, tvb, optoff, 1, FALSE);
            proto_tree_add_item(v_tree, hf_bootp_fqdn_e, tvb, optoff, 1, FALSE);
            proto_tree_add_item(v_tree, hf_bootp_fqdn_o, tvb, optoff, 1, FALSE);
            proto_tree_add_item(v_tree, hf_bootp_fqdn_s, tvb, optoff, 1, FALSE);
            /* XXX: use code from packet-dns for return code decoding */
            proto_tree_add_item(v_tree, hf_bootp_fqdn_rcode1, tvb, optoff+1, 1, FALSE);
            /* XXX: use code from packet-dns for return code decoding */
            proto_tree_add_item(v_tree, hf_bootp_fqdn_rcode2, tvb, optoff+2, 1, FALSE);
            if (optlen > 3) {
                  if (fqdn_flags & F_FQDN_E) {
                        /* XXX: use code from packet-dns for binary encoded name */
                        proto_tree_add_item(v_tree, hf_bootp_fqdn_name,
                            tvb, optoff+3, optlen-3, FALSE);

                  } else {
                        proto_tree_add_item(v_tree, hf_bootp_fqdn_asciiname,
                            tvb, optoff+3, optlen-3, FALSE);
                  }
            }
            break;

      case 82:        /* Relay Agent Information Option */
            optend = optoff + optlen;
            while (optoff < optend)
                  optoff = bootp_dhcp_decode_agent_info(v_tree, tvb, optoff, optend);
            break;

      case 85:        /* Novell Servers (RFC 2241) */
            /* Option 85 can be sent as a string */
            /* Added by Greg Morris (gmorris[AT]novell.com) */
            if (novell_string) {
                  proto_item_append_text(vti, " = \"%s\"",
                      tvb_format_stringzpad(tvb, optoff, optlen));
            } else {
                  if (optlen == 4) {
                        /* one IP address */
                        proto_item_append_text(vti, " = %s",
                              ip_to_str(tvb_get_ptr(tvb, optoff, 4)));
                  } else {
                        /* > 1 IP addresses. Let's make a sub-tree */
                        for (i = optoff, optleft = optlen; optleft > 0;
                            i += 4, optleft -= 4) {
                              if (optleft < 4) {
                                    proto_tree_add_text(v_tree, tvb, i, optleft,
                                        "Option length isn't a multiple of 4");
                                    break;
                              }
                              proto_tree_add_text(v_tree, tvb, i, 4, "IP Address: %s",
                                    ip_to_str(tvb_get_ptr(tvb, i, 4)));
                        }
                  }
            }
              break;

      case 94: {  /* Client network interface identifier */
            guint8 id_type;

            id_type = tvb_get_guint8(tvb, optoff);

            if (id_type == 0x01) {
                  proto_tree_add_item(v_tree, hf_bootp_client_network_id_major_ver,
                                  tvb, optoff + 1, 1, TRUE);
                  proto_tree_add_item(v_tree, hf_bootp_client_network_id_minor_ver,
                                  tvb, optoff + 2, 1, TRUE);
            }

            break;
      }

      case 90:    /* DHCP Authentication */
      case 210:   /* Was this used for authentication at one time? */
            if (optlen < 11) {
                  proto_item_append_text(vti, " length isn't >= 11");
                  break;
            }
            optleft = optlen;
            protocol = tvb_get_guint8(tvb, optoff);
            proto_tree_add_text(v_tree, tvb, optoff, 1, "Protocol: %s (%u)",
                            val_to_str(protocol, authen_protocol_vals, "Unknown"),
                            protocol);
            optoff++;
            optleft--;

            algorithm = tvb_get_guint8(tvb, optoff);
            switch (protocol) {

            case AUTHEN_PROTO_DELAYED_AUTHEN:
                  proto_tree_add_text(v_tree, tvb, optoff, 1,
                            "Algorithm: %s (%u)",
                            val_to_str(algorithm, authen_da_algo_vals, "Unknown"),
                            algorithm);
                  break;

            default:
                  proto_tree_add_text(v_tree, tvb, optoff, 1,
                            "Algorithm: %u", algorithm);
                  break;
            }
            optoff++;
            optleft--;

            rdm = tvb_get_guint8(tvb, optoff);
            proto_tree_add_text(v_tree, tvb, optoff, 1,
                            "Replay Detection Method: %s (%u)",
                            val_to_str(rdm, authen_rdm_vals, "Unknown"),
                            rdm);
            optoff++;
            optleft--;

            switch (rdm) {

            case AUTHEN_RDM_MONOTONIC_COUNTER:
                  proto_tree_add_text(v_tree, tvb, optoff, 8,
                            "RDM Replay Detection Value: %" G_GINT64_MODIFIER "x",
                            tvb_get_ntoh64(tvb, optoff));
                  break;

            default:
                  proto_tree_add_text(v_tree, tvb, optoff, 8,
                            "Replay Detection Value: %s",
                            tvb_bytes_to_str(tvb, optoff, 8));
                  break;
            }
            optoff += 8;
            optleft -= 8;

            switch (protocol) {

            case AUTHEN_PROTO_DELAYED_AUTHEN:
                  switch (algorithm) {

                  case AUTHEN_DELAYED_ALGO_HMAC_MD5:
                        if (*dhcp_type_p && !strcmp(*dhcp_type_p, OPT53_DISCOVER)) {
                              /* Discover has no Secret ID nor HMAC MD5 Hash */
                              break;
                        } else {
                              if (optlen < 31) {
                                    proto_item_append_text(vti,
                                          " length isn't >= 31");
                                    break;
                              }
                              proto_tree_add_text(v_tree, tvb, optoff, 4,
                                    "Secret ID: 0x%08x",
                                    tvb_get_ntohl(tvb, optoff));
                              optoff += 4;
                              optleft -= 4;
                              proto_tree_add_text(v_tree, tvb, optoff, 16,
                                    "HMAC MD5 Hash: %s",
                                    tvb_bytes_to_str(tvb, optoff, 16));
                              break;
                        }
                  default:
                        if (optleft == 0)
                              break;
                        proto_tree_add_text(v_tree, tvb, optoff, optleft,
                              "Authentication Information: %s",
                              tvb_bytes_to_str(tvb, optoff, optleft));
                        break;
                  }
                  break;

            default:
                  if (optleft == 0)
                        break;
                  proto_tree_add_text(v_tree, tvb, optoff, optleft,
                        "Authentication Information: %s",
                        tvb_bytes_to_str(tvb, optoff, optleft));
                  break;
            }
            break;

      case 125: {       /* V-I Vendor-specific Information */
              int enterprise = 0;
            int s_end = 0;
            int s_option_len = 0;
            proto_tree *e_tree = 0;

            optend = optoff + optlen;

              optleft = optlen;

            while (optleft > 0) {

              if (optleft < 5) {
                proto_tree_add_text(v_tree, tvb, optoff,
                              optleft, "Vendor-specific Information: malformed option");
                break;
              }

              enterprise = tvb_get_ntohl(tvb, optoff);

              vti = proto_tree_add_text(v_tree, tvb, optoff, 4,
                                  "Enterprise-number: %s-%u",
                                  val_to_str( enterprise, sminmpec_values, "Unknown"),
                                  enterprise);

              s_option_len = tvb_get_guint8(tvb, optoff + 4);

              optoff += 5;
              optleft -= 5;

              /* Handle DSL Forum TR-111 Option 125 */
              if ( enterprise == 3561 ) {

                s_end = optoff + s_option_len;
                if ( s_end > optend ) {
                  proto_tree_add_text(v_tree, tvb, optoff, 1,
                                "no room left in option for enterprise %u data", enterprise);
                  break;
                }


                e_tree = proto_item_add_subtree(vti, ett_bootp_option);
                while (optoff < s_end) {

                  optoff = dissect_vendor_tr111_suboption(e_tree,
                                                 tvb, optoff, s_end);
                }
              } else {

                /* skip over the data and look for next enterprise number */
                optoff += s_option_len;
              }

              optleft -= s_option_len;

            }
            break;
      }

      default:    /* not special */
            /* The PacketCable CCC option number can vary.  If this is a CCC option,
               handle it and skip the "opaque" case below.
             */
            if (code == pkt_ccc_option) {
                  skip_opaque = TRUE;
                  proto_item_append_text(vti,
                        "CableLabs Client Configuration (%d bytes)",
                        optlen);
                  optend = optoff + optlen;
                  while (optoff < optend) {
                        switch (pkt_ccc_protocol_version) {
                              case PACKETCABLE_CCC_I05:
                                    optoff = dissect_packetcable_i05_ccc(v_tree, tvb, optoff, optend);
                                    break;
                              case PACKETCABLE_CCC_DRAFT5:
                              case PACKETCABLE_CCC_RFC_3495:
                                    optoff = dissect_packetcable_ietf_ccc(v_tree, tvb, optoff, optend, pkt_ccc_protocol_version);
                                    break;
                              default: /* XXX Should we do something here? */
                                    break;
                        }
                  }
            }

            break;
      }

      if (ftype == special)
            return consumed;
      if (ftype == opaque) {
            if (skip_opaque) /* Currently used by PacketCable CCC */
                  return consumed;
      }

      switch (ftype) {
      case ipv4:
            if (optlen != 4) {
                  proto_item_append_text(vti,
                      " - length isn't 4");
                  break;
            }
            proto_item_append_text(vti, " = %s",
                  ip_to_str(tvb_get_ptr(tvb, optoff, 4)));
            break;

      case ipv4_list:
            if (optlen == 4) {
                  /* one IP address */
                  proto_item_append_text(vti, " = %s",
                        ip_to_str(tvb_get_ptr(tvb, optoff, 4)));
            } else {
                  /* > 1 IP addresses. Let's make a sub-tree */
                  for (i = optoff, optleft = optlen; optleft > 0;
                      i += 4, optleft -= 4) {
                        if (optleft < 4) {
                              proto_tree_add_text(v_tree, tvb, i, voff + consumed - i,
                                  "Option length isn't a multiple of 4");
                              break;
                        }
                        proto_tree_add_text(v_tree, tvb, i, 4, "IP Address: %s",
                              ip_to_str(tvb_get_ptr(tvb, i, 4)));
                  }
            }
            break;

      case string:
            /* Fix for non null-terminated string supplied by
             * John Lines <John.Lines[AT]aeat.co.uk>
             */
            proto_item_append_text(vti, " = \"%s\"",
                        tvb_format_stringzpad(tvb, optoff, consumed-2));
            break;

      case opaque:
            break;

      case val_boolean:
            if (optlen != 1) {
                  proto_item_append_text(vti,
                      " - length isn't 1");
                  break;
            }
            tfs = (const struct true_false_string *) bootp_get_opt_data(code);
            if(tfs){
                  i = tvb_get_guint8(tvb, optoff);
                  if (i != 0 && i != 1) {
                        proto_item_append_text(vti,
                            " = Invalid Value %d", i);
                  } else {
                        proto_item_append_text(vti, " = %s",
                              i == 0 ? tfs->false_string : tfs->true_string);
                  }
            }
            break;

      case val_u_byte:
            if (optlen != 1) {
                  proto_item_append_text(vti,
                      " - length isn't 1");
                  break;
            }
            vs = (const value_string *) bootp_get_opt_data(code);
            byte = tvb_get_guint8(tvb, optoff);
            if (vs != NULL) {
                  proto_item_append_text(vti, " = %s",
                      val_to_str(byte, vs, "Unknown (%u)"));
            } else
                  proto_item_append_text(vti, " = %u", byte);
            break;

      case val_u_short: {
            gushort vd;

            if (optlen != 2) {
                  proto_item_append_text(vti,
                      " - length isn't 2");
                  break;
            }

            vs = (const value_string *) bootp_get_opt_data(code);
            vd = tvb_get_ntohs(tvb, optoff);

            if (vs != NULL) {
                  proto_item_append_text(vti, " = %s",
                            val_to_str(vd, vs, "Unknown (%u)"));
            } else
                  proto_item_append_text(vti, " = %u", vd);

            break;
      }

      case val_u_short_list:
            if (optlen == 2) {
                  /* one gushort */
                  proto_item_append_text(vti, " = %u",
                      tvb_get_ntohs(tvb, optoff));
            } else {
                  /* > 1 gushort */
                  for (i = optoff, optleft = optlen; optleft > 0;
                      i += 2, optleft -= 2) {
                        if (optleft < 2) {
                              proto_tree_add_text(v_tree, tvb, i, voff + consumed - i,
                                  "Option length isn't a multiple of 2");
                              break;
                        }
                        proto_tree_add_text(v_tree, tvb, i, 4, "Value: %u",
                              tvb_get_ntohs(tvb, i));
                  }
            }
            break;

      case val_u_long:
            if (optlen != 4) {
                  proto_item_append_text(vti,
                      " - length isn't 4");
                  break;
            }
            proto_item_append_text(vti, " = %u",
                tvb_get_ntohl(tvb, optoff));
            break;

      case time_in_secs:
            if (optlen != 4) {
                  proto_item_append_text(vti,
                      " - length isn't 4");
                  break;
            }
            time_secs = tvb_get_ntohl(tvb, optoff);
            proto_item_append_text(vti, " = %s",
                ((time_secs == 0xffffffff) ?
                  "infinity" :
                  time_secs_to_str(time_secs)));
            break;

      default:
            break;
      }

      return consumed;
}

static int
bootp_dhcp_decode_agent_info(proto_tree *v_tree, tvbuff_t *tvb, int optoff,
    int optend)
{
      int suboptoff = optoff;
      guint8 subopt;
      guint8 subopt_len;

      subopt = tvb_get_guint8(tvb, optoff);
      suboptoff++;

      if (suboptoff >= optend) {
            proto_tree_add_text(v_tree, tvb, optoff, 1,
                  "Suboption %d: no room left in option for suboption length",
                  subopt);
            return (optend);
      }
      subopt_len = tvb_get_guint8(tvb, suboptoff);
      suboptoff++;

      if (suboptoff+subopt_len > optend) {
            proto_tree_add_text(v_tree, tvb, optoff, optend-optoff,
                  "Suboption %d: no room left in option for suboption value",
                  subopt);
            return (optend);
      }
      switch (subopt) {
      case 1:
            proto_tree_add_text(v_tree, tvb, optoff, subopt_len + 2,
                            "Agent Circuit ID: %s",
                            tvb_bytes_to_str(tvb, suboptoff, subopt_len));
            break;
      case 2:
            proto_tree_add_text(v_tree, tvb, optoff, subopt_len + 2,
                            "Agent Remote ID: %s",
                            tvb_bytes_to_str(tvb, suboptoff, subopt_len));
            break;
      default:
            proto_tree_add_text(v_tree, tvb, optoff, subopt_len + 2,
                            "Invalid agent suboption %d (%d bytes)",
                            subopt, subopt_len);
            break;
      }
      optoff += (subopt_len + 2);
      return optoff;
}

static int
dissect_vendor_pxeclient_suboption(proto_tree *v_tree, tvbuff_t *tvb,
    int optoff, int optend)
{
      int suboptoff = optoff;
      guint8 subopt;
      guint8 subopt_len;
      int suboptleft;
      proto_tree *o43pxeclient_v_tree;
      proto_item *vti;

      static struct opt_info o43pxeclient_opt[]= {
            /* 0 */ {"nop", special, NULL},     /* dummy */
            /* 1 */ {"PXE mtftp IP", ipv4_list, NULL},
            /* 2 */ {"PXE mtftp client port", val_u_le_short, NULL},
            /* 3 */ {"PXE mtftp server port",val_u_le_short, NULL},
            /* 4 */ {"PXE mtftp timeout", val_u_byte, NULL},
            /* 5 */ {"PXE mtftp delay", val_u_byte, NULL},
            /* 6 */ {"PXE discovery control", val_u_byte, NULL},
                  /*
                   * Correct: b0 (lsb): disable broadcast discovery
                   *    b1: disable multicast discovery
                   *    b2: only use/accept servers in boot servers
                   *    b3: download bootfile without prompt/menu/disc
                   */
            /* 7 */ {"PXE multicast address", ipv4_list, NULL},
            /* 8 */ {"PXE boot servers", special, NULL},
            /* 9 */ {"PXE boot menu", special, NULL},
            /* 10 */ {"PXE menu prompt", special, NULL},
            /* 11 */ {"PXE multicast address alloc", special, NULL},
            /* 12 */ {"PXE credential types", special, NULL},
            /* 71 {"PXE boot item", special, NULL}, */
            /* 255 {"PXE end options", special, NULL} */
      };

      subopt = tvb_get_guint8(tvb, suboptoff);
      suboptoff++;

      if (subopt == 0) {
            proto_tree_add_text(v_tree, tvb, optoff, 1, "Padding");
                return (suboptoff);
      } else if (subopt == 255) {   /* End Option */
            proto_tree_add_text(v_tree, tvb, optoff, 1, "End PXEClient option");
            /* Make sure we skip any junk left this option */
            return (optend);
      }

      if (suboptoff >= optend) {
            proto_tree_add_text(v_tree, tvb, optoff, 1,
                  "Suboption %d: no room left in option for suboption length",
                  subopt);
            return (optend);
      }
      subopt_len = tvb_get_guint8(tvb, suboptoff);
      suboptoff++;

      if (suboptoff+subopt_len > optend) {
            proto_tree_add_text(v_tree, tvb, optoff, optend-optoff,
                  "Suboption %d: no room left in option for suboption value",
                  subopt);
            return (optend);
      }
      if ( subopt == 71 ) {   /* 71 {"PXE boot item", special} */
            /* case special */
            /* I may need to decode that properly one day */
            proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                  "Suboption %d: %s (%d byte%s)" ,
                  subopt, "PXE boot item",
                  subopt_len, plurality(subopt_len, "", "s"));
      } else if ((subopt < 1) || (subopt >= array_length(o43pxeclient_opt))) {
            proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                  "Unknown suboption %d (%d byte%s)", subopt, subopt_len,
                  plurality(subopt_len, "", "s"));
      } else {
            switch (o43pxeclient_opt[subopt].ftype) {

            case special:
                  /* I may need to decode that properly one day */
                  proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                        "Suboption %d: %s (%d byte%s)",
                        subopt, o43pxeclient_opt[subopt].text,
                        subopt_len, plurality(subopt_len, "", "s"));
                  break;

            case ipv4_list:
                  if (subopt_len == 4) {
                        /* one IP address */
                        proto_tree_add_text(v_tree, tvb, optoff, 6,
                            "Suboption %d : %s = %s",
                            subopt, o43pxeclient_opt[subopt].text,
                            ip_to_str(tvb_get_ptr(tvb, suboptoff, 4)));
                  } else {
                        /* > 1 IP addresses. Let's make a sub-tree */
                        vti = proto_tree_add_text(v_tree, tvb, optoff,
                            subopt_len+2, "Suboption %d: %s",
                            subopt, o43pxeclient_opt[subopt].text);
                        o43pxeclient_v_tree = proto_item_add_subtree(vti, ett_bootp_option);
                        for (suboptleft = subopt_len; suboptleft > 0;
                            suboptoff += 4, suboptleft -= 4) {
                              if (suboptleft < 4) {
                                    proto_tree_add_text(o43pxeclient_v_tree,
                                        tvb, suboptoff, suboptleft,
                                        "Suboption length isn't a multiple of 4");
                                    break;
                              }
                              proto_tree_add_text(o43pxeclient_v_tree,
                                  tvb, suboptoff, 4, "IP Address: %s",
                                  ip_to_str(tvb_get_ptr(tvb, suboptoff, 4)));
                        }
                  }
                  break;

/* XXX            case string:
                  proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                        "Suboption %d: %s", subopt, o43pxeclient_opt[subopt].text);
                  break;
   XXX */

            case val_u_byte:
                  if (subopt_len != 1) {
                        proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                              "Suboption %d: suboption length isn't 1", subopt);
                        break;
                  }
                  proto_tree_add_text(v_tree, tvb, optoff, 3, "Suboption %d: %s = %u",
                      subopt, o43pxeclient_opt[subopt].text,
                      tvb_get_guint8(tvb, suboptoff));
                  break;

            case val_u_le_short:
                  if (subopt_len != 2) {
                        proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                              "Suboption %d: suboption length isn't 2", subopt);
                        break;
                  }
                  proto_tree_add_text(v_tree, tvb, optoff, 4, "Suboption %d: %s = %u",
                      subopt, o43pxeclient_opt[subopt].text,
                      tvb_get_letohs(tvb, suboptoff));
                  break;

            default:
                  proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,"ERROR, please report: Unknown subopt type handler %d", subopt);
                  break;
            }
      }
      optoff += (subopt_len + 2);
      return optoff;
}


static int
dissect_vendor_cablelabs_suboption(proto_tree *v_tree, tvbuff_t *tvb,
    int optoff, int optend)
{
      int suboptoff = optoff;
      guint8 subopt, byte_val;
      guint8 subopt_len;

      static struct opt_info o43cablelabs_opt[]= {
            /* 0 */ {"nop", special, NULL},     /* dummy */
            /* 1 */ {"Suboption Request List", string, NULL},
            /* 2 */ {"Device Type", string, NULL},
            /* 3 */ {"eSAFE Types", string, NULL},
            /* 4 */ {"Serial Number", string, NULL},
            /* 5 */ {"Hardware Version", string, NULL},
            /* 6 */ {"Software Version", string, NULL},
            /* 7 */ {"Boot ROM version", string, NULL},
            /* 8 */ {"Organizationally Unique Identifier", special, NULL},
            /* 9 */ {"Model Number", string, NULL},
            /* 10 */ {"Vendor Name", string, NULL},
            /* *** 11-30: CableHome *** */
            /* 11 */ {"Address Realm", special, NULL},
            /* 12 */ {"CM/PS System Description", string, NULL},
            /* 13 */ {"CM/PS Firmware Revision", string, NULL},
            /* 14 */ {"Firewall Policy File Version", string, NULL},
            /* 15 */ {"Unassigned (CableHome)", special, NULL},
            /* 16 */ {"Unassigned (CableHome)", special, NULL},
            /* 17 */ {"Unassigned (CableHome)", special, NULL},
            /* 18 */ {"Unassigned (CableHome)", special, NULL},
            /* 19 */ {"Unassigned (CableHome)", special, NULL},
            /* 20 */ {"Unassigned (CableHome)", special, NULL},
            /* 21 */ {"Unassigned (CableHome)", special, NULL},
            /* 22 */ {"Unassigned (CableHome)", special, NULL},
            /* 23 */ {"Unassigned (CableHome)", special, NULL},
            /* 24 */ {"Unassigned (CableHome)", special, NULL},
            /* 25 */ {"Unassigned (CableHome)", special, NULL},
            /* 26 */ {"Unassigned (CableHome)", special, NULL},
            /* 27 */ {"Unassigned (CableHome)", special, NULL},
            /* 28 */ {"Unassigned (CableHome)", special, NULL},
            /* 29 */ {"Unassigned (CableHome)", special, NULL},
            /* 30 */ {"Unassigned (CableHome)", special, NULL},
            /* *** 31-50: PacketCable *** */
            /* 31 */ {"MTA MAC Address", special, NULL},
            /* 32 */ {"Correlation ID", val_u_long, NULL},
            /* 33-50 {"Unassigned (PacketCable)", special, NULL}, */
            /* *** 51-127: CableLabs *** */
            /* 51-127 {"Unassigned (CableLabs)", special, NULL}, */
            /* *** 128-254: Vendors *** */
            /* 128-254 {"Unassigned (Vendors)", special, NULL}, */
            /* 255 {"end options", special, NULL} */
      };

      static const value_string cablehome_subopt11_vals[] = {
            { 1, "PS WAN-Man" },
            { 2, "PS WAN-Data" },
            { 0, NULL }
      };

      subopt = tvb_get_guint8(tvb, suboptoff);
      suboptoff++;

      if (subopt == 0) {
            proto_tree_add_text(v_tree, tvb, optoff, 1, "Padding");
                return (suboptoff);
      } else if (subopt == 255) {   /* End Option */
            proto_tree_add_text(v_tree, tvb, optoff, 1, "End CableLabs option");
            /* Make sure we skip any junk left this option */
            return (optend);
      }

      if (suboptoff >= optend) {
            proto_tree_add_text(v_tree, tvb, optoff, 1,
                  "Suboption %d: no room left in option for suboption length",
                  subopt);
            return (optend);
      }
      subopt_len = tvb_get_guint8(tvb, suboptoff);
      suboptoff++;

      if (suboptoff+subopt_len > optend) {
            proto_tree_add_text(v_tree, tvb, optoff, optend-optoff,
                  "Suboption %d: no room left in option for suboption value",
                  subopt);
            return (optend);
      }
      if ( (subopt < 1 ) || (subopt >= array_length(o43cablelabs_opt)) ) {
            proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                  "Suboption %d: Unassigned (%d byte%s)", subopt, subopt_len,
                  plurality(subopt_len, "", "s"));
      } else {
            switch (o43cablelabs_opt[subopt].ftype) {

            case special:
                  if ( subopt == 8 ) {    /* OUI */
                        /* CableLabs specs treat 43.8 inconsistently
                         * as either binary (3b) or string (6b) */
                        if (subopt_len == 3) {
                              proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                                    "Suboption %d: OUI = %s", subopt,
                                    bytes_to_str_punct(tvb_get_ptr(tvb, suboptoff, 3), 3, ':'));
                        } else if (subopt_len == 6) {
                              proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                                    "Suboption %d: OUI = \"%s\"", subopt,
                                    tvb_format_stringzpad(tvb, suboptoff, subopt_len));
                        } else {
                              proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                                    "Suboption %d: suboption length isn't 3 or 6", subopt);
                                }
                        break;
                  } else if ( subopt == 11 ) { /* Address Realm */
                        if (subopt_len != 1) {
                              proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                                    "Suboption %d: suboption length isn't 1", subopt);
                              break;
                        }
                        byte_val = tvb_get_guint8(tvb, suboptoff);
                        proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                              "Suboption %d: %s = %s (0x%02x)",
                              subopt, o43cablelabs_opt[subopt].text,
                              val_to_str(byte_val, cablehome_subopt11_vals, "Unknown"), byte_val);
                  } else if ( subopt == 31 ) { /* MTA MAC address */
                        if (subopt_len != 6) {
                              proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                                    "Suboption %d: suboption length isn't 6", subopt);
                              break;
                        }
                        proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                              "Suboption %d: %s = %s",
                              subopt,  o43cablelabs_opt[subopt].text,
                              bytes_to_str_punct(tvb_get_ptr(tvb, suboptoff, 6), 6, ':'));
                  } else {
                        proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                              "Suboption %d: %s (%d byte%s)" ,
                              subopt, o43cablelabs_opt[subopt].text,
                              subopt_len, plurality(subopt_len, "", "s"));
                  }
                  break;

            case string:
                  proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                        "Suboption %d: %s = \"%s\"", subopt,
                        o43cablelabs_opt[subopt].text,
                        tvb_format_stringzpad(tvb, suboptoff, subopt_len));
                  break;

            case bytes:
                  proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                        "Suboption %d: %s = 0x%s", subopt,
                        o43cablelabs_opt[subopt].text,
                        tvb_bytes_to_str(tvb, suboptoff, subopt_len));
                  break;

            case val_u_long:
                  if (subopt_len != 4) {
                        proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                              "Suboption %d: suboption length isn't 4", subopt);
                        break;
                  }
                  proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                        "Suboption %d: %s = %u", subopt,
                        o43cablelabs_opt[subopt].text,
                        tvb_get_ntohl(tvb, suboptoff));
                  break;

            default:
                  proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,"ERROR, please report: Unknown subopt type handler %d", subopt);
                  break;
            }
      }
      optoff += (subopt_len + 2);
      return optoff;
}



static int
dissect_netware_ip_suboption(proto_tree *v_tree, tvbuff_t *tvb,
    int optoff, int optend)
{
      int suboptoff = optoff;
      guint8 subopt;
      guint8 subopt_len;
      int suboptleft;
      const struct true_false_string *tfs;
      int i;
      proto_tree *o63_v_tree;
      proto_item *vti;

      static struct opt_info o63_opt[]= {
            /* 0 */ {"",none,NULL},
            /* 1 */ {"NWIP does not exist on subnet",presence,NULL},
            /* 2 */ {"NWIP exists in options area",presence,NULL},
            /* 3 */ {"NWIP exists in sname/file",presence,NULL},
            /* 4 */ {"NWIP exists, but too big",presence,NULL},
            /* 5 */ {"Broadcast for nearest Netware server",val_boolean,TFS(&yes_no_tfs)},
            /* 6 */ {"Preferred DSS server",ipv4_list,NULL},
            /* 7 */ {"Nearest NWIP server",ipv4_list,NULL},
            /* 8 */ {"Autoretries",val_u_byte,NULL},
            /* 9 */ {"Autoretry delay, secs",val_u_byte,NULL},
            /* 10*/ {"Support NetWare/IP v1.1",val_boolean,TFS(&yes_no_tfs)},
            /* 11*/ {"Primary DSS",ipv4,NULL}
      };

      subopt = tvb_get_guint8(tvb, optoff);
      suboptoff++;

      if (suboptoff >= optend) {
            proto_tree_add_text(v_tree, tvb, optoff, 1,
                  "Suboption %d: no room left in option for suboption length",
                  subopt);
            return (optend);
      }
      subopt_len = tvb_get_guint8(tvb, suboptoff);
      suboptoff++;

      if (subopt >= array_length(o63_opt)) {
            proto_tree_add_text(v_tree, tvb, optoff, subopt_len + 2, "Unknown suboption %d", subopt);
      } else {
            switch (o63_opt[subopt].ftype) {

            case presence:
                  if (subopt_len != 0) {
                        proto_tree_add_text(v_tree, tvb, optoff, subopt_len + 2,
                              "Suboption %d: length isn't 0", subopt);
                        break;
                  }
                  proto_tree_add_text(v_tree, tvb, optoff, 2, "Suboption %d: %s", subopt, o63_opt[subopt].text);
                  break;

            case ipv4:
                  if (subopt_len != 4) {
                        proto_tree_add_text(v_tree, tvb, optoff, subopt_len + 2,
                              "Suboption %d: length isn't 4", subopt);
                        break;
                  }
                  if (suboptoff+4 > optend) {
                        proto_tree_add_text(v_tree, tvb, optoff, optend-optoff,
                            "Suboption %d: no room left in option for suboption value",
                            subopt);
                        return (optend);
                  }
                  proto_tree_add_text(v_tree, tvb, optoff, 6,
                      "Suboption %d: %s = %s" ,
                      subopt, o63_opt[subopt].text,
                      ip_to_str(tvb_get_ptr(tvb, suboptoff, 4)));
                  break;

            case ipv4_list:
                  if (subopt_len == 4) {
                        /* one IP address */
                        proto_tree_add_text(v_tree, tvb, optoff, 6,
                            "Suboption %d : %s = %s",
                            subopt, o63_opt[subopt].text,
                            ip_to_str(tvb_get_ptr(tvb, suboptoff, 4)));
                  } else {
                        /* > 1 IP addresses. Let's make a sub-tree */
                        vti = proto_tree_add_text(v_tree, tvb, optoff,
                            subopt_len+2, "Suboption %d: %s",
                            subopt, o63_opt[subopt].text);
                        o63_v_tree = proto_item_add_subtree(vti, ett_bootp_option);
                        for (suboptleft = subopt_len; suboptleft > 0;
                            suboptoff += 4, suboptleft -= 4) {
                              if (suboptleft < 4) {
                                    proto_tree_add_text(o63_v_tree,
                                        tvb, suboptoff, suboptleft,
                                        "Suboption length isn't a multiple of 4");
                                    break;
                              }
                              proto_tree_add_text(o63_v_tree, tvb, suboptoff, 4, "IP Address: %s",
                                  ip_to_str(tvb_get_ptr(tvb, suboptoff, 4)));
                        }
                  }
                  break;

            case val_boolean:
                  if (subopt_len != 1) {
                        proto_tree_add_text(v_tree, tvb, optoff, subopt_len + 2,
                              "Suboption %d: suboption length isn't 1", subopt);
                        break;
                  }
                  if (suboptoff+1 > optend) {
                        proto_tree_add_text(v_tree, tvb, optoff, optend-optoff,
                            "Suboption %d: no room left in option for suboption value",
                            subopt);
                        return (optend);
                  }
                  tfs = (const struct true_false_string *) o63_opt[subopt].data;
                  i = tvb_get_guint8(tvb, suboptoff);
                  if (i != 0 && i != 1) {
                        proto_tree_add_text(v_tree, tvb, optoff, 3,
                            "Suboption %d: %s = Invalid Value %d",
                            subopt, o63_opt[subopt].text, i);
                  } else {
                        proto_tree_add_text(v_tree, tvb, optoff, 3,
                            "Suboption %d: %s = %s", subopt,
                            o63_opt[subopt].text,
                            i == 0 ? tfs->false_string : tfs->true_string);
                  }
                  break;

            case val_u_byte:
                  if (subopt_len != 1) {
                        proto_tree_add_text(v_tree, tvb, optoff, subopt_len + 2,
                              "Suboption %d: length isn't 1", subopt);
                        break;
                  }
                  if (suboptoff+1 > optend) {
                        proto_tree_add_text(v_tree, tvb, optoff, optend-optoff,
                            "Suboption %d: no room left in option for suboption value",
                            subopt);
                        return (optend);
                  }
                  proto_tree_add_text(v_tree, tvb, optoff, 3, "Suboption %d: %s = %u",
                      subopt, o63_opt[subopt].text,
                      tvb_get_guint8(tvb, suboptoff));
                  break;

            default:
                  proto_tree_add_text(v_tree, tvb, optoff, subopt_len + 2,"Unknown suboption %d", subopt);
                  break;
            }
      }
      optoff += (subopt_len + 2);
      return optoff;
}



static int
dissect_vendor_tr111_suboption(proto_tree *v_tree, tvbuff_t *tvb,
    int optoff, int optend)
{
      int suboptoff = optoff;
      guint8 subopt;
      guint8 subopt_len;

      /* Reference: TR-111 DHCP Option 125 Sub-Option Data Fields
           Page 10.
         */

      static struct opt_info o125_tr111_opt[]= {
            /* 0 */ {"nop", special, NULL},     /* dummy */
            /* 1 */ {"DeviceManufacturerOUI", string, NULL},
            /* 2 */ {"DeviceSerialNumber", string, NULL},
            /* 3 */ {"DeviceProductClass", string, NULL},
            /* 4 */ {"GatewayManufacturerOUI", string, NULL},
            /* 5 */ {"GatewaySerialNumber", string, NULL},
            /* 6 */ {"GatewayProductClass", string, NULL},
      };

      subopt = tvb_get_guint8(tvb, suboptoff);
      suboptoff++;

      if (suboptoff >= optend) {
            proto_tree_add_text(v_tree, tvb, optoff, 1,
                  "Suboption %d: no room left in option for suboption length",
                  subopt);
            return (optend);
      }
      subopt_len = tvb_get_guint8(tvb, suboptoff);
      suboptoff++;

      if (suboptoff+subopt_len > optend) {
            proto_tree_add_text(v_tree, tvb, optoff, optend-optoff,
                  "Suboption %d: no room left in option for suboption value",
                  subopt);
            return (optend);
      }


      if ((subopt < 1) || (subopt >= array_length(o125_tr111_opt))) {
            proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                  "Unknown suboption %d (%d byte%s)", subopt, subopt_len,
                  plurality(subopt_len, "", "s"));
      } else {
            switch (o125_tr111_opt[subopt].ftype) {

            case special:
                  /* I may need to decode that properly one day */
                  proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                        "Suboption %d: %s (%d byte%s)",
                        subopt, o125_tr111_opt[subopt].text,
                        subopt_len, plurality(subopt_len, "", "s"));
                  break;

            case string:
                  proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,
                        "Suboption %d: %s = \"%s\"", subopt,
                        o125_tr111_opt[subopt].text,
                        tvb_format_stringzpad(tvb, suboptoff, subopt_len));
                  break;

            default:
                  proto_tree_add_text(v_tree, tvb, optoff, subopt_len+2,"ERROR, please report: Unknown subopt type handler %d", subopt);
                  break;
            }
      }
      optoff += (subopt_len + 2);
      return optoff;
}


/* PacketCable Multimedia Terminal Adapter device capabilities (option 60).
   Ref: PKT-SP-I05-021127 sections 8.2 and 10 */

#define PKT_MDC_TLV_OFF 10


/* These are ASCII-encoded hexadecimal digits.  We use the raw hex equivalent for
   convenience. */
#define PKT_MDC_VERSION             0x3031  /* "01" */
#define PKT_MDC_TEL_END             0x3032  /* "02" */
#define PKT_MDC_TGT                 0x3033  /* "03" */
#define PKT_MDC_HTTP_ACC            0x3034  /* "04" */
#define PKT_MDC_SYSLOG              0x3035  /* "05" */
#define PKT_MDC_NCS                 0x3036  /* "06" */
#define PKT_MDC_PRI_LINE            0x3037  /* "07" */
#define PKT_MDC_VENDOR_TLV          0x3038  /* "08" */
#define PKT_MDC_NVRAM_STOR          0x3039  /* "09" */
#define PKT_MDC_PROV_REP            0x3041  /* "0A" */
#define PKT_MDC_PROV_REP_LC         0x3061  /* "0A" */
#define PKT_MDC_SUPP_CODECS         0x3042  /* "0B" */
#define PKT_MDC_SUPP_CODECS_LC            0x3062  /* "0b" */
#define PKT_MDC_SILENCE             0x3043  /* "0C" */
#define PKT_MDC_SILENCE_LC          0x3063  /* "0c" */
#define PKT_MDC_ECHO_CANCEL         0x3044  /* "0D" */
#define PKT_MDC_ECHO_CANCEL_LC            0x3064  /* "0d" */
#define PKT_MDC_RSVP                0x3045  /* "0E" */
#define PKT_MDC_RSVP_LC             0x3065  /* "0e" */
#define PKT_MDC_UGS_AD              0x3046  /* "0F" */
#define PKT_MDC_UGS_AD_LC           0x3066  /* "0f" */
#define PKT_MDC_IF_INDEX            0x3130  /* "10" */
#define PKT_MDC_FLOW_LOG            0x3131  /* "11" */
#define PKT_MDC_PROV_FLOWS          0x3132      /* "12" */
/* PacketCable 1.5: */
#define PKT_MDC_T38_VERSION         0x3133      /* "13" */
#define     PKT_MDC_T38_EC                0x3134      /* "14" */
#define     PKT_MDC_RFC2833_DTMF          0x3135      /* "15" */
#define PKT_MDC_VOICE_METRICS       0x3136      /* "16" */
#define     PKT_MDC_MIBS                  0x3137      /* "17" */
#define     PKT_MDC_MGPI                  0x3138      /* "18" */

static const value_string pkt_mdc_type_vals[] = {
      { PKT_MDC_VERSION,            "PacketCable Version" },
      { PKT_MDC_TEL_END,            "Number Of Telephony Endpoints" },
      { PKT_MDC_TGT,                "TGT Support" },
      { PKT_MDC_HTTP_ACC,           "HTTP Download File Access Method Support" },
      { PKT_MDC_SYSLOG,       "MTA-24 Event SYSLOG Notification Support" },
      { PKT_MDC_NCS,                "NCS Service Flow Support" },
      { PKT_MDC_PRI_LINE,           "Primary Line Support" },
      { PKT_MDC_VENDOR_TLV,         "Vendor Specific TLV Type(s)" },
      { PKT_MDC_NVRAM_STOR,         "NVRAM Ticket/Session Keys Storage Support" },
      { PKT_MDC_PROV_REP,           "Provisioning Event Reporting Support" },
      { PKT_MDC_PROV_REP_LC,        "Provisioning Event Reporting Support" },
      { PKT_MDC_SUPP_CODECS,        "Supported CODEC(s)" },
      { PKT_MDC_SUPP_CODECS_LC,     "Supported CODEC(s)" },
      { PKT_MDC_SILENCE,            "Silence Suppression Support" },
      { PKT_MDC_SILENCE_LC,         "Silence Suppression Support" },
      { PKT_MDC_ECHO_CANCEL,        "Echo Cancellation Support" },
      { PKT_MDC_ECHO_CANCEL_LC,     "Echo Cancellation Support" },
      { PKT_MDC_RSVP,               "RSVP Support/ Reserved" },
      { PKT_MDC_RSVP_LC,            "RSVP Support/ Reserved" },
      { PKT_MDC_UGS_AD,       "UGS-AD Support" },
      { PKT_MDC_UGS_AD_LC,          "UGS-AD Support" },
      { PKT_MDC_IF_INDEX,           "MTA's \"ifIndex\" starting number in \"ifTable\"" },
      { PKT_MDC_FLOW_LOG,           "Provisioning Flow Logging Support" },
      { PKT_MDC_PROV_FLOWS,         "Supported Provisioning Flows" },
      /* PacketCable 1.5: */
      { PKT_MDC_T38_VERSION,        "T38 Version Support" },
      { PKT_MDC_T38_EC,       "T38 Error Correction Support" },
      { PKT_MDC_RFC2833_DTMF,       "RFC 2833 DTMF Support" },
      { PKT_MDC_VOICE_METRICS,      "Voice Metrics Support" },
      { PKT_MDC_MIBS,               "MIB Support" },
      { PKT_MDC_MGPI,               "Multiple Grants Per Interval Support" },
      { 0,                          NULL }
};

static const value_string pkt_mdc_version_vals[] = {
      { 0x3030,   "PacketCable 1.0" },
      { 0x3031,   "PacketCable 1.1/1.5" }, /* 1.5 replaces 1.1-1.3 */
      { 0x3032,   "PacketCable 1.2" },
      { 0x3033,   "PacketCable 1.3" },
      { 0,        NULL }
};

static const value_string pkt_mdc_boolean_vals[] = {
      { 0x3030,   "No" },
      { 0x3031,   "Yes" },
      { 0,        NULL }
};

static const value_string pkt_mdc_codec_vals[] = {
      { 0x3031,   "other" },           /* "01" */
      { 0x3032,   "unknown" },
      { 0x3033,   "G.729" },
      { 0x3034,   "reserved" },
      { 0x3035,   "G.729E" },
      { 0x3036,   "PCMU" },
      { 0x3037,   "G.726-32" },
      { 0x3038,   "G.728" },
      { 0x3039,   "PCMA" },            /* "09" */
      { 0x3041,   "G.726-16" },        /* "0A" */
      { 0x3042,   "G.726-24" },
      { 0x3043,   "G.726-40" },
      { 0x3044,   "iLBC" },
      { 0x3045,   "BV16" },
      { 0x3046,   "telephone-event" }, /* "0F" */
      { 0,        NULL }
};

static const value_string pkt_mdc_t38_version_vals[] = {
      { 0x3030,   "Unsupported" },
      { 0x3031,   "T.38 Version Zero" }, /* default */
      { 0x3032,   "T.38 Version One" },
      { 0x3033,   "T.38 Version Two" },
      { 0x3035,   "T.38 Version Three" },
      { 0,        NULL }
};

static const value_string pkt_mdc_t38_ec_vals[] = {
      { 0x3030,   "None" },
      { 0x3031,   "Redundancy" }, /* default */
      { 0x3032,   "FEC" },
      { 0,        NULL }
};

static const value_string pkt_mdc_mib_orgs[] = {
      { 0x3030,   "CableLabs" },
      { 0x3031,   "IETF" },
      { 0x3032,   "Reserved" },
      { 0x3033,   "Reserved" },
      { 0x3034,   "Reserved" },
      { 0x3035,   "Reserved" },
      { 0x3036,   "Reserved" },
      { 0x3037,   "Reserved" },
      { 0x3038,   "Reserved" },
      { 0x3039,   "Reserved" },
      { 0,        NULL }
};

/* DOCSIS Cable Modem device capabilities (option 60). */
#define DOCS_CM_TLV_OFF 12

#define DOCS_CM_CONCAT_SUP    0x3031  /* "01" */
#define DOCS_CM_DOCSIS_VER    0x3032  /* "02" */
#define DOCS_CM_FRAG_SUP      0x3033  /* "03" */
#define DOCS_CM_PHS_SUP       0x3034  /* "04" */
#define DOCS_CM_IGMP_SUP      0x3035  /* "05" */
#define DOCS_CM_PRIV_SUP      0x3036  /* "06" */
#define DOCS_CM_DSAID_SUP     0x3037  /* "07" */
#define DOCS_CM_USID_SUP      0x3038  /* "08" */
#define DOCS_CM_FILT_SUP      0x3039  /* "09" */
#define DOCS_CM_TET_MI        0x3041  /* "0A" */
#define DOCS_CM_TET_MI_LC     0x3061  /* "0a" */
#define DOCS_CM_TET           0x3042  /* "0B" */
#define DOCS_CM_TET_LC        0x3062  /* "0b" */
#define DOCS_CM_DCC_SUP       0x3043  /* "0C" */
#define DOCS_CM_DCC_SUP_LC    0x3063  /* "0c" */
#define DOCS_CM_IPFILT_SUP    0x3044  /* "0D" */
#define DOCS_CM_IPFILT_SUP_LC 0x3064  /* "0d" */
#define DOCS_CM_LLCFILT_SUP   0x3045  /* "0E" */
#define DOCS_CM_LLCFILT_SUP_LC      0x3065  /* "0e" */

static const value_string docs_cm_type_vals[] = {
      { DOCS_CM_CONCAT_SUP,   "Concatenation Support" },
      { DOCS_CM_DOCSIS_VER,   "DOCSIS Version" },
      { DOCS_CM_FRAG_SUP,     "Fragmentation Support" },
      { DOCS_CM_PHS_SUP,      "PHS Support" },
      { DOCS_CM_IGMP_SUP,     "IGMP Support" },
      { DOCS_CM_PRIV_SUP,     "Privacy Support" },
      { DOCS_CM_DSAID_SUP,    "Downstream SAID Support" },
      { DOCS_CM_USID_SUP,     "Upstream SID Support" },
      { DOCS_CM_FILT_SUP,     "Optional Filtering Support" },
      { DOCS_CM_TET_MI, "Transmit Equalizer Taps per Modulation Interval" },
      { DOCS_CM_TET_MI_LC,    "Transmit Equalizer Taps per Modulation Interval" },
      { DOCS_CM_TET,          "Number of Transmit Equalizer Taps" },
      { DOCS_CM_TET_LC, "Number of Transmit Equalizer Taps" },
      { DOCS_CM_DCC_SUP,      "DCC Support" },
      { DOCS_CM_DCC_SUP_LC,   "DCC Support" },
      { DOCS_CM_IPFILT_SUP,   "IP Filters Support" },
      { DOCS_CM_IPFILT_SUP_LC,      "IP Filters Support" },
      { DOCS_CM_LLCFILT_SUP,  "LLC Filters Support" },
      { DOCS_CM_LLCFILT_SUP_LC,     "LLC Filters Support" },
      { 0, NULL }
};

static const value_string docs_cm_version_vals[] = {
      { 0x3030,   "DOCSIS 1.0" },
      { 0x3031,   "DOCSIS 1.1" },
      { 0x3032,   "DOCSIS 2.0" },
      { 0,        NULL }
};

static const value_string docs_cm_privacy_vals[] = {
      { 0x3030,   "BPI Support" },
      { 0x3031,   "BPI Plus Support" },
      { 0,        NULL }
};


static const value_string pkt_mdc_supp_flow_vals[] = {
      { 1 << 0, "Secure Flow (Full Secure Provisioning Flow)" },
      { 1 << 1, "Hybrid Flow" },
      { 1 << 2, "Basic Flow" },
      { 0, NULL }
};

#define PKT_MDC_MIB_CL 0x3030
static const value_string pkt_mdc_cl_mib_vals[] = {
      { 1 << 0, "PacketCable 1.5 MTA MIB" },
      { 1 << 1, "PacketCable 1.5 Signaling MIB" },
      { 1 << 2, "PacketCable 1.5 Management Event MIB" },
      { 1 << 3, "PacketCable 1.5 MTA Extension MIB" },
      { 1 << 4, "PacketCable 1.5 Signaling Extension MIB" },
      { 1 << 5, "PacketCable 1.5 MEM Extension MIB" },
      { 1 << 6, "Reserved" },
      { 1 << 7, "Reserved" },
      { 0, NULL }
};

#define PKT_MDC_MIB_IETF 0x3031
static const value_string pkt_mdc_ietf_mib_vals[] = {
      { 1 << 0, "IETF MTA MIB" },
      { 1 << 1, "IETF Signaling MIB" },
      { 1 << 2, "IETF Management Event MIB" },
      { 1 << 3, "Reserved" },
      { 1 << 4, "Reserved" },
      { 1 << 5, "Reserved" },
      { 1 << 6, "Reserved" },
      { 1 << 7, "Reserved" },
      { 0, NULL }
};


static void
dissect_packetcable_mta_cap(proto_tree *v_tree, tvbuff_t *tvb, int voff, int len)
{
      guint16 raw_val;
      unsigned long flow_val = 0;
      int off = PKT_MDC_TLV_OFF + voff;
      int tlv_len, i, subopt_off, max_len, mib_val;
      guint8 asc_val[3] = "  ", flow_val_str[5];
      char bit_fld[64];
      proto_item *ti, *mib_ti;
      proto_tree *subtree, *subtree2;

      tvb_memcpy (tvb, asc_val, off, 2);
      if (sscanf((gchar*)asc_val, "%x", &tlv_len) != 1 || tlv_len < 1) {
            proto_tree_add_text(v_tree, tvb, off, len - off,
                  "Bogus length: %s", asc_val);
            return;
      } else {
            proto_tree_add_uint_format_value(v_tree, hf_bootp_pkt_mtacap_len, tvb, off, 2,
                        tlv_len, "%d", tlv_len);
            off += 2;

            while (off - voff < len) {
                  /* Type */
                  raw_val = tvb_get_ntohs (tvb, off);

                  /* Length */
                  tvb_memcpy(tvb, asc_val, off + 2, 2);
                  if (sscanf((gchar*)asc_val, "%x", &tlv_len) != 1 || tlv_len < 1) {
                        proto_tree_add_text(v_tree, tvb, off, len - off,
                                          "[Bogus length: %s]", asc_val);
                        return;
                  } else {
                        /* Value(s) */

                        ti = proto_tree_add_text(v_tree,
                            tvb, off, (tlv_len * 2) + 4,
                            "0x%s: %s = ",
                            tvb_format_text(tvb, off, 2),
                            val_to_str(raw_val, pkt_mdc_type_vals, "unknown"));
                        switch (raw_val) {
                              case PKT_MDC_VERSION:
                                    raw_val = tvb_get_ntohs(tvb, off + 4);
                                    proto_item_append_text(ti,
                                        "%s (%s)",
                                        val_to_str(raw_val, pkt_mdc_version_vals, "Reserved"),
                                        tvb_format_stringzpad(tvb, off + 4, 2) );
                                    break;
                              case PKT_MDC_TEL_END:
                              case PKT_MDC_IF_INDEX:
                                    proto_item_append_text(ti,
                                        "%s",
                                        tvb_format_stringzpad(tvb, off + 4, 2) );
                                    break;
                              case PKT_MDC_TGT:
                              case PKT_MDC_HTTP_ACC:
                              case PKT_MDC_SYSLOG:
                              case PKT_MDC_NCS:
                              case PKT_MDC_PRI_LINE:
                              case PKT_MDC_NVRAM_STOR:
                              case PKT_MDC_PROV_REP:
                              case PKT_MDC_PROV_REP_LC:
                              case PKT_MDC_SILENCE:
                              case PKT_MDC_SILENCE_LC:
                              case PKT_MDC_ECHO_CANCEL:
                              case PKT_MDC_ECHO_CANCEL_LC:
                              case PKT_MDC_RSVP:
                              case PKT_MDC_RSVP_LC:
                              case PKT_MDC_UGS_AD:
                              case PKT_MDC_UGS_AD_LC:
                              case PKT_MDC_FLOW_LOG:
                              case PKT_MDC_RFC2833_DTMF:
                              case PKT_MDC_VOICE_METRICS:
                                    raw_val = tvb_get_ntohs(tvb, off + 4);
                                    proto_item_append_text(ti,
                                        "%s (%s)",
                                        val_to_str(raw_val, pkt_mdc_boolean_vals, "unknown"),
                                        tvb_format_stringzpad(tvb, off + 4, 2) );
                                    break;
                              case PKT_MDC_SUPP_CODECS:
                              case PKT_MDC_SUPP_CODECS_LC:
                                    for (i = 0; i < tlv_len; i++) {
                                          raw_val = tvb_get_ntohs(tvb, off + 4 + (i * 2) );
                                          proto_item_append_text(ti,
                                              "%s%s (%s)",
                                              plurality(i + 1, "", ", "),
                                              val_to_str(raw_val, pkt_mdc_codec_vals, "unknown"),
                                              tvb_format_stringzpad(tvb, off + 4 + (i * 2), 2) );
                                    }
                                    break;
                              case PKT_MDC_PROV_FLOWS:
                                    tvb_memcpy(tvb, flow_val_str, off + 4, 4);
                                    flow_val_str[4] = '\0';
                                    flow_val = strtoul((gchar*)flow_val_str, NULL, 16);
                                    proto_item_append_text(ti,
                                        "0x%04lx", flow_val);
                                    break;
                              case PKT_MDC_T38_VERSION:
                                    raw_val = tvb_get_ntohs(tvb, off + 4);
                                    proto_item_append_text(ti,
                                        "%s (%s)",
                                        val_to_str(raw_val, pkt_mdc_t38_version_vals, "unknown"),
                                        tvb_format_stringzpad(tvb, off + 4, 2) );
                                    break;
                              case PKT_MDC_T38_EC:
                                    raw_val = tvb_get_ntohs(tvb, off + 4);
                                    proto_item_append_text(ti,
                                        "%s (%s)",
                                        val_to_str(raw_val, pkt_mdc_t38_ec_vals, "unknown"),
                                        tvb_format_stringzpad(tvb, off + 4, 2) );
                                    break;
                              case PKT_MDC_MIBS:
                                    break;
                              case PKT_MDC_VENDOR_TLV:
                              default:
                                    proto_item_append_text(ti,
                                        "%s",
                                        tvb_format_stringzpad(tvb, off + 4, tlv_len * 2) );
                                    break;
                        }
                  }
                  subtree = proto_item_add_subtree(ti, ett_bootp_option);
                  if (raw_val == PKT_MDC_PROV_FLOWS) {
                        for (i = 0 ; i < 3; i++) {
                              if (flow_val & pkt_mdc_supp_flow_vals[i].value) {
                                    decode_bitfield_value(bit_fld, flow_val, pkt_mdc_supp_flow_vals[i].value, 16);
                                    proto_tree_add_text(subtree, tvb, off + 4, 4, "%s%s",
                                          bit_fld, pkt_mdc_supp_flow_vals[i].strptr);
                              }
                        }
                  } else if (raw_val == PKT_MDC_MIBS) {
                  /* 17 06 02 00 38 02 01 07 */
                        subopt_off = off + 4;
                        max_len = subopt_off + (tlv_len * 2);
                        while (subopt_off < max_len) {
                              raw_val = tvb_get_ntohs(tvb, subopt_off);
                              if (raw_val != 0x3032) { /* We only know how to handle a length of 2 */
                                    tvb_memcpy(tvb, asc_val, subopt_off, 2);
                                    proto_tree_add_text(subtree, tvb, subopt_off, 2,
                                                      "[Bogus length: %s]", asc_val);
                                    return;
                              }

                              subopt_off += 2;
                              raw_val = tvb_get_ntohs(tvb, subopt_off);
                              tvb_memcpy(tvb, asc_val, subopt_off, 2);

                              mib_ti = proto_tree_add_text(subtree, tvb, subopt_off, 2, "%s (%s)",
                                    val_to_str(raw_val, pkt_mdc_mib_orgs, "Unknown"), asc_val);
                              if (subopt_off > off + 4 + 2) {
                                    proto_item_append_text(ti, ", ");
                              }
                              proto_item_append_text(ti, "%s", val_to_str(raw_val, pkt_mdc_mib_orgs, "Unknown"));

                              subopt_off += 2;
                              tvb_memcpy(tvb, asc_val, subopt_off, 2);
                              if (sscanf((gchar*)asc_val, "%x", &mib_val) != 1) {
                                    proto_tree_add_text(v_tree, tvb, subopt_off, 2,
                                                      "[Bogus bitfield: %s]", asc_val);
                                    return;
                              }
                              switch (raw_val) {
                                    case PKT_MDC_MIB_CL:
                                          subtree2 = proto_item_add_subtree(mib_ti, ett_bootp_option);

                                          for (i = 0; i < 8; i++) {
                                                if (mib_val & pkt_mdc_cl_mib_vals[i].value) {
                                                      decode_bitfield_value(bit_fld, mib_val, pkt_mdc_cl_mib_vals[i].value, 8);
                                                      proto_tree_add_text(subtree2, tvb, subopt_off, 2,
                                                            "%s%s", bit_fld, pkt_mdc_cl_mib_vals[i].strptr);
                                                }
                                          }
                                          break;
                                    case PKT_MDC_MIB_IETF:
                                          subtree2 = proto_item_add_subtree(mib_ti, ett_bootp_option);

                                          for (i = 0; i < 8; i++) {
                                                if (mib_val & pkt_mdc_ietf_mib_vals[i].value) {
                                                      decode_bitfield_value(bit_fld, mib_val, pkt_mdc_ietf_mib_vals[i].value, 8);
                                                      proto_tree_add_text(subtree2, tvb, subopt_off, 2,
                                                            "%s%s", bit_fld, pkt_mdc_ietf_mib_vals[i].strptr);
                                                }
                                          }
                                          break;
                                    default:
                                          break;
                              }
                              subopt_off += 2;
                        }

                  }
                  off += (tlv_len * 2) + 4;
            }
      }
}

static void
dissect_docsis_cm_cap(proto_tree *v_tree, tvbuff_t *tvb, int voff, int len)
{
      unsigned long raw_val;
      int off = DOCS_CM_TLV_OFF + voff;
      int tlv_len, i;
      guint8 asc_val[4] = "  ";
      proto_item *ti;

      tvb_memcpy (tvb, asc_val, off, 2);
      if (sscanf((gchar*)asc_val, "%x", &tlv_len) != 1 || tlv_len < 1) {
            proto_tree_add_text(v_tree, tvb, off, len - off,
                            "Bogus length: %s", asc_val);
            return;
      } else {
            proto_tree_add_uint_format_value(v_tree, hf_bootp_docsis_cmcap_len, tvb, off, 2,
                        tlv_len, "%d", tlv_len);
            off += 2;

            while (off - voff < len) {
                  /* Type */
                  raw_val = tvb_get_ntohs (tvb, off);

                  /* Length */
                  tvb_memcpy(tvb, asc_val, off + 2, 2);
                  if (sscanf((gchar*)asc_val, "%x", &tlv_len) != 1 || tlv_len < 1) {
                        proto_tree_add_text(v_tree, tvb, off, len - off,
                                          "[Bogus length: %s]", asc_val);
                        return;
                  } else {
                        /* Value(s) */
                        ti = proto_tree_add_text(v_tree, tvb, off,
                            (tlv_len * 2) + 4,
                            "0x%s: %s = ",
                            tvb_format_text(tvb, off, 2),
                            val_to_str(raw_val, docs_cm_type_vals, "unknown"));
                        switch (raw_val) {
                              case DOCS_CM_CONCAT_SUP:
                              case DOCS_CM_FRAG_SUP:
                              case DOCS_CM_PHS_SUP:
                              case DOCS_CM_IGMP_SUP:
                              case DOCS_CM_DCC_SUP:
                              case DOCS_CM_DCC_SUP_LC:
                                    for (i = 0; i < tlv_len; i++) {
                                          raw_val = tvb_get_ntohs(tvb, off + 4 + (i * 2) );
                                          proto_item_append_text(ti,
                                              "%s%s (%s)",
                                              plurality(i + 1, "", ", "),
                                              val_to_str(raw_val, pkt_mdc_boolean_vals, "unknown"),
                                              tvb_format_text(tvb, off + 4 + (i * 2), 2) );
                                    }
                                    break;
                              case DOCS_CM_DOCSIS_VER:
                                    raw_val = tvb_get_ntohs(tvb, off + 4);
                                    proto_item_append_text(ti,
                                        "%s (%s)",
                                        val_to_str(raw_val, docs_cm_version_vals, "Reserved"),
                                        tvb_format_text(tvb, off + 4, 2) );
                                    break;
                              case DOCS_CM_PRIV_SUP:
                                    raw_val = tvb_get_ntohs(tvb, off + 4);
                                    proto_item_append_text(ti,
                                        "%s (%s)",
                                        val_to_str(raw_val, docs_cm_privacy_vals, "Reserved"),
                                        tvb_format_text(tvb, off + 4, 2) );
                                    break;
                              case DOCS_CM_DSAID_SUP:
                              case DOCS_CM_USID_SUP:
                              case DOCS_CM_TET_MI:
                              case DOCS_CM_TET_MI_LC:
                              case DOCS_CM_TET:
                              case DOCS_CM_TET_LC:
                                    tvb_memcpy (tvb, asc_val, off + 4, 2);
                                    raw_val = strtoul((gchar*)asc_val, NULL, 16);
                                    proto_item_append_text(ti,
                                        "%lu", raw_val);
                                    break;
                              case DOCS_CM_IPFILT_SUP:
                              case DOCS_CM_IPFILT_SUP_LC:
                              case DOCS_CM_LLCFILT_SUP:
                              case DOCS_CM_LLCFILT_SUP_LC:
                                    tvb_memcpy (tvb, asc_val, off + 4, 4);
                                    raw_val = strtoul((gchar*)asc_val, NULL, 16);
                                    proto_item_append_text(ti,
                                        "%lu", raw_val);
                                    break;
                              case DOCS_CM_FILT_SUP:
                                    tvb_memcpy (tvb, asc_val, off + 4, 2);
                                    raw_val = strtoul((gchar*)asc_val, NULL, 16);
                                    if (raw_val & 0x01)
                                          proto_item_append_text(ti,
                                              "802.1p filtering");
                                    if (raw_val & 0x02) {
                                          if (raw_val & 0x01)
                                                proto_item_append_text(ti, ", ");
                                          proto_item_append_text(ti,
                                              "802.1Q filtering");
                                    }
                                    if (! raw_val & 0x03)
                                          proto_item_append_text(ti,
                                              "None");
                                    proto_item_append_text(ti,
                                        " (0x%02lx)", raw_val);
                                    break;
                        }
                  }
                  off += (tlv_len * 2) + 4;
            }
      }
}


/* Definitions specific to PKT-SP-PROV-I05-021127 begin with "PKT_CCC_I05".
   Definitions specific to IETF draft 5 and RFC 3495 begin with "PKT_CCC_IETF".
   Shared definitions begin with "PKT_CCC".
 */
#define PKT_CCC_PRI_DHCP       1
#define PKT_CCC_SEC_DHCP       2
#define PKT_CCC_I05_SNMP       3
#define PKT_CCC_IETF_PROV_SRV  3
#define PKT_CCC_I05_PRI_DNS    4
#define PKT_CCC_IETF_AS_KRB    4
#define PKT_CCC_I05_SEC_DNS    5
#define PKT_CCC_IETF_AP_KRB    5
#define PKT_CCC_KRB_REALM      6
#define PKT_CCC_TGT_FLAG       7
#define PKT_CCC_PROV_TIMER     8
#define PKT_CCC_CMS_FQDN       9
#define PKT_CCC_IETF_SEC_TKT   9
#define PKT_CCC_AS_KRB        10
#define PKT_CCC_AP_KRB        11
#define PKT_CCC_MTA_KRB_CLEAR 12

static const value_string pkt_i05_ccc_opt_vals[] = {
      { PKT_CCC_PRI_DHCP,           "Primary DHCP Server" },
      { PKT_CCC_SEC_DHCP,           "Secondary DHCP Server" },
      { PKT_CCC_I05_SNMP,           "SNMP Entity" },
      { PKT_CCC_I05_PRI_DNS,        "Primary DNS Server" },
      { PKT_CCC_I05_SEC_DNS,        "Secondary DNS Server" },
      { PKT_CCC_KRB_REALM,          "Kerberos Realm" },
      { PKT_CCC_TGT_FLAG,           "MTA should fetch TGT?" },
      { PKT_CCC_PROV_TIMER,         "Provisioning Timer" },
      { PKT_CCC_CMS_FQDN,           "CMS FQDN" },
      { PKT_CCC_AS_KRB,       "AS-REQ/AS-REP Backoff and Retry" },
      { PKT_CCC_AP_KRB,       "AP-REQ/AP-REP Backoff and Retry" },
      { PKT_CCC_MTA_KRB_CLEAR,      "MTA should clear Kerberos tickets?" },
      { 0, NULL },
};

static const value_string pkt_draft5_ccc_opt_vals[] = {
      { PKT_CCC_PRI_DHCP,           "TSP's Primary DHCP Server" },
      { PKT_CCC_SEC_DHCP,           "TSP's Secondary DHCP Server" },
      { PKT_CCC_IETF_PROV_SRV,      "TSP's Provisioning Server" },
      { PKT_CCC_IETF_AS_KRB,        "TSP's AS-REQ/AS-REP Backoff and Retry" },
      { PKT_CCC_IETF_AP_KRB,        "TSP's AP-REQ/AP-REP Backoff and Retry" },
      { PKT_CCC_KRB_REALM,          "TSP's Kerberos Realm Name" },
      { PKT_CCC_TGT_FLAG,           "TSP's Ticket Granting Server Utilization" },
      { PKT_CCC_PROV_TIMER,         "TSP's Provisioning Timer Value" },
      { PKT_CCC_IETF_SEC_TKT,       "PacketCable Security Ticket Control" },
      { 0, NULL },
};

static const value_string pkt_i05_ccc_ticket_ctl_vals[] = {
      { 1, "Invalidate Provisioning Application Server's ticket" },
      { 2, "Invalidate all CMS Application Server tickets" },
      { 3, "Invalidate all Application Server tickets" },
      { 0, NULL },
};

static int
dissect_packetcable_i05_ccc(proto_tree *v_tree, tvbuff_t *tvb, int optoff,
    int optend)
{
      int suboptoff = optoff;
      guint8 subopt, subopt_len, fetch_tgt, timer_val, ticket_ctl;
      proto_tree *pkt_s_tree;
      proto_item *vti;

      subopt = tvb_get_guint8(tvb, optoff);
      suboptoff++;

      if (suboptoff >= optend) {
            proto_tree_add_text(v_tree, tvb, optoff, 1,
                  "Suboption %d: no room left in option for suboption length",
                  subopt);
            return (optend);
      }
      subopt_len = tvb_get_guint8(tvb, optoff);
      suboptoff++;

      vti = proto_tree_add_text(v_tree, tvb, optoff, subopt_len + 2,
          "Suboption %u: %s: ", subopt,
          val_to_str(subopt, pkt_i05_ccc_opt_vals, "unknown/reserved") );

      switch (subopt) {
            case PKT_CCC_PRI_DHCP:  /* String values */
            case PKT_CCC_SEC_DHCP:
            case PKT_CCC_I05_SNMP:
            case PKT_CCC_I05_PRI_DNS:
            case PKT_CCC_I05_SEC_DNS:
            case PKT_CCC_KRB_REALM:
            case PKT_CCC_CMS_FQDN:
                  proto_item_append_text(vti, "%s (%u byte%s)",
                              tvb_format_stringzpad(tvb, suboptoff, subopt_len),
                              subopt_len,
                              plurality(subopt_len, "", "s") );
                  suboptoff += subopt_len;
                  break;

            case PKT_CCC_TGT_FLAG:
                  if (suboptoff+1 > optend) {
                        proto_item_append_text(vti,
                            "no room left in option for suboption value");
                        return (optend);
                  }
                  fetch_tgt = tvb_get_guint8(tvb, suboptoff);
                  proto_item_append_text(vti, "%s (%u byte%s%s)",
                              fetch_tgt ? "Yes" : "No",
                              subopt_len,
                              plurality(subopt_len, "", "s"),
                              subopt_len != 1 ? " [Invalid]" : "");
                  suboptoff += subopt_len;
                  break;

            case PKT_CCC_PROV_TIMER:
                  if (suboptoff+1 > optend) {
                        proto_item_append_text(vti,
                            "no room left in option for suboption value");
                        return (optend);
                  }
                  timer_val = tvb_get_guint8(tvb, suboptoff);
                  proto_item_append_text(vti, "%u%s (%u byte%s%s)", timer_val,
                              timer_val > 30 ? " [Invalid]" : "",
                              subopt_len,
                              plurality(subopt_len, "", "s"),
                              subopt_len != 1 ? " [Invalid]" : "");
                  suboptoff += subopt_len;
                  break;

            case PKT_CCC_AS_KRB:
                  if (suboptoff+12 > optend) {
                        proto_item_append_text(vti,
                            "no room left in option for suboption value");
                        return (optend);
                  }
                  proto_item_append_text(vti, "(%u byte%s%s)", subopt_len,
                              plurality(subopt_len, "", "s"),
                              subopt_len != 12 ? " [Invalid]" : "");
                  if (subopt_len == 12) {
                        pkt_s_tree = proto_item_add_subtree(vti, ett_bootp_option);
                        proto_tree_add_text(pkt_s_tree, tvb, suboptoff, 4,
                                    "pktcMtaDevRealmUnsolicitedKeyNomTimeout: %u",
                                    tvb_get_ntohl(tvb, suboptoff));
                        proto_tree_add_text(pkt_s_tree, tvb, suboptoff + 4, 4,
                                    "pktcMtaDevRealmUnsolicitedKeyMaxTimeout: %u",
                                    tvb_get_ntohl(tvb, suboptoff + 4));
                        proto_tree_add_text(pkt_s_tree, tvb, suboptoff + 8, 4,
                                    "pktcMtaDevRealmUnsolicitedKeyMaxRetries: %u",
                                    tvb_get_ntohl(tvb, suboptoff + 8));
                  }
                  suboptoff += subopt_len;
                  break;

            case PKT_CCC_AP_KRB:
                  if (suboptoff+12 > optend) {
                        proto_item_append_text(vti,
                            "no room left in option for suboption value");
                        return (optend);
                  }
                  proto_item_append_text(vti, "(%u byte%s%s)", subopt_len,
                              plurality(subopt_len, "", "s"),
                              subopt_len != 12 ? " [Invalid]" : "");
                  if (subopt_len == 12) {
                        pkt_s_tree = proto_item_add_subtree(vti, ett_bootp_option);
                        proto_tree_add_text(pkt_s_tree, tvb, suboptoff, 4,
                                    "pktcMtaDevProvUnsolicitedKeyNomTimeout: %u",
                                    tvb_get_ntohl(tvb, suboptoff));
                        proto_tree_add_text(pkt_s_tree, tvb, suboptoff + 4, 4,
                                    "pktcMtaDevProvUnsolicitedKeyMaxTimeout: %u",
                                    tvb_get_ntohl(tvb, suboptoff + 4));
                        proto_tree_add_text(pkt_s_tree, tvb, suboptoff + 8, 4,
                                    "pktcMtaDevProvUnsolicitedKeyMaxRetries: %u",
                                    tvb_get_ntohl(tvb, suboptoff + 8));
                  }
                  suboptoff += subopt_len;
                  break;

            case PKT_CCC_MTA_KRB_CLEAR:
                  if (suboptoff+1 > optend) {
                        proto_item_append_text(vti,
                            "no room left in option for suboption value");
                        return (optend);
                  }
                  ticket_ctl = tvb_get_guint8(tvb, suboptoff);
                  proto_item_append_text(vti, "%s (%u) (%u byte%s%s)",
                              val_to_str (ticket_ctl, pkt_i05_ccc_ticket_ctl_vals, "unknown/invalid"),
                              ticket_ctl,
                              subopt_len,
                              plurality(subopt_len, "", "s"),
                              subopt_len != 1 ? " [Invalid]" : "");
                  suboptoff += subopt_len;
                  break;

            default:
                  suboptoff += subopt_len;
                  break;

      }
      return suboptoff;
}


static const value_string sec_tcm_vals[] = {
      { 1 << 0, "PacketCable Provisioning Server" },
      { 1 << 1, "All PacketCable Call Management Servers" },
      { 0, NULL }
};

static int
dissect_packetcable_ietf_ccc(proto_tree *v_tree, tvbuff_t *tvb, int optoff,
    int optend, int revision)
{
      int suboptoff = optoff;
      guint8 subopt, subopt_len;
      guint32 ipv4_addr;
      guint8 prov_type, fetch_tgt, timer_val;
      guint16 sec_tcm;
      proto_tree *pkt_s_tree;
      proto_item *vti;
      int max_timer_val = 255, i;
      const char *dns_name;
      char bit_fld[24];

      subopt = tvb_get_guint8(tvb, suboptoff);
      suboptoff++;

      if (suboptoff >= optend) {
            proto_tree_add_text(v_tree, tvb, optoff, 1,
                  "Suboption %d: no room left in option for suboption length",
                  subopt);
            return (optend);
      }
      subopt_len = tvb_get_guint8(tvb, suboptoff);
      suboptoff++;

      vti = proto_tree_add_text(v_tree, tvb, optoff, subopt_len + 2,
          "Suboption %u: %s: ", subopt,
          val_to_str(subopt, pkt_draft5_ccc_opt_vals, "unknown/reserved") );

      switch (subopt) {
            case PKT_CCC_PRI_DHCP:  /* IPv4 values */
            case PKT_CCC_SEC_DHCP:
                  if (suboptoff+4 > optend) {
                        proto_item_append_text(vti,
                            "no room left in option for suboption value");
                        return (optend);
                  }
                  ipv4_addr = tvb_get_ipv4(tvb, suboptoff);
                  proto_item_append_text(vti, "%s (%u byte%s%s)",
                              ip_to_str((guint8 *)&ipv4_addr),
                              subopt_len,
                              plurality(subopt_len, "", "s"),
                              subopt_len != 4 ? " [Invalid]" : "");
                  suboptoff += subopt_len;
                  break;

            case PKT_CCC_IETF_PROV_SRV:
                  if (suboptoff+1 > optend) {
                        proto_item_append_text(vti,
                            "no room left in option for suboption value");
                        return (optend);
                  }
                  prov_type = tvb_get_guint8(tvb, suboptoff);
                  suboptoff += 1;
                  switch (prov_type) {
                        case 0:
                              /* XXX - check suboption length */
                              get_dns_name(tvb, suboptoff, suboptoff, &dns_name);
                              proto_item_append_text(vti, "%s (%u byte%s)", dns_name,
                                          subopt_len - 1, plurality(subopt_len, "", "s") );
                              break;
                        case 1:
                              if (suboptoff+4 > optend) {
                                    proto_item_append_text(vti,
                                        "no room left in option for suboption value");
                                    return (optend);
                              }
                              ipv4_addr = tvb_get_ipv4(tvb, suboptoff);
                              proto_item_append_text(vti, "%s (%u byte%s%s)",
                                          ip_to_str((guint8 *)&ipv4_addr),
                                          subopt_len,
                                          plurality(subopt_len, "", "s"),
                                          subopt_len != 5 ? " [Invalid]" : "");
                              break;
                        default:
                              proto_item_append_text(vti, "Invalid type: %u (%u byte%s)",
                                          prov_type,
                                          subopt_len,
                                          plurality(subopt_len, "", "s") );
                              break;
                  }
                  suboptoff += subopt_len - 1;
                  break;

            case PKT_CCC_IETF_AS_KRB:
                  if (suboptoff+12 > optend) {
                        proto_item_append_text(vti,
                            "no room left in option for suboption value");
                        return (optend);
                  }
                  proto_item_append_text(vti, "(%u byte%s%s)", subopt_len,
                              plurality(subopt_len, "", "s"),
                              subopt_len != 12 ? " [Invalid]" : "");
                  if (subopt_len == 12) {
                        pkt_s_tree = proto_item_add_subtree(vti, ett_bootp_option);
                        proto_tree_add_text(pkt_s_tree, tvb, suboptoff, 4,
                                    "pktcMtaDevRealmUnsolicitedKeyNomTimeout: %u",
                                    tvb_get_ntohl(tvb, suboptoff));
                        proto_tree_add_text(pkt_s_tree, tvb, suboptoff + 4, 4,
                                    "pktcMtaDevRealmUnsolicitedKeyMaxTimeout: %u",
                                    tvb_get_ntohl(tvb, suboptoff + 4));
                        proto_tree_add_text(pkt_s_tree, tvb, suboptoff + 8, 4,
                                    "pktcMtaDevRealmUnsolicitedKeyMaxRetries: %u",
                                    tvb_get_ntohl(tvb, suboptoff + 8));
                  }
                  suboptoff += subopt_len;
                  break;

            case PKT_CCC_IETF_AP_KRB:
                  proto_item_append_text(vti, "(%u byte%s%s)", subopt_len,
                              plurality(subopt_len, "", "s"),
                              subopt_len != 12 ? " [Invalid]" : "");
                  if (subopt_len == 12) {
                        pkt_s_tree = proto_item_add_subtree(vti, ett_bootp_option);
                        proto_tree_add_text(pkt_s_tree, tvb, suboptoff, 4,
                              "pktcMtaDevProvUnsolicitedKeyNomTimeout: %u",
                              tvb_get_ntohl(tvb, suboptoff));
                        proto_tree_add_text(pkt_s_tree, tvb, suboptoff + 4, 4,
                                    "pktcMtaDevProvUnsolicitedKeyMaxTimeout: %u",
                                    tvb_get_ntohl(tvb, suboptoff + 4));
                        proto_tree_add_text(pkt_s_tree, tvb, suboptoff + 8, 4,
                              "pktcMtaDevProvUnsolicitedKeyMaxRetries: %u",
                              tvb_get_ntohl(tvb, suboptoff + 8));
                  }
                  suboptoff += subopt_len;
                  break;

            case PKT_CCC_KRB_REALM: /* String values */
                  /* XXX - check suboption length */
                  get_dns_name(tvb, suboptoff, suboptoff, &dns_name);
                  proto_item_append_text(vti, "%s (%u byte%s)", dns_name,
                              subopt_len, plurality(subopt_len, "", "s") );
                  suboptoff += subopt_len;
                  break;

            case PKT_CCC_TGT_FLAG:
                  if (suboptoff+1 > optend) {
                        proto_item_append_text(vti,
                            "no room left in option for suboption value");
                        return (optend);
                  }
                  fetch_tgt = tvb_get_guint8(tvb, suboptoff);
                  proto_item_append_text(vti, "%s (%u byte%s%s)",
                              fetch_tgt ? "Yes" : "No",
                              subopt_len,
                              plurality(subopt_len, "", "s"),
                              subopt_len != 1 ? " [Invalid]" : "");
                  suboptoff += 1;
                  break;

            case PKT_CCC_PROV_TIMER:
                  if (suboptoff+1 > optend) {
                        proto_item_append_text(vti,
                            "no room left in option for suboption value");
                        return (optend);
                  }
                  if (revision == PACKETCABLE_CCC_DRAFT5)
                        max_timer_val = 30;
                  timer_val = tvb_get_guint8(tvb, suboptoff);
                  proto_item_append_text(vti, "%u%s (%u byte%s%s)", timer_val,
                              timer_val > max_timer_val ? " [Invalid]" : "",
                              subopt_len,
                              plurality(subopt_len, "", "s"),
                              subopt_len != 1 ? " [Invalid]" : "");
                  suboptoff += 1;
                  break;

            case PKT_CCC_IETF_SEC_TKT:
                  if (suboptoff+2 > optend) {
                        proto_item_append_text(vti,
                            "no room left in option for suboption value");
                        return (optend);
                  }
                  sec_tcm = tvb_get_ntohs(tvb, suboptoff);
                  proto_item_append_text(vti, "0x%04x (%u byte%s%s)", sec_tcm, subopt_len,
                              plurality(subopt_len, "", "s"),
                              subopt_len != 2 ? " [Invalid]" : "");
                  if (subopt_len == 2) {
                        pkt_s_tree = proto_item_add_subtree(vti, ett_bootp_option);
                        for (i = 0; i < 2; i++) {
                              if (sec_tcm & sec_tcm_vals[i].value) {
                                    decode_bitfield_value(bit_fld, sec_tcm, sec_tcm_vals[i].value, 16);
                                    proto_tree_add_text(pkt_s_tree, tvb, suboptoff, 2, "%sInvalidate %s",
                                          bit_fld, sec_tcm_vals[i].strptr);
                              }
                        }
                  }
                  suboptoff += subopt_len;
                  break;

            default:
                  suboptoff += subopt_len;
                  break;
      }
      return suboptoff;
}

#define BOOTREQUEST     1
#define BOOTREPLY 2

static const value_string op_vals[] = {
      { BOOTREQUEST,    "Boot Request" },
      { BOOTREPLY,      "Boot Reply" },
      { 0,        NULL }
};

static void
dissect_bootp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
      proto_tree  *bp_tree = NULL;
      proto_item  *ti;
      proto_tree  *flag_tree = NULL;
      proto_item  *fi;
      guint8            op;
      guint8            htype, hlen;
      const guint8      *haddr;
      int         voff, eoff, tmpvoff; /* vendor offset, end offset */
      guint32           ip_addr;
      gboolean    at_end;
      const char  *dhcp_type = NULL;
      const guint8      *vendor_class_id = NULL;
      guint16           flags;
      int         offset_delta;

      if (check_col(pinfo->cinfo, COL_PROTOCOL))
            col_set_str(pinfo->cinfo, COL_PROTOCOL, "BOOTP");
      if (check_col(pinfo->cinfo, COL_INFO)) {
            /*
             * In case we throw an exception fetching the opcode, etc.
             */
            col_clear(pinfo->cinfo, COL_INFO);
      }

      op = tvb_get_guint8(tvb, 0);
      htype = tvb_get_guint8(tvb, 1);
      hlen = tvb_get_guint8(tvb, 2);
      if (check_col(pinfo->cinfo, COL_INFO)) {
            switch (op) {

            case BOOTREQUEST:
                  if ((htype == ARPHRD_ETHER || htype == ARPHRD_IEEE802)
                      && hlen == 6)
                        col_add_fstr(pinfo->cinfo, COL_INFO, "Boot Request from %s (%s)",
                            arphrdaddr_to_str(tvb_get_ptr(tvb, 28, hlen),
                                hlen, htype),
                            get_ether_name(tvb_get_ptr(tvb, 28, hlen)));
                  else
                        col_add_fstr(pinfo->cinfo, COL_INFO, "Boot Request from %s",
                            arphrdaddr_to_str(tvb_get_ptr(tvb, 28, hlen),
                              hlen, htype));
                  break;

            case BOOTREPLY:
                  col_set_str(pinfo->cinfo, COL_INFO, "Boot Reply");
                  break;

            default:
                  col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown BOOTP message type (%u)",
                      op);
                  break;
            }
      }

      if (tree) {
            ti = proto_tree_add_item(tree, proto_bootp, tvb, 0, -1, FALSE);
            bp_tree = proto_item_add_subtree(ti, ett_bootp);

            proto_tree_add_uint(bp_tree, hf_bootp_type, tvb,
                                 0, 1,
                                 op);
            proto_tree_add_uint_format_value(bp_tree, hf_bootp_hw_type, tvb,
                                     1, 1,
                                     htype,
                                     "%s",
                                     arphrdtype_to_str(htype,
                                               "Unknown (0x%02x)"));
            proto_tree_add_uint(bp_tree, hf_bootp_hw_len, tvb,
                            2, 1, hlen);
            proto_tree_add_item(bp_tree, hf_bootp_hops, tvb,
                            3, 1, FALSE);
            proto_tree_add_item(bp_tree, hf_bootp_id, tvb,
                            4, 4, FALSE);
            proto_tree_add_item(bp_tree, hf_bootp_secs, tvb,
                            8, 2, FALSE);
            flags = tvb_get_ntohs(tvb, 10);
            fi = proto_tree_add_uint(bp_tree, hf_bootp_flags, tvb,
                            10, 2, flags);
            proto_item_append_text(fi, " (%s)",
                (flags & BOOTP_BC) ? "Broadcast" : "Unicast");
            flag_tree = proto_item_add_subtree(fi, ett_bootp_flags);
            proto_tree_add_boolean(flag_tree, hf_bootp_flags_broadcast, tvb,
                            10, 2, flags);
            proto_tree_add_uint(flag_tree, hf_bootp_flags_reserved, tvb,
                            10, 2, flags);
            proto_tree_add_item(bp_tree, hf_bootp_ip_client, tvb,
                            12, 4, FALSE);
            proto_tree_add_item(bp_tree, hf_bootp_ip_your, tvb,
                            16, 4, FALSE);
            proto_tree_add_item(bp_tree, hf_bootp_ip_server, tvb,
                            20, 4, FALSE);
            proto_tree_add_item(bp_tree, hf_bootp_ip_relay, tvb,
                            24, 4, FALSE);

            if (hlen > 0 && hlen <= 16) {
                  haddr = tvb_get_ptr(tvb, 28, hlen);
                  if ((htype == ARPHRD_ETHER || htype == ARPHRD_IEEE802)
                      && hlen == 6)
                        proto_tree_add_ether(bp_tree, hf_bootp_hw_ether_addr, tvb, 28, 6, haddr);
                  else
                        /* The chaddr element is 16 bytes in length,
                           although only the first hlen bytes are used */
                        proto_tree_add_bytes_format_value(bp_tree, hf_bootp_hw_addr, tvb,
                                       28, 16,
                                       haddr,
                                       "%s",
                                       arphrdaddr_to_str(haddr,
                                                     hlen,
                                                     htype));
            }
            else {
                  proto_tree_add_text(bp_tree,  tvb,
                                       28, 16, "Client address not given");
            }

            /* The server host name is optional */
            if (tvb_get_guint8(tvb, SERVER_NAME_OFFSET) != '\0') {
                  proto_tree_add_item(bp_tree, hf_bootp_server, tvb,
                                       SERVER_NAME_OFFSET,
                                       SERVER_NAME_LEN, FALSE);
            }
            else {
                  proto_tree_add_string_format(bp_tree, hf_bootp_server, tvb,
                                       SERVER_NAME_OFFSET,
                                       SERVER_NAME_LEN,
                                       (const gchar*)tvb_get_ptr(tvb, SERVER_NAME_OFFSET, 1),
                                       "Server host name not given");
            }

            /* Boot file */
            if (tvb_get_guint8(tvb, FILE_NAME_OFFSET) != '\0') {
                  proto_tree_add_item(bp_tree, hf_bootp_file, tvb,
                                       FILE_NAME_OFFSET,
                                       FILE_NAME_LEN, FALSE);
            }
            else {
                  proto_tree_add_string_format(bp_tree, hf_bootp_file, tvb,
                                       FILE_NAME_OFFSET,
                                       FILE_NAME_LEN,
                                       (const gchar*)tvb_get_ptr(tvb, FILE_NAME_OFFSET, 1),
                                       "Boot file name not given");
            }
      }

      voff = VENDOR_INFO_OFFSET;

      /* rfc2132 says it SHOULD exist, not that it MUST exist */
      if (tvb_bytes_exist(tvb, voff, 4)) {
            if (tvb_get_ntohl(tvb, voff) == 0x63825363) {
                  if (tree) {
                        ip_addr = tvb_get_ipv4(tvb, voff);
                        proto_tree_add_ipv4_format_value(bp_tree, hf_bootp_cookie, tvb,
                            voff, 4, ip_addr,
                            "(OK)");
                  }
                  voff += 4;
            }
            else {
                  if (tree) {
                        proto_tree_add_text(bp_tree,  tvb,
                              voff, 64, "Bootp vendor specific options");
                  }
                  voff += 64;
            }
      }

      eoff = tvb_reported_length(tvb);

      /*
       * In the first pass, we just look for the DHCP message type
       * and Vendor class identifier options.
       */
      tmpvoff = voff;
      at_end = FALSE;
      while (tmpvoff < eoff && !at_end) {
            offset_delta = bootp_option(tvb, 0, tmpvoff, eoff, TRUE, &at_end,
                &dhcp_type, &vendor_class_id);
            if (offset_delta <= 0) {
                  THROW(ReportedBoundsError);
            }
            tmpvoff += offset_delta;
      }

      /*
       * If there was a DHCP message type option, flag this packet
       * as DHCP.
       */
      if (dhcp_type != NULL) {
            /*
             * Yes, this is a DHCP packet, and "dhcp_type" is the
             * packet type.
             */
            if (check_col(pinfo->cinfo, COL_PROTOCOL))
                  col_set_str(pinfo->cinfo, COL_PROTOCOL, "DHCP");
            if (check_col(pinfo->cinfo, COL_INFO))
                  col_add_fstr(pinfo->cinfo, COL_INFO, "DHCP %-8s - Transaction ID 0x%x",
                      dhcp_type, tvb_get_ntohl(tvb, 4));
            if (tree)
                  proto_tree_add_boolean_hidden(bp_tree, hf_bootp_dhcp,
                      tvb, 0, 0, 1);
            tap_queue_packet( bootp_dhcp_tap, pinfo, dhcp_type);
      }

      /*
       * If we're not building the protocol tree, we don't need to
       * make a second pass.
       */
      if (tree == NULL)
            return;

      /*
       * OK, now build the protocol tree.
       */
      at_end = FALSE;
      while (voff < eoff && !at_end) {
            offset_delta = bootp_option(tvb, bp_tree, voff, eoff, FALSE, &at_end,
                &dhcp_type, &vendor_class_id);
            if (offset_delta <= 0) {
                  THROW(ReportedBoundsError);
            }
            voff += offset_delta;
      }
      if (voff < eoff) {
            /*
             * Padding after the end option.
             */
            proto_tree_add_text(bp_tree, tvb, voff, eoff - voff, "Padding");
      }
}

void
proto_register_bootp(void)
{
  static hf_register_info hf[] = {
    { &hf_bootp_dhcp,
      { "Frame is DHCP",                "bootp.dhcp",    FT_BOOLEAN,
        BASE_NONE,                  NULL,        0x0,
        "", HFILL }},

    { &hf_bootp_type,
      { "Message type",             "bootp.type",      FT_UINT8,
         BASE_DEC,                  VALS(op_vals),   0x0,
            "", HFILL }},

    { &hf_bootp_hw_type,
      { "Hardware type",                  "bootp.hw.type", FT_UINT8,
        BASE_HEX,             NULL,        0x0,
            "", HFILL }},

    { &hf_bootp_hw_len,
      { "Hardware address length",  "bootp.hw.len",  FT_UINT8,
        BASE_DEC,             NULL,        0x0,
            "", HFILL }},

    { &hf_bootp_hops,
      { "Hops",                           "bootp.hops",      FT_UINT8,
        BASE_DEC,             NULL,        0x0,
            "", HFILL }},

    { &hf_bootp_id,
      { "Transaction ID",                 "bootp.id",  FT_UINT32,
        BASE_HEX,              NULL,             0x0,
            "", HFILL }},

    { &hf_bootp_secs,
      { "Seconds elapsed",                "bootp.secs",      FT_UINT16,
        BASE_DEC,              NULL,             0x0,
            "", HFILL }},

    { &hf_bootp_flags,
      { "Bootp flags",                    "bootp.flags",   FT_UINT16,
        BASE_HEX,             NULL,        0x0,
            "", HFILL }},

    { &hf_bootp_flags_broadcast,
      { "Broadcast flag",                 "bootp.flags.bc", FT_BOOLEAN,
        16,             TFS(&flag_set_broadcast), BOOTP_BC,
            "", HFILL }},

    { &hf_bootp_flags_reserved,
      { "Reserved flags",                 "bootp.flags.reserved", FT_UINT16,
        BASE_HEX,             NULL,       BOOTP_MBZ,
            "", HFILL }},

    { &hf_bootp_ip_client,
      { "Client IP address",              "bootp.ip.client",FT_IPv4,
        BASE_NONE,                  NULL,         0x0,
            "", HFILL }},

    { &hf_bootp_ip_your,
      { "Your (client) IP address", "bootp.ip.your",  FT_IPv4,
        BASE_NONE,                  NULL,         0x0,
            "", HFILL }},

    { &hf_bootp_ip_server,
      { "Next server IP address",   "bootp.ip.server",FT_IPv4,
        BASE_NONE,                  NULL,         0x0,
            "", HFILL }},

    { &hf_bootp_ip_relay,
      { "Relay agent IP address",   "bootp.ip.relay", FT_IPv4,
        BASE_NONE,                  NULL,         0x0,
            "", HFILL }},

    { &hf_bootp_hw_addr,
      { "Client hardware address",  "bootp.hw.addr", FT_BYTES,
        BASE_NONE,                  NULL,        0x0,
            "", HFILL }},

    { &hf_bootp_hw_ether_addr,
      { "Client MAC address",       "bootp.hw.mac_addr", FT_ETHER,
        BASE_NONE,                  NULL,        0x0,
            "", HFILL }},

    { &hf_bootp_server,
      { "Server host name",         "bootp.server",  FT_STRING,
        BASE_NONE,                  NULL,        0x0,
            "", HFILL }},

    { &hf_bootp_file,
      { "Boot file name",           "bootp.file",      FT_STRING,
        BASE_NONE,                  NULL,        0x0,
            "", HFILL }},

    { &hf_bootp_cookie,
      { "Magic cookie",             "bootp.cookie",    FT_IPv4,
         BASE_NONE,                 NULL,        0x0,
            "", HFILL }},

    { &hf_bootp_vendor,
      { "Bootp Vendor Options",           "bootp.vendor", FT_BYTES,
        BASE_NONE,                  NULL,        0x0,
            "", HFILL }},

    { &hf_bootp_fqdn_s,
      { "Server",       "bootp.fqdn.s",         FT_BOOLEAN,
        8,              TFS(&tfs_fqdn_s), F_FQDN_S,
            "If true, server should do DDNS update", HFILL }},

    { &hf_bootp_fqdn_o,
      { "Server overrides",   "bootp.fqdn.o",         FT_BOOLEAN,
        8,              TFS(&tfs_fqdn_o), F_FQDN_O,
            "If true, server insists on doing DDNS update", HFILL }},

    { &hf_bootp_fqdn_e,
      { "Encoding",     "bootp.fqdn.e",         FT_BOOLEAN,
        8,              TFS(&tfs_fqdn_e), F_FQDN_E,
            "If true, name is binary encoded", HFILL }},

    { &hf_bootp_fqdn_n,
      { "Server DDNS",  "bootp.fqdn.n",         FT_BOOLEAN,
        8,              TFS(&tfs_fqdn_n), F_FQDN_N,
            "If true, server should not do any DDNS updates", HFILL }},

    { &hf_bootp_fqdn_mbz,
      { "Reserved flags",     "bootp.fqdn.mbz", FT_UINT8,
        BASE_HEX,       NULL,             F_FQDN_MBZ,
            "", HFILL }},

    { &hf_bootp_fqdn_rcode1,
      { "A-RR result",              "bootp.fqdn.rcode1",     FT_UINT8,
        BASE_DEC,       NULL,              0x0,
            "Result code of A-RR update", HFILL }},

    { &hf_bootp_fqdn_rcode2,
      { "PTR-RR result",            "bootp.fqdn.rcode2",     FT_UINT8,
        BASE_DEC,       NULL,              0x0,
            "Result code of PTR-RR update", HFILL }},

    { &hf_bootp_fqdn_name,
      { "Client name",        "bootp.fqdn.name",      FT_BYTES,
        BASE_NONE,            NULL,             0x0,
            "Name to register via DDNS", HFILL }},

    { &hf_bootp_fqdn_asciiname,
      { "Client name",        "bootp.fqdn.name",      FT_STRING,
        BASE_NONE,            NULL,             0x0,
            "Name to register via DDNS", HFILL }},

    { &hf_bootp_pkt_mtacap_len,
      { "MTA DC Length",      "bootp.vendor.pktc.mtacap_len",
        FT_UINT8, BASE_DEC, NULL, 0x0,
        "PacketCable MTA Device Capabilities Length", HFILL }},

    { &hf_bootp_docsis_cmcap_len,
      { "CM DC Length",       "bootp.vendor.docsis.cmcap_len",
        FT_UINT8, BASE_DEC, NULL, 0x0,
        "DOCSIS Cable Modem Device Capabilities Length", HFILL }},

    { &hf_bootp_alcatel_vid,
      { "Voice VLAN ID",      "bootp.vendor.alcatel.vid",
        FT_UINT16, BASE_DEC, NULL, 0x0,
        "Alcatel VLAN ID to define Voice VLAN", HFILL }},

    { &hf_bootp_client_identifier_uuid,
      { "Client Identifier (UUID)",    "bootp.client_id_uuid",
      FT_GUID, BASE_NONE, NULL, 0x0,
      "Client Machine Identifier (UUID)", HFILL }},

    { &hf_bootp_client_network_id_major_ver,
      { "Client Network ID Major Version",    "bootp.client_network_id_major",
      FT_UINT8, BASE_DEC, NULL, 0x0,
      "Client Machine Identifier, Major Version", HFILL }},

    { &hf_bootp_client_network_id_minor_ver,
      { "Client Network ID Minor Version",    "bootp.client_network_id_minor",
      FT_UINT8, BASE_DEC, NULL, 0x0,
      "Client Machine Identifier, Major Version", HFILL }},

    { &hf_bootp_option_type,
      { "Option", "bootp.option.type",
        FT_UINT8, BASE_DEC, NULL, 0x0,
        "Bootp/Dhcp option type", HFILL }},

    { &hf_bootp_option_length,
      { "Length", "bootp.option.length",
        FT_UINT8, BASE_DEC, NULL, 0x0,
        "Bootp/Dhcp option length", HFILL }},

    { &hf_bootp_option_value,
      { "Value",  "bootp.option.value",
        FT_BYTES, BASE_NONE, NULL, 0x0,
        "Bootp/Dhcp option value", HFILL }},

  };

  static gint *ett[] = {
    &ett_bootp,
    &ett_bootp_flags,
    &ett_bootp_option,
    &ett_bootp_fqdn,
  };

  module_t *bootp_module;

  proto_bootp = proto_register_protocol("Bootstrap Protocol", "BOOTP/DHCP",
                              "bootp");
  proto_register_field_array(proto_bootp, hf, array_length(hf));
  proto_register_subtree_array(ett, array_length(ett));
  bootp_dhcp_tap = register_tap("bootp");

  /* Allow dissector to find be found by name. */
  register_dissector("bootp", dissect_bootp, proto_bootp);

  bootp_module = prefs_register_protocol(proto_bootp, NULL);

  prefs_register_bool_preference(bootp_module, "novellserverstring",
    "Decode Option 85 as String",
    "Novell Servers option 85 can be configured as a string instead of address",
    &novell_string);

  prefs_register_enum_preference(bootp_module, "pkt.ccc.protocol_version",
    "PacketCable CCC protocol version",
    "The PacketCable CCC protocol version",
    &pkt_ccc_protocol_version,
    pkt_ccc_protocol_versions,
    FALSE);

  prefs_register_uint_preference(bootp_module, "pkt.ccc.option",
    "PacketCable CCC option",
    "Option Number for PacketCable CableLabs Client Configuration",
    10,
    &pkt_ccc_option);


}

void
proto_reg_handoff_bootp(void)
{
  dissector_handle_t bootp_handle;

  bootp_handle = create_dissector_handle(dissect_bootp, proto_bootp);
  dissector_add("udp.port", UDP_PORT_BOOTPS, bootp_handle);
  dissector_add("udp.port", UDP_PORT_BOOTPC, bootp_handle);
}

Generated by  Doxygen 1.6.0   Back to index