Logo Search packages:      
Sourcecode: lanmap version File versions  Download package

facts.c

/* ex: set tabstop=4 noet: */
/* manage our crappy little shoestring database of facts concerning the network, as reported */
/**
 *
 */
/* TODO: break out reporting logic to its own file */
/* TODO: this file is getting too big and ugly! */

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* memcmp */
#include <time.h> /* time() */
#ifdef WIN32
      #include <process.h> /* ? */
#else
      #include <unistd.h> /* getcwd() */
#endif
#include <math.h> /* log() */
#ifndef WIN32
      #include <sys/file.h>
#endif
#ifdef WIN32
      #include <direct.h> /* getcwd */
#endif
#include <errno.h>
#include <limits.h> /* PATH_MAX */
#include "facts.h"
#include "lanmap.h"
#include "protocols.h"
#include "os_classify.h"
#include "debug.h"
#include "list.h"
#include "lhash.h"
#include "misc.h"

/* finer-grained debugs */
#undef DEBUG_MORE
#define DEBUG_HINT_TREE
#undef DEBUG_IP_TRAFFIC
#undef DEBUG_GATEWAY
#undef DEBUG_PROT_STATS
#undef DEBUG_NO_HINTS /* let us know when there are no hints for an ip */


#define DOT_FOOTER                              "}\n"
#define DOT_NO_DIRECTION
#ifdef DOT_NO_DIRECTION
      #define TRAFFIC_FACTOR(n)           ((n) / 2)
#else
      #define TRAFFIC_FACTOR(n)           (n)
#endif

#define DEBUG_FACTS


#define GRAPH_NODE_OUTSIDE                "Outside" /* name of the "cloud" node */
#define GRAPH_FONT_SIZE_MIN               6

#define KILOBYTE                          1024

extern int Verbose;
extern struct ip Ip_Listening;
extern uint32_t Dev_Mask[IFACE_MAX];
extern time_t Dump_Freq;
extern const struct prot_descr Prot_Descr[];
extern char Image_Type[];
extern char Output_Dir[];
extern char Run_Program[];

/* control dumping frequency */
static time_t Last_Dump = 0;


/**
 * indicators of certain operating systems by activity
 */
static const hint HINTS_POS[] = {
      /*id                                                  certainty   len   os    */
      { HINT_NONE,                                          0,                0,    { { OS_NONE, 0, OS_NONE } } },
      /* MAC */
      { HINT_MAC_VEND_APPLE,                          0,                2,    { { OS_MACOS, 80, OS_NONE }, { OS_OSX, 80, OS_NONE } } },
      /* ARP */
      { HINT_ARP_,                                          0,                1,    { { OS_NONE, 50, OS_NONE } } },
      /* BOOTP */
      /* "vendor class" field stuff... how easily fakeable is this? we give it a low score anyways, it
       * should be enough to push one ahead of the other, but only if they're tied */
      { HINT_BOOTP_VENDOR_MACOS,                      0,                1,    { { OS_MACOS, 20, OS_NONE } } },
      { HINT_BOOTP_VENDOR_MACOS_9,              0,                1,    { { OS_MACOS_9X, 30, OS_NONE } } },
      { HINT_BOOTP_VENDOR_MACOS_92,             0,                1,    { { OS_MACOS_92X, 30, OS_NONE } } },
      { HINT_BOOTP_VENDOR_MACOS_922,                  0,                1,    { { OS_MACOS_922, 30, OS_NONE } } },
      { HINT_BOOTP_VENDOR_MSFT_50,              0,                1,    { { OS_WIN_NT_50_X, 30, OS_NONE } } },
      { HINT_BOOTP_VENDOR_MSFT_51,              0,                1,    { { OS_WIN_NT_51_X, 30, OS_NONE } } },
};
#define HINTS_POS_COUNT                   (sizeof HINTS_POS / sizeof HINTS_POS[0])

/**
 * if something *ISN'T* there we can take away points for it... UNUSED SO FAR
 */
static const hint HINTS_NEG[] = {
      /*id                                                  certainty   len   os    */
      { HINT_NONE,                                          0,                0,    { { OS_NONE, 0, OS_NONE } } },
      /* MAC */
      { HINT_MAC_VEND_APPLE,                          0,                2,    { { OS_MACOS, -20, OS_NONE }, { OS_OSX, -20, OS_NONE } } },
};
#define HINTS_NEG_COUNT                   (sizeof HINTS_NEG / sizeof HINTS_NEG[0])


/**
 * TCP SYN packet fingerprints... many operating systems are identifiable by their TCP SYN packets alone
 * @note exported
 */

const struct tcp_sig TCP_SYN_PRINTS[] = {
/*    ttl   df    window      hdl   ts    ws    optsopt[]
 *          oslen os[oslen][3] */

/* * * * Windows * * * */

/* * WinNT 4 * */

{     128,1,      TCP_MULT_MTU | 31,      24,   -1,   -1,   1,    { TCP_OPT_MSS },
            1, { { OS_WIN_NT_40_SP6A, 90, OS_WIN_NT_40_SP6A } } },
{     128,1,      64512,      24,   -1,   -1,   1,    { TCP_OPT_MSS },
            1, { { OS_WIN_NT_40_SP6A, 90, OS_WIN_NT_40_SP6A } } },
/* Windows NT 4 SP1 */
{     128,1,      8192, 24,   -1,   -1,   1,    { TCP_OPT_MSS },
            1, { { OS_WIN_NT_40, 90, OS_WIN_NT_40_SP6 } } },

/* 10.43.111.201(), 10.43.97.23(), 10.43.97.45(), 10.43.97.76(carterman), 10.43.97.45() */
{     128,1,      65535,      28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
      3, { { OS_WIN_NT_50_SP2, 90,  OS_WIN_NT_50_SP2 }, { OS_WIN_NT_51_SP1, 90, OS_WIN_NT_51_SP2 }, { OS_WIN_NT_52, 90, OS_WIN_NT_52 } } },
{     128,1,      TCP_MULT_MOD | 8192,28, -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
      2, { { OS_WIN_NT_50_SP2, 90, OS_NONE }, { OS_WIN_NT_51_SP1, 90, OS_WIN_NT_51_SP1 } } },
{     128,1,      TCP_MULT_MSS | 20,      28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
      1, { { OS_WIN_NT_50_SP3, 90, OS_WIN_NT_50_SP3 } } },
{     128,1,      TCP_MULT_MSS | 45,      28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
      2, { { OS_WIN_NT_50_SP4, 90, OS_WIN_NT_50_SP4 }, { OS_WIN_NT_51_SP1, 90, OS_WIN_NT_51_SP1 } } },
{     128,1,      40320,      28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
      1, { { OS_WIN_NT_50_SP4, 90, OS_WIN_NT_50_SP4 } } },

/* * * Windows XP * * */

{     128,1,      TCP_MULT_MSS | 6, 28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            2, { { OS_WIN_NT_51, 90, OS_WIN_NT_51 }, { OS_WIN_NT_50_SP2, 90, OS_NONE } } },
{     128,1,      TCP_MULT_MSS | 12,      28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            1, { { OS_WIN_NT_51_SP1, 90, OS_WIN_NT_51_SP1 } } },
{     128,1,      TCP_MULT_MSS | 44,      28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            2, { { OS_WIN_NT_51_SP1, 90, OS_WIN_NT_51_SP1 }, { OS_WIN_NT_50_SP3, 90, OS_WIN_NT_50_SP3 } } },
/* 10.43.97.1(kevin), 10.43.97.68(crushbone), 10.43.97.38(jreynolds) */
{     128,1,      64512,      28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            2, { { OS_WIN_NT_51_SP1, 90, OS_WIN_NT_51_SP1 }, { OS_WIN_NT_50_SP3, 90, OS_WIN_NT_50_SP3 } } },
/* 10.43.97.13 */
{     128,1,      32767,      40,   0,    -1,   7,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            1, { { OS_WIN_NT_51_SP2, 90, OS_WIN_NT_51_SP2 } } },
{     128,1,      32768,      28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            1, { { OS_WIN_NT_51_SP2, 90, OS_WIN_NT_51_SP2 } } },
/* my WinXP SP2 + firewall machine */
/* 10.44.22.201 TCP_SYN: ttl:128,df:1,window:65535,hdl:40,ts:0,ws:-1,opts:7(MSS,NOP,NOP,TS,NOP,NOP,SACK)(mss:1460) */
{     128,1,      65535,      40,   0,    -1,   7,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            1, { { OS_WIN_NT_51_SP2, 90, OS_WIN_NT_51_SP2 } } },

/* * * Windows 2003 * * */

{     32,   1,    32768,      32,   -1,   0,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
      1, { { OS_WIN_NT_52_X, 90, OS_NONE } } },


/* * * * Linux * * * */

/*    ttl   df    window      hdl   ts    ws    opts  opt[]
 *          oslen os[oslen][3] */

{     64,   0,    512,  24,   -1,   -1,   0,    { 0 },
            1, { { OS_LINUX_20X, 90, OS_LINUX_20X } } },
{     64,   0,    16384,      24,   -1,   -1,   0,    { 0 },
            1, { { OS_LINUX_20X, 90, OS_LINUX_20X } } },

{     64,   1,    TCP_MULT_MSS | 20,      40,   1,    0,    5,    { TCP_OPT_MSS, TCP_OPT_SACK, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_WS },
            1, { { OS_LINUX_22X, 90, OS_LINUX_22X } } },
{     64,   1,    TCP_MULT_MSS | 22,      40,   1,    0,    5,    { TCP_OPT_MSS, TCP_OPT_SACK, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_WS },
            1, { { OS_LINUX_22X, 90, OS_LINUX_22X } } },
{     64,   1,    TCP_MULT_MSS | 11,      40,   1,    0,    5,    { TCP_OPT_MSS, TCP_OPT_SACK, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_WS },
            1, { { OS_LINUX_22X, 90, OS_LINUX_22X } } },

{     64,   1,    TCP_MULT_MSS | 2, 40,   1,    0,    5,    { TCP_OPT_MSS, TCP_OPT_SACK, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_WS },
            1, { { OS_LINUX_24X, 90, OS_LINUX_24X } } },
{     64,   1,    TCP_MULT_MSS | 3, 40,   1,    0,    5,    { TCP_OPT_MSS, TCP_OPT_SACK, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_WS },
            1, { { OS_LINUX_24X, 90, OS_LINUX_24X } } },
{     64,   1,    TCP_MULT_MSS | 4, 40,   1,    0,    5,    { TCP_OPT_MSS, TCP_OPT_SACK, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_WS },
            1, { { OS_LINUX_2418, 90, OS_LINUX_25X } } },

{     64,   1,    TCP_MULT_MSS | 3, 40,   1,    1,    5,    { TCP_OPT_MSS, TCP_OPT_SACK, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_WS },
            1, { { OS_LINUX_25X, 90, OS_LINUX_25X } } },
{     64,   1,    TCP_MULT_MSS | 3, 40,   1,    2,    5,    { TCP_OPT_MSS, TCP_OPT_SACK, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_WS },
            1, { { OS_LINUX_25X, 90, OS_LINUX_25X } } },
{     64,   1,    TCP_MULT_MSS | 4, 40,   1,    1,    5,    { TCP_OPT_MSS, TCP_OPT_SACK, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_WS },
            1, { { OS_LINUX_25X, 90, OS_LINUX_25X } } },
{     64,   1,    TCP_MULT_MSS | 4, 40,   1,    2,    5,    { TCP_OPT_MSS, TCP_OPT_SACK, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_WS },
            2, { { OS_LINUX_25X, 60, OS_LINUX_25X } , { OS_LINUX_26X, 60, OS_LINUX_26X } } },
{     64,   1,    TCP_MULT_MSS | 4, 40,   1,    7,    5,    { TCP_OPT_MSS, TCP_OPT_SACK, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_WS },
            1, { { OS_LINUX_268, 60, OS_NONE } } },

/* 192.168.1.107 (Unknown) fingerprint: ttl:64(64),window:65535,hdl:40,ts:1,ws:0,opts:6(MSS,NOP,WS,NOP,NOP,TS,)(mss:1460) */
{     64, 1,      65535,      40,   1,    0,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            1, { { OS_OSX_1 , 90, OS_OSX_4 } } },

/* OpenBSD sigs updated from /etc/pf.os on OpenBSD */
{     64,   0,    16384,      40,   1,    0,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            2, { { OS_OPENBSD_26, 90, OS_OPENBSD_26 }, { OS_NETBSD_13, 90, OS_NETBSD_13 } } },

/* OpenBSD slice.my.domain 3.7 GENERIC#50 i386 Intel Pentium III ("GenuineIntel" 686-class, 128KB L2 cache */
{     64,   1,    16384,      44,   1,    0,    9,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            1, { { OS_OPENBSD_3X, 90, OS_OPENBSD_3X } } },
{     64,   0,    16384,      44,   1,    0,    9,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            1, { { OS_OPENBSD_3X, 90, OS_OPENBSD_3X } } }, /* NOTE: no df, pf scrub */

{     64,   1,    57344,      44,   1,    0,    9,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            1, { { OS_OPENBSD_33, 90, OS_OPENBSD_35 } } },
{     64,   0,    57344,      44,   1,    0,    9,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            1, { { OS_OPENBSD_33, 90, OS_OPENBSD_35 } } }, /* NOTE: no df, pf scrub */

/* 10.43.111.1 fingerprint: ttl:64,df:1,window:65535,hdl:44,ts:1,ws:0,opts:9(MSS,NOP,NOP,SACK,NOP,WS,NOP,NOP,TS)(mss:1460) */
{     64,   1,    65535,      44,   1,    0,    9,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            1, { { OS_OPENBSD_3X, 90, OS_OPENBSD_3X } } },

/*    ttl   df    window      hdl   ts    ws    optsopt[]
 *          oslen os[oslen][3] */

/* * * FreebSD 4.11-RELEASE * * */
/* * * DragonFlyBSD 1.0A (inherited) * * */
/* 3 initial... */
{     64,   1,    57344,      40,   1,    0,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            1, { { OS_FREEBSD_4X, 90, OS_FREEBSD_5X } } },
/* 5 subsequent */
{     64,   1,    57344,      24,   -1,   -1,   1,    { TCP_OPT_MSS },
            1, { { OS_FREEBSD_4X, 90, OS_FREEBSD_5X } } },

/* * * DragonFlyBSD 1.2 * * */
/* 192.168.1.102 fingerprint: ttl:64,df:0,window:57344,hdl:44,ts:1,ws:0,opts:9(MSS,NOP,WS,NOP,NOP,SACK,NOP,NOP,TS,) */
{     64,   0,    57344,      44,   1,    0,    9,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            1, { { OS_DFLYBSD_12, 90, OS_DFLYBSD_12 } } },

/* * * FreeBSD 5.3-RELEASE * * */
/* 3 initial... */
{     64,   1,    65535,      44,   1,    1,    9,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            1, { { OS_FREEBSD_5X, 1, OS_FREEBSD_5X } } },
/* 5 subsequent */
{     64,   1,    65535 /* 131070 scaled */,    28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            1, { { OS_FREEBSD_5X, 1, OS_FREEBSD_5X } } },


/* * * Netware 6.0 SP5 - thanks dooky * * */
{     128,1,      6144, 32,   -1,   0,    6,    { TCP_OPT_MSS, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_SACK, TCP_OPT_NOP, TCP_OPT_NOP },
            1, { { OS_NETWARE_6X, 90, OS_NETWARE_6X } } },
/* Netware 5.X */
{     128,1,      16384,      24,   -1,   -1,   0,    { 0 },
            1, { { OS_NETWARE_5X, 90, OS_NETWARE_5X } } },

/*    ttl   df    window      hdl   ts    ws    optsopt[]
 *          oslen os[oslen][3] */
/* FreeBSD 4.x... */
{     64,   1,    16384,      24,   -1,   -1,   0,    { 0 },
            1, { { OS_FREEBSD_2X, 40, OS_FREEBSD_42 } } },
{     64,   1,    16384,      40,   1,    0,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS, },
            1, { { OS_FREEBSD_44, 80, OS_FREEBSD_44 } } },
{     64,   1,    1024, 40,   1,    0,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS, },
            1, { { OS_FREEBSD_44, 80, OS_FREEBSD_44 } } },
{     64,   1,    57344,      24,   1,    -1,   0,    { 0 },
            1, { { OS_FREEBSD_46, 80, OS_FREEBSD_48 } } },
{     64,   1,    57344,      40,   1,    0,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS, },
            1, { { OS_FREEBSD_46, 80, OS_FREEBSD_48 } } },
{     64,   1,    32768,      40,   1,    0,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS, },
            1, { { OS_FREEBSD_46, 80, OS_FREEBSD_48 } } },
/* FreeBSD 4.8->,5X->5.1 */
/* OS X.1 -> (inherited) */
{     64, 1,      32768,      40,   1,    0,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            3, { { OS_FREEBSD_48, 60, OS_NONE }, { OS_FREEBSD_5X, 60, OS_FREEBSD_51 }, { OS_OSX , 60, OS_NONE } } },
/* 10.43.97.41 (Unknown) fingerprint: ttl:64,df:1,window:65535,hdl:40,ts:1,ws:1,opts:6(MSS,NOP,WS,NOP,NOP,TS,) */
/* nmap says: OS details: Apple Mac OS X 10.3.0 - 10.3.3 */
{     64, 1,      65535,      40,   0,    1,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            3, { { OS_FREEBSD_48, 60, OS_NONE }, { OS_FREEBSD_5X, 60, OS_FREEBSD_51 }, { OS_OSX , 60, OS_NONE } } },
{     64, 1,      65535,      40,   1,    1,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            3, { { OS_FREEBSD_48, 60, OS_NONE }, { OS_FREEBSD_5X, 60, OS_FREEBSD_51 }, { OS_OSX , 60, OS_NONE } } },

/* FreeBSD 5... uses a zero IP id? */


/* Solaris 10 x86 */
{     64, 1,      49640,      32,   -1,   1,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            1, { { OS_SOLARIS_10, 90, OS_SOLARIS_10 } } },


/* * * * * * * * * * * * * * EMBEDDED DEVICES * * * * * * * * * * * * * * * */

/*    ttl   df    window      hdl   ts    ws    optsopt[]
 *          oslen os[oslen][3] */

/* Moxa Technologies NPort Express Serial<->Ethernet Bridges */
/* 10.43.97.10 fingerprint: ttl:64,df:0,window:0,hdl:24,ts:-1,ws:-1,opts:1(MSS)(mss:1460) */
{     64, 0,      0,          24,   -1,   -1,   1,    { TCP_OPT_MSS }, /* zero window, wtf? */
            1, { { OS_MOXA_NPORT_EXP, 40, OS_NONE } } },
{     64, 0,      4096, 24,   -1,   -1,   1,    { TCP_OPT_MSS }, /* pretty generic... */
            1, { { OS_MOXA_NPORT_EXP, 20, OS_NONE } } },

/* Symbol Technologies Spectrum24 Client Bridge CB-1000-0000-US */
/* 10.43.96.198 fingerprint: ttl:128,df:0,window:8192,hdl:24,ts:-1,ws:-1,opts:1(MSS)(mss:1460) */
{     128,0,      8192, 24,   -1,   -1,   1,    { TCP_OPT_MSS }, /* NOTE: real TTL is 120 but we guess powers of 2 :/ */
            1, { {  OS_SYMBOL_SPEC24, 20, OS_NONE } } },

/* Atop GW21-SW MAXI */
/*  10.44.22.100 TCP_SYN: ttl:64,df:0,window:1560,hdl:24,ts:-1,ws:-1,opts:1(MSS)(mss:1460) */
{     64,0, 1560, 24,   -1,   -1,   1,    { TCP_OPT_MSS },
            1, { {  OS_ATOP_GW, 20, OS_NONE } } },

/* Perle IOLan */
/* TCP_SYN (no match)     10.44.26.12 ttl:64,df:0,window:1536,hdl:24,ts:-1,ws:-1,opts:1(MSS)(mss:768) */
{     64,   0,    1536, 24,   -1,   -1,   1,    { TCP_OPT_MSS },
            1, { { OS_PERLE_IOLAN, 20, OS_PERLE_IOLAN } } },

/* 3Com Wireless Workgroup Bridge (WWB) */
/* TCP_SYN (no match)     10.43.97.73 ttl:64,df:0,window:8192,hdl:40,ts:1,ws:0,opts:6(MSS,NOP,WS,NOP,NOP,TS)(mss:1460) */
{     64,   0,    8192, 40,   1,    0,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            1, { { OS_3COM_WWB, 20, OS_3COM_WWB } } },

/* nmap scan... maybe do something with this, maybe not */
/* 10.43.97.5 TCP_SYN: ttl:64,df:0,window:3072,hdl:20,ts:-1,ws:-1,opts:0()(mss:-1) */
/* TCP_SYN (no match)   192.168.1.105 ttl:64,df:0,window:3072,hdl:40,ts:1,ws:10,opts:6(WS,NOP,MSS,TS,END,END)(mss:265) */
{     64,0, TCP_MULT_MOD | 1024,    20,   -1,   -1,   0,    { 0 },
            0, { {  OS_NONE, 0, OS_NONE } } },
{     64,0, TCP_MULT_MOD | 1024,    40,   -1,   -1,   6,    { TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_MSS, TCP_OPT_TS, TCP_OPT_END, TCP_OPT_END },
            0, { {  OS_NONE, 0, OS_NONE } } },


/* * * * * * * * * * * * * unknowns * * * * * * * * * * * * * */

#if 0



/* TCP_SYN (no match)     10.43.97.73 ttl:64,df:0,window:8192,hdl:40,ts:1,ws:0,opts:6(MSS,NOP,WS,NOP,NOP,TS)(mss:1460) */


/*    ttl   df    window      hdl   ts    ws    optsopt[]
 *          oslen os[oslen][3] */

/* 66.187.129.2 fingerprint: ttl:48,window:512,hdl:24,mss:1460,ts:-1,ws:-1,opts:1(MSS,) */
{     64,   -1,   512,  24,   -1,   -1,   1,    { TCP_OPT_MSS },
            0, { { 0 } } },
/* 64.14.48.135 fingerprint: ttl:52,window:4096,hdl:44,mss:1460,ts:-1,ws:0,opts:9(MSS,NOP,NOP,SACK,NOP,WS,NOP,NOP,TS,) */
{     64,   -1,   4096, 44,   1,    0,    9,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            0, { { 0 } } },
/* 63.236.215.21 fingerprint: ttl:50(64),window:5440,hdl:40,ts:1,ws:0,opts:5(MSS,SACK,TS,NOP,WS,)(mss:1360 */
{     64,   -1,   5440, 40,   1,    0,    5,    { TCP_OPT_MSS, TCP_OPT_SACK, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_WS },
            0, { { 0 } } },
/* 64.233.184.202 fingerprint: ttl:45(64),window:5720,hdl:40,mss:1430,ts:1,ws:0,opts:5(MSS,SACK,TS,NOP,WS,) */
/* who are these guys, they connect to us... */
{     64,   -1,   5720, 40,   1,    0,    5,    { TCP_OPT_MSS, TCP_OPT_SACK, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_WS },
            0, { { 0 } } },
/* 208.0.20.2 */
{     -1,   -1,   5840, 32,   -1,   1,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK, TCP_OPT_NOP, TCP_OPT_WS },
            0, { { 0 } } },
/*  64.12.116.70 fingerprint: ttl:48(64),window:5840,hdl:32,ts:-1,ws:0,opts:6(MSS,NOP,NOP,SACK,NOP,WS,)(mss:1460) */

/* 69.177.109.14 (ttl:48) */
/* 12.107.209.250 fingerprint: ttl:51,window:5840,hdl:40,mss:1460,ts:1,ws:0,opts:5(MSS,SACK,TS,NOP,WS,) */
/* 69.177.109.14 */
{     64,   1,    5840, 20,   1,    2,    5,    { TCP_OPT_MSS, TCP_OPT_SACK, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_WS },
            0, { { 0 } } },
{     64,   1,    5840, 40,   1,    0,    5,    { TCP_OPT_MSS, TCP_OPT_SACK, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_WS },
            0, { { 0 } } },
/* 69.8.185.6 fingerprint: ttl:49,window:5840,hdl:28,mss:1460,ts:-1,ws:2,opts:3(MSS,NOP,WS,) */
{     64,   -1,   5840, 28,   -1,   2,    3,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS },
            0, { { 0 } } },
/* 69.8.185.5 fingerprint: ttl:49,window:5840,hdl:28,mss:1460,ts:-1,ws:2,opts:3(MSS,NOP,WS,) */
{     64,   -1,   5840, 28,   -1,   2,    3,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS },
            0, { { 0 } } },
/* 209.59.181.199 fingerprint: ttl:49(64),window:5840,hdl:24,ts:-1,ws:-1,opts:1(MSS,)(mss:1460) */
{     64,   -1,   5840, 24,   -1,   -1,   1,    { TCP_OPT_MSS },
            0, { { 0 } } },
/* 167.7.251.168 fingerprint: ttl:108,window:6144,hdl:32,mss:1380,ts:-1,ws:0,opts:6(MSS,WS,NOP,SACK,NOP,NOP,) */
{     128,-1,     6144, 32,   -1,   0,    6,    { TCP_OPT_MSS, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_SACK, TCP_OPT_NOP, TCP_OPT_NOP },
            0, { { 0 } } },
/* 24.232.246.170 fingerprint: ttl:108(128),window:8192,hdl:28,mss:1460,ts:-1,ws:-1,opts:4(MSS,NOP,NOP,SACK,) */
{     128,-1,     8192, 28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            0, { { 0 } } },
/* 205.183.255.215 fingerprint: ttl:241(256),window:8760,hdl:24,mss:1460,ts:-1,ws:-1,opts:1(MSS,) */
{     256,-1,     8760, 24,   -1,   -1,   1,    { TCP_OPT_MSS },
            0, { { 0 } } },
/* 220.162.40.67 fingerprint: ttl:46(64),window:14600,hdl:28,ts:-1,ws:-1,opts:4(MSS,NOP,NOP,SACK,)(mss:1414) */
{     64,   -1,   14600,      28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
        0, { { 0 } } },
/* 64.69.123.130 */
{     -1,   -1,   16384,      44,   1,    0,    9,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
       0, { { 0 } } },
/* 69.60.101.46 fingerprint: ttl:64,df:1,window:16384,hdl:24,ts:-1,ws:-1,opts:1(MSS,)(mss:1460) */
{     64,   1,    16384,      24,   -1,   -1,   1,    { TCP_OPT_MSS },
       0, { { 0 } } },
/*    72.11.147.22 fingerprint: ttl:48(64),window:16384,hdl:40,mss:1460,ts:1,ws:0,opts:6(MSS,NOP,WS,NOP,NOP,TS,) */
{     64,   -1,   16384,      40,   1,    0,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            0, { { 0 } } },
/* 206.15.100.16 fingerprint: ttl:53(64),window:16384,hdl:28,ts:-1,ws:-1,opts:4(MSS,NOP,NOP,SACK,)(mss:1380) */
{     64,   -1,   16384,      28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_SACK },
            0, { { 0 } } },
/*   207.69.195.71 fingerprint: ttl:51,window:24820,hdl:28,mss:1460,ts:-1,ws:-1,opts:4(NOP,NOP,SACK,MSS,) */
{     64,   -1,   24820,      28,   -1,   -1,   4,    { TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK, TCP_OPT_MSS },
            0, { { 0 } } },
/*   85.152.176.81 fingerprint: ttl:115,window:25200,hdl:28,mss:1460,ts:-1,ws:-1,opts:4(MSS,NOP,NOP,SACK,) */
{     128,-1,     24820,      28,   -1,   -1,   4,    { TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            0, { { 0 } } },
/*   68.76.109.135 fingerprint: ttl:110(128),window:25200,hdl:28,mss:1260,ts:-1,ws:-1,opts:4(MSS,NOP,NOP,SACK,) */
{     128,-1,     25200,      28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            0, { { 0 } } },
/*  65.77.106.36 (Unknown) fingerprint: ttl:64,df:1,window:32120,hdl:40,ts:1,ws:0,opts:5(MSS,SACK,TS,NOP,WS,)(mss:1460) */
/* NOTE: 32120/1460 -> 22 */
{     64,   1,    32120,      40,   1,    0,    5,    { TCP_OPT_MSS, TCP_OPT_SACK, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_WS },
            0, { { 0 } } },
/*   213.13.237.38 fingerprint: ttl:116,window:32768,hdl:28,mss:1452,ts:-1,ws:-1,opts:4(MSS,NOP,NOP,SACK,) */
{     128,-1,     32768,      28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            0, { { 0 } } },
/*     10.43.97.65 fingerprint: ttl:128(128),window:32768,hdl:28,mss:1460,ts:-1,ws:-1,opts:4(MSS,NOP,NOP,SACK,) */
{     128,-1,     32768,      28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            0, { { 0 } } },
/*    63.240.76.49 fingerprint: ttl:53,window:32850,hdl:44,mss:1460,ts:1,ws:1, opts:9(NOP,WS,NOP,NOP,TS,NOP,NOP,SACK,MSS,) */
{     128,-1,     32850,      44,   1,    1,    9,    { TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK, TCP_OPT_MSS },
            0, { { 0 } } },
{     64, -1,     32850,      44,   1,    1,    9,    { TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK, TCP_OPT_MSS },
            0, { { 0 } } },
/*    69.134.38.52 fingerprint: ttl:109(128),window:44620,hdl:28,mss:1460,ts:-1,ws:-1,opts:4(MSS,NOP,NOP,SACK,) */
{     128,-1,     44620,      28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            0, { { 0 } } },
/*   64.125.87.193 fingerprint: ttl:51(64),window:55168,hdl:32,mss:1460,ts:-1,ws:3,opts:6(MSS,NOP,WS,NOP,NOP,SACK,) */
{     64,   -1,   55168,      32,   -1,   3,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            0, { { 0 } } },

/* 221.219.243.252 fingerprint: ttl:103,window:58944,hdl:32,mss:1452,ts:-1,ws:2,opts:6(MSS,NOP,WS,NOP,NOP,SACK,) */
{     128,-1,     58944,      32,   -1,   0,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            0, { { 0 } } },
/*   218.84.84.136 fingerprint: ttl:110(128),window:64800,hdl:28,mss:1440,ts:-1,ws:-1,opts:4(MSS,NOP,NOP,SACK,) */
{     128,-1,     64800,      28,   -1,   -1,   4,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            0, { { 0 } } },
/* 201.245.252.63 fingerprint: ttl:107(128),window:64800,hdl:40,mss:1440,ts:0,ws:-1,opts:7(MSS,NOP,NOP,TS,NOP,NOP,SACK,) */
{     128,-1,     64800,      40,   0,    -1,   7,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            0, { { 0 } } },
/*  84.155.123.114 */
{     -1,   -1,   65535,      32,   -1,   0,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            0, { { 0 } } },
/*     10.43.97.41 fingerprint: ttl:64,window:65535,hdl:40,mss:1460,ts:-1,ws:0,opts:6(MSS,NOP,WS,NOP,NOP,TS,) */
{     64,   -1,   65535,      40,   1,    0,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            0, { { 0 } } },
/*  66.94.237.40 (Unknown) fingerprint: ttl:64,df:1,window:65535,hdl:40,ts:1,ws:1,opts:6(MSS,NOP,WS,NOP,NOP,TS,)(mss:1460) */
{     64,   1,    65535,      40,   1,    1,    6,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS },
            0, { { 0 } } },
/*  213.103.150.14 fingerprint: ttl:34,window:65535,hdl:44,mss:1444,ts:0,ws:3,opts:9(MSS,NOP,WS,NOP,NOP,TS,NOP,NOP,SACK,) */
{     64,   -1,   65535,      44,   0,    3,    9,    { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            0, { { 0 } } },
/*  206.190.37.110 fingerprint: ttl:53(64),window:65535,hdl:48,ts:1,ws:1,opts:14(MSS,NOP,WS,NOP,NOP,TS,NOP,NOP,CC_NEW,ECHO_REQ,NOP,POS,54,235,)(mss:1460) */
/* nmap says: (web81507.mail.yahoo.com) OS details: FreeBSD 4.3 - 4.4PRERELEASE, FreeBSD 4.9 - 5.1, FreeBSD 5.1-CURRENT (June 2003) on Sparc64 */
{     64,   -1,   65535,      48,   1,    1,    14,   { TCP_OPT_MSS, TCP_OPT_NOP, TCP_OPT_WS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_TS, TCP_OPT_NOP, TCP_OPT_NOP, TCP_OPT_SACK },
            0, { { 0 } } },

#if 0
/* nmap scan */
     10.43.97.7 fingerprint: ttl:41(64),window:2048,hdl:40,ts:1,ws:10,opts:6(WS,NOP,MSS,TS,END,END,)(mss:265)
 10.43.111.1 fingerprint: ttl:64,df:1,window:65535,hdl:44,ts:1,ws:0,opts:9(MSS,NOP,NOP,SACK,NOP,WS,NOP,NOP,TS)(mss:1460)
#endif

#endif

/* end */ /* NOTE: we're still doing sequential search... need some kind of order so we can use binary and we can get rid of this :/ */
{     -1, -1,     -1,         -1,         -1,   -1,   -1,   { 0 }, 0, { { 0 } } }
};
#define TCP_SYN_PRINT_COUNT                     (sizeof TCP_SYN_PRINTS / sizeof TCP_SYN_PRINTS[0])

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

static const u_char PING_PL_xABCD[] =           "\xAB\xCD";
static const u_char PING_PL_a_TO_w[] =          "abcdefghijklmnopqrstuvw";
static const u_char PING_PL_A_TO_W[] =          "ABCDEFGHIJKLMNOPQRSTUVW";
/* My Windows XP SP2 box sends this... */
static const u_char PING_WIN_XP_SP2[] =         "0123456789abcdefghijklmnopqrstuv";
static const u_char PING_PL_MUUSS_SHORT[] =     "\x00\x01\x02\x03\x04\x05\x06\x07";
static const u_char PING_PL_MUUSS[] =           "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
      "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
      "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F"
      "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F"
      "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F"
      "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F"
      "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F"
      "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F"
      "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F"
      "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F"
      "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF"
      "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF"
      "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF"
      "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF"
      "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF"
      "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"
      "\x00\x01\x02\x03\x04\x05\x06\x07";
/* mysterious 56 byte (minus 8-byte timestamp(?)) */
static const u_char PING_PL_LINKSYS_WTF[] =
      "\x80\x46\xB8\x2A\x30\x35\x36\x00"
      "\x00\x00\x00\x10\x58\x60\x42\x00\x02"
      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
      "\x00";
static const u_char PING_PL_MOXA_NPORT_EXP[] = "\xee\xef"
      "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
      "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
      "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
      "\x20\x21\x22\x23\x24\x25";
/* some windows machines send part of the Microsoft logo over icmp to test connection speed to the host controller */
static const u_char PING_PL_MS_LOGO[1472] = /* "\xff\xd8\xff\xfe\x00\x08WANG2\x02\xff\xe0\x00\x10JFIF\x00\x01\x01\x01\x00`\x00`\x00\x00\xff\xdb\x00\x43\x00\x10\x0b\x0c\x0e\x0c\x0a\x10\x0e\x0d\x0e\x12\x11\x10\x13\x18(\x1a\x18\x16\x16\x18\x31#%\x1d(:3=<9387@H\x5cN@DWE78PmQW_bghg>Mqypdx\x5c\x65gc\xff\xdb\x00\x43\x01\x11\x12\x12\x18\x15\x18/\x1a\x1a/cB8Bcccccccccccccccccccccccccccccccccccccccccccccccccc\xff\xc0\x00\x11\x08\x00&\x00\x9e\x03\x01!\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05\x04\x04\x00\x00\x01}\x01\x02\x03\x00\x04\x11\x05\x12!1A\x06\x13Qa\x07\"q\x14\x32\x81\x91\xa1\x08#B\xb1\xc1\x15R\xd1\xf0$3br\x82\x09\x0a\x16\x17\x18\x19\x1a%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05\x04\x04\x00\x01\x02w\x00\x01\x02\x03\x11\x04\x05!1\x06\x12\x41Q\x07\x61q\x13\"2\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09#3R\xf0\x15\x62r\xd1\x0a\x16$4\xe1%\xf1\x17\x18\x19\x1a&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00?\x00\xed\x35mJ\x1d#M\x96\xfa\xe1\x64h\xa2\xc6\xe1\x18\x05\xb9 q\x92=j\xae\x81\xe2+/\x10G3Y\x89P\xc2@t\x95@#=\x0f\x04\x8e\xc7\xbfj\x00\xd6\xaeu\xfcg\xa6.\xb6\x34\xa5\x8e\xe5\xe6\xf3\xc4\x1b\xd5\x06\xcd\xe4\xe3\xb9\xcf\x07\x8e\x9d\xbb\xd0\x06\x86\xa7\xad\xdbiw\xb6\x16\xb3\xa4\xac\xf7\xd2yq\x94\x00\x80r\xa3\x9c\x91\xfd\xe1\xebZT\x00Q@\x05\x14\x00Q@\x05\x14\x00Q@\x0d\x92\x44\x8a\x36\x92GTD\x05\x99\x98\xe0(\x1dI5\xcc\xdb\xf8\xf3I\xb9\xd4\x62\xb3\x86+\xb6ie\x11$\x9b\x14)$\xe0\x1e[8\xfc\x33\xed@\x1b\xd6\xba\x8d\xa5\xe5\xd5\xcd\xb5\xb4\xeb$\xb6\xa4,\xca\xa0\xfc\x84\xe7\x8c\xf4=\x0fN\x98\xabT\x01\xcf\xf8\xef\xfe\x45\x0b\xef\xfbg\xff\x00\xa3\x16\xb9\x9f\x0c\x11\xa1\xf8\xa2\xc6\x1d\xca\x96\xfa\xa5\x84N\x07\x98@\x0eP\x1c\x90z\x92\xca\xc0\x0f\xf6\xf8\xf4\xa0\x0e\xf3S\xbdM7M\xb9\xbd\x93i\x10\x46_k6\xdd\xc4\x0e\x17>\xe7\x03\xf1\xaf-\xd3\xac\xda\x39\xfc;\xa8L\xdb\xe7\xbe\xd4\x19\xda\x42\xc4\xb3\x05x\xc7\x39\xef\xbby\xfch\x03\xd1\x35\xadw\xfb'Q\xd2\xed>\xcd\xe6\xfd\xbe_/v\xfd\xbb\x39Q\x9c`\xe7\xef{t\xabZ\xbe\xafi\xa2\xd9}\xaa\xf5\xd9P\x9d\xaa\x15I,\xd8$\x01\xf5\xc1\xeb\x81@\x1c\xef\xfc'\x13G\xfe\x91q\xa0_G\xa7\x9e\x45\xce\x0f*~\xe9\xc1\x00s\x91\xfc]\xfb\xd7\x45.\xa9\x07\xf6$\xba\xa5\xab-\xc4)\x03L\xbbN7m\x04\xe3\xdb\xa6=\xa8\x03\x9d\xb6\xf1\xc4\xd7\xe9\x00\xd3tI\xee\xe7o\xf5\xc8\x8ev\xc2K\x10\xa0\xb6\xdcr\x06rp\x07\xaf\x5cmx\x83\xc4\x16\x9a\x05\xa8\x92\xe7s\xcb o&%\x07\xf7\x84\x63\xbf\x41\xd4u\xfdzP\x06*\xf8\xde\xe2\xdeh\x8e\xab\xa0\xdd\xd8Z\xbb\xeci\xdfq\x0aO\xb1Q\x9f\xe7\x8c\xf5\xae\x99\xf5\x1bH\xf4\xd1\xa8\xc9:\xa5\xa1\x8cK\xe6\x30#\xe5##\x8e\xbc\xe4q\xd6\x80\x39\x9f\xf8Mo.?{\xa7\xf8n\xfa\xe6\xd5\xbe\xe4\xbc\x8d\xde\xbd\x14\x8e\xb9\x1d{V\xc7\x87<Ek\xe2\x1by\x1e\xdd$\x8aXv\x89\x63q\xf7I\x1d\x8fq\xc1\xf4<t\x14\x01\x1f\x88|Mo\xa2\x32[\xac\x32]_K\xb7\xcb\xb6@A`I\x19\xce\x0f\xa1\x18\x19\x39\xc7\x1d\xeb\x36\x0f\x1c\x18n\x92=kI\xb9\xd2\xe2\x90\x1d\xb2\xc8\x19\xb2G\xb6\xd0\x7f,\xf5\x1fZ\x00\xb9\xe3MJ\xe6\xcbJ\x92\x08\x34\xe9n\x92\xe6\x09RIS8\x80m\xc6\xe3\x80}I\xed\xd2\xb9\xbf\x0cx\x82\xff\x00M\xd0\xe1\xb7\xb4\xf0\xdd\xcd\xd2\x65\x98\xdc\x44\x18\x09\x09\x63\xcf\x08s\x8e\x07^\xd4\x01_\xc3\xfa\xf5\xfd\x96\xab\xacO\x06\x87st\xf7\x33\xef\x92$\xdd\x98\x0e\xe7;N\x14\xfa\x91\xdb\xa5zu\x00s\xfe;\xff\x00\x91\x42\xfb\xfe\xd9\xff\x00\xe8\xc5\xac\x1f\x11\xdbJ\xbe\x0e\xd0u[b\xc2k\x08\xe1`\xdc\x61\x41U\xe7\x07\xaf\xcc\x13\xf3\x34\x01k\xc6\xfa\x93_hze\xa5\x92\xc9\xbfVtdV\x0a\x32\xbc\x10\xa4\x93\xc1\xdc\xc9\xf9\x1e}[\xe2\x8b\x64\xb3\xd5|#k\x19\x62\x90N#R\xddH\x0d\x10\x19\xfc\xa8\x02o\x1a\x7f\xc8\xc3\xe1\x8f\xfa\xfb\xff\x00\xd9\xe3\xa8\xfc\x45\x0a\xea>?\xd1\xb4\xfb\x93\xba\xd5\x62\x33yx\x18'\xe6'9\x1c\x83\xb1\x41\x1e\x94" */
"\xff\xd8\xff\xfe\x00\x08WANG2\x02\xff\xe0\x00\x10JFIF\x00\x01\x01\x01"
"\x00`\x00`\x00\x00\xff\xdb\x00\x43\x00\x10\x0b\x0c\x0e\x0c\x0a\x10\x0e\x0d\x0e\x12\x11\x10\x13\x18(\x1a\x18\x16\x16\x18\x31#%\x1d(:3=<9387@H\x5cN@DWE78PmQW_bghg>Mqypdx\x5c\x65gc\xff\xdb"
"\x00\x43\x01\x11\x12\x12\x18\x15\x18/\x1a\x1a/cB8Bcccccccccccccccccccccccccccccccccccccccccccccccccc\xff\xc0\x00\x11\x08\x00&\x00\x9e\x03\x01!\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f"
"\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05\x04\x04"
"\x00\x00\x01}\x01\x02\x03\x00\x04\x11\x05\x12!1A\x06\x13Qa\x07\"q\x14\x32\x81\x91\xa1\x08#B\xb1\xc1\x15R\xd1\xf0$3br\x82\x09\x0a\x16\x17\x18\x19\x1a%&'()*456789:CDEFGHIJSTUVWXYZcdefghijs"
"tuvwxyz\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2"
"\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00"
"\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05\x04\x04\x00\x01\x02w\x00\x01\x02\x03\x11\x04\x05!1\x06\x12\x41Q"
"\x07\x61q\x13\"2\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09#3R\xf0\x15\x62r\xd1\x0a\x16$4\xe1%\xf1\x17\x18\x19\x1a&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\x82\x83\x84\x85\x86\x87\x88\x89\x8a"
"\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3"
"\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00?\x00\xed\x35mJ\x1d#M\x96\xfa\xe1\x64h\xa2\xc6\xe1\x18\x05\xb9 q\x92=j"
"\xae\x81\xe2+/\x10G3Y\x89P\xc2@t\x95@#=\x0f\x04\x8e\xc7\xbfj\x00\xd6\xaeu\xfcg\xa6.\xb6\x34\xa5\x8e\xe5\xe6\xf3\xc4\x1b\xd5\x06\xcd\xe4\xe3\xb9\xcf\x07\x8e\x9d\xbb\xd0\x06\x86\xa7\xad\xdb"
"iw\xb6\x16\xb3\xa4\xac\xf7\xd2yq\x94\x00\x80r\xa3\x9c\x91\xfd\xe1\xebZT\x00Q@\x05\x14\x00Q@\x05\x14\x00Q@\x0d\x92\x44\x8a\x36\x92GTD\x05\x99\x98\xe0(\x1dI5\xcc\xdb\xf8\xf3I\xb9\xd4\x62"
"\xb3\x86+\xb6ie\x11$\x9b\x14)$\xe0\x1e[8\xfc\x33\xed@\x1b\xd6\xba\x8d\xa5\xe5\xd5\xcd\xb5\xb4\xeb$\xb6\xa4,\xca\xa0\xfc\x84\xe7\x8c\xf4=\x0fN\x98\xabT\x01\xcf\xf8\xef\xfe\x45\x0b\xef\xfb"
"g\xff\x00\xa3\x16\xb9\x9f\x0c\x11\xa1\xf8\xa2\xc6\x1d\xca\x96\xfa\xa5\x84N\x07\x98@\x0eP\x1c\x90z\x92\xca\xc0\x0f\xf6\xf8\xf4\xa0\x0e\xf3S\xbdM7M\xb9\xbd\x93i\x10\x46_k6\xdd\xc4\x0e\x17"
">\xe7\x03\xf1\xaf-\xd3\xac\xda\x39\xfc;\xa8L\xdb\xe7\xbe\xd4\x19\xda\x42\xc4\xb3\x05x\xc7\x39\xef\xbby\xfch\x03\xd1\x35\xadw\xfb'Q\xd2\xed>\xcd\xe6\xfd\xbe_/v\xfd\xbb\x39Q\x9c`\xe7\xef"
"{t\xabZ\xbe\xafi\xa2\xd9}\xaa\xf5\xd9P\x9d\xaa\x15I,\xd8$\x01\xf5\xc1\xeb\x81@\x1c\xef\xfc'\x13G\xfe\x91q\xa0_G\xa7\x9e\x45\xce\x0f*~\xe9\xc1\x00s\x91\xfc]\xfb\xd7\x45.\xa9\x07\xf6$\xb"
"a\xa5\xab-\xc4)\x03L\xbbN7m\x04\xe3\xdb\xa6=\xa8\x03\x9d\xb6\xf1\xc4\xd7\xe9\x00\xd3tI\xee\xe7o\xf5\xc8\x8ev\xc2K\x10\xa0\xb6\xdcr\x06rp\x07\xaf\x5cmx\x83\xc4\x16\x9a\x05\xa8\x92\xe7s\xcb"
"o&%\x07\xf7\x84\x63\xbf\x41\xd4u\xfdzP\x06*\xf8\xde\xe2\xdeh\x8e\xab\xa0\xdd\xd8Z\xbb\xeci\xdfq\x0aO\xb1Q\x9f\xe7\x8c\xf5\xae\x99\xf5\x1bH\xf4\xd1\xa8\xc9:\xa5\xa1\x8cK\xe6\x30#\xe5#"
"#\x8e\xbc\xe4q\xd6\x80\x39\x9f\xf8Mo.?{\xa7\xf8n\xfa\xe6\xd5\xbe\xe4\xbc\x8d\xde\xbd\x14\x8e\xb9\x1d{V\xc7\x87<Ek\xe2\x1by\x1e\xdd$\x8aXv\x89\x63q\xf7I\x1d\x8fq\xc1\xf4<t\x14\x01\x1f\x88"
"|Mo\xa2\x32[\xac\x32]_K\xb7\xcb\xb6@A`I\x19\xce\x0f\xa1\x18\x19\x39\xc7\x1d\xeb\x36\x0f\x1c\x18n\x92=kI\xb9\xd2\xe2\x90\x1d\xb2\xc8\x19\xb2G\xb6\xd0\x7f,\xf5\x1fZ\x00\xb9\xe3MJ\xe6\xcb"
"J\x92\x08\x34\xe9n\x92\xe6\x09RIS8\x80m\xc6\xe3\x80}I\xed\xd2\xb9\xbf\x0cx\x82\xff\x00M\xd0\xe1\xb7\xb4\xf0\xdd\xcd\xd2\x65\x98\xdc\x44\x18\x09\x09\x63\xcf\x08s\x8e\x07^\xd4\x01_\xc3\xfa"
"\xf5\xfd\x96\xab\xacO\x06\x87st\xf7\x33\xef\x92$\xdd\x98\x0e\xe7;N\x14\xfa\x91\xdb\xa5zu\x00s\xfe;\xff\x00\x91\x42\xfb\xfe\xd9\xff\x00\xe8\xc5\xac\x1f\x11\xdbJ\xbe\x0e\xd0u[b\xc2k\x08\xe1"
"`\xdc\x61\x41U\xe7\x07\xaf\xcc\x13\xf3\x34\x01k\xc6\xfa\x93_hze\xa5\x92\xc9\xbfVtdV\x0a\x32\xbc\x10\xa4\x93\xc1\xdc\xc9\xf9\x1e}[\xe2\x8b\x64\xb3\xd5|#k\x19\x62\x90N#R\xddH\x0d\x10\x19"
"\xfc\xa8\x02o\x1a\x7f\xc8\xc3\xe1\x8f\xfa\xfb\xff\x00\xd9\xe3\xa8\xfc\x45\x0a\xea>?\xd1\xb4\xfb\x93\xba\xd5\x62\x33yx\x18'\xe6'9\x1c\x83\xb1\x41\x1e\x94"
;
static const u_char PING_WIN_2K3[] = "abcdefghijklmnopqrstuvwxyzabcdefghi";
static const u_char PING_PL_PIX[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
      "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f";
static const u_char PING_IOLAN[32] = "abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80"; /* mystery device */
static const u_char PING_ZERO[1] = "\x00";

/**
 * ICMP Echo signatures
 * document: docs/protocols/icmp-sig.txt
 * @fixme argh, we search through the whole list every time. make more efficient...
 */
const struct ping_sig PING_SIGS[] = {
      /*                            seq         pl          ploff-                                                                                          */
      /*type            ttl   msbf,id     df    bytes set,plpatlen                              payload                       oslen os    */

      /* Windows... need to investigate id meaning... */
      /* TODO: figure out lowercase vs uppercase payload */
      { PING_REQ, 32,   0,    0x0100,     0,    32,         0,    sizeof PING_PL_a_TO_w - 1,    PING_PL_a_TO_w,   1,    { { OS_WIN_NT_40, 15, OS_WIN_NT_40_SP3 } } },
      { PING_REQ, 32,   0,    0x0200,     0,    32,         0,    sizeof PING_PL_a_TO_w - 1,    PING_PL_a_TO_w,   1,    { { OS_WIN_NT_51_X, 15, OS_NONE } } },
      { PING_REQ, 32,   0,    0x0200,     0,    32,         0,    sizeof PING_PL_A_TO_W - 1,    PING_PL_A_TO_W,         1,    { { OS_WIN_NT_51_X, 15, OS_NONE } } },
      { PING_REQ, 128,0,      0x0200,     0,    32,         0,    sizeof PING_PL_a_TO_w - 1,    PING_PL_a_TO_w,         1,    { { OS_WIN_NT_50_X, 15, OS_WIN_NT_50_X } } },
      { PING_REQ, 32,   0,    0x0300,     0,    32,         0,    sizeof PING_PL_a_TO_w - 1,    PING_PL_a_TO_w,         1,    { { OS_WIN_NT_51_X, 15, OS_NONE } } },
      { PING_REQ, 128,0,      0x0300,     0,    32,         0,    sizeof PING_PL_a_TO_w - 1,    PING_PL_a_TO_w,         1,    { { OS_WIN_NT_50_X, 15, OS_WIN_NT_52_X } } },
      { PING_REQ, 32,   0,    0x0400,     0,    32,         0,    sizeof PING_PL_A_TO_W - 1,    PING_PL_A_TO_W,         1,    { { OS_WIN_NT_51_X, 15, OS_NONE } } },
      { PING_REQ, 128,0,      0x0400,     0,    32,         0,    sizeof PING_PL_a_TO_w - 1,    PING_PL_a_TO_w,         1,    { { OS_WIN_NT_50_X, 15, OS_WIN_NT_51_X } } },
      { PING_REQ, 32,   0,    0x0500,     0,    32,         0,    sizeof PING_PL_A_TO_W - 1,    PING_PL_A_TO_W,         1,    { { OS_WIN_NT_51_X, 15, OS_NONE } } },
      { PING_REQ, 128,0,      0x0500,     0,    32,         0,    sizeof PING_PL_a_TO_w - 1,    PING_PL_a_TO_w,         1,    { { OS_WIN_NT_51_X, 15, OS_NONE } } },

      /* windows tracert... pretty weak indicator, but we'll take it */
      { PING_REQ, -1,   0,    0x0100,     0,    64,         0,    1,                                        PING_ZERO,              1,    { { OS_WIN_NT_40, 5, OS_WIN_NT_40_SP3 } } },
      { PING_REQ, -1,   0,    0x0200,     0,    64,         0,    1,                                        PING_ZERO,              1,    { { OS_WIN_NT_50_SVR, 5, OS_WIN_NT_50_SVR } } },
      { PING_REQ, -1,   0,    0x0400,     0,    64,         0,    1,                                        PING_ZERO,              1,    { { OS_WIN, 5, OS_NONE } } },
      { PING_REQ, -1,   0,    0x0500,     0,    64,         0,    1,                                        PING_ZERO,              1,    { { OS_WIN_NT_50_X, 5, OS_NONE } } },

      /* "icmp windows logo", see icmp.txt */
      /* NOTE: "more fragments" is set, but we don't fingerprint it! */
      { PING_REQ, 128,0,      0x0200,     0,    sizeof PING_PL_MS_LOGO, 0,    sizeof PING_PL_MS_LOGO, PING_PL_MS_LOGO,
            3,    { { OS_WIN_NT_50, 90, OS_WIN_NT_50_SP3 }, { OS_WIN_NT_51, 90, OS_WIN_NT_51_SP2 }, { OS_WIN_NT_52, 90, OS_WIN_NT_52 } } },

      /*                            seq         pl          ploff-                                                                                          */
      /*type            ttl   msbf,id     df    bytes set,plpatlen                              payload                       oslen os    */

      /* Linux has msbf, Muuss and sets df! */
      { PING_REQ, 64,   1,    -1,   1,    56,         8,    sizeof PING_PL_MUUSS - 1,     PING_PL_MUUSS,          1,    { { OS_LINUX, 25, OS_LINUX } } },

      /* FreeBSD 4... inherited by DFlyBSD */
      { PING_REQ, 64,   0,    -1,   0,    56,         8,    sizeof PING_PL_MUUSS - 1,     PING_PL_MUUSS,          1,    { { OS_FREEBSD_4X, 15, OS_FREEBSD_5X } } },

      /* FreeBSD 5 switches endianness!... inherited by OSX */
      { PING_REQ, 64,   1,    -1,   0,    56,         8,    sizeof PING_PL_MUUSS - 1,     PING_PL_MUUSS,          1,    { { OS_FREEBSD_5X, 15, OS_FREEBSD_5X } } },
      /* NetBSD... inherited by OpenBSD */
      { PING_REQ, 255,1,      -1,   0,    56,         8,    sizeof PING_PL_MUUSS - 1,     PING_PL_MUUSS,          2,    { { OS_NETBSD, 10, OS_NONE }, { OS_SOLARIS_10, 10, OS_SOLARIS_10 } } },
      /* Cisco IOS */
      { PING_REQ, 255,1,      -1,   0,    72,         8,    sizeof PING_PL_xABCD - 1,     PING_PL_xABCD,          1,    { { OS_IOS, 15, OS_NONE } } },
      /* Cisco PIX IOS */
      { PING_REQ, 255,1,      -1,   0,    sizeof PING_PL_PIX,     0,    sizeof PING_PL_PIX,     PING_PL_PIX,      1, { { OS_IOS_PIX, 15, OS_NONE } } },
      /* Netware */
      /* 12-byte default payload is distinctive, but payload itself is not easily identifiable */
      { PING_REQ, 128,0,      -1,   0,    12,         0,    0,                                        NULL,                         1,    { { OS_NETWARE, 10, OS_NONE } } },
      /* Moxa NPort Express */
      /* PING (no match) (10.43.97.10     -> 10.43.97.7     ) ttl: 32 seq_msbf:1(0x0001) id:0x50CF df:0 plbytes:56 payload:\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$% */
      { PING_REQ, 32,   1,    -1,   0,    56,         0,    sizeof PING_PL_MOXA_NPORT_EXP - 1,  PING_PL_MOXA_NPORT_EXP, 1,    { { OS_MOXA_NPORT_EXP, 20, OS_NONE } } },

      /*                            seq         pl          ploff-                                                                                          */
      /*type            ttl   msbf,id     df    bytes set,plpatlen                              payload                       oslen os    */

      /* mystery device */
      { PING_REQ, 255,1,      -1,   0,    32,         0,    sizeof PING_IOLAN,                  PING_IOLAN,             1,    { { OS_PERLE_IOLAN, 15, OS_PERLE_IOLAN } } },   

      /* catch-alls */

      { PING_RESP,32,   -1,   -1,   -1,   -1,         -1,   -1,                                       NULL,                   1,    { { OS_WIN, 1, OS_WIN } } },
      { PING_RESP,64,   -1,   -1,   -1,   -1,         -1,   -1,                                       NULL,                   3,    { { OS_FREEBSD_4X, 1, OS_FREEBSD_5X }, { OS_LINUX, 1, OS_LINUX }, { OS_OSX, 1, OS_OSX } } },
      { PING_RESP,128,-1,     -1,   -1,   -1,         -1,   -1,                                       NULL,                   1,    { { OS_WIN, 1, OS_WIN } } },
      { PING_RESP,255,-1,     -1,   -1,   -1,         -1,   -1,                                       NULL,                   4,    { { OS_NETBSD, 1, OS_NONE }, { OS_SOLARIS_10, 1, OS_SOLARIS }, { OS_IOS, 1, OS_IOS }, { OS_IOS_PIX, 1, OS_IOS_PIX } } },

      /* end with oslen == -1 */
      { -1,       -1,   -1,   -1,   -1,   -1,         0,    0,                                        NULL,                   -1,   { { 0 } } }

};
#if 0
/* wtf, gcc sucks

from WinXP SP2 to its gateway
PING (no match) (10.43.97.143    -> 10.43.96.8     ) ttl:128 seq_msbf:0(0x0700) id:0x0200 df:0 plbytes:18 payload:\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00

Unknown:
PING (no match) (192.168.1.100   -> 192.168.1.101  ) ttl: 64 seq_msbf:1(0x0002) id:0x233F df:1 plbytes:56 payload:\x838\x16C\xb2\x11\x0c\x00\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&'()*+,-./01234567
PING (no match) (10.43.97.1      -> 10.43.96.8     ) ttl:128 seq_msbf:0(0xAB02) id:0x0200 df:0 plbytes:18 payload:\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
PING (no match) (10.43.97.11     -> 10.43.96.8     ) ttl:  1 seq_msbf:0(0x0300) id:0x0200 df:0 plbytes:18 payload:DHCPC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00

Windows 2K...
PING (no match) (10.43.97.2      -> 10.43.96.6     ) ttl: 32 seq_msbf:0(0x0300) id:0x0700 df:0 plbytes:32 payload:ABCDEFGHIJKLMNOPQRSTUVWABCDEFGHI

from WinXP SP2 (mine) to ?
PING (no match) (10.43.97.5      -> 10.43.96.9     ) ttl:128 seq_msbf:0(0xED00) id:0x0400 df:0 plbytes:0 payload:


*/
#endif

#define PING_SIG_COUNT        (sizeof PING_SIGS / sizeof PING_SIGS[0])


/**
 * BOOTP discover signatures
 */
const struct bootp_sig BOOTP_SIGS[] = {
/*    ttl   type        flagflag                            reqsflagreq                                                                   oslnos */
/* NOTE: -1 is a wildcard, -2 for ttl signifies end of list */

{     128,BOOTP_DISC,   5,    { 53,61,12,60,55 },                 11, { 1,15,3,6,44,46,47,31,33,43 },                   1,    { { OS_WIN_NT_50_X, 20, OS_WIN_NT_51_X } } },
{     128,BOOTP_INF,    5,    { 53,61,12,60,55 },                 12,   { 1,15,3,6,44,46,47,31,33,249,43,252 },         1,    { { OS_WIN_NT_50_X, 20, OS_WIN_NT_51_X } } },

{     128,BOOTP_DISC,   7,    { 53,116,61,50,12,60,55 },    11, { 1,15,3,6,44,46,47,31,33,249,43 },               1,    { { OS_WIN_NT_51_SP1, 20, OS_NONE } } },
{     128,BOOTP_REQ,    6,    { 53,116,61,12,60,55 },       11,   { 1,15,3,6,44,46,47,31,33,249,43 },             1,    { { OS_WIN_NT_51_SP1, 20, OS_NONE } } },
{     128,BOOTP_REQ,    8,    { 53,61,50,54,12,81,60,55 },11, { 1,15,3,6,44,46,47,31,33,249,43 },                 1,    { { OS_WIN_NT_51_SP1, 20, OS_NONE } } },
{     128,BOOTP_REQ,    7,    { 53,61,50,12,81,60,55 },     11, { 1,15,3,6,44,46,47,31,33,249,43 },               1,    { { OS_WIN_NT_51_SP1, 20, OS_NONE } } },

/* Windows NT 4 SP1,SP3 */
{     128,BOOTP_DISC,   4,    { 53,61,12,55 },              7, { 1,15,3,44,46,47,6 },                                   1,    { { OS_WIN_NT_40, 20, OS_WIN_NT_40_SP3 } } }, /* doesn't have an IP in mind */
{     128,BOOTP_DISC,   5,    { 53,61,50,12,55 },                 7, { 1,15,3,44,46,47,6 },                                   1,    { { OS_WIN_NT_40, 20, OS_WIN_NT_40_SP3 } } }, /* already has IP */
{     128,BOOTP_REQ,    6,    { 53,61,50,54,12,55 },        7, { 1,15,3,44,46,47,6 },                                   1,    { { OS_WIN_NT_40, 20, OS_WIN_NT_40_SP3 } } },

/* FreeBSD 5.x */
/* hmm some unexpected machines trigger these... */
{     16,   BOOTP_DISC, 2,    { 53,55 },                          7,    { 1,28,2,3,15,6,12 },                                 2,    { { OS_FREEBSD_5X, 20, OS_FREEBSD_5X }, { OS_DFLYBSD_1X, 1, OS_DFLYBSD_1X } } },
{     16,   BOOTP_REQ,  4,    { 53,54,50,55 },              7,    { 1,28,2,3,15,6,12 },                                 2,    { { OS_FREEBSD_5X, 20, OS_FREEBSD_5X }, { OS_DFLYBSD_1X, 1, OS_DFLYBSD_1X } } },

/* FreeBSD 4.x */
{     16,   BOOTP_DISC, 3,    { 53,50,55 },                       7,    { 1,28,2,3,15,6,12 },                                 1,    { { OS_FREEBSD_4X, 1, OS_FREEBSD_4X } } },
{     16,   BOOTP_REQ,  4,    { 53,54,50,55 },              7,    { 1,28,2,3,15,6,12 },                                 1,    { { OS_FREEBSD_4X, 1, OS_FREEBSD_4X } } },

/* Windows 2K Pro */
/* get addr for the first time */
{     128,BOOTP_DISC,   7,    { 53,251,61,50,12,60,55 },    10,   { 1,15,3,6,44,46,47,31,33,43 },                       1,    { { OS_WIN_NT_50, 40, OS_WIN_NT_50 } } },
{     128,BOOTP_REQ,    8,    { 53,61,50,54,12,81,60,55 },10,     { 1,15,3,6,44,46,47,31,33,43 },                       1,    { { OS_WIN_NT_50, 40, OS_WIN_NT_50 } } },
/* get IP address back */
{     128,BOOTP_REQ,    6,    { 53,61,12,81,60,55 },        6,    { 53,61,12,81,60,55 },                                1,    { { OS_WIN_NT_50, 40, OS_WIN_NT_50 } } },



/* Windows 2K Server */
/* get addr for the first time */
{     128,BOOTP_DISC,   6,    { 53,251,61,12,60,55 },       10,   { 1,15,3,6,44,46,47,31,33,43 },                       1,    { { OS_WIN_NT_50_SVR, 40, OS_WIN_NT_50_SVR } } },
{     128,BOOTP_REQ,    8,    { 53,61,50,54,12,81,60,55 },10,     { 1,15,3,6,44,46,47,31,33,43 },                       1,    { { OS_WIN_NT_50_SVR, 40, OS_WIN_NT_50_SVR } } },
/* get IP address back */
{     128,BOOTP_REQ,    7,    { 53,61,50,12,81,60,55 },     10,   { 1,15,3,6,44,46,47,31,33,43 },                       2,    { { OS_WIN_NT_50_SVR, 40, OS_WIN_NT_50_SVR }, { OS_WIN_NT_51_X, 40, OS_WIN_NT_51_X } } },
{     128,BOOTP_INF,    5,    { 53,61,12,60,55 },                 11, { 1,15,3,6,44,46,47,31,33,43,252 },               2,    { { OS_WIN_NT_50_SVR, 40, OS_WIN_NT_50_SVR }, { OS_WIN_NT_51_X, 40, OS_WIN_NT_51_X } } },

/* Windows 2003 Server */
{     128,BOOTP_REQ,    7,    { 53,77,1,50,12,60,55 },      11,   { 1,15,3,6,44,46,47,31,33,249,43 },             1,    { { OS_WIN_NT_52_X, 40, OS_WIN_NT_52_X } } },

/* OS X.4 */
{     255,BOOTP_DISC,   6,    { 53,55,57,61,51,12 },        10,   { 1,3,6,15,112,113,78,79,95,252 },              1,    { { OS_OSX_4, 40, OS_OSX_4 } } },
{     255,BOOTP_DISC,   7,    { 53,55,57,61,50,54,12 },     10,   { 1,3,6,15,112,113,78,79,95,252 },              1,    { { OS_OSX_4, 40, OS_OSX_4 } } },

/* OpenBSD 3.x */
{     16,   BOOTP_DISC, 4,    { 53,50,12,55 },              6,    { 1,28,3,15,6,12 },                                         1,    { { OS_OPENBSD_3X, 40, OS_OPENBSD_3X } } },

/* "Macromedia Flash Proxy Auto-Discovery" on windows */
{     128, BOOTP_INF, 4,      { 43,53,55,60 },              1,    { 43 },                                                           0,    { { OS_WIN, 1, OS_WIN } } },

/* Solaris 10 x86 */
{     255,BOOTP_DISC,   5,    { 53,57,51,60,55 },                 7,    {  1,3,6,12,15,28,43 },                               1,    { { OS_SOLARIS_10, 40, OS_SOLARIS_10 } } },
{     255,BOOTP_REQ,    7,    { 53,51,57,50,54,60,55 },     7,    {  1,3,6,12,15,28,43 },                               1,    { { OS_SOLARIS_10, 40, OS_SOLARIS_10 } } },

/* VMWare */
/* NOTE: real TTL is 20, but we guess in powers of 2 */
{     32,   BOOTP_DISC, 4,    { 53,55,57,97,93,94,60 },     25,   { 1,2,3,5,6,11,12,13,15,16,17,18,43,54,60,67,128,129,130,131,132,133,134,135 },     1,    { { OS_VMWARE_5X, 50, OS_VMWARE_5X } } },

/* UKNOWNS... */

/* karenlaptop... some kind of windows... */
{     128,BOOTP_DISC,   7,    { 53,77,116,61,12,60,55 },    11,   { 1,15,3,6,44,46,47,31,33,249,4 },              1,    { { OS_UNKNOWN, 1, OS_UNKNOWN } } },
{     128,BOOTP_REQ,    8,    { 53,77,61,50,54,12,60,55 },11,     { 1,15,3,6,44,46,47,31,33,249,43 },             1,    { { OS_UNKNOWN, 1, OS_UNKNOWN } } },
/* NOTE: must be on shutdown, figure out if there are any other situations that do this... could be very useful */
{     128,BOOTP_REL,    3,    { 53,54,51 },                       0,    { 0 },                                                            1,    { { OS_UNKNOWN, 1, OS_UNKNOWN } } },

/* 10.43.97.1... nmap says broadband router? */
{     64,   BOOTP_DISC, 2,    { 53,61 },                          0,    { 0 },                                                            0,    { { OS_UNKNOWN, 20, OS_NONE } } },


/*    ttl   type        flagflag                            reqsflagreq                                                                   oslnos */

/* WRT54G denying itself */
{     64,   BOOTP_NAK,  2,    { 53, 54 },                         0,    { 0 },                                                                        1,    { { OS_LINUX, 1, OS_LINUX } } },
{     64,   BOOTP_OFF,  7,    { 53,54,51,1,3,6,15 },        0,    { 0 },                                                                        1,    { { OS_LINUX, 1, OS_LINUX } } },
{     64,   BOOTP_ACK,  7,    { 53,54,51,1,3,6,15 },        0,    { 0 },                                                                        1,    { { OS_LINUX, 1, OS_LINUX } } },

/* fin. */
{     -2,   0,    0,    { 0 },                                                0,    { 0 },                                                                        0,    { { 0, 0, 0 } } }
};
#if 0
/*
Linux using 
DHCP Client Daemon v.1.3.22-pl4
Copyright (C) 1996 - 1997 Yoichi Hariguchi <yoichi@fore.com>
Copyright (C) January, 1998 Sergei Viznyuk <sv@phystech.com>
Location: http://www.phystech.com/download/
BOOTP SIG type:6, ttl:64, flags:2(53,54), reqs:0()(msgtype:0)
BOOTP SIG type:2, ttl:64, flags:8(53,54,51,1,3,6,15,0), reqs:0()(msgtype:0)
BOOTP SIG type:5, ttl:64, flags:8(53,54,51,1,3,6,15,0), reqs:0()(msgtype:0)
*/
#endif
#define BOOTP_SIG_COUNT       (sizeof BOOTP_SIGS / sizeof BOOTP_SIGS[0])

/**
 * human-readable descriptions of hints
 */
/* CLEANUP: unused?! nuke this crap */
static const char *HINT_TXT[] = {
      "None",
      /* MAC */
      "NIC Vendor Apple",
      /* ARP */
      "Arp...",
      /* BOOTP */
      "BOOTP Vendor MacOS",
      "BOOTP Vendor MacOS 9",
      "BOOTP Vendor MacOS 9.2",
      "BOOTP Vendor MacOS 9.2.2",
      "BOOTP Vendor MSFT 5.0",
      "BOOTP Vendor MSFT 5.1"
};

/**
 * an OS identification
 */
00770 typedef struct {
      int source; /* the test which revealed this information */
      uint32_t os;
      int sure; /* how sure they are */
      u_int count; /* the number of times they've reported it */
} os_id;

typedef struct {
      size_t packets[PROT_COUNT];
      size_t bytes[PROT_COUNT];
      size_t conn[CONN_COUNT];
} traffic_stats;

/**
 * a distinct entity on the network
 */
00786 typedef struct {
      lhash_t macs; /* struct mac * -> NULL */
      char hostname[64];
      lhash_t hostnames;
      char os_guess[OS_BUFLEN];
      lhash_t roles;
      int hw;
      int vend;
      int traffic_total, traffic_ext;
      int hints_pos[HINTS_POS_COUNT];
      int hints_tcp_syn[TCP_SYN_PRINT_COUNT];
      int hints_ping[PING_SIG_COUNT];
      int hints_bootp[BOOTP_SIG_COUNT];
} machine;

static lhash_t Macs; /* struct mac * -> lhash_t { struct ip * -> NULL } */
static lhash_t Mac_Facts; /* struct mac * -> facts * */
static lhash_t Mac_Traffic; /* struct mac * -> lhash_t { struct mac * : count } */
static lhash_t Mac_Traffic_Ext; /* external in/out traffic; struct ip * : count */
static lhash_t Mac_Hints_Pos; /* struct mac * ->  * */
static lhash_t Mac_Hints_Bootp; /* struct ip * -> (*int)[] */
static lhash_t Ips; /* struct ip * -> lhash_t { struct mac * -> NULL } */
static lhash_t Ip_Facts; /* struct ip * -> facts * */
static lhash_t Ip_Traffic; /* struct ip * -> lhash_t { struct ip * : count } */
static lhash_t Ip_Traffic_Total; /* struct ip * -> count */
static lhash_t Ip_Hostname; /* struct ip * -> const char * */
static lhash_t Ip_Hints_Pos; /* struct ip * -> (*int)[] */
static lhash_t Ip_Hints_Tcp_Syn; /* struct ip * -> (*int)[] */
static lhash_t Ip_Hints_Ping; /* struct ip * -> (*int)[] */
static lhash_t Ip_Subnet;

/* used in calculating Machines for reporting. they're global so we avoid init overhead
 * every time we report */
static lhash_t Macs_Calced; /*  */
static lhash_t Mac_Machine; /* struct mac * -> Machine *  */
static lhash_t Ip_Machine; /* struct ip * -> Machine *  */
static list_t Machines; /*  */

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/* callbacks for use in hash tables */
static int mac_cmp(const void *va, const void *vb);
static void * mac_copy(void *v);

static int int_cmp(const void *a, const void *b);
static void * int_copy(void *v);
static int uint32t_cmp(const void *a, const void *b);
static void * uint32t_copy(void *v);
static void facts_free(void *v);

static void dump_ips(void);
static void dump_macs(void);

static void machines_calc(void);
static void machines_dump(void);
static void machines_cleanup(void);

static const char * bytes_to_str(size_t, char *, size_t);



static const char *CMP_TXT[] = {
      "?",
      "==",
      "!=",
      ">",
      ">=",
      "<",
      "<="
};

static const char *FACT_ARCH_TXT[] = {
      "Arch Unknown"
};

/**
 * @note MUST be SYNCED with FACT_ROLE_
 */
static const char *FACT_ROLE_TXT[] = {
      "Unknown",
      "Desktop",
      "Laptop",
      "Printer",
      "Router",
      "Switch",
      "Host",
      "Repeater",
      "Bridge",
      "Access Point",
      "Wireless",
      "Server",
      "Web Server",
      "Mail Server",
      "DNS Server",
      "SMB Server",
      "Print Server",
      "SQL Server",
      "File Server",
      "DHCP Server",
      "Web Client",
      "Mail Client",
      "DNS Client",
      "Print Client",
      "SQL Client"
};

/**
 * graph icon for role
 */
static const char *FACT_ROLE_IMG[] = {
      "desktop",
      "desktop",
      "laptop",
      "printer",
      "router",
      "switch",
      "host",
      "repeater",
      "bridge",
      "ap",
      "wifi",
      "server",
      "httpd",
      "maild",
      "server", /* dnsd */
      "smbd",
      "printd",
      "sqld",
      "filed",
      "server", /* dhcpd */
      "webc",
      "mailc",
      "dnsc",
      "printc",
      "sqlc"
};

struct os_descr {
      uint32_t id;
      const char *descr;
};

/**
 * text labels for hardware
 */
static const char *FACT_HW_TXT[] = {
      "Hw Unknown",
      "Linksys",
      "Cisco",
      "Cisco Catalyst",
      "Apple",
      "Apple iBook",
      "Apple Powerbook",
      "Power Mac",
      "Power Mac G4",
      "Power Mac G4 (Graphite)"
};

#if KEEP_THIS_FOR_LATER
/**
 * graph icon for hardware
 */
static const char *FACT_HW_IMG[] = {
      "desktop-64.svg",
      "linksys-64.svg", /* generic linksys */
      "cisco_catalyst_2950_12-64.svg", /* generic cisco hardware */
      "cisco_catalyst_2950_12-64.svg", /* cisco catalyst */
      "Apple",
      "Apple iBook",
      "Apple Powerbook",
      "Power Mac",
      "apple_powermac_g4-64.svg",
      "apple_powermac_g4_graphite-64.svg"
};
#endif

static const char *FACT_PROP_TXT[] = {
      "Prop Unknown",
      "Exists",
      "Alive",
      "Hostname",
      "Uptime",
      "Reboot",
      "DHCP"
};


/**
 * initialize all of our reporting data structures
 */
int reporting_init(void)
{
      Last_Dump = 0;

      lhash_init(&Macs, HASH_SLOTS_MAC, hash_func_mac, mac_cmp);
            lhash_set_key_funcs(&Macs, mac_copy, free);
            lhash_set_val_funcs(&Macs, NULL, lhash_free_cb);

      lhash_init(&Mac_Facts, HASH_SLOTS_MAC, hash_func_mac, mac_cmp);
            lhash_set_key_funcs(&Mac_Facts, mac_copy, free);
            lhash_set_val_funcs(&Mac_Facts, NULL, facts_free);

      lhash_init(&Mac_Traffic, HASH_SLOTS_MAC, hash_func_mac, mac_cmp);
            lhash_set_key_funcs(&Mac_Traffic, mac_copy, free);
            lhash_set_val_funcs(&Mac_Traffic, NULL, lhash_free_cb);

      lhash_init(&Mac_Traffic_Ext, HASH_SLOTS_MAC, hash_func_mac, mac_cmp);
            lhash_set_key_funcs(&Mac_Traffic_Ext, mac_copy, free);
            lhash_set_val_funcs(&Mac_Traffic_Ext, NULL, free);

      lhash_init_ez(&Mac_Hints_Pos, 256);
            lhash_set_key_func_cmp(&Mac_Hints_Pos, mac_cmp);
            lhash_set_key_funcs(&Mac_Hints_Pos, mac_copy, free);
            lhash_set_val_funcs(&Mac_Hints_Pos, NULL, free);

      lhash_init(&Ips, 128, hash_func_ip, ip_cmp);
            lhash_set_key_func_cmp(&Ips, ip_cmp);
            lhash_set_key_funcs(&Ips, ip_copy, free);
            lhash_set_val_funcs(&Ips, NULL, lhash_free_cb);

      lhash_init(&Ip_Facts, 128, hash_func_ip, ip_cmp);
            lhash_set_key_funcs(&Ip_Facts, ip_copy, free);
            lhash_set_val_funcs(&Ip_Facts, NULL, facts_free);

      lhash_init(&Ip_Traffic, 128, hash_func_ip, ip_cmp);
            lhash_set_key_funcs(&Ip_Traffic, ip_copy, free);
            lhash_set_val_funcs(&Ip_Traffic, NULL, lhash_free_cb);

      lhash_init_ez(&Ip_Traffic_Total, 128);
            lhash_set_key_func_cmp(&Ip_Traffic_Total, ip_cmp);
            lhash_set_key_funcs(&Ip_Traffic_Total, ip_copy, free);
            lhash_set_val_funcs(&Ip_Traffic_Total, NULL, free);

      lhash_init_ez(&Ip_Hostname, 64);
            lhash_set_key_func_cmp(&Ip_Hostname, ip_cmp);
            lhash_set_key_funcs(&Ip_Hostname, ip_copy, free);
            lhash_set_val_funcs(&Ip_Hostname, NULL, lhash_free_cb);

      lhash_init_ez(&Ip_Hints_Pos, 64);
            lhash_set_key_func_cmp(&Ip_Hints_Pos, ip_cmp);
            lhash_set_key_funcs(&Ip_Hints_Pos, ip_copy, free);
            lhash_set_val_funcs(&Ip_Hints_Pos, NULL, free);

      lhash_init_ez(&Ip_Hints_Tcp_Syn, 64);
            lhash_set_key_func_cmp(&Ip_Hints_Tcp_Syn, ip_cmp);
            lhash_set_key_funcs(&Ip_Hints_Tcp_Syn, ip_copy, free);
            lhash_set_val_funcs(&Ip_Hints_Tcp_Syn, NULL, free);

      lhash_init(&Ip_Hints_Ping, HASH_SLOTS_IP, hash_func_ip, ip_cmp);
            lhash_set_key_funcs(&Ip_Hints_Ping, ip_copy, free);
            lhash_set_val_funcs(&Ip_Hints_Ping, NULL, free);

      lhash_init(&Ip_Subnet, 16, hash_func_ip, ip_cmp);
            lhash_set_key_funcs(&Ip_Subnet, ip_copy, free);
            lhash_set_val_funcs(&Ip_Subnet, NULL, free);

      lhash_init_ez(&Mac_Hints_Bootp, 256);
            lhash_set_key_func_cmp(&Mac_Hints_Bootp, mac_cmp);
            lhash_set_key_funcs(&Mac_Hints_Bootp, mac_copy, free);
            lhash_set_val_funcs(&Mac_Hints_Bootp, NULL, free);

      lhash_init_ez(&Macs_Calced, 256);
            lhash_set_key_func_cmp(&Macs_Calced, mac_cmp);

      /* machines */

      lhash_init_ez(&Mac_Machine, 256);
            lhash_set_key_func_cmp(&Mac_Machine, mac_cmp);

      lhash_init_ez(&Ip_Machine, 256);
            lhash_set_key_func_cmp(&Ip_Machine, ip_cmp);

      list_init(&Machines);

      return 1;
}


/**
 *
 */
static int mac_cmp(const void *va, const void *vb)
{
      const struct mac *a, *b;
      if (NULL == va) {
            return (NULL == vb ? 0 : -1);
      } else if (NULL == vb) {
            return 1;
      }
      a = va, b = vb;
      return memcmp(a->addr, b->addr, sizeof a->addr);
}

/**
 * allocate and duplicate a struct mac *
 */
static void * mac_copy(void *v)
{
      const struct mac *orig;
      struct mac *copy;
#ifdef _DEBUG
      assert(NULL != v);
#endif
      orig = v;
      copy = malloc(sizeof *copy);
      if (NULL == copy) {
            DEBUGF(__FILE__, __LINE__, "");
            return NULL;
      }
      memcpy(copy, orig, sizeof *copy);
      return copy;
}

/**
 * compare 2 struct ips for equality
 * @note callback signature
 */
int ip_cmp(const void *va, const void *vb)
{
      const struct ip *a, *b;

#ifdef _DEBUG
      assert(NULL != va);
      assert(NULL != vb);
#endif

      if (NULL == va) {
            return (NULL == vb ? 0 : -1);
      } else if (NULL == vb) {
            return 1;
      }

      a = va, b = vb;

      if (a->version != b->version) {
            /* IPv4 vs IPv6 comparison */
            const struct ip *v4, *v6;
#ifdef _DEBUG
            assert((IPV4 == a->version && IPV6 == b->version) || (IPV6 == a->version && IPV4 == b->version));
#endif
            if (IPV4 == a->version) {
                  v4 = a, v6 = b;
            } else {
                  v4 = b, v6 = a;
            }

            /* ISATAP -- ::0:5efe:W.X.Y.Z -- IPv4 embedded in IPv6 */
            /* FIXME: endian assumptions? */
            if (0 == memcmp("\x00\x00\x5e\xfe", v6->addr.v6 + 8, 4)) {
                  return memcmp(v6->addr.v6 + 12, v4->addr.v4, sizeof v4->addr.v4);
            }
            
            /* otherwise the addresses are not comparable.... */

            return (a->version > b->version ? 1 : -1);
      }

      if (0 == memcmp(a->mask, IP_MASK_EMPTY, sizeof a->mask) && 0 == memcmp(b->mask, IP_MASK_EMPTY, sizeof b->mask)) {
            /* straight-up comparison */
            switch (a->version) {
            case IPV4:
                  return memcmp(&a->addr.v4, &b->addr.v4, sizeof a->addr.v4);
                  break;
            case IPV6:
                  return memcmp(&a->addr.v6, &b->addr.v6, sizeof a->addr.v6);
                  break;
            default:
                  DEBUGF(__FILE__, __LINE__,
                        "unexpected IP version: %d", a->version);
                  abort();
                  return 0;
                  break;
            }
      } else { /* respect the masks */
            int unsigned i;
            u_char m, ca, cb;
            for (i = 0; i < (IPV6 == a->version ? sizeof a->addr.v4 : sizeof a->addr.v6); i++) {
                  m = a->mask[i] & b->mask[i];
                  ca = m & a->addr.v6[i]; /* NOTE: v6 and v4 line up */
                  cb = m & b->addr.v6[i];
                  if (ca != cb)
                        return (ca > cb ? 1 : -1);
            }
            return 0;
      }
}


/**
 * cmp function for mac/ip combos
 */
int mac_ip_ptr_cmp(const void *va, const void *vb)
{
      const struct mac_ip_ptr *a, *b;
      int ip;
#ifdef _DEBUG
      assert(NULL != va);
      assert(NULL != vb);
#endif
      a = va, b = vb;
      ip = ip_cmp(a->ip, b->ip);
      if (ip)
            return ip;
      return mac_cmp(a->mac, b->mac);
}

/**
 * cmp function for mac/ip combos
 */
void * mac_ip_ptr_copy(void *v)
{
      struct mac_ip_ptr *mp;
#ifdef _DEBUG
      assert(NULL != v);
#endif
      mp = malloc(sizeof *mp);
      if (NULL == mp) {
            DEBUGF(__FILE__, __LINE__, "malloc(%u) failed", sizeof *mp);
            return NULL;
      }
      memcpy(mp, v, sizeof *mp);
      return mp;
}

/**
 * allocate and duplicate a struct ip *
 */
void * ip_copy(void *v)
{
      const struct ip *orig = v;
      struct ip *copy;
#ifdef _DEBUG
      assert(NULL != v);
#endif
      copy = malloc(sizeof *copy);
      if (NULL == copy) {
            DEBUGF(__FILE__, __LINE__, "malloc(%u) failed", sizeof *copy);
            return NULL;
      }
      memcpy(copy, orig, sizeof *copy);
      return copy;
}

/**
 *
 */
static int int_cmp(const void *a, const void *b)
{
      const int *ai, *bi;
#ifdef _DEBUG
      assert(NULL != a);
      assert(NULL != b);
#endif
      ai = a, bi = b;
      return (*ai > *bi ? 1 : *bi > *ai ? -1 : 0);
}

/**
 * 
 */
static void * int_copy(void *v)
{
      int *copy;
#ifdef _DEBUG
      assert(NULL != v);
#endif
      copy = malloc(sizeof *copy);
      if (NULL == copy) {
            DEBUGF(__FILE__, __LINE__, "int_copy failed!");
      } else {
            *copy = *(int *)v;
      }
      return copy;
}

/**
 *
 */
static int uint32t_cmp(const void *a, const void *b)
{
      const uint32_t *ai, *bi;
#ifdef _DEBUG
      assert(NULL != a);
      assert(NULL != b);
#endif
      ai = a, bi = b;
      return (*ai > *bi ? 1 : *bi > *ai ? -1 : 0);
}

/**
 * 
 */
static void * uint32t_copy(void *v)
{
      uint32_t *copy;
#ifdef _DEBUG
      assert(NULL != v);
#endif
      copy = malloc(sizeof *copy);
      if (NULL == copy) {
            DEBUGF(__FILE__, __LINE__, "uint32t_copy failed!");
      } else {
            *copy = *(uint32_t *)v;
      }
      return copy;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#if 0
/**
 * 
 */
/* FIXME: hardcoding! */
static facts * facts_new(void)
{
      facts *fc = malloc(sizeof *fc);
      if (NULL == fc) {
            DEBUGF(__FILE__, __LINE__, "malloc facts failed");
            return NULL;
      }

      lhash_init_ez(&fc->facts, 64);
            lhash_set_key_funcs(&fc->facts_sure, int_copy, free);
            lhash_set_key_func_cmp(&fc->facts_sure, int_cmp);

      lhash_init_ez(&fc->facts_sure, 64);
            lhash_set_key_funcs(&fc->facts_sure, fact_sure_copy, free);
            lhash_set_key_func_cmp(&fc->facts_sure, fact_sure_cmp);

      return fc;
}
#endif

/**
 *
 */
static void facts_free(void *v)
{
      /* FIXME: write! */
}

/**
 * 
 */
/* FIXME: hardcoding! */
static machine * machine_new(void)
{
      machine *ent = malloc(sizeof *ent);
      if (NULL == ent) {
            DEBUGF(__FILE__, __LINE__, "malloc machine failed");
            return NULL;
      }
      lhash_init_ez(&ent->macs, 2);
            lhash_set_key_func_cmp(&ent->macs, mac_cmp);
      ent->hostname[0] = '\0';
      
      lhash_init_ez(&ent->hostnames, 8);

      ent->os_guess[0] = '\0';
      memset(ent->hints_pos, 0, sizeof ent->hints_pos);
      memset(ent->hints_tcp_syn, 0, sizeof ent->hints_tcp_syn);
      memset(ent->hints_ping, 0, sizeof ent->hints_ping);
      memset(ent->hints_bootp, 0, sizeof ent->hints_bootp);

      lhash_init_ez(&ent->roles, 2);
            lhash_set_key_func_cmp(&ent->roles, int_cmp);
            lhash_set_key_funcs(&ent->roles, int_copy, free);
      ent->hw = 0;
      ent->traffic_total = ent->traffic_ext = 0;

      return ent;
}

/**
 * de-allocate a machine and its members
 * @note callback signature for use in an lhash
 */
static void machine_free(void *v)
{
      machine *m = v;
#ifdef _DEBUG
      assert(NULL != v);
#endif
      lhash_destroy(&m->macs);
      lhash_destroy(&m->roles);
      free(m);
}


/**
 * see if it's time to report all stats
 */
void check_dump(void)
{
      time_t now = time(NULL);

#ifdef DEBUG_CHECK_DUMP
      DEBUGF(__FILE__, __LINE__, "check_dump: now: %lu, last: %lu, freq: %lu",
            now, Last_Dump, Dump_Freq);
#endif

      if (Last_Dump + Dump_Freq <= now) {
            dump_world();
            Last_Dump = now;
      }
}

/**
 * ensure a mac exists
 * @fixme it's inefficient to do this every time, no?
 */
/* FIXME: hardcoding */
static lhash_t * mac_ensure(const struct mac *mac)
{
       lhash_t *h;
#ifdef _DEBUG
      assert(NULL != mac);
#endif


      h = lhash_get(&Macs, mac);
      if (NULL == h) {
            h = lhash_new(2, hash_func_ip, ip_cmp);
                  lhash_set_key_funcs(h, ip_copy, free);
                  lhash_set_val_funcs(h, NULL, free);
            if (NULL != h) {
                  lhash_set(&Macs, mac, h);
            }
      }

      return h;
}

/**
 * ensure entries for ip in Ips and Ip_Facts exist
 * @return Ip[ip] hash
 */
/* FIXME: hardcoding */
static lhash_t * ip_ensure(const struct ip *ip)
{
       lhash_t *h;
#ifdef _DEBUG
      assert(NULL != ip);
#endif

#ifdef DEBUG_MORE
      { /* we see this a lot... */
            char ip_buf[IP_ADDR_BUFLEN];
            DEBUGF(__FILE__, __LINE__, "ip_ensure(\"%s\")",
                  ip_addr_to_str(ip, ip_buf, sizeof ip_buf));
      }
#endif

      h = lhash_get(&Ips, ip);
      if (NULL == h) {
            h = lhash_new(2, hash_func_mac, mac_cmp);
                  lhash_set_key_funcs(h, mac_copy, free);
                  lhash_set_val_funcs(h, NULL, free);
            if (NULL != h)
                  lhash_set(&Ips, ip, h);
      }

      return h;
}

#if 0
/**
 * increment a fact
 */
static int fact_set(facts *f, int fact)
{
      int *val;
#ifdef _DEBUG
      assert(NULL != f);
#endif
      val = lhash_get(&f->facts, &fact);
      if (NULL == val) {
            int one = 1;
            lhash_set(&f->facts, &fact, &one);
            return one;
      } else {
            (*val)++;
            return *val;
      }
}

/**
 * increment a fact for which sureness is a factor
 */
static int fact_sure_set(facts *f, int fact, int sure)
{
      fact_sure key;
      fact_sure *val;
#ifdef _DEBUG
      assert(NULL != f);
#endif
      key.fact = fact;
      key.sure = sure;
      val = lhash_get(&f->facts_sure, &key);
      if (NULL == val) {
            key.count = 1;
            lhash_set(&f->facts_sure, &key, &key.count);
            return key.count;
      } else {
            (val->count)++;
            return val->count;
      }
}
#endif

/**
 *
 */
void report_mac(const struct mac *mac)
{
#ifdef _DEBUG
      assert(NULL != mac);
#endif
      VV {
            char mac_buf[MAC_ADDR_BUFLEN];
            printf("mac %s seen\n",
                  mac_addr_to_str(mac, mac_buf, sizeof mac_buf));
      }
      (void)mac_ensure(mac);
}

/**
 * record that traffic was sent from one mac to another
 */
void report_mac_traffic(const struct mac *from, const struct mac *to,
      size_t *packets, size_t *bytes)
{
      lhash_t *h;
      traffic_stats *stats;
      int unsigned i;

#ifdef _DEBUG
      assert(NULL != from);
      assert(NULL != to);
      assert(NULL != packets);
      assert(NULL != bytes);
#endif

      /* check that ips already exist... otherwise we end up adding a ton of external IPs */
      if (NULL == lhash_get(&Macs, from) || NULL == lhash_get(&Macs, to)) {
            return;
      }

#ifdef DOT_NO_DIRECTION
      /* only record in one direction */
      if (mac_cmp(from, to) < 0) {
            const struct mac *tmp;
            tmp = from;
            from = to;
            to = tmp;
      }
#endif

      h = lhash_get(&Mac_Traffic, from);
      if (NULL == h) {
            h = lhash_new(4, hash_func_mac, mac_cmp); /* FIXME: hardcode */
            if (NULL == h) {
                  DEBUGF(__FILE__, __LINE__, "lhash_new failed");
                  return;
            }
            lhash_set_key_funcs(h, mac_copy, free);
            lhash_set_val_funcs(h, NULL, free);
            lhash_set(&Mac_Traffic, from, h);
      }
      stats = lhash_get(h, to);
      if (NULL == stats) {
            stats = malloc(sizeof *stats);
            if (NULL == stats) {
                  DEBUGF(__FILE__, __LINE__, "malloc(%u) failed", sizeof *stats);
                  return;
            }
            memset(stats, 0, sizeof *stats);
            lhash_set(h, to, stats);
      }

      /* add stats */
      for (i = 0; i < PROT_COUNT; i++) {
            stats->packets[i] += packets[i];
            stats->bytes[i] += bytes[i];
      }
}

/**
 *
 */
void report_mac_hint(const struct mac *mac, int hint)
{
      int (*hints)[HINTS_POS_COUNT];
#ifdef _DEBUG
      assert(NULL != mac);
      if (hint > 0 && hint < (int)HINTS_POS_COUNT) abort();
#endif
#ifdef DEBUG
      {
            char mac_buf[IP_ADDR_BUFLEN];
            printf("mac_hint %s -> %s (%d)\n",
                  mac_addr_to_str(mac, mac_buf, sizeof mac_buf),
                  hint_to_str(hint), hint);
      }
#endif
      hints = lhash_get(&Mac_Hints_Pos, mac);
      if (NULL == hints) {
            hints = malloc(sizeof *hints);
            if (NULL == hints) {
                  DEBUGF(__FILE__, __LINE__, "malloc(%u) failed!", sizeof *hints);
                  return;
            }
            memset(hints, 0, sizeof *hints);
            lhash_set(&Mac_Hints_Pos, mac, hints);
      }
      (*hints)[hint]++;
}

/**
 *
 */
void report_ip_hint(const struct ip *ip, int hint)
{
      int (*hints)[HINTS_POS_COUNT];
#ifdef _DEBUG
      assert(NULL != ip);
      assert(hint > 0 && hint < (int)HINTS_POS_COUNT);
#endif
#ifdef DEBUG
      {
            char ip_buf[IP_ADDR_BUFLEN];
            printf("ip_hint %s -> %s (%d)\n",
                  ip_addr_to_str(ip, ip_buf, sizeof ip_buf),
                  hint_to_str(hint), hint);
      }
#endif
      hints = lhash_get(&Ip_Hints_Pos, ip);
      if (NULL == hints) {
            hints = malloc(sizeof *hints);
            if (NULL == hints) {
                  DEBUGF(__FILE__, __LINE__, "malloc(%u) failed!", sizeof *hints);
                  return;
            }
            memset(hints, 0, sizeof *hints);
            lhash_set(&Ip_Hints_Pos, ip, hints);
      }
      (*hints)[hint]++;
}

/**
 *
 */
void report_ip_tcp_syn_sig(const struct ip *ip, int hint)
{
      int (*hints)[TCP_SYN_PRINT_COUNT];
#ifdef _DEBUG
      assert(NULL != ip);
      assert(hint < (int)TCP_SYN_PRINT_COUNT);
#endif
#ifdef _DEBUG
      {
            char ip_buf[IP_ADDR_BUFLEN];
            printf("tcp_syn %s -> %d\n",
                  ip_addr_to_str(ip, ip_buf, sizeof ip_buf), hint);
      }
#endif
      hints = lhash_get(&Ip_Hints_Tcp_Syn, ip);
      if (NULL == hints) {
            hints = malloc(sizeof *hints);
            if (NULL == hints) {
                  DEBUGF(__FILE__, __LINE__, "malloc(%u) failed!", sizeof *hints);
                  return;
            }
            memset(hints, 0, sizeof *hints);
            lhash_set(&Ip_Hints_Tcp_Syn, ip, hints);
      }
      (*hints)[hint]++;
}

/**
 *
 */
void report_ip_ping_sig(const struct ip *ip, int hint)
{
      int (*hints)[PING_SIG_COUNT];
#ifdef _DEBUG
      assert(NULL != ip);
      assert(hint < (int)PING_SIG_COUNT);
#endif
      (void)ip_ensure(ip);
      VVVV {
            char ip_buf[IP_ADDR_BUFLEN];
            printf("ping sig %s -> %d\n",
                  ip_addr_to_str(ip, ip_buf, sizeof ip_buf), hint);
      }
      hints = lhash_get(&Ip_Hints_Ping, ip);
      if (NULL == hints) {
            hints = malloc(sizeof *hints);
            if (NULL == hints) {
                  DEBUGF(__FILE__, __LINE__, "malloc(%u) failed!", sizeof *hints);
                  return;
            }
            memset(hints, 0, sizeof *hints);
            lhash_set(&Ip_Hints_Ping, ip, hints);
      }
      (*hints)[hint]++;
}

/**
 *
 */
void report_mac_bootp_sig(const struct mac *mac, int hint)
{
      int (*hints)[BOOTP_SIG_COUNT];
#ifdef _DEBUG
      assert(NULL != mac);
      assert(hint < (int)BOOTP_SIG_COUNT);
#endif
      mac_ensure(mac);
#ifdef _DEBUG
      {
            char mac_buf[MAC_ADDR_BUFLEN];
            printf("bootp sig %s -> %d\n",
                  mac_addr_to_str(mac, mac_buf, sizeof mac_buf), hint);
      }
#endif
      hints = lhash_get(&Mac_Hints_Bootp, mac);
      if (NULL == hints) {
            hints = malloc(sizeof *hints);
            if (NULL == hints) {
                  DEBUGF(__FILE__, __LINE__, "malloc(%u) failed!", sizeof *hints);
                  return;
            }
            memset(hints, 0, sizeof *hints);
            lhash_set(&Mac_Hints_Bootp, mac, hints);
      }
      (*hints)[hint]++;
}


/**
 * report a fact on a certain mac
 */
void report_mac_fact(const struct mac *mac, int fact)
{
      lhash_t *facts;
      int *count;
#ifdef _DEBUG
      assert(NULL != mac);
#endif
      VVVV {
            char mac_buf[MAC_ADDR_BUFLEN];
            printf("fact %s : %s\n",
                  mac_addr_to_str(mac, mac_buf, sizeof mac_buf),
                  fact_to_str(fact));
      }
      if (NULL != mac_ensure(mac)) {
            facts = lhash_get(&Mac_Facts, mac);
            if (NULL == facts) {
                  facts = lhash_new(4, hash_func_int, int_cmp);
                  if (NULL == facts) {
                        DEBUGF(__FILE__, __LINE__, "");
                        return;
                  }
                        lhash_set_key_funcs(facts, int_copy, free);
                        lhash_set_val_funcs(facts, int_copy, free);
                        lhash_set(&Mac_Facts, mac, facts);
            }
            count = lhash_get(facts, &fact);
            if (NULL == count) {
                  count = malloc(sizeof *count);
                  if (NULL == count)
                        return;
                  *count = 1;
                  lhash_set(facts, &fact, count);
                  return;
            }
            (*count)++;
      }
}

/**
 * link mac <-> ip addresses
 */
void report_mac_ip(const struct mac *mac, const struct ip *ip)
{
      lhash_t *h;
      int *count;

#ifdef _DEBUG
      assert(NULL != mac);
      assert(NULL != ip);
#endif

      if (!ip_is_private(ip) || mac_is_special(mac)) {
            return;
      }

      VVV {
            char mac_buf[MAC_ADDR_BUFLEN];
            char ip_buf[IP_ADDR_BUFLEN];
            printf("mac %s <-> ip %s\n",
                  mac_addr_to_str(mac, mac_buf, sizeof mac_buf),
                  ip_addr_to_str(ip, ip_buf, sizeof ip_buf));
      }

      /* ensure mac entry, add mac -> ip, increment ip seen count */
      h = mac_ensure(mac);
      if (NULL != h) do {
            count = lhash_get(h, ip);
            if (NULL == count) {
                  count = malloc(sizeof *count);
                  if (NULL == count) {
                        DEBUGF(__FILE__, __LINE__, "wtf?!");
                        break;
                  }
                  *count = 1;
                  lhash_set(h, ip, count);
                  break;
            }
            (*count)++;
      } while (0);

      /* ensure ip entry, add ip -> mac, increment mac seen count */
      h = ip_ensure(ip);
      if (NULL != h) do {
            count = lhash_get(h, mac);
            if (NULL == count) {
                  count = malloc(sizeof *count);
                  if (NULL == count)
                        break;
                  *count = 1;
                  lhash_set(h, mac, count);
                  break;
            }
            (*count)++;
      } while (0);
}

/**
 *
 */
void report_mac_os(const struct mac *mac, int os, int weight, const char *extra)
{
      lhash_t *facts;
      int *count;

#ifdef _DEBUG
      assert(NULL != mac);
      /* extra may be NULL */
#endif
      VVV {
            char mac_buf[MAC_ADDR_BUFLEN];
            printf("%s is running %s \"%s\" (%d)\n",
                  mac_addr_to_str(mac, mac_buf, sizeof mac_buf),
                  fact_to_str(os),
                  (NULL != extra ? extra : ""),
                  weight);
      }
      (void)mac_ensure(mac);
      facts = lhash_get(&Mac_Facts, mac);
      if (NULL == facts)
            return;
      count = lhash_get(facts, &os);
      if (NULL == count) {
            count = malloc(sizeof *count);
            if (NULL == count)
                  return;
            *count = 1;
            lhash_set(facts, &os, count);
            return;
      }
      (*count)++;
}

/**
 * report a fact on a certain ip
 */
void report_ip_fact(const struct ip *ip, int fact)
{
      lhash_t *facts;
      int *count;
#ifdef _DEBUG
      assert(NULL != ip);
#endif

      if (!ip_is_private(ip) || ip_is_special(ip)) {
            /* don't record facts for outside IPs or for protocol-special IPs */
            return;
      }

      VVVV {
            char ip_buf[IP_ADDR_BUFLEN];
            printf("fact %s : %s\n",
                  ip_addr_to_str(ip, ip_buf, sizeof ip_buf),
                  fact_to_str(fact));
      }
      if (ip_ensure(ip)) {
            facts = lhash_get(&Ip_Facts, ip);
            if (NULL == facts) {
                  facts = lhash_new(4, hash_func_int, int_cmp);
                  if (NULL == facts) {
                        DEBUGF(__FILE__, __LINE__, "lhash_new failed");
                        return;
                  }
                        lhash_set_key_funcs(facts, int_copy, free);
                        lhash_set_val_funcs(facts, NULL, free);
                        lhash_set(&Ip_Facts, ip, facts);
            }
            count = lhash_get(facts, &fact);
            if (NULL == count) {
                  count = malloc(sizeof *count);
                  if (NULL == count)
                        return;
                  *count = 1;
                  lhash_set(facts, &fact, count);
                  return;
            }
            (*count)++;
      }
}


/**
 *
 */
void report_ip_subnet(const struct ip *ip, const struct ip *subnet)
{
      lhash_t *h;
#ifdef _DEBUG
      assert(NULL != ip);
      assert(NULL != subnet);
#endif
      h = lhash_get(&Ip_Subnet, subnet);
      if (NULL == h) {
            h = lhash_new(64, hash_func_ip, ip_cmp);
            if (NULL == h) {
                  DEBUGF(__FILE__, __LINE__, "lhash_new failed");
                  return;
            }
                  lhash_set_key_funcs(h, ip_copy, free);
                  lhash_set(&Ip_Subnet, ip, h);
      }
      /* FIXME: todo */
}

/**
 * report a hostname for a particular IP...
 * @note hostname needn't been DNS, could be from anywhere we find a descriptive string
 */
void report_ip_hostname(const struct ip *ip, const char *hostname)
{
      lhash_t *h;
      int *count;
#ifdef _DEBUG
      assert(NULL != ip);
      assert(NULL != hostname);
#endif
      h = lhash_get(&Ip_Hostname, ip);
      if (NULL == h) {
            h = lhash_new(1, hash_func_DJB, vxstrcmp);
            if (NULL == h)
                  return;
            lhash_set_key_funcs(h, vxstrdup, free);
            lhash_set_val_funcs(h, NULL, free);
            lhash_set(&Ip_Hostname, ip, h);
      }
      count = lhash_get(h, hostname);
      if (NULL == count) {
            count = malloc(sizeof *count);
            if (NULL == count)
                  return;
            lhash_set(h, hostname, count);
      }
      (*count)++;
}

void report_ip_domain(const struct ip *, const char *);
void report_reboot(const struct ip *);
void report_role_by_mac(int, const struct mac *);
void report_role_by_ip(int, struct ip *);
void report_relation_by_mac(int, const struct mac *, const struct mac *);
void report_relation_by_ip(int, const struct ip *, const struct ip *);

/**
 * record that traffic was sent from one ip to another
 */
/* TODO: needs cleanup */
void report_ip_traffic(const struct mac *macfrom, const struct ip *ipfrom,
      const struct mac *macto, const struct ip *ipto)
{
      const struct ip *ipin, *ipout; /* */
      const struct mac *macin, *macgateway; /*  */
      lhash_t *h;
      int unsigned *count;

#ifdef _DEBUG
      assert(NULL != ipfrom);
      assert(NULL != macfrom);
      assert(NULL != ipto);
      assert(NULL != macto);
#endif

#ifdef DEBUG_IP_TRAFFIC
      {
            char macfrom_buf[MAC_ADDR_BUFLEN], macto_buf[MAC_ADDR_BUFLEN];
            char ipfrom_buf[IP_ADDR_BUFLEN], ipto_buf[IP_ADDR_BUFLEN];

            DEBUGF(__FILE__, __LINE__,
                  "report_ip_traffic(macfrom=\"%s\"(%p), ipfrom=\"%s\"(%p), macto=\"%s\"(%p), ipto=\"%s\"(%p))",
                  mac_addr_to_str(macfrom, macfrom_buf, sizeof macfrom_buf),
                  (void *)lhash_get(&Macs, macfrom),
                  ip_addr_to_str(ipfrom, ipfrom_buf, sizeof ipfrom_buf),
                  (void *)lhash_get(&Ips, ipfrom),
                  mac_addr_to_str(macto, macto_buf, sizeof macto_buf),
                  (void *)lhash_get(&Macs, macto),
                  ip_addr_to_str(ipto, ipto_buf, sizeof ipto_buf),
                  (void *)lhash_get(&Ips, ipto));
      }
#endif


      /* is this outgoing/incoming traffic? */
      do {

            if (!ip_is_private(ipto)) {
                  ipin = ipfrom, ipout = ipto;
                  macin = macfrom, macgateway = macto;
            } else if (!ip_is_private(ipfrom)) {
                  ipin = ipto, ipout = ipfrom;
                  macin = macto, macgateway = macfrom;
            } else { /* internal traffic */
#ifdef HMMM_DOES_THIS_HURT_OR_HELP
                  /* NOTE: i found that misconfigured(?) clients with the wrong macs would cause
                   * server machines to become huge jumbles of mac/ip combos... */
                  report_mac_ip(macfrom, ipfrom);
                  report_mac_ip(macto, ipto);
#endif
                  break;
            }

#ifdef DEBUG_IP_TRAFFIC
            {
                  char ip_buf[IP_ADDR_BUFLEN];
                  DEBUGF(__FILE__, __LINE__, "%s is an external IP",
                        ip_addr_to_str(ipout, ip_buf, sizeof ip_buf));
            }
#endif

            if (!mac_is_special(macgateway) && !ip_is_special(ipout)) {
                  /* external traffic means he's routing... */
#ifdef DEBUG_GATEWAY
                  char mac_buf[MAC_ADDR_BUFLEN], ip_buf[IP_ADDR_BUFLEN];
                  DEBUGF(__FILE__, __LINE__, "ip %s/mac %s is a router!",
                        ip_addr_to_str(ipout, ip_buf, sizeof ip_buf),
                        mac_addr_to_str(macgateway, mac_buf, sizeof mac_buf));
#endif
                  report_mac_fact(macgateway, FACT_ROLE_ROUTER);
            }

            {
                  /* record src->gateway traffic */
                  /* NOTE: mac traffic is already being reported by the ethernet parsers... */

                  /* record gateway->dest traffic */
                  count = lhash_get(&Mac_Traffic_Ext, macgateway);
                  if (NULL == count) {
                        count = malloc(sizeof *count);
                        if (NULL == count) {
                              DEBUGF(__FILE__, __LINE__, "malloc(%u) failed!", sizeof *count);
                              break;
                        }
                        *count = 0;
                        lhash_set(&Mac_Traffic_Ext, macgateway, count);
                  }
                  (*count)++;
            }

            return; /* we're done */

      } while (0);

      if (ip_is_broadcast(ipto)) {
            /* we've figured out a subnet this IP belongs to, maybe(?) */
            return;
      } else {
            /* what to do here? current problem: if a box is scanning the network sequentially
             * and we just add addresses blindly, our graph will be unmanagably large. however,
             * if we don't report something then no one will know the net is being sequentially
             * scanned... */
            /* for now, just skip */
            struct ip *ipfrom_check, *ipto_check;
            ipfrom_check = lhash_get(&Ips, ipfrom);
            ipto_check = lhash_get(&Ips, ipto);
            if ((NULL == ipfrom_check || NULL == ipto_check)
                  && !ip_is_special(ipto)) {
                  char ip_buf[IP_ADDR_BUFLEN];
                  printf("traffic ");
                  printf((ipfrom_check ? "%s" : "(%s)"), ip_addr_to_str(ipfrom, ip_buf, sizeof ip_buf));
                  printf(" -> ");
                  printf((ipto_check ? "%s" : "(%s)"), ip_addr_to_str(ipto, ip_buf, sizeof ip_buf));
                  printf(" doesn't exist...\n");
                  return;
            }
      }

      /* record traffic to->from */

#ifdef DOT_NO_DIRECTION
      /* only record in one direction */
      if (ip_cmp(ipfrom, ipto) < 0) {
            const struct ip *iptmp;
            iptmp = ipfrom;
            ipfrom = ipto;
            ipto = iptmp;
      }
#endif

      h = lhash_get(&Ip_Traffic, ipfrom);
      if (NULL == h) {
            h = lhash_new(16, hash_func_ip, ip_cmp);
            if (NULL == h) {
                  DEBUGF(__FILE__, __LINE__, "");
                  return;
            }
            lhash_set_key_funcs(h, ip_copy, free);
            lhash_set_val_funcs(h, NULL, free);
            lhash_set(&Ip_Traffic, ipfrom, h);
      }
      count = lhash_get(h, ipto);
      if (NULL == count) {
            count = malloc(sizeof *count);
            if (NULL == count) {
                  DEBUGF(__FILE__, __LINE__, "");
                  lhash_delete(&Ip_Traffic, ipfrom);
                  return;
            }
            *count = 0;
            lhash_set(h, ipto, count);
      }
      (*count)++;

#ifdef DEBUG_FACTS
#if 0
      {
            char from_buf[IP_ADDR_BUFLEN];
            char to_buf[IP_ADDR_BUFLEN];
            h = lhash_get(&Ip_Traffic, ipfrom);
            count = lhash_get(h, ipto);

            printf("%s -> %s (%d) (%d entr%s)\n",
                  ip_addr_to_str(ipfrom, from_buf, sizeof from_buf),
                  ip_addr_to_str(ipto, to_buf, sizeof to_buf),
                  *count, lhash_size(h),
                  (1 == lhash_size(h) ? "y" : "ies"));
      }
#endif
#if 0
      { /* ARGH?! iterating over traffic isn't working... */
            lhash_t *traf;
            char ip_buf[IP_ADDR_BUFLEN];

            /* print traffic to other IPs */
            printf("FROM %s:\n", ip_addr_to_str(from, ip_buf, sizeof ip_buf));
            traf = lhash_get(&Ip_Traffic, ipfrom);
            if (NULL == traf) {
                  printf("\tNULL!?\n");
                  assert(0);
            } else {
                  lhash_each_t each_traf;
                  lhash_entry_t *entry_traf;
                  lhash_each_reset(&each_traf, traf);
                  while (NULL != (entry_traf = lhash_next(&each_traf))) {
                        printf("\t%s(%d)\n",
                              ip_addr_to_str(entry_traf->key, ip_buf, sizeof ip_buf),
                              *(int *)entry_traf->val);
                  }
            }
      }
#endif
#endif
}

/**
 * calculate color of edge based on activity
 * goes from light gray -> black -> red
 */
char * colorize(size_t i, char *color, size_t colorlen)
{
      int c = 0xF0 - ((int)(log(i) / LOG2) * 10);
      if (c < 0) {
            c = (int)(c < -255 ? 255 : -c);
            snprintf(color, colorlen, "#%02X0000", c);
      } else {
            snprintf(color, colorlen, "#%02X%02X%02X", c, c, c);
      }
      return color;
}

/**
 * write all seen IPs and asociated MACs
 */
static void dump_ips(void)
{
      lhash_each_t each_mac, each_ip;
      lhash_entry_t *entry_mac, *entry_ip;
      char mac_buf[MAC_ADDR_BUFLEN];
      char ip_buf[IP_ADDR_BUFLEN];

      printf("IP -> MAC\n----------------------------------\n");
      lhash_each_reset(&each_ip, &Ips);
      while (NULL != (entry_ip = lhash_next(&each_ip))) {
            printf("%24s:",
                  ip_addr_to_str(entry_ip->key, ip_buf, sizeof ip_buf));
            lhash_each_reset(&each_mac, entry_ip->val);
            while (NULL != (entry_mac = lhash_next(&each_mac))) {
                  printf(" %s(%d),",
                        mac_addr_to_str(entry_mac->key, mac_buf, sizeof mac_buf),
                        *(int *)entry_mac->val);
            }
            printf("\n");
      }
}

/**
 * dump MAC addresses and associated IPs to stdout
 */
static void dump_macs(void)
{
      lhash_each_t each_mac, each_ip;
      lhash_entry_t *entry_mac, *entry_ip;
      char mac_buf[MAC_ADDR_BUFLEN];
      char ip_buf[IP_ADDR_BUFLEN];

      printf("MAC -> IP\n----------------------------------\n");
      lhash_each_reset(&each_mac, &Macs);
      while (NULL != (entry_mac = lhash_next(&each_mac))) {
            printf("%24s:",
                  mac_addr_to_str(entry_mac->key, mac_buf, sizeof mac_buf));
            lhash_each_reset(&each_ip, entry_mac->val);
            while (NULL != (entry_ip = lhash_next(&each_ip))) {
                  printf(" %s(%d),",
                        ip_addr_to_str(entry_ip->key, ip_buf, sizeof ip_buf),
                        *(int *)entry_ip->val);
            }
            printf("\n");
      }
}

/**
 * dump mac->ips, ip->macs
 */
/* FIXME: holy hell this is getting too big */
void dump_world(void)
{
      FILE *out;

#if 0
      printf("---------------- MACs (%u) ----------------------\n",
                  lhash_size(&Macs));
      lhash_each_reset(&each_mac, &Macs);
      while (NULL != (entry_mac = lhash_next(&each_mac))) {
            printf("%24s:",
                  mac_addr_to_str(entry_mac->key, mac_buf, sizeof mac_buf));
            lhash_each_reset(&each_ip, entry_mac->val);
            while (NULL != (entry_ip = lhash_next(&each_ip))) {
                  printf(" %s(%d),",
                        ip_addr_to_str(entry_ip->key, ip_buf, sizeof ip_buf),
                        *(int *)entry_ip->val);
            }
            printf("\n");
      }
#endif

      /* dump extra info to stdout */
#ifdef _DEBUG
      dump_macs();
      dump_ips();
#endif

      out = fopen(DOT_FILE, "w");
      if (NULL == out) {
            perror(DOT_FILE);
            ERRF(__FILE__, __LINE__, "couldn't open '%s'!", DOT_FILE);
            return;
      }

      /* include header file */
      {
            FILE *headfile;
            char Dot_Header_File[PATH_MAX] = xstr(LANMAP_DATADIR) DOT_HEADER_FILE;
            char headline[1024];

            headfile = fopen(Dot_Header_File, "r");
            if (NULL == headfile) {
                  perror(Dot_Header_File);
                  fclose(out);
                  return;
            }
#ifndef WIN32
            flock(fileno(headfile), LOCK_EX);
#endif
            while (fgets(headline, sizeof headline, headfile)) {
                  fputs(headline, out);
            }

            /* height=1.400,width=1.400 */
            fprintf(out, "\"Outside\" [style=solid,height=0.7,color=white,fontsize=12,shapefile=\"%s/graph/img/cloud.%s\"];\n",
                  xstr(LANMAP_DATADIR), Image_Type);
#ifndef WIN32
            flock(fileno(headfile), LOCK_UN);
#endif
            fclose(headfile);
      }

      /* group ip/macs into groups by relation into "machines" */
      machines_calc();
      V { machines_dump(); }

      { /* scope */
      list_node_t *node;
      machine *ent;
      lhash_each_t each_mac, each_ip;
      lhash_entry_t *entry_mac, *entry_ip;
      lhash_t *mac_ips;
      char mac_buf[MAC_ADDR_BUFLEN];
      char ip_buf[IP_ADDR_BUFLEN];
      char bytes_buf[32] = "";
      char colorbuf[8] = ""; /* FIXME "#FFFFFF" */

      for (
            node = list_first(&Machines);
            node != NULL;
            node = list_node_next(node)
      ) {
            char nodebuf[1024]; /* for node name */
            int the_role;
            ent = list_node_data(node);

            the_role = FACT_ROLE; /* reset */
            nodebuf[0] = '\0';

            /* construct hostnames */
            if (lhash_size(&ent->hostnames) > 0) {
                  strlcat(nodebuf, "\\\"", sizeof nodebuf);
                  {
                        lhash_each_t each_name;
                        lhash_entry_t *ent_name;
                        int first = 1;
                        lhash_each_reset(&each_name, &ent->hostnames);
                        while (NULL != (ent_name = lhash_next(&each_name))) {
                              if (!first)
                                    strlcat(nodebuf, ",", sizeof nodebuf);
                              strlcat(nodebuf, ent_name->key, sizeof nodebuf);
                              first = 0;
                        }
                  }
                  strlcat(nodebuf, "\\\"\\n", sizeof nodebuf);
            }

            if ('\0' != ent->os_guess[0]) {
                  strlcat(nodebuf, "(", sizeof nodebuf);
                  strlcat(nodebuf, ent->os_guess, sizeof nodebuf);
                  strlcat(nodebuf, ")\\n", sizeof nodebuf);
            }

            /* role and/or hardware */
            if (lhash_size(&ent->roles)) {
                  lhash_each_t each;
                  lhash_entry_t *entry;
                  lhash_each_reset(&each, &ent->roles);
                  while (NULL != (entry = lhash_next(&each))) {
                        the_role = *(int *)entry->key;
                        strlcat(nodebuf, fact_to_str(the_role), sizeof nodebuf);
                        strlcat(nodebuf, ", ", sizeof nodebuf);
                  }
                  nodebuf[strlen(nodebuf) - 2] = '\0'; /* chop trailing ", " */
                  if (ent->hw) {
                        strlcat(nodebuf, " (", sizeof nodebuf);
                        strlcat(nodebuf, fact_to_str(ent->hw), sizeof nodebuf);
                        strlcat(nodebuf, ")", sizeof nodebuf);
                  }
                  strlcat(nodebuf, "\\n", sizeof nodebuf);
            } else if (ent->hw) {
                  strlcat(nodebuf, fact_to_str(ent->hw), sizeof nodebuf);
                  strlcat(nodebuf, "\\n", sizeof nodebuf);
            }

            lhash_each_reset(&each_mac, &ent->macs);
            while (NULL != (entry_mac = lhash_next(&each_mac))) {
                  mac_ips = entry_mac->val;
                  strlcat(nodebuf, " ", sizeof nodebuf);
                  strlcat(nodebuf, mac_addr_to_str(entry_mac->key, mac_buf, sizeof mac_buf), sizeof nodebuf);
                  /* mac description */
                  strlcat(nodebuf, " (", sizeof nodebuf);
                  strlcat(nodebuf, mac_addr_descr(entry_mac->key), sizeof nodebuf);
                  strlcat(nodebuf, ")", sizeof nodebuf);
                  strlcat(nodebuf, "\\n", sizeof nodebuf);
                  if (NULL == mac_ips)
                        continue;
                  lhash_each_reset(&each_ip, mac_ips);
                  while (NULL != (entry_ip = lhash_next(&each_ip))) {
                        strlcat(nodebuf, "   ", sizeof nodebuf);
                        strlcat(nodebuf, ip_addr_to_str(entry_ip->key, ip_buf, sizeof ip_buf), sizeof nodebuf);
                        strlcat(nodebuf, "\\n", sizeof nodebuf);
                  }
            } /* each mac */

            fprintf(out, "\"%lu\" [label=\"%s\",shapefile=\"%s/%s.%s\"];\n",
                  (long unsigned)ent, nodebuf, xstr(LANMAP_DATADIR) ICON_DIR, FACT_ROLE_IMG[the_role % FACT_ROLE], Image_Type);

      } /* end Machines loop */

      fprintf(out, "/* mac traffic... */\n");

      { /* mac traffic scope */
            lhash_each_t each_traf;
            lhash_entry_t *entry_traf;
            /* record traffic */
            lhash_each_reset(&each_traf, &Mac_Traffic);
            while (NULL != (entry_traf = lhash_next(&each_traf))) {
                  traffic_stats *stats;
                  machine *ent = lhash_get(&Mac_Machine, entry_traf->key);
                  VVVV {
                        printf("%s -> machine %lu\n",
                              mac_addr_to_str(entry_traf->key, mac_buf, sizeof mac_buf), (long unsigned)ent);
                  }
                  /* print traffic to other IPs */
                  if (NULL == ent) {
                        fprintf(out, "\"%s (%s)\"\n",
                              mac_addr_to_str(entry_traf->key, mac_buf, sizeof mac_buf),
                              mac_addr_descr(entry_traf->key));
                  } else {
                        size_t total_bytes;
                        int unsigned i;

                        lhash_each_reset(&each_mac, entry_traf->val);
                        while (NULL != (entry_mac = lhash_next(&each_mac))) {
                              /* there may be IP traffic going to a destination which does not have a
                               * machine entry; if so, we use the ip address as the name of the node,
                               * else we use the pointer address of the machine, ugh :/ */
                              char dest[256];
                              int unsigned most_traffic = 1; /* most traffic entry */
                              machine *machine_dest = lhash_get(&Mac_Machine, entry_mac->key);
                              if (NULL == machine_dest) {
                                    char mac_dest[IP_ADDR_BUFLEN];
                                    snprintf(dest, sizeof dest, "%s (%s)",
                                          mac_addr_to_str(entry_mac->key, mac_dest, sizeof mac_dest),
                                          mac_addr_descr(entry_mac->key));
                              } else {
                                    snprintf(dest, sizeof dest, "%lu", (long unsigned)machine_dest);
                              }

                              stats = entry_mac->val;

                              /* total up traffic stats... do this shit elsewhere! */
                              total_bytes = 0;
                              if (NULL != stats) {
                                    for (i = 0; i < PROT_COUNT; i++) {
#ifdef DEBUG_PROT_STATS
                                          if (stats->bytes[i] != 0)
                                                printf("protocol %s(%u) -> bytes %u\n",
                                                      Prot_Descr[i].tla, i, stats->bytes[i]);
#endif
                                          total_bytes += stats->bytes[i];
                                          if (stats->bytes[i] > stats->bytes[most_traffic])
                                                most_traffic = i;
                                    }
                              }

                              (void)colorize(total_bytes, colorbuf, sizeof colorbuf);
                              fprintf(out, "\"%lu\" -- \"%s\" [weight=\"%d\",label=\"%s\\n%s(%.0f%%)\",color=\"%s\",fontcolor=\"%s\",fontsize=%u];\n",
                                    (long unsigned)ent, dest, 
                                    /* weight has to be low, or else the graph gets extremely large */
                                    (int unsigned)(log(total_bytes) / LOG2),
                                    bytes_to_str(total_bytes, bytes_buf, sizeof bytes_buf),
                                    Prot_Descr[most_traffic].tla,
                                    ((float)(NULL == stats ? total_bytes : stats->bytes[most_traffic]) / total_bytes) * 100.0F,
                                    colorbuf, colorbuf,
                                    /* FIXME: so ugly */
                                    MAX(GRAPH_FONT_SIZE_MIN, (int unsigned)(log(total_bytes /  KILOBYTE) / LOG2)));
                        }
                  }
            }
      } /* mac traffic scope */

      fprintf(out, "/* external traffic... */\n");

      /* TODO: implement byte and packet tracking */
      { /* external traffic scope */
            lhash_each_t each_traf;
            lhash_entry_t *entry_traf;

            /* record traffic */
            lhash_each_reset(&each_traf, &Mac_Traffic_Ext);
            while (NULL != (entry_traf = lhash_next(&each_traf))) {
                  int unsigned count;
                  machine *ent;
                  
                  ent = lhash_get(&Mac_Machine, entry_traf->key);
                  if (NULL == ent) {
                        continue;
                  }
                  count = *(int unsigned *)entry_traf->val;
                  VVVV {
                        printf("%s -> %s\n",
                              mac_addr_to_str(entry_traf->key, mac_buf, sizeof mac_buf),
                              GRAPH_NODE_OUTSIDE);
                  }

                  (void)colorize(count, colorbuf, sizeof colorbuf);
                  fprintf(out, "\"%lu\" -- \"%s\" [weight=\"%d\",label=\"%u packets\",color=\"%s\",fontcolor=\"%s\",fontsize=%u];\n",
                        (long unsigned)ent, GRAPH_NODE_OUTSIDE,
                        (int unsigned)(log(count) / LOG2),
                        count,
                        colorbuf, colorbuf, (int unsigned)(log(count) / LOG2));
            }
      } /* external traffic scope */

      } /* machine scope */

      machines_cleanup();

      fprintf(out, DOT_FOOTER);
      fclose(out);


#ifdef _DEBUG
      printf(
            "Macs slots:%u, size:%u\n"
            "Mac_Facts slots:%u, size:%u\n"
            "Mac_Traffic slots:%u, size:%u\n"
            "Mac_Traffic_Ext slots:%u, size:%u\n"
            "Mac_Hints_Pos slots:%u, size:%u\n"
            "Ips slots:%u, size:%u\n"
            "Ip_Facts slots:%u, size:%u\n"
            "Ip_Traffic slots:%u, size:%u\n"
            "Ip_Traffic_Total slots:%u, size:%u\n"
            "Ip_Hostname slots:%u, size:%u\n"
            "Ip_Hints_Pos slots:%u, size:%u\n"
            "Ip_Hints_Tcp_Syn slots:%u, size:%u\n"
            "Ip_Hints_Ping slots:%u, size:%u\n"
            "Mac_Hints_Bootp slots:%u, size:%u\n",
            lhash_slots(&Macs), lhash_size(&Macs),
            lhash_slots(&Mac_Facts), lhash_size(&Mac_Facts),
            lhash_slots(&Mac_Traffic), lhash_size(&Mac_Traffic),
            lhash_slots(&Mac_Traffic_Ext), lhash_size(&Mac_Traffic_Ext),
            lhash_slots(&Mac_Hints_Pos), lhash_size(&Mac_Hints_Pos),
            lhash_slots(&Ips), lhash_size(&Ips),
            lhash_slots(&Ip_Facts), lhash_size(&Ip_Facts),
            lhash_slots(&Ip_Traffic), lhash_size(&Ip_Traffic),
            lhash_slots(&Ip_Traffic_Total), lhash_size(&Ip_Traffic_Total),
            lhash_slots(&Ip_Hostname), lhash_size(&Ip_Hostname),
            lhash_slots(&Ip_Hints_Pos), lhash_size(&Ip_Hints_Pos),
            lhash_slots(&Ip_Hints_Tcp_Syn), lhash_size(&Ip_Hints_Tcp_Syn),
            lhash_slots(&Ip_Hints_Ping), lhash_size(&Ip_Hints_Ping),
            lhash_slots(&Mac_Hints_Bootp), lhash_size(&Mac_Hints_Bootp)
      );
#endif

      /* generate graph */
      {
            char cmd[PATH_MAX * 3];
#if 0
#ifdef _DEBUG
            {
                  char cwd[PATH_MAX];
                  DEBUGF(__FILE__, __LINE__, "cwd: %s, running: %s",
                        getcwd(cwd, sizeof cwd), DOT_CMD);
            }
#endif
#endif
            snprintf(cmd, sizeof cmd, "%s -T%s -o %s/tmp.lanmap %s && mv %s/tmp.lanmap %slanmap.%s && rm %s",
                  Run_Program, Image_Type, xstr(LANMAP_DATADIR), DOT_FILE, xstr(LANMAP_DATADIR), Output_Dir, Image_Type, DOT_FILE);
            DEBUGF(__FILE__, __LINE__, "cmd: %s", cmd);
            errno = 0;
            if (-1 == system(cmd) || 0 != errno) {
                  perror(cmd);
            }
      }

}


/**
 * recursively match MAC -> IP -> MAC until we've traversed all.
 * whole function takes place inside machine_calc()'s
 * @see machine_calc
 */
static void machines_calc_recurse(const struct mac *mac, lhash_t *ips, machine *ent)
{
      lhash_each_t each_ip;
      lhash_entry_t *entry_ip;

      lhash_each_t each_fact;
      lhash_entry_t *entry_fact;
      lhash_t *facts;

#ifdef _DEBUG
      assert(NULL != mac);
      assert(NULL != ips);
      assert(NULL != ent);
#endif

      lhash_set(&Mac_Machine, mac, ent);
      
      /* mac->facts->machine... wrong placement, a hack */
      do {
            char mac_buf[MAC_ADDR_BUFLEN];
            facts = lhash_get(&Mac_Facts, mac);
            if (NULL == facts)
                  break;
            lhash_each_reset(&each_fact, facts);
            while (NULL != (entry_fact = lhash_next(&each_fact))) {
                  int fact = *(int *)entry_fact->key;

                  VVVV {
                  printf("mac %s fact (%d): %s\n",
                        mac_addr_to_str(mac, mac_buf, sizeof mac_buf),
                        fact, fact_to_str(fact));
                  }

                  if (FACT_ROLE & fact) {
                        lhash_set(&ent->roles, &fact, NULL);
                  } else if (FACT_HW & fact) {
                        ent->hw = fact;
                  }
            }
      } while (0);

      lhash_each_reset(&each_ip, ips);
      while (NULL != (entry_ip = lhash_next(&each_ip))) {
            lhash_each_t each_mac;
            lhash_entry_t *entry_mac;
            lhash_t *macs;

            { /* hostname */
                  lhash_t *names = lhash_get(&Ip_Hostname, entry_ip->key);
                  /* TODO: sort by popularity */
                  if (NULL != names) {
                        lhash_each_t each_name;
                        lhash_entry_t *ent_name;
                        lhash_each_reset(&each_name, names);
                        while (NULL != (ent_name = lhash_next(&each_name))) {
                              lhash_set(&ent->hostnames, ent_name->key, NULL);
                        }
                  }
            }

            /* ip->facts->machine... wrong placement, a hack */

            do {
                  facts = lhash_get(&Ip_Facts, entry_ip->key);
                  if (NULL == facts)
                        break;
                  lhash_each_reset(&each_fact, facts);
                  while (NULL != (entry_fact = lhash_next(&each_fact))) {
                        int fact = *(int *)entry_fact->key;
                        if (FACT_ROLE & fact) {
                              lhash_set(&ent->roles, &fact, NULL);
                        } else if (FACT_HW & fact) {
                              ent->hw = fact;
                        } 
                  }
            } while (0);

            { /* count up total traffic */
                  int *count = lhash_get(&Ip_Traffic_Total, entry_ip->key);
                  if (NULL != count)
                        ent->traffic_total += *count;
            }


            /* * * * merge hints * * * * */

            do { /* merge positive os hints  */
                  size_t i;
                  int (*hints)[HINTS_POS_COUNT] = lhash_get(&Ip_Hints_Pos, entry_ip->key);
                  if (NULL == hints) {
#ifdef DEBUG_NO_HINTS
                        char ip_buf[IP_ADDR_BUFLEN];
                        DEBUGF(__FILE__, __LINE__, "no ip hints for ip %s!",
                              ip_addr_to_str(entry_ip->key, ip_buf, sizeof ip_buf));
#endif
                        break;
                  }
#ifdef DEBUG_HINTS_MERGE
                  {
                        char mac_buf[MAC_ADDR_BUFLEN], ip_buf[IP_ADDR_BUFLEN];
                        printf("pos os hints ip %s -> mac %s... ",
                              ip_addr_to_str(entry_ip->key, ip_buf, sizeof ip_buf),
                              mac_addr_to_str(mac, mac_buf, sizeof mac_buf));
                  }
#endif
                  for (i = 0; i < HINTS_POS_COUNT; i++) {
                        ent->hints_pos[i] += (*hints)[i];
#ifdef DEBUG_HINTS_MERGE
                        printf("[%d]+%d=%d, ", i, (*hints)[i], ent->hints_pos[i]);
#endif
                  }
#ifdef DEBUG_HINTS_MERGE
                        printf("\n");
#endif
            } while (0);

            do { /* merge tcp syn hints */
                  size_t i;
                  int (*hints)[] = lhash_get(&Ip_Hints_Tcp_Syn, entry_ip->key);
                  if (NULL == hints) {
#ifdef DEBUG_NO_HINTS
                        DEBUGF(__FILE__, __LINE__, "no tcp hints for this ip!");
#endif
                        break;
                  }
#ifdef _DEBUG
                  {
                        char mac_buf[MAC_ADDR_BUFLEN], ip_buf[IP_ADDR_BUFLEN];
                        printf("tcp syn hints ip %s -> mac %s... ",
                              ip_addr_to_str(entry_ip->key, ip_buf, sizeof ip_buf),
                              mac_addr_to_str(mac, mac_buf, sizeof mac_buf));
                  }
#endif

                  for (i = 0; i < TCP_SYN_PRINT_COUNT; i++) {
                        ent->hints_tcp_syn[i] += (*hints)[i];
#ifdef _DEBUG
                        printf("[%d]+%d=%d, ", i, (*hints)[i], ent->hints_tcp_syn[i]);
#endif
                  }
#ifdef _DEBUG
                        printf("\n");
#endif
            } while (0);

            do { /* merge ping hints */
                  size_t i;
                  int (*hints)[] = lhash_get(&Ip_Hints_Ping, entry_ip->key);
                  if (NULL == hints) {
#ifdef DEBUG_NO_HINTS
                        DEBUGF(__FILE__, __LINE__, "no ping for this ip!");
#endif
                        break;
                  }
#ifdef _DEBUG
                  {
                        char mac_buf[MAC_ADDR_BUFLEN], ip_buf[IP_ADDR_BUFLEN];
                        printf("ping hints ip %s -> mac %s... ",
                              ip_addr_to_str(entry_ip->key, ip_buf, sizeof ip_buf),
                              mac_addr_to_str(mac, mac_buf, sizeof mac_buf));
                  }
#endif

                  for (i = 0; i < PING_SIG_COUNT; i++) {
                        ent->hints_ping[i] += (*hints)[i];
#ifdef _DEBUG
                        printf("[%d]+%d=%d, ", i, (*hints)[i], ent->hints_ping[i]);
#endif
                  }
#ifdef _DEBUG
                        printf("\n");
#endif
            } while (0);

            do { /* merge bootp hints */
                  size_t i;
                  int (*hints)[] = lhash_get(&Mac_Hints_Bootp, mac);
                  if (NULL == hints) {
#ifdef DEBUG_NO_HINTS
                        DEBUGF(__FILE__, __LINE__, "no bootp for this ip!");
#endif
                        break;
                  }
                  for (i = 0; i < BOOTP_SIG_COUNT; i++) {
                        ent->hints_bootp[i] += (*hints)[i];
#ifdef _DEBUG
                        printf("[%d]+%d=%d, ", i, (*hints)[i], ent->hints_bootp[i]);
#endif
                  }
#ifdef _DEBUG
                        printf("\n");
#endif
            } while (0);

            /* * * * end merge hints * * * * */

            lhash_set(&Ip_Machine, entry_ip->key, ent);
            /* do not group special ips to a machine... */
            if (ip_is_special(entry_ip->key))
                  continue;
            macs = lhash_get(&Ips, entry_ip->key);
            if (NULL == macs)
                  continue;
            lhash_each_reset(&each_mac, macs);
            while (NULL != (entry_mac = lhash_next(&each_mac))) {
                  struct mac *mac2 = entry_mac->key;
                  lhash_t *ips;
                  if (lhash_key_exists(&Macs_Calced, &mac2)) {
                        #ifdef _DEBUG
                              char mac_buf[MAC_ADDR_BUFLEN], ip_buf[IP_ADDR_BUFLEN];
                              DEBUGF(__FILE__, __LINE__, "%s/%s already calced...",
                                    mac_addr_to_str(mac2, mac_buf, sizeof mac_buf),
                                    ip_addr_to_str(entry_ip->key, ip_buf, sizeof ip_buf));
                        #endif
                        continue;
                  }
                  ips = lhash_get(&Macs, mac2);
                  lhash_set(&Macs_Calced, &mac2, NULL);
                  lhash_set(&ent->macs, mac2, ips);
                  machines_calc_recurse(mac2, ips, ent);
            }
      }

}

/**
 * join MAC<->IP mappings into machines
 */
static void machines_calc(void)
{
      lhash_each_t each;
      lhash_entry_t *entry;

      lhash_each_reset(&each, &Macs);
      while (NULL != (entry = lhash_next(&each))) {
            machine *ent;
            struct mac *mac = entry->key;
            ent = machine_new();
            if (NULL == ent)
                  continue;
            lhash_set(&ent->macs, mac, entry->val);
            machines_calc_recurse(mac, entry->val, ent);
            list_append_ptr(&Machines, ent);

            do { /* merge mac hints  */
                  size_t i;
                  int (*hints)[HINTS_POS_COUNT] = lhash_get(&Mac_Hints_Pos, mac);
                  if (NULL == hints) {
#ifdef DEBUG_NO_HINTS
                        DEBUGF(__FILE__, __LINE__, "no mac hints for ip!");
#endif
                        break;
                  }
#ifdef DEBUG_HINTS_MERGE
                  {
                        char mac_buf[MAC_ADDR_BUFLEN], ip_buf[IP_ADDR_BUFLEN];
                        printf("pos os hints mac %s... ",
                              mac_addr_to_str(mac, mac_buf, sizeof mac_buf));
                  }
#endif
                  for (i = 0; i < HINTS_POS_COUNT; i++) {
                        ent->hints_pos[i] += (*hints)[i];
#ifdef DEBUG_HINTS_MERGE
                        printf("[%d]+%d=%d, ", i, (*hints)[i], ent->hints_pos[i]);
#endif
                  }
#ifdef DEBUG_HINTS_MERGE
                        printf("\n");
#endif
            } while (0);


            /* calculate operating system guess */
            {
                  size_t i;
                  int j;

                  os_tree_reset(); /* reset tree so we can use it */

                  /* apply general positive os hints to tree */
                  for (i = 0; i < HINTS_POS_COUNT; i++) {
#ifdef DEBUG_HINT_TREE
                        if (ent->hints_pos[i])
                              DEBUGF(__FILE__, __LINE__, "pos [%d]=%d", i, ent->hints_pos[i]);
#endif
                        if (ent->hints_pos[i] > 0 && HINTS_POS[i].len > 0) {
                              /* adjust down and then multiply by certainty */
                              for (j = 0; j < HINTS_POS[i].len; j++) {
                                    int score = (int)((ceil(log(ent->hints_pos[i]) / LOG2) * HINTS_POS[i].os[j][1]) + 1);
#ifdef DEBUG_HINT_TREE
                                    if (score > 0) {
                                          DEBUGF(__FILE__, __LINE__, "count %d -> score %d",
                                                ent->hints_pos[i], score);
                                    }
#endif
                                    os_tree_apply(HINTS_POS[i].os[j][0], score, HINTS_POS[i].os[j][2]);
                              }
                        }
                  }

                  /* apply tcp syn hints to tree */
                  for (i = 0; i < TCP_SYN_PRINT_COUNT; i++) {
#ifdef DEBUG_HINT_TREE
                        if (ent->hints_tcp_syn[i])
                              DEBUGF(__FILE__, __LINE__, "syn [%d]=%d", i, ent->hints_tcp_syn[i]);
#endif
                        if (ent->hints_tcp_syn[i] > 0 && TCP_SYN_PRINTS[i].oslen > 0) {
                              /* adjust down and then multiply by certainty */
                              for (j = 0; j < TCP_SYN_PRINTS[i].oslen; j++) {
                                    int score = (int)((ceil(log(ent->hints_tcp_syn[i]) / LOG2) * TCP_SYN_PRINTS[i].os[j][1]) + 1);
#ifdef DEBUG_HINT_TREE
                                    if (score > 0) {
                                          DEBUGF(__FILE__, __LINE__, "count %d -> score %d",
                                                ent->hints_pos[i], score);
                                    }
#endif
                                    os_tree_apply(TCP_SYN_PRINTS[i].os[j][0], score, TCP_SYN_PRINTS[i].os[j][2]);
                              }
                        }
                  }

                  /* apply ping sig to tree */
                  for (i = 0; i < PING_SIG_COUNT; i++) {
#ifdef DEBUG_HINT_TREE
                        if (ent->hints_ping[i])
                              DEBUGF(__FILE__, __LINE__, "ping[%d]=%d", i, ent->hints_ping[i]);
#endif
                        if (ent->hints_ping[i] > 0 && PING_SIGS[i].oslen > 0) {
                              /* adjust down and then multiply by certainty */
                              for (j = 0; j < PING_SIGS[i].oslen; j++) {
                                    int score = (int)((ceil(log(ent->hints_ping[i]) / LOG2) * PING_SIGS[i].os[j][1]) + 1);
#ifdef DEBUG_HINT_TREE
                                    if (score > 0) {
                                          DEBUGF(__FILE__, __LINE__, "count %d -> score %d",
                                                ent->hints_ping[i], score);
                                    }
#endif
                                    os_tree_apply(PING_SIGS[i].os[j][0], score, PING_SIGS[i].os[j][2]);
                              }
                        }
                  }

                  /* apply bootp sig to tree */
                  for (i = 0; i < BOOTP_SIG_COUNT; i++) {
#ifdef DEBUG_HINT_TREE
                        if (ent->hints_bootp[i])
                              DEBUGF(__FILE__, __LINE__, "bootp[%d]=%d", i, ent->hints_bootp[i]);
#endif
                        if (ent->hints_bootp[i] > 0 && BOOTP_SIGS[i].oslen > 0) {
                              /* adjust down and then multiply by certainty */
                              for (j = 0; j < BOOTP_SIGS[i].oslen; j++) {
                                    int score = (int)((ceil(log(ent->hints_bootp[i]) / LOG2) * BOOTP_SIGS[i].os[j][1]) + 1);
#ifdef DEBUG_HINT_TREE
                                    if (score > 0) {
                                          DEBUGF(__FILE__, __LINE__, "count %d -> score %d",
                                                ent->hints_bootp[i], score);
                                    }
#endif
                                    os_tree_apply(BOOTP_SIGS[i].os[j][0], score, BOOTP_SIGS[i].os[j][2]);
                              }
                        }
                  }

                  os_calculate(ent->os_guess, sizeof ent->os_guess);
            }

      }

      /* machines calculated */
}

/**
 * dump something like this to stdout:
 * Machine:
 *    Hostname "pizzabox"
 *    Operating System: "I dunno" (positive!)
 *    00:11:22:33:44:55
 *          1.2.3.4
 *    66:77:88:99:AA:BB
 *          5.6.7.8
 */
static void machines_dump(void)
{
      list_node_t *node;
      machine *ent;
      lhash_each_t each_mac, each_ip;
      lhash_entry_t *entry_mac, *entry_ip;
      lhash_t *mac_ips;
      char mac_buf[MAC_ADDR_BUFLEN];
      char ip_buf[IP_ADDR_BUFLEN];

      printf("====== %lu Machine%s =======\n",
            (long unsigned)list_size(&Machines),
            (1 == list_size(&Machines) ? "" : "s"));

      for (
            node = list_first(&Machines);
            node != NULL;
            node = list_node_next(node)
      ) {
            ent = list_node_data(node);

            printf(
                  "Machine (%lu):\n"
                  "  Roles: ",
                  (long unsigned)ent
            );

            lhash_each_reset(&each_mac, &ent->roles);
            while (NULL != (entry_mac = lhash_next(&each_mac)))
                  printf("%s ", fact_to_str(*(int *)entry_mac->key));

            printf("\n"
                  "  Hostname: \"%s\"\n"
                  "  Operating System: \"%s\"\n",
                  ent->hostname,
                  ent->os_guess
            );

            lhash_each_reset(&each_mac, &ent->macs);
            while (NULL != (entry_mac = lhash_next(&each_mac))) {
                  mac_ips = entry_mac->val;
                  printf("  %s\n",
                        mac_addr_to_str(entry_mac->key, mac_buf, sizeof mac_buf));
                  if (NULL == mac_ips)
                        continue;
                  lhash_each_reset(&each_ip, mac_ips);
                  while (NULL != (entry_ip = lhash_next(&each_ip))) {
                        printf("    %s\n",
                              ip_addr_to_str(entry_ip->key, ip_buf, sizeof ip_buf));
                  }
            }
      }
      
}

/**
 *
 */
static void machines_cleanup(void)
{
      lhash_delete_all(&Mac_Machine);
      lhash_delete_all(&Ip_Machine);
      list_nodes_free(&Machines, machine_free);
      lhash_delete_all(&Macs_Calced);
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */



/**
 *
 */
const char * fact_to_str(int fact)
{
      int mod = fact % 0x400;
      if (fact >= FACT_ROLE && fact < FACT_ROLE_WHOOPS_TOO_HIGH) {
            return FACT_ROLE_TXT[mod];
      } else if (fact >= FACT_PROP && fact < FACT_PROP_WHOOPS_TOO_HIGH) {
            return FACT_PROP_TXT[mod];
      } else if (fact >= FACT_HW && fact < FACT_HW_WHOOPS_TOO_HIGH) {
            return FACT_HW_TXT[mod];
      } else {
            return "?"; /* MAGIC: */
      }
}


/**
 * convert hint index to human-readable string
 */
const char * hint_to_str(int hint)
{
      if (HINT_NONE > hint || hint > HINT_OOPS_TOO_HIGH)
            return HINT_TXT[HINT_NONE];
      return HINT_TXT[hint];
}

static const char *Byte_Str_Suffix[] = {
      "bytes", "KB", "MB", "GB", "TB", "PB", "EB"
};

/**
 *
 */
static const char * bytes_to_str(size_t bytes, char *buf, size_t buflen)
{
      size_t factor = 0, tmp, mult; 

#ifdef _DEBUG
      assert(NULL != buf);
#endif

      /* figure out scale factor */
      for (tmp = bytes; tmp >= KILOBYTE; tmp >>= 10, factor++);

      mult = 1 << (10 * factor);

      if (factor < sizeof Byte_Str_Suffix / sizeof Byte_Str_Suffix[0]) {
            snprintf(buf, buflen, "%.1f %s",
                  (float)bytes / mult,
                  Byte_Str_Suffix[factor]);
      } else {
            snprintf(buf, buflen, "%u^%lu(?!)", KILOBYTE, (long unsigned)factor);
      }

#if 0
      DEBUGF(__FILE__, __LINE__, "bytes_to_str() bytes:%u, factor:%u, tmp:%u, str:\"%s\"...",
            bytes, factor, tmp, buf);
#endif

      return buf;
}


Generated by  Doxygen 1.6.0   Back to index