5.3   Extracting Information from a Structure

Extracting the information needed from the ifnet structure can be broken into several steps. First, determine how the data is stored. Then locate the first element, identify the remaining elements, and finally, query each element to obtain its contents. As you proceed through this chapter, you will build up expressions as you go. For example, you will write a Tcl expression and then use the value returned by that routine as an argument for the next Tcl expression.

5.3.1   Definitions and Structures

A network interface is an entity over which packets can be sent. It can represent a physical Ethernet hardware device or a logical device (such as the loopback interface) that simply relays packets. Generally, when a VxWorks target has Ethernet capability, there are at least two interfaces: one for the device and one loopback interface for local packet communication.

A network interface in VxWorks is represented as a linked list of structures. The structure that contains most of the information for a network interface, struct ifnet, and the subsidiary structure, struct if_data, are found in the include file installDir/target/h/net/if.h:

struct ifnet { 
    char *              if_name;        /* name, as "en" or "lo" */ 
    struct ifnet *      if_next;        /* all struct ifnets are chained */ 
    struct ifaddr *     if_addrlist;    /* linked list of addresses per if */ 
    int                 if_pcount;      /* number of promiscuous listeners */ 
    caddr_t             if_bpf;         /* packet filter structure */ 
    u_short             if_index;       /* numeric abbreviation for if */ 
    short               if_unit;        /* sub-unit for lower level driver */ 
    short               if_timer;       /* time until if_watchdog called */ 
    short               if_flags;       /* up/down, broadcast, etc. */ 
    struct if_data      if_data;        /* stats and other data about if */ 
    struct mBlk *       pInmMblk;       /* chain of multicast addresses */ 
    ... 
    struct ifqueue      if_snd;         /* output queue */ 
};
struct if_data { /* generic interface information */ u_char ifi_type; /* ethernet, tokenring, etc. */ u_char ifi_addrlen; /* media address length */ u_char ifi_hdrlen; /* media header length */ u_long ifi_mtu; /* maximum transmission unit */ u_long ifi_metric; /* routing metric (external only) */ u_long ifi_baudrate; /* linespeed */ /* volatile statistics */ u_long ifi_ipackets; /* packets received on interface */ u_long ifi_ierrors; /* input errors on interface */ u_long ifi_opackets; /* packets sent on interface */ u_long ifi_oerrors; /* output errors on interface */ u_long ifi_collisions; /* collisions on csma interfaces */ u_long ifi_ibytes; /* total number of octets received */ u_long ifi_obytes; /* total number of octets sent */ u_long ifi_imcasts; /* packets received via multicast */ u_long ifi_omcasts; /* packets sent via multicast */ u_long ifi_iqdrops; /* dropped on input, this interface */ u_long ifi_noproto; /* destined for unsupported protocol */ u_long ifi_lastchange; /* last updated */ };

In VxWorks, the global variable ifnet points to the structure that contains the first network interface. If there is an additional network interface, the last item in the structure points to the next structure. (See Figure 5-2.)   

5.3.2   Locating the Start of the First Structure

The first piece of information needed is the address of the first node of the list, which is stored in the global variable ifnet. The address of this symbol can be found in the target server symbol table. The wtxtcl procedure to query the symbol table is:

wtxtcl> wtxSymFind -name ifnet 
_ifnet 0x95ae0 0x9 0 0 ""

This procedure returns a list of information about the symbol, which includes a pointer to its location in memory. Use wtxMemRead to find the location of the first structure by extracting the symbol address from the list returned by wtxSymFind and using this as an argument to wtxMemRead:

wtxtcl> set block [wtxMemRead [lindex [wtxSymFind -name ifnet] 1] 4] 
mblk0 
wtxtcl> set ifnet [memBlockGet -l $block] 
0x001a3098 
wtxtcl> memBlockDelete $block 
wtxtcl> set ifnet 
0x001a3098 

Read a memory block of 4 bytes and store the block handle in the variable block. Then fetch the 4 bytes in this block to the variable ifnet and free the block. You now know the location of the first element of the linked list.

5.3.3   Listing All Network Interfaces

One algorithm to move from one structure of the linked list to the next looks like the following:

    p = ifnet; 
    while (p != NULL) 
        { 
        print p; 
        p = p->if_next; 
        }

You could implement this with WTX Tcl by repeatedly using wtxMemRead, but in Tornado there is a simpler way: you can write a Gopher script and send it to the target agent from the Tornado shell. The target agent executes it in one step and returns the results. Here is the Gopher implementation of the algorithm:

    ifnet {! + 4 *}

For detailed information on writing a Gopher script, see 4.3.11 Gopher Support. A summary of the function of the script tokens follows.

ifnet
is an unsigned integer constant which sets the value of the Gopher pointer.

open brace ({)
introduces a loop; the subprogram inside the braces is executed while the pointer is not NULL.

exclamation (!)
copies the value of the pointer to the Gopher tape (result buffer).

+4
increments the pointer by this value. (We computed the value 4 by inspecting the sizes of the constituent elements.)

asterisk (*)
dereferences the pointer.

close brace (})
ends the subroutine. (Note that for all Gopher constructions using matching delimiters ({ }, < >, and so on) the "outer" value of the pointer is saved during the execution of the inner material, and restored when the closing delimiter is executed.)

Now that you have a Gopher script to move the pointer from the first element of the first structure to the first element of each successive structure in the linked list, you need to write a Tcl routine that uses the Gopher script to traverse the linked list, printing out the address of each node. The wtxGopherEval procedure allows you to send a Gopher script to the target for interpretation and execution. The procedure netIfList returns a list of network interface descriptors (pointers to ifnet structures):

proc netIfList {} { 
    set block [wtxMemRead [lindex [wtxSymFind -name ifnet] 1] 4] 
    set ifnet [memBlockGet -l $block] 
    return [wtxGopherEval "$ifnet {! +4 *}"] 
    }

Try this routine from the shell by using the ? character to toggle to the shell Tcl prompt:

-> ? 
tcl> source homeDir/.wind/netIfList.tcl 
tcl> netIfList 
0x94e8c 0x9531c

Restart the shell before switching to the Tcl prompt or source the file.

5.3.4   Listing the Contents of Each Structure

The next step is to write a routine that extracts the relevant fields from one network interface descriptor in the form of a list. The list serves as an intermediate representation which is the basis for the shell's and browser's presentations of the data.

Use the Gopher language interpreter to extract the data. (For a detailed discussion of the Gopher operators, see 4.3.11 Gopher Support.) The first field of interest in the ifnet structure is the if_name field. You used netIfList to find the address of the first byte of the structure, which is also the address of the string. Use the following Gopher string to dereference this pointer and write the string to the tape:

    <*$>

Remember that the angle brackets delineate a subcontext in which the outer pointer value is saved. When the * is executed, the pointer is dereferenced, but outside the angle brackets, the old value of the pointer is preserved. The operator $ copies the string to the tape. After leaving the subcontext, the pointer still points to the top of the structure.

Now, fetch the next two interesting fields, if_unit and if_flags. The pointer is still pointing to if_name, therefore use a signed constant to advance it over the twenty two bytes of the pointer (+22). The @ operator with the w suffix (16-bit word) writes the value at the pointer to the tape. A side effect of the @ operator is that the pointer value is advanced past the data written; in effect, @w copies the 16-bit value at the pointer to the tape and increases the value of the pointer by 2. Next, fetch if_mtu and if_metric, which are stored in the if_data structure. Use a signed constant (+2) to skip over the field if_timer, and (+4) to skip the first bytes (including one byte for padding) of the if_data structure to get the if_mtu and if_metric fields. To read the four fields starting with the pointer at the if_unit field, the fragment is:

    +22 @w +2 @w +4 @@

For the five packet-statistics fields (ifi_ipackets through ifi_collisions), advance the pointer to the first field and use the @ operator five times.

    +4 @@@@@

The routine netIfInfo combines these fragments to return the values of interest, given the address of a single network interface.

proc netIfInfo {netIf} {
return [wtxGopherEval "$netIf <*$> +22 @w +2 @w +4 @@ +4 @@@@@"]
}

Test the two routines with the following expression:

tcl> foreach netIf [netIfList] {puts stdout [netIfInfo $netIf]} 
ei 0 0x8063 0x5dc 0 0x19b 0 0x1 0 0 
lo 0 0x8069 0x8000 0 0 0 0 0 0

You now have most of the information needed to duplicate the output produced by the target shell routine ifShow( ). You still need the Internet addresses of the interfaces, which are stored in a linked list attached to each node of the ifNet list by way of the if_addrlist pointer. The following example shows the definition of the if_addr structure:

struct ifaddr { 
    struct sockaddr *   ifa_addr;       /* address of interface */ 
    struct sockaddr *   ifa_dstaddr;    /* other end of p-to-p link */ 
#define ifa_broadaddr   ifa_dstaddr     /* broadcast address interface */ 
    struct sockaddr *   ifa_netmask;    /* used to determine subnet */ 
    struct ifnet *      ifa_ifp;        /* back-pointer to interface */ 
    struct ifaddr *     ifa_next;       /* next address for interface */ 
    void (*ifa_rtrequest)();            /* check or clean routes */ 
                                         /* (+ or -)'d */ 
    u_short             ifa_flags;      /* mostly rt_flags for cloning */ 
    short               ifa_refcnt;     /* count of references */ 
    int                 ifa_metric;     /* cost of going out this */ 
                                         /* interface */ 
}; 
 
struct sockaddr_in { 
    short        sin_family; 
    u_short      sin_port; 
    struct       in_addr sin_addr; 
    char         sin_zero[8]; 
}; 
struct in_addr { 
        u_long s_addr; 
};

The second set of four bytes in the sockaddr structures contains the Internet addresses. Note that they are stored in Internet byte order (big-endian). If you were to use an @l operator to extract them, the result would come out swapped if the target architecture is little-endian. To avoid this, read the addresses as four one-byte values. Given a pointer to the first node in a list of these structures and the various offsets, the Gopher script to extract the two addresses in each node (ifa_addr and ifa_dstaddr) is:

<*{+4 @b@b@b@b 0}> +4 <*{+4 @b@b@b@b 0}> 

The netIfAddrList routine takes a network interface descriptor, advances to the if_addrlist field and dereferences it, and then repeatedly executes this fragment to copy the Internet addresses (we skip the first address, which represents an internal linkage).

proc netIfAddrList {netIf} { 
    return [wtxGopherEval "$netIf +8 * {+16 * {<*{+4 @b@b@b@b 0}> +4  
        <*{+4 @b@b@b@b 0}>+12 *}0}"]
}

Testing from the shell yields:

tcl> foreach netIf [netIfList] {puts stdout [netIfAddrList $netIf]} 
0x93 0xb 0x50 0xca 0x93 0xb 0x50 0xff 
0x7f 0 0 0x1 0x7f 0 0 0x1

Each interface has two addresses so each result has eight octets. The program that calls this routine must partition the results into four-octet sets.

By designing these routines to return results in Tcl list form, you have created information gathering routines that can be used in multiple contexts. They can be used by the shell, the browser, or other any Tcl application. Storing them in the resource/tcl directory makes them available to all Tcl applications (see Figure 5-1).


*

NOTE: Wind River Systems has developed a "Gopher generator" tool which generates gopher scripts from a C structure. This tool is not supported, but is available free of charge. It can be downloaded from the Wind River System WEB server (www.wrs.com).