diff options
Diffstat (limited to 'epan/dissectors/packet-arp.c')
-rw-r--r-- | epan/dissectors/packet-arp.c | 1043 |
1 files changed, 1043 insertions, 0 deletions
diff --git a/epan/dissectors/packet-arp.c b/epan/dissectors/packet-arp.c new file mode 100644 index 0000000000..a0e5153e13 --- /dev/null +++ b/epan/dissectors/packet-arp.c @@ -0,0 +1,1043 @@ +/* packet-arp.c + * Routines for ARP packet disassembly (RFC 826) + * + * $Id$ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <glib.h> +#include <epan/packet.h> +#include <epan/strutil.h> +#include <epan/resolv.h> +#include "packet-arp.h" +#include "etypes.h" +#include "arcnet_pids.h" + +static int proto_arp = -1; +static int hf_arp_hard_type = -1; +static int hf_arp_proto_type = -1; +static int hf_arp_hard_size = -1; +static int hf_atmarp_sht = -1; +static int hf_atmarp_shl = -1; +static int hf_atmarp_sst = -1; +static int hf_atmarp_ssl = -1; +static int hf_arp_proto_size = -1; +static int hf_arp_opcode = -1; +static int hf_atmarp_spln = -1; +static int hf_atmarp_tht = -1; +static int hf_atmarp_thl = -1; +static int hf_atmarp_tst = -1; +static int hf_atmarp_tsl = -1; +static int hf_atmarp_tpln = -1; +static int hf_arp_src_hw = -1; +static int hf_arp_src_hw_mac = -1; +static int hf_arp_src_proto = -1; +static int hf_arp_src_proto_ipv4 = -1; +static int hf_arp_dst_hw = -1; +static int hf_arp_dst_hw_mac = -1; +static int hf_arp_dst_proto = -1; +static int hf_arp_dst_proto_ipv4 = -1; +static int hf_atmarp_src_atm_num_e164 = -1; +static int hf_atmarp_src_atm_num_nsap = -1; +static int hf_atmarp_src_atm_subaddr = -1; +static int hf_atmarp_dst_atm_num_e164 = -1; +static int hf_atmarp_dst_atm_num_nsap = -1; +static int hf_atmarp_dst_atm_subaddr = -1; + +static gint ett_arp = -1; +static gint ett_atmarp_nsap = -1; +static gint ett_atmarp_tl = -1; + +static dissector_handle_t atmarp_handle; + +/* Definitions taken from Linux "linux/if_arp.h" header file, and from + + http://www.iana.org/assignments/arp-parameters + + */ + +/* ARP protocol HARDWARE identifiers. */ +#define ARPHRD_NETROM 0 /* from KA9Q: NET/ROM pseudo */ +#define ARPHRD_ETHER 1 /* Ethernet 10Mbps */ +#define ARPHRD_EETHER 2 /* Experimental Ethernet */ +#define ARPHRD_AX25 3 /* AX.25 Level 2 */ +#define ARPHRD_PRONET 4 /* PROnet token ring */ +#define ARPHRD_CHAOS 5 /* Chaosnet */ +#define ARPHRD_IEEE802 6 /* IEEE 802.2 Ethernet/TR/TB */ +#define ARPHRD_ARCNET 7 /* ARCnet */ +#define ARPHRD_HYPERCH 8 /* Hyperchannel */ +#define ARPHRD_LANSTAR 9 /* Lanstar */ +#define ARPHRD_AUTONET 10 /* Autonet Short Address */ +#define ARPHRD_LOCALTLK 11 /* Localtalk */ +#define ARPHRD_LOCALNET 12 /* LocalNet (IBM PCNet/Sytek LocalNET) */ +#define ARPHRD_ULTRALNK 13 /* Ultra link */ +#define ARPHRD_SMDS 14 /* SMDS */ +#define ARPHRD_DLCI 15 /* Frame Relay DLCI */ +#define ARPHRD_ATM 16 /* ATM */ +#define ARPHRD_HDLC 17 /* HDLC */ +#define ARPHRD_FIBREC 18 /* Fibre Channel */ +#define ARPHRD_ATM2225 19 /* ATM (RFC 2225) */ +#define ARPHRD_SERIAL 20 /* Serial Line */ +#define ARPHRD_ATM2 21 /* ATM */ +#define ARPHRD_MS188220 22 /* MIL-STD-188-220 */ +#define ARPHRD_METRICOM 23 /* Metricom STRIP */ +#define ARPHRD_IEEE1394 24 /* IEEE 1394.1995 */ +#define ARPHRD_MAPOS 25 /* MAPOS */ +#define ARPHRD_TWINAX 26 /* Twinaxial */ +#define ARPHRD_EUI_64 27 /* EUI-64 */ + +/* ARP / RARP structs and definitions */ +#ifndef ARPOP_REQUEST +#define ARPOP_REQUEST 1 /* ARP request. */ +#endif +#ifndef ARPOP_REPLY +#define ARPOP_REPLY 2 /* ARP reply. */ +#endif +/* Some OSes have different names, or don't define these at all */ +#ifndef ARPOP_RREQUEST +#define ARPOP_RREQUEST 3 /* RARP request. */ +#endif +#ifndef ARPOP_RREPLY +#define ARPOP_RREPLY 4 /* RARP reply. */ +#endif +#ifndef ARPOP_IREQUEST +#define ARPOP_IREQUEST 8 /* Inverse ARP (RFC 1293) request. */ +#endif +#ifndef ARPOP_IREPLY +#define ARPOP_IREPLY 9 /* Inverse ARP reply. */ +#endif +#ifndef ATMARPOP_NAK +#define ATMARPOP_NAK 10 /* ATMARP NAK. */ +#endif + +static const value_string op_vals[] = { + {ARPOP_REQUEST, "request" }, + {ARPOP_REPLY, "reply" }, + {ARPOP_RREQUEST, "reverse request"}, + {ARPOP_RREPLY, "reverse reply" }, + {ARPOP_IREQUEST, "inverse request"}, + {ARPOP_IREPLY, "inverse reply" }, + {0, NULL } }; + +static const value_string atmop_vals[] = { + {ARPOP_REQUEST, "request" }, + {ARPOP_REPLY, "reply" }, + {ARPOP_IREQUEST, "inverse request"}, + {ARPOP_IREPLY, "inverse reply" }, + {ATMARPOP_NAK, "nak" }, + {0, NULL } }; + +#define ATMARP_IS_E164 0x40 /* bit in type/length for E.164 format */ +#define ATMARP_LEN_MASK 0x3F /* length of {sub}address in type/length */ + +/* + * Given the hardware address type and length, check whether an address + * is an Ethernet address - the address must be of type "Ethernet" or + * "IEEE 802.x", and the length must be 6 bytes. + */ +#define ARP_HW_IS_ETHER(ar_hrd, ar_hln) \ + (((ar_hrd) == ARPHRD_ETHER || (ar_hrd) == ARPHRD_IEEE802) \ + && (ar_hln) == 6) + +/* + * Given the protocol address type and length, check whether an address + * is an IPv4 address - the address must be of type "IP", and the length + * must be 4 bytes. + */ +#define ARP_PRO_IS_IPv4(ar_pro, ar_pln) \ + ((ar_pro) == ETHERTYPE_IP && (ar_pln) == 4) + +gchar * +arphrdaddr_to_str(const guint8 *ad, int ad_len, guint16 type) +{ + if (ad_len == 0) + return "<No address>"; + if (ARP_HW_IS_ETHER(type, ad_len)) { + /* Ethernet address (or IEEE 802.x address, which is the same type of + address). */ + return ether_to_str(ad); + } + return bytes_to_str(ad, ad_len); +} + +static gchar * +arpproaddr_to_str(const guint8 *ad, int ad_len, guint16 type) +{ + if (ad_len == 0) + return "<No address>"; + if (ARP_PRO_IS_IPv4(type, ad_len)) { + /* IPv4 address. */ + return ip_to_str(ad); + } + return bytes_to_str(ad, ad_len); +} + +#define N_ATMARPNUM_TO_STR_STRINGS 2 +#define MAX_E164_STR_LEN 20 + +static gchar * +atmarpnum_to_str(const guint8 *ad, int ad_tl) +{ + int ad_len = ad_tl & ATMARP_LEN_MASK; + static gchar str[N_ATMARPNUM_TO_STR_STRINGS][MAX_E164_STR_LEN+3+1]; + static int cur_idx; + gchar *cur; + + if (ad_len == 0) + return "<No address>"; + + if (ad_tl & ATMARP_IS_E164) { + /* + * I'm assuming this means it's an ASCII (IA5) string. + */ + cur_idx++; + if (cur_idx >= N_ATMARPNUM_TO_STR_STRINGS) + cur_idx = 0; + cur = &str[cur_idx][0]; + if (ad_len > MAX_E164_STR_LEN) { + /* Can't show it all. */ + memcpy(cur, ad, MAX_E164_STR_LEN); + strcpy(&cur[MAX_E164_STR_LEN], "..."); + } else { + memcpy(cur, ad, ad_len); + cur[ad_len + 1] = '\0'; + } + return cur; + } else { + /* + * NSAP. + * + * XXX - break down into subcomponents. + */ + return bytes_to_str(ad, ad_len); + } +} + +static gchar * +atmarpsubaddr_to_str(const guint8 *ad, int ad_tl) +{ + int ad_len = ad_tl & ATMARP_LEN_MASK; + + if (ad_len == 0) + return "<No address>"; + + /* + * E.164 isn't considered legal in subaddresses (RFC 2225 says that + * a null or unknown ATM address is indicated by setting the length + * to 0, in which case the type must be ignored; we've seen some + * captures in which the length of a subaddress is 0 and the type + * is E.164). + * + * XXX - break down into subcomponents? + */ + return bytes_to_str(ad, ad_len); +} + +static const value_string hrd_vals[] = { + {ARPHRD_NETROM, "NET/ROM pseudo" }, + {ARPHRD_ETHER, "Ethernet" }, + {ARPHRD_EETHER, "Experimental Ethernet"}, + {ARPHRD_AX25, "AX.25" }, + {ARPHRD_PRONET, "ProNET" }, + {ARPHRD_CHAOS, "Chaos" }, + {ARPHRD_IEEE802, "IEEE 802" }, + {ARPHRD_ARCNET, "ARCNET" }, + {ARPHRD_HYPERCH, "Hyperchannel" }, + {ARPHRD_LANSTAR, "Lanstar" }, + {ARPHRD_AUTONET, "Autonet Short Address"}, + {ARPHRD_LOCALTLK, "Localtalk" }, + {ARPHRD_LOCALNET, "LocalNet" }, + {ARPHRD_ULTRALNK, "Ultra link" }, + {ARPHRD_SMDS, "SMDS" }, + {ARPHRD_DLCI, "Frame Relay DLCI" }, + {ARPHRD_ATM, "ATM" }, + {ARPHRD_HDLC, "HDLC" }, + {ARPHRD_FIBREC, "Fibre Channel" }, + {ARPHRD_ATM2225, "ATM (RFC 2225)" }, + {ARPHRD_SERIAL, "Serial Line" }, + {ARPHRD_ATM2, "ATM" }, + {ARPHRD_MS188220, "MIL-STD-188-220" }, + {ARPHRD_METRICOM, "Metricom STRIP" }, + {ARPHRD_IEEE1394, "IEEE 1394.1995" }, + {ARPHRD_MAPOS, "MAPOS" }, + {ARPHRD_TWINAX, "Twinaxial" }, + {ARPHRD_EUI_64, "EUI-64" }, + {0, NULL } }; + +gchar * +arphrdtype_to_str(guint16 hwtype, const char *fmt) { + return val_to_str(hwtype, hrd_vals, fmt); +} + +/* Offsets of fields within an ARP packet. */ +#define AR_HRD 0 +#define AR_PRO 2 +#define AR_HLN 4 +#define AR_PLN 5 +#define AR_OP 6 +#define MIN_ARP_HEADER_SIZE 8 + +/* Offsets of fields within an ATMARP packet. */ +#define ATM_AR_HRD 0 +#define ATM_AR_PRO 2 +#define ATM_AR_SHTL 4 +#define ATM_AR_SSTL 5 +#define ATM_AR_OP 6 +#define ATM_AR_SPLN 8 +#define ATM_AR_THTL 9 +#define ATM_AR_TSTL 10 +#define ATM_AR_TPLN 11 +#define MIN_ATMARP_HEADER_SIZE 12 + +static void +dissect_atm_number(tvbuff_t *tvb, int offset, int tl, int hf_e164, + int hf_nsap, proto_tree *tree) +{ + int len = tl & ATMARP_LEN_MASK; + proto_item *ti; + proto_tree *nsap_tree; + + if (tl & ATMARP_IS_E164) + proto_tree_add_item(tree, hf_e164, tvb, offset, len, FALSE); + else { + ti = proto_tree_add_item(tree, hf_nsap, tvb, offset, len, FALSE); + if (len >= 20) { + nsap_tree = proto_item_add_subtree(ti, ett_atmarp_nsap); + dissect_atm_nsap(tvb, offset, len, nsap_tree); + } + } +} + +void +dissect_atm_nsap(tvbuff_t *tvb, int offset, int len, proto_tree *tree) +{ + guint8 afi; + + afi = tvb_get_guint8(tvb, offset); + switch (afi) { + + case 0x39: /* DCC ATM format */ + case 0xBD: /* DCC ATM group format */ + proto_tree_add_text(tree, tvb, offset + 0, 3, + "Data Country Code%s: 0x%04X", + (afi == 0xBD) ? " (group)" : "", + tvb_get_ntohs(tvb, offset + 1)); + proto_tree_add_text(tree, tvb, offset + 3, 10, + "High Order DSP: %s", + tvb_bytes_to_str(tvb, offset + 3, 10)); + proto_tree_add_text(tree, tvb, offset + 13, 6, + "End System Identifier: %s", + tvb_bytes_to_str(tvb, offset + 13, 6)); + proto_tree_add_text(tree, tvb, offset + 19, 1, + "Selector: 0x%02X", tvb_get_guint8(tvb, offset + 19)); + break; + + case 0x47: /* ICD ATM format */ + case 0xC5: /* ICD ATM group format */ + proto_tree_add_text(tree, tvb, offset + 0, 3, + "International Code Designator%s: 0x%04X", + (afi == 0xC5) ? " (group)" : "", + tvb_get_ntohs(tvb, offset + 1)); + proto_tree_add_text(tree, tvb, offset + 3, 10, + "High Order DSP: %s", + tvb_bytes_to_str(tvb, offset + 3, 10)); + proto_tree_add_text(tree, tvb, offset + 13, 6, + "End System Identifier: %s", + tvb_bytes_to_str(tvb, offset + 13, 6)); + proto_tree_add_text(tree, tvb, offset + 19, 1, + "Selector: 0x%02X", tvb_get_guint8(tvb, offset + 19)); + break; + + case 0x45: /* E.164 ATM format */ + case 0xC3: /* E.164 ATM group format */ + proto_tree_add_text(tree, tvb, offset + 0, 9, + "E.164 ISDN%s: %s", + (afi == 0xC3) ? " (group)" : "", + tvb_bytes_to_str(tvb, offset + 1, 8)); + proto_tree_add_text(tree, tvb, offset + 9, 4, + "High Order DSP: %s", + tvb_bytes_to_str(tvb, offset + 3, 10)); + proto_tree_add_text(tree, tvb, offset + 13, 6, + "End System Identifier: %s", + tvb_bytes_to_str(tvb, offset + 13, 6)); + proto_tree_add_text(tree, tvb, offset + 19, 1, + "Selector: 0x%02X", tvb_get_guint8(tvb, offset + 19)); + break; + + default: + proto_tree_add_text(tree, tvb, offset, 1, + "Unknown AFI: 0x%02X", afi); + proto_tree_add_text(tree, tvb, offset + 1, len - 1, + "Rest of address: %s", + tvb_bytes_to_str(tvb, offset + 1, len - 1)); + break; + } +} + +/* + * RFC 2225 ATMARP - it's just like ARP, except where it isn't. + */ +static void +dissect_atmarp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + guint16 ar_hrd; + guint16 ar_pro; + guint8 ar_shtl; + guint8 ar_shl; + guint8 ar_sstl; + guint8 ar_ssl; + guint16 ar_op; + guint8 ar_spln; + guint8 ar_thtl; + guint8 ar_thl; + guint8 ar_tstl; + guint8 ar_tsl; + guint8 ar_tpln; + int tot_len; + proto_tree *arp_tree; + proto_item *ti; + gchar *op_str; + int sha_offset, ssa_offset, spa_offset; + int tha_offset, tsa_offset, tpa_offset; + const guint8 *sha_val, *ssa_val, *spa_val; + const guint8 *tha_val, *tsa_val, *tpa_val; + gchar *sha_str, *ssa_str, *spa_str; + gchar *tha_str, *tsa_str, *tpa_str; + proto_tree *tl_tree; + proto_item *tl; + + /* Override the setting to "ARP/RARP". */ + pinfo->current_proto = "ATMARP"; + + ar_hrd = tvb_get_ntohs(tvb, ATM_AR_HRD); + ar_pro = tvb_get_ntohs(tvb, ATM_AR_PRO); + ar_shtl = tvb_get_guint8(tvb, ATM_AR_SHTL); + ar_shl = ar_shtl & ATMARP_LEN_MASK; + ar_sstl = tvb_get_guint8(tvb, ATM_AR_SSTL); + ar_ssl = ar_sstl & ATMARP_LEN_MASK; + ar_op = tvb_get_ntohs(tvb, AR_OP); + ar_spln = tvb_get_guint8(tvb, ATM_AR_SPLN); + ar_thtl = tvb_get_guint8(tvb, ATM_AR_THTL); + ar_thl = ar_thtl & ATMARP_LEN_MASK; + ar_tstl = tvb_get_guint8(tvb, ATM_AR_TSTL); + ar_tsl = ar_tstl & ATMARP_LEN_MASK; + ar_tpln = tvb_get_guint8(tvb, ATM_AR_TPLN); + + tot_len = MIN_ATMARP_HEADER_SIZE + ar_shl + ar_ssl + ar_spln + + ar_thl + ar_tsl + ar_tpln; + + /* Adjust the length of this tvbuff to include only the ARP datagram. + Our caller may use that to determine how much of its packet + was padding. */ + tvb_set_reported_length(tvb, tot_len); + + /* Extract the addresses. */ + sha_offset = MIN_ATMARP_HEADER_SIZE; + if (ar_shl != 0) { + sha_val = tvb_get_ptr(tvb, sha_offset, ar_shl); + sha_str = atmarpnum_to_str(sha_val, ar_shtl); + } else { + sha_val = NULL; + sha_str = "<No address>"; + } + + ssa_offset = sha_offset + ar_shl; + if (ar_ssl != 0) { + ssa_val = tvb_get_ptr(tvb, ssa_offset, ar_ssl); + ssa_str = atmarpsubaddr_to_str(ssa_val, ar_sstl); + } else { + ssa_val = NULL; + ssa_str = NULL; + } + + spa_offset = ssa_offset + ar_ssl; + spa_val = tvb_get_ptr(tvb, spa_offset, ar_spln); + spa_str = arpproaddr_to_str(spa_val, ar_spln, ar_pro); + + tha_offset = spa_offset + ar_spln; + if (ar_thl != 0) { + tha_val = tvb_get_ptr(tvb, tha_offset, ar_thl); + tha_str = atmarpnum_to_str(tha_val, ar_thtl); + } else { + tha_val = NULL; + tha_str = "<No address>"; + } + + tsa_offset = tha_offset + ar_thl; + if (ar_tsl != 0) { + tsa_val = tvb_get_ptr(tvb, tsa_offset, ar_tsl); + tsa_str = atmarpsubaddr_to_str(tsa_val, ar_tstl); + } else { + tsa_val = NULL; + tsa_str = NULL; + } + + tpa_offset = tsa_offset + ar_tsl; + tpa_val = tvb_get_ptr(tvb, tpa_offset, ar_tpln); + tpa_str = arpproaddr_to_str(tpa_val, ar_tpln, ar_pro); + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) { + switch (ar_op) { + + case ARPOP_REQUEST: + case ARPOP_REPLY: + case ATMARPOP_NAK: + default: + col_set_str(pinfo->cinfo, COL_PROTOCOL, "ATMARP"); + break; + + case ARPOP_RREQUEST: + case ARPOP_RREPLY: + col_set_str(pinfo->cinfo, COL_PROTOCOL, "ATMRARP"); + break; + + case ARPOP_IREQUEST: + case ARPOP_IREPLY: + col_set_str(pinfo->cinfo, COL_PROTOCOL, "Inverse ATMARP"); + break; + } + } + + if (check_col(pinfo->cinfo, COL_INFO)) { + switch (ar_op) { + case ARPOP_REQUEST: + col_add_fstr(pinfo->cinfo, COL_INFO, "Who has %s? Tell %s", + tpa_str, spa_str); + break; + case ARPOP_REPLY: + col_add_fstr(pinfo->cinfo, COL_INFO, "%s is at %s%s%s", spa_str, sha_str, + ((ssa_str != NULL) ? "," : ""), + ((ssa_str != NULL) ? ssa_str : "")); + break; + case ARPOP_IREQUEST: + col_add_fstr(pinfo->cinfo, COL_INFO, "Who is %s%s%s? Tell %s%s%s", + tha_str, + ((tsa_str != NULL) ? "," : ""), + ((tsa_str != NULL) ? tsa_str : ""), + sha_str, + ((ssa_str != NULL) ? "," : ""), + ((ssa_str != NULL) ? ssa_str : "")); + break; + case ARPOP_IREPLY: + col_add_fstr(pinfo->cinfo, COL_INFO, "%s%s%s is at %s", + sha_str, + ((ssa_str != NULL) ? "," : ""), + ((ssa_str != NULL) ? ssa_str : ""), + spa_str); + break; + case ATMARPOP_NAK: + col_add_fstr(pinfo->cinfo, COL_INFO, "I don't know where %s is", spa_str); + break; + default: + col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown ATMARP opcode 0x%04x", ar_op); + break; + } + } + + if (tree) { + if ((op_str = match_strval(ar_op, atmop_vals))) + ti = proto_tree_add_protocol_format(tree, proto_arp, tvb, 0, tot_len, + "ATM Address Resolution Protocol (%s)", + op_str); + else + ti = proto_tree_add_protocol_format(tree, proto_arp, tvb, 0, tot_len, + "ATM Address Resolution Protocol (opcode 0x%04x)", ar_op); + arp_tree = proto_item_add_subtree(ti, ett_arp); + + proto_tree_add_uint(arp_tree, hf_arp_hard_type, tvb, ATM_AR_HRD, 2, ar_hrd); + + proto_tree_add_uint(arp_tree, hf_arp_proto_type, tvb, ATM_AR_PRO, 2,ar_pro); + + tl = proto_tree_add_text(arp_tree, tvb, ATM_AR_SHTL, 1, + "Sender ATM number type/length: %s/%u", + (ar_shtl & ATMARP_IS_E164 ? + "E.164" : + "ATM Forum NSAPA"), + ar_shl); + tl_tree = proto_item_add_subtree(tl, ett_atmarp_tl); + proto_tree_add_boolean(tl_tree, hf_atmarp_sht, tvb, ATM_AR_SHTL, 1, ar_shtl); + proto_tree_add_uint(tl_tree, hf_atmarp_shl, tvb, ATM_AR_SHTL, 1, ar_shtl); + + tl = proto_tree_add_text(arp_tree, tvb, ATM_AR_SSTL, 1, + "Sender ATM subaddress type/length: %s/%u", + (ar_sstl & ATMARP_IS_E164 ? + "E.164" : + "ATM Forum NSAPA"), + ar_ssl); + tl_tree = proto_item_add_subtree(tl, ett_atmarp_tl); + proto_tree_add_boolean(tl_tree, hf_atmarp_sst, tvb, ATM_AR_SSTL, 1, ar_sstl); + proto_tree_add_uint(tl_tree, hf_atmarp_ssl, tvb, ATM_AR_SSTL, 1, ar_sstl); + + proto_tree_add_uint(arp_tree, hf_arp_opcode, tvb, AR_OP, 2, ar_op); + + proto_tree_add_uint(arp_tree, hf_atmarp_spln, tvb, ATM_AR_SPLN, 1, ar_spln); + + tl = proto_tree_add_text(arp_tree, tvb, ATM_AR_THTL, 1, + "Target ATM number type/length: %s/%u", + (ar_thtl & ATMARP_IS_E164 ? + "E.164" : + "ATM Forum NSAPA"), + ar_thl); + tl_tree = proto_item_add_subtree(tl, ett_atmarp_tl); + proto_tree_add_boolean(tl_tree, hf_atmarp_tht, tvb, ATM_AR_THTL, 1, ar_thtl); + proto_tree_add_uint(tl_tree, hf_atmarp_thl, tvb, ATM_AR_THTL, 1, ar_thtl); + + tl = proto_tree_add_text(arp_tree, tvb, ATM_AR_TSTL, 1, + "Target ATM subaddress type/length: %s/%u", + (ar_tstl & ATMARP_IS_E164 ? + "E.164" : + "ATM Forum NSAPA"), + ar_tsl); + tl_tree = proto_item_add_subtree(tl, ett_atmarp_tl); + proto_tree_add_boolean(tl_tree, hf_atmarp_tst, tvb, ATM_AR_TSTL, 1, ar_tstl); + proto_tree_add_uint(tl_tree, hf_atmarp_tsl, tvb, ATM_AR_TSTL, 1, ar_tstl); + + proto_tree_add_uint(arp_tree, hf_atmarp_tpln, tvb, ATM_AR_TPLN, 1, ar_tpln); + + if (ar_shl != 0) + dissect_atm_number(tvb, sha_offset, ar_shtl, hf_atmarp_src_atm_num_e164, + hf_atmarp_src_atm_num_nsap, arp_tree); + + if (ar_ssl != 0) + proto_tree_add_bytes_format(arp_tree, hf_atmarp_src_atm_subaddr, tvb, ssa_offset, + ar_ssl, + ssa_val, + "Sender ATM subaddress: %s", ssa_str); + + if (ar_spln != 0) { + proto_tree_add_item(arp_tree, + ARP_PRO_IS_IPv4(ar_pro, ar_spln) ? hf_arp_src_proto_ipv4 + : hf_arp_src_proto, + tvb, spa_offset, ar_spln, FALSE); + } + + if (ar_thl != 0) + dissect_atm_number(tvb, tha_offset, ar_thtl, hf_atmarp_dst_atm_num_e164, + hf_atmarp_dst_atm_num_nsap, arp_tree); + + if (ar_tsl != 0) + proto_tree_add_bytes_format(arp_tree, hf_atmarp_dst_atm_subaddr, tvb, tsa_offset, + ar_tsl, + tsa_val, + "Target ATM subaddress: %s", tsa_str); + + if (ar_tpln != 0) { + proto_tree_add_item(arp_tree, + ARP_PRO_IS_IPv4(ar_pro, ar_tpln) ? hf_arp_dst_proto_ipv4 + : hf_arp_dst_proto, + tvb, tpa_offset, ar_tpln, FALSE); + } + } +} + +static const guint8 mac_broadcast[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const guint8 mac_allzero[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static void +dissect_arp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + guint16 ar_hrd; + guint16 ar_pro; + guint8 ar_hln; + guint8 ar_pln; + guint16 ar_op; + int tot_len; + proto_tree *arp_tree; + proto_item *ti; + gchar *op_str; + int sha_offset, spa_offset, tha_offset, tpa_offset; + const guint8 *sha_val, *spa_val, *tha_val, *tpa_val; + gboolean is_gratuitous; + + /* Call it ARP, for now, so that if we throw an exception before + we decide whether it's ARP or RARP or IARP or ATMARP, it shows + up in the packet list as ARP. + + Clear the Info column so that, if we throw an exception, it + shows up as a short or malformed ARP frame. */ + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "ARP"); + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + /* Hardware Address Type */ + ar_hrd = tvb_get_ntohs(tvb, AR_HRD); + if (ar_hrd == ARPHRD_ATM2225) { + call_dissector(atmarp_handle, tvb, pinfo, tree); + return; + } + /* Protocol Address Type */ + ar_pro = tvb_get_ntohs(tvb, AR_PRO); + /* Hardware Address Size */ + ar_hln = tvb_get_guint8(tvb, AR_HLN); + /* Protocol Address Size */ + ar_pln = tvb_get_guint8(tvb, AR_PLN); + /* Operation */ + ar_op = tvb_get_ntohs(tvb, AR_OP); + + tot_len = MIN_ARP_HEADER_SIZE + ar_hln*2 + ar_pln*2; + + /* Adjust the length of this tvbuff to include only the ARP datagram. + Our caller may use that to determine how much of its packet + was padding. */ + tvb_set_reported_length(tvb, tot_len); + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) { + switch (ar_op) { + + case ARPOP_REQUEST: + case ARPOP_REPLY: + default: + col_set_str(pinfo->cinfo, COL_PROTOCOL, "ARP"); + break; + + case ARPOP_RREQUEST: + case ARPOP_RREPLY: + col_set_str(pinfo->cinfo, COL_PROTOCOL, "RARP"); + break; + + case ARPOP_IREQUEST: + case ARPOP_IREPLY: + col_set_str(pinfo->cinfo, COL_PROTOCOL, "Inverse ARP"); + break; + } + } + + /* Get the offsets of the addresses. */ + /* Source Hardware Address */ + sha_offset = MIN_ARP_HEADER_SIZE; + /* Source Protocol Address */ + spa_offset = sha_offset + ar_hln; + /* Target Hardware Address */ + tha_offset = spa_offset + ar_pln; + /* Target Protocol Address */ + tpa_offset = tha_offset + ar_hln; + + if ((ar_op == ARPOP_REPLY || ar_op == ARPOP_REQUEST) && + ARP_HW_IS_ETHER(ar_hrd, ar_hln) && + ARP_PRO_IS_IPv4(ar_pro, ar_pln)) { + + /* inform resolv.c module of the new discovered addresses */ + + guint ip; + const guint8 *mac; + + /* Add sender address if sender MAC address is neither a broadcast/ + multicast address nor an all-zero address and if sender IP address + isn't all zeroes. */ + tvb_memcpy(tvb, (guint8 *)&ip, spa_offset, sizeof(ip)); + mac = tvb_get_ptr(tvb, sha_offset, 6); + if ((mac[0] & 0x01) == 0 && memcmp(mac, mac_allzero, 6) != 0 && ip != 0) + add_ether_byip(ip, mac); + + /* Add target address if target MAC address is neither a broadcast/ + multicast address nor an all-zero address and if target IP address + isn't all zeroes. */ + tvb_memcpy(tvb, (guint8 *)&ip, tpa_offset, sizeof(ip)); + mac = tvb_get_ptr(tvb, tha_offset, 6); + if ((mac[0] & 0x01) == 0 && memcmp(mac, mac_allzero, 6) != 0 && ip != 0) + add_ether_byip(ip, mac); + } + + if (!tree && !check_col(pinfo->cinfo, COL_INFO)) { + /* We're not building a protocol tree and we're not setting the Info + column, so we don't have any more work to do. */ + return; + } + + sha_val = tvb_get_ptr(tvb, sha_offset, ar_hln); + spa_val = tvb_get_ptr(tvb, spa_offset, ar_pln); + tha_val = tvb_get_ptr(tvb, tha_offset, ar_hln); + tpa_val = tvb_get_ptr(tvb, tpa_offset, ar_pln); + + /* ARP requests with the same sender and target protocol address + are flagged as "gratuitous ARPs", i.e. ARPs sent out as, in + effect, an announcement that the machine has MAC address + XX:XX:XX:XX:XX:XX and IPv4 address YY.YY.YY.YY, to provoke + complaints if some other machine has the same IPv4 address. */ + if ((ar_op == ARPOP_REQUEST) && (memcmp(spa_val, tpa_val, ar_pln) == 0)) + is_gratuitous = TRUE; + else + is_gratuitous = FALSE; + + if (check_col(pinfo->cinfo, COL_INFO)) { + switch (ar_op) { + case ARPOP_REQUEST: + if (is_gratuitous) + col_add_fstr(pinfo->cinfo, COL_INFO, "Who has %s? Gratuitous ARP", + arpproaddr_to_str(tpa_val, ar_pln, ar_pro)); + else + col_add_fstr(pinfo->cinfo, COL_INFO, "Who has %s? Tell %s", + arpproaddr_to_str(tpa_val, ar_pln, ar_pro), + arpproaddr_to_str(spa_val, ar_pln, ar_pro)); + break; + case ARPOP_REPLY: + col_add_fstr(pinfo->cinfo, COL_INFO, "%s is at %s", + arpproaddr_to_str(spa_val, ar_pln, ar_pro), + arphrdaddr_to_str(sha_val, ar_hln, ar_hrd)); + break; + case ARPOP_RREQUEST: + case ARPOP_IREQUEST: + col_add_fstr(pinfo->cinfo, COL_INFO, "Who is %s? Tell %s", + arphrdaddr_to_str(tha_val, ar_hln, ar_hrd), + arphrdaddr_to_str(sha_val, ar_hln, ar_hrd)); + break; + case ARPOP_RREPLY: + col_add_fstr(pinfo->cinfo, COL_INFO, "%s is at %s", + arphrdaddr_to_str(tha_val, ar_hln, ar_hrd), + arpproaddr_to_str(tpa_val, ar_pln, ar_pro)); + break; + case ARPOP_IREPLY: + col_add_fstr(pinfo->cinfo, COL_INFO, "%s is at %s", + arphrdaddr_to_str(sha_val, ar_hln, ar_hrd), + arpproaddr_to_str(spa_val, ar_pln, ar_pro)); + break; + default: + col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown ARP opcode 0x%04x", ar_op); + break; + } + } + + if (tree) { + if ((op_str = match_strval(ar_op, op_vals))) { + if (is_gratuitous) + op_str = "request/gratuitous ARP"; + ti = proto_tree_add_protocol_format(tree, proto_arp, tvb, 0, tot_len, + "Address Resolution Protocol (%s)", op_str); + } else + ti = proto_tree_add_protocol_format(tree, proto_arp, tvb, 0, tot_len, + "Address Resolution Protocol (opcode 0x%04x)", ar_op); + arp_tree = proto_item_add_subtree(ti, ett_arp); + proto_tree_add_uint(arp_tree, hf_arp_hard_type, tvb, AR_HRD, 2, ar_hrd); + proto_tree_add_uint(arp_tree, hf_arp_proto_type, tvb, AR_PRO, 2, ar_pro); + proto_tree_add_uint(arp_tree, hf_arp_hard_size, tvb, AR_HLN, 1, ar_hln); + proto_tree_add_uint(arp_tree, hf_arp_proto_size, tvb, AR_PLN, 1, ar_pln); + proto_tree_add_uint(arp_tree, hf_arp_opcode, tvb, AR_OP, 2, ar_op); + if (ar_hln != 0) { + proto_tree_add_item(arp_tree, + ARP_HW_IS_ETHER(ar_hrd, ar_hln) ? hf_arp_src_hw_mac : hf_arp_src_hw, + tvb, sha_offset, ar_hln, FALSE); + } + if (ar_pln != 0) { + proto_tree_add_item(arp_tree, + ARP_PRO_IS_IPv4(ar_pro, ar_pln) ? hf_arp_src_proto_ipv4 + : hf_arp_src_proto, + tvb, spa_offset, ar_pln, FALSE); + } + if (ar_hln != 0) { + proto_tree_add_item(arp_tree, + ARP_HW_IS_ETHER(ar_hrd, ar_hln) ? hf_arp_dst_hw_mac : hf_arp_dst_hw, + tvb, tha_offset, ar_hln, FALSE); + } + if (ar_pln != 0) { + proto_tree_add_item(arp_tree, + ARP_PRO_IS_IPv4(ar_pro, ar_pln) ? hf_arp_dst_proto_ipv4 + : hf_arp_dst_proto, + tvb, tpa_offset, ar_pln, FALSE); + } + } +} + +void +proto_register_arp(void) +{ + static struct true_false_string type_bit = { "E.164", "ATM Forum NSAPA" }; + + static hf_register_info hf[] = { + { &hf_arp_hard_type, + { "Hardware type", "arp.hw.type", + FT_UINT16, BASE_HEX, VALS(hrd_vals), 0x0, + "", HFILL }}, + + { &hf_arp_proto_type, + { "Protocol type", "arp.proto.type", + FT_UINT16, BASE_HEX, VALS(etype_vals), 0x0, + "", HFILL }}, + + { &hf_arp_hard_size, + { "Hardware size", "arp.hw.size", + FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_atmarp_sht, + { "Sender ATM number type", "arp.src.htype", + FT_BOOLEAN, 8, &type_bit, ATMARP_IS_E164, + "", HFILL }}, + + { &hf_atmarp_shl, + { "Sender ATM number length", "arp.src.hlen", + FT_UINT8, BASE_DEC, NULL, ATMARP_LEN_MASK, + "", HFILL }}, + + { &hf_atmarp_sst, + { "Sender ATM subaddress type", "arp.src.stype", + FT_BOOLEAN, 8, &type_bit, ATMARP_IS_E164, + "", HFILL }}, + + { &hf_atmarp_ssl, + { "Sender ATM subaddress length", "arp.src.slen", + FT_UINT8, BASE_DEC, NULL, ATMARP_LEN_MASK, + "", HFILL }}, + + { &hf_arp_proto_size, + { "Protocol size", "arp.proto.size", + FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_arp_opcode, + { "Opcode", "arp.opcode", + FT_UINT16, BASE_HEX, VALS(op_vals), 0x0, + "", HFILL }}, + + { &hf_atmarp_spln, + { "Sender protocol size", "arp.src.pln", + FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_atmarp_tht, + { "Target ATM number type", "arp.dst.htype", + FT_BOOLEAN, 8, &type_bit, ATMARP_IS_E164, + "", HFILL }}, + + { &hf_atmarp_thl, + { "Target ATM number length", "arp.dst.hlen", + FT_UINT8, BASE_DEC, NULL, ATMARP_LEN_MASK, + "", HFILL }}, + + { &hf_atmarp_tst, + { "Target ATM subaddress type", "arp.dst.stype", + FT_BOOLEAN, 8, &type_bit, ATMARP_IS_E164, + "", HFILL }}, + + { &hf_atmarp_tsl, + { "Target ATM subaddress length", "arp.dst.slen", + FT_UINT8, BASE_DEC, NULL, ATMARP_LEN_MASK, + "", HFILL }}, + + { &hf_atmarp_tpln, + { "Target protocol size", "arp.dst.pln", + FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL }}, + + { &hf_arp_src_hw, + { "Sender hardware address", "arp.src.hw", + FT_BYTES, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_arp_src_hw_mac, + { "Sender MAC address", "arp.src.hw_mac", + FT_ETHER, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_atmarp_src_atm_num_e164, + { "Sender ATM number (E.164)", "arp.src.atm_num_e164", + FT_STRING, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_atmarp_src_atm_num_nsap, + { "Sender ATM number (NSAP)", "arp.src.atm_num_nsap", + FT_BYTES, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_atmarp_src_atm_subaddr, + { "Sender ATM subaddress", "arp.src.atm_subaddr", + FT_BYTES, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_arp_src_proto, + { "Sender protocol address", "arp.src.proto", + FT_BYTES, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_arp_src_proto_ipv4, + { "Sender IP address", "arp.src.proto_ipv4", + FT_IPv4, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_arp_dst_hw, + { "Target hardware address", "arp.dst.hw", + FT_BYTES, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_arp_dst_hw_mac, + { "Target MAC address", "arp.dst.hw_mac", + FT_ETHER, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_atmarp_dst_atm_num_e164, + { "Target ATM number (E.164)", "arp.dst.atm_num_e164", + FT_STRING, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_atmarp_dst_atm_num_nsap, + { "Target ATM number (NSAP)", "arp.dst.atm_num_nsap", + FT_BYTES, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_atmarp_dst_atm_subaddr, + { "Target ATM subaddress", "arp.dst.atm_subaddr", + FT_BYTES, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_arp_dst_proto, + { "Target protocol address", "arp.dst.proto", + FT_BYTES, BASE_NONE, NULL, 0x0, + "", HFILL }}, + + { &hf_arp_dst_proto_ipv4, + { "Target IP address", "arp.dst.proto_ipv4", + FT_IPv4, BASE_NONE, NULL, 0x0, + "", HFILL }} + }; + static gint *ett[] = { + &ett_arp, + &ett_atmarp_nsap, + &ett_atmarp_tl, + }; + + proto_arp = proto_register_protocol("Address Resolution Protocol", + "ARP/RARP", "arp"); + proto_register_field_array(proto_arp, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + atmarp_handle = create_dissector_handle(dissect_atmarp, proto_arp); + + register_dissector( "arp" , dissect_arp, proto_arp ); +} + +void +proto_reg_handoff_arp(void) +{ + dissector_handle_t arp_handle; + + arp_handle = find_dissector("arp"); + + dissector_add("ethertype", ETHERTYPE_ARP, arp_handle); + dissector_add("ethertype", ETHERTYPE_REVARP, arp_handle); + dissector_add("arcnet.protocol_id", ARCNET_PROTO_ARP_1051, arp_handle); + dissector_add("arcnet.protocol_id", ARCNET_PROTO_ARP_1201, arp_handle); + dissector_add("arcnet.protocol_id", ARCNET_PROTO_RARP_1201, arp_handle); + +} |