Put the IGMP type field value into the PIM tree, as is done for other
[obnox/wireshark/wip.git] / packet-bootp.c
index 934be29feb3e9a6f585dc2ed8efc184e746c012f..74d123954b08c4f81c9c443539d9cbebec4ba490 100644 (file)
@@ -2,7 +2,7 @@
  * 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
@@ -72,7 +72,7 @@ static guint ett_bootp_option = -1;
 
 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 {
@@ -83,6 +83,8 @@ 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,
@@ -113,7 +115,9 @@ get_dhcp_type(guint8 byte)
 
 /* 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;
@@ -195,7 +199,7 @@ bootp_option(tvbuff_t *tvb, proto_tree *bp_tree, int voff, int eoff)
                /*  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 },
@@ -359,15 +363,23 @@ bootp_option(tvbuff_t *tvb, proto_tree *bp_tree, int voff, int eoff)
                        }
                }
                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;
        }
@@ -379,6 +391,41 @@ bootp_option(tvbuff_t *tvb, proto_tree *bp_tree, int voff, int eoff)
         */
        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;
@@ -430,8 +477,22 @@ bootp_option(tvbuff_t *tvb, proto_tree *bp_tree, int voff, int eoff)
                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 */
@@ -463,6 +524,12 @@ bootp_option(tvbuff_t *tvb, proto_tree *bp_tree, int voff, int eoff)
                }
                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
@@ -588,7 +655,7 @@ bootp_option(tvbuff_t *tvb, proto_tree *bp_tree, int voff, int eoff)
                         * 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;
 
@@ -701,6 +768,125 @@ bootp_dhcp_decode_agent_info(proto_tree *v_tree, tvbuff_t *tvb, int optp)
        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)
 {
@@ -815,10 +1001,10 @@ dissect_bootp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        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");
@@ -939,27 +1125,49 @@ dissect_bootp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
        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);
        }
 }
 
@@ -970,82 +1178,82 @@ proto_register_bootp(void)
     { &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,