* Routines for BOOTP/DHCP packet disassembly
* Gilbert Ramirez <gram@xiexie.org>
*
- * $Id: packet-bootp.c,v 1.51 2001/05/03 07:02:50 girlich Exp $
+ * $Id: packet-bootp.c,v 1.54 2001/06/18 02:17:45 guy Exp $
*
* The information used comes from:
* RFC 951: Bootstrap Protocol
enum field_type { none, ipv4, string, toggle, yes_no, special, opaque,
time_in_secs,
- val_u_byte, val_u_short, val_u_long,
+ val_u_byte, val_u_short, val_u_le_short, val_u_long,
val_s_long };
struct opt_info {
#define NUM_OPT_INFOS 211
#define NUM_O63_SUBOPTS 11
+static int dissect_vendor_pxeclient_suboption(proto_tree *v_tree, tvbuff_t *tvb,
+ int optp);
static int dissect_netware_ip_suboption(proto_tree *v_tree, tvbuff_t *tvb,
int optp);
static int bootp_dhcp_decode_agent_info(proto_tree *v_tree, tvbuff_t *tvb,
/* Returns the number of bytes consumed by this option. */
static int
-bootp_option(tvbuff_t *tvb, proto_tree *bp_tree, int voff, int eoff)
+bootp_option(tvbuff_t *tvb, proto_tree *bp_tree, int voff, int eoff,
+ gboolean first_pass, const char **dhcp_type_p,
+ const guint8 **vendor_class_id_p)
{
char *text;
enum field_type ftype;
/* 57 */ { "Maximum DHCP Message Size", val_u_short },
/* 58 */ { "Renewal Time Value", time_in_secs },
/* 59 */ { "Rebinding Time Value", time_in_secs },
- /* 60 */ { "Vendor class identifier", opaque },
+ /* 60 */ { "Vendor class identifier", special },
/* 61 */ { "Client identifier", special },
/* 62 */ { "Novell/Netware IP domain", string },
/* 63 */ { "Novell Options", special },
}
}
i = i - voff;
- if (bp_tree != NULL)
- proto_tree_add_text(bp_tree, tvb, voff, i, "Padding");
+ if (!first_pass) {
+ if (bp_tree != NULL) {
+ proto_tree_add_text(bp_tree, tvb, voff, i,
+ "Padding");
+ }
+ }
consumed = i;
return consumed;
break;
case 255: /* End Option */
- if (bp_tree != NULL)
- proto_tree_add_text(bp_tree, tvb, voff, 1, "End Option");
+ if (!first_pass) {
+ if (bp_tree != NULL) {
+ proto_tree_add_text(bp_tree, tvb, voff, 1,
+ "End Option");
+ }
+ }
consumed = 1;
return consumed;
}
*/
vlen = tvb_get_guint8(tvb, voff+1);
consumed = vlen + 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
+ */
+ if (first_pass) {
+ switch (code) {
+
+ case 53:
+ *dhcp_type_p =
+ get_dhcp_type(tvb_get_guint8(tvb, voff+2));
+ 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;
break;
case 43: /* Vendor-Specific Info */
- proto_tree_add_text(bp_tree, tvb, voff, consumed,
- "Option %d: %s", code, text);
+ /* PXE protocol 2.1 as described in the intel specs */
+ if (*vendor_class_id_p != NULL &&
+ strncmp(*vendor_class_id_p, "PXEClient", strlen("PXEClient")) == 0) {
+ vti = proto_tree_add_text(bp_tree, tvb, voff,
+ consumed, "Option %d: %s (PXEClient)", code, text);
+ v_tree = proto_item_add_subtree(vti, ett_bootp_option);
+
+ optp = voff+2;
+ while (optp < voff+consumed) {
+ optp = dissect_vendor_pxeclient_suboption(v_tree,
+ tvb, optp);
+ }
+ } else {
+ proto_tree_add_text(bp_tree, tvb, voff, consumed,
+ "Option %d: %s (%d bytes)", code, text, vlen);
+ }
break;
case 46: /* NetBIOS-over-TCP/IP Node Type */
}
break;
+ case 60: /* Vendor class identifier */
+ proto_tree_add_text(bp_tree, tvb, voff, consumed,
+ "Option %d: %s = \"%.*s\"", code, text, vlen,
+ tvb_get_ptr(tvb, voff+2, consumed-2));
+ break;
+
case 61: /* Client Identifier */
/* We *MAY* use hwtype/hwaddr. If we have 7 bytes, I'll
guess that the first is the hwtype, and the last 6
* John Lines <John.Lines@aeat.co.uk>
*/
proto_tree_add_text(bp_tree, tvb, voff, consumed,
- "Option %d: %s = %.*s", code, text, vlen,
+ "Option %d: %s = \"%.*s\"", code, text, vlen,
tvb_get_ptr(tvb, voff+2, consumed-2));
break;
return optp;
}
+static int
+dissect_vendor_pxeclient_suboption(proto_tree *v_tree, tvbuff_t *tvb, int optp)
+{
+ guint8 subopt;
+ guint8 subopt_len;
+ int slask;
+ proto_tree *o43pxeclient_v_tree;
+ proto_item *vti;
+
+ struct o43pxeclient_opt_info {
+ char *text;
+ enum field_type ft;
+ };
+
+ static struct o43pxeclient_opt_info o43pxeclient_opt[]= {
+ /* 0 */ {"nop", special}, /* dummy */
+ /* 1 */ {"PXE mtftp IP", ipv4},
+ /* 2 */ {"PXE mtftp client port", val_u_le_short},
+ /* 3 */ {"PXE mtftp server port",val_u_le_short},
+ /* 4 */ {"PXE mtftp timeout", val_u_byte},
+ /* 5 */ {"PXE mtftp delay", val_u_byte},
+ /* 6 */ {"PXE discovery control", val_u_byte},
+ /*
+ * 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},
+ /* 8 */ {"PXE boot servers", special},
+ /* 9 */ {"PXE boot menu", special},
+ /* 10 */ {"PXE menu prompt", special},
+ /* 11 */ {"PXE multicast address alloc", special},
+ /* 12 */ {"PXE credential types", special},
+ /* 71 {"PXE boot item", special} */
+ /* 255 {"PXE end options", special} */
+ };
+#define NUM_O43PXECLIENT_SUBOPTS (12)
+
+ subopt = tvb_get_guint8(tvb, optp);
+
+ if (subopt == 0 ) {
+ proto_tree_add_text(v_tree, tvb, optp, 1, "Padding");
+ return (optp+1);
+ } else if (subopt == 255) { /* End Option */
+ proto_tree_add_text(v_tree, tvb, optp, 1, "End PXEClient option");
+ /* Make sure we skip any junk left this option */
+ return (optp+255);
+ }
+
+ subopt_len = tvb_get_guint8(tvb, optp+1);
+
+ 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, optp, subopt_len+2,
+ "Suboption %d: %s (%d byte%s)" ,
+ subopt, "PXE boot item",
+ subopt_len, (subopt_len != 1)?"s":"");
+ } else if ( (subopt < 1 ) || (subopt > NUM_O43PXECLIENT_SUBOPTS) ) {
+ proto_tree_add_text(v_tree, tvb, optp, subopt_len+2,
+ "Unknown suboption %d (%d byte%s)", subopt, subopt_len,
+ (subopt_len != 1)?"s":"");
+ } else {
+ switch (o43pxeclient_opt[subopt].ft) {
+
+/* XXX case string:
+ proto_tree_add_text(v_tree, tvb, optp, subopt_len+2,
+ "Suboption %d: %s", subopt, o43pxeclient_opt[subopt].text);
+ break;
+ XXX */
+ case special:
+ /* I may need to decode that properly one day */
+ proto_tree_add_text(v_tree, tvb, optp, subopt_len+2,
+ "Suboption %d: %s (%d byte%s)" ,
+ subopt, o43pxeclient_opt[subopt].text,
+ subopt_len, (subopt_len != 1)?"s":"");
+ break;
+
+ case val_u_le_short:
+ proto_tree_add_text(v_tree, tvb, optp, 4, "Suboption %d: %s = %u",
+ subopt, o43pxeclient_opt[subopt].text,
+ tvb_get_letohs(tvb, optp+2));
+ break;
+
+ case val_u_byte:
+ proto_tree_add_text(v_tree, tvb, optp, 3, "Suboption %d: %s = %u",
+ subopt, o43pxeclient_opt[subopt].text,
+ tvb_get_guint8(tvb, optp+2));
+ break;
+
+ case ipv4:
+ if (subopt_len == 4) {
+ /* one IP address */
+ proto_tree_add_text(v_tree, tvb, optp, 6,
+ "Suboption %d : %s = %s",
+ subopt, o43pxeclient_opt[subopt].text,
+ ip_to_str(tvb_get_ptr(tvb, optp+2, 4)));
+ } else {
+ /* > 1 IP addresses. Let's make a sub-tree */
+ vti = proto_tree_add_text(v_tree, tvb, optp,
+ subopt_len+2, "Suboption %d: %s",
+ subopt, o43pxeclient_opt[subopt].text);
+ o43pxeclient_v_tree = proto_item_add_subtree(vti, ett_bootp_option);
+ for (slask = optp + 2 ; slask < optp+subopt_len; slask += 4) {
+ proto_tree_add_text(o43pxeclient_v_tree, tvb, slask, 4, "IP Address: %s",
+ ip_to_str(tvb_get_ptr(tvb, slask, 4)));
+ }
+ }
+ break;
+ default:
+ proto_tree_add_text(v_tree, tvb, optp, subopt_len+2,"ERROR, please report: Unknown subopt type handler %d", subopt);
+ break;
+ }
+ }
+ optp += (subopt_len + 2);
+ return optp;
+}
+
static int
dissect_netware_ip_suboption(proto_tree *v_tree, tvbuff_t *tvb, int optp)
{
guint8 op;
guint8 htype, hlen;
const guint8 *haddr;
- int voff, eoff; /* vender offset, end offset */
+ int voff, eoff, tmpvoff; /* vendor offset, end offset */
guint32 ip_addr;
- gboolean is_dhcp = FALSE;
- const char *dhcp_type;
+ const char *dhcp_type = NULL;
+ const guint8 *vendor_class_id = NULL;
if (check_col(pinfo->fd, COL_PROTOCOL))
col_set_str(pinfo->fd, COL_PROTOCOL, "BOOTP");
voff = 240;
eoff = tvb_reported_length(tvb);
+
+ /*
+ * In the first pass, we just look for the DHCP message type
+ * and Vendor class identifier options.
+ */
+ tmpvoff = voff;
+ while (tmpvoff < eoff) {
+ tmpvoff += bootp_option(tvb, 0, tmpvoff, eoff, TRUE,
+ &dhcp_type, &vendor_class_id);
+ }
+
+ /*
+ * 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->fd, COL_PROTOCOL))
+ col_set_str(pinfo->fd, COL_PROTOCOL, "DHCP");
+ if (check_col(pinfo->fd, COL_INFO))
+ col_add_fstr(pinfo->fd, 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);
+ }
+
+ /*
+ * 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.
+ */
while (voff < eoff) {
- /* Handle the DHCP option specially here, so that we
- can flag DHCP packets as such. */
- if (!is_dhcp && tvb_get_guint8(tvb, voff) == 53) {
- /*
- * We haven't already seen a DHCP Type option, and
- * this is a DHCP Type option, so flag this packet
- * as DHCP.
- */
- dhcp_type = get_dhcp_type(tvb_get_guint8(tvb, voff+2));
- if (check_col(pinfo->fd, COL_PROTOCOL))
- col_set_str(pinfo->fd, COL_PROTOCOL, "DHCP");
- if (check_col(pinfo->fd, COL_INFO))
- col_add_fstr(pinfo->fd, 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);
- is_dhcp = TRUE;
- }
- voff += bootp_option(tvb, bp_tree, voff, eoff);
+ voff += bootp_option(tvb, bp_tree, voff, eoff, FALSE,
+ &dhcp_type, &vendor_class_id);
}
}
{ &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_flag,
{ "Broadcast flag", "bootp.flag", FT_UINT16,
BASE_HEX, NULL, 0x0,
- "" }},
+ "", 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_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 }},
};
static gint *ett[] = {
&ett_bootp,