Beacon packet to assist in protocol trace synchrontization

Blue Bar separator

Syncing up two (or more) network traces should be simple because all analyzers are running network time protocol (NTP) clients using the same time server. In reality clocks are never in sync and you have to try to find the same packets in all the traces. Depending on where and how the traces where captured you may be able to match TCP packets based on IP addresses, port numbers, sequence numbers and the IP identification field.

:"matching
Figure 1 - matching frames using TCP and IP header values

However some traces, for example from active and standby network adapter taps or from two load balancing network adapter taps, will not have the same set of TCP packets. For them you have to look for broadcast UDP packets and match based on the sender's IP address, port numbers and the IP identification field.

:matching
:matching
Figure 2 - Matching frames using UDP header values

What do you do if there are no IP broadcast packets? In that case you are reduced to looking for common timings in such things as ARP packets.

:gratuitous
Figure 3 - Gratuitous ARP matching

Of course these last two options only work if the traces are taken on the same subnet or at least in the same broadcast domain.

Syncing up events on a host to a network trace should also be simple because both host and analyzer are running NTP clients using the same time server. The reality is much worse because not only are the clocks not in sync but typically event logs and traces have nothing you can match on.

In order to get around these issues I wrote the beacon program (VOS STCP and MS Windows console application source). It sends a UDP message containing a unique beacon number, a time stamp to the nearest second and an ID message. Figure 4 shows the packet. Note the almost 10 second difference between the time stamp that the analyzer places on the frame (20:04:31.961596) and the time stamp in the packet itself (20:04:22).

:image
Figure 4 - Image of Beacon packet

The destination IP address can be a unique host or a directed broadcast (network/subnet broadcast address). By using a unique host address you can easily follow the path through the network from sender to target host across multiple networks, assuming you have analyzers on those networks,

By using the limited broadcast address you are guaranteed that every analyzer in the broadcast domain will see the packet. Unfortunately, every host will also see them. Even at its fastest of 1 beacon per second this should not be a significant drain on any host. However, there is a way to get a packet to every analyzer in a broadcast domain without using an actual broadcast. First, you find an IP address that is not being used. Ordinarily you cannot send packets to that address because there is nothing to respond to an ARP request so there is no mapping between IP address and Ethernet MAC address. The second step is to define a MAC address for that IP address.

 
arp 164.152.77.40 -set 0:1:2:3:4:5                                             
164.152.77.40 mapped to mac address 00-01-02-03-04-05                          

ready  20:13:13

Figure 5 - Setting up for a non-broadcast broadcast

Once that is done packets can be sent and since none of the switches have seen the MAC address as a source address they must flood the packet out all network ports. Since the MAC address is for a unique host all hosts not in promiscuous mode will ignore it at the chip level, no interrupts or CPU resources needed. It is a non-broadcast broadcast.

:image-of-non-broadcast-broadcast
Figure 6 - Non-broadcast broadcast

If all host IP addresses in the subnet are assigned you can still use this technique - provided there is at least one host in the subnet that the beacon system does not communicate with. You assign the IP address for that host the bogus MAC address. Even that host will ignore the beacon packet because the Ethernet MAC address does no't match. Only if the host's Ethernet adapter is running in promiscuous mode will the beacon packet be forwarded up the IP stack.

As figure 7 shows beacon takes 4 arguments. An IP address a port number, how many seconds to sleep between beacons and a message. If the message has spaces it must be enclosed in quotes. The beacon program will echo the arguments before starting.

 
beacon 164.152.77.40 20000 10 'm15 non-broadcast broadcast'                    
beacon 164.152.77.40 20000 10 m15 non-broadcast broadcast

Figure 7 - Running beacon

If you do not supply all arguments you will get a usage message

 
beacon
Usage:
     beacon IP-Address Port-Number Seconds-Between-Beacons ID                  

Figure 8 - Beacon usage message

There is some sanity checking on the arguments. The IP address string must at least be long enough to be a real IP address (7 characters) but not too long (> 15 characters). The port number must be positive and less than 65536. The sleep time must be positive and if the ID message is longer than 32 characters it will be truncated to 32.

beacon.c for VOS STCP

/* beacon.c (for VOS STCP) begins here

Version 0.00 07-11-22
Version 0.10 10-11-26 Added disclaimer

noah.davids@stratus.com

See
  http://noahdavids.org/self_published/beacon.html for documentation */ 

/* This software is provided on an "AS IS" basis, WITHOUT ANY WARRANTY OR ANY SUPPORT OF ANY KIND. 
   The AUTHOR SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
   PARTICULAR PURPOSE. This disclaimer applies, despite any verbal representations of any kind provided
   by the author or anyone else.
*/

#include <sys/select.h>                                                      
#include <prototypes/inet_proto.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <c_utilities.h>
#include <time.h>

#define bzero(s, len)             memset((char *)(s), 0, len)

int errno;

getCurrentDateTime (char * szDT)
{
time_t tTime;
struct tm *tmLT;

tTime = time ((time_t *) 0);
tmLT = localtime (&tTime);

sprintf (szDT, "%4ld-%02ld-%02ld %02ld:%02ld:%02ld",
          tmLT -> tm_year+1900,
          tmLT -> tm_mon,
          tmLT -> tm_mday,
          tmLT -> tm_hour,
          tmLT -> tm_min,
          tmLT -> tm_sec);
}


main (argc, argv)
int    argc;
char   *argv [];

{
    char                 szIPAddr [16];
    unsigned int         iPort;
    int                  iSleepTime;
    char                 szID [33];

    int                  sd;
    int                  on = 1;
    struct sockaddr      saddr;
    struct sockaddr_in   *sin;

    char                 szBuffer [100];
    long                 lBeaconCount;
    char                 szDateTime [32];

    if (argc != 5)
       {
       printf ("%s %s\n", "Usage:\n\tbeacon IP-Address Port-Number",
          "Seconds-Between-Beacons ID");
       exit (0);
       }

    if (strlen (argv [1]) > 15)
       {
       printf ("First argument (%s) is too long to be a valid IP address.\n",
          argv [1]);
       exit (0);
       }
    else if (strlen (argv [1]) < 7)
       {
       printf ("First argument (%s) is too short to be a valid IP address.\n",
          argv [1]);
       exit (0);
       }

    strcpy (szIPAddr, argv [1]);
    iPort = atoi(argv [2]);
    if (iPort == 0)
       {
       printf ("The port number argument (%s) did not convert into an %s\n",
           argv [2], "interger > 0 correctly");
       exit (0);
       }
    if (iPort > 65535)
       {
       printf ("The port argument (%s) %s\n", argv [2],
            "is larger than the maximum allowed port (65535)\n");
       exit (1);
       }

    iSleepTime = atoi (argv [3]);
    if (iSleepTime <= 0)
       {
       printf ("Sleep time (%s) needs to be a number greater than 0\n",
          argv [3]);
       exit (0);
       }

    if (strlen (argv [4]) > 32)
       {
       printf ("ID message (%s) to long truncating at 32 characters\n",
          argv [4]);
       }
    strncpy (szID, argv [4], 32);

    printf ("beacon %s %d %d %s\n", szIPAddr, iPort, iSleepTime, szID);

    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sd < 0)
       {
       printf ("Error %d during socket call\n", errno);
       exit (1);
       }

    if (setsockopt (sd, SOL_SOCKET,
          SO_BROADCAST, (char *) &on, sizeof (on)) < 0)
       {
       printf ("Error %d in setsockopt.\n",errno);
       exit (1);
       }

    sin = (struct sockaddr_in *)&saddr;
    sin->sin_family = AF_INET;
    sin->sin_port = iPort;
    sin->sin_addr.s_addr = inet_addr(szIPAddr);
    if (sin->sin_addr.s_addr == -1)
       {
       printf ("Improper connect address: %s\n", szIPAddr);
       exit (-1);
       }

lBeaconCount = 0;
while (1)
     {
        lBeaconCount++;
        getCurrentDateTime ((char *) &szDateTime);
        sprintf (szBuffer, "Beacon %ld from %s at %s",
               lBeaconCount, szID, szDateTime);
        if (sendto (sd, szBuffer, strlen (szBuffer), 0,
               &saddr, sizeof (saddr)) < 0)
           printf ("Error %d sending beacon #%ld\n", errno, lBeaconCount);
         
        sleep (iSleepTime);
     }
}
/* beacon.c (for VOS STCP) ends here */

beacon.c for Microsoft Windows

To download the executable beacon.exe file click here. The Base64 MD5 digest for beacon.exe is 1B2M2Y8AsgTpgAmY7PhCfg.


// beacon.c (for Microsoft Windows) begins here

// Version 0.00 08-6-29
// Version 0.10 10-11-26 Added disclaimer

// noah.davids@stratus.com

// See
//  http://noahdavids.org/self_published/beacon.html for documentation */ 

// This software is provided on an "AS IS" basis, WITHOUT ANY WARRANTY OR ANY SUPPORT OF ANY KIND. 
// The AUTHOR SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
// PARTICULAR PURPOSE. This disclaimer applies, despite any verbal representations of any kind provided
// by the author or anyone else.


#include "stdafx.h"
#include "winsock2.h"
#include "time.h"

#define bzero(s, len)             memset((char *)(s), 0, len)

int _tmain(int argc, _TCHAR* argv[])
{
    char                 szIPAddr [16];
    unsigned int         iPort;
    int                  iSleepTime;
    char                 szID [33];

    SOCKET               sd;
    int                  on = 1;
    struct sockaddr      sockDestinationAddr;
    struct sockaddr_in   *sin;

    char                 szBuffer [100];
    long                 lBeaconCount;

    #define VERSION_MAJOR         2
    #define VERSION_MINOR         0

    WSADATA	WsaData;
    WORD	VersionRequested;
    short Error;	

    time_t	now;

if (argc != 5)
       {
       printf ("%s %s\n", "Usage:\n\tbeacon IP-Address Port-Number",
          "Seconds-Between-Beacons ID");
       exit (0);
       }

if (strlen (argv [1]) > 15)
   {
   printf ("First argument (%s) is too long to be a valid IP address.\n", argv [1]);
   exit (0);
   }
else if (strlen (argv [1]) < 7)
     {
     printf ("First argument (%s) is too short to be a valid IP address.\n", argv [1]);
     exit (0);
     }

strcpy (szIPAddr, argv [1]);
iPort = atoi(argv [2]);
if (iPort == 0)
   {
   printf ("The port number argument (%s) did not convert into an %s\n",
           argv [2], "interger > 0 correctly");
   exit (0);
   }

if (iPort > 65535)
   {
   printf ("The port argument (%s) %s\n", argv [2],
          "is larger than the maximum allowed port (65535)\n");
   exit (1);
   }

iSleepTime = atoi (argv [3]);
if (iSleepTime <= 0)
   {
   printf ("Sleep time (%s) needs to be a number greater than 0\n", argv [3]);
   exit (0);
   }

if (strlen (argv [4]) > 32)
   {
   printf ("ID message (%s) to long truncating at 32 characters\n", argv [4]);
   }
strncpy (szID, argv [4], 32);

printf ("beacon %s %d %d %s\n", szIPAddr, iPort, iSleepTime, szID);

VersionRequested = MAKEWORD(VERSION_MAJOR, VERSION_MINOR);
Error = WSAStartup(VersionRequested, &WsaData);
if (Error)
   {
   printf ("beacon: No winsock found - exiting\n");
   exit (0);
   }
	
if (LOBYTE(WsaData.wVersion) != VERSION_MAJOR || HIBYTE(WsaData.wVersion) != VERSION_MINOR)
   {
   printf ("beacon: WSA Version problem: wHighVersion:%x, wVersion:%x - exiting\n",
			WsaData.wHighVersion, WsaData.wVersion);
   exit (0);
   }

sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd == INVALID_SOCKET)
   {
   printf ("beacon: Error %d during socket call - exiting\n", errno);
   exit (errno);
   }

Error = setsockopt (sd, SOL_SOCKET, SO_BROADCAST, (char *) &on, sizeof (on));
if (Error == SOCKET_ERROR)
   {
   printf ("Error %d in setsockopt.\n",WSAGetLastError ());
   exit (WSAGetLastError ());
   }

bzero((char *)&sockDestinationAddr, sizeof(struct sockaddr));
sin = (struct sockaddr_in *)&sockDestinationAddr;
sin->sin_family = AF_INET;
sin->sin_port = htons (iPort); 
sin->sin_addr.s_addr = inet_addr (szIPAddr);
if (sin->sin_addr.s_addr == -1)
   {
   printf ("beacon: Improper connect address: %s - exiting\n", szIPAddr);
   exit (-1);
   }

lBeaconCount = 0;
while (1)
     {
     lBeaconCount++;
     time (&now);
     sprintf (szBuffer, "Beacon %ld from %s at %s", lBeaconCount, szID, ctime (&now));
     Error = sendto (sd, (LPSTR) &szBuffer, strlen (szBuffer), 0, (LPSOCKADDR)
			&sockDestinationAddr, sizeof(struct sockaddr));
     if (Error == SOCKET_ERROR)
        {
        printf ("beacon: Error %d sending beacon %ld\n", WSAGetLastError (), lBeaconCount);
        exit (WSAGetLastError ());
        }
         
     _sleep (iSleepTime * 1000);
     }
}

// beacon.c (for Microsoft Windows) ends here



Blue Bar separator
This page was last modified on 10-11-26
mailbox Send comments and suggestions
to ndav1@cox.net