Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Database

Your Own Network Data Snooper


MAY93: YOUR OWN NETWORK DATA SNOOPER

This article contains the following listings: SNOOP.ARC

Rahner is an independent consultant living near Sacramento, California. He can be reached by phone at 916-722-1939 or through CompuServe at 71450,757.


One problem with today's complex computer systems is that they require more and more sophisticated test gear. I remember, for example, when you could use an AM radio to find out if your S-100 bus was working--just by putting it next to the cable and listening for the static. But this trick won't work anymore. Not long ago I was trying to figure out if data packets were being transmitted through my network cable. I couldn't feel, touch, or hear them--even my trusty radio couldn't pick them up. Consequently, I ended up writing my own "network data snooper" that lets me view data packets in real time. The snooper, which is presented in this article, gives me a feel for the continuity of the request packets and their corresponding responses, and logs the received packets to a file for later retrieval. It also allows me to "tune in" specific node addresses and sockets.

For instance, one project I'm working on involves a program running as a Netware-loadable module (NLM) on a file server attached to the network. The NLM acts as a process synchronizer for requests made by the other nodes on the network using IPX or SPX packets. The network uses D-Link's NE-2000-compatible Ethernet interface cards. The heart of these cards is National Semiconductor's DP8390 Network Interface Controller (NIC) chip.

The NIC allows the PC to receive and transmit data packets over the network according to the IEEE 802.3 specification. Table 1 describes the 802.3 basic data-packet format. The minimum size of a transmitted packet is 128 bytes. The first 64 bytes and the FCS are generated by the NIC. The application must provide the destination address, source address, length, and at least 46 bytes of data. If the size of the structure is less than 128 bytes, whatever data happens to be in the transmission buffer will be sent, too.

Table 1: IEEE 802.3 data-packet format.

  Byte     Description
  ------------------------------------------------------------------------

  00-61    Preamble.  These bytes are transmitted before each packet and
           stripped by the NIC as they are received.

  62-63    Start of Frame Delimiter.  Indicates that the meat of the
           packet is about to start and is stripped with the preamble.

  64-69    Destination Address.  Address of the interface board to which
           this packet is destined.

  70-75    Source Address.  Address of the interface board for which this
           packet was generated.

  76-77    Length.  Number of bytes contained within the body of this
           packet.

  78-???   Data.  The body of the packet.

  ?+1-?+4  FCS.  A 32-bit CRC appended to all packets.

NIC Transmissions and Receptions

The NIC performs all packet transactions using an external memory buffer. Because the NIC uses 16-bit addresses, this external buffer can be up to 64 Kbytes. D-Link uses 8K for the DE-1000 (an NE-1000 compatible interface) and 16K for the DE-2000 (NE-2000). Using the NIC's control registers, you can divide the memory into two separate circular buffers--one for transmissions, the other for receptions. I created a simple, two-packet ping-pong buffer for transmissions and use the rest of the NIC's memory for a circular-reception buffer.

Transmissions by their very nature are nonvolatile. They can be scheduled and buffered externally so as to eliminate any asynchronous traits. The amount of time required to transmit a packet at 10 MHz is more than it takes to move a new data packet into position to be transmitted; therefore, in an interrupt-driven system, only two transmit buffers are required.

Receptions will always be asynchronous, ready to catch us at the most inopportune times. The lion's share of available memory should be used to buffer received packets.

To define the reception buffer, the NIC has the page-start and page-stop registers. The page-start register contains the address of the start of the memory area used for packet receptions. The page-stop register contains the address of the memory that immediately follows the last byte used to store received packets. Both of these ports are 8-bit registers, so only the most significant byte of a 16-bit memory address is described. The least significant byte is set to 0. To define a circular reception buffer that starts at 0600h and ends after 08FFh, page start would be loaded with a 6 and page stop with a 9. Most other memory-pointer registers contain only the upper eight bits of an address; therefore, the memory controlled by the NIC is divided into 256-byte logical regions called "pages."

The reception buffer is also controlled by the current-page and boundary-pointer registers. The current-page register points to the page where the next-received packet will be placed by the NIC. The boundary-pointer register contains the page number from which the received packets can be drawn. Both of these registers are updated by the NIC when packets are received and when those packets are removed by the host CPU.

The transmission of packets uses the transmit page-start and transmit byte-count registers. The transmit page-start register contains the page number of the start of a packet to be transmitted. The transmit byte-count register (divided into the most- and least-significant bytes as register 0 and 1) tells the NIC how many bytes are to be transmitted. Any transmission-buffering scheme must be implemented externally to the NIC.

The RAM used by the NIC can be a memory area shared with the host CPU or, as with the NE-2000, a memory area isolated from the CPU by the NIC. In the case of the isolated memory setup, the NIC provides DMA registers that allow the host CPU to move into and out of that buffer. The NIC documentation refers to the isolated memory area as "remote" memory. Two 16-bit DMA registers are used for accessing this remote memory: the remote start address and the remote byte count.

Moving data into the NIC buffer for transmission is simple. First, the remote start address needs to be set to the starting address of the buffer into which the prepared packet will be placed. Because packets always start on page boundaries, the least-significant byte of this address will usually be set to 0. Second, the remote byte-count register needs to be set to the number of units (bytes or words) that will be sent to the memory buffer. The NE-2000 accepts 16-bit data, so 100 bytes of packet data would require a remote byte count of 50. The number of bytes moved to remote memory is disconnected from the number of bytes that will be transmitted; therefore, if you only have 30 bytes of data to transmit, there is no need to move in 34 more bytes to fill out the minimum packet size of 64 bytes. To move the data into the NIC buffer, the Command register is set to remote write/start and the bytes (or words) are output to the data port.

Once a packet has been placed in a remote buffer, it can be sent out to the Ethernet cable by writing to the transmit page-start, transmit byte-count, and command registers. The transmit page-start register defines the page number from which a transmission will be initiated. The transmit byte-count register determines the number of bytes that will be sent (minimum of 64 bytes). Once the transmit page-start and byte-count registers have been set appropriately, the packet can be sent by asserting the transmit packet bit of the command register.

Pulling received data from the remote buffer is roughly the same as putting the data into the remote buffer. The remote start-address and remote byte-count registers are set up in the same fashion. The command register is set to remote read/start and then the bytes (or words) are input from the data port.

Because the movement of data to the NIC buffer uses the same registers as the data from the NIC buffer, it is up to the driver or application to control that process with a semaphore of some kind.

Packet Structure

The first 14 bytes of any packet sent to the NIC's transmission buffer should contain addressing and size information. The first six bytes of this header are the address of the node to which the packet is destined. The second six bytes are the address of the Ethernet adapter from which the packet is being sent. The last two bytes are the number of bytes contained within the packet--including this header. The packet can be transmitted regardless of the actual contents of this header. The destination address is required to get the packet to the physical address of where it's intended. Neither the source address nor the byte-count value are used or validated by the transmitting or receiving NIC. Although the last two fields are not important for communication, the receiving node's application may use them for its own purposes. For example, a Netware 3.11 file server expects them to contain the correct source-node ID and the size of the packet (rounded up to the nearest word) in Motorola format.

When a packet is received, the NIC creates a 4-byte prefix that can be read by the host CPU. The first byte of this prefix is the status associated with the received packet. The second is the number of the next page following the end of the packet. The last two bytes of this prefix contain the number of bytes actually received from the source of the packet. The transmission header created by the source node follows this prefix. For various reasons, the packet size in the prefix does not necessarily have to match the count in the transmit header.

Bits and Crumbs to Get Us Back Home

For the NIC to receive packets other than broadcasts, the physical-address registers must be filled with a legal address. Generally, this address is retrieved from a ROM read after the NE-2000 is reset. The ROM-based address can be ignored, and any 6-byte value may be placed in these registers (except all-1s). Multiple boards can have the same physical address and all will receive any packets directed at that address.

If an application is so inclined, the NIC can be configured to accept all packets passing through the network by asserting bit 4 of the receive-configuration register. This is called the "promiscuous physical bit" in the NIC documentation. When this bit is set, all packets, regardless of their destination address, will be received, but not (contrary to the uncertainty principle) affected by, the NIC.

Implementation and Things That Matter

The major parts of the snooper are the NIC driver, the real-time memory manager, a task manager and the display functions. Listing One (page 98) is SNOOP.H, the program's include file, and Listing Two (page 98) is SNOOP.C, the main entry and looping functions for the program. Because of its size, the complete system is available electronically; see "Availability," page 7. I've compiled the code using Watcom's 286 C compiler, Microsoft's Macro Assembler 6.0, and my own user-interface library, which is included. The make and link files are also included to aid in converting to another compiler.

My goal was to be able to view packets in real time and store them for study. Viewing packets in real time requires a nonscrolling visual because it allows static placement of data. Nonmoving visual fields enhance the user's ability to comprehend fast data transitions. The data-packet display format was shamelessly stolen from the venerable DDT. The packet data is written to a disk file, and if the network traffic overflows the storage buffers, data will be discarded.

The basic flow of the program starts when the incoming packet generates an interrupt, which is handled by the interrupt service routine (ISR). The ISR reads in the packet header and allocates a chunk of memory from the memory manager. If a memory chunk is available, the remainder of the packet is read in and placed in that memory area. The packet pages in the NIC's remote buffer are released to be used again.

The foreground process gets around to checking if any packets have been read by comparing the packet storage count to 0. If any packets have been received, the application retrieves a pointer to the packet buffer and checks the source/destination addresses against those that are of interest. The qualified packet is displayed and logged. The memory used to store the packet is released back to the memory manager, and life goes on.

A majority of the functions are written in assembly language because I was losing packets when I wrote them in C. The video routines expect the monitor to be of the standard monochrome or color memory-mapped variety.

The Datastream

Having a snooper is a lot like being a passenger on a glass-bottom boat--there sure are a lot of fish and they sure are pretty, but what are their names?

In a typical datastream, there are SAPs, routing information, and Netware core-protocol (NCP) packets. The SAPs and routing packets are nice, but are really not all that interesting. The NCPs may be a more interesting target, so let's look at how to decipher them.

Before going further, let me say that this information should not be taken as the absolute word on NCP. I don't have documentation from Novell that can be used as a reference. All the information in this article was obtained by observation, with no official communications from Novell. On the bright side, I've never signed a nondisclosure agreement with Novell, so I'm not constrained.

NCP uses the basic IPX packet structure to communicate from the node to the server. The process is usually initiated by the node, followed by a response packet from the server. The packet-type number used for request and response is 17. On a single-server network, the request packet is directed at the physical address of the server (placed in the transmission header of the NIC). The IPX header contains the logical network and node address of the NCP server. The destination socket is 451H. The source-address portion of the IPX header contains the address of the node to which a response is expected. The source-socket number is different than 451H. On all the networks I've observed, the response socket is 4003H. The rest of the NCP header is the same as any other IPX packet header. The structure of an NCP request is shown in Table 2; Table 3shows the structure of an NCP response.

Table 2: Structure of an NCP request.

  Offset  Description
  ------------------------------------------------------------------------

  00-01   Request signature.  Contains 02222h to indicate an NCP
          request.  The signature of 01111h is used to acquire a
          connection number.

  02      Sequence.  Contains the sequence number of this request packet.
          The response will contain the same sequence number.

  03      Connection number.  The connection number that the node is
          using to access file-server resources.  This number is provided
          by the file server when the node attaches for the first time.

  04-05   GOK1.  Unknown.  Varies between 1 and 2, but doesn't seem to
          affect how any of the requests are processed.

  06      Command.  The command request number.  The values used here
          tend to have a direct correlation to the INT 21h shell
          commands.  For example, request number 17h represents all
          the E3h INT 21h requests.

  07      GOK2. Unknown (usually 0).

  08      Start of the data specific to the command number.

Table 3: Structure of an NCP response.

  Offset  Description
  -------------------------------------------------------------------------

  00-01   Response signature.  Contains 03333h to indicate an NCP response.

  02      Sequence.  Contains the sequence number of its associated request
          packet.

  03      Connection number.  The connection number of the node being
          serviced.

  04-05   GOK1.  Tends to be one less than the request.

  06      Error code.  If nonzero, the request is denied for the reason
          defined by this error-code number.

  07      GOK2. Unknown.

To illustrate how this process works, we first need to get a connection number so that we can make more involved NCP requests. In C, this request must use the format shown in Example 1. The response from the server is to provide a NULL packet that has the connection field filled with the number that the node should use for further communication.

Example 1: A connection number request.

  0x1111  Signature
  0x00    Initialize the sequence counter
  0xFF    Need a connection
  0xFF00  No reason for this
  0x00    Command 0

To illustrate opening a file, I'll use a print-server configuration file (see Example 2) that allows access to unlogged connections. If successful, the response could be like Example 3.

Example 2: An example print-server configuration file.

  0x2222  Signature
  0x01    Sequence
  0x07    My connection number
  0x0002  <blah> <blah> <blah>
  0x4C    Open file command
  0x00    Filler
  0x0106  Open attribute
  0x1D    Length of the filename to follow
  xxxxxx  SYS: SYSTEM/19000003/PRINT.000
          Note: the '.' that separates PRINT from 000 is
          actually a 0xAE, because Netware requires the
          the periods to be converted

Example 3: A successful response to Example 2.

  0x3333     Response signature
  0x01       Sequence
  0x07       Connection
  0x0001     Yep, it's one less
  0x00       Good return code
  0x00       Filler
  0x13,0x53  Handle to use to access the file's data
  0x7C,0x2D
  0x00,0x00
  0x0000     ??? Always 0.
  PRINT.000  Filename.  Always 12 bytes, NULL padded.
  4 bytes    File attribute of some sort.  Always is 0x200000.
  4 bytes    File size in Motorola format.
  0x2719     File creation date.  MS-DOS format.
  0x2719     Date of last access to this file.  MS-DOS format.
  0x2719     Date of the last update to this file.  MS-DOS format.
  0x287E     Time of the last update to this file.  MS-DOS format.

The login process involves three steps. The first is acquiring an encryption key from the server. The second is to take that key and generate a password sequence that involves a table-driven XOR folding of the text password, the bindery ID number with that encryption key. The last step is to send that encryption sequence to the server in a login request packet.

To get the encryption key, transmit the request packet in Example 4(a); the response is shown in Example 4(b).

Example 4: (a) Transmitting a request for the encryption key; (b) the response.

  (a)

  0x17       Command
  0x00       Filler
  0x01       Number of bytes to follow
  0x17       Request for encryption key.  This key will be
             different for every request made of the server.

  (b)

  0x00       Good return code
  0x00       Filler
  0xDE,0x6C  Eight byte encryption key.
  0x18,0x36
  0x93,0x72
  0xD2,0xA3

Now, suppose the bindery-ID number for the account we are trying to log into is 0x18000001, and the password is "YO HO HO." Passing those parameters through the encryption function yields a login request packet of the form shown in Example 5.

Example 5: Passing parameters through the encryption function yields a login request packet.

  0x17       Command
  0x00       Filler
  0x11       Length of the request data to follow
  0xC4,0xA6  Encryption sequence
  0x39,0x23
  0xFA,0x0B
  0xFA,0x32
  0x100      Type of object logging into
  0x05       Length of object name to follow
  TEST1      Name of the account

Needless to say, once I had the proper tools, I did an extensive mapping study of the Netware commands and their corresponding NCP packets.

Conclusion

This snooper gives you a powerful tool with which to hunt for the various protocol bugs that crop up. I've found it particularly helpful with regard to IPX/SPX internode communication. In closing, I'd like to thank Henry Ngai at D-Link (Irvine, California) for providing information on the address of the data and reset ports.



_YOUR OWN NETWORK DATA SNOOPER_
by Rahner James


[LISTING ONE]
<a name="0130_0013">

/***************************************************************************
* Title: SNOOP.H -- Copyright (c) October 1992, Ryu Consulting by Rahner James
* This is the header file to be used for print server and NIC activities
***************************************************************************/

#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <rjuser.h>

#define SAVE_BAD_PACKETS    1
#define ACCEPT_RUNT_PACKETS (1 << 1)
#define ACCEPT_BROADCAST    (1 << 2)
#define ACCEPT_MULTICAST    (1 << 3)
#define SLUT                (1 << 4)
#define MONITOR_MODE        (1 << 5)

#define IPX_HEADER_SIZE     30
#define NIC_HEADER_SIZE     18

/******* ECB Flags *******/
#define ECB_OWNER_DRIVER    0
#define ECB_OWNER_APP       1
#define ECB_FREE_XMIT       (1 >> 1)

/* Socket numbers */
#define NCP_SOCKET      0x451
#define SAP_SOCKET      0x452
#define ROUTE_SOCKET        0x453

/* Error return codes */
#define NCPERR_NOERROR          0
#define NCPERR_NO_QJOB          -43
#define NCPERR_FILENAME         -128
#define NCPERR_ALLOC            -129
#define NCPERR_TIMEOUT          -130
#define NCPERR_NOACCOUNT        -131
#define NCPERR_NOACCESS         -132
#define NCPERR_LOST_CONNECT     -133
#define NCPERR_MALFORMED        -134

typedef struct NIC_ECB_S
{
    ui              next;
    us              flags;
    void (FAR       *esr)();
    volatile char   in_use;
    volatile char   completion;
    uc              node[6];
    us              NIC_length;

/* The IPX structure follows: */
    us          checksum;
    us          ipx_length;
    uc          transport;
    uc          packet_type;
    ul          dest_network;
    uc          dest_node[6];
    us          dest_socket;
    ul          src_network;
    uc          src_node[6];
    us          src_socket;
} NIC_ECB_T;
typedef struct TASK_S
{
    struct TASK_S FAR *next;
    struct TASK_S FAR *prev;
    uc          priority;
    uc          status;
    void (FAR *function)( struct TASK_S FAR * );
    void FAR  *buffer;
} TASK_T;

extern  ui  Base_Port;
extern  ul  Int_Count;
extern  uc  IRQ_Number, DMA_Channel, NE2000_Address[6];
extern  ui  Buffer_Start,Buffer_Size,Total_Allocated,First_Free,Last_MCB;
extern  volatile ul Tick_Count;
extern  volatile ui Total_Unread_Buffers;
extern  volatile ui Total_Xmit_Buffers;
extern  volatile ui Total_Xmit_Errors;
extern  volatile ui Total_Recv_Errors;
extern  volatile ui Total_Missed_Packets;
extern  volatile ul Total_Xmit_Packets;
extern  volatile ul Total_Recv_Packets;
extern  uc NIC_Normal_Recv_Config;

extern  int init_ne2000( void );
extern  int start_ne2000_interrupts( void );
extern  int stop_ne2000_interrupts( void );
extern  int init_memory( void );
extern  void FAR *alloc_NIC_ecb( ui );
extern  int free_NIC_ecb( void FAR * );
extern  NIC_ECB_T FAR *read_packet( void );
extern  void start_time_isr( void );
extern  void stop_time_isr( void );
extern  void show_packet_body( void FAR *, int, int );
extern  void show_net_address( int, int, void FAR * );
extern  void show_node_address( int, int, uc FAR * );
extern  int is_packet_valid( NIC_ECB_T FAR * );

extern  int is_ipx( void );
extern  int add_task( TASK_T FAR * );
extern  int do_next_task( void );
extern void atoaddr( char FAR *, char FAR * );
extern void FAR *farset( void FAR *, char, unsigned int );

#ifdef __WATCOMC__
    #pragma aux init_ne2000 "_*";
    #pragma aux start_ne2000_interrupts "_*";
    #pragma aux stop_ne2000_interrupts "_*";
    #pragma aux init_memory "_*";
    #pragma aux alloc_NIC_ecb "_*" parm [cx] value [es di];
    #pragma aux free_NIC_ecb "_*" parm [es di];
    #pragma aux read_packet "_*" value [es di];
    #pragma aux start_time_isr "_*";
    #pragma aux stop_time_isr "_*";

    #pragma aux is_ipx "_*";
    #pragma aux show_packet_body "_*" parm [dx ax] [cx] [bx];
    #pragma aux show_net_address "_*" parm [di] [ax] [cx si];
    #pragma aux show_node_address "_*" parm [di] [ax] [cx si];
    #pragma aux is_packet_valid "_*" parm [es di];

    #pragma aux add_task "_*" parm [es di];
    #pragma aux do_next_task "_*";

    #pragma aux atoaddr "_*" parm [es di] [dx si];
    #pragma aux farset "_*" parm [es di] [ax] [cx] value [es bx];
#endif

<a name="0130_0014">
<a name="0130_0015">
[LISTING TWO]
<a name="0130_0015">

/***************************************************************************
* Title: SNOOP.C -- Copyright (c) October 1992, Ryu Consulting by Rahner James
* This file contains the main entry and looping functions
***************************************************************************/
#define     _SNOOP_C_
#include    "snoop.h"

/***************************************************************************
* Global Data Items--All variables that have global accessibility have first
*  letters of each whole word within their name capitalized. All variable names
*  (local or global) begin with a noun or a word that is a noun by context.
***************************************************************************/
/* Names of the type of packets that will be passed */
uc *Type_Names[] =
{
    "Unknown Type, maybe IPX",  // 00 - anything, but probably IPX
    "Routing Info",         // 01 - Routing information packet
    "Echo Packet",          // 02 - Hello, hello, hello ...
    "Error Packet",         // 03 - Never saw one
    "IPX",              // 04 - IPX packet
    "SPX",              // 05 - SPX packet
    "unknown",          // 06
    "unknown",          // 07
    "unknown",          // 08
    "unknown",          // 09
    "unknown",          // 10
    "unknown",          // 11
    "unknown",          // 12
    "unknown",          // 13
    "unknown",          // 14
    "unknown",          // 15
    "unknown",          // 16
    "NCP"               // 17 - Netware Core Protocol packet
};
int     Match_ID_Count = 0;     // Number of ID in match this array
uc      Match_IDs[20][6];   // Storage for arrays we have to match
int     Match_Socket_Count = 0; // Sockets in socket match array
us      Match_Socket[20];   // Sockets we have to match
int     Handle = -1;        // Handle for the loggin file
int     Full_Display = 1;   // !0 if we want to see entire packet
char    And_Flag = 0;           // 0 = socket or ID, !0 = socket and ID
char    Broadcast_Flag = 0;     // 0=no checking, !0=check

/**************************************************************************
* void PARSE_LINE( int ARGC, char *ARGV[] )--Parses command line. Given: ARGC=
*   number of command line tokens; ARGV -> array of pointers to command line
*   tokens. Returns: Command line parsed and variables updated
***************************************************************************/
void parse_line( int argc, char *argv[] )
{
    int     i,j;
    farset( NE2000_Address, 0, 6 );
    for ( i=1 ; i < argc ; ++i )

   {
        if ( (*argv[i] == '/') || (*argv[i] == '\\') || (*argv[i] == '-') )
            ++*argv[i];
        switch ( *argv[i] )
        {
            case 'A':       // Catch all packets
            case 'a':
                NIC_Normal_Recv_Config |= SLUT;
                break;
            case 'B':       // Check broadcast as well
            case 'b':
                Broadcast_Flag = 1;
                break;
            case 'C':       // AND flag
            case 'c':
                And_Flag = 1;
                break;
            case 'D':       // OR flag
            case 'd':
                And_Flag = 0;
                break;
            case 'F':       // File to output the packet to
            case 'f':
                ++argv[i];
                if ( (*argv[i] == '=') || (*argv[i] == ' ') )
                    ++argv[i];
                print( "\fOpening file %s ...", argv[i] );
                 Handle = dos_open( argv[i], OPEN_RW+OPEN_CREATE );
                print( "\nReturn = %d", Handle );
                if ( Handle < 0 )
                    exit( 2 );
                dos_write( Handle, &i, 0 );
                break;
            case 'I':       // Match ID
            case 'i':
                if ( Match_ID_Count >= 20 )
                    break;
                ++argv[i];
                if ( (*argv[i] == '=') || (*argv[i] == ' ') )
                    ++argv[i];
                   atoaddr( Match_IDs[Match_ID_Count++], argv[i] );
                break;
            case 'N':       // Set current node ID
            case 'n':
                ++argv[i];
                if ( (*argv[i] == '=') || (*argv[i] == ' ') )
                    ++argv[i];
                if ( strlen(argv[i]) == 12 )
                    atoaddr( NE2000_Address, argv[i] );
                break;
            case 'P':       // Base port
            case 'p':
                ++argv[i];
                if ( (*argv[i] == '=') || (*argv[i] == ' ') )
                    ++argv[i];

               j = atoh( argv[i] );
                if ( (j >= 0x200) && (j <= 0x360) )
                    Base_Port = j;
                break;
            case 'R':       // IRQ number
            case 'r':
                ++argv[i];
                if ( (*argv[i] == '=') || (*argv[i] == ' ') )
                    ++argv[i];
                j = atoi( argv[i] );
                if ( (j > 1) && (j < 16) )
                    IRQ_Number = j;
                break;
            case 'S':       // Match socket
            case 's':
                if ( Match_Socket_Count >= 20 )

                    break;
                ++argv[i];
                if ( (*argv[i] == '=') || (*argv[i] == ' ') )
                    ++argv[i];
              Match_Socket[Match_Socket_Count++] = atoh( argv[i] );
                break;
        case '?':
        print(  "\n\nCommand lin syntax is:"
        "\n\ttest [option 1] [option 2] ... [option n]" );
        print(  "\n\nCommand line options:"
        "\n\tA         - accept all packets "
        "\n\tB         - accept broadcast packets (use with I option)"
        "\n\tC         - accept only correct IDs and sockets"
        "\n\tD         - accept correct IDs or sockets (default)"
        "\n\tFxxxxxxxx - name of output file for data logging"
        "\n\tI######## - node ID to accept data for"
        "\n\tN######## - set our packet ID as this number"
        "\n\tP###      - set base port address (in hex)"
        "\n\tR##       - IRQ number (in decimal)"
        "\n\tS####     - socket number to accept data for (in hex)"
        "\n\nNote: if no ID or socket qualifiers are set, all
                                                     packets will be accepted."
        "\n" );
         exit( 0 );
         break;
       default:
       break;
      }
    }
}
/**************************************************************************
* int DISPLAY_PACKET(NIC_ECB_T FAR *NP )--Displays contents of a packet. Given:
*   NP -> reception packet. Returns: 0 if nothing going on, !0 if exit program
***************************************************************************/
int display_packet( NIC_ECB_T FAR *np )
{
    int         y, i, j;
    char FAR    *cp;
    static char first_time = 1;

   if ( first_time )
    {
        clear_line( 0, BLUE + (WHITE BACKGROUND) );
        clear_line( 1, (INTENSE WHITE)+(CYAN BACKGROUND) );
                center( "IPX and NIC Header Information", 1, (INTENSE WHITE)+
                                                           (CYAN BACKGROUND) );
        print( "%p%r[            ]", 34,0 );
        print( "%p%rTransport:%pType:", 2,2, 26,2 );
        print( "%p%rIPX Length:%pSrc:", 2,3, 26,3 );
        print( "%p%rNIC Length:%pDest:", 2,4, 26,4 );
        first_time = 0;
        no_cursor();
    }
    print( "%p%r%ld packets%pUnread =%4d", 2,0, Total_Recv_Packets, 67,0,
                                                       Total_Unread_Buffers );
    show_node_address( 35,0, np->node );
    if ( Full_Display == 0 )
        return 0;
    show_net_address( 32,3, &np->src_network );
    show_net_address( 32,4, &np->dest_network );

    clear_area( 36,2, _Screen_Width-1,2, 0 );
    print( "%p%r%4d%p%4d%p%2X%p%d - %s",    14,3,   np->ipx_length,
                        14,4,   np->NIC_length,
                        16,2,   np->transport,
                        32,2,   np->packet_type,
         np->packet_type > 17 ? "unknown" : Type_Names[np->packet_type] );
    switch ( np->src_socket )
    {
        case 0x451:
            print( " (File Service)" );
            break;
        case 0x452:
            print( " (SAP)" );
            break;
        case 0x453:
            print( " (Route Information)" );
            break;
        case 0x455:
            print( " (NetBIOS Packet)" );
            break;
        case 0x456:
            print( " (Diagnostic Packet)" );
            break;
        default:
            break;
    }
    show_packet_body( &np[1], np->ipx_length, np->NIC_length );
    return 0;
}

/***************************************************************************
* int INIT_SNOOPER( int ARGC, char *ARGV[] )--Initializes snooper, program's
*   internals, and display. Given: ARGC = number of command line tokens ARGV ->
*   array of pointers to the command line tokens. Returns: 0 if all went well;

*   otherwise an exit code
***************************************************************************/
int init_snooper( int argc, char *argv[] )
{
    int i;
        print( "\fEthernet Snooper for NE-2000 Compatible Adapters on
                                                         Netware, version 1.00"
        "\nCopyright (c) September 1992, Ryu Consulting, 916/722-1939"
        "\nWritten by Rahner James\n" );
    if ( is_ipx() )
    {
        print( "\nIPX is currently installed, please remove it" );
        beep();
        return 1;
    }
    if ( argc > 1 )
        parse_line( argc, argv );
    print( "\nReturn from initialization of memory pool = %d", init_memory() );
    print( "\nReturn from initialization of NE2000 = %d", i=init_ne2000() );
    if ( i )
        return 2;
    clear_screen( (INTENSE WHITE)+(BLUE BACKGROUND) );
    clear_line( 5, (INTENSE WHITE)+(CYAN BACKGROUND) );
    center("IPX Packet Data Display",5,(INTENSE WHITE)+(CYAN BACKGROUND) );
    clear_area( 0,6,_Screen_Width-1,_Screen_Height-1,(INTENSE WHITE)+
                                                          (GREEN BACKGROUND) );
    print( "%p%rEthernet Snooper, version 1.00, waiting for first
                                                    packet\n%58c", 2,0, 0xc4 );
    return 0;
}
/***************************************************************************
* int MAIN( int ARGC, char *ARGV[] )--Initial entry point into this program.
*   Given: ARGC = number of command line tokens ARGV -> array of pointers to
*   command line tokens. Returns: Exit code
***************************************************************************/
main( int argc, char *argv[] )
{
    int     i,j, x,y, loop_flag = 1;
    ui      key;
    uc      FAR *cp;
    NIC_ECB_T   FAR *np;
/* First, set everthing up */
    if ( i=init_snooper(argc,argv) )
        return i;
/* Now, loop waiting for packets or a keystroke */
    while ( loop_flag )
    {
        do_next_task();
        if ( kbhit() )
        {
            if ( charin() == ESC )
                loop_flag = 0;
        }
        if ( Total_Unread_Buffers )
        {

           if ( (np = read_packet()) == NULL )
            {
      print( "%p%a\nGot a packet but no data", 0,_Screen_Height-1, WHITE );
                beep();
                break;
            }
            if ( is_packet_valid(np) )
            {
                display_packet( np );
                if ( Handle >= 0 )
                  dos_write( Handle, np, np->NIC_length+0x12 );
            }
            free_NIC_ecb( np );
        } // if (Total_Unread_Buffers)
    } // while
/* Close everything up and go home */
    if ( Handle >= 0 )
        dos_close( Handle );
    print( "%p%a\n", 0,_Screen_Height-1, WHITE );
    clear_line( _Screen_Height-1, WHITE );
    print( "%p%aStopping ...\n", 0,_Screen_Height-1, WHITE );
    i = stop_ne2000_interrupts();
    stop_time_isr();
    print( "Return     = %d", i );
    print( "\nTotal interrupts received = %ld", Int_Count );
    show_cursor();
}


Example 1

     0x1111    Signature
     0x00      Initialize the sequence counter
     0xFF      Need a connection
     0xFF00    No reason for this
     0x00      Command 0



Example 2

     0x2222    Signature
     0x01      Sequence
     0x07      My connection number
     0x0002    <blah> <blah> <blah>
     0x4C      Open file command
     0x00      Filler
     0x0106    Open attribute
     0x1D      Length of the filename to follow
     xxxxxx    SYS:SYSTEM/19000003/PRINT.000
               Note: the '.' that separates PRINT from 000 is
               actually a 0xAE, because Netware requires the
               the periods to be converted.

Example 3

     0x3333    Response signature
     0x01      Sequence
     0x07      Connection
     0x0001    Yep, it's one less
     0x00      Good return code
     0x00      Filler
     0x13,0x53 Handle to use to access the file's data
     0x7C,0x2D
     0x00,0x00
     0x0000    ??? Always 0.
     PRINT.000 Filename.  Always 12 bytes, NULL padded.
     4 bytes   File attribute of some sort.  Always is 0x200000.
     4 bytes   File size in Motorola format.
     0x2719    File creation date.  MS-DOS format.
     0x2719    Date of last access to this file.  MS-DOS format.
     0x2719    Date of the last update to this file.  MS-DOS
               format.
     0x287E    Time of the last update to this file.  MS-DOS
               format.


Example 4:

(a)

     0x17      Command
     0x00      Filler
     0x01      Number of bytes to follow
     0x17      Request for encryption key.  This key will be
               different for every request made of the server.

(b)
     0x00      Good return code
     0x00      Filler
     0xDE,0x6C Eight byte encryption key.
     0x18,0x36
     0x93,0x72

     0xD2,0xA3


Example 5

     0x17      Command
     0x00      Filler
     0x11      Length of the request data to follow
     0xC4,0xA6 Encryption sequence
     0x39,0x23
     0xFA,0x0B
     0xFA,0x32
     0x100     Type of object logging into
     0x05      Length of object name to follow
     TEST1     Name of the account













Copyright © 1993, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.