wireshark/epan/dissectors/packet-dhcpv6.c

1718 lines
55 KiB
C

/* packet-dhpcv6.c
* Routines for DHCPv6 packet disassembly
* Copyright 2004, Nicolas DICHTEL - 6WIND - <nicolas.dichtel@6wind.com>
* Jun-ichiro itojun Hagino <itojun@iijlab.net>
* IItom Tsutomu MIENO <iitom@utouto.com>
* SHIRASAKI Yasuhiro <yasuhiro@gnome.gr.jp>
* Tony Lindstrom <tony.lindstrom@ericsson.com>
*
* $Id$
*
* The information used comes from:
* RFC3315.txt (DHCPv6)
* RFC3319.txt (SIP options)
* RFC3633.txt (Prefix options)
* RFC3646.txt (DNS servers/domains)
* RFC3898.txt (NIS options)
* RFC4704.txt (Client FQDN)
* RFC5007.txt (DHCPv6 Leasequery)
* RFC5417.txt (CAPWAP Access Controller DHCP Option)
* draft-ietf-dhc-dhcpv6-opt-timeconfig-03.txt
* draft-ietf-dhc-dhcpv6-opt-lifetime-00.txt
*
* Note that protocol constants are still subject to change, based on IANA
* assignment decisions.
*
* 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.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include <glib.h>
#include <epan/packet.h>
#include <epan/sminmpec.h>
#include <epan/strutil.h>
#include "packet-arp.h"
static int proto_dhcpv6 = -1;
static int hf_dhcpv6_msgtype = -1;
static int hf_fqdn_1 = -1;
static int hf_fqdn_2 = -1;
static int hf_fqdn_3 = -1;
static int hf_fqdn_4 = -1;
static gint ett_dhcpv6 = -1;
static gint ett_dhcpv6_option = -1;
static gint ett_dhcpv6_option_vsoption = -1;
static gint ett_dhcpv6_vendor_option = -1;
static gint ett_dhcpv6_pkt_option = -1;
#define UDP_PORT_DHCPV6_DOWNSTREAM 546
#define UDP_PORT_DHCPV6_UPSTREAM 547
#define DHCPV6_LEASEDURATION_INFINITY 0xffffffff
#define SOLICIT 1
#define ADVERTISE 2
#define REQUEST 3
#define CONFIRM 4
#define RENEW 5
#define REBIND 6
#define REPLY 7
#define RELEASE 8
#define DECLINE 9
#define RECONFIGURE 10
#define INFORMATION_REQUEST 11
#define RELAY_FORW 12
#define RELAY_REPLY 13
#define LEASEQUERY 14
#define LEASEQUERY_REPLY 15
#define OPTION_CLIENTID 1
#define OPTION_SERVERID 2
#define OPTION_IA_NA 3
#define OPTION_IA_TA 4
#define OPTION_IAADDR 5
#define OPTION_ORO 6
#define OPTION_PREFERENCE 7
#define OPTION_ELAPSED_TIME 8
#define OPTION_RELAY_MSG 9
/* #define OPTION_SERVER_MSG 10 */
#define OPTION_AUTH 11
#define OPTION_UNICAST 12
#define OPTION_STATUS_CODE 13
#define OPTION_RAPID_COMMIT 14
#define OPTION_USER_CLASS 15
#define OPTION_VENDOR_CLASS 16
#define OPTION_VENDOR_OPTS 17
#define OPTION_INTERFACE_ID 18
#define OPTION_RECONF_MSG 19
#define OPTION_RECONF_ACCEPT 20
#define OPTION_SIP_SERVER_D 21
#define OPTION_SIP_SERVER_A 22
#define OPTION_DNS_SERVERS 23
#define OPTION_DOMAIN_LIST 24
#define OPTION_IA_PD 25
#define OPTION_IAPREFIX 26
#define OPTION_NIS_SERVERS 27
#define OPTION_NISP_SERVERS 28
#define OPTION_NIS_DOMAIN_NAME 29
#define OPTION_NISP_DOMAIN_NAME 30
#define OPTION_SNTP_SERVERS 31
#define OPTION_LIFETIME 32
#define OPTION_BCMCS_SERVER_D 33
#define OPTION_BCMCS_SERVER_A 34
#define OPTION_GEOCONF_CIVIC 36
#define OPTION_REMOTE_ID 37
#define OPTION_SUBSCRIBER_ID 38
#define OPTION_CLIENT_FQDN 39
#define OPTION_PANA_AGENT 40
#define OPTION_TIME_ZONE 41
#define OPTION_TZDB 42
#define OPTION_ERO 43
#define OPTION_LQ_QUERY 44
#define OPTION_CLIENT_DATA 45
#define OPTION_CLT_TIME 46
#define OPTION_LQ_RELAY_DATA 47
#define OPTION_LQ_CLIENT_LINK 48
#define OPTION_CAPWAP_AC_V6 52
/* temporary value until defined by IETF */
#define OPTION_MIP6_HA 165
#define OPTION_MIP6_HOA 166
#define OPTION_NAI 167
#define DUID_LLT 1
#define DUID_EN 2
#define DUID_LL 3
#define DUID_LL_OLD 4
static void
dissect_dhcpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
gboolean downstream, int off, int eoff);
static const value_string msgtype_vals[] = {
{ SOLICIT, "Solicit" },
{ ADVERTISE, "Advertise" },
{ REQUEST, "Request" },
{ CONFIRM, "Confirm" },
{ RENEW, "Renew" },
{ REBIND, "Rebind" },
{ REPLY, "Reply" },
{ RELEASE, "Release" },
{ DECLINE, "Decline" },
{ RECONFIGURE, "Reconfigure" },
{ INFORMATION_REQUEST, "Information-request" },
{ RELAY_FORW, "Relay-forw" },
{ RELAY_REPLY, "Relay-reply" },
{ LEASEQUERY, "Leasequery" },
{ LEASEQUERY_REPLY, "Leasequery-reply" },
{ 0, NULL }
};
static const value_string opttype_vals[] = {
{ OPTION_CLIENTID, "Client Identifier" },
{ OPTION_SERVERID, "Server Identifier" },
{ OPTION_IA_NA, "Identity Association for Non-temporary Address" },
{ OPTION_IA_TA, "Identity Association for Temporary Address" },
{ OPTION_IAADDR, "IA Address" },
{ OPTION_ORO, "Option Request" },
{ OPTION_PREFERENCE, "Preference" },
{ OPTION_ELAPSED_TIME, "Elapsed time" },
{ OPTION_RELAY_MSG, "Relay Message" },
/* { OPTION_SERVER_MSG, "Server message" }, */
{ OPTION_AUTH, "Authentication" },
{ OPTION_UNICAST, "Server unicast" },
{ OPTION_STATUS_CODE, "Status code" },
{ OPTION_RAPID_COMMIT, "Rapid Commit" },
{ OPTION_USER_CLASS, "User Class" },
{ OPTION_VENDOR_CLASS, "Vendor Class" },
{ OPTION_VENDOR_OPTS, "Vendor-specific Information" },
{ OPTION_INTERFACE_ID, "Interface-Id" },
{ OPTION_RECONF_MSG, "Reconfigure Message" },
{ OPTION_RECONF_ACCEPT, "Reconfigure Accept" },
{ OPTION_SIP_SERVER_D, "SIP Server Domain Name List" },
{ OPTION_SIP_SERVER_A, "SIP Servers IPv6 Address List" },
{ OPTION_DNS_SERVERS, "DNS recursive name server" },
{ OPTION_DOMAIN_LIST, "Domain Search List" },
{ OPTION_IA_PD, "Identity Association for Prefix Delegation" },
{ OPTION_IAPREFIX, "IA Prefix" },
{ OPTION_NIS_SERVERS, "Network Information Server" },
{ OPTION_NISP_SERVERS, "Network Information Server V2" },
{ OPTION_NIS_DOMAIN_NAME, "Network Information Server Domain Name" },
{ OPTION_NISP_DOMAIN_NAME,"Network Information Server V2 Domain Name" },
{ OPTION_SNTP_SERVERS, "Simple Network Time Protocol Server" },
{ OPTION_LIFETIME, "Lifetime" },
{ OPTION_BCMCS_SERVER_D, "BCMCS Server Domain" },
{ OPTION_BCMCS_SERVER_A, "BCMCS Servers IPv6 Address List" },
{ OPTION_GEOCONF_CIVIC, "Geoconf Civic Address" },
{ OPTION_REMOTE_ID, "Remote Identifier" },
{ OPTION_SUBSCRIBER_ID, "Subscriber Identifier" },
{ OPTION_CLIENT_FQDN, "Fully Qualified Domain Name" },
{ OPTION_PANA_AGENT, "PANA Agents IPv6 Address List" },
{ OPTION_TIME_ZONE, "Time Zone" },
{ OPTION_TZDB, "Time Zone Database" },
{ OPTION_ERO, "Echo Request Option" },
{ OPTION_LQ_QUERY, "Leasequery Query" },
{ OPTION_CLIENT_DATA, "Leasequery Client Data" },
{ OPTION_CLT_TIME, "Client Last Transaction Time" },
{ OPTION_LQ_RELAY_DATA, "Leasequery Relay Data" },
{ OPTION_LQ_CLIENT_LINK, "Leasequery Client Link Address List" },
{ OPTION_CAPWAP_AC_V6, "CAPWAP Access Controllers" },
{ OPTION_MIP6_HA, "Mobile IPv6 Home Agent" },
{ OPTION_MIP6_HOA, "Mobile IPv6 Home Address" },
{ OPTION_NAI, "Network Access Identifier" },
{ 0, NULL }
};
static const value_string statuscode_vals[] =
{
{0, "Success" },
{1, "UnspecFail" },
{2, "NoAddrAvail" },
{3, "NoBinding" },
{4, "NotOnLink" },
{5, "UseMulticast" },
{6, "NoPrefixAvail" },
{7, "UnknownQueryType" },
{8, "MalformedQuery" },
{9, "NotConfigured" },
{10, "NotAllowed" },
{0, NULL }
};
static const value_string duidtype_vals[] =
{
{ DUID_LLT, "link-layer address plus time" },
{ DUID_EN, "assigned by vendor based on Enterprise number" },
{ DUID_LL, "link-layer address" },
{ DUID_LL_OLD, "link-layer address (old)" },
{ 0, NULL }
};
/* This FQDN draft is a mess, I've tried to understand,
but N,O,S bit descriptions are really cryptic */
static const true_false_string fqdn_n = {
/* "Client doesn't want server to perform DNS update", "" */
"N bit set","N bit cleared"
};
static const true_false_string fqdn_o = {
"O bit set", "O bit cleared"
};
static const true_false_string fqdn_s = {
/* "Forward mapping (FQDN-to-IPv6, AAAA) performed by client",
"Forward mapping (FQDN-to-IPv6, AAAA) performed by server" */
"S bit set", "S bit cleared"
};
static void
dhcpv6_enterprise_number(proto_tree * subtree, tvbuff_t *tvb, int offset)
{
guint32 enterprise_number;
enterprise_number = tvb_get_ntohl(tvb, offset);
proto_tree_add_text(subtree, tvb, offset, 4,
"Enterprise-number: %s (%u)",
val_to_str(enterprise_number, sminmpec_values, "%u"),
enterprise_number);
}
/* CableLabs Common Vendor Specific Options */
#define CL_OPTION_ORO 0x0001 /* 1 */
#define CL_OPTION_DEVICE_TYPE 0x0002 /* 2 */
#define CL_OPTION_EMBEDDED_COMPONENT_LIST 0x0003 /* 3 */
#define CL_OPTION_DEVICE_SERIAL_NUMBER 0x0004 /* 4 */
#define CL_OPTION_HARDWARE_VERSION_NUMBER 0x0005 /* 5 */
#define CL_OPTION_SOFTWARE_VERSION_NUMBER 0x0006 /* 6 */
#define CL_OPTION_BOOT_ROM_VERSION 0x0007 /* 7 */
#define CL_OPTION_VENDOR_OUI 0x0008 /* 8 */
#define CL_OPTION_MODEL_NUMBER 0x0009 /* 9 */
#define CL_OPTION_VENDOR_NAME 0x000a /* 10 */
/* 11-32 is currently reserved */
#define CL_OPTION_TFTP_SERVERS 0x0020 /* 32 */
#define CL_OPTION_CONFIG_FILE_NAME 0x0021 /* 33 */
#define CL_OPTION_SYSLOG_SERVERS 0x0022 /* 34 */
#define CL_OPTION_TLV5 0x0023 /* 35 */
#define CL_OPTION_DEVICE_ID 0x0024 /* 36 */
#define CL_OPTION_RFC868_SERVERS 0x0025 /* 37 */
#define CL_OPTION_TIME_OFFSET 0x0026 /* 38 */
/** CableLabs DOCSIS Project Vendor Specific Options */
#define CL_OPTION_DOCS_CMTS_CAP 0x0401 /* 1025 */
#define CL_CM_MAC_ADDR 0x0402 /* 1026 */
#define CL_EROUTER_CONTAINER_OPTION 0x403 /* 1027 */
/** CableLabs PacketCable Project Vendor Specific Options **/
#define CL_OPTION_CCC 0x087a /* 2170 */
/** CableLabs TLVs for DOCS_CMTS_CAP Vendor Option **/
#define CL_OPTION_DOCS_CMTS_TLV_VERS_NUM 0x01 /* 1 */
static const value_string cl_vendor_subopt_values[] = {
/* 1 */ { CL_OPTION_ORO, "Option Request = " },
/* 2 */ { CL_OPTION_DEVICE_TYPE, "Device Type = " },
/* 3 */ { CL_OPTION_EMBEDDED_COMPONENT_LIST, "Embedded Components = " },
/* 4 */ { CL_OPTION_DEVICE_SERIAL_NUMBER, "Serial Number = " },
/* 5 */ { CL_OPTION_HARDWARE_VERSION_NUMBER, "Hardware Version = " },
/* 6 */ { CL_OPTION_SOFTWARE_VERSION_NUMBER, "Software Version = " },
/* 7 */ { CL_OPTION_BOOT_ROM_VERSION, "Boot ROM Version = " },
/* 8 */ { CL_OPTION_VENDOR_OUI, "Organization Unique Identifier = " },
/* 9 */ { CL_OPTION_MODEL_NUMBER, "Model Number = " },
/* 10 */ { CL_OPTION_VENDOR_NAME, "Vendor Name = " },
/* 32 */ { CL_OPTION_TFTP_SERVERS, "TFTP Server Addresses : " },
/* 33 */ { CL_OPTION_CONFIG_FILE_NAME, "Configuration File Name = " },
/* 34 */ { CL_OPTION_SYSLOG_SERVERS, "Syslog Servers : " },
/* 35 */ { CL_OPTION_TLV5, "TLV5 = " },
/* 36 */ { CL_OPTION_DEVICE_ID, "Device Identifier = " },
/* 37 */ { CL_OPTION_RFC868_SERVERS, "Time Protocol Servers : " },
/* 38 */ { CL_OPTION_TIME_OFFSET, "Time Offset = " },
/* 1025 */ {CL_OPTION_DOCS_CMTS_CAP, "CMTS Capabilities Option : " },
/* 1026 */ {CL_CM_MAC_ADDR, "CM MAC Address Option = " },
/* 1027 */ {CL_EROUTER_CONTAINER_OPTION, "eRouter Container Option : " },
/* 2170 */ {CL_OPTION_CCC, "CableLabs Client Configuration : " },
{ 0, NULL}
/* 1 */ };
#define PKT_CCC_PRI_DHCP 0x0001
#define PKT_CCC_SEC_DHCP 0x0002
#define PKT_CCC_IETF_PROV_SRV 0x0003
#define PKT_CCC_IETF_AS_KRB 0x0004
#define PKT_CCC_IETF_AP_KRB 0x0005
#define PKT_CCC_KRB_REALM 0x0006
#define PKT_CCC_TGT_FLAG 0x0007
#define PKT_CCC_PROV_TIMER 0x0008
#define PKT_CCC_IETF_SEC_TKT 0x0009
/** 10 -255 Reservered for future extensions **/
static const value_string pkt_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 sec_tcm_vals[] = {
{ 1 << 0, "PacketCable Provisioning Server" },
{ 1 << 1, "PacketCable Call Manager Servers" },
{ 0, NULL },
};
static void dissect_cablelabs_specific_opts(proto_tree *v_tree, tvbuff_t *tvb, int voff, int len);
static int dissect_packetcable_ccc_option(proto_tree *v_tree, tvbuff_t *tvb, int optoff, int optend);
/* End CableLabs Vendor Specific Options */
/* Adds domain */
static void
dhcpv6_domain(proto_tree * subtree, tvbuff_t *tvb, int offset, guint16 optlen)
{
int start_offset=offset;
char domain[256];
int pos;
guint8 len;
pos=0;
while(optlen){
/* this is the start of the domain name */
if(!pos){
start_offset=offset;
}
domain[pos]=0;
/* read length of the next substring */
len = tvb_get_guint8(tvb, offset);
/* Microsoft dhcpv6 clients aren't currently RFC 4704 conform: They send an
* ASCII string instead of a DNS record encoded domain name. Catch that case
* to allow us to continue after such a malformed record.
*/
if ( optlen < len ) {
proto_tree_add_text(subtree, tvb, start_offset, optlen, "Malformed DNS name record (MS Vista client?)");
return;
}
offset++;
optlen--;
/* if len==0 and pos>0 we have read an entire domain string */
if(!len){
if(!pos){
/* empty string, this must be an error? */
proto_tree_add_text(subtree, tvb, start_offset, offset-start_offset, "Malformed option");
return;
} else {
proto_tree_add_text(subtree, tvb, start_offset, offset-start_offset, "Domain: %s", domain);
pos=0;
continue;
}
}
/* add the substring to domain */
if(pos){
domain[pos]='.';
pos++;
}
if(pos+len>254){
/* too long string, this must be an error? */
proto_tree_add_text(subtree, tvb, start_offset, offset-start_offset, "Malformed option");
return;
}
tvb_memcpy(tvb, domain+pos, offset, len);
pos+=len;
offset+=len;
optlen-=len;
}
if(pos){
domain[pos]=0;
proto_tree_add_text(subtree, tvb, start_offset, offset-start_offset, "Domain: %s", domain);
}
}
/* Returns the number of bytes consumed by this option. */
static int
dhcpv6_option(tvbuff_t *tvb, packet_info *pinfo, proto_tree *bp_tree,
gboolean downstream, int off, int eoff, gboolean *at_end)
{
guint8 *buf;
guint16 opttype;
guint16 optlen;
guint16 hwtype;
guint16 temp_optlen = 0;
proto_item *ti;
proto_tree *subtree;
proto_tree *subtree_2;
int i;
struct e_in6_addr in6;
guint16 duidtype;
guint32 enterprise_no;
/* option type and length must be present */
if (eoff - off < 4) {
*at_end = TRUE;
return 0;
}
opttype = tvb_get_ntohs(tvb, off);
optlen = tvb_get_ntohs(tvb, off + 2);
/* all option data must be present */
if (eoff - off < 4 + optlen) {
*at_end = TRUE;
return 0;
}
ti = proto_tree_add_text(bp_tree, tvb, off, 4 + optlen,
"%s", val_to_str(opttype, opttype_vals, "DHCP option %u"));
subtree = proto_item_add_subtree(ti, ett_dhcpv6_option);
proto_tree_add_text(subtree, tvb, off, 2, "option type: %d", opttype);
proto_tree_add_text(subtree, tvb, off + 2, 2, "option length: %d",
optlen);
off += 4;
switch (opttype) {
case OPTION_CLIENTID:
case OPTION_SERVERID:
if (optlen < 2) {
proto_tree_add_text(subtree, tvb, off, optlen,
"DUID: malformed option");
break;
}
duidtype = tvb_get_ntohs(tvb, off);
proto_tree_add_text(subtree, tvb, off, 2,
"DUID type: %s (%u)",
val_to_str(duidtype,
duidtype_vals, "Unknown"),
duidtype);
switch (duidtype) {
case DUID_LLT:
if (optlen < 8) {
proto_tree_add_text(subtree, tvb, off,
optlen, "DUID: malformed option");
break;
}
hwtype=tvb_get_ntohs(tvb, off + 2);
proto_tree_add_text(subtree, tvb, off + 2, 2,
"Hardware type: %s (%u)", arphrdtype_to_str(hwtype, "Unknown"),
hwtype);
/* XXX seconds since Jan 1 2000 */
proto_tree_add_text(subtree, tvb, off + 4, 4,
"Time: %u", tvb_get_ntohl(tvb, off + 4));
if (optlen > 8) {
proto_tree_add_text(subtree, tvb, off + 8,
optlen - 8, "Link-layer address: %s",
arphrdaddr_to_str(tvb_get_ptr(tvb, off+8, optlen-8), optlen-8, hwtype));
}
break;
case DUID_EN:
if (optlen < 6) {
proto_tree_add_text(subtree, tvb, off,
optlen, "DUID: malformed option");
break;
}
dhcpv6_enterprise_number(subtree, tvb, off + 2);
if (optlen > 6) {
buf = tvb_bytes_to_str(tvb, off + 6, optlen - 6);
proto_tree_add_text(subtree, tvb, off + 6,
optlen - 6, "identifier: %s", buf);
}
break;
case DUID_LL:
case DUID_LL_OLD:
if (optlen < 4) {
proto_tree_add_text(subtree, tvb, off,
optlen, "DUID: malformed option");
break;
}
hwtype=tvb_get_ntohs(tvb, off + 2);
proto_tree_add_text(subtree, tvb, off + 2, 2,
"Hardware type: %s (%u)",
arphrdtype_to_str(hwtype, "Unknown"),
hwtype);
if (optlen > 4) {
proto_tree_add_text(subtree, tvb, off + 4,
optlen - 4, "Link-layer address: %s",
arphrdaddr_to_str(tvb_get_ptr(tvb, off+4, optlen-4), optlen-4, hwtype));
}
break;
}
break;
case OPTION_IA_NA:
case OPTION_IA_PD:
if (optlen < 12) {
if (opttype == OPTION_IA_NA)
proto_tree_add_text(subtree, tvb, off,
optlen, "IA_NA: malformed option");
else
proto_tree_add_text(subtree, tvb, off,
optlen, "IA_PD: malformed option");
break;
}
proto_tree_add_text(subtree, tvb, off, 4,
"IAID: %u",
tvb_get_ntohl(tvb, off));
if (tvb_get_ntohl(tvb, off+4) == DHCPV6_LEASEDURATION_INFINITY) {
proto_tree_add_text(subtree, tvb, off+4, 4,
"T1: infinity");
} else {
proto_tree_add_text(subtree, tvb, off+4, 4,
"T1: %u", tvb_get_ntohl(tvb, off+4));
}
if (tvb_get_ntohl(tvb, off+8) == DHCPV6_LEASEDURATION_INFINITY) {
proto_tree_add_text(subtree, tvb, off+8, 4,
"T2: infinity");
} else {
proto_tree_add_text(subtree, tvb, off+8, 4,
"T2: %u", tvb_get_ntohl(tvb, off+8));
}
temp_optlen = 12;
while ((optlen - temp_optlen) > 0) {
temp_optlen += dhcpv6_option(tvb, pinfo, subtree, downstream,
off+temp_optlen, off + optlen, at_end);
if (*at_end) {
/* Bad option - just skip to the end */
temp_optlen = optlen;
}
}
break;
case OPTION_IA_TA:
if (optlen < 4) {
proto_tree_add_text(subtree, tvb, off,
optlen, "IA_TA: malformed option");
break;
}
proto_tree_add_text(subtree, tvb, off, 4,
"IAID: %u",
tvb_get_ntohl(tvb, off));
temp_optlen = 4;
while ((optlen - temp_optlen) > 0) {
temp_optlen += dhcpv6_option(tvb, pinfo, subtree, downstream,
off+temp_optlen, off + optlen, at_end);
if (*at_end) {
/* Bad option - just skip to the end */
temp_optlen = optlen;
}
}
break;
case OPTION_IAADDR:
{
guint32 preferred_lifetime, valid_lifetime;
if (optlen < 24) {
proto_tree_add_text(subtree, tvb, off,
optlen, "IAADDR: malformed option");
break;
}
tvb_get_ipv6(tvb, off, &in6);
proto_tree_add_text(subtree, tvb, off,
sizeof(in6), "IPv6 address: %s",
ip6_to_str(&in6));
preferred_lifetime = tvb_get_ntohl(tvb, off + 16);
valid_lifetime = tvb_get_ntohl(tvb, off + 20);
if (preferred_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
proto_tree_add_text(subtree, tvb, off + 16, 4,
"Preferred lifetime: infinity");
} else {
proto_tree_add_text(subtree, tvb, off + 16, 4,
"Preferred lifetime: %u", preferred_lifetime);
}
if (valid_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
proto_tree_add_text(subtree, tvb, off + 20, 4,
"Valid lifetime: infinity");
} else {
proto_tree_add_text(subtree, tvb, off + 20, 4,
"Valid lifetime: %u", valid_lifetime);
}
temp_optlen = 24;
while ((optlen - temp_optlen) > 0) {
temp_optlen += dhcpv6_option(tvb, pinfo, subtree, downstream,
off+temp_optlen, off + optlen, at_end);
if (*at_end) {
/* Bad option - just skip to the end */
temp_optlen = optlen;
}
}
}
break;
case OPTION_ORO:
case OPTION_ERO:
for (i = 0; i < optlen; i += 2) {
guint16 requested_opt_code;
requested_opt_code = tvb_get_ntohs(tvb, off + i);
proto_tree_add_text(subtree, tvb, off + i,
2, "Requested Option code: %s (%d)",
val_to_str(requested_opt_code,
opttype_vals,
"Unknown"),
requested_opt_code);
}
break;
case OPTION_PREFERENCE:
if (optlen != 1) {
proto_tree_add_text(subtree, tvb, off,
optlen, "PREFERENCE: malformed option");
break;
}
proto_tree_add_text(subtree, tvb, off, 1,
"pref-value: %d",
(guint32)tvb_get_guint8(tvb, off));
break;
case OPTION_ELAPSED_TIME:
if (optlen != 2) {
proto_tree_add_text(subtree, tvb, off,
optlen, "ELAPSED-TIME: malformed option");
break;
}
proto_tree_add_text(subtree, tvb, off, 2,
"elapsed-time: %u ms",
10*(guint32)tvb_get_ntohs(tvb, off));
break;
case OPTION_RELAY_MSG:
if (optlen == 0) {
proto_tree_add_text(subtree, tvb, off,
optlen, "RELAY-MSG: malformed option");
} else {
/* here, we should dissect a full DHCP message */
dissect_dhcpv6(tvb, pinfo, subtree, downstream, off, off + optlen);
}
break;
case OPTION_AUTH:
if (optlen < 11) {
proto_tree_add_text(subtree, tvb, off,
optlen, "AUTH: malformed option");
break;
}
proto_tree_add_text(subtree, tvb, off, 1,
"Protocol: %d",
(guint32)tvb_get_guint8(tvb, off));
proto_tree_add_text(subtree, tvb, off+1, 1,
"Algorithm: %d",
(guint32)tvb_get_guint8(tvb, off+1));
proto_tree_add_text(subtree, tvb, off+2, 1,
"RDM: %d",
(guint32)tvb_get_guint8(tvb, off+2));
proto_tree_add_text(subtree, tvb, off+3, 8,
"Replay Detection");
if (optlen != 11)
proto_tree_add_text(subtree, tvb, off+11, optlen-11,
"Authentication Information");
break;
case OPTION_UNICAST:
if (optlen != 16) {
proto_tree_add_text(subtree, tvb, off,
optlen, "UNICAST: malformed option");
break;
}
tvb_get_ipv6(tvb, off, &in6);
proto_tree_add_text(subtree, tvb, off,
sizeof(in6), "IPv6 address: %s",
ip6_to_str(&in6));
break;
case OPTION_STATUS_CODE:
{
guint16 status_code;
char *status_message = 0;
status_code = tvb_get_ntohs(tvb, off);
proto_tree_add_text(subtree, tvb, off, 2,
"Status Code: %s (%d)",
val_to_str(status_code, statuscode_vals,
"Unknown"),
status_code);
if (optlen - 2 > 0) {
status_message = tvb_get_ephemeral_string(tvb, off + 2, optlen - 2);
proto_tree_add_text(subtree, tvb, off + 2, optlen - 2,
"Status Message: %s",
status_message);
}
}
break;
case OPTION_VENDOR_CLASS:
if (optlen < 4) {
proto_tree_add_text(subtree, tvb, off,
optlen, "VENDOR_CLASS: malformed option");
break;
}
dhcpv6_enterprise_number(subtree, tvb, off);
if (optlen > 4) {
buf = tvb_bytes_to_str(tvb, off + 4, optlen - 4);
proto_tree_add_text(subtree, tvb, off+4, optlen-4,
"vendor-class-data: %s", buf);
}
break;
case OPTION_VENDOR_OPTS:
if (optlen < 4) {
proto_tree_add_text(subtree, tvb, off,
optlen, "VENDOR_OPTS: malformed option");
break;
}
enterprise_no = tvb_get_ntohl(tvb, off);
proto_tree_add_text(subtree, tvb, off, 4,
"enterprise-number: %s (%u)",
val_to_str(enterprise_no, sminmpec_values, "Unknown"), enterprise_no);
if (optlen >= 4) {
if (enterprise_no == 4491) {
dissect_cablelabs_specific_opts(subtree, tvb, off+4, optlen-4);
} else {
int optoffset = 0;
while((optlen - 4 - optoffset) > 0) {
int olen = tvb_get_ntohs(tvb, off + optoffset + 6);
ti = proto_tree_add_text(subtree, tvb, off + optoffset + 4,
4 + olen, "option");
subtree_2 = proto_item_add_subtree(ti, ett_dhcpv6_option_vsoption);
proto_tree_add_text(subtree_2, tvb, off + optoffset + 4, 2,
"option code: %u", tvb_get_ntohs(tvb, off + optoffset + 4));
proto_tree_add_text(subtree_2, tvb, off + optoffset + 6, 2,
"option length: %u", olen);
proto_tree_add_text(subtree_2, tvb, off + optoffset + 8, olen,
"option-data");
optoffset += (4 + olen);
}
}
}
break;
case OPTION_INTERFACE_ID:
if (optlen == 0) {
proto_tree_add_text(subtree, tvb, off,
optlen, "INTERFACE_ID: malformed option");
break;
}
buf = tvb_get_ephemeral_string(tvb, off, optlen);
proto_tree_add_text(subtree, tvb, off, optlen, "Interface-ID: %s", buf);
break;
case OPTION_RECONF_MSG:
if (optlen != 1) {
proto_tree_add_text(subtree, tvb, off,
optlen, "RECONF_MSG: malformed option");
break;
}
proto_tree_add_text(subtree, tvb, off, optlen,
"Reconfigure-type: %s",
val_to_str(tvb_get_guint8(tvb, off),
msgtype_vals,
"Message Type %u"));
break;
case OPTION_SIP_SERVER_D:
if (optlen > 0) {
proto_tree_add_text(subtree, tvb, off, optlen,
"SIP Servers Domain Search List");
}
dhcpv6_domain(subtree,tvb, off, optlen);
break;
case OPTION_SIP_SERVER_A:
if (optlen % 16) {
proto_tree_add_text(subtree, tvb, off, optlen,
"SIP servers address: malformed option");
break;
}
for (i = 0; i < optlen; i += 16) {
tvb_get_ipv6(tvb, off + i, &in6);
proto_tree_add_text(subtree, tvb, off + i,
sizeof(in6), "SIP servers address: %s",
ip6_to_str(&in6));
}
break;
case OPTION_DNS_SERVERS:
if (optlen % 16) {
proto_tree_add_text(subtree, tvb, off, optlen,
"DNS servers address: malformed option");
break;
}
for (i = 0; i < optlen; i += 16) {
tvb_get_ipv6(tvb, off + i, &in6);
proto_tree_add_text(subtree, tvb, off + i,
sizeof(in6), "DNS servers address: %s",
ip6_to_str(&in6));
}
break;
case OPTION_DOMAIN_LIST:
if (optlen > 0) {
proto_tree_add_text(subtree, tvb, off, optlen, "DNS Domain Search List");
}
dhcpv6_domain(subtree,tvb, off, optlen);
break;
case OPTION_NIS_SERVERS:
if (optlen % 16) {
proto_tree_add_text(subtree, tvb, off, optlen,
"NIS servers address: malformed option");
break;
}
for (i = 0; i < optlen; i += 16) {
tvb_get_ipv6(tvb, off + i, &in6);
proto_tree_add_text(subtree, tvb, off + i,
sizeof(in6), "NIS servers address: %s",
ip6_to_str(&in6));
}
break;
case OPTION_NISP_SERVERS:
if (optlen % 16) {
proto_tree_add_text(subtree, tvb, off, optlen,
"NISP servers address: malformed option");
break;
}
for (i = 0; i < optlen; i += 16) {
tvb_get_ipv6(tvb, off + i, &in6);
proto_tree_add_text(subtree, tvb, off + i,
sizeof(in6), "NISP servers address: %s",
ip6_to_str(&in6));
}
break;
case OPTION_NIS_DOMAIN_NAME:
if (optlen > 0) {
proto_tree_add_text(subtree, tvb, off, optlen, "nis-domain-name");
}
dhcpv6_domain(subtree,tvb, off, optlen);
break;
case OPTION_NISP_DOMAIN_NAME:
if (optlen > 0) {
proto_tree_add_text(subtree, tvb, off, optlen, "nisp-domain-name");
}
dhcpv6_domain(subtree,tvb, off, optlen);
break;
case OPTION_SNTP_SERVERS:
if (optlen % 16) {
proto_tree_add_text(subtree, tvb, off, optlen,
"SNTP servers address: malformed option");
break;
}
for (i = 0; i < optlen; i += 16) {
tvb_get_ipv6(tvb, off + i, &in6);
proto_tree_add_text(subtree, tvb, off + i,
sizeof(in6), "SNTP servers address: %s",
ip6_to_str(&in6));
}
break;
case OPTION_LIFETIME:
if (optlen != 4) {
proto_tree_add_text(subtree, tvb, off,
optlen, "LIFETIME: malformed option");
break;
}
proto_tree_add_text(subtree, tvb, off, 4,
"Lifetime: %d",
(guint32)tvb_get_ntohl(tvb, off));
break;
case OPTION_BCMCS_SERVER_D:
if (optlen > 0) {
proto_tree_add_text(subtree, tvb, off, optlen,
"BCMCS Servers Domain Search List");
}
dhcpv6_domain(subtree,tvb, off, optlen);
break;
case OPTION_BCMCS_SERVER_A:
if (optlen % 16) {
proto_tree_add_text(subtree, tvb, off, optlen,
"BCMCS servers address: malformed option");
break;
}
for (i = 0; i < optlen; i += 16) {
tvb_get_ipv6(tvb, off + i, &in6);
proto_tree_add_text(subtree, tvb, off + i,
sizeof(in6), "BCMCS servers address: %s",
ip6_to_str(&in6));
}
break;
case OPTION_REMOTE_ID:
if (optlen < 4) {
proto_tree_add_text(subtree, tvb, off,
optlen, "REMOTE_ID: malformed option");
break;
}
dhcpv6_enterprise_number(subtree, tvb, off);
off += 4;
optlen -= 4;
buf = tvb_bytes_to_str(tvb, off, optlen);
proto_tree_add_text(subtree, tvb, off, optlen, "Remote-ID: %s", buf);
break;
case OPTION_SUBSCRIBER_ID:
if (optlen == 0) {
proto_tree_add_text(subtree, tvb, off,
optlen, "SUBSCRIBER_ID: malformed option");
break;
}
buf = tvb_get_ephemeral_string(tvb, off, optlen);
proto_tree_add_text(subtree, tvb, off, optlen, "Subscriber-ID: %s", buf);
break;
case OPTION_CLIENT_FQDN:
if (optlen < 1) {
proto_tree_add_text(subtree, tvb, off,
optlen, "FQDN: malformed option");
break;
}
/*
* +-----+-+-+-+
* | MBZ |N|O|S|
* +-----+-+-+-+
*/
proto_tree_add_item(subtree, hf_fqdn_1, tvb, off, 1, FALSE);
proto_tree_add_item(subtree, hf_fqdn_2, tvb, off, 1, FALSE);
proto_tree_add_item(subtree, hf_fqdn_3, tvb, off, 1, FALSE);
proto_tree_add_item(subtree, hf_fqdn_4, tvb, off, 1, FALSE);
/* proto_tree_add_text(subtree, tvb, off, 1, */
/* "flags: %d", */
/* (guint32)tvb_get_guint8(tvb, off)); */
dhcpv6_domain(subtree,tvb, off+1, (guint16) (optlen-1));
break;
case OPTION_PANA_AGENT:
if (optlen % 16) {
proto_tree_add_text(subtree, tvb, off, optlen,
"PANA agent address: malformed option");
break;
}
for (i = 0; i < optlen; i += 16) {
tvb_get_ipv6(tvb, off + i, &in6);
proto_tree_add_text(subtree, tvb, off + i,
sizeof(in6), "PANA agents address: %s",
ip6_to_str(&in6));
}
break;
case OPTION_TIME_ZONE:
if (optlen > 0) {
buf = tvb_get_ephemeral_string(tvb, off, optlen);
proto_tree_add_text(subtree, tvb, off, optlen, "time-zone: %s", buf);
}
break;
case OPTION_TZDB:
if (optlen > 0) {
buf = tvb_get_ephemeral_string(tvb, off, optlen);
proto_tree_add_text(subtree, tvb, off, optlen, "tz-database: %s", buf);
}
break;
case OPTION_LQ_QUERY:
{
guint8 query_type;
struct e_in6_addr in6_local;
if (optlen < 17) {
proto_tree_add_text(subtree, tvb, off, optlen,
"LQ-QUERY: malformed option");
break;
}
query_type = tvb_get_guint8(tvb, off);
switch (query_type) {
case 1:
proto_tree_add_text(subtree, tvb, off, 1,
"Query-type: %s (%u)",
"by-address", query_type);
break;
case 2:
proto_tree_add_text(subtree, tvb, off, 1,
"Query-type: %s (%u)",
"by-clientID", query_type);
break;
default:
proto_tree_add_text(subtree, tvb, off, 1,
"Query-type: %s (%u)",
"unknown?", query_type);
break;
}
tvb_get_ipv6(tvb, off + 1, &in6_local);
proto_tree_add_text(subtree, tvb, off + 1, 16,
"Link address: %s", ip6_to_str(&in6_local));
temp_optlen = 17;
while ((optlen - temp_optlen) > 0) {
temp_optlen += dhcpv6_option(tvb, pinfo, subtree,
downstream, off + temp_optlen,
off + optlen, at_end);
if (*at_end) {
/* Bad option - just skip to the end */
temp_optlen = optlen;
}
}
}
break;
case OPTION_CLIENT_DATA:
temp_optlen = 0;
while ((optlen - temp_optlen) > 0) {
temp_optlen += dhcpv6_option(tvb, pinfo, subtree,
downstream, off + temp_optlen,
off + optlen, at_end);
if (*at_end) {
/* Bad option - just skip to the end */
temp_optlen = optlen;
}
}
break;
case OPTION_CLT_TIME:
if (optlen != 4) {
proto_tree_add_text(subtree, tvb, off, optlen,
"CLT_TIME: malformed option");
break;
}
proto_tree_add_text(subtree, tvb, off, 4,
"Clt_time: %d",
(guint32)tvb_get_ntohl(tvb, off));
break;
case OPTION_LQ_RELAY_DATA:
if (optlen < 16) {
proto_tree_add_text(subtree, tvb, off, optlen,
"LQ_RELAY_DATA: malformed option");
break;
}
tvb_get_ipv6(tvb, off, &in6);
proto_tree_add_text(subtree, tvb, off, 16,
"Peer address: %s", ip6_to_str(&in6));
proto_tree_add_text(subtree, tvb, off + 16, optlen - 16,
"DHCPv6 relay message");
break;
case OPTION_LQ_CLIENT_LINK:
if (optlen % 16) {
proto_tree_add_text(subtree, tvb, off, optlen,
"LQ client links address: malformed option");
break;
}
for (i = 0; i < optlen; i += 16) {
tvb_get_ipv6(tvb, off + i, &in6);
proto_tree_add_text(subtree, tvb, off + i,
sizeof(in6), "LQ client links address: %s",
ip6_to_str(&in6));
}
break;
case OPTION_CAPWAP_AC_V6:
if (optlen % 16) {
proto_tree_add_text(subtree, tvb, off, optlen,
"CAPWAP Access Controllers address: malformed option");
break;
}
for (i = 0; i < optlen; i += 16) {
tvb_get_ipv6(tvb, off + i, &in6);
proto_tree_add_text(subtree, tvb, off + i,
sizeof(in6), "CAPWAP Access Controllers address: %s",
ip6_to_str(&in6));
}
break;
case OPTION_IAPREFIX:
{
guint32 preferred_lifetime, valid_lifetime;
guint8 prefix_length;
struct e_in6_addr in6_local;
if (optlen < 25) {
proto_tree_add_text(subtree, tvb, off,
optlen, "IAPREFIX: malformed option");
break;
}
preferred_lifetime = tvb_get_ntohl(tvb, off);
valid_lifetime = tvb_get_ntohl(tvb, off + 4);
prefix_length = tvb_get_guint8(tvb, off + 8);
if (preferred_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
proto_tree_add_text(subtree, tvb, off, 4,
"Preferred lifetime: infinity");
} else {
proto_tree_add_text(subtree, tvb, off, 4,
"Preferred lifetime: %u", preferred_lifetime);
}
if (valid_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
proto_tree_add_text(subtree, tvb, off + 4, 4,
"Valid lifetime: infinity");
} else {
proto_tree_add_text(subtree, tvb, off + 4, 4,
"Valid lifetime: %u", valid_lifetime);
}
proto_tree_add_text(subtree, tvb, off + 8, 1,
"Prefix length: %d", prefix_length);
tvb_get_ipv6(tvb, off + 9, &in6_local);
proto_tree_add_text(subtree, tvb, off + 9,
16, "Prefix address: %s",
ip6_to_str(&in6_local));
temp_optlen = 25;
while ((optlen - temp_optlen) > 0) {
temp_optlen += dhcpv6_option(tvb, pinfo, subtree, downstream,
off+temp_optlen, off + optlen, at_end);
if (*at_end) {
/* Bad option - just skip to the end */
temp_optlen = optlen;
}
}
}
break;
case OPTION_MIP6_HA:
if (optlen != 16) {
proto_tree_add_text(subtree, tvb, off, optlen,
"MIP6_HA: malformed option");
break;
}
tvb_get_ipv6(tvb, off, &in6);
proto_tree_add_text(subtree, tvb, off,
16, "Home Agent: %s", ip6_to_str(&in6));
break;
case OPTION_MIP6_HOA:
if (optlen != 16) {
proto_tree_add_text(subtree, tvb, off, optlen,
"MIP6_HOA: malformed option");
break;
}
tvb_get_ipv6(tvb, off, &in6);
proto_tree_add_text(subtree, tvb, off,
16, "Home Address: %s", ip6_to_str(&in6));
break;
case OPTION_NAI:
if (optlen < 4) {
proto_tree_add_text(subtree, tvb, off, optlen,
"NAI: malformed option");
break;
}
proto_tree_add_text(subtree, tvb, off, optlen,
"NAI : %s", tvb_get_ptr(tvb, off, optlen - 2));
break;
}
return 4 + optlen;
}
static void
dissect_dhcpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
gboolean downstream, int off, int eoff)
{
proto_tree *bp_tree = NULL;
proto_item *ti;
guint8 msgtype, hop_count ;
guint32 xid;
struct e_in6_addr in6;
gboolean at_end;
downstream = 0; /* feature reserved */
msgtype = tvb_get_guint8(tvb, off);
if (tree) {
ti = proto_tree_add_item(tree, proto_dhcpv6, tvb, 0, -1, FALSE);
bp_tree = proto_item_add_subtree(ti, ett_dhcpv6);
}
if (msgtype == RELAY_FORW || msgtype == RELAY_REPLY) {
if (!off) {
if (check_col(pinfo->cinfo, COL_INFO)) {
col_add_str(pinfo->cinfo, COL_INFO,
val_to_str(msgtype,
msgtype_vals,
"Message Type %u"));
}
}
proto_tree_add_uint(bp_tree, hf_dhcpv6_msgtype, tvb, off, 1, msgtype);
hop_count = tvb_get_guint8(tvb, off+1);
proto_tree_add_text(bp_tree, tvb, off+1, 1, "Hop count: %d", hop_count);
tvb_get_ipv6(tvb, off+2, &in6);
proto_tree_add_text(bp_tree, tvb, off+2, sizeof(in6),
"Link-address: %s",ip6_to_str(&in6));
tvb_get_ipv6(tvb, off+18, &in6);
proto_tree_add_text(bp_tree, tvb, off+18, sizeof(in6),
"Peer-address: %s",ip6_to_str(&in6));
off += 34;
} else {
xid = tvb_get_ntohl(tvb, off) & 0x00ffffff;
if (!off) {
if (check_col(pinfo->cinfo, COL_INFO)) {
col_add_str(pinfo->cinfo, COL_INFO,
val_to_str(msgtype,
msgtype_vals,
"Message Type %u"));
}
}
if (tree) {
proto_tree_add_uint(bp_tree, hf_dhcpv6_msgtype, tvb, off, 1,
msgtype);
proto_tree_add_text(bp_tree, tvb, off+1, 3, "Transaction-ID: 0x%08x", xid);
#if 0
tvb_get_ipv6(tvb, 4, &in6);
proto_tree_add_text(bp_tree, tvb, 4, sizeof(in6),
"Server address: %s", ip6_to_str(&in6));
#endif
}
off += 4;
}
at_end = FALSE;
while (off < eoff && !at_end)
off += dhcpv6_option(tvb, pinfo, bp_tree, downstream, off, eoff, &at_end);
}
static void
dissect_dhcpv6_downstream(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
col_set_str(pinfo->cinfo, COL_PROTOCOL, "DHCPv6");
col_clear(pinfo->cinfo, COL_INFO);
dissect_dhcpv6(tvb, pinfo, tree, TRUE, 0, tvb_reported_length(tvb));
}
static void
dissect_dhcpv6_upstream(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
col_set_str(pinfo->cinfo, COL_PROTOCOL, "DHCPv6");
col_clear(pinfo->cinfo, COL_INFO);
dissect_dhcpv6(tvb, pinfo, tree, FALSE, 0, tvb_reported_length(tvb));
}
void
proto_register_dhcpv6(void)
{
static hf_register_info hf[] = {
{ &hf_dhcpv6_msgtype,
{ "Message type", "dhcpv6.msgtype", FT_UINT8,
BASE_DEC, VALS(msgtype_vals), 0x0,
NULL, HFILL }},
{ &hf_fqdn_1,
{ "Reserved", "dhcpv6.msgtype.reserved", FT_UINT8, BASE_HEX, NULL, 0xF8, NULL, HFILL}},
{ &hf_fqdn_2,
{ "N", "dhcpv6.msgtype.n", FT_BOOLEAN, 8, TFS(&fqdn_n), 0x4, NULL, HFILL}},
{ &hf_fqdn_3,
{ "O", "dhcpv6.msgtype.o", FT_BOOLEAN, 8, TFS(&fqdn_o), 0x2, NULL, HFILL}},
{ &hf_fqdn_4,
{ "S", "dhcpv6.msgtype.s", FT_BOOLEAN, 8, TFS(&fqdn_s), 0x1, NULL, HFILL}}
};
static gint *ett[] = {
&ett_dhcpv6,
&ett_dhcpv6_option,
&ett_dhcpv6_option_vsoption,
&ett_dhcpv6_vendor_option,
&ett_dhcpv6_pkt_option,
};
proto_dhcpv6 = proto_register_protocol("DHCPv6", "DHCPv6", "dhcpv6");
proto_register_field_array(proto_dhcpv6, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
/* Allow other dissectors to find this one by name.
Just choose upstream version for now as they are identical. */
register_dissector("dhcpv6", dissect_dhcpv6_upstream, proto_dhcpv6);
}
void
proto_reg_handoff_dhcpv6(void)
{
dissector_handle_t dhcpv6_handle;
dhcpv6_handle = create_dissector_handle(dissect_dhcpv6_downstream,
proto_dhcpv6);
dissector_add("udp.port", UDP_PORT_DHCPV6_DOWNSTREAM, dhcpv6_handle);
dhcpv6_handle = create_dissector_handle(dissect_dhcpv6_upstream,
proto_dhcpv6);
dissector_add("udp.port", UDP_PORT_DHCPV6_UPSTREAM, dhcpv6_handle);
}
static void
dissect_cablelabs_specific_opts(proto_tree *v_tree, tvbuff_t *tvb, int voff, int len)
{
guint16 type;
guint16 tlv_len; /* holds the number of elements in the tlv */
guint16 opt_len; /* holds the length of the suboption */
guint16 sub_value;
int off = voff;
int sub_off; /** The offset for the sub-option */
proto_item *ti;
int i;
int field_len; /* holds the lenght of one occurrence of a field */
proto_tree *subtree;
struct e_in6_addr in6;
if (len > 4) {
while (off - voff < len) {
/* Type */
type = tvb_get_ntohs(tvb, off);
ti = proto_tree_add_text(v_tree, tvb, off, 2,
"Suboption %d: %s", type, val_to_str(type,
cl_vendor_subopt_values, "unknown"));
/* Length */
tlv_len = tvb_get_ntohs(tvb, off+2);
/* Values */
sub_off = off + 4;
switch(type) {
/* String types */
case CL_OPTION_DEVICE_TYPE :
case CL_OPTION_DEVICE_SERIAL_NUMBER :
case CL_OPTION_HARDWARE_VERSION_NUMBER :
case CL_OPTION_SOFTWARE_VERSION_NUMBER :
case CL_OPTION_BOOT_ROM_VERSION :
case CL_OPTION_MODEL_NUMBER :
case CL_OPTION_VENDOR_NAME :
case CL_OPTION_CONFIG_FILE_NAME :
case CL_OPTION_EMBEDDED_COMPONENT_LIST :
opt_len = tlv_len;
field_len = tlv_len;
proto_item_append_text(ti, "\"%s\"",
tvb_format_stringzpad(tvb, sub_off, field_len));
break;
case CL_OPTION_ORO :
field_len = 2;
opt_len = tlv_len * field_len;
if (opt_len > 0) {
for (i = 0; i < tlv_len; i++) {
sub_value = tvb_get_ntohs(tvb, sub_off);
proto_item_append_text(ti, " %d", sub_value);
sub_off += field_len;
}
}
break;
/* List of IPv6 Address */
case CL_OPTION_TFTP_SERVERS :
case CL_OPTION_SYSLOG_SERVERS :
case CL_OPTION_RFC868_SERVERS :
field_len = 16;
opt_len = tlv_len;
subtree = proto_item_add_subtree(ti, ett_dhcpv6_vendor_option);
if ((tlv_len % field_len) == 0) {
for (i = 0; i < tlv_len/field_len; i++) {
tvb_get_ipv6(tvb, sub_off, &in6);
proto_tree_add_text(subtree, tvb, sub_off,
sizeof(in6), "IPv6 address %d: %s",
i+1, ip6_to_str(&in6));
sub_off += field_len;
}
}
break;
case CL_OPTION_VENDOR_OUI :
case CL_OPTION_DEVICE_ID :
opt_len = tlv_len;
field_len = tlv_len;
if (tlv_len != 6) {
proto_item_append_text(ti, "Bogus value length=%d",
tlv_len);
}
else {
proto_item_append_text(ti, "%s",
tvb_bytes_to_str(tvb, sub_off, field_len));
}
break;
case CL_OPTION_TLV5 :
opt_len = tlv_len;
field_len = tlv_len;
proto_item_append_text(ti, "%s",
tvb_bytes_to_str(tvb, sub_off, field_len));
break;
case CL_OPTION_TIME_OFFSET :
opt_len = tlv_len;
proto_item_append_text(ti, "%d", tvb_get_ntohl(tvb, sub_off));
break;
case CL_OPTION_DOCS_CMTS_CAP :
opt_len = tlv_len;
field_len = 0;
subtree = proto_item_add_subtree(ti, ett_dhcpv6_vendor_option);
/* tlv_len contains the total length of all the TLVs for this
option */
if (tlv_len > 0) {
for (i = 0; field_len < opt_len; i++) {
int tagLen = 0;
int tag = 0;
tag = tvb_get_guint8(tvb, sub_off);
sub_off++;
tagLen = tvb_get_guint8(tvb, sub_off);
sub_off++;
if (tag == CL_OPTION_DOCS_CMTS_TLV_VERS_NUM &&
tagLen == 2) {
int major = 0;
int minor = 0;
major = tvb_get_guint8(tvb, sub_off);
sub_off++;
minor = tvb_get_guint8(tvb, sub_off);
sub_off++;
proto_tree_add_text(subtree, tvb, sub_off,
sizeof(4), "DOCSIS Version Number %d.%d",
major, minor);
}
else
sub_off += tagLen;
field_len += tagLen + 2;
}
}
else
proto_tree_add_text(subtree, tvb, sub_off,
sizeof(0), "empty");
break;
case CL_CM_MAC_ADDR :
opt_len = tlv_len;
field_len = tlv_len;
if (tlv_len != 6) {
proto_item_append_text(ti, "Bogus value length=%d",
tlv_len);
}
else {
/*proto_item_append_text(ti, "CM MAC Address Option = %s", */
proto_item_append_text(ti, "%s",
bytes_to_str_punct(tvb_get_ptr(tvb, sub_off, opt_len), opt_len, ':'));
/* tvb_bytes_to_str(tvb, sub_off, opt_len)); */
}
sub_off += field_len;
break;
case CL_EROUTER_CONTAINER_OPTION :
opt_len = tlv_len;
field_len = tlv_len;
proto_item_append_text(ti, " %s (len=%d)",
tvb_bytes_to_str(tvb, sub_off, opt_len), tlv_len);
sub_off += field_len;
break;
case CL_OPTION_CCC :
opt_len = tlv_len;
field_len = 0;
subtree = proto_item_add_subtree(ti, ett_dhcpv6_vendor_option);
proto_item_append_text(ti, " (%d bytes)", opt_len);
while (field_len < opt_len) {
sub_value = dissect_packetcable_ccc_option(subtree, tvb,
sub_off, (opt_len - field_len));
sub_off += sub_value;
field_len += sub_value;
}
sub_off += field_len;
default:
opt_len = tlv_len;
break;
}
off += (opt_len + 4);
}
}
else {
proto_tree_add_text(v_tree, tvb, off, len-off,
"Bogus length: %d", len);
}
}
static int
dissect_packetcable_ccc_option(proto_tree *v_tree, tvbuff_t *tvb, int optoff,
int optend)
{
/** THE ENCODING OF THIS SUBOPTION HAS CHANGED FROM DHCPv4
the code and length fields have grown from a single octet to
two octets each. **/
int suboptoff = optoff;
guint16 subopt, subopt_len, sec_tcm;
guint8 fetch_tgt, timer_val, type;
proto_item *vti;
proto_tree *pkt_s_tree;
guint32 ipv4_addr;
guchar kr_name; /** A character in the kerberos realm name option */
guint8 kr_value; /* The integer value of the character currently being tested */
int kr_fail_flag = 0; /* Flag indicating an invalid character was found */
int kr_pos = 0; /* The position of the first invalid character */
int i =0;
char bit_fld[24];
subopt = tvb_get_ntohs(tvb, optoff);
suboptoff += 2;
subopt_len = tvb_get_ntohs(tvb, suboptoff);
suboptoff += 2;
/* There must be at least five octets left to be a valid sub element */
if (optend <= 0) {
proto_tree_add_text(v_tree, tvb, optoff, 1,
"Sub element %d: no room left in option for suboption length",
subopt);
return (optend);
}
/* g_print("dissect packetcable ccc option subopt_len=%d optend=%d\n\n", subopt_len, optend); */
vti = proto_tree_add_text(v_tree, tvb, optoff, subopt_len + 4,
"Sub element %u: %s: ", subopt,
val_to_str(subopt, pkt_ccc_opt_vals, "unknown/reserved") );
switch (subopt) {
case PKT_CCC_PRI_DHCP: /* IPv4 address values */
case PKT_CCC_SEC_DHCP:
if (subopt_len == 4) {
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]" : "");
}
else {
proto_tree_add_text(vti, tvb, suboptoff, subopt_len,
"Bogus length: %d", subopt_len);
}
suboptoff += subopt_len;
break;
case PKT_CCC_IETF_PROV_SRV :
type = tvb_get_guint8(tvb, suboptoff);
/** Type 0 is FQDN **/
if (type == 0) {
proto_item_append_text(vti, "%s (%u byte%s)",
tvb_format_stringzpad(tvb, suboptoff+1, subopt_len-1),
subopt_len,
plurality(subopt_len-1, "", "s") );
}
/** Type 0 is IPv4 **/
else if (type == 1) {
if (subopt_len == 5) {
ipv4_addr = tvb_get_ipv4(tvb, suboptoff+1);
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]" : "");
}
else {
proto_item_append_text(vti, "Bogus length: %d", subopt_len);
}
}
else {
proto_item_append_text(vti, "Invalid type: %u (%u byte%s)",
type, subopt_len, plurality(subopt_len, "", "s"));
}
suboptoff += subopt_len;
break;
case PKT_CCC_IETF_AS_KRB :
case PKT_CCC_IETF_AP_KRB :
if (subopt_len == 12) {
pkt_s_tree = proto_item_add_subtree(vti, ett_dhcpv6_pkt_option);
proto_tree_add_text(pkt_s_tree, tvb, suboptoff, 4,
"Nominal Timeout : %u", tvb_get_ntohl(tvb, suboptoff));
proto_tree_add_text(pkt_s_tree, tvb, suboptoff+4, 4,
"Maximum Timeout : %u", tvb_get_ntohl(tvb, suboptoff+4));
proto_tree_add_text(pkt_s_tree, tvb, suboptoff+8, 4,
"Maximum Retry Count : %u", tvb_get_ntohl(tvb, suboptoff+8));
}
else {
proto_item_append_text(vti, "Bogus length: %d", subopt_len);
}
suboptoff += subopt_len;
break;
case PKT_CCC_KRB_REALM:
if (subopt_len > 0) {
/** The only allowable characters are
A-Z (upper case only) 65-90
'.', 46
'/', 47
'\', 92
'=', 61
'"', 34
',', 44
and
':' 58
so loop through and
make sure it conforms to the expected syntax.
**/
for (i=0; i < subopt_len; i++) {
kr_name = tvb_get_guint8(tvb, suboptoff + i);
kr_value = (int)kr_name;
if ((kr_value >= 65 && kr_value <= 90) ||
kr_value == 34 ||
kr_value == 44 ||
kr_value == 46 ||
kr_value == 47 ||
kr_value == 58 ||
kr_value == 61 ||
kr_value == 92) {
}
else if (!kr_fail_flag) {
kr_pos = i;
kr_fail_flag = 1;
}
proto_item_append_text(vti, "%c",
kr_name);
}
if (kr_fail_flag) {
proto_item_append_text(vti, " (%u byte%s [Invalid at byte=%d]) ",
subopt_len,
plurality(subopt_len, "", "s"),
kr_pos);
}
else {
proto_item_append_text(vti, " (%u byte%s%s) ",
subopt_len,
plurality(subopt_len, "", "s"),
kr_fail_flag != 0 ? " [Invalid]" : "");
}
}
suboptoff += subopt_len;
break;
case PKT_CCC_TGT_FLAG:
fetch_tgt = tvb_get_guint8(tvb, suboptoff);
proto_item_append_text(vti, "%s (%u byte%s%s)",
fetch_tgt == 1 ? "True" : "False",
subopt_len,
plurality(subopt_len, "", "s"),
subopt_len != 1 ? " [Invalid]" : "");
suboptoff += subopt_len;
break;
case PKT_CCC_PROV_TIMER:
timer_val = tvb_get_guint8(tvb, suboptoff);
/* proto_item_append_text(vti, "%u%s (%u byte%s%s)", timer_val,
timer_val > 30 ? " [Invalid]" : "", */
proto_item_append_text(vti, "%u (%u byte%s%s)", timer_val,
subopt_len,
plurality(subopt_len, "", "s"),
subopt_len != 1 ? " [Invalid]" : "");
suboptoff += subopt_len;
break;
case PKT_CCC_IETF_SEC_TKT :
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_dhcpv6_pkt_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, "%s %s",
bit_fld, sec_tcm_vals[i].strptr);
}
}
}
suboptoff += subopt_len;
break;
default:
suboptoff += subopt_len;
break;
}
/** Return the number of bytes processed **/
return (suboptoff - optoff);
}