1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 04:31:29 +00:00

Use an std::map instead of a custom hash table for MAC address prefix

lookup. The hash table used linear probing which got very slow as the
hash table got full. Using std::map is about 10 times faster. The hash
table was slow enough that it took the majority of the time for me in an
ARP scan of a single address.

# nmap -sP 192.168.0.190
mac_prefix_init took 0.49261 s.
Nmap done: 1 IP address (1 host up) scanned in 0.59 seconds

# nmap -sP 192.168.0.190
mac_prefix_init took 0.04392 s.
Nmap done: 1 IP address (1 host up) scanned in 0.13 seconds

The memory usage of std::map is probably greater. The hash table used
19037 pointers and about 13000 structures of size 8 (on a 32-bit
architecture), or about 176 KB. Assuming the map has left, right, and
parent pointers, and a red-black indicator per node, the usage is 16
bytes per prefix plus 8 bytes for the structure data, or 304 KB total.
But this makes fingerdiff so much faster, I want to leave it in place at
least until this round of OS integration is done.
This commit is contained in:
david
2009-11-11 21:25:07 +00:00
parent 53deb965ce
commit 86d766a34e

View File

@@ -91,6 +91,7 @@
/* $Id$ */ /* $Id$ */
#include <map>
/* Character pool memory allocation */ /* Character pool memory allocation */
#include "MACLookup.h" #include "MACLookup.h"
@@ -101,28 +102,12 @@
extern NmapOps o; extern NmapOps o;
struct MAC_entry { std::map<int, char *> MacTable;
int prefix; /* -1 means none set */
char *vendor;
};
struct MAC_hash_table {
int table_capacity; /* How many members the table can hold */
int table_members; /* How many members it has now */
struct MAC_entry **table;
} MacTable;
static inline int MacCharPrefix2Key(const u8 *prefix) { static inline int MacCharPrefix2Key(const u8 *prefix) {
return (prefix[0] << 16) + (prefix[1] << 8) + prefix[2]; return (prefix[0] << 16) + (prefix[1] << 8) + prefix[2];
} }
/* Hashes the prefix into a position from 0 to table_capacity - 1. Does not
check if the position is free or anything */
static inline int MACTableHash(int prefix, int table_capacity) {
// Maybe I should think about changing this sometime.
return prefix % table_capacity;
}
static void mac_prefix_init() { static void mac_prefix_init() {
static int initialized = 0; static int initialized = 0;
if (initialized) return; if (initialized) return;
@@ -130,14 +115,9 @@ static void mac_prefix_init() {
char filename[256]; char filename[256];
FILE *fp; FILE *fp;
char line[128]; char line[128];
int pfx, pos; int pfx;
char *endptr, *p; char *endptr, *vendor;
int lineno = 0; int lineno = 0;
struct MAC_entry *ME;
MacTable.table_capacity = 19037;
MacTable.table_members = 0;
MacTable.table = (struct MAC_entry **) safe_zalloc(MacTable.table_capacity * sizeof(struct MAC_entry *));
/* Now it is time to read in all of the entries ... */ /* Now it is time to read in all of the entries ... */
if (nmap_fetchfile(filename, sizeof(filename), "nmap-mac-prefixes") != 1){ if (nmap_fetchfile(filename, sizeof(filename), "nmap-mac-prefixes") != 1){
@@ -169,26 +149,17 @@ static void mac_prefix_init() {
/* Now grab the vendor */ /* Now grab the vendor */
while(*endptr && isspace((int) (unsigned char) *endptr)) endptr++; while(*endptr && isspace((int) (unsigned char) *endptr)) endptr++;
assert(*endptr); assert(*endptr);
p = endptr; vendor = endptr;
while(*endptr && *endptr != '\n' && *endptr != '\r') endptr++; while(*endptr && *endptr != '\n' && *endptr != '\r') endptr++;
*endptr = '\0'; *endptr = '\0';
// Create the new MAC_entry if (MacTable.find(pfx) == MacTable.end()) {
ME = (struct MAC_entry *) cp_alloc(sizeof(struct MAC_entry)); MacTable[pfx] = cp_strdup(vendor);
ME->prefix = pfx; } else {
ME->vendor = cp_strdup(p); if (o.debugging > 1)
error("MAC prefix %06X is duplicated in %s; ignoring duplicates.", pfx, filename);
}
if (MacTable.table_members > MacTable.table_capacity * 0.8)
error("WARNING: nmap-mac-prefixes has grown to more than 80%% of our hash table size. MacTable.table_capacity should be increased");
// Now insert it into the table
if (MacTable.table_members >= MacTable.table_capacity)
fatal("No space for further MAC prefixes from nmap-mac-prefixes. Increase MacTable.table_capacity");
pos = MACTableHash(pfx, MacTable.table_capacity);
while (MacTable.table[pos]) pos = (pos + 1) % MacTable.table_capacity;
MacTable.table[pos] = ME;
MacTable.table_members++;
} }
fclose(fp); fclose(fp);
@@ -196,16 +167,14 @@ static void mac_prefix_init() {
} }
static struct MAC_entry *findMACEntry(int prefix) { static const char *findMACEntry(int prefix) {
int pos = MACTableHash(prefix, MacTable.table_capacity); std::map<int, char *>::iterator i;
while (MacTable.table[pos]) {
if (MacTable.table[pos]->prefix == prefix)
return MacTable.table[pos];
pos = (pos + 1) % MacTable.table_capacity;
}
i = MacTable.find(prefix);
if (i == MacTable.end())
return NULL; return NULL;
return i->second;
} }
/* Takes a three byte MAC address prefix (passing the whole MAC is OK /* Takes a three byte MAC address prefix (passing the whole MAC is OK
@@ -213,13 +182,10 @@ static struct MAC_entry *findMACEntry(int prefix) {
NULL is returned if no vendor is found for the given prefix or if there NULL is returned if no vendor is found for the given prefix or if there
is some other error. */ is some other error. */
const char *MACPrefix2Corp(const u8 *prefix) { const char *MACPrefix2Corp(const u8 *prefix) {
struct MAC_entry *ent;
if (!prefix) fatal("%s called with a NULL prefix", __func__); if (!prefix) fatal("%s called with a NULL prefix", __func__);
mac_prefix_init(); mac_prefix_init();
ent = findMACEntry(MacCharPrefix2Key(prefix)); return findMACEntry(MacCharPrefix2Key(prefix));
return (ent)? ent->vendor : NULL;
} }
/* Takes a string and looks through the table for a vendor name which /* Takes a string and looks through the table for a vendor name which
@@ -229,16 +195,17 @@ const char *MACPrefix2Corp(const u8 *prefix) {
is not particularly efficient and so should be rewriteen if it is is not particularly efficient and so should be rewriteen if it is
called often */ called often */
bool MACCorp2Prefix(const char *vendorstr, u8 *mac_data) { bool MACCorp2Prefix(const char *vendorstr, u8 *mac_data) {
std::map<int, char *>::iterator i;
if (!vendorstr) fatal("%s: vendorstr is NULL", __func__); if (!vendorstr) fatal("%s: vendorstr is NULL", __func__);
if (!mac_data) fatal("%s: mac_data is NULL", __func__); if (!mac_data) fatal("%s: mac_data is NULL", __func__);
mac_prefix_init(); mac_prefix_init();
for(int i = 0; i < MacTable.table_capacity; i++ ) { for (i = MacTable.begin(); i != MacTable.end(); i++) {
if (MacTable.table[i]) if (strcasestr(i->second, vendorstr)) {
if (strcasestr(MacTable.table[i]->vendor, vendorstr)) { mac_data[0] = i->first >> 16;
mac_data[0] = MacTable.table[i]->prefix >> 16; mac_data[1] = (i->first >> 8) & 0xFF;
mac_data[1] = (MacTable.table[i]->prefix >> 8) & 0xFF; mac_data[2] = i->first & 0xFF;
mac_data[2] = MacTable.table[i]->prefix & 0xFF;
return true; return true;
} }
} }