diff --git a/httpdocs/inc/mac_stats_id.inc b/httpdocs/inc/mac_stats_id.inc
index b28373abb1..4f965fce4c 100644
--- a/httpdocs/inc/mac_stats_id.inc
+++ b/httpdocs/inc/mac_stats_id.inc
@@ -15,6 +15,8 @@ function mac_table_setID (row) {
// mac_stats_top
row.find("td").eq(index++).attr('id', mac_key+"_hosts");
+ row.find("td").eq(index++).attr('id', mac_key+"_arpsent");
+ row.find("td").eq(index++).attr('id', mac_key+"_arprcvd");
row.find("td").eq(index++).attr('id', mac_key+"_since");
// mac_stats_bottom
@@ -39,6 +41,8 @@ function mac_row_update(mac_key) {
success: function(content) {
var data = jQuery.parseJSON(content);
$("#"+mac_key+'_hosts').html(data.column_hosts);
+ $("#"+mac_key+'_arpsent').html(data.column_arp_sent);
+ $("#"+mac_key+'_arprcvd').html(data.column_arp_rcvd);
$("#"+mac_key+'_alerts').html(data.column_alerts);
$("#"+mac_key+'_since').html(data.column_since);
$("#"+mac_key+'_breakdown').html(data.column_breakdown);
diff --git a/httpdocs/inc/mac_stats_top.inc b/httpdocs/inc/mac_stats_top.inc
index bec924d9a6..514e7a886c 100644
--- a/httpdocs/inc/mac_stats_top.inc
+++ b/httpdocs/inc/mac_stats_top.inc
@@ -8,6 +8,24 @@
},
{
+ title: "ARP Sent",
+ field: "column_arp_sent",
+ sortable: true,
+ css: {
+ textAlign: 'center'
+ }
+
+ },
+ {
+ title: "ARP Received",
+ field: "column_arp_rcvd",
+ sortable: true,
+ css: {
+ textAlign: 'center'
+ }
+
+ },
+ {
title: "Seen Since",
field: "column_since",
sortable: true,
diff --git a/include/Mac.h b/include/Mac.h
index 1713344f72..fc8dbdbb42 100644
--- a/include/Mac.h
+++ b/include/Mac.h
@@ -30,6 +30,7 @@ class Mac : public GenericHashEntry, public GenericTrafficElement {
const char * manuf;
u_int16_t vlan_id;
bool special_mac;
+ ArpStats arp_stats;
public:
Mac(NetworkInterface *_iface, u_int8_t _mac[6], u_int16_t _vlanId);
@@ -50,6 +51,14 @@ class Mac : public GenericHashEntry, public GenericTrafficElement {
last_seen = iface->getTimeLastPktRcvd();
}
inline void incRcvdStats(u_int pkt_len) { rcvd.incStats(pkt_len); }
+
+ inline void incSentArpRequests() { arp_stats.sent_requests++; }
+ inline void incSentArpReplies() { arp_stats.sent_replies++; }
+ inline void incRcvdArpRequests() { arp_stats.rcvd_requests++; }
+ inline void incRcvdArpReplies() { arp_stats.rcvd_replies++; }
+ inline u_int64_t getNumSentArp() { return (u_int64_t)arp_stats.sent_requests + arp_stats.sent_replies; }
+ inline u_int64_t getNumRcvdArp() { return (u_int64_t)arp_stats.rcvd_requests + arp_stats.rcvd_replies; }
+
bool idle();
void lua(lua_State* vm, bool show_details, bool asListElement);
inline char* get_string_key(char *buf, u_int buf_len) { return(Utils::formatMac(mac, buf, buf_len)); }
@@ -57,3 +66,4 @@ class Mac : public GenericHashEntry, public GenericTrafficElement {
};
#endif /* _MAC_H_ */
+
diff --git a/include/ntop_typedefs.h b/include/ntop_typedefs.h
index a5b4d40588..3a90c14b68 100644
--- a/include/ntop_typedefs.h
+++ b/include/ntop_typedefs.h
@@ -244,6 +244,8 @@ typedef enum {
/* Macs */
column_num_hosts,
column_manufacturer,
+ column_arp_sent,
+ column_arp_rcvd
} sortField;
typedef struct {
@@ -253,6 +255,11 @@ typedef struct {
ifOutOctets, ifOutPackets, ifOutErrors;
} sFlowInterfaceStats;
+typedef struct {
+ u_int32_t sent_requests, sent_replies;
+ u_int32_t rcvd_requests, rcvd_replies;
+} ArpStats;
+
typedef struct {
const char *class_name;
const luaL_Reg *class_methods;
diff --git a/scripts/lua/mac_details.lua b/scripts/lua/mac_details.lua
index 3430534005..67b1e59465 100644
--- a/scripts/lua/mac_details.lua
+++ b/scripts/lua/mac_details.lua
@@ -116,7 +116,16 @@ if((mac_info["bytes.sent"]+mac_info["bytes.rcvd"]) > 0) then
end
print("
| Traffic Sent / Received | " .. formatPackets(mac_info["packets.sent"]) .. " / ".. bytesToSize(mac_info["bytes.sent"]) .. " | " .. formatPackets(mac_info["packets.rcvd"]) .. " / ".. bytesToSize(mac_info["bytes.rcvd"]) .. " |
\n")
-
+print([[
+
+ | ARP |
+ Replies |
+ Requests |
+
+
+ | ]]..mac_info["arp_replies.sent"]..[[ replies sent / ]]..mac_info["arp_requests.rcvd"]..[[ requests received |
+ ]]..mac_info["arp_requests.sent"]..[[ requests sent / ]]..mac_info["arp_replies.rcvd"]..[[ replies received |
+
]])
print("")
diff --git a/scripts/lua/modules/mac_utils.lua b/scripts/lua/modules/mac_utils.lua
index 67d1e9e067..32df66181c 100644
--- a/scripts/lua/modules/mac_utils.lua
+++ b/scripts/lua/modules/mac_utils.lua
@@ -22,6 +22,9 @@ function mac2record(mac)
if(manufacturer == nil) then manufacturer = "" end
record["column_manufacturer"] = manufacturer
+ record["column_arp_sent"] = tostring(mac["arp_requests.sent"] + mac["arp_replies.sent"])
+ record["column_arp_rcvd"] = tostring(mac["arp_requests.rcvd"] + mac["arp_replies.rcvd"])
+
record["column_hosts"] = mac["num_hosts"]..""
record["column_since"] = secondsToTime(now - mac["seen.first"]+1)
diff --git a/src/Mac.cpp b/src/Mac.cpp
index fa3762f865..18ba30a845 100644
--- a/src/Mac.cpp
+++ b/src/Mac.cpp
@@ -25,6 +25,7 @@
Mac::Mac(NetworkInterface *_iface, u_int8_t _mac[6], u_int16_t _vlanId) : GenericHashEntry(_iface) {
memcpy(mac, _mac, 6), vlan_id = _vlanId;
+ memset(&arp_stats, 0, sizeof(arp_stats));
special_mac = Utils::isSpecialMac(mac);
if(iface->getTimeLastPktRcvd() > 0)
first_seen = last_seen = iface->getTimeLastPktRcvd();
@@ -122,6 +123,11 @@ void Mac::lua(lua_State* vm, bool show_details, bool asListElement) {
if(manuf)
lua_push_str_table_entry(vm, "manufacturer", (char*)manuf);
+ lua_push_int_table_entry(vm, "arp_requests.sent", arp_stats.sent_requests);
+ lua_push_int_table_entry(vm, "arp_requests.rcvd", arp_stats.rcvd_requests);
+ lua_push_int_table_entry(vm, "arp_replies.sent", arp_stats.sent_replies);
+ lua_push_int_table_entry(vm, "arp_replies.rcvd", arp_stats.rcvd_replies);
+
lua_push_bool_table_entry(vm, "special_mac", special_mac);
((GenericTrafficElement*)this)->lua(vm, show_details);
}
diff --git a/src/NetworkInterface.cpp b/src/NetworkInterface.cpp
index 35af00aa02..c2622abd5a 100644
--- a/src/NetworkInterface.cpp
+++ b/src/NetworkInterface.cpp
@@ -1875,6 +1875,23 @@ bool NetworkInterface::dissectPacket(const struct pcap_pkthdr *h,
if(srcMac) srcMac->incSentStats(rawsize);
if(dstMac) dstMac->incRcvdStats(rawsize);
+ /* Do not count ARP broadcast requests */
+ if (srcMac && dstMac && !dstMac->isSpecialMac()) {
+ const u_int16_t arp_opcode_offset = ip_offset + 6;
+ u_int16_t arp_opcode = 0;
+
+ if ((eth_type == 0x0806 /* ARP */) && (h->len > (u_int16_t)(arp_opcode_offset + 1)))
+ arp_opcode = (packet[arp_opcode_offset] << 8) + packet[arp_opcode_offset + 1];
+
+ if (arp_opcode == 0x1 /* ARP request */) {
+ srcMac->incSentArpRequests();
+ dstMac->incRcvdArpRequests();
+ } else if (arp_opcode == 0x2 /* ARP reply */) {
+ srcMac->incSentArpReplies();
+ dstMac->incRcvdArpReplies();
+ }
+ }
+
incStats(h->ts.tv_sec, eth_type, NDPI_PROTOCOL_UNKNOWN, rawsize,
1, 24 /* 8 Preamble + 4 CRC + 12 IFG */);
break;
@@ -2726,6 +2743,14 @@ static bool mac_search_walker(GenericHashEntry *he, void *user_data) {
r->elems[r->actNumEntries++].stringValue = m->get_manufacturer() ? (char*)m->get_manufacturer() : (char*)"zzz";
break;
+ case column_arp_sent:
+ r->elems[r->actNumEntries++].numericValue = m->getNumSentArp();
+ break;
+
+ case column_arp_rcvd:
+ r->elems[r->actNumEntries++].numericValue = m->getNumRcvdArp();
+ break;
+
default:
ntop->getTrace()->traceEvent(TRACE_WARNING, "Internal error: column %d not handled", r->sorter);
break;
@@ -3156,6 +3181,8 @@ int NetworkInterface::sortMacs(struct flowHostRetriever *retriever,
else if(!strcmp(sortColumn, "column_traffic")) retriever->sorter = column_traffic, sorter = numericSorter;
else if(!strcmp(sortColumn, "column_hosts")) retriever->sorter = column_num_hosts, sorter = numericSorter;
else if(!strcmp(sortColumn, "column_manufacturer")) retriever->sorter = column_manufacturer, sorter = stringSorter;
+ else if(!strcmp(sortColumn, "column_arp_sent")) retriever->sorter = column_arp_sent, sorter = numericSorter;
+ else if(!strcmp(sortColumn, "column_arp_rcvd")) retriever->sorter = column_arp_rcvd, sorter = numericSorter;
else ntop->getTrace()->traceEvent(TRACE_WARNING, "Unknown sort column %s", sortColumn), sorter = numericSorter;
// make sure the caller has disabled the purge!!