diff options
author | Michael Mann <mmann78@netscape.net> | 2017-04-22 00:09:35 -0400 |
---|---|---|
committer | Michael Mann <mmann78@netscape.net> | 2017-04-24 20:27:03 +0000 |
commit | 6cbea5c01c636b1e4b2737e325addeb5ca32a11c (patch) | |
tree | 340994c75f4a2211e9430744335ff1879118a27d /epan | |
parent | 4b2ee367434572f288f1272cc4c1722a0c484798 (diff) |
Convert BOOTP options into a dissector table.
This allows for much easier addition or overriding of options and use of Decode As.
This includes adding heuristic dissector tables for vendor specific options.
Change-Id: If52c00bbc23d89386ba3e777600f665609856de0
Reviewed-on: https://code.wireshark.org/review/21297
Petri-Dish: Michael Mann <mmann78@netscape.net>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com>
Reviewed-by: Michael Mann <mmann78@netscape.net>
Diffstat (limited to 'epan')
-rw-r--r-- | epan/dissectors/packet-bootp.c | 2520 |
1 files changed, 1416 insertions, 1104 deletions
diff --git a/epan/dissectors/packet-bootp.c b/epan/dissectors/packet-bootp.c index 1b23428317..500c12f5e5 100644 --- a/epan/dissectors/packet-bootp.c +++ b/epan/dissectors/packet-bootp.c @@ -612,7 +612,6 @@ static expert_field ei_bootp_mal_duid = EI_INIT; static expert_field hf_bootp_opt_overload_file_end_missing = EI_INIT; static expert_field hf_bootp_opt_overload_sname_end_missing = EI_INIT; static expert_field hf_bootp_subopt_unknown_type = EI_INIT; -static expert_field ei_bootp_option77_user_class_malformed = EI_INIT; static expert_field ei_bootp_option_civic_location_bad_cattype = EI_INIT; static expert_field ei_bootp_option_dhcp_name_service_invalid = EI_INIT; static expert_field ei_bootp_option_sip_server_address_encoding = EI_INIT; @@ -631,7 +630,21 @@ static expert_field ei_bootp_option_isns_ignored_bitfield = EI_INIT; static expert_field ei_bootp_option242_avaya_l2qvlan_invalid = EI_INIT; static expert_field ei_bootp_option242_avaya_vlantest_invalid = EI_INIT; +static dissector_table_t bootp_option_table; +static dissector_table_t bootp_enterprise_table; +static heur_dissector_list_t bootp_vendor_id_subdissector; +static heur_dissector_list_t bootp_vendor_info_subdissector; static dissector_handle_t bootp_handle; +static dissector_handle_t bootpopt_basic_handle; + +typedef struct bootp_option_data +{ + guchar option; + guint8 *overload; + tvbuff_t *orig_tvb; + const char *dhcp_type; + const guint8 *vendor_class_id; +} bootp_option_data_t; /* RFC2937 The Name Service Search Option for DHCP */ #define RFC2937_LOCAL_NAMING_INFORMATION 0 @@ -984,37 +997,11 @@ static const enum_val_t pkt_ccc_protocol_versions[] = { static gint pkt_ccc_protocol_version = PACKETCABLE_CCC_RFC_3495; static guint pkt_ccc_option = 122; - -static int dissect_vendor_pxeclient_suboption(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree, - tvbuff_t *tvb, int optoff, int optend); -static int dissect_vendor_cablelabs_suboption(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree, - tvbuff_t *tvb, int optoff, int optend); -static int dissect_vendor_bsdp_suboption(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree, - tvbuff_t *tvb, int optoff, int optend); -static gboolean test_encapsulated_vendor_options(tvbuff_t *tvb, int optoff, int optend); -static int dissect_vendor_alcatel_suboption(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree, - tvbuff_t *tvb, int optoff, int optend); -static int dissect_netware_ip_suboption(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree, - tvbuff_t *tvb, int optoff, int optend); -static int dissect_vendor_tr111_suboption(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree, - tvbuff_t *tvb, int optoff, int optend); -static int bootp_dhcp_decode_agent_info(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree, - tvbuff_t *tvb, int optoff, int optend); -static void dissect_packetcable_mta_cap(proto_tree *v_tree, packet_info *pinfo, tvbuff_t *tvb, - int voff, int len); static void dissect_docsis_cm_cap(proto_tree *v_tree, tvbuff_t *tvb, int voff, int len, gboolean opt125); -static int dissect_packetcable_i05_ccc(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree, - tvbuff_t *tvb, int optoff, int optend); -static int dissect_packetcable_ietf_ccc(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree, - tvbuff_t *tvb, int optoff, int optend, int revision); -static int dissect_vendor_cl_suboption(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree, - tvbuff_t *tvb, int optoff, int optend); -static int dissect_vendor_generic_suboption(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree, - tvbuff_t *tvb, int optoff, int optend); -static int dissect_isns(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree, - tvbuff_t *tvb, int optoff, int optlen); -static void dissect_vendor_bsdp_boot_image(proto_tree *v_tree, tvbuff_t *tvb, int optoff); + +#define ARUBA_INSTANT_AP "ArubaInstantAP" +#define ARUBA_AP "ArubaAP" #define OPT53_DISCOVER "Discover" /* http://www.iana.org/assignments/bootp-dhcp-parameters */ @@ -1215,6 +1202,9 @@ static const string_string option242_avaya_static_vals[] = { /* bootp options administration */ #define BOOTP_OPT_NUM 256 +/* All of the options that have a "basic" type that can be handled by dissect_bootpopt_basic_type() */ +#define BOOTP_OPTION_BASICTYPE_RANGE "1-20,22-32,34-42,44-51,53-54,56-59,64-76,86-87,91-93,100-101,112-113,116,118,137-138,150,161,209-210,252" + /* Re-define structure. Values to be updated by bootp_init_protocol */ static struct opt_info bootp_opt[BOOTP_OPT_NUM]; @@ -1492,6 +1482,7 @@ typedef struct { static uat_bootp_record_t *uat_bootp_records = NULL; static uat_t *bootp_uat = NULL; static guint num_bootp_records_uat = 0; +static wmem_list_t *saved_uat_opts = NULL; /* List of previous options from UAT to "free" from dissection */ static void* uat_bootp_record_copy_cb(void* n, const void* o, size_t siz _U_) { uat_bootp_record_t* new_record = (uat_bootp_record_t *)n; @@ -1735,33 +1726,11 @@ bootp_handle_basic_types(packet_info *pinfo, proto_tree *tree, proto_item *item, return consumed; } -/* Returns the number of bytes consumed by this option. */ static int -bootp_option(tvbuff_t *tvb, packet_info *pinfo, proto_tree *bp_tree, proto_item *bp_item, int voff, - int eoff, gboolean first_pass, gboolean *at_end, const char **dhcp_type_p, - const guint8 **vendor_class_id_p, guint8 *overload_p) +dissect_bootpopt_basic_type(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data) { struct opt_info *opt; - enum field_type ftype; - guchar code = tvb_get_guint8(tvb, voff); - int optlen; - guchar byte; - int i, consumed, basictype_consumed; - int optoff, optleft, optend; - proto_tree *v_tree; - proto_item *vti, *ti; - guint8 protocol; - guint8 algorithm; - guint8 rdm; - guint8 fqdn_flags; - int o52voff, o52eoff; - gboolean o52at_end; - guint8 s_option; - guint8 s_len; - const guchar *dns_name; - guint dns_name_len; - gboolean option_handled = TRUE; - + bootp_option_data_t *option_data = (bootp_option_data_t*)data; struct basic_types_hfs default_hfs = { &hf_bootp_option_value, &hf_bootp_option_value_ip_address, @@ -1776,6 +1745,29 @@ bootp_option(tvbuff_t *tvb, packet_info *pinfo, proto_tree *bp_tree, proto_item &hf_bootp_option_value_u32 }; + opt = bootp_get_opt(option_data->option); + if (opt == NULL) + return 0; + + return bootp_handle_basic_types(pinfo, tree, tree, tvb, opt->ftype, + 0, tvb_reported_length(tvb), opt->phf, &default_hfs); +} + +/* Returns the number of bytes consumed by this option. */ +static int +bootp_option(tvbuff_t *tvb, packet_info *pinfo, 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, guint8 *overload_p) +{ + struct opt_info *opt; + guchar code = tvb_get_guint8(tvb, voff); + int optlen; + int i, consumed; + proto_tree *v_tree; + proto_item *vti, *ti_value; + tvbuff_t *option_tvb; + bootp_option_data_t option_data; + /* Options whose length isn't "optlen + 2". */ switch (code) { @@ -1899,1134 +1891,1061 @@ bootp_option(tvbuff_t *tvb, packet_info *pinfo, proto_tree *bp_tree, proto_item return consumed; } - /* function type may be overridden and that shouldn't be a 'saved' change */ - ftype = opt->ftype; - - optoff = voff+2; - vti = proto_tree_add_uint_format_value(bp_tree, hf_bootp_option_type, tvb, voff, consumed, code, "(%d) %s", code, opt->text); v_tree = proto_item_add_subtree(vti, ett_bootp_option); proto_tree_add_item(v_tree, hf_bootp_option_length, tvb, voff+1, 1, ENC_BIG_ENDIAN); - ti = proto_tree_add_item(v_tree, hf_bootp_option_value, tvb, voff+2, optlen, ENC_NA); - PROTO_ITEM_SET_HIDDEN(ti); + ti_value = proto_tree_add_item(v_tree, hf_bootp_option_value, tvb, voff+2, optlen, ENC_NA); + PROTO_ITEM_SET_HIDDEN(ti_value); - /* Special cases */ - switch (code) { + /* prepate data for dissector table */ + option_tvb = tvb_new_subset_length(tvb, voff+2, optlen); + option_data.option = code; + option_data.overload = overload_p; + option_data.dhcp_type = *dhcp_type_p; + option_data.vendor_class_id = *vendor_class_id_p; + option_data.orig_tvb = tvb; - case 21: /* Policy Filter */ - for (i = optoff, optleft = optlen; - optleft > 0; i += 8, optleft -= 8) { - if (optleft < 8) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "Option length isn't a multiple of 8"); - break; - } + if (!dissector_try_uint_new(bootp_option_table, code, option_tvb, pinfo, v_tree, FALSE, &option_data)) { + /* hf_bootp_option_value is already in tree, just make it visible */ + PROTO_ITEM_SET_VISIBLE(ti_value); + } - proto_tree_add_item(v_tree, hf_bootp_option_policy_filter_ip, tvb, i, 4, ENC_BIG_ENDIAN); - proto_tree_add_item(v_tree, hf_bootp_option_policy_filter_subnet_mask, tvb, i+4, 4, ENC_BIG_ENDIAN); - } - break; + return consumed; +} - case 33: /* Static Route */ - for (i = optoff, optleft = optlen; - optleft > 0; i += 8, optleft -= 8) { - if (optleft < 8) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "Option length isn't a multiple of 8"); - break; - } +static int +dissect_bootpopt_policy_filter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + int offset = 0; - proto_tree_add_item(v_tree, hf_bootp_option_static_route_ip, tvb, i, 4, ENC_BIG_ENDIAN); - proto_tree_add_item(v_tree, hf_bootp_option_static_route_router, tvb, i+4, 4, ENC_BIG_ENDIAN); - } - break; + while (tvb_reported_length_remaining(tvb, offset) >= 8) { + proto_tree_add_item(tree, hf_bootp_option_policy_filter_ip, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(tree, hf_bootp_option_policy_filter_subnet_mask, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + } + + if (tvb_reported_length_remaining(tvb, offset) > 0) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "Option length isn't a multiple of 8"); + } + + return tvb_captured_length(tvb); +} + +static int +dissect_bootpopt_static_route(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + int offset = 0; - case 43: /* Vendor-Specific Info */ - s_option = tvb_get_guint8(tvb, optoff); + while (tvb_reported_length_remaining(tvb, offset) >= 8) { + proto_tree_add_item(tree, hf_bootp_option_static_route_ip, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(tree, hf_bootp_option_static_route_router, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + } + + if (tvb_reported_length_remaining(tvb, offset) > 0) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "Option length isn't a multiple of 8"); + } - /* 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); + return tvb_captured_length(tvb); +} - optend = optoff + optlen; - while (optoff < optend) { - optoff = dissect_vendor_pxeclient_suboption(pinfo, vti, v_tree, - tvb, optoff, optend); +static int +dissect_bootpopt_vendor_specific_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) +{ + heur_dtbl_entry_t *hdtbl_entry; + proto_tree *vendor_tree; + + if (!dissector_try_heuristic(bootp_vendor_info_subdissector, tvb, pinfo, tree, &hdtbl_entry, data)) { + /* Default Vendor-Specific Info.. display in bytes */ + vendor_tree = proto_item_add_subtree(tree, ett_bootp_option); + proto_tree_add_item(vendor_tree, hf_bootp_option43_value, tvb, 0, tvb_reported_length(tvb), ENC_NA); + } + + return tvb_captured_length(tvb); +} + +static int +dissect_bootpopt_option_overload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) +{ + int offset = 0; + int suboffset, suboffset_end; + gboolean at_end; + bootp_option_data_t *option_data = (bootp_option_data_t*)data; + guint32 byte; + + if (tvb_reported_length(tvb) < 1) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "length isn't >= 1"); + return 1; + } + + proto_tree_add_item_ret_uint(tree, hf_bootp_option_option_overload, tvb, offset, 1, ENC_BIG_ENDIAN, &byte); + + /* Just in case we find an option 52 in sname or file */ + if ((*option_data->overload >= 1) && (*option_data->overload <= 3)) { + if (byte & OPT_OVERLOAD_SNAME) { + proto_item *oti; + proto_tree *overload_tree = proto_tree_add_subtree(tree, option_data->orig_tvb, + SERVER_NAME_OFFSET, SERVER_NAME_LEN, ett_bootp_server_hostname, &oti, + "Server host name option overload"); + guint8 ignore_overload = 0; + suboffset = SERVER_NAME_OFFSET; + suboffset_end = SERVER_NAME_OFFSET + SERVER_NAME_LEN; + at_end = FALSE; + rfc3396_dns_domain_search_list.index_current_block = 0; + rfc3396_sip_server.index_current_block = 0; + while (suboffset < suboffset_end && !at_end) { + suboffset += bootp_option(option_data->orig_tvb, pinfo, overload_tree, suboffset, + suboffset_end, FALSE, &at_end, + &option_data->dhcp_type, &option_data->vendor_class_id, + &ignore_overload); } - } 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, "OpenCable2.0", strlen("OpenCable2.0")) == 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(pinfo, vti, v_tree, - tvb, optoff, optend); + if (!at_end) + { + expert_add_info(pinfo, oti, &hf_bootp_opt_overload_sname_end_missing); } - } else if (*vendor_class_id_p != NULL && - ((strncmp((const gchar*)*vendor_class_id_p, "ArubaAP", strlen("ArubaAP")) == 0) )) { - /* Aruba AP */ - proto_item_append_text(vti, " (Aruba AP)"); - proto_tree_add_item(v_tree, hf_bootp_option43_arubaap_controllerip, tvb, optoff, optlen, ENC_ASCII|ENC_NA); - } else if (*vendor_class_id_p != NULL && - ((strncmp((const gchar*)*vendor_class_id_p, "ArubaInstantAP", strlen("ArubaInstantAP")) == 0) )) { - gint32 nameorglen, ampiplen; - /* Aruba Instant AP */ - proto_item_append_text(vti, " (Aruba Instant AP)"); - vti = proto_tree_add_item(v_tree, hf_bootp_option43_arubaiap, tvb, optoff, optlen, ENC_ASCII|ENC_NA); - v_tree = proto_item_add_subtree(vti, ett_bootp_option43_suboption); - nameorglen = tvb_find_guint8(tvb, optoff, optlen, ',') - optoff; - proto_tree_add_item(v_tree, hf_bootp_option43_arubaiap_nameorg, tvb, optoff, nameorglen, ENC_ASCII|ENC_NA); - ampiplen = tvb_find_guint8(tvb, optoff+nameorglen+1, optlen-nameorglen-1, ',') - (optoff+nameorglen+1); - proto_tree_add_item(v_tree, hf_bootp_option43_arubaiap_ampip, tvb, optoff+nameorglen+1, ampiplen, ENC_ASCII|ENC_NA); - proto_tree_add_item(v_tree, hf_bootp_option43_arubaiap_password, tvb, optoff+nameorglen+1+ampiplen+1, optlen-(nameorglen+1+ampiplen+1), ENC_ASCII|ENC_NA); - } else if (*vendor_class_id_p != NULL && - ((strncmp((const gchar*)*vendor_class_id_p, PACKETCABLE_BSDP, strlen(PACKETCABLE_BSDP)) == 0) )) { - /* Apple BSDP */ - proto_item_append_text(vti, " (Boot Server Discovery Protocol (BSDP))"); - - optend = optoff + optlen; - while (optoff < optend) { - optoff = dissect_vendor_bsdp_suboption(pinfo, vti, v_tree, - tvb, optoff, optend); + } + if (byte & OPT_OVERLOAD_FILE) { + proto_item *oti; + proto_tree *overload_tree = proto_tree_add_subtree(tree, option_data->orig_tvb, + FILE_NAME_OFFSET, FILE_NAME_LEN, ett_bootp_filename_option, &oti, + "Boot file name option overload"); + guint8 ignore_overload = 0; + suboffset = FILE_NAME_OFFSET; + suboffset_end = FILE_NAME_OFFSET + FILE_NAME_LEN; + at_end = FALSE; + rfc3396_dns_domain_search_list.index_current_block = 0; + rfc3396_sip_server.index_current_block = 0; + while (suboffset < suboffset_end && !at_end) { + suboffset += bootp_option(option_data->orig_tvb, pinfo, overload_tree, suboffset, + suboffset_end, FALSE, &at_end, + &option_data->dhcp_type, &option_data->vendor_class_id, + &ignore_overload); } - } else if ((s_option==58 || s_option==64 || s_option==65 - || s_option==66 || s_option==67) - && test_encapsulated_vendor_options(tvb, optoff, optoff+optlen)) { - /* Note that this is a rather weak (permissive) heuristic, */ - /* but since it comes last, I guess this is OK. */ - /* Add any stronger (less permissive) heuristics before this! */ - /* Alcatel-Lucent DHCP Extensions */ - proto_item_append_text(vti, " (Alcatel-Lucent)"); - optend = optoff + optlen; - while (optoff < optend) { - optoff = dissect_vendor_alcatel_suboption(pinfo, vti, v_tree, - tvb, optoff, optend); + if (!at_end) + { + expert_add_info(pinfo, oti, &hf_bootp_opt_overload_file_end_missing); } - } else { - /* Default Vendor-Specific Info.. display in bytes */ - proto_tree_add_item(v_tree, hf_bootp_option43_value, tvb, optoff, optlen, ENC_NA); } - break; + /* The final end option is not in overload */ + *option_data->overload = 0; + } - case 52: /* Option Overload */ - if (optlen < 1) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "length isn't >= 1"); - break; - } + return tvb_captured_length(tvb); +} - byte = tvb_get_guint8(tvb, optoff); - proto_tree_add_item(v_tree, *opt->phf, tvb, optoff, 1, ENC_BIG_ENDIAN); - - /* Just in case we find an option 52 in sname or file */ - if (voff > VENDOR_INFO_OFFSET && byte >= 1 && byte <= 3) { - if (byte & OPT_OVERLOAD_FILE) { - proto_item *oti; - proto_tree_add_subtree(bp_tree, tvb, - FILE_NAME_OFFSET, FILE_NAME_LEN, ett_bootp_filename_option, &oti, - "Boot file name option overload"); - o52voff = FILE_NAME_OFFSET; - o52eoff = FILE_NAME_OFFSET + FILE_NAME_LEN; - o52at_end = FALSE; - rfc3396_dns_domain_search_list.index_current_block = 0; - rfc3396_sip_server.index_current_block = 0; - while (o52voff < o52eoff && !o52at_end) { - o52voff += bootp_option(tvb, pinfo, bp_tree, bp_item, o52voff, - o52eoff, FALSE, &o52at_end, - dhcp_type_p, vendor_class_id_p, - overload_p); - } - if (!o52at_end) - { - expert_add_info(pinfo, oti, &hf_bootp_opt_overload_file_end_missing); - } - } - if (byte & OPT_OVERLOAD_SNAME) { - proto_item *oti; - proto_tree_add_subtree(bp_tree, tvb, - SERVER_NAME_OFFSET, SERVER_NAME_LEN, ett_bootp_server_hostname, &oti, - "Server host name option overload"); - o52voff = SERVER_NAME_OFFSET; - o52eoff = SERVER_NAME_OFFSET + SERVER_NAME_LEN; - o52at_end = FALSE; - rfc3396_dns_domain_search_list.index_current_block = 0; - rfc3396_sip_server.index_current_block = 0; - while (o52voff < o52eoff && !o52at_end) { - o52voff += bootp_option(tvb, pinfo, bp_tree, bp_item, o52voff, - o52eoff, FALSE, &o52at_end, - dhcp_type_p, vendor_class_id_p, - overload_p); - } - if (!o52at_end) - { - expert_add_info(pinfo, oti, &hf_bootp_opt_overload_sname_end_missing); - } - } - /* The final end option is not in overload */ - *overload_p = 0; - } - break; +static int +dissect_bootpopt_dhcp(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data _U_) +{ + guint32 type; - case 53: - /* Show the message type name on the Message Type option, and in the protocol root */ - proto_item_append_text(vti, " (%s)", val_to_str(tvb_get_guint8(tvb, voff+2), - opt53_text, - "Unknown Message Type (0x%02x)")); - proto_item_append_text(bp_item, " (%s)", val_to_str(tvb_get_guint8(tvb, voff+2), - opt53_text, - "Unknown Message Type (0x%02x)")); - break; + proto_tree_add_item_ret_uint(tree, hf_bootp_option_dhcp, tvb, 0, 1, ENC_NA, &type); + /* Show the message type name on the Message Type option, and in the protocol root */ + proto_item_append_text(tree, " (%s)", val_to_str(type, opt53_text, "Unknown Message Type (0x%02x)")); + proto_item_append_text(proto_item_get_parent(tree), " (%s)", val_to_str(type, opt53_text, "Unknown Message Type (0x%02x)")); - case 55: /* Parameter Request List */ - for (i = 0; i < optlen; i++) { - byte = tvb_get_guint8(tvb, optoff+i); - proto_tree_add_uint_format_value(v_tree, hf_bootp_option_parameter_request_list_item, - tvb, optoff+i, 1, byte, "(%d) %s", byte, bootp_get_opt_text(byte)); - } - break; + return tvb_captured_length(tvb); +} - case 60: /* Vendor class identifier */ - /* - * XXX - RFC 2132 says this is a string of octets; - * should we check for non-printables? - */ +static int +dissect_bootpopt_param_request_list(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data _U_) +{ + int offset = 0; + guint8 byte; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + byte = tvb_get_guint8(tvb, offset); + proto_tree_add_uint_format_value(tree, hf_bootp_option_parameter_request_list_item, + tvb, offset, 1, byte, "(%d) %s", byte, bootp_get_opt_text(byte)); + offset++; + } - proto_tree_add_item(v_tree, hf_bootp_option_vendor_class_id, tvb, optoff, consumed-2, ENC_ASCII|ENC_NA); - if ((tvb_memeql(tvb, optoff, (const guint8*)PACKETCABLE_MTA_CAP10, - (int)strlen(PACKETCABLE_MTA_CAP10)) == 0) - || - (tvb_memeql(tvb, optoff, (const guint8*)PACKETCABLE_MTA_CAP15, - (int)strlen(PACKETCABLE_MTA_CAP15)) == 0) - || - (tvb_memeql(tvb, optoff, (const guint8*)PACKETCABLE_MTA_CAP20, - (int)strlen(PACKETCABLE_MTA_CAP20)) == 0)) - { - dissect_packetcable_mta_cap(v_tree, pinfo, tvb, optoff, optlen); - } else - if ((tvb_memeql(tvb, optoff, (const guint8*)PACKETCABLE_CM_CAP11, - (int)strlen(PACKETCABLE_CM_CAP11)) == 0) - || - (tvb_memeql(tvb, optoff, (const guint8*)PACKETCABLE_CM_CAP20, - (int)strlen(PACKETCABLE_CM_CAP20)) == 0 )) - { - dissect_docsis_cm_cap(v_tree, tvb, optoff, optlen, FALSE); - } else - if (tvb_memeql(tvb, optoff, (const guint8*)PACKETCABLE_CM_CAP30, - (int)strlen(PACKETCABLE_CM_CAP30)) == 0 ) - { - proto_tree_add_item(v_tree, hf_bootp_option_vendor_class_data, tvb, optoff, optlen, ENC_ASCII|ENC_NA); - } else - if (tvb_memeql(tvb, optoff, (const guint8*)PACKETCABLE_BSDPD, - (int)strlen(PACKETCABLE_BSDPD)) == 0 ) - { - proto_tree_add_item(v_tree, hf_bootp_option_vendor_class_data, tvb, optoff+(int)strlen(PACKETCABLE_BSDPD), optlen-(int)strlen(PACKETCABLE_BSDPD), ENC_ASCII|ENC_NA); - } - break; + return tvb_captured_length(tvb); +} - case 61: /* Client Identifier */ - 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_item(v_tree, - hf_bootp_hw_type, tvb, optoff, 1, - ENC_NA); - - if (byte == ARPHRD_ETHER || byte == ARPHRD_IEEE802) - proto_tree_add_item(v_tree, - hf_bootp_hw_ether_addr, tvb, optoff+1, 6, - ENC_NA); - else - proto_tree_add_string(v_tree, hf_bootp_client_hardware_address, tvb, optoff+1, 6, - tvb_arphrdaddr_to_str(tvb, optoff+1, 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, bootp_uuid_endian); - - /* From RFC 4361 paragraph 6.1 DHCPv4 Client Behavior: - To send an RFC 3315-style binding identifier in a DHCPv4 'client - identifier' option, the type of the 'client identifier' option is set - to 255. */ - } else if (byte == 255) { - guint16 duidtype; - guint16 hwtype; - - /* The type field is immediately followed by the IAID, which is - an opaque 32-bit quantity */ - proto_tree_add_string(v_tree, hf_bootp_client_id_iaid, tvb, optoff+1, 4, - tvb_arphrdaddr_to_str(tvb, optoff+1, 4, byte)); - optoff = optoff + 5; - duidtype = tvb_get_ntohs(tvb, optoff); - proto_tree_add_item(v_tree, hf_bootp_client_id_duid_type, tvb, optoff, 2, ENC_BIG_ENDIAN); - switch (duidtype) { - case DUID_LLT: - if (optlen < 8) { - expert_add_info(pinfo, vti, &ei_bootp_mal_duid); - break; - } - hwtype=tvb_get_ntohs(tvb, optoff + 2); - proto_tree_add_item(v_tree, hf_bootp_client_identifier_duid_llt_hw_type, - tvb, optoff + 2, 2, ENC_BIG_ENDIAN); - - /* XXX seconds since Jan 1 2000 */ - proto_tree_add_item(v_tree, hf_bootp_client_identifier_time, tvb, optoff + 4, 4, ENC_BIG_ENDIAN); - if (optlen > 8) { - proto_tree_add_string(v_tree, hf_bootp_client_identifier_link_layer_address, tvb, optoff + 8, - optlen - 13, tvb_arphrdaddr_to_str(tvb, optoff+8, optlen-13, hwtype)); - } - break; - case DUID_EN: - if (optlen < 6) { - expert_add_info(pinfo, vti, &ei_bootp_mal_duid); - break; - } - proto_tree_add_item(v_tree, hf_bootp_client_identifier_enterprise_num, tvb, optoff + 2, 4, ENC_BIG_ENDIAN); - if (optlen > 6) { - proto_tree_add_item(v_tree, hf_bootp_client_identifier, tvb, optoff + 6, optlen - 11, ENC_NA); - } - break; - case DUID_LL: - if (optlen < 4) { - expert_add_info(pinfo, vti, &ei_bootp_mal_duid); - break; - } - hwtype=tvb_get_ntohs(tvb, optoff + 2); - proto_tree_add_item(v_tree, hf_bootp_client_identifier_duid_ll_hw_type, - tvb, optoff + 2, 2, ENC_BIG_ENDIAN); +static int +dissect_bootpopt_vendor_class_identifier(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) +{ + heur_dtbl_entry_t *hdtbl_entry; - if (optlen > 4) { - proto_tree_add_string(v_tree, hf_bootp_client_identifier_link_layer_address, tvb, optoff + 4, - optlen - 9, tvb_arphrdaddr_to_str(tvb, optoff+4, optlen-9, hwtype)); - } + /* + * XXX - RFC 2132 says this is a string of octets; + * should we check for non-printables? + */ + proto_tree_add_item(tree, hf_bootp_option_vendor_class_id, tvb, 0, tvb_reported_length(tvb), ENC_ASCII|ENC_NA); + dissector_try_heuristic(bootp_vendor_id_subdissector, tvb, pinfo, tree, &hdtbl_entry, data); + + return tvb_captured_length(tvb); +} + +static int +dissect_bootpopt_client_identifier(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + int offset = 0; + int length = tvb_reported_length(tvb); + guchar byte; + + if (length > 0) + byte = tvb_get_guint8(tvb, offset); + 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 (length == 7 && byte > 0 && byte < 48) { + proto_tree_add_item(tree, hf_bootp_hw_type, tvb, offset, 1, ENC_NA); + + if (byte == ARPHRD_ETHER || byte == ARPHRD_IEEE802) + proto_tree_add_item(tree, hf_bootp_hw_ether_addr, tvb, offset+1, 6, ENC_NA); + else + proto_tree_add_string(tree, hf_bootp_client_hardware_address, tvb, offset+1, 6, + tvb_arphrdaddr_to_str(tvb, offset+1, 6, byte)); + } else if (length == 17 && byte == 0) { + /* Identifier is a UUID */ + proto_tree_add_item(tree, hf_bootp_client_identifier_uuid, tvb, offset + 1, 16, bootp_uuid_endian); + + /* From RFC 4361 paragraph 6.1 DHCPv4 Client Behavior: + To send an RFC 3315-style binding identifier in a DHCPv4 'client + identifier' option, the type of the 'client identifier' option is set + to 255. */ + } else if (byte == 255) { + guint16 duidtype; + guint16 hwtype; + + /* The type field is immediately followed by the IAID, which is + an opaque 32-bit quantity */ + proto_tree_add_string(tree, hf_bootp_client_id_iaid, tvb, offset+1, 4, + tvb_arphrdaddr_to_str(tvb, offset+1, 4, byte)); + offset += 5; + duidtype = tvb_get_ntohs(tvb, offset); + proto_tree_add_item(tree, hf_bootp_client_id_duid_type, tvb, offset, 2, ENC_BIG_ENDIAN); + switch (duidtype) { + case DUID_LLT: + if (length < 8) { + expert_add_info(pinfo, tree, &ei_bootp_mal_duid); break; } - } else if (byte == 0 && optlen > 1) { - /* identifier other than a hardware address (e.g. a fully qualified domain name) */ - proto_tree_add_item(v_tree, hf_bootp_client_identifier_type, tvb, optoff, 1, ENC_NA); - proto_tree_add_item(v_tree, hf_bootp_client_identifier_undef, tvb, optoff+1, optlen-1, ENC_ASCII|ENC_NA); - } else { - /* otherwise, it's opaque data */ - } - break; - case 77: { /* User Class Information RFC 3004 */ - guchar user_class_instance_index; - proto_item *vtix; - proto_tree *o77_v_tree; - if (optlen < 2) { - expert_add_info_format(pinfo, v_tree, &ei_bootp_bad_length, "length isn't >= 2"); + hwtype=tvb_get_ntohs(tvb, offset + 2); + proto_tree_add_item(tree, hf_bootp_client_identifier_duid_llt_hw_type, + tvb, offset + 2, 2, ENC_BIG_ENDIAN); + + /* XXX seconds since Jan 1 2000 */ + proto_tree_add_item(tree, hf_bootp_client_identifier_time, tvb, offset + 4, 4, ENC_BIG_ENDIAN); + if (length > 8) { + proto_tree_add_string(tree, hf_bootp_client_identifier_link_layer_address, tvb, offset + 8, + length - 13, tvb_arphrdaddr_to_str(tvb, offset+8, length-13, hwtype)); + } break; - } - optleft = optlen; - for (user_class_instance_index = 0, i = 0, byte = tvb_get_guint8(tvb, optoff); i < optlen; byte = tvb_get_guint8(tvb, optoff + i), user_class_instance_index++) { - /* Create subtree for instance of User Class. */ - vtix = proto_tree_add_uint_format_value(v_tree, hf_bootp_option77_user_class, - tvb, optoff + i, byte + 1, user_class_instance_index, "[%d]", user_class_instance_index); - o77_v_tree = proto_item_add_subtree(vtix, ett_bootp_option77_instance); - - /* Add length for instance of User Class. */ - proto_tree_add_item(o77_v_tree, hf_bootp_option77_user_class_length, - tvb, optoff + i, 1, ENC_BIG_ENDIAN); - - if (byte == 0) { - expert_add_info_format(pinfo, vtix, &ei_bootp_bad_length, "UC_Len_%u isn't >= 1 (UC_Len_%u = 0)", user_class_instance_index, user_class_instance_index); + case DUID_EN: + if (length < 6) { + expert_add_info(pinfo, tree, &ei_bootp_mal_duid); break; } - optleft -= byte + 1; - if (optleft < 0) { - expert_add_info(pinfo, vtix, &ei_bootp_option77_user_class_malformed); + proto_tree_add_item(tree, hf_bootp_client_identifier_enterprise_num, tvb, offset + 2, 4, ENC_BIG_ENDIAN); + if (length > 6) { + proto_tree_add_item(tree, hf_bootp_client_identifier, tvb, offset + 6, length - 11, ENC_NA); + } + break; + case DUID_LL: + if (length < 4) { + expert_add_info(pinfo, tree, &ei_bootp_mal_duid); break; } + hwtype=tvb_get_ntohs(tvb, offset + 2); + proto_tree_add_item(tree, hf_bootp_client_identifier_duid_ll_hw_type, + tvb, offset + 2, 2, ENC_BIG_ENDIAN); - /* Add data for instance of User Class. */ - proto_tree_add_item(o77_v_tree, hf_bootp_option77_user_class_data, - tvb, optoff + i + 1, byte, ENC_NA); - - /* Slide to next instance of User Class if any. */ - i += byte + 1; + if (length > 4) { + proto_tree_add_string(tree, hf_bootp_client_identifier_link_layer_address, tvb, offset + 4, + length - 9, tvb_arphrdaddr_to_str(tvb, offset+4, length-9, hwtype)); + } + break; } - break; + } else if (byte == 0 && length > 1) { + /* identifier other than a hardware address (e.g. a fully qualified domain name) */ + proto_tree_add_item(tree, hf_bootp_client_identifier_type, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_bootp_client_identifier_undef, tvb, offset+1, length-1, ENC_ASCII|ENC_NA); + } else { + /* otherwise, it's opaque data */ } - 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_item(v_tree, - hf_bootp_hw_type, tvb, optoff, 1, - ENC_NA); - if (byte == ARPHRD_ETHER || byte == ARPHRD_IEEE802) - proto_tree_add_item(v_tree, - hf_bootp_hw_ether_addr, tvb, optoff+1, 6, - ENC_NA); - else - proto_tree_add_string(v_tree, hf_bootp_client_hardware_address, tvb, optoff+1, 6, - tvb_arphrdaddr_to_str(tvb, optoff+1, 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, bootp_uuid_endian); - } else { - /* otherwise, it's opaque data */ - } - break; - case 63: /* NetWare/IP options (RFC 2242) */ + return tvb_captured_length(tvb); +} - optend = optoff + optlen; - while (optoff < optend) - optoff = dissect_netware_ip_suboption(pinfo, vti, v_tree, tvb, optoff, optend); - break; +static int +dissect_bootpopt_user_class_information(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + guchar user_class_instance_index = 0; + int offset = 0; + guint32 class_length; + proto_item *vtix, *len_item; + proto_tree *o77_v_tree; + if (tvb_reported_length(tvb) < 2) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "length isn't >= 2"); + return 1; + } + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + /* Create subtree for instance of User Class. */ + vtix = proto_tree_add_uint_format_value(tree, hf_bootp_option77_user_class, + tvb, offset, 1, user_class_instance_index, "[%d]", user_class_instance_index); + o77_v_tree = proto_item_add_subtree(vtix, ett_bootp_option77_instance); - case 78: /* SLP Directory Agent Option RFC2610 Added by Greg Morris (gmorris@novell.com)*/ - if (optlen < 1) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "length isn't >= 1"); + /* Add length for instance of User Class. */ + len_item = proto_tree_add_item_ret_uint(o77_v_tree, hf_bootp_option77_user_class_length, + tvb, offset, 1, ENC_BIG_ENDIAN, &class_length); + proto_item_set_len(vtix, class_length+1); + offset += 1; + + if (class_length == 0) { + expert_add_info_format(pinfo, len_item, &ei_bootp_bad_length, "UC_Len_%u isn't >= 1 (UC_Len_%u = 0)", user_class_instance_index, user_class_instance_index); break; } - optleft = optlen; - byte = tvb_get_guint8(tvb, optoff); - proto_tree_add_item(v_tree, hf_bootp_option_slp_directory_agent_value, tvb, optoff, 1, ENC_BIG_ENDIAN); - optoff++; - optleft--; - if (byte == 0x80) { - if (optleft == 0) - break; - optoff++; - optleft--; - } - for (i = optoff; optleft > 0; i += 4, optleft -= 4) { - if (optleft < 4) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "Option length isn't a multiple of 4"); - break; - } + /* Add data for instance of User Class. */ + proto_tree_add_item(o77_v_tree, hf_bootp_option77_user_class_data, tvb, offset, class_length, ENC_NA); + offset += class_length; + user_class_instance_index++; + } - proto_tree_add_item(v_tree, hf_bootp_option_slp_directory_agent_slpda_address, tvb, i, 4, ENC_BIG_ENDIAN); - } - break; + return tvb_captured_length(tvb); +} + +static int +dissect_bootpopt_slp_directory_agent(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + int offset = 0; + guint32 byte; - case 79: /* SLP Service Scope Option RFC2610 Added by Greg Morris (gmorris@novell.com)*/ - proto_tree_add_item(v_tree, hf_bootp_option_slp_service_scope_value, tvb, optoff, 1, ENC_BIG_ENDIAN); + if (tvb_reported_length(tvb) < 1) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "length isn't >= 1"); + return 1; + } - optoff++; - optleft = optlen - 1; - proto_tree_add_item(v_tree, hf_bootp_option_slp_service_scope_string, tvb, optoff, optleft, ENC_ASCII|ENC_NA); - break; + proto_tree_add_item_ret_uint(tree, hf_bootp_option_slp_directory_agent_value, tvb, offset, 1, ENC_BIG_ENDIAN, &byte); + offset++; - case 81: /* Client Fully Qualified Domain Name */ - { - static const int * fqdn_hf_flags[] = { - &hf_bootp_fqdn_mbz, - &hf_bootp_fqdn_n, - &hf_bootp_fqdn_e, - &hf_bootp_fqdn_o, - &hf_bootp_fqdn_s, - NULL - }; - - if (optlen < 3) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "length isn't >= 3"); - break; - } + if (byte == 0x80) { + if (tvb_reported_length_remaining(tvb, offset) == 0) + return offset; - fqdn_flags = tvb_get_guint8(tvb, optoff); - proto_tree_add_bitmask(v_tree, tvb, optoff, hf_bootp_fqdn_flags, - ett_bootp_fqdn_flags, fqdn_hf_flags, ENC_BIG_ENDIAN); - - /* XXX: use code from packet-dns for return code decoding */ - proto_tree_add_item(v_tree, hf_bootp_fqdn_rcode1, tvb, optoff+1, 1, ENC_BIG_ENDIAN); - /* XXX: use code from packet-dns for return code decoding */ - proto_tree_add_item(v_tree, hf_bootp_fqdn_rcode2, tvb, optoff+2, 1, ENC_BIG_ENDIAN); - if (optlen > 3) { - if (fqdn_flags & F_FQDN_E) { - get_dns_name(tvb, optoff+3, optlen-3, optoff+3, &dns_name, &dns_name_len); - proto_tree_add_string(v_tree, hf_bootp_fqdn_name, - tvb, optoff+3, optlen-3, format_text(wmem_packet_scope(), dns_name, dns_name_len)); - } else { - proto_tree_add_item(v_tree, hf_bootp_fqdn_asciiname, - tvb, optoff+3, optlen-3, ENC_ASCII|ENC_NA); - } - } - } - break; + offset++; + } - case 82: /* Relay Agent Information Option */ - optend = optoff + optlen; - while (optoff < optend) - optoff = bootp_dhcp_decode_agent_info(pinfo, vti, v_tree, tvb, optoff, optend); - break; + while (tvb_reported_length_remaining(tvb, offset) >= 4) { + proto_tree_add_item(tree, hf_bootp_option_slp_directory_agent_slpda_address, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + } - case 83: /* iSNS Option (RFC 4174) */ - optoff = dissect_isns(pinfo, vti, v_tree, tvb, optoff, optlen); - break; + if (tvb_reported_length_remaining(tvb, offset) > 0) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "Option length isn't a multiple of 4"); + } - 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_tree_add_item(v_tree, hf_bootp_option_novell_dss_string, tvb, optoff, optlen, ENC_ASCII|ENC_NA); - } else{ - /* IP addresses */ - for (i = optoff, optleft = optlen; optleft > 0; - i += 4, optleft -= 4) { - if (optleft < 4) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "Option length isn't a multiple of 4"); - break; - } + return tvb_captured_length(tvb); +} - proto_tree_add_item(v_tree, hf_bootp_option_novell_dss_ip, tvb, i, 4, ENC_BIG_ENDIAN); - } - } - break; +static int +dissect_bootpopt_slp_service_scope(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data _U_) +{ + int offset = 0; - case 94: { /* Client network interface identifier */ - guint8 id_type; + proto_tree_add_item(tree, hf_bootp_option_slp_service_scope_value, tvb, offset, 1, ENC_BIG_ENDIAN); - id_type = tvb_get_guint8(tvb, optoff); + offset++; + proto_tree_add_item(tree, hf_bootp_option_slp_service_scope_string, tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_ASCII|ENC_NA); - if (id_type == 0x01) { - proto_tree_add_item(v_tree, hf_bootp_option_client_network_id_major_ver, - tvb, optoff + 1, 1, ENC_LITTLE_ENDIAN); - proto_tree_add_item(v_tree, hf_bootp_option_client_network_id_minor_ver, - tvb, optoff + 2, 1, ENC_LITTLE_ENDIAN); - } + return tvb_captured_length(tvb); +} - break; +static int +dissect_bootpopt_client_full_domain_name(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data _U_) +{ + static const int * fqdn_hf_flags[] = { + &hf_bootp_fqdn_mbz, + &hf_bootp_fqdn_n, + &hf_bootp_fqdn_e, + &hf_bootp_fqdn_o, + &hf_bootp_fqdn_s, + NULL + }; + guint8 fqdn_flags; + int offset = 0, length = tvb_reported_length(tvb); + const guchar *dns_name; + guint dns_name_len; + + if (length < 3) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "length isn't >= 3"); + return 1; } - case 90: /* DHCP Authentication */ - if (optlen < 11) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "length isn't >= 11"); - break; + fqdn_flags = tvb_get_guint8(tvb, offset); + proto_tree_add_bitmask(tree, tvb, offset, hf_bootp_fqdn_flags, + ett_bootp_fqdn_flags, fqdn_hf_flags, ENC_BIG_ENDIAN); + + /* XXX: use code from packet-dns for return code decoding */ + proto_tree_add_item(tree, hf_bootp_fqdn_rcode1, tvb, offset+1, 1, ENC_BIG_ENDIAN); + /* XXX: use code from packet-dns for return code decoding */ + proto_tree_add_item(tree, hf_bootp_fqdn_rcode2, tvb, offset+2, 1, ENC_BIG_ENDIAN); + + if (length > 3) { + if (fqdn_flags & F_FQDN_E) { + get_dns_name(tvb, offset+3, length-3, offset+3, &dns_name, &dns_name_len); + proto_tree_add_string(tree, hf_bootp_fqdn_name, + tvb, offset+3, length-3, format_text(wmem_packet_scope(), dns_name, dns_name_len)); + } else { + proto_tree_add_item(tree, hf_bootp_fqdn_asciiname, tvb, offset+3, length-3, ENC_ASCII|ENC_NA); } - optleft = optlen; - protocol = tvb_get_guint8(tvb, optoff); + } - proto_tree_add_item(v_tree, hf_bootp_option_dhcp_authentication_protocol, tvb, optoff, 1, ENC_BIG_ENDIAN); - optoff++; - optleft--; + return tvb_captured_length(tvb); +} - algorithm = tvb_get_guint8(tvb, optoff); - switch (protocol) { +static int +dissect_bootpopt_novell_servers(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data _U_) +{ + int offset = 0; - case AUTHEN_PROTO_DELAYED_AUTHEN: - proto_tree_add_item(v_tree, hf_bootp_option_dhcp_authentication_alg_delay, tvb, optoff, 1, ENC_BIG_ENDIAN); - break; + /* Option 85 can be sent as a string */ + /* Added by Greg Morris (gmorris[AT]novell.com) */ + if (novell_string) { + proto_tree_add_item(tree, hf_bootp_option_novell_dss_string, tvb, offset, tvb_reported_length(tvb), ENC_ASCII|ENC_NA); + } else { + /* IP addresses */ + while (tvb_reported_length_remaining(tvb, offset) >= 4) { - default: - proto_tree_add_item(v_tree, hf_bootp_option_dhcp_authentication_algorithm, tvb, optoff, 1, ENC_BIG_ENDIAN); - break; + proto_tree_add_item(tree, hf_bootp_option_novell_dss_ip, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; } - optoff++; - optleft--; - rdm = tvb_get_guint8(tvb, optoff); - proto_tree_add_item(v_tree, hf_bootp_option_dhcp_authentication_rdm, tvb, optoff, 1, ENC_BIG_ENDIAN); - optoff++; - optleft--; + if (tvb_reported_length_remaining(tvb, offset) > 0) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "Option length isn't a multiple of 4"); + } + } - switch (rdm) { + return tvb_captured_length(tvb); +} - case AUTHEN_RDM_MONOTONIC_COUNTER: - proto_tree_add_item(v_tree, hf_bootp_option_dhcp_authentication_rdm_replay_detection, tvb, optoff, 8, ENC_BIG_ENDIAN); - break; +static int +dissect_bootpopt_dhcp_authentication(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) +{ + int offset = 0; + bootp_option_data_t *option_data = (bootp_option_data_t*)data; + guint32 protocol, rdm; + guint8 algorithm; + + if (tvb_reported_length(tvb) < 11) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "length isn't >= 11"); + return 1; + } - default: - proto_tree_add_item(v_tree, hf_bootp_option_dhcp_authentication_rdm_rdv, tvb, optoff, 8, ENC_ASCII|ENC_NA); - break; - } - optoff += 8; - optleft -= 8; + proto_tree_add_item_ret_uint(tree, hf_bootp_option_dhcp_authentication_protocol, tvb, offset, 1, ENC_BIG_ENDIAN, &protocol); + offset++; - switch (protocol) { + algorithm = tvb_get_guint8(tvb, offset); + switch (protocol) { - case AUTHEN_PROTO_DELAYED_AUTHEN: - switch (algorithm) { + case AUTHEN_PROTO_DELAYED_AUTHEN: + proto_tree_add_item(tree, hf_bootp_option_dhcp_authentication_alg_delay, tvb, offset, 1, ENC_BIG_ENDIAN); + break; - 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) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "length isn't >= 31"); - break; - } + default: + proto_tree_add_item(tree, hf_bootp_option_dhcp_authentication_algorithm, tvb, offset, 1, ENC_BIG_ENDIAN); + break; + } + offset++; - proto_tree_add_item(v_tree, hf_bootp_option_dhcp_authentication_secret_id, tvb, optoff, 4, ENC_BIG_ENDIAN); - optoff += 4; - /*optleft -= 4;*/ - proto_tree_add_item(v_tree, hf_bootp_option_dhcp_authentication_hmac_md5_hash, tvb, optoff, 16, ENC_ASCII|ENC_NA); - break; - } + proto_tree_add_item_ret_uint(tree, hf_bootp_option_dhcp_authentication_rdm, tvb, offset, 1, ENC_BIG_ENDIAN, &rdm); + offset++; - default: - if (optleft == 0) + switch (rdm) { + + case AUTHEN_RDM_MONOTONIC_COUNTER: + proto_tree_add_item(tree, hf_bootp_option_dhcp_authentication_rdm_replay_detection, tvb, offset, 8, ENC_BIG_ENDIAN); + break; + + default: + proto_tree_add_item(tree, hf_bootp_option_dhcp_authentication_rdm_rdv, tvb, offset, 8, ENC_ASCII|ENC_NA); + break; + } + offset += 8; + + switch (protocol) { + + case AUTHEN_PROTO_DELAYED_AUTHEN: + switch (algorithm) { + + case AUTHEN_DELAYED_ALGO_HMAC_MD5: + if (option_data->dhcp_type && !strcmp(option_data->dhcp_type, OPT53_DISCOVER)) { + /* Discover has no Secret ID nor HMAC MD5 Hash */ + break; + } else { + if (tvb_reported_length_remaining(tvb, offset) < 31) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "length isn't >= 31"); break; + } - proto_tree_add_item(v_tree, hf_bootp_option_dhcp_authentication_information, tvb, optoff, optleft, ENC_ASCII|ENC_NA); + proto_tree_add_item(tree, hf_bootp_option_dhcp_authentication_secret_id, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(tree, hf_bootp_option_dhcp_authentication_hmac_md5_hash, tvb, offset, 16, ENC_ASCII|ENC_NA); break; } - break; default: - if (optleft == 0) + if (tvb_reported_length_remaining(tvb, offset) == 0) break; - proto_tree_add_item(v_tree, hf_bootp_option_dhcp_authentication_information, tvb, optoff, optleft, ENC_ASCII|ENC_NA); + proto_tree_add_item(tree, hf_bootp_option_dhcp_authentication_information, tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_ASCII|ENC_NA); break; } break; - case 99: /* civic location (RFC 4776) */ + default: + if (tvb_reported_length_remaining(tvb, offset) == 0) + break; - optleft = optlen; - if (optleft >= 3) - { - proto_tree_add_item(v_tree, hf_bootp_option_civic_location_what, tvb, optoff, 1, ENC_BIG_ENDIAN); - proto_tree_add_item(v_tree, hf_bootp_option_civic_location_country, tvb, optoff+1, 2, ENC_ASCII|ENC_NA); - optleft = optleft - 3; - optoff = optoff + 3; + proto_tree_add_item(tree, hf_bootp_option_dhcp_authentication_information, tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_ASCII|ENC_NA); + break; + } - while (optleft >= 2) - { - int calength = tvb_get_guint8(tvb, optoff+1); - proto_tree_add_item(v_tree, hf_bootp_option_civic_location_ca_type, tvb, optoff, 1, ENC_BIG_ENDIAN); - proto_tree_add_item(v_tree, hf_bootp_option_civic_location_ca_length, tvb, optoff+1, 1, ENC_BIG_ENDIAN); + return tvb_captured_length(tvb); +} - optoff += 2; - optleft -= 2; +static int +dissect_bootpopt_client_network_interface_id(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data _U_) +{ + int offset = 0; + guint8 id_type; + + id_type = tvb_get_guint8(tvb, offset); + offset++; + + if (id_type == 0x01) { + proto_tree_add_item(tree, hf_bootp_option_client_network_id_major_ver, + tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset++; + proto_tree_add_item(tree, hf_bootp_option_client_network_id_minor_ver, + tvb, offset, 1, ENC_LITTLE_ENDIAN); + } - if (calength == 0) - continue; + return tvb_captured_length(tvb); +} - if (optleft >= calength) - { - proto_tree_add_item(v_tree, hf_bootp_option_civic_location_ca_value, tvb, optoff, calength, ENC_ASCII|ENC_NA); +static int +dissect_bootpopt_client_identifier_uuid(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data _U_) +{ + int offset = 0, length = tvb_reported_length(tvb); + guint8 byte; - optoff = optoff + calength; - optleft = optleft - calength; - } - else - { - optleft = 0; - expert_add_info(pinfo, vti, &ei_bootp_option_civic_location_bad_cattype); - } - } - } - break; + if (length > 0) + byte = tvb_get_guint8(tvb, offset); + 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 (length == 7 && byte > 0 && byte < 48) { + proto_tree_add_item(tree, hf_bootp_hw_type, tvb, offset, 1, ENC_NA); + if (byte == ARPHRD_ETHER || byte == ARPHRD_IEEE802) + proto_tree_add_item(tree, hf_bootp_hw_ether_addr, tvb, offset+1, 6, ENC_NA); + else + proto_tree_add_string(tree, hf_bootp_client_hardware_address, tvb, offset+1, 6, + tvb_arphrdaddr_to_str(tvb, offset+1, 6, byte)); + } else if (length == 17 && byte == 0) { + /* Identifier is a UUID */ + proto_tree_add_item(tree, hf_bootp_client_identifier_uuid, tvb, offset + 1, 16, bootp_uuid_endian); + } else { + /* otherwise, it's opaque data */ + } - case 117: /* The Name Service Search Option for DHCP (RFC 2937) */ - if (optlen < 2) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "length isn't >= 2"); - } else if (optlen & 1) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "length (%u) isn't even number", optlen); - } else { - guint16 ns; - for (i = 0, ns = tvb_get_ntohs(tvb, optoff); i < optlen; i += 2, ns = tvb_get_ntohs(tvb, optoff + i)) { - switch (ns) { - case RFC2937_LOCAL_NAMING_INFORMATION: - proto_tree_add_string(v_tree, hf_bootp_option_dhcp_name_service_search_option, tvb, optoff + i, 2, "Local naming information (e.g., an /etc/hosts file on a UNIX machine) (0)"); - break; - case RFC2937_DOMAIN_NAME_SERVER_OPTION: - proto_tree_add_string(v_tree, hf_bootp_option_dhcp_name_service_search_option, tvb, optoff + i, 2, "Domain Name Server Option (6)"); - break; - case RFC2937_NETWORK_INFORMATION_SERVERS_OPTION: - proto_tree_add_string(v_tree, hf_bootp_option_dhcp_name_service_search_option, tvb, optoff + i, 2, "Network Information Servers Option (41)"); - break; - case RFC2937_NETBIOS_OVER_TCP_IP_NAME_SERVER_OPTION: - proto_tree_add_string(v_tree, hf_bootp_option_dhcp_name_service_search_option, tvb, optoff + i, 2, "NetBIOS over TCP/IP Name Server Option (44)"); - break; - case RFC2937_NETWORK_INFORMATION_SERVICE_PLUS_SERVERS_OPTION: - proto_tree_add_string(v_tree, hf_bootp_option_dhcp_name_service_search_option, tvb, optoff + i, 2, "Network Information Service+ Servers Option (65)"); - break; - default: - expert_add_info_format(pinfo, vti, &ei_bootp_option_dhcp_name_service_invalid, - "Invalid Name Service (%u). RFC 2937 defines only 0, 6, 41, 44, and 65 as possible values.", ns); - break; - } - } - } - break; + return tvb_captured_length(tvb); +} - case 119: { /* Dynamic Host Configuration Protocol (DHCP) Domain Search Option (RFC 3397) */ - /* Encoding Long Options in the Dynamic Host Configuration Protocol (DHCPv4) (RFC 3396) */ - /* Domain Names - Implementation And Specification (RFC 1035) */ - char tmpChar[BOOTP_MAX_NO_CHAR]; - rfc3396_dns_domain_search_list.index_current_block++; - if (rfc3396_dns_domain_search_list.total_number_of_block > 1) { - g_snprintf(tmpChar, BOOTP_MAX_NO_CHAR, "%u/%u", rfc3396_dns_domain_search_list.index_current_block, rfc3396_dns_domain_search_list.total_number_of_block); - proto_tree_add_string(v_tree, hf_bootp_option_dhcp_dns_domain_search_list_rfc_3396_detected, tvb, optoff, optlen, tmpChar); - if (rfc3396_dns_domain_search_list.index_current_block != rfc3396_dns_domain_search_list.total_number_of_block) { - g_snprintf(tmpChar, BOOTP_MAX_NO_CHAR, "%u/%u", rfc3396_dns_domain_search_list.total_number_of_block, rfc3396_dns_domain_search_list.total_number_of_block); - proto_tree_add_string(v_tree, hf_bootp_option_dhcp_dns_domain_search_list_refer_last_option, tvb, optoff, optlen, tmpChar); - } - } +static int +dissect_bootpopt_civic_location(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + int offset = 0; - if (rfc3396_dns_domain_search_list.tvb_composite == NULL && optlen) { - /* We use composite tvb for managing RFC 3396 */ - rfc3396_dns_domain_search_list.tvb_composite = tvb_new_composite(); - } + if (tvb_reported_length(tvb) >= 3) + { + proto_tree_add_item(tree, hf_bootp_option_civic_location_what, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tree, hf_bootp_option_civic_location_country, tvb, offset, 2, ENC_ASCII|ENC_NA); + offset += 2; - /* Concatenate the block before being interpreted for managing RFC 3396 */ - if (optlen) - tvb_composite_append(rfc3396_dns_domain_search_list.tvb_composite, tvb_new_subset_length(tvb, optoff, optlen)); + while (tvb_reported_length_remaining(tvb, offset) >= 2) + { + guint32 calength; + proto_tree_add_item(tree, hf_bootp_option_civic_location_ca_type, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item_ret_uint(tree, hf_bootp_option_civic_location_ca_length, tvb, offset, 1, ENC_BIG_ENDIAN, &calength); + offset++; - if (rfc3396_dns_domain_search_list.index_current_block == rfc3396_dns_domain_search_list.total_number_of_block - && rfc3396_dns_domain_search_list.tvb_composite) { - /* Here, we are into the last (or unique) option 119. */ - /* We will display the information about fqdn */ - unsigned int consumedx = 0; - unsigned int offset = 0; - tvb_composite_finalize(rfc3396_dns_domain_search_list.tvb_composite); + if (calength == 0) + continue; - while (offset < tvb_reported_length(rfc3396_dns_domain_search_list.tvb_composite)) { - /* use the get_dns_name method that manages all techniques of RFC 1035 (compression pointer and so on) */ - consumedx = get_dns_name(rfc3396_dns_domain_search_list.tvb_composite, offset, - tvb_reported_length(rfc3396_dns_domain_search_list.tvb_composite), 0, &dns_name, &dns_name_len); - if (rfc3396_dns_domain_search_list.total_number_of_block == 1) { - /* RFC 3396 is not used, so we can easily link the fqdn with v_tree. */ - proto_tree_add_string(v_tree, hf_bootp_option_dhcp_dns_domain_search_list_fqdn, tvb, optoff + offset, consumedx, dns_name); - } else { - /* RFC 3396 is used, so the option is split into several option 119. We don't link fqdn with v_tree. */ - proto_tree_add_string(v_tree, hf_bootp_option_dhcp_dns_domain_search_list_fqdn, tvb, 0, 0, dns_name); - } - offset += consumedx; + if (tvb_reported_length_remaining(tvb, offset) >= (int)calength) + { + proto_tree_add_item(tree, hf_bootp_option_civic_location_ca_value, tvb, offset, calength, ENC_ASCII|ENC_NA); + offset += calength; } - rfc3396_dns_domain_search_list.tvb_composite = NULL; - } - break; - } - case 120: { /* SIP Servers (RFC 3361) */ - /* Encoding Long Options in the Dynamic Host Configuration Protocol (DHCPv4) (RFC 3396) */ - /* Domain Names - Implementation And Specification (RFC 1035) */ - char tmpChar[BOOTP_MAX_NO_CHAR]; - rfc3396_sip_server.index_current_block++; - if (rfc3396_sip_server.total_number_of_block > 1) { - g_snprintf(tmpChar, BOOTP_MAX_NO_CHAR, "%u/%u", rfc3396_sip_server.index_current_block, rfc3396_sip_server.total_number_of_block); - proto_tree_add_string(v_tree, hf_bootp_option_sip_server_rfc_3396_detected, tvb, optoff, optlen, tmpChar); - if (rfc3396_sip_server.index_current_block != rfc3396_sip_server.total_number_of_block) { - g_snprintf(tmpChar, BOOTP_MAX_NO_CHAR, "%u/%u", rfc3396_sip_server.total_number_of_block, rfc3396_sip_server.total_number_of_block); - proto_tree_add_string(v_tree, hf_bootp_option_sip_server_refer_last_option, tvb, optoff, optlen, tmpChar); + else + { + expert_add_info(pinfo, tree, &ei_bootp_option_civic_location_bad_cattype); + break; } } + } + + return tvb_captured_length(tvb); +} + +static int +dissect_bootpopt_name_server_search(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + int offset = 0, length = tvb_reported_length(tvb); + guint16 ns; + + if (length < 2) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "length isn't >= 2"); + return 1; + } else if (length & 1) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "length (%u) isn't even number", length); + return 1; + } - if (rfc3396_sip_server.tvb_composite == NULL && optlen) { - /* We use composite tvb for managing RFC 3396 */ - rfc3396_sip_server.tvb_composite = tvb_new_composite(); + while (tvb_reported_length_remaining(tvb, offset) > 0) { + ns = tvb_get_ntohs(tvb, offset); + /* XXX - Make this a value_string */ + switch (ns) { + case RFC2937_LOCAL_NAMING_INFORMATION: + proto_tree_add_string(tree, hf_bootp_option_dhcp_name_service_search_option, tvb, offset, 2, "Local naming information (e.g., an /etc/hosts file on a UNIX machine) (0)"); + break; + case RFC2937_DOMAIN_NAME_SERVER_OPTION: + proto_tree_add_string(tree, hf_bootp_option_dhcp_name_service_search_option, tvb, offset, 2, "Domain Name Server Option (6)"); + break; + case RFC2937_NETWORK_INFORMATION_SERVERS_OPTION: + proto_tree_add_string(tree, hf_bootp_option_dhcp_name_service_search_option, tvb, offset, 2, "Network Information Servers Option (41)"); + break; + case RFC2937_NETBIOS_OVER_TCP_IP_NAME_SERVER_OPTION: + proto_tree_add_string(tree, hf_bootp_option_dhcp_name_service_search_option, tvb, offset, 2, "NetBIOS over TCP/IP Name Server Option (44)"); + break; + case RFC2937_NETWORK_INFORMATION_SERVICE_PLUS_SERVERS_OPTION: + proto_tree_add_string(tree, hf_bootp_option_dhcp_name_service_search_option, tvb, offset, 2, "Network Information Service+ Servers Option (65)"); + break; + default: + expert_add_info_format(pinfo, tree, &ei_bootp_option_dhcp_name_service_invalid, + "Invalid Name Service (%u). RFC 2937 defines only 0, 6, 41, 44, and 65 as possible values.", ns); + break; } + offset += 2; + } - /* Concatenate the block before being interpreted for managing RFC 3396 */ - if (optlen) - tvb_composite_append(rfc3396_sip_server.tvb_composite, tvb_new_subset_length(tvb, optoff, optlen)); + return tvb_captured_length(tvb); +} - if (rfc3396_sip_server.index_current_block == rfc3396_sip_server.total_number_of_block - && rfc3396_sip_server.tvb_composite) { - /* Here, we are into the last (or unique) option 120. */ - /* We will display the information about SIP server */ - guint8 enc; - unsigned int offset = 1; /* ignore enc */ - tvb_composite_finalize(rfc3396_sip_server.tvb_composite); +static int +dissect_bootpopt_dhcp_domain_search(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data _U_) +{ + int length = tvb_reported_length(tvb); + const guchar *dns_name; + guint dns_name_len; - enc = tvb_get_guint8(rfc3396_sip_server.tvb_composite, 0); - if (rfc3396_sip_server.total_number_of_block == 1) { + /* Encoding Long Options in the Dynamic Host Configuration Protocol (DHCPv4) (RFC 3396) */ + /* Domain Names - Implementation And Specification (RFC 1035) */ + rfc3396_dns_domain_search_list.index_current_block++; + if (rfc3396_dns_domain_search_list.total_number_of_block > 1) { + proto_tree_add_string(tree, hf_bootp_option_dhcp_dns_domain_search_list_rfc_3396_detected, tvb, 0, length, + wmem_strdup_printf(wmem_packet_scope(), "%u/%u", rfc3396_dns_domain_search_list.index_current_block, rfc3396_dns_domain_search_list.total_number_of_block)); + if (rfc3396_dns_domain_search_list.index_current_block != rfc3396_dns_domain_search_list.total_number_of_block) { + proto_tree_add_string(tree, hf_bootp_option_dhcp_dns_domain_search_list_refer_last_option, tvb, 0, length, + wmem_strdup_printf(wmem_packet_scope(), "%u/%u", rfc3396_dns_domain_search_list.total_number_of_block, rfc3396_dns_domain_search_list.total_number_of_block)); + } + } + + if (rfc3396_dns_domain_search_list.tvb_composite == NULL && length) { + /* We use composite tvb for managing RFC 3396 */ + rfc3396_dns_domain_search_list.tvb_composite = tvb_new_composite(); + } + + /* Concatenate the block before being interpreted for managing RFC 3396 */ + if (length) + tvb_composite_append(rfc3396_dns_domain_search_list.tvb_composite, tvb_new_subset_length(tvb, 0, length)); + + if (rfc3396_dns_domain_search_list.index_current_block == rfc3396_dns_domain_search_list.total_number_of_block + && rfc3396_dns_domain_search_list.tvb_composite) { + /* Here, we are into the last (or unique) option 119. */ + /* We will display the information about fqdn */ + unsigned int consumedx = 0; + unsigned int composite_offset = 0; + tvb_composite_finalize(rfc3396_dns_domain_search_list.tvb_composite); + + while (composite_offset < tvb_reported_length(rfc3396_dns_domain_search_list.tvb_composite)) { + /* use the get_dns_name method that manages all techniques of RFC 1035 (compression pointer and so on) */ + consumedx = get_dns_name(rfc3396_dns_domain_search_list.tvb_composite, composite_offset, + tvb_reported_length(rfc3396_dns_domain_search_list.tvb_composite), 0, &dns_name, &dns_name_len); + if (rfc3396_dns_domain_search_list.total_number_of_block == 1) { /* RFC 3396 is not used, so we can easily link the fqdn with v_tree. */ - proto_tree_add_uint(v_tree, hf_bootp_option_sip_server_enc, tvb, optoff, 1, enc); + proto_tree_add_string(tree, hf_bootp_option_dhcp_dns_domain_search_list_fqdn, tvb, composite_offset, consumedx, dns_name); } else { - /* RFC 3396 is used, so the option is split into several option 120. We don't link fqdn with v_tree. */ - proto_tree_add_uint(v_tree, hf_bootp_option_sip_server_enc, tvb, 0, 0, enc); + /* RFC 3396 is used, so the option is split into several option 119. We don't link fqdn with v_tree. */ + proto_tree_add_string(tree, hf_bootp_option_dhcp_dns_domain_search_list_fqdn, tvb, 0, 0, dns_name); } + composite_offset += consumedx; + } + rfc3396_dns_domain_search_list.tvb_composite = NULL; + } - switch (enc) { - case RFC_3361_ENC_FQDN: { - unsigned int consumedx = 0; - if (tvb_reported_length(rfc3396_sip_server.tvb_composite) < 3) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "length isn't >= 3 (len = %u)", tvb_reported_length(rfc3396_sip_server.tvb_composite)); - break; - } + return tvb_captured_length(tvb); +} - while (offset < tvb_reported_length(rfc3396_sip_server.tvb_composite)) { - /* use the get_dns_name method that manages all techniques of RFC 1035 (compression pointer and so on) */ - consumedx = get_dns_name(rfc3396_sip_server.tvb_composite, offset, tvb_reported_length(rfc3396_sip_server.tvb_composite), - 1 /* ignore enc */, &dns_name, &dns_name_len); - - if (rfc3396_sip_server.total_number_of_block == 1) { - /* RFC 3396 is not used, so we can easily link the fqdn with v_tree. */ - proto_tree_add_string(v_tree, hf_bootp_option_sip_server_name, tvb, optoff + offset, consumedx, dns_name); - } else { - /* RFC 3396 is used, so the option is split into several option 120. We don't link fqdn with v_tree. */ - proto_tree_add_string(v_tree, hf_bootp_option_sip_server_name, tvb, 0, 0, format_text(wmem_packet_scope(), dns_name, dns_name_len)); - } - offset += consumedx; - } - rfc3396_sip_server.tvb_composite = NULL; +static int +dissect_bootpopt_sip_servers(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + int length = tvb_reported_length(tvb); + const guchar *dns_name; + guint dns_name_len; + + /* Encoding Long Options in the Dynamic Host Configuration Protocol (DHCPv4) (RFC 3396) */ + /* Domain Names - Implementation And Specification (RFC 1035) */ + rfc3396_sip_server.index_current_block++; + if (rfc3396_sip_server.total_number_of_block > 1) { + proto_tree_add_string(tree, hf_bootp_option_sip_server_rfc_3396_detected, tvb, 0, length, + wmem_strdup_printf(wmem_packet_scope(), "%u/%u", rfc3396_sip_server.index_current_block, rfc3396_sip_server.total_number_of_block)); + if (rfc3396_sip_server.index_current_block != rfc3396_sip_server.total_number_of_block) { + proto_tree_add_string(tree, hf_bootp_option_sip_server_refer_last_option, tvb, 0, length, + wmem_strdup_printf(wmem_packet_scope(), "%u/%u", rfc3396_sip_server.total_number_of_block, rfc3396_sip_server.total_number_of_block)); + } + } + + if (rfc3396_sip_server.tvb_composite == NULL && length) { + /* We use composite tvb for managing RFC 3396 */ + rfc3396_sip_server.tvb_composite = tvb_new_composite(); + } + + /* Concatenate the block before being interpreted for managing RFC 3396 */ + if (length) + tvb_composite_append(rfc3396_sip_server.tvb_composite, tvb_new_subset_length(tvb, 0, length)); + + if (rfc3396_sip_server.index_current_block == rfc3396_sip_server.total_number_of_block + && rfc3396_sip_server.tvb_composite) { + /* Here, we are into the last (or unique) option 120. */ + /* We will display the information about SIP server */ + guint8 enc; + unsigned int composite_offset = 1; /* ignore enc */ + tvb_composite_finalize(rfc3396_sip_server.tvb_composite); + + enc = tvb_get_guint8(rfc3396_sip_server.tvb_composite, 0); + if (rfc3396_sip_server.total_number_of_block == 1) { + /* RFC 3396 is not used, so we can easily link the fqdn with tree. */ + proto_tree_add_uint(tree, hf_bootp_option_sip_server_enc, tvb, 0, 1, enc); + } else { + /* RFC 3396 is used, so the option is split into several option 120. We don't link fqdn with tree. */ + proto_tree_add_uint(tree, hf_bootp_option_sip_server_enc, tvb, 0, 0, enc); + } + + switch (enc) { + case RFC_3361_ENC_FQDN: { + unsigned int consumedx = 0; + if (tvb_reported_length(rfc3396_sip_server.tvb_composite) < 3) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "length isn't >= 3 (len = %u)", tvb_reported_length(rfc3396_sip_server.tvb_composite)); break; } - case RFC_3361_ENC_IPADDR: - if (tvb_reported_length(rfc3396_sip_server.tvb_composite) < 5) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "length isn't >= 5 (len = %u)", tvb_reported_length(rfc3396_sip_server.tvb_composite)); - break; - } - /* x % 2^n == x & (2^n - 1) note : (assuming x is a positive integer) */ - if ((tvb_reported_length(rfc3396_sip_server.tvb_composite) - 1) & 3) { - if (rfc3396_sip_server.total_number_of_block == 1) - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "length isn't a multiple of 4 plus 1 (len = %u).", tvb_reported_length(rfc3396_sip_server.tvb_composite)); - else - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, - "length isn't a multiple of 4 plus 1 (len = %u). For your information with RFC 3396, the length is the length sum of all options 120 into this BOOTP packet.", - tvb_reported_length(rfc3396_sip_server.tvb_composite)); - break; - } - while (offset < tvb_reported_length(rfc3396_sip_server.tvb_composite)) { - if (rfc3396_sip_server.total_number_of_block == 1) { - /* RFC 3396 is not used, so we can easily link the fqdn with v_tree. */ - proto_tree_add_item(v_tree, hf_bootp_option_sip_server_address, rfc3396_sip_server.tvb_composite, offset, 4, ENC_BIG_ENDIAN); - } else { - /* RFC 3396 is used, so the option is split into several option 120. We don't link fqdn with v_tree. */ - /* Since we don't use the "numbered argument" as described by README.developer, we have to repeat the arguments :( */ - g_snprintf(tmpChar, BOOTP_MAX_NO_CHAR, "%u.%u.%u.%u (%u.%u.%u.%u)", - tvb_get_guint8(rfc3396_sip_server.tvb_composite, offset), - tvb_get_guint8(rfc3396_sip_server.tvb_composite, offset + 1), - tvb_get_guint8(rfc3396_sip_server.tvb_composite, offset + 2), - tvb_get_guint8(rfc3396_sip_server.tvb_composite, offset + 3), - tvb_get_guint8(rfc3396_sip_server.tvb_composite, offset), - tvb_get_guint8(rfc3396_sip_server.tvb_composite, offset + 1), - tvb_get_guint8(rfc3396_sip_server.tvb_composite, offset + 2), - tvb_get_guint8(rfc3396_sip_server.tvb_composite, offset + 3) - ); - proto_tree_add_string(v_tree, hf_bootp_option_sip_server_address_stringz, tvb, 0, 0, tmpChar); - } - offset += 4; + + while (composite_offset < tvb_reported_length(rfc3396_sip_server.tvb_composite)) { + /* use the get_dns_name method that manages all techniques of RFC 1035 (compression pointer and so on) */ + consumedx = get_dns_name(rfc3396_sip_server.tvb_composite, composite_offset, tvb_reported_length(rfc3396_sip_server.tvb_composite), + 1 /* ignore enc */, &dns_name, &dns_name_len); + + if (rfc3396_sip_server.total_number_of_block == 1) { + /* RFC 3396 is not used, so we can easily link the fqdn with v_tree. */ + proto_tree_add_string(tree, hf_bootp_option_sip_server_name, tvb, composite_offset, consumedx, dns_name); + } else { + /* RFC 3396 is used, so the option is split into several option 120. We don't link fqdn with v_tree. */ + proto_tree_add_string(tree, hf_bootp_option_sip_server_name, tvb, 0, 0, format_text(wmem_packet_scope(), dns_name, dns_name_len)); } - break; - default: - expert_add_info_format(pinfo, vti, &ei_bootp_option_sip_server_address_encoding, "RFC 3361 defines only 0 and 1 for Encoding byte (Encoding = %u).", enc); - break; + composite_offset += consumedx; } - } - break; - } - case 121: /* Classless Static Route */ - case 249: { /* Classless Static Route (Microsoft) */ - int mask_width, significant_octets; - optend = optoff + optlen; - /* minimum length is 5 bytes */ - if (optlen < 5) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "length isn't >= 5"); + rfc3396_sip_server.tvb_composite = NULL; break; } - while (optoff < optend) { - mask_width = tvb_get_guint8(tvb, optoff); - /* mask_width <= 32 */ - if (mask_width > 32) { - expert_add_info_format(pinfo, vti, &ei_bootp_option_classless_static_route, "Mask width (%d) > 32", mask_width); + case RFC_3361_ENC_IPADDR: + if (tvb_reported_length(rfc3396_sip_server.tvb_composite) < 5) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "length isn't >= 5 (len = %u)", tvb_reported_length(rfc3396_sip_server.tvb_composite)); break; } - significant_octets = (mask_width + 7) / 8; - vti = proto_tree_add_bytes_format(v_tree, hf_bootp_option_classless_static_route, tvb, optoff, - 1 + significant_octets + 4, NULL, - " "); - optoff++; - /* significant octets + router(4) */ - if (optend < optoff + significant_octets + 4) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "Remaining length (%d) < %d bytes", optend - optoff, significant_octets + 4); + /* x % 2^n == x & (2^n - 1) note : (assuming x is a positive integer) */ + if ((tvb_reported_length(rfc3396_sip_server.tvb_composite) - 1) & 3) { + if (rfc3396_sip_server.total_number_of_block == 1) + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "length isn't a multiple of 4 plus 1 (len = %u).", tvb_reported_length(rfc3396_sip_server.tvb_composite)); + else + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, + "length isn't a multiple of 4 plus 1 (len = %u). For your information with RFC 3396, the length is the length sum of all options 120 into this BOOTP packet.", + tvb_reported_length(rfc3396_sip_server.tvb_composite)); break; } - if(mask_width == 0) - proto_item_append_text(vti, "default"); - else { - for(i = 0 ; i < significant_octets ; i++) { - if (i > 0) - proto_item_append_text(vti, "."); - byte = tvb_get_guint8(tvb, optoff++); - proto_item_append_text(vti, "%d", byte); + while (composite_offset < tvb_reported_length(rfc3396_sip_server.tvb_composite)) { + if (rfc3396_sip_server.total_number_of_block == 1) { + /* RFC 3396 is not used, so we can easily link the fqdn with v_tree. */ + proto_tree_add_item(tree, hf_bootp_option_sip_server_address, rfc3396_sip_server.tvb_composite, composite_offset, 4, ENC_BIG_ENDIAN); + } else { + /* RFC 3396 is used, so the option is split into several option 120. We don't link fqdn with v_tree. */ + /* Since we don't use the "numbered argument" as described by README.developer, we have to repeat the arguments :( */ + proto_tree_add_string(tree, hf_bootp_option_sip_server_address_stringz, tvb, 0, 0, + wmem_strdup_printf(wmem_packet_scope(), "%u.%u.%u.%u (%u.%u.%u.%u)", + tvb_get_guint8(rfc3396_sip_server.tvb_composite, composite_offset), + tvb_get_guint8(rfc3396_sip_server.tvb_composite, composite_offset + 1), + tvb_get_guint8(rfc3396_sip_server.tvb_composite, composite_offset + 2), + tvb_get_guint8(rfc3396_sip_server.tvb_composite, composite_offset + 3), + tvb_get_guint8(rfc3396_sip_server.tvb_composite, composite_offset), + tvb_get_guint8(rfc3396_sip_server.tvb_composite, composite_offset + 1), + tvb_get_guint8(rfc3396_sip_server.tvb_composite, composite_offset + 2), + tvb_get_guint8(rfc3396_sip_server.tvb_composite, composite_offset + 3))); } - for(i = significant_octets ; i < 4 ; i++) - proto_item_append_text(vti, ".0"); - proto_item_append_text(vti, "/%d", mask_width); + composite_offset += 4; } - proto_item_append_text(vti, "-%s", tvb_ip_to_str(tvb, optoff)); - optoff += 4; + break; + default: + expert_add_info_format(pinfo, tree, &ei_bootp_option_sip_server_address_encoding, "RFC 3361 defines only 0 and 1 for Encoding byte (Encoding = %u).", enc); + break; } - break; } - case 123: /* coordinate based location RFC 3825 or CableLabs DSS_ID */ - if (optlen == 16) { - int c; - unsigned char lci[16]; - struct rfc3825_location_fixpoint_t location_fp; - struct rfc3825_location_decimal_t location; - - for (c=0; c < 16;c++) - lci[c] = (unsigned char) tvb_get_guint8(tvb, optoff + c); - - /* convert lci encoding into fixpoint location */ - rfc3825_lci_to_fixpoint(lci, &location_fp); - - /* convert location from decimal to fixpoint */ - i = rfc3825_fixpoint_to_decimal(&location_fp, &location); - - if (i != RFC3825_NOERROR) { - ti = proto_tree_add_uint(v_tree, hf_bootp_option_rfc3825_error, tvb, optoff, 1, i); - proto_item_set_len(ti, optlen); - } else { - proto_tree_add_double_format_value(v_tree, hf_bootp_option_rfc3825_latitude, tvb, optoff, 5, location.latitude, "%15.10f", location.latitude); - proto_tree_add_double_format_value(v_tree, hf_bootp_option_rfc3825_longitude, tvb, optoff+5, 5, location.longitude, "%15.10f", location.longitude); - proto_tree_add_double_format_value(v_tree, hf_bootp_option_rfc3825_latitude_res, tvb, optoff, 1, location.latitude_res, "%15.10f", location.latitude_res); - proto_tree_add_double_format_value(v_tree, hf_bootp_option_rfc3825_longitude_res, tvb, optoff+5, 1, location.longitude_res, "%15.10f", location.longitude_res); - proto_tree_add_double_format_value(v_tree, hf_bootp_option_rfc3825_altitude, tvb, optoff+12, 4, location.altitude, "%15.10f", location.altitude); - proto_tree_add_double_format_value(v_tree, hf_bootp_option_rfc3825_altitide_res, tvb, optoff+10, 2, location.altitude_res, "%15.10f", location.altitude_res); - proto_tree_add_uint(v_tree, hf_bootp_option_rfc3825_altitude_type, tvb, optoff+10, 1, location.altitude_type); - proto_tree_add_uint(v_tree, hf_bootp_option_rfc3825_map_datum, tvb, optoff+15, 1, location.datum_type); - } - } else if (optlen < 69) { /* CableLabs DSS_ID */ - - proto_tree_add_item(v_tree, hf_bootp_option_cl_dss_id_option, tvb, optoff, 1, ENC_BIG_ENDIAN); - proto_tree_add_item(v_tree, hf_bootp_option_cl_dss_id_len, tvb, optoff+1, 1, ENC_BIG_ENDIAN); - s_len = tvb_get_guint8(tvb, optoff+1); - proto_tree_add_item(v_tree, hf_bootp_option_cl_dss_id, tvb, optoff+2, s_len, ENC_ASCII|ENC_NA); - - if (optlen > s_len+2) { /* Second DSS_ID*/ + return tvb_captured_length(tvb); +} - proto_tree_add_item(v_tree, hf_bootp_option_cl_dss_id_option, tvb, optoff+2+s_len, 1, ENC_BIG_ENDIAN); - proto_tree_add_item(v_tree, hf_bootp_option_cl_dss_id_len, tvb, optoff+1+2+s_len, 1, ENC_BIG_ENDIAN); - s_len = tvb_get_guint8(tvb, optoff+1+2+s_len); - proto_tree_add_item(v_tree, hf_bootp_option_cl_dss_id, tvb, optoff+2+2+s_len, s_len, ENC_ASCII|ENC_NA); +static int +dissect_bootpopt_classless_static_route(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + int offset = 0; + int i, mask_width, significant_octets; + proto_item* route_item; + + /* minimum length is 5 bytes */ + if (tvb_reported_length(tvb) < 5) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "length isn't >= 5"); + return 1; + } + while (tvb_reported_length_remaining(tvb, offset) > 0) { + mask_width = tvb_get_guint8(tvb, offset); + /* mask_width <= 32 */ + if (mask_width > 32) { + expert_add_info_format(pinfo, tree, &ei_bootp_option_classless_static_route, "Mask width (%d) > 32", mask_width); + break; + } + significant_octets = (mask_width + 7) / 8; + route_item = proto_tree_add_bytes_format(tree, hf_bootp_option_classless_static_route, tvb, offset, + 1 + significant_octets + 4, NULL, " "); + offset++; + /* significant octets + router(4) */ + if (tvb_reported_length_remaining(tvb, offset + significant_octets + 4) < 0) { + expert_add_info_format(pinfo, route_item, &ei_bootp_bad_length, "Remaining length (%d) < %d bytes", tvb_reported_length_remaining(tvb, offset), significant_octets + 4); + break; + } + if(mask_width == 0) + proto_item_append_text(route_item, "default"); + else { + for(i = 0 ; i < significant_octets ; i++) { + if (i > 0) + proto_item_append_text(route_item, "."); + proto_item_append_text(route_item, "%d", tvb_get_guint8(tvb, offset++)); } - } else { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "Invalid length of DHCP option!"); + for(i = significant_octets ; i < 4 ; i++) + proto_item_append_text(route_item, ".0"); + proto_item_append_text(route_item, "/%d", mask_width); } - break; + proto_item_append_text(route_item, "-%s", tvb_ip_to_str(tvb, offset)); + offset += 4; + } - case 124: { /* V-I Vendor Class (RFC 3925) */ - int data_len; + return tvb_captured_length(tvb); +} - if (optlen == 1) { - /* CableLab specific */ - proto_tree_add_item(v_tree, hf_bootp_option_vi_class_cl_address_mode, tvb, optoff, optlen, ENC_BIG_ENDIAN); - break; - } +static int +dissect_bootpopt_coordinate_based_location(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + int offset = 0, length = tvb_reported_length(tvb); + proto_item* ti; - optleft = optlen; + if (length == 16) { + int ret; + unsigned char lci[16]; + struct rfc3825_location_fixpoint_t location_fp; + struct rfc3825_location_decimal_t location; - while (optleft > 0) { + tvb_memcpy(tvb, lci, offset, 16); - if (optleft < 5) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "length < 5"); - break; - } + /* convert lci encoding into fixpoint location */ + rfc3825_lci_to_fixpoint(lci, &location_fp); - proto_tree_add_item(v_tree, hf_bootp_option_vi_class_enterprise, tvb, optoff, 4, ENC_BIG_ENDIAN); - proto_tree_add_item(v_tree, hf_bootp_option_vi_class_data_length, tvb, optoff, 1, ENC_BIG_ENDIAN); - data_len = tvb_get_guint8(tvb, optoff + 4); + /* convert location from decimal to fixpoint */ + ret = rfc3825_fixpoint_to_decimal(&location_fp, &location); - optoff += 5; - optleft -= 5; + if (ret != RFC3825_NOERROR) { + ti = proto_tree_add_uint(tree, hf_bootp_option_rfc3825_error, tvb, offset, 1, ret); + proto_item_set_len(ti, length); + } else { + proto_tree_add_double_format_value(tree, hf_bootp_option_rfc3825_latitude, tvb, offset, 5, location.latitude, "%15.10f", location.latitude); + proto_tree_add_double_format_value(tree, hf_bootp_option_rfc3825_longitude, tvb, offset+5, 5, location.longitude, "%15.10f", location.longitude); + proto_tree_add_double_format_value(tree, hf_bootp_option_rfc3825_latitude_res, tvb, offset, 1, location.latitude_res, "%15.10f", location.latitude_res); + proto_tree_add_double_format_value(tree, hf_bootp_option_rfc3825_longitude_res, tvb, offset+5, 1, location.longitude_res, "%15.10f", location.longitude_res); + proto_tree_add_double_format_value(tree, hf_bootp_option_rfc3825_altitude, tvb, offset+12, 4, location.altitude, "%15.10f", location.altitude); + proto_tree_add_double_format_value(tree, hf_bootp_option_rfc3825_altitide_res, tvb, offset+10, 2, location.altitude_res, "%15.10f", location.altitude_res); + proto_tree_add_uint(tree, hf_bootp_option_rfc3825_altitude_type, tvb, offset+10, 1, location.altitude_type); + proto_tree_add_uint(tree, hf_bootp_option_rfc3825_map_datum, tvb, offset+15, 1, location.datum_type); + } + } else if (length < 69) { /* CableLabs DSS_ID */ + int s_len; - proto_tree_add_item(v_tree, hf_bootp_option_vi_class_data, tvb, optoff, data_len, ENC_ASCII|ENC_NA); + proto_tree_add_item(tree, hf_bootp_option_cl_dss_id_option, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_bootp_option_cl_dss_id_len, tvb, offset+1, 1, ENC_BIG_ENDIAN); + s_len = tvb_get_guint8(tvb, offset+1); + proto_tree_add_item(tree, hf_bootp_option_cl_dss_id, tvb, offset+2, s_len, ENC_ASCII|ENC_NA); - /* look for next enterprise number */ - optoff += data_len; - optleft -= data_len; + if (length > s_len+2) { /* Second DSS_ID*/ + + proto_tree_add_item(tree, hf_bootp_option_cl_dss_id_option, tvb, offset+2+s_len, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_bootp_option_cl_dss_id_len, tvb, offset+1+2+s_len, 1, ENC_BIG_ENDIAN); + s_len = tvb_get_guint8(tvb, offset+1+2+s_len); + proto_tree_add_item(tree, hf_bootp_option_cl_dss_id, tvb, offset+2+2+s_len, s_len, ENC_ASCII|ENC_NA); } - break; + } else { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "Invalid length of DHCP option!"); } - case 125: { /* V-I Vendor-specific Information (RFC 3925) */ - int enterprise = 0; - int s_end = 0; - int option_data_len = 0; - proto_tree *e_tree = 0; - - optend = optoff + optlen; - optleft = optlen; + return tvb_captured_length(tvb); +} - while (optleft > 0) { +static int +dissect_bootpopt_vi_vendor_class(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + int offset = 0; + int data_len; - if (optleft < 5) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "length < 5"); - break; - } + if (tvb_reported_length(tvb) == 1) { + /* CableLab specific */ + proto_tree_add_item(tree, hf_bootp_option_vi_class_cl_address_mode, tvb, 0, 1, ENC_BIG_ENDIAN); + return 1; + } - enterprise = tvb_get_ntohl(tvb, optoff); - vti = proto_tree_add_item(v_tree, hf_bootp_option125_enterprise, tvb, optoff, 4, ENC_BIG_ENDIAN); - e_tree = proto_item_add_subtree(vti, ett_bootp_option); + while (tvb_reported_length_remaining(tvb, offset) >= 5) { - optoff += 4; - optleft -= 4; + proto_tree_add_item(tree, hf_bootp_option_vi_class_enterprise, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(tree, hf_bootp_option_vi_class_data_length, tvb, offset, 1, ENC_BIG_ENDIAN); + data_len = tvb_get_guint8(tvb, offset); + offset += 1; - proto_tree_add_item_ret_uint(e_tree, hf_bootp_option125_length, tvb, optoff, 1, ENC_NA, &option_data_len); + proto_tree_add_item(tree, hf_bootp_option_vi_class_data, tvb, offset, data_len, ENC_ASCII|ENC_NA); - optoff += 1; - optleft -= 1; + /* look for next enterprise number */ + offset += data_len; + } - s_end = optoff + option_data_len; - if ( s_end > optend ) { - expert_add_info_format(pinfo, vti, &ei_bootp_option125_enterprise_malformed, "no room left in option for enterprise %u data", enterprise); - break; - } + if (tvb_reported_length_remaining(tvb, offset) > 0) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "length < 5"); + } - while (optoff < s_end) { - switch (enterprise) { + return tvb_captured_length(tvb); +} - case 3561: /* ADSL Forum */ - optoff = dissect_vendor_tr111_suboption(pinfo, vti, e_tree, tvb, optoff, s_end); - break; +static int +dissect_bootpopt_dhcp_captive_portal(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data _U_) +{ + proto_item *ti_cp; + ti_cp = proto_tree_add_item(tree, hf_bootp_option_captive_portal, tvb, 0, tvb_reported_length(tvb), ENC_ASCII|ENC_NA); + PROTO_ITEM_SET_URL(ti_cp); - case 4491: /* CableLab */ - optoff = dissect_vendor_cl_suboption(pinfo, vti, e_tree, tvb, optoff, s_end); - break; + return tvb_captured_length(tvb); +} - default: - optoff = dissect_vendor_generic_suboption(pinfo, vti, e_tree, tvb, optoff, s_end); - break; - } - } - optleft -= option_data_len; - } - break; - } +static int +dissect_bootpopt_6RD_option(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data _U_) +{ + int offset = 0; - case 160: { /* DHCP Captive-Portal */ - proto_item *ti_cp; - ti_cp = proto_tree_add_item(v_tree, hf_bootp_option_captive_portal, tvb, optoff, optlen, ENC_ASCII|ENC_NA); - PROTO_ITEM_SET_URL(ti_cp); - break; + if (tvb_reported_length(tvb) < 22) { + expert_add_info(pinfo, tree, &ei_bootp_option_6RD_malformed); + return 1; } - - case 212: { /* 6RD option (RFC 5969) */ - if (optlen < 22) { - expert_add_info(pinfo, vti, &ei_bootp_option_6RD_malformed); - break; + proto_tree_add_item(tree, hf_bootp_option_6RD_ipv4_mask_len, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_bootp_option_6RD_prefix_len, tvb, offset+1, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_bootp_option_6RD_prefix, tvb, offset+2, 16, ENC_NA); + proto_tree_add_item(tree, hf_bootp_option_6RD_border_relay_ip, tvb, offset+18, 4, ENC_BIG_ENDIAN); + + /* More Border Relay IPv4 addresses included */ + if (tvb_reported_length(tvb) > 22) { + offset += 22; + while (tvb_reported_length_remaining(tvb, offset) >= 4) { + proto_tree_add_item(tree, hf_bootp_option_6RD_border_relay_ip, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; } - - proto_tree_add_item(v_tree, hf_bootp_option_6RD_ipv4_mask_len, tvb, optoff, 1, ENC_BIG_ENDIAN); - proto_tree_add_item(v_tree, hf_bootp_option_6RD_prefix_len, tvb, optoff+1, 1, ENC_BIG_ENDIAN); - proto_tree_add_item(v_tree, hf_bootp_option_6RD_prefix, tvb, optoff+2, 16, ENC_NA); - proto_tree_add_item(v_tree, hf_bootp_option_6RD_border_relay_ip, tvb, optoff+18, 4, ENC_BIG_ENDIAN); - - /* More Border Relay IPv4 addresses included */ - if (optlen > 22) { - optoff += 22; - for (i = optoff, optleft = optlen - 22; optleft > 0; i += 4, optleft -= 4) { - if (optleft < 4) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "Option length isn't a multiple of 4"); - break; - } - - proto_tree_add_item(v_tree, hf_bootp_option_6RD_border_relay_ip, tvb, i, 4, ENC_BIG_ENDIAN); - } + if (tvb_reported_length_remaining(tvb, offset) > 0) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "Option length isn't a multiple of 4"); } - break; } - case 242: { /* Avaya IP Telephone */ - proto_tree *o242avaya_v_tree; - proto_item *avaya_ti; - const guint8 *avaya_option = NULL; - gchar *field = NULL; - wmem_strbuf_t *avaya_param_buf = NULL; + return tvb_captured_length(tvb); +} - /* minimum length is 5 bytes */ - if (optlen < 5) { - expert_add_info_format(pinfo, vti, &ei_bootp_bad_length, "Avaya IP Telephone option length isn't >= 5"); - optoff += optlen; - break; - } - avaya_ti = proto_tree_add_item_ret_string(v_tree, hf_bootp_option242_avaya, tvb, optoff, optlen, ENC_ASCII|ENC_NA, wmem_packet_scope(), &avaya_option); - o242avaya_v_tree = proto_item_add_subtree(avaya_ti, ett_bootp_option242_suboption); - avaya_param_buf = wmem_strbuf_new(wmem_packet_scope(), ""); - for ( field = strtok((char*)avaya_option, ","); field; field = strtok(NULL, ",") ) { - if (!strchr(field, '=')) { - if (wmem_strbuf_get_len(avaya_param_buf) == 0) { - expert_add_info_format(pinfo, vti, &hf_bootp_subopt_unknown_type, "ERROR, Unknown parameter %s", field); - optoff += (int)strlen(field); - break; - } - wmem_strbuf_append_printf(avaya_param_buf,",%s", field); - } - else { - if (wmem_strbuf_get_len(avaya_param_buf) > 0) { - dissect_vendor_avaya_param(o242avaya_v_tree, pinfo, vti, tvb, optoff, avaya_param_buf); - optoff += (int)wmem_strbuf_get_len(avaya_param_buf) + 1; - wmem_strbuf_truncate(avaya_param_buf, 0); - } - wmem_strbuf_append(avaya_param_buf, field); +static int +dissect_bootpopt_avaya_ip_telephone(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data _U_) +{ + int offset = 0; + proto_tree *o242avaya_v_tree; + proto_item *avaya_ti; + const guint8 *avaya_option = NULL; + gchar *field = NULL; + wmem_strbuf_t *avaya_param_buf = NULL; + + /* minimum length is 5 bytes */ + if (tvb_reported_length(tvb) < 5) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "Avaya IP Telephone option length isn't >= 5"); + return 1; + } + avaya_ti = proto_tree_add_item_ret_string(tree, hf_bootp_option242_avaya, tvb, offset, tvb_reported_length(tvb), ENC_ASCII|ENC_NA, wmem_packet_scope(), &avaya_option); + o242avaya_v_tree = proto_item_add_subtree(avaya_ti, ett_bootp_option242_suboption); + avaya_param_buf = wmem_strbuf_new(wmem_packet_scope(), ""); + for ( field = strtok((char*)avaya_option, ","); field; field = strtok(NULL, ",") ) { + if (!strchr(field, '=')) { + if (wmem_strbuf_get_len(avaya_param_buf) == 0) { + expert_add_info_format(pinfo, avaya_ti, &hf_bootp_subopt_unknown_type, "ERROR, Unknown parameter %s", field); + offset += (int)strlen(field); + break; } + wmem_strbuf_append_printf(avaya_param_buf,",%s", field); } - if (wmem_strbuf_get_len(avaya_param_buf) > 0) { - dissect_vendor_avaya_param(o242avaya_v_tree, pinfo, vti, tvb, optoff, avaya_param_buf); - } - break; - } - - default: /* not special */ - /* The PacketCable CCC option number can vary. If this is a CCC option, - handle it as a special. - */ - if (code == pkt_ccc_option) { - ftype = special; - 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(pinfo, vti, v_tree, tvb, optoff, optend); - break; - case PACKETCABLE_CCC_DRAFT5: - case PACKETCABLE_CCC_RFC_3495: - optoff = dissect_packetcable_ietf_ccc(pinfo, vti, v_tree, tvb, optoff, optend, pkt_ccc_protocol_version); - break; - default: /* XXX Should we do something here? */ - break; - } + else { + if (wmem_strbuf_get_len(avaya_param_buf) > 0) { + dissect_vendor_avaya_param(o242avaya_v_tree, pinfo, avaya_ti, tvb, offset, avaya_param_buf); + offset += (int)wmem_strbuf_get_len(avaya_param_buf) + 1; + wmem_strbuf_truncate(avaya_param_buf, 0); } - } else { - option_handled = FALSE; + wmem_strbuf_append(avaya_param_buf, field); } - - break; } - - basictype_consumed = bootp_handle_basic_types(pinfo, v_tree, vti, tvb, ftype, - optoff, optlen, opt->phf, &default_hfs); - - if ((basictype_consumed == 0) && (option_handled == FALSE) && - (opt->phf == NULL) && (optlen > 0)) { - proto_tree_add_item(v_tree, hf_bootp_option_value, tvb, voff+2, optlen, ENC_NA); + if (wmem_strbuf_get_len(avaya_param_buf) > 0) { + dissect_vendor_avaya_param(o242avaya_v_tree, pinfo, avaya_ti, tvb, offset, avaya_param_buf); } - return consumed; + return tvb_captured_length(tvb); } static const value_string option82_suboption_vals[] = { @@ -3222,6 +3141,18 @@ bootp_dhcp_decode_agent_info(packet_info *pinfo, proto_item *v_ti, proto_tree *v return optoff; } +static int +dissect_bootpopt_relay_agent_info(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data _U_) +{ + int offset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + offset = bootp_dhcp_decode_agent_info(pinfo, tree, tree, tvb, offset, tvb_reported_length(tvb)); + } + + return tvb_captured_length(tvb); +} + static const value_string option43_pxeclient_suboption_vals[] = { { 0, "Padding" }, { 1, "PXE mtftp IP" }, @@ -3343,6 +3274,29 @@ dissect_vendor_pxeclient_suboption(packet_info *pinfo, proto_item *v_ti, proto_t return optoff; } +static gboolean +dissect_pxeclient_vendor_info_heur( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) +{ + int offset = 0; + bootp_option_data_t *option_data = (bootp_option_data_t*)data; + proto_tree* vendor_tree; + + /* PXE protocol 2.1 as described in the Intel specs */ + if ((option_data->vendor_class_id == NULL) || + (strncmp((const gchar*)option_data->vendor_class_id, "PXEClient", strlen("PXEClient")) != 0)) + return FALSE; + + proto_item_append_text(tree, " (PXEClient)"); + vendor_tree = proto_item_add_subtree(tree, ett_bootp_option); + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + offset = dissect_vendor_pxeclient_suboption(pinfo, tree, vendor_tree, + tvb, offset, tvb_reported_length(tvb)); + } + + return TRUE; +} + static void dissect_vendor_avaya_param(proto_tree *tree, packet_info *pinfo, proto_item *vti, tvbuff_t *tvb, int optoff, wmem_strbuf_t *avaya_param_buf) @@ -3551,8 +3505,8 @@ rfc3825_fixpoint_to_decimal(struct rfc3825_location_fixpoint_t *fixpoint, struct return RFC3825_NOERROR; } -static int dissect_isns(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree, - tvbuff_t *tvb, int optoff, int optlen) +static int +dissect_bootpopt_isns(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { static const int *isns_functions_hf_flags[] = { &hf_bootp_option_isns_functions_enabled, @@ -3596,19 +3550,19 @@ static int dissect_isns(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree guint16 function_flags, dd_access_flags, administrative_flags; guint32 server_security_flags; - proto_tree *tree; + proto_tree *server_tree; proto_item *item; - int optend, heartbeat_set = 0; + int length = tvb_reported_length(tvb); + int offset = 0, heartbeat_set = 0; - if (optlen < 14) { - expert_add_info_format(pinfo, v_ti, &ei_bootp_bad_length, "length must be >= 14"); - return optoff; + if (length < 14) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "length must be >= 14"); + return 1; } - optend = optoff + optlen; - item = proto_tree_add_bitmask(v_tree, tvb, optoff, hf_bootp_option_isns_functions, + item = proto_tree_add_bitmask(tree, tvb, offset, hf_bootp_option_isns_functions, ett_bootp_isns_functions, isns_functions_hf_flags, ENC_BIG_ENDIAN); - function_flags = tvb_get_ntohs(tvb, optoff); + function_flags = tvb_get_ntohs(tvb, offset); /* RFC 4174, section "2.1. iSNS Functions Field" specifies that if * the field "Function Fields Enabled" is set to 0, then "the contents * of all other iSNS Function fields MUST be ignored. We will display @@ -3618,54 +3572,53 @@ static int dissect_isns(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree if (ISNS_BITFIELD_NZ_MUST_BE_IGNORED(function_flags, F_ISNS_FUNCTIONS_ENABLED)) expert_add_info(pinfo, item, &ei_bootp_option_isns_ignored_bitfield); - optoff += 2; - item = proto_tree_add_bitmask(v_tree, tvb, optoff, hf_bootp_option_isns_discovery_domain_access, + offset += 2; + item = proto_tree_add_bitmask(tree, tvb, offset, hf_bootp_option_isns_discovery_domain_access, ett_bootp_isns_discovery_domain_access, isns_dda_hf_flags, ENC_BIG_ENDIAN); - dd_access_flags = tvb_get_ntohs(tvb, optoff); + dd_access_flags = tvb_get_ntohs(tvb, offset); if (ISNS_BITFIELD_NZ_MUST_BE_IGNORED(dd_access_flags, F_ISNS_DD_ACCESS_ENABLED)) expert_add_info(pinfo, item, &ei_bootp_option_isns_ignored_bitfield); - optoff += 2; - administrative_flags = tvb_get_ntohs(tvb, optoff); + offset += 2; + administrative_flags = tvb_get_ntohs(tvb, offset); if (administrative_flags & F_ISNS_ADMIN_FLAGS_ENABLED) { if ((administrative_flags & F_ISNS_ADMIN_FLAGS_HEARTBEAT)) { - if (optlen < 18) { - expert_add_info_format(pinfo, v_ti, &ei_bootp_bad_length, "length must be >= 18"); - return optoff; + if (length < 18) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "length must be >= 18"); + return offset; } heartbeat_set = 1; } } - item = proto_tree_add_bitmask(v_tree, tvb, optoff, hf_bootp_option_isns_administrative_flags, + item = proto_tree_add_bitmask(tree, tvb, offset, hf_bootp_option_isns_administrative_flags, ett_bootp_isns_administrative_flags, isns_administrative_flags, ENC_BIG_ENDIAN); if (ISNS_BITFIELD_NZ_MUST_BE_IGNORED(administrative_flags, F_ISNS_ADMIN_FLAGS_ENABLED)) expert_add_info(pinfo, item, &ei_bootp_option_isns_ignored_bitfield); - optoff += 2; - item = proto_tree_add_bitmask(v_tree, tvb, optoff, hf_bootp_option_isns_server_security_bitmap, + offset += 2; + item = proto_tree_add_bitmask(tree, tvb, offset, hf_bootp_option_isns_server_security_bitmap, ett_bootp_isns_server_security_bitmap, isns_server_security_flags, ENC_BIG_ENDIAN); - server_security_flags = tvb_get_ntohl(tvb, optoff); + server_security_flags = tvb_get_ntohl(tvb, offset); if (ISNS_BITFIELD_NZ_MUST_BE_IGNORED(server_security_flags, F_ISNS_SRV_SEC_BITMAP_ENABLED)) expert_add_info(pinfo, item, &ei_bootp_option_isns_ignored_bitfield); - optoff += 4; + offset += 4; if (heartbeat_set) { - proto_tree_add_item(v_tree, hf_bootp_option_isns_heartbeat_originator_addr, - tvb, optoff, 4, ENC_BIG_ENDIAN); - optoff += 4; + proto_tree_add_item(tree, hf_bootp_option_isns_heartbeat_originator_addr, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; } - proto_tree_add_item(v_tree, hf_bootp_option_isns_primary_server_addr, - tvb, optoff, 4, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_bootp_option_isns_primary_server_addr, tvb, offset, 4, ENC_BIG_ENDIAN); - optoff += 4; - if (optoff < optend) { - tree = proto_tree_add_subtree(v_tree, tvb, optoff, 0, ett_bootp_isns_secondary_server_addr, + offset += 4; + if (tvb_reported_length_remaining(tvb, offset) > 0) { + server_tree = proto_tree_add_subtree(tree, tvb, offset, 0, ett_bootp_isns_secondary_server_addr, &item, "Secondary iSNS Servers"); - optoff += bootp_handle_basic_types(pinfo, tree, item, tvb, ipv4_list, optoff, optend - optoff, + bootp_handle_basic_types(pinfo, server_tree, item, tvb, ipv4_list, offset, tvb_reported_length_remaining(tvb, offset), &hf_bootp_option_isns_secondary_server_addr_list, NULL); } - return optoff; + + return tvb_captured_length(tvb); } static const value_string option43_cl_suboption_vals[] = { @@ -3867,6 +3820,80 @@ dissect_vendor_cablelabs_suboption(packet_info *pinfo, proto_item *v_ti, proto_t return optoff; } +static gboolean +dissect_cablelabs_vendor_info_heur( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) +{ + int offset = 0; + bootp_option_data_t *option_data = (bootp_option_data_t*)data; + proto_tree* vendor_tree; + + if ((option_data->vendor_class_id != NULL) && + ((strncmp((const gchar*)option_data->vendor_class_id, "pktc", strlen("pktc")) == 0) || + (strncmp((const gchar*)option_data->vendor_class_id, "docsis", strlen("docsis")) == 0) || + (strncmp((const gchar*)option_data->vendor_class_id, "OpenCable2.0", strlen("OpenCable2.0")) == 0) || + (strncmp((const gchar*)option_data->vendor_class_id, "CableHome", strlen("CableHome")) == 0))) { + /* CableLabs standard - see www.cablelabs.com/projects */ + proto_item_append_text(tree, " (CableLabs)"); + vendor_tree = proto_item_add_subtree(tree, ett_bootp_option); + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + offset = dissect_vendor_cablelabs_suboption(pinfo, tree, vendor_tree, + tvb, offset, tvb_reported_length(tvb)); + } + return TRUE; + } + + return FALSE; +} + +static gboolean +dissect_aruba_ap_vendor_info_heur( tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data) +{ + int offset = 0; + bootp_option_data_t *option_data = (bootp_option_data_t*)data; + proto_tree* vendor_tree; + + if ((option_data->vendor_class_id == NULL) || + (strncmp((const gchar*)option_data->vendor_class_id, ARUBA_AP, strlen(ARUBA_AP)) != 0)) + return FALSE; + + proto_item_append_text(tree, " (Aruba AP)"); + vendor_tree = proto_item_add_subtree(tree, ett_bootp_option); + + proto_tree_add_item(vendor_tree, hf_bootp_option43_arubaap_controllerip, tvb, offset, tvb_reported_length(tvb), ENC_ASCII|ENC_NA); + return TRUE; +} + +static gboolean +dissect_aruba_instant_ap_vendor_info_heur( tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data) +{ + int offset = 0; + int reported_len = tvb_reported_length(tvb); + bootp_option_data_t *option_data = (bootp_option_data_t*)data; + proto_tree* vendor_tree; + proto_item* vendor_item; + gint32 nameorglen, ampiplen; + + /* Aruba Instant AP */ + if ((option_data->vendor_class_id == NULL) || + (strncmp((const gchar*)option_data->vendor_class_id, ARUBA_INSTANT_AP, strlen(ARUBA_INSTANT_AP)) != 0)) + return FALSE; + + proto_item_append_text(tree, " (Aruba Instant AP)"); + + vendor_item = proto_tree_add_item(tree, hf_bootp_option43_arubaiap, tvb, offset, reported_len, ENC_ASCII|ENC_NA); + vendor_tree = proto_item_add_subtree(vendor_item, ett_bootp_option43_suboption); + nameorglen = tvb_find_guint8(tvb, offset, tvb_reported_length(tvb), ','); + proto_tree_add_item(vendor_tree, hf_bootp_option43_arubaiap_nameorg, tvb, offset, nameorglen, ENC_ASCII|ENC_NA); + offset += (nameorglen+1); + ampiplen = tvb_find_guint8(tvb, offset, reported_len-nameorglen-1, ',') - offset; + proto_tree_add_item(vendor_tree, hf_bootp_option43_arubaiap_ampip, tvb, offset, ampiplen, ENC_ASCII|ENC_NA); + offset += (ampiplen+1); + proto_tree_add_item(vendor_tree, hf_bootp_option43_arubaiap_password, tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_ASCII|ENC_NA); + + return TRUE; +} + static const value_string option43_bsdp_suboption_vals[] = { { 1, "Message Type" }, { 2, "Version" }, @@ -4005,6 +4032,30 @@ dissect_vendor_bsdp_suboption(packet_info *pinfo, proto_item *v_ti, proto_tree * return optoff; } +static gboolean +dissect_packetcable_bsdpd_vendor_info_heur( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) +{ + int offset = 0; + bootp_option_data_t *option_data = (bootp_option_data_t*)data; + proto_tree* vendor_tree; + + if ((option_data->vendor_class_id == NULL) || + (strncmp((const gchar*)option_data->vendor_class_id, PACKETCABLE_BSDP, strlen(PACKETCABLE_BSDP)) != 0)) + return FALSE; + + /* Apple BSDP */ + proto_item_append_text(tree, " (Boot Server Discovery Protocol (BSDP))"); + vendor_tree = proto_item_add_subtree(tree, ett_bootp_option); + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + offset = dissect_vendor_bsdp_suboption(pinfo, tree, vendor_tree, + tvb, offset, tvb_reported_length(tvb)); + } + + return TRUE; +} + + static int dissect_vendor_generic_suboption(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree, tvbuff_t *tvb, int optoff, int optend) @@ -4042,6 +4093,50 @@ dissect_vendor_generic_suboption(packet_info *pinfo, proto_item *v_ti, proto_tre return suboptoff; } + +static int +dissect_bootpopt_vi_vendor_specific_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + int offset = 0; + int enterprise = 0; + int s_end = 0; + int option_data_len = 0; + proto_item *vti; + proto_tree *e_tree; + + while (tvb_reported_length_remaining(tvb, offset) >= 5) { + + vti = proto_tree_add_item_ret_uint(tree, hf_bootp_option125_enterprise, tvb, offset, 4, ENC_BIG_ENDIAN, &enterprise); + e_tree = proto_item_add_subtree(vti, ett_bootp_option); + offset += 4; + + proto_tree_add_item_ret_uint(e_tree, hf_bootp_option125_length, tvb, offset, 1, ENC_NA, &option_data_len); + offset += 1; + + s_end = offset + option_data_len; + if ( tvb_reported_length_remaining(tvb, s_end) < 0 ) { + expert_add_info_format(pinfo, vti, &ei_bootp_option125_enterprise_malformed, "no room left in option for enterprise %u data", enterprise); + break; + } + + while (offset < s_end) { + tvbuff_t *enterprise_tvb = tvb_new_subset_length(tvb, offset, option_data_len); + int bytes_dissected = dissector_try_uint(bootp_enterprise_table, enterprise, enterprise_tvb, pinfo, e_tree); + if (bytes_dissected == 0) { + offset = dissect_vendor_generic_suboption(pinfo, vti, e_tree, tvb, offset, s_end); + } else{ + offset += bytes_dissected; + } + } + } + + if (tvb_reported_length_remaining(tvb, offset) > 0) { + expert_add_info_format(pinfo, tree, &ei_bootp_bad_length, "length < 5"); + } + + return tvb_captured_length(tvb); +} + static const value_string option43_alcatel_suboption_vals[] = { { 0, "Padding" }, { 58, "Voice VLAN ID" }, @@ -4179,6 +4274,35 @@ dissect_vendor_alcatel_suboption(packet_info *pinfo, proto_item *v_ti, proto_tre return optoff; } +static gboolean +dissect_alcatel_lucent_vendor_info_heur( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + int offset = 0; + guint8 s_option; + proto_tree* vendor_tree; + + if (tvb_reported_length(tvb) < 1) + return FALSE; + + s_option = tvb_get_guint8(tvb, offset); + if ((s_option==58 || s_option==64 || s_option==65 + || s_option==66 || s_option==67) + && test_encapsulated_vendor_options(tvb, offset, tvb_reported_length(tvb))) { + + /* Alcatel-Lucent DHCP Extensions */ + proto_item_append_text(tree, " (Alcatel-Lucent)"); + vendor_tree = proto_item_add_subtree(tree, ett_bootp_option); + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + offset = dissect_vendor_alcatel_suboption(pinfo, tree, vendor_tree, + tvb, offset, tvb_reported_length(tvb)); + } + return TRUE; + } + + return FALSE; +} + static const value_string option63_suboption_vals[] = { { 1, "NWIP does not exist on subnet" }, { 2, "NWIP exists in options area" }, @@ -4274,6 +4398,18 @@ dissect_netware_ip_suboption(packet_info *pinfo, proto_item *v_ti, proto_tree *v return optoff; } +static int +dissect_bootpopt_netware_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + int offset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + offset = dissect_netware_ip_suboption(pinfo, tree, tree, tvb, offset, tvb_reported_length(tvb)); + } + + return tvb_captured_length(tvb); +} + static const value_string option125_tr111_suboption_vals[] = { { 1, "DeviceManufacturerOUI" }, { 2, "DeviceSerialNumber" }, @@ -4285,13 +4421,12 @@ static const value_string option125_tr111_suboption_vals[] = { }; static int -dissect_vendor_tr111_suboption(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree, - tvbuff_t *tvb, int optoff, int optend) +dissect_vendor_tr111_suboption(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { - int suboptoff = optoff; + int offset = 0; proto_tree *o125_v_tree; proto_item *vti, *ti; - guint8 subopt, subopt_len; + guint8 subopt, subopt_len; struct basic_types_hfs default_hfs = { NULL, @@ -4321,55 +4456,54 @@ dissect_vendor_tr111_suboption(packet_info *pinfo, proto_item *v_ti, proto_tree /* 6 */ {"GatewayProductClass", string, &hf_bootp_option125_tr111_gateway_product_class}, }; - subopt = tvb_get_guint8(tvb, optoff); - suboptoff++; + subopt = tvb_get_guint8(tvb, offset); + offset++; - if (suboptoff >= optend) { - expert_add_info_format(pinfo, v_ti, &ei_bootp_missing_subopt_length, + if (tvb_reported_length_remaining(tvb, offset) < 1) { + expert_add_info_format(pinfo, tree, &ei_bootp_missing_subopt_length, "Suboption %d: no room left in option for suboption length", subopt); - return (optend); + return offset; } - subopt_len = tvb_get_guint8(tvb, suboptoff); - vti = proto_tree_add_uint_format_value(v_tree, hf_bootp_option125_tr111_suboption, - tvb, optoff, subopt_len+2, subopt, "(%d) %s", + subopt_len = tvb_get_guint8(tvb, offset); + vti = proto_tree_add_uint_format_value(tree, hf_bootp_option125_tr111_suboption, + tvb, offset, subopt_len+2, subopt, "(%d) %s", subopt, val_to_str_const(subopt, option125_tr111_suboption_vals, "Unknown")); o125_v_tree = proto_item_add_subtree(vti, ett_bootp_option125_tr111_suboption); - proto_tree_add_item(o125_v_tree, hf_bootp_suboption_length, tvb, suboptoff, 1, ENC_BIG_ENDIAN); - suboptoff++; + proto_tree_add_item(o125_v_tree, hf_bootp_suboption_length, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; - if (suboptoff+subopt_len > optend) { + if (tvb_reported_length_remaining(tvb, offset) < subopt_len) { expert_add_info_format(pinfo, vti, &ei_bootp_missing_subopt_value, "Suboption %d: no room left in option for suboption value", subopt); - return (optend); + return offset; } - ti = proto_tree_add_item(v_tree, hf_bootp_option125_value, tvb, suboptoff, subopt_len, ENC_NA); + ti = proto_tree_add_item(tree, hf_bootp_option125_value, tvb, offset, subopt_len, ENC_NA); PROTO_ITEM_SET_HIDDEN(ti); if (subopt < array_length(o125_tr111_opt)) { - if (bootp_handle_basic_types(pinfo, o125_v_tree, vti, tvb, o125_tr111_opt[subopt].ftype, suboptoff, subopt_len, o125_tr111_opt[subopt].phf, &default_hfs) == 0) { + if (bootp_handle_basic_types(pinfo, o125_v_tree, vti, tvb, o125_tr111_opt[subopt].ftype, offset, subopt_len, o125_tr111_opt[subopt].phf, &default_hfs) == 0) { if (o125_tr111_opt[subopt].ftype == special) { if (o125_tr111_opt[subopt].phf != NULL) - proto_tree_add_item(v_tree, *o125_tr111_opt[subopt].phf, tvb, suboptoff, subopt_len, ENC_BIG_ENDIAN); + proto_tree_add_item(o125_v_tree, *o125_tr111_opt[subopt].phf, tvb, offset, subopt_len, ENC_BIG_ENDIAN); else - proto_tree_add_item(v_tree, hf_bootp_option125_value, tvb, suboptoff, subopt_len, ENC_NA); + proto_tree_add_item(o125_v_tree, hf_bootp_option125_value, tvb, offset, subopt_len, ENC_NA); } else if (o125_tr111_opt[subopt].ftype == oui) { /* Get hex string. Expecting 6 characters. */ - gchar *oui_string = tvb_get_string_enc(wmem_packet_scope(), tvb, suboptoff, subopt_len, ENC_ASCII); + gchar *oui_string = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, subopt_len, ENC_ASCII); /* Convert to OUI number. Only 3 bytes so no data lost in downcast. */ guint32 oui_number = (guint32)strtol(oui_string, NULL, 16); /* Add item using oui_vals */ - proto_tree_add_uint(v_tree, *o125_tr111_opt[subopt].phf, tvb, suboptoff, subopt_len, oui_number); + proto_tree_add_uint(o125_v_tree, *o125_tr111_opt[subopt].phf, tvb, offset, subopt_len, oui_number); } else if (o125_tr111_opt[subopt].phf == NULL) - proto_tree_add_item(v_tree, hf_bootp_option125_value, tvb, suboptoff, subopt_len, ENC_NA); + proto_tree_add_item(o125_v_tree, hf_bootp_option125_value, tvb, offset, subopt_len, ENC_NA); } } - optoff += (subopt_len + 2); - return optoff; + return subopt_len + 2; } static const value_string option125_cl_suboption_vals[] = { @@ -4390,11 +4524,10 @@ static const value_string pkt_mib_env_ind_opt_vals[] = { }; static int -dissect_vendor_cl_suboption(packet_info *pinfo, proto_item *v_ti, proto_tree *v_tree, - tvbuff_t *tvb, int optoff, int optend) +dissect_vendor_cl_suboption(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { - int suboptoff = optoff; - guint8 subopt, subopt_len; + int offset = 0; + guint8 subopt, subopt_len; proto_tree *o125_v_tree; proto_item *vti; @@ -4421,56 +4554,55 @@ dissect_vendor_cl_suboption(packet_info *pinfo, proto_item *v_ti, proto_tree *v_ /* 5 */ {"Modem Capabilities : ", special, &hf_bootp_option125_cl_modem_capabilities}, }; - subopt = tvb_get_guint8(tvb, optoff); - suboptoff++; + subopt = tvb_get_guint8(tvb, offset); + offset++; - if (suboptoff >= optend) { - expert_add_info_format(pinfo, v_ti, &ei_bootp_missing_subopt_length, + if (tvb_reported_length_remaining(tvb, offset) < 1) { + expert_add_info_format(pinfo, tree, &ei_bootp_missing_subopt_length, "Suboption %d: no room left in option for suboption length", subopt); - return (optend); + return offset; } - subopt_len = tvb_get_guint8(tvb, suboptoff); - vti = proto_tree_add_uint_format_value(v_tree, hf_bootp_option125_cl_suboption, - tvb, optoff, subopt_len+2, subopt, "(%d) %s", + subopt_len = tvb_get_guint8(tvb, offset); + vti = proto_tree_add_uint_format_value(tree, hf_bootp_option125_cl_suboption, + tvb, offset, subopt_len+2, subopt, "(%d) %s", subopt, val_to_str_const(subopt, option125_cl_suboption_vals, "Unknown")); o125_v_tree = proto_item_add_subtree(vti, ett_bootp_option125_cl_suboption); - proto_tree_add_item(o125_v_tree, hf_bootp_suboption_length, tvb, suboptoff, 1, ENC_BIG_ENDIAN); - suboptoff++; + proto_tree_add_item(o125_v_tree, hf_bootp_suboption_length, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; - if (suboptoff+subopt_len > optend) { + if (tvb_reported_length_remaining(tvb, offset) < subopt_len) { expert_add_info_format(pinfo, vti, &ei_bootp_missing_subopt_value, "Suboption %d: no room left in option for suboption value", subopt); - return (optend); + return offset; } if (subopt < array_length(o125_cl_opt)) { if (bootp_handle_basic_types(pinfo, o125_v_tree, vti, tvb, o125_cl_opt[subopt].ftype, - suboptoff, subopt_len, o125_cl_opt[subopt].phf, &default_hfs) == 0) { + offset, subopt_len, o125_cl_opt[subopt].phf, &default_hfs) == 0) { switch(o125_cl_opt[subopt].ftype) { case special: if (o125_cl_opt[subopt].phf != NULL) - proto_tree_add_item(o125_v_tree, *o125_cl_opt[subopt].phf, tvb, suboptoff, subopt_len, ENC_BIG_ENDIAN); + proto_tree_add_item(o125_v_tree, *o125_cl_opt[subopt].phf, tvb, offset, subopt_len, ENC_BIG_ENDIAN); else - proto_tree_add_item(o125_v_tree, hf_bootp_option125_value, tvb, suboptoff, subopt_len, ENC_NA); + proto_tree_add_item(o125_v_tree, hf_bootp_option125_value, tvb, offset, subopt_len, ENC_NA); switch(subopt){ case 5: /* Modem Capabilities */ - dissect_docsis_cm_cap(o125_v_tree, tvb, optoff, subopt_len+2, TRUE); + dissect_docsis_cm_cap(o125_v_tree, tvb, offset-2, subopt_len+2, TRUE); break; } break; default: if (o125_cl_opt[subopt].phf == NULL) - proto_tree_add_item(o125_v_tree, hf_bootp_option125_value, tvb, suboptoff, subopt_len, ENC_NA); + proto_tree_add_item(o125_v_tree, hf_bootp_option125_value, tvb, offset, subopt_len, ENC_NA); break; } } } - optoff += (subopt_len + 2); - return optoff; + return subopt_len + 2; } /* PacketCable Multimedia Terminal Adapter device capabilities (option 60). @@ -4882,6 +5014,26 @@ dissect_packetcable_mta_cap(proto_tree *v_tree, packet_info *pinfo, tvbuff_t *tv } } +static gboolean +dissect_packetcable_mta_vendor_id_heur( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_ ) +{ + guint8* vendor_id; + + if (tvb_reported_length(tvb) < 8) { + return FALSE; + } + + vendor_id = tvb_get_string_enc(wmem_packet_scope(), tvb, 0, 8, ENC_ASCII|ENC_NA); + if ((strcmp((const char*)vendor_id, PACKETCABLE_MTA_CAP10) == 0) || + (strcmp((const char*)vendor_id, PACKETCABLE_MTA_CAP15) == 0) || + (strcmp((const char*)vendor_id, PACKETCABLE_MTA_CAP20) == 0)) { + dissect_packetcable_mta_cap(tree, pinfo, tvb, 0, tvb_reported_length(tvb)); + return TRUE; + } + + return FALSE; +} + /* DOCSIS Cable Modem device capabilities (option 60/option 125). */ #define DOCSIS_CM_CAP_TLV_OFF 12 @@ -5473,6 +5625,47 @@ dissect_docsis_cm_cap(proto_tree *v_tree, tvbuff_t *tvb, int voff, int len, gboo } } +static gboolean +dissect_packetcable_cm_vendor_id_heur( tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_ ) +{ + guint8* vendor_id; + + if (tvb_reported_length(tvb) < 10) { + return FALSE; + } + + vendor_id = tvb_get_string_enc(wmem_packet_scope(), tvb, 0, 10, ENC_ASCII|ENC_NA); + if ((strcmp((const char*)vendor_id, PACKETCABLE_CM_CAP11) == 0) || + (strcmp((const char*)vendor_id, PACKETCABLE_CM_CAP20) == 0)) { + dissect_docsis_cm_cap(tree, tvb, 0, tvb_reported_length(tvb), FALSE); + return TRUE; + } + + if ((strcmp((const char*)vendor_id, PACKETCABLE_CM_CAP30) == 0)) { + proto_tree_add_item(tree, hf_bootp_option_vendor_class_data, tvb, 0, tvb_reported_length(tvb), ENC_ASCII|ENC_NA); + return TRUE; + } + + return FALSE; +} + +static gboolean +dissect_packetcable_bsdpd_vendor_id_heur( tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_ ) +{ + int vendor_id_len = (int)strlen(PACKETCABLE_BSDPD); + if ((int)tvb_reported_length(tvb) < vendor_id_len) { + return FALSE; + } + + if (tvb_memeql(tvb, 0, (const guint8*)PACKETCABLE_BSDPD, vendor_id_len) == 0 ) { + proto_tree_add_item(tree, hf_bootp_option_vendor_class_data, tvb, vendor_id_len, tvb_reported_length_remaining(tvb, vendor_id_len), ENC_ASCII|ENC_NA); + return TRUE; + } + + return FALSE; +} + + /* 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". @@ -5832,6 +6025,29 @@ dissect_packetcable_ietf_ccc(packet_info *pinfo, proto_item *v_ti, proto_tree *v return suboptoff; } +static int +dissect_bootpopt_packetcable_ccc(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data _U_) +{ + int offset = 0; + + while (tvb_reported_length_remaining(tvb, offset) > 0) { + switch (pkt_ccc_protocol_version) { + + case PACKETCABLE_CCC_I05: + offset = dissect_packetcable_i05_ccc(pinfo, tree, tree, tvb, offset, tvb_reported_length(tvb)); + break; + case PACKETCABLE_CCC_DRAFT5: + case PACKETCABLE_CCC_RFC_3495: + offset = dissect_packetcable_ietf_ccc(pinfo, tree, tree, tvb, offset, tvb_reported_length(tvb), pkt_ccc_protocol_version); + break; + default: /* XXX Should we do something here? */ + break; + } + } + + return tvb_captured_length(tvb); +} + #define BOOTREQUEST 1 #define BOOTREPLY 2 @@ -5924,7 +6140,7 @@ dissect_bootp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U rfc3396_dns_domain_search_list.index_current_block = 0; rfc3396_sip_server.index_current_block = 0; while (tmpvoff < eoff && !at_end) { - offset_delta = bootp_option(tvb, pinfo, NULL, NULL, tmpvoff, eoff, TRUE, &at_end, + offset_delta = bootp_option(tvb, pinfo, NULL, tmpvoff, eoff, TRUE, &at_end, &dhcp_type, &vendor_class_id, &overload); if (offset_delta <= 0) { proto_tree_add_expert(bp_tree, pinfo, &ei_bootp_option_parse_err, @@ -6064,7 +6280,7 @@ dissect_bootp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U rfc3396_dns_domain_search_list.index_current_block = 0; rfc3396_sip_server.index_current_block = 0; while (voff < eoff && !at_end) { - offset_delta = bootp_option(tvb, pinfo, bp_tree, bp_ti, voff, eoff, FALSE, &at_end, + offset_delta = bootp_option(tvb, pinfo, bp_tree, voff, eoff, FALSE, &at_end, &dhcp_type, &vendor_class_id, &overload); if (offset_delta <= 0) { proto_tree_add_expert(bp_tree, pinfo, &ei_bootp_option_parse_err, @@ -6095,15 +6311,42 @@ bootp_init_protocol(void) /* first copy default_bootp_opt[] to bootp_opt[]. This resets all values to default */ memcpy(bootp_opt, default_bootp_opt, sizeof(bootp_opt)); + if ((num_bootp_records_uat > 0) && (saved_uat_opts == NULL)) + { + saved_uat_opts = wmem_list_new(NULL); + } + /* Now apply the custom options */ for (i = 0; i < num_bootp_records_uat; i++) { bootp_opt[uat_bootp_records[i].opt].text = wmem_strdup(wmem_file_scope(), uat_bootp_records[i].text); bootp_opt[uat_bootp_records[i].opt].ftype = uat_bootp_records[i].ftype; bootp_opt[uat_bootp_records[i].opt].phf = NULL; + + /* Apply the custom option to the dissection table*/ + dissector_change_uint("bootp.option", uat_bootp_records[i].opt, bootpopt_basic_handle); + + /* Save the option so it can be cleared later */ + wmem_list_append(saved_uat_opts, GUINT_TO_POINTER(uat_bootp_records[i].opt)); } } +static void +bootp_clear_uat_bootpopt(gpointer data, gpointer user_data _U_) +{ + dissector_reset_uint("bootp.option", GPOINTER_TO_UINT(data)); +} + +static void +bootp_cleanup_protocol(void) +{ + wmem_list_foreach(saved_uat_opts, bootp_clear_uat_bootpopt, NULL); + + wmem_destroy_list(saved_uat_opts); + saved_uat_opts = NULL; +} + + /* TAP STAT INFO */ typedef enum { @@ -8626,7 +8869,6 @@ proto_register_bootp(void) { &hf_bootp_opt_overload_file_end_missing, { "bootp.option.option_overload.file_end_missing", PI_PROTOCOL, PI_ERROR, "file overload end option missing", EXPFILL }}, { &hf_bootp_opt_overload_sname_end_missing, { "bootp.option.option_overload.sname_end_missing", PI_PROTOCOL, PI_ERROR, "sname overload end option missing", EXPFILL }}, { &hf_bootp_subopt_unknown_type, { "bootp.subopt.unknown_type", PI_PROTOCOL, PI_ERROR, "ERROR, please report: Unknown subopt type handler", EXPFILL }}, - { &ei_bootp_option77_user_class_malformed, { "bootp.option.user_class.malformed", PI_PROTOCOL, PI_ERROR, "User Class Information: malformed option", EXPFILL }}, { &ei_bootp_option_civic_location_bad_cattype, { "bootp.option.civic_location.bad_cattype", PI_PROTOCOL, PI_ERROR, "Error with CAType", EXPFILL }}, { &ei_bootp_option_dhcp_name_service_invalid, { "bootp.option.dhcp_name_service.invalid", PI_PROTOCOL, PI_ERROR, "Invalid Name Service", EXPFILL }}, { &ei_bootp_option_sip_server_address_encoding, { "bootp.option.sip_server_address.encoding", PI_PROTOCOL, PI_ERROR, "RFC 3361 defines only 0 and 1 for Encoding byte", EXPFILL }}, @@ -8669,8 +8911,7 @@ proto_register_bootp(void) module_t *bootp_module; expert_module_t* expert_bootp; - proto_bootp = proto_register_protocol("Bootstrap Protocol", "BOOTP/DHCP", - "bootp"); + 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"); @@ -8678,8 +8919,14 @@ proto_register_bootp(void) expert_bootp = expert_register_protocol(proto_bootp); expert_register_field_array(expert_bootp, ei, array_length(ei)); - /* register init routine to setup the custom bootp options */ + bootp_option_table = register_dissector_table("bootp.option", "BOOTP Options", proto_bootp, FT_UINT8, BASE_DEC); + bootp_vendor_id_subdissector = register_heur_dissector_list("bootp.vendor_id", proto_bootp); + bootp_vendor_info_subdissector = register_heur_dissector_list("bootp.vendor_info", proto_bootp); + bootp_enterprise_table = register_dissector_table("bootp.enterprise", "V-I Vendor Specific Enterprise", proto_bootp, FT_UINT32, BASE_DEC); + + /* register init/cleanup routine to handle the custom bootp options */ register_init_routine(&bootp_init_protocol); + register_cleanup_routine(&bootp_cleanup_protocol); /* Allow dissector to find be found by name. */ bootp_handle = register_dissector("bootp", dissect_bootp, proto_bootp); @@ -8740,7 +8987,72 @@ proto_register_bootp(void) void proto_reg_handoff_bootp(void) { + range_t *bootopt_basictype_range; + dissector_add_uint_range_with_preference("udp.port", BOOTP_UDP_PORT_RANGE, bootp_handle); + + /* Create dissection function handles for all BOOTP options */ + bootpopt_basic_handle = create_dissector_handle( dissect_bootpopt_basic_type, -1 ); + range_convert_str(wmem_epan_scope(), &bootopt_basictype_range, BOOTP_OPTION_BASICTYPE_RANGE, 0xFF); + dissector_add_uint_range("bootp.option", bootopt_basictype_range, bootpopt_basic_handle); + + dissector_add_uint("bootp.option", 21, create_dissector_handle( dissect_bootpopt_policy_filter, -1 )); + dissector_add_uint("bootp.option", 33, create_dissector_handle( dissect_bootpopt_static_route, -1 )); + dissector_add_uint("bootp.option", 43, create_dissector_handle( dissect_bootpopt_vendor_specific_info, -1 )); + dissector_add_uint("bootp.option", 52, create_dissector_handle( dissect_bootpopt_option_overload, -1 )); + dissector_add_uint("bootp.option", 53, create_dissector_handle( dissect_bootpopt_dhcp, -1 )); + dissector_add_uint("bootp.option", 55, create_dissector_handle( dissect_bootpopt_param_request_list, -1 )); + dissector_add_uint("bootp.option", 60, create_dissector_handle( dissect_bootpopt_vendor_class_identifier, -1 )); + dissector_add_uint("bootp.option", 61, create_dissector_handle( dissect_bootpopt_client_identifier, -1 )); + dissector_add_uint("bootp.option", 63, create_dissector_handle( dissect_bootpopt_netware_ip, -1 )); + dissector_add_uint("bootp.option", 77, create_dissector_handle( dissect_bootpopt_user_class_information, -1 )); + dissector_add_uint("bootp.option", 78, create_dissector_handle( dissect_bootpopt_slp_directory_agent, -1 )); + dissector_add_uint("bootp.option", 79, create_dissector_handle( dissect_bootpopt_slp_service_scope, -1 )); + dissector_add_uint("bootp.option", 81, create_dissector_handle( dissect_bootpopt_client_full_domain_name, -1 )); + dissector_add_uint("bootp.option", 82, create_dissector_handle( dissect_bootpopt_relay_agent_info, -1 )); + dissector_add_uint("bootp.option", 83, create_dissector_handle( dissect_bootpopt_isns, -1 )); + dissector_add_uint("bootp.option", 85, create_dissector_handle( dissect_bootpopt_novell_servers, -1 )); + dissector_add_uint("bootp.option", 90, create_dissector_handle( dissect_bootpopt_dhcp_authentication, -1 )); + dissector_add_uint("bootp.option", 94, create_dissector_handle( dissect_bootpopt_client_network_interface_id, -1 )); + dissector_add_uint("bootp.option", 97, create_dissector_handle( dissect_bootpopt_client_identifier_uuid, -1 )); + dissector_add_uint("bootp.option", 99, create_dissector_handle( dissect_bootpopt_civic_location, -1 )); + dissector_add_uint("bootp.option", 117, create_dissector_handle( dissect_bootpopt_name_server_search, -1 )); + dissector_add_uint("bootp.option", 119, create_dissector_handle( dissect_bootpopt_dhcp_domain_search, -1 )); + dissector_add_uint("bootp.option", 120, create_dissector_handle( dissect_bootpopt_sip_servers, -1 )); + dissector_add_uint("bootp.option", 121, create_dissector_handle( dissect_bootpopt_classless_static_route, -1 )); + /* The PacketCable CCC option number can vary. Still handled through preference */ + dissector_add_uint("bootp.option", 122, create_dissector_handle( dissect_bootpopt_packetcable_ccc, -1 )); + + dissector_add_uint("bootp.option", 123, create_dissector_handle( dissect_bootpopt_coordinate_based_location, -1 )); + dissector_add_uint("bootp.option", 124, create_dissector_handle( dissect_bootpopt_vi_vendor_class, -1 )); + dissector_add_uint("bootp.option", 125, create_dissector_handle( dissect_bootpopt_vi_vendor_specific_info, -1 )); + dissector_add_uint("bootp.option", 160, create_dissector_handle( dissect_bootpopt_dhcp_captive_portal, -1 )); + dissector_add_uint("bootp.option", 212, create_dissector_handle( dissect_bootpopt_6RD_option, -1 )); + dissector_add_uint("bootp.option", 242, create_dissector_handle( dissect_bootpopt_avaya_ip_telephone, -1 )); + dissector_add_uint("bootp.option", 249, create_dissector_handle( dissect_bootpopt_classless_static_route, -1 )); + + /* Create heuristic dissection for BOOTP vendor class id */ + heur_dissector_add( "bootp.vendor_id", dissect_packetcable_mta_vendor_id_heur, "PacketCable MTA", "packetcable_mta_bootp", proto_bootp, HEURISTIC_ENABLE ); + heur_dissector_add( "bootp.vendor_id", dissect_packetcable_cm_vendor_id_heur, "PacketCable CM", "packetcable_cm_bootp", proto_bootp, HEURISTIC_ENABLE ); + heur_dissector_add( "bootp.vendor_id", dissect_packetcable_bsdpd_vendor_id_heur, "PacketCable BSDPD", "packetcable_bsdpd_bootp", proto_bootp, HEURISTIC_ENABLE ); + + /* Create heuristic dissection for BOOTP vendor specific information */ + + /* Note that this is a rather weak (permissive) heuristic, + it's put first so it ends up at the end of the list, I guess this is OK. + Add any stronger (less permissive) heuristics after this! + XXX - Should we just disable by default? */ + heur_dissector_add( "bootp.vendor_info", dissect_alcatel_lucent_vendor_info_heur, "Alcatel-Lucent", "alcatel_lucent_bootp", proto_bootp, HEURISTIC_ENABLE ); + + heur_dissector_add( "bootp.vendor_info", dissect_pxeclient_vendor_info_heur, "PXEClient", "pxeclient_bootp", proto_bootp, HEURISTIC_ENABLE ); + heur_dissector_add( "bootp.vendor_info", dissect_cablelabs_vendor_info_heur, "CableLabs", "cablelabs_bootp", proto_bootp, HEURISTIC_ENABLE ); + heur_dissector_add( "bootp.vendor_info", dissect_aruba_ap_vendor_info_heur, ARUBA_AP, "aruba_ap_bootp", proto_bootp, HEURISTIC_ENABLE ); + heur_dissector_add( "bootp.vendor_info", dissect_aruba_instant_ap_vendor_info_heur, ARUBA_INSTANT_AP, "aruba_instant_ap_bootp", proto_bootp, HEURISTIC_ENABLE ); + heur_dissector_add( "bootp.vendor_info", dissect_packetcable_bsdpd_vendor_info_heur, "PacketCable BSDPD", "packetcable_bsdpd_info_bootp", proto_bootp, HEURISTIC_ENABLE ); + + /* Create dissection function handles for BOOTP Enterprise dissection */ + dissector_add_uint("bootp.enterprise", 4491, create_dissector_handle( dissect_vendor_cl_suboption, -1 )); + dissector_add_uint("bootp.enterprise", 3561, create_dissector_handle( dissect_vendor_tr111_suboption, -1 )); } /* |