*
* 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.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "packet-arp.h"
#include <epan/etypes.h>
#include <epan/arcnet_pids.h>
+#include <epan/conversation.h>
+#include <epan/prefs.h>
+#include <epan/expert.h>
static int proto_arp = -1;
static int hf_arp_hard_type = -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_arp_packet_storm = -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 dissector_handle_t atmarp_handle;
+
+/* Used for determining if frequency of ARP requests constitute a storm */
+#define STORM 1
+#define NO_STORM 2
+
+/* Preference settings */
+static gboolean global_arp_detect_request_storm = FALSE;
+static guint32 global_arp_detect_request_storm_packets = 30;
+static guint32 global_arp_detect_request_storm_period = 100;
+
+static guint32 arp_request_count = 0;
+static nstime_t time_at_start_of_count;
+
+
/* Definitions taken from Linux "linux/if_arp.h" header file, and from
http://www.iana.org/assignments/arp-parameters
}
}
+/* Take note that a request has been seen */
+void request_seen(packet_info *pinfo)
+{
+ /* Don't count frame again after already recording first time around. */
+ if (p_get_proto_data(pinfo->fd, proto_arp) == 0)
+ {
+ arp_request_count++;
+ }
+}
+
+/* Has storm request rate been exceeded with this request? */
+void check_for_storm_count(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ gboolean report_storm = FALSE;
+
+ if (p_get_proto_data(pinfo->fd, proto_arp) != 0)
+ {
+ /* Read any previous stored packet setting */
+ report_storm = (p_get_proto_data(pinfo->fd, proto_arp) == (void*)STORM);
+ }
+ else
+ {
+ /* Seeing packet for first time - check against preference settings */
+ gint seconds_delta = pinfo->fd->abs_ts.secs - time_at_start_of_count.secs;
+ gint nseconds_delta = pinfo->fd->abs_ts.nsecs - time_at_start_of_count.nsecs;
+ gint gap = (seconds_delta*1000) + (nseconds_delta / 1000000);
+
+ /* Reset if gap exceeds period or -ve gap (indicates we're rescanning from start) */
+ if ((gap > (gint)global_arp_detect_request_storm_period) ||
+ (gap < 0))
+ {
+ /* Time period elapsed without threshold being exceeded */
+ arp_request_count = 1;
+ time_at_start_of_count = pinfo->fd->abs_ts;
+ p_add_proto_data(pinfo->fd, proto_arp, (void*)NO_STORM);
+ return;
+ }
+ else
+ if (arp_request_count > global_arp_detect_request_storm_packets)
+ {
+ /* Storm detected, record and reset start time. */
+ report_storm = TRUE;
+ p_add_proto_data(pinfo->fd, proto_arp, (void*)STORM);
+ time_at_start_of_count = pinfo->fd->abs_ts;
+ }
+ else
+ {
+ /* Threshold not exceeded yet - no storm */
+ p_add_proto_data(pinfo->fd, proto_arp, (void*)NO_STORM);
+ }
+ }
+
+ if (report_storm)
+ {
+ /* Report storm and reset counter */
+ proto_item *ti = proto_tree_add_none_format(tree, hf_arp_packet_storm, tvb, 0, 0,
+ "Packet storm detected (%u packets in < %u ms)",
+ global_arp_detect_request_storm_packets,
+ global_arp_detect_request_storm_period);
+ expert_add_info_format(pinfo, ti,
+ PI_SEQUENCE, PI_NOTE,
+ "ARP packet storm detected (%u packets in < %u ms)",
+ global_arp_detect_request_storm_packets,
+ global_arp_detect_request_storm_period);
+ arp_request_count = 0;
+ }
+}
+
+
/*
* RFC 2225 ATMARP - it's just like ARP, except where it isn't.
*/
guint8 ar_pln;
guint16 ar_op;
int tot_len;
- proto_tree *arp_tree;
+ proto_tree *arp_tree = NULL;
proto_item *ti;
const gchar *op_str;
int sha_offset, spa_offset, tha_offset, tpa_offset;
switch (ar_op) {
case ARPOP_REQUEST:
+ if (global_arp_detect_request_storm)
+ {
+ request_seen(pinfo);
+ }
case ARPOP_REPLY:
default:
col_set_str(pinfo->cinfo, COL_PROTOCOL, "ARP");
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))
+ /* ARP requests/replies 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. Requests are to
+ provoke complaints if some other machine has the same IPv4 address,
+ replies are used to announce relocation of network address, like
+ in failover solutions. */
+ if (((ar_op == ARPOP_REQUEST) || (ar_op == ARPOP_REPLY)) && (memcmp(spa_val, tpa_val, ar_pln) == 0))
is_gratuitous = TRUE;
else
is_gratuitous = FALSE;
switch (ar_op) {
case ARPOP_REQUEST:
if (is_gratuitous)
- col_add_fstr(pinfo->cinfo, COL_INFO, "Who has %s? Gratuitous ARP",
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Gratuitous ARP for %s (Request)",
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(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));
+ if (is_gratuitous)
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Gratuitous ARP for %s (Reply)",
+ arpproaddr_to_str(spa_val, ar_pln, ar_pro));
+ else
+ 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:
if (tree) {
if ((op_str = match_strval(ar_op, op_vals))) {
- if (is_gratuitous)
+ if (is_gratuitous && (ar_op == ARPOP_REQUEST))
op_str = "request/gratuitous ARP";
+ if (is_gratuitous && (ar_op == ARPOP_REPLY))
+ op_str = "reply/gratuitous ARP";
ti = proto_tree_add_protocol_format(tree, proto_arp, tvb, 0, tot_len,
"Address Resolution Protocol (%s)", op_str);
} else
tvb, tpa_offset, ar_pln, FALSE);
}
}
+
+ if (global_arp_detect_request_storm)
+ {
+ check_for_storm_count(tvb, pinfo, arp_tree);
+ }
}
void
{ &hf_arp_dst_proto_ipv4,
{ "Target IP address", "arp.dst.proto_ipv4",
FT_IPv4, BASE_NONE, NULL, 0x0,
+ "", HFILL }},
+
+ { &hf_arp_packet_storm,
+ { "", "arp.packet-storm-detected",
+ FT_NONE, BASE_NONE, NULL, 0x0,
"", HFILL }}
};
+
static gint *ett[] = {
&ett_arp,
&ett_atmarp_nsap,
- &ett_atmarp_tl,
+ &ett_atmarp_tl
};
+ module_t *arp_module;
+
proto_arp = proto_register_protocol("Address Resolution Protocol",
"ARP/RARP", "arp");
proto_register_field_array(proto_arp, hf, array_length(hf));
atmarp_handle = create_dissector_handle(dissect_atmarp, proto_arp);
register_dissector( "arp" , dissect_arp, proto_arp );
+
+ /* Preferences */
+ arp_module = prefs_register_protocol(proto_arp, NULL);
+
+ prefs_register_bool_preference(arp_module, "detect_request_storms",
+ "Detect ARP request storms",
+ "Attempt to detect excessive rate of ARP requests",
+ &global_arp_detect_request_storm);
+
+ prefs_register_uint_preference(arp_module, "detect_storm_number_of_packets",
+ "Number of requests to detect during period",
+ "Number of requests needed within period to indicate a storm",
+ 10, &global_arp_detect_request_storm_packets);
+
+ prefs_register_uint_preference(arp_module, "detect_storm_period",
+ "Detection period (in ms)",
+ "Period in milliseconds during which a packet storm may be detected",
+ 10, &global_arp_detect_request_storm_period);
}
void
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);
-
}