/*
* Copyright (C) 1998-2007 Luca Deri <deri@ntop.org>
*
* http://www.ntop.org/
*
* 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.
*/
/*
* NOTE: About the tables in this program and vendortable.h...
*
* IPX SAP
* The official list is maintained by iana at
* http://www.iana.org/assignments/novell-sap-numbers
*
* While there is Makefile code to download and rebuild the structure (make dnsapt sapt),
* as of 01-2003, it doesn't work.
*
* The file format has been changed, the sapt.sed file is lost, and besides it builds a
* saptable.h file which you would manually have to insert into vendor.c.
*
* OTOP, Novell doesn't change things very often.
*
* Vendor
* The official list is updated daily at http://standards.ieee.org/regauth/oui/oui.txt
* The vendor table data does change, frequently, albeit irrelevantly to most of us.
* The ntop version of oui.txt is updated infrequently.
*
* To update (if say, you're seeing a lot of unknown values), download the list:
*
* $ make dnvt
*
* Warning: the mac address list frequently discussed on the 'net at
* http://www.cavebear.com/CaveBear/Ethernet/vendor.html hasn't been
* updated since 1999.
*
* If you want to save (disk) space, an oui.txt customized with only the ones
* you really need is certainly possible.
*
* Special MAC
* The special mac table is referenced - lookupHost() in hash.c calls
* getSpecialMacInfo() in util.c - each time a new (pseudo) local host
* is found.
*
* I (Burton) am unaware of any official or even pseudo-offical list.
*
* A 'special' MAC is one that's assigned (often in an RFC, IEEE standard,
* by consensus or in some other unusual way). It doesn't represent a PHYSICAL
* device or manufacturer.
*
* It's a mess...
*
* For example, 0x0180C2-000000 through -00000f, are reserved in
* "ANSI/IEEE Standard 802.1D (1998 edition)". See page 70 in
* http://standards.ieee.org/getieee802/download/802.1D-1998.pdf
*
* For example, the 2nd bit of a MAC address denotes an LAA, a Locally Assigned
* Address value. See IEEE 802.3-2002 (or earlier versions).
* Because bits are xmitted low to high , this stream, 0100000000000000...
* is MAC address 0x02000000. (See RFC 1638)
*
* These don't represent a manufacturer, but are values arbitrarily
* set by some network admin...
*
* Also, see http://www.iana.org/assignments/ppp-numbers for CF0000xxxxxx
*/
/*
* NOTE: About optimzing hash size...
*
* See the #define TEST_HASHSIZE_xxxxxx's below.
*
* Enabling it may run for a bit, as it tests each possible odd value from
* some low number up to whatever the MAX_ value is in globals-defines.h.
* (Testing will stop if a zero collision value is found).
*
* Testing will produce a running list, showing the best so far.
*
* TEST_HASHSIZE: Testing specialMacHash...wait
* TEST_HASHSIZE: specialMacHash 51 16
* TEST_HASHSIZE: specialMacHash 53 13
* TEST_HASHSIZE: specialMacHash 55 11
* TEST_HASHSIZE: specialMacHash 65 8
* TEST_HASHSIZE: specialMacHash 79 5
* TEST_HASHSIZE: specialMacHash 125 3
* TEST_HASHSIZE: specialMacHash 133 2
* TEST_HASHSIZE: specialMacHash 141 1
* TEST_HASHSIZE: specialMacHash BEST is 0 collisions, size 167
*
* The ipxsap table is referenced only for reporting - so a few collisions
* isn't a huge deal.
*/
typedef struct {
unsigned long ipxsapId;
char* ipxsapName;
} IPXSAPInfo;
#include "ntop.h"
static char* macInputFiles[] = {
"specialMAC.txt",
"oui.txt",
NULL
};
static IPXSAPInfo ipxSAP[] = {
{ 0x0000, "Unknown" },
{ 0x0001, "User" },
{ 0x0002, "User Group" },
{ 0x0003, "Print Queue" },
{ 0x0004, "File server" },
{ 0x0005, "Job server" },
{ 0x0007, "Print server" },
{ 0x0008, "Archive server" },
{ 0x0009, "Archive server" },
{ 0x000a, "Job queue" },
{ 0x000b, "Administration" },
{ 0x0021, "NAS SNA gateway" },
{ 0x0024, "Remote bridge" },
{ 0x0026, "Bridge server" },
{ 0x0027, "TCP/IP gateway" },
{ 0x002d, "Time Synchronization VAP" },
{ 0x002e, "Archive Server Dynamic SAP" },
{ 0x0047, "Advertising print server" },
{ 0x004b, "Btrieve VAP 5.0" },
{ 0x004c, "SQL VAP" },
{ 0x0050, "Btrieve VAP" },
{ 0x0053, "Print Queue VAP" },
{ 0x007a, "TES NetWare for VMS" },
{ 0x0098, "NetWare access server" },
{ 0x009a, "Named Pipes server" },
{ 0x009e, "Portable NetWare Unix" },
{ 0x0107, "NetWare 386" },
{ 0x0111, "Test server" },
{ 0x0133, "NetWare Name Service" },
{ 0x0166, "NetWare management" },
{ 0x023f, "SMS Testing and Development" },
{ 0x026a, "NetWare management" },
{ 0x026b, "Time synchronization" },
{ 0x027b, "NetWare Management Agent" },
{ 0x0278, "NetWare Directory server" },
{ 0x030c, "HP LaserJet / Quick Silver" },
{ 0x0355, "Arcada Software" },
{ 0x0361, "NETINELO" },
{ 0x037e, "Powerchute UPS Monitoring" },
{ 0x03e1, "UnixWare Application Server" },
{ 0x044c, "Archive" },
{ 0x055d, "Attachmate SNA gateway" },
{ 0x0610, "Adaptec SCSI Management" },
{ 0x0640, "NT Server-RPC/GW for NW/Win95 User Level Sec" },
{ 0x064e, "NT Server-IIS" },
{ 0x0810, "ELAN License Server Demo" },
{ 0x8002, "Intel NetPort Print Server" },
{ 0x0000, NULL }
};
IPXSAPInfo* ipxSAPhash[MAX_IPXSAP_NAME_HASH];
/* *********************************** */
static int addIPXSAPTableEntry(IPXSAPInfo* theMacHash[],
IPXSAPInfo* entry,
u_int tableLen) {
u_int idx;
unsigned long ipxsapValue;
int hashLoadCollisions=0;
#ifdef PARM_USE_MACHASH_INVERT
ipxsapValue = 256*256*(unsigned long)(entry->ipxsapId & 0xff)
+ 256*(unsigned long)((entry->ipxsapId >> 8) & 0xff)
+ (unsigned long)((entry->ipxsapId >> 16) & 0xff);
#else
idx = (u_int)(entry->ipxsapId % tableLen);
#endif
idx = (u_int)((u_int)ipxsapValue % tableLen);
#ifdef DEBUG
traceEvent(CONST_TRACE_INFO, "DEBUG: addIPXSAPTableEntry(%06x, %s) gives %ld (mod %d = %d)",
entry->ipxsapId,
entry->ipxsapName,
ipxsapValue,
tableLen,
idx);
#endif
/* Count # of collisions during load - only ONCE per item */
if(theMacHash[idx] != NULL) {
hashLoadCollisions++;
#ifdef DEBUG
traceEvent(CONST_TRACE_INFO, "DEBUG: HashLoad Collision - %d %x '%s",
idx, entry->ipxsapId, entry->ipxsapName);
#endif
}
for(;;) {
if(theMacHash[idx] == NULL) {
theMacHash[idx] = entry;
break;
}
idx = (idx+1)%tableLen;
}
return hashLoadCollisions;
}
/* *********************************** */
char* getSAPInfo(u_int16_t sapInfo, short encodeString) {
u_int idx;
unsigned long ipxsapValue = (unsigned long)sapInfo;
IPXSAPInfo* cursor;
idx = (u_int)((u_int)sapInfo % MAX_IPXSAP_NAME_HASH);
for(;;) {
cursor = ipxSAPhash[idx];
if(ipxSAPhash[idx] == NULL) {
/* Unknown vendor */
return("");
} else if(ipxSAPhash[idx] != NULL) {
if(ipxSAPhash[idx]->ipxsapId == ipxsapValue) {
if(encodeString) {
static char ipxsapName[256];
int a, b;
for(a=0, b=0; ipxSAPhash[idx]->ipxsapName[a] != '\0'; a++)
if(ipxSAPhash[idx]->ipxsapName[a] == ' ') {
ipxsapName[b++] = '&';
ipxsapName[b++] = 'n';
ipxsapName[b++] = 'b';
ipxsapName[b++] = 's';
ipxsapName[b++] = 'p';
ipxsapName[b++] = ';';
} else
ipxsapName[b++] = ipxSAPhash[idx]->ipxsapName[a];
ipxsapName[b] = '\0';
return(ipxsapName);
} else
return(ipxSAPhash[idx]->ipxsapName);
}
}
idx = (idx+1)%MAX_IPXSAP_NAME_HASH;
}
return(""); /* NOTREACHED */
}
/* *********************************** */
static char* getMACInfo(int special, u_char* ethAddress, short encodeString) {
datum key_data, data_data;
static char tmpBuf[96];
char *workBuf;
char etherbuf[LEN_ETHERNET_ADDRESS_DISPLAY];
workBuf = etheraddr_string(ethAddress, etherbuf);
memcpy(&tmpBuf, workBuf, LEN_ETHERNET_ADDRESS_DISPLAY+1);
#ifdef VENDOR_DEBUG
traceEvent(CONST_TRACE_INFO, "VENDOR_DEBUG: %slookup '%s'",
special == 1 ? "special " : "", tmpBuf);
#endif
if(special == TRUE) {
/* Search the database for the specified MAC address - full 48 bit */
key_data.dptr = tmpBuf;
key_data.dsize = strlen(tmpBuf)+1;
#ifdef VENDOR_DEBUG
traceEvent(CONST_TRACE_INFO, "VENDOR_DEBUG: Fetching 48bit '%s'", tmpBuf);
#endif
data_data = gdbm_fetch(myGlobals.macPrefixFile, key_data);
if(data_data.dptr == NULL) {
/* Maybe this is just the initial MAC (e.g 00:11:22) */
if(key_data.dsize > 8) {
key_data.dptr[8] = '\0';
key_data.dsize = 9;
}
}
if((data_data.dptr != NULL) && (((MACInfo*)data_data.dptr)->isSpecial = 's')) {
strncpy(tmpBuf, ((MACInfo*)data_data.dptr)->vendorName, sizeof(tmpBuf));
free(data_data.dptr);
myGlobals.numVendorLookupFound48bit++;
return(tmpBuf);
}
}
/* Try the 24 bit */
tmpBuf[LEN_ETHERNET_VENDOR_DISPLAY-1] = '\0'; /* Mask off left 24 bits */
key_data.dptr = tmpBuf;
key_data.dsize = strlen(tmpBuf)+1;
#ifdef VENDOR_DEBUG
traceEvent(CONST_TRACE_INFO, "VENDOR_DEBUG: Fetching 24bit '%s'", tmpBuf);
#endif
data_data = gdbm_fetch(myGlobals.macPrefixFile, key_data);
if(data_data.dptr != NULL) {
if(((special == TRUE) && (((MACInfo*)data_data.dptr)->isSpecial = 's')) ||
((special == FALSE) && (((MACInfo*)data_data.dptr)->isSpecial != 's'))) {
strncpy(tmpBuf, ((MACInfo*)data_data.dptr)->vendorName, sizeof(tmpBuf));
free(data_data.dptr);
myGlobals.numVendorLookupFound24bit++;
return(tmpBuf);
}
}
/* Hand coded for LAA/Multicast */
if(((ethAddress[5] & 0x01) == 0) && ((ethAddress[6] & 0x01) == 0)) {
/*
This is a dummy MAC that instead contains an IP addresses
in the first four bytes
Example: 0D:68:2B:C1:00:00
*/
return("");
}
/* Hand coded for LAA/Multicast */
if((ethAddress[0] & 0x01) != 0) {
myGlobals.numVendorLookupFoundMulticast++;
return("Multicast");
}
if((ethAddress[0] & 0x02) != 0) {
myGlobals.numVendorLookupFoundLAA++;
return("LAA (Locally assigned address)");
}
traceEvent(CONST_TRACE_NOISY, "MAC prefix '%s' not found in vendor database", tmpBuf);
return("");
}
/* *********************************** */
char* getVendorInfo(u_char* ethAddress, short encodeString) {
char* ret;
if(memcmp(ethAddress, myGlobals.otherHostEntry->ethAddress, LEN_ETHERNET_ADDRESS) == 0)
return("");
ret = getMACInfo(1, ethAddress, encodeString);
myGlobals.numVendorLookupCalls++;
if((ret != NULL) && (ret[0] != '\0'))
return(ret);
else
return("");
}
/* *********************************** */
char* getSpecialMacInfo(HostTraffic* el, short encodeString) {
char* ret = getMACInfo(1, (u_char*)&(el->ethAddress), encodeString);
myGlobals.numVendorLookupSpecialCalls++;
if((ret != NULL) && (ret[0] != '\0'))
return(ret);
else
return("");
}
/* *********************************** */
void createVendorTable(struct stat *dbStat) {
int idx, numRead, numLoaded;
FILE *fd = NULL;
char tmpLine[LEN_GENERAL_WORK_BUFFER];
char tmpMACkey[LEN_ETHERNET_ADDRESS_DISPLAY+1];
char *tmpMAC, *tmpTag1, *tmpTag2, *tmpVendor, *strtokState;
struct macInfo macInfoEntry;
datum data_data, key_data;
u_char compressedFormat;
#ifdef TEST_HASHSIZE_IPXSAP
{
int i, j, best, besti;
traceEvent(CONST_TRACE_ALWAYSDISPLAY, "TEST_HASHSIZE: Testing ipxSAP (%s) from 51 -> %d...wait",
#ifdef PARM_USE_MACHASH_INVERT
"invert",
#else
"normal",
#endif
MAX_IPXSAP_NAME_HASH);
best=99999;
besti=0;
for (i=51; i<=MAX_IPXSAP_NAME_HASH; i += 2) {
j=0;
for(idx=0; ipxSAP[idx].ipxsapName != NULL; idx++)
j += addIPXSAPTableEntry(ipxSAPhash, &ipxSAP[idx], i);
if(j == 0) {
best=0;
besti=i;
break;
} else if( j < best ) {
best = j;
besti = i;
traceEvent(CONST_TRACE_ALWAYSDISPLAY, "TEST_HASHSIZE: ipxSAP %3d %3d", i, j);
}
memset(ipxSAPhash, 0, sizeof(ipxSAPhash));
}
traceEvent(CONST_TRACE_ALWAYSDISPLAY, "TEST_HASHSIZE: ipxSAP BEST is %d collisions, size %d", best, besti);
}
#endif
myGlobals.ipxsapHashLoadSize = sizeof(ipxSAPhash);
for(idx=0; ipxSAP[idx].ipxsapName != NULL; idx++) {
myGlobals.ipxsapHashLoadSize += sizeof(IPXSAPInfo) + strlen(ipxSAP[idx].ipxsapName);
myGlobals.ipxsapHashLoadCollisions +=
addIPXSAPTableEntry(ipxSAPhash, &ipxSAP[idx], MAX_IPXSAP_NAME_HASH);
}
/*
* Ok, so we've loaded the static table.
* Now load the gdbm database for the real stuff
* (database was created and opened in initGdbm() in initialize.c)
*
* Here's a sample entry from oui.txt:
OUI Organization
company_id Organization
Address
00-00-00 (hex) XEROX CORPORATION
000000 (base 16) XEROX CORPORATION
M/S 105-50C
800 PHILLIPS ROAD
WEBSTER NY 14580
* and from (our, specially created) special.txt:
018024 (special 24) Kalpana Etherswitch
0180C2000000 (special 48) Bridge Sp. Tree/OSI Route
* We use the (base 16) as our key and add similar values for special
* mac entries so we could co-mingled them.
*
* Note that any line without the 2nd word of '(base' or '(special' is
* simply ignored - allows comments if you're careful.
*
*/
traceEvent(CONST_TRACE_INFO, "VENDOR: Loading MAC address table.");
for(idx=0; macInputFiles[idx] != NULL; idx++) {
fd=checkForInputFile("VENDOR",
"MAC address table",
macInputFiles[idx],
dbStat,
&compressedFormat);
if(fd != NULL) {
numLoaded=0;
numRead=0;
while(readInputFile(fd,
"VENDOR",
FALSE,
compressedFormat,
5000,
tmpLine, sizeof(tmpLine),
&numRead) == 0) {
myGlobals.numVendorLookupRead++;
if( (strstr(tmpLine, "(base") == NULL) &&
(strstr(tmpLine, "(special") == NULL) ) {
continue;
}
tmpMAC = strtok_r(tmpLine, " \t", &strtokState);
if(tmpMAC == NULL) continue;
tmpTag1 = strtok_r(NULL, " \t", &strtokState);
if(tmpTag1 == NULL) continue;
if((strcmp(tmpTag1, "(base") == 0) || (strcmp(tmpTag1, "(special") == 0)) {
tmpTag2 = strtok_r(NULL, " \t", &strtokState);
if(tmpTag2 == NULL) continue;
tmpVendor = strtok_r(NULL, "\n", &strtokState);
if(tmpVendor == NULL) continue;
/* Skip leading blanks and tabs*/
while ( (tmpVendor[0] == ' ') || (tmpVendor[0] == '\t') ) tmpVendor++;
memset(&macInfoEntry, 0, sizeof(macInfoEntry));
if(strcmp(tmpTag1, "(special") == 0) {
macInfoEntry.isSpecial = 's';
} else {
macInfoEntry.isSpecial = 'r';
}
memcpy(&(macInfoEntry.vendorName[0]),
tmpVendor,
min(strlen(tmpVendor)+1, sizeof(macInfoEntry.vendorName)-1));
data_data.dptr = (void*)(&macInfoEntry);
data_data.dsize = sizeof(macInfoEntry);
tmpMACkey[0]='\0';
strncat(tmpMACkey, tmpMAC, 2);
strncat(tmpMACkey, ":", (sizeof(tmpMACkey) - strlen(tmpMACkey) - 1));
strncat(tmpMACkey, tmpMAC+2, 2);
strncat(tmpMACkey, ":", (sizeof(tmpMACkey) - strlen(tmpMACkey) - 1));
strncat(tmpMACkey, tmpMAC+4, 2);
if(strcmp(tmpTag2, "48)") == 0) {
/* special 48 - full tag */
strncat(tmpMACkey, ":", (sizeof(tmpMACkey) - strlen(tmpMACkey) - 1));
strncat(tmpMACkey, tmpMAC+6, 2);
strncat(tmpMACkey, ":", (sizeof(tmpMACkey) - strlen(tmpMACkey) - 1));
strncat(tmpMACkey, tmpMAC+8, 2);
strncat(tmpMACkey, ":", (sizeof(tmpMACkey) - strlen(tmpMACkey) - 1));
strncat(tmpMACkey, tmpMAC+10, 2);
}
key_data.dptr = tmpMACkey;
key_data.dsize = strlen(tmpMACkey)+1;
if(gdbm_store(myGlobals.macPrefixFile, key_data, data_data, GDBM_REPLACE) != 0) {
traceEvent(CONST_TRACE_WARNING,
"VENDOR: unable to add record '%s': {%d, %s} - skipped",
tmpMACkey, macInfoEntry.isSpecial, macInfoEntry.vendorName);
} else {
numLoaded++;
myGlobals.numVendorLookupAdded++;
if(macInfoEntry.isSpecial == 's')
myGlobals.numVendorLookupAddedSpecial++;
#ifdef VENDOR_DEBUG
traceEvent(CONST_TRACE_INFO, "VENDOR_DEBUG: Added '%s': {%c, %s}",
tmpMACkey, macInfoEntry.isSpecial, macInfoEntry.vendorName);
#endif
}
}
} /* while ! eof */
traceEvent(CONST_TRACE_INFO, "VENDOR: ...loaded %d records", numLoaded);
} else {
traceEvent(CONST_TRACE_INFO,
"VENDOR: ntop continues ok");
}
} /* for macInputFiles */
if (!myGlobals.runningPref.printFcOnly) {
traceEvent(CONST_TRACE_INFO, "Fingerprint: Loading signature file");
fd = checkForInputFile("Fingerprint", "Fingerprint file...",
CONST_OSFINGERPRINT_FILE, NULL, &compressedFormat);
if(fd != NULL) {
char line[384], lineKey[8];
int numEntries=0;
numLoaded = 0;
while(readInputFile(fd, NULL, FALSE, compressedFormat, 0, line, sizeof(line), &numLoaded) == 0) {
if((line[0] == '\0') || (line[0] == '#') || (strlen(line) < 30)) continue;
line[strlen(line)-1] = '\0';
safe_snprintf(__FILE__, __LINE__, lineKey, sizeof(lineKey), "%d", numEntries++);
memset(&key_data, 0, sizeof(key_data));
key_data.dptr = lineKey; key_data.dsize = strlen(key_data.dptr);
memset(&data_data, 0, sizeof(data_data));
data_data.dptr = line; data_data.dsize = strlen(line);
if(gdbm_store(myGlobals.fingerprintFile, key_data, data_data, GDBM_REPLACE) != 0)
traceEvent(CONST_TRACE_ERROR, "While adding %s=%s.", lineKey, line);
}
traceEvent(CONST_TRACE_INFO, "Fingerprint: ...loaded %d records", numEntries);
} else
traceEvent(CONST_TRACE_NOISY, "Unable to find fingeprint signature file.");
}
}
syntax highlighted by Code2HTML, v. 0.9.1