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!!