nexp(1) A framework for crafting network packets and processing responses

SYNOPSIS

nexp [-V] [-v] [-t] [-c cmd] [-s seed] [-h] [cmdfile] [args]

INTRODUCTION

Network Expect (nexp) is a framework that allows to easily build tools that can interact with network traffic. Following a script, traffic can be injected into the network, and decisions can be taken, and acted upon, based on received network traffic. An interpreted language provides branching and high-level control structures to direct the interaction with the network.

Network Expect

was heavily influenced and inspired on the Expect program written by Don Libes, which allows to "talk" to interactive programs in a scripted fashion. Because of this, you will find a lot of similarities between commands in Network Expect and commands in Don Libes' Expect. If you are a regular Expect user, it should not be very difficult to start writing Network Expect scripts because the basics are the same.

In Don Libes' Expect, scripts can send data to a process just as if a user were interactively typing commands. Then, the script would read the responses sent by the application and take decisions accordingly. In Network Expect's case, a script could send traffic to a network device and then take decisions based on the received network traffic. The type of things that Network Expect can do are usually very low level network operations, which usually require writing a custom application in a language like C.

Network Expect's philosophy is based on the observation that network applications always operate on an action-reaction principle in which something is sent to an application running on a remote host and a response is then expected.

Some of the things that Network Expect can do include:

• Generate arbitrary network traffic and inject it into a network at layer 2 or layer 3.

A wide range of protocols is supported, including IP version 6 as well as protocol options like IPv4, IPv6 and TCP options, something that other tools may not offer. For example, Network Expect supports TCP MD5 signatures (RFC 2385).

This Network Expect functionality is very similar to the functionality provided by several packet crafting and forging open source tools like Nemesis, Packit, hping, Scapy, and others.

• Listen for network traffic and take decisions based on the type of traffic received.

• Open a sniffer trace in PCAP format and replay it after changing some values in the original packet capture.

• Emulate network protocols to see how they interact with other speakers of that protocol. For example, emulating a TCP server to investigate approaches to randomization of TCP Initial Sequence Numbers (ISN) can be easily done in Network Expect.

USAGE

Network Expect reads cmdfile for a list of commands to execute. Network Expect may also be invoked implicitly on systems which support the #! notation by marking the script executable, and making the first line in your script:

#!/usr/bin/nexp -f

Of course, the path must accurately describe where Network Expect lives; /usr/bin is just an example.

The -c flag prefaces a command to be executed before any in the script. The command should be quoted to prevent being broken up by the shell. This option may be used multiple times. Multiple commands may be executed with a single -c by separating them with semicolons. Commands are executed in the order they appear.

-V causes Network Expect to print its version number and exit.

The -s flag allows to specify a random seed that will cause predicatibility of pseudo-random numbers generated by Network Expect during execution of a script. In cases where Network Expect is used as a protocol fuzzer, this option is useful to be able to re-generate a specific test case.

-v increases the verbosity level. Some commands display additional information when the verbosity level is higher.

The -t flag changes the display format used by commands that display dates or generate strings that represent dates.

Optional arguments are constructed into a list and stored in the variable named argv. argc is initialized to the length of argv.

argv0 is defined to be the name of the script (or binary if no script is used). For example, the following prints out the name of the script and the first three arguments:

puts "$argv0 [lrange $argv 0 2]"

NETWORK LISTENERS AND SPEAKERS

An integral part of Network Expect is the concept of network listeners and network speakers, which are the Network Expect equivalent to spawned processes in Don Libes' Expect world. In Expect, the send command sends data to a spawned process, and the expect command waits for a specific pattern in the data received from a spawned process.

In Network Expect, the command to send data to the network, called send_network, uses a speaker that specifies how the traffic will be injected. Network Expect speakers can specify IPv4 or IPv6 sockets, in which case packets will be injected at layer 3 and will be routed by the operating system kernel. Network Expect speakers can also specify a physical interface, in which case the packet will be injected at layer 2. A network speaker can also be a PCAP file (also known as a "savefile"), in which case packets will be written to this file instead of injected to the network. Other network speaker types include TCP and UDP sockets, in which case the payload generated by the send_network is transported by TCP segments or UDP datagrams built by the operating system kernel.

Network Expect listeners, on the other hand, specify where packets will be read from. Listeners can be associated with either a physical interface, or with a PCAP file. In either case an optional PCAP filter and/or Wireshark display filter can be associated with the listener to limit the type of packets the listener will return. When reading from a PCAP file the inter-packet delay between packets can be kept, or packets can be read at full speed.

Both listeners and speakers are created with the Network Expect command spawn_network, just as in Don Libes' Expect spawned processes are created with the spawn command.

The way to specify network listeners and speakers in commands that require them is the same regardless of the command. Network listeners are always specified using the -i switch (think input) followed by the name of the listener, and network speakers are specified using the -o switch (think output) followed by the name of the speaker.

Network isteners and speakers are created using the spawn_network command. Just as in Don Libes' Expect one cannot choose the spawn ID returned by the spawn command, in Network Expect it is not possible to choose the name of a network listener or speaker. However, one can assign the network listener or speaker name to a variable and use that variable whenever a network listener or speaker needs to be specified.

What follows are a few examples of creation of network listeners and speakers:

spawn_network -i eth1 icmp and src host 172.16.1.1

This command creates a network listener on interface eth1 and assigns the filter "icmp and src host 172.16.1.1", i.e. "listen only for ICMP messages coming from host 172.16.1.1". Since the -o switch has not been specified this command will not create a network speaker.

spawn_network -o eth0 -r /tmp/packets.pcap tcp and host 172.16.1.1

This command creates a network speaker on interface eth0 and a network listener that will read from the PCAP file "/tmp/packets.pcap" TCP segments to or from the host 172.16.1.1.

spawn_network -nolistener -6

This will create a network speaker for injecting IPv6 packets at layer 3. Since the -nolistener switch has been specified, this command only creates a network speaker and no listener.

spawn_network -nolistener -w /tmp/mypackets.pcap

This only creates a speaker that will write packets to the PCAP file "/tmp/mypackets.pcap". Note that when writing packets to a PCAP file, injection implicitley takes place at layer 2, and using an Ethernet header. This must be taken into consideration when specifying the packet to send using the send_network command.

Network Expect listeners and speakers play a very important role in the operation of Network Expect so becoming confortable with them is key to understanding Network Expect's philosophy.

NUMERIC SPECIFICATIONS

When defining packets, Network Expect allows to specify values for most fields in protocol headers using a syntax that gives great flexibility. This syntax allows to make the value of a field change with each packet that is generated. The syntax is better presented with an example. Suppose that we have an hypothetical protocol field called port that is used to specify a numeric value at packet generation time. The following numeric specifications can be used to specify the actual value

port = 'telnet' (fixed): the generated value will always be 23, telnet's port number.

port = 23 (fixed): the generated value will always be 23.

port = 23++ (increment): the generated value will be 23 initially, and will be incremented by one with each successive packet.

port = 23-- (increment): the generated value will be 23 initially, and will be decremented by one with each successive packet.

port = 23+=5 (decrement): the generated value will be 23 initially, and will be incremented by 5 with each successive packet.

port = 23-=5 (decrement): the generated value will be 23 initially, and will be decremented by 5 with each successive packet.

port = 23..25 (range): the generated value will start with 23, will be incremented by one until it reaches 25, and then will go back to 23.

port = 25..23 (range): the generated value will start with 25, will be decremented by one until it reaches 23, and then will go back to 25.

port = 'random': the generated value will be a random number in each successive packet.

COMMANDS

Network Expect uses Tcl (Tool Command Language). Tcl provides control flow (e.g., if, for, break), expression evaluation and several other features such as recursion, procedure definition, etc. Commands used here but not defined (e.g., set, if, exec) are Tcl commands (see tcl(3)). Network Expect introduces additional commands, described below. Unless otherwise specified, commands return the empty string.

Commands are listed alphabetically so that they can be quickly located. However, new users may find it easier to start by reading the descriptions of spawn_network, send_network, expect_network, and send_expect, in that order.

barray new | length | delete | examine | dump | slice | cmp | string <args>

The barray command is used to perform management of variables of type barray (byte array.)

barray new <payload spec> will create and return a new barray variable.

barray length <barray variable> will return the number of bytes that the barray variable uses.

barray delete <barray variable> will delete a barray variable.

barray examine <barray variable> [/<FMT>] [<offset>] allows to inspect the contents of the specified barray variable, starting at the optional offset, and using the format optionally specified with /FMT. /FMT can include an optional count followed by the display format, '<' or '>' to specify little endian or big endian, and the size of each element to display. Display formats are strings ('s'), octal ('o'), hexadecimal ('x'), signed decimal ('d'), and unsigned decimal ('u'). Sizes are byte ('b'), half-word ('h'), and word ('w').

barray dump <barray variable> produces a hexadecimal dump of the specified barray variable.

barray slice <barray variable> <slice spec> returns a slice of the specified barray variable. slice spec must be in the format <[start]:[end]> where start and end are offset into the barray variable. If start is ommited the start offset is 0, and if end is ommited the end offset is the end of the barray variable.

barray cmp <barray variable 1> <barray variable 2> compares two barray variables and returns an integer less than, equal to, or greater than zero if the first n bytes of barray variable 1 are found, respectively, to be less than, to match, or be greater than the first n bytes of barray variable 2. n is calculated to be the minimum of the lengths of both barray variables.

close_network <listener/speaker name>

This command closes the network listener and/or speaker referenced by <listener/speaker name>. Closing a network listener is not a very important thing to do since the operation just releases system resources used by the listener. However, closing a network speaker is a very important, especially when the speaker is associated with a PCAP file since closing the speaker closes the associated PCAP file.

expect_network [-timeout <timeout>] [-i <listener ID>] {condition1} {body1} ... [-i <listener ID>] {conditionN} {bodyN}

The expect_network command waits for network traffic from one or more network listeners (specified with the -i option) until a condition evaluates to true, until a specified time period has passed, or until an end-of-file is seen (when the listener is associated with a PCAP file.)

Conditions from the most recent expect_network_before command are implicitly used before any other conditions. Conditions from the most recent expect_network_after command are implicitly used after any other conditions.

Conditions are regular Tcl expressions, and bodies are sets of Tcl commands. If the final body is empty, it may be omitted.

If the arguments to the entire expect_network statement require more than one line, all the arguments may be "braced" into one so as to avoid terminating each line with a backslash. In this one case, the usual Tcl substitutions will occur despite the braces.

If a condition is the keyword eof, the corresponding body is executed upon end-of-file (this is only meaningful when a listener is reading from a PCAP file, not on a live capture from an interface.) If a condition is the keyword timeout, the corresponding body is executed upon timeout. If no timeout keyword is used, an implicit null action is executed upon timeout. The default timeout period is 0.5 seconds but may be set, for example to 30, by the command "set timeout 30". An infinite timeout may be designated by the value -1.

If a condition is true, then the corresponding body is executed. expect_network returns the result of the body (or the empty string if no condition was true.) In the event that multiple conditions match, the one appearing first is used to select a body.

Each time a new packet arrives, it is decoded and the conditions are evaluated in the order they are listed. When a packet is decoded, the values of the different fields in the packet are made available to Tcl scripts via Tcl variables. Scripts can use these values in an condition for a expect_network command.

In the following example, a listener for ARP requests on interface eth0 is created and then an expect_network command is used to wait for actual ARP requests and respond to them with an ARP reply:

# Spawn a listener for ARP requests
spawn_network -i eth0 host 192.168.1.1 and {arp[6:2]} == 1
expect_network {1} {
    # Received an ARP request, send ARP reply
    send_network -o eth0 \
        ether(src = $mymac, dst = $arp(sha) )/ \
        arp-reply(tha = $arp(sha), tip = $arp(sip), \
                  sha = $mymac, sip = 192.168.1.1)
    nexp_continue
}
          

The -timeout flag causes the current expect_network command to use the following value as a timeout instead of using the value of the timeout variable.

Actions such as break and continue cause control structures (i.e., for, proc) to behave in the usual way. The command nexp_continue allows expect_network itself to continue executing rather than returning as it normally would.

This is useful for avoiding explicit loops or repeated expect_network statements. The following example is part of a fragment to respond to ICMP echo and ARP requests. The nexp_continue avoids having to write a second expect_network statement (to look for the requests again.)

# Spawn a listener for ARP requests
spawn_network -i $interface host $myip and {arp[6:2]} == 1
set arpl $listener_id
# Spawn a listener for ICMP messages sent to us
spawn_network -i $interface icmp and dst host $myip
set icmpl $listener_id
expect_network -i $arpl {1} {
    # Received an ARP request, send ARP reply
    send_network -o $interface \
        ether(src = $mymac, dst = $arp(sha) )/ \
        arp-reply(tha = $arp(sha), tip = $arp(sip), \
                  sha = $mymac, sip = $myip)
    nexp_continue
} -i $icmpl {$icmp(type) == 8} {
    # Received ICMP echo request, send echo reply
    send_network -o ip \
        ip(src = $myip, dst = $ip(src) )/ \
        icmp-echoreply(id = $icmp(id), seq = $icmp(seq) )/ \
        raw($raw)
    nexp_continue
}
          

expect_network_after [expect_args]

works identically to the expect_network_before except that if conditions from both expect_network and expect_network_after evaluate to true, the expect_network conditions is used. See the expect_network_before command for more information.

expect_network_background [expect_args]

takes the same arguments as expect_network, however it returns immediately. Conditions are evaluated whenever a new packet arrives. The expression timeout and default are meaningless to expect_network_background and are silently discarded. Otherwise, the expect_network_background command uses expect_network_before and expect_network_after conditions just like expect_network does.

Please note that if a condition of the expect_network_background command evaluates to true, the command will not continue to listen for traffic unless the body includes a nexp_continue command that forces expect_network_background to continue executing.

expect_network_before [expect_args]

takes the same arguments as expect_network, however it returns immediately. Condition-action pairs from the most recent expect_network_before with the same network listener ID are implicitly added to any following expect_network commands. If a condition evaluates to true, it is treated as if it had been specified in the expect_network command itself, and the associated body is executed in the context of the expect_network command. If conditions from both expect_network_before and expect_network can evaluate to true, the expect_network_before condition is used.

Unless overridden by a -i flag, expect_network_before conditions are evaluated against the network listener ID defined at the time that the expect_network_before command was executed (not when its condition evaluated to true.)

The -info flag causes expect_network_before to return the current specifications of what conditions will be evaluated. By default, it reports on the current network listener. An optional network listener ID specification may be given for information on that network listener.

Instead of a network listener specification, the flag -all will cause -info to report on all network listeners.

The output of the -info flag can be reused as the argument to expect_network_before.

iflist [<-refresh>]

iflist returns a list that contains the name of all interfaces in the system. This list is built when Network Expect starts. If the optional argument -refresh is specified then the list is refreshed before returning it.

nexp_continue

The command nexp_continue allows expect itself to continue executing rather than returning as it normally would.

nexp_version [[-exit] <version>]

is useful for assuring that the script is compatible with the current version of Network Expect.

With no arguments, the current version of Expect is returned. This version may then be encoded in your script. If you actually know that you are not using features of recent versions, you can specify an earlier version.

Versions consist of two numbers separated by dots. First is the major number. Scripts written for versions of Network Expect with a different major number will almost certainly not work. nexp_version returns an error if the major numbers do not match.

Second is the minor number. Scripts written for a version with a greater minor number than the current version may depend upon some new feature and might not run. nexp_version returns an error if the major numbers match, but the script minor number is greater than that of the running Network Expect.

With the -exit flag, Expect prints an error and exits if the version is out of date.

outif <target>

The outif command returns the outgoing interface that would be used to reach target. target can be an IP address or host name.

packet decode | hash | data | dump | ts | tdelta <args>

The packet command allows to manage variables of type packet.

packet decode <packet variable> will decode the specified packet variable, just as if the packet was received during the execution of a expect_network command.

packet hash <packet variable> calculates a hash of the specified packet variable.

packet data <packet variable> returns a barray variable that contains all of the packet's data.

packet dump <packet variable> displays an hexadecimal dump (on standard output) of the packet's data.

packet ts <packet variable> will return a timeval variable that corresponds to the timestamp associated with the specified packet variable.

packet tdelta <packet variable 1> <packet variable 2> calculates the time delta between the timestamp associated with <packet variable 1> and the timestamp associated with <packet variable 2>.

pdu <new | delete | dup | append | count | build | list> <args>

The pdu command is used to manage variables of type pdu (Protocol Data Unit.)

pdu new <PDU definition> will create and return a new pdu variable. The PDU definition is the same as used in other Network Expect commands like send_network and send_expect.

pdu delete <pdu variable> will delete a pdu variable.

pdu dup <pdu variable> will duplicate a pdu variable.

pdu append <pdu variable 1> <pdu variable 2> will append the pdu variable pdu variable 2 to the pdu variable pdu variable 1. The result is pdu variable 1.

pdu count <pdu variable> returns the number of packets that would be generated if that PDU were to be sent using the send_network command, for example. The number of packets depends on the numeric specifications used in the definition of the PDU.

pdu build <pdu variable> builds the specified pdu variable and returns a variable of type packet.

pdu list displays the list of PDUs that Network Expect knows about, i.e. the PDUs that can be created via certain Network Expect commands like send_network, send_expect, and pdu new.

random [x:y | number x:y | ip [a.b.c.d/nn | a.b.c.d:e.f.g.h] | mac]

The random command can be used to obtain a variety of random objects. Current supported objects are numbers, IPv4 addresses, and MAC addresses. It is possible to obtain a random number or IP address from within a range. In the case of numbers the range is specified using the notation x:y. In the case of IP addresses the range can be specified using the IP address/netmask bits notation, or using the a.b.c.d:e.f.g.h notation. If no object is specified then a random number is returned.

send_expect [-i <listener ID>] [-o <speaker ID>] [-timeout <timeout>] [-n <number of tries>] <PDU definition>

This command sends a number of packets to a target or list of targets and then waits for responses. After responses are received, the command matches sent packets with received packets. This command was inspired by the sr() family of commands in Scapy, the packet manipulation tool by Philippe Biondi.

The -i flag specifies what listener to use to read responses. It is possible to specify multiple listeners by using this option multiple times.

The -o flag specifies what speaker to use when injecting the stimulus.

-timeout specifies how long to wait, after all packets have been sent, for answers to the injected stimulus. The timeout is specified in seconds and the default is 1 second.

-n specifies how many times to retry sending the stimulus if not all sent packets have a corresponding received answer.

The definition of the PDU to send is the same as it used in the send_network command.

The send_expect command creates three lists: the list of sent packets and the corresponding list of matching answers, and the list of unanswered packets. A script can then use these lists, decode packets, and present some useful information. For example, the following code snippet sends ICMP echo requests to all hosts in a network a displays which hosts replied:

set network 192.168.1.1-192.168.1.254
# Spawn a listener. We don't really have to specify a filter,
# like in "spawn_network {icmp[icmptype] == icmp-echoreply}"
# because the send_expect command will intelligently match
# injected stimulus (ICMP echo requests) with received
# answers (ICMP echo replies). A filter means less work for
# the send_expect command, but other than that it adds nothing.
spawn_network
send_expect -n 2 -4 -D $network -icmp-echo random:random \
    -payload "12345678901234567890" -delay .001
puts "\n[llength $_(received)] hosts sent echo-replies back:\n"
foreach r $_(received) s $_(sent) {
    packet decode r
    puts [format "$pdu(1,tot_len) bytes from $ip(src): ttl=$ip(ttl) time=%.3f ms" [expr [packet tdelta r s]*1000] ]
}
          

send_network [options] <PDU definition>

This command allows the creation custom TCP/IP packets based on packet definitions provided by the user through command-line arguments passed to the program, through a command file specified in the command-line, or through a mix of these two methods. send_network refers to instances of protocols in the TCP/IP protocol suite as protocol data units, or PDUs for short. A protocol data unit always has a header (think of the IP version 4 header, or the TCP header), may or may not have options (think of the TCP options, or IP version 6 extension headers), and may or may not have a payload. Due to the extensive use of encapsulation in the TCP/IP protocol suite, the payload of a PDU can be another payload. For example, an Ethernet frame can have as its payload an IP version 4 packet, which in itself is another PDU. Then this IP version 4 packet can have as its payload a TCP segment, which is another PDU, and so on.

When defining PDUs that send_network will create you start by creating the definitions for PDUs that are closer to the physical layer, and then move up the protocol stack until you reach, in some cases, and if that is what you want, the application layer. To implement this approach through a command-line interface (CLI) you start by entering the lower-level PDUs closer to the beginning of the command-line, or, if you are reading your packet definitions from a file, by entering definitions for lower-level PDUs at the beginning of the file. After you have defined a certain PDU you cannot define another PDU that is lower than the previous in the protocol stack. For example, you cannot define a TCP segment and then create an Ethernet frame as the segment's payload, just because that does not make sense (defining a TCP segment and then a BGP message does make sense, for example.) For this reason, order does matter in the command-line (or in the file, if defining PDUs in a file) when creating the PDUs.

sleep <seconds>

causes the script to sleep for the given number of seconds. seconds may be a decimal number. Interrupts are processed while Network Expect sleeps.

spawn_network [-nolistener] [-fullspeed] [-info] [-i <input interface>] [-o <output interface>] [-r <input PCAP file>] [-p] [-s <snaplen>] [-w <output PCAP file>] [-hexdump] [-stdout] [-4] [-6] [<PCAP filter>]

The spawn_network command is used to find information about existing listeners and speakers or to create network speakers and network listeners.

To find information about existing network listeners and speakers the spawn_network command needs to be invoked with only the -info option.

Listeners are created by using the options -i or -r. -i specifies an interface to listen for traffic on, and -r specifies the use of a PCAP file for reading instead of reading live traffic from an interface. In both cases, an optional PCAP filter can be specified to limit the type of traffic that will be read. If -i is not used then Network Expect will try to find a suitable interface. -fullspeed causes reading from a PCAP file at full speed, without preserving the inter-packet delay present in the savefile. If this option is not specified then the inter-packet delay present in the savefile will not be preserved.

Speakers are created using one of -o, -w, hexdump, stdout, -4, or -6. -o specifies the use of an interface for injecting traffic into the network. This implies the use of layer 2 injection in which the Ethernet header is specified by the user. -w specifies that all traffic will be sent to the PCAP file PCAP file. This option requires the use of layer 2 injection, i.e. the Ethernet header must be included. -hexdump specifies that all packets be sent to standard output in hexadecimal format. -stdout causes all packets to be sent to standard output in raw format. The -4 and -6 options specify the creation of layer 3 speakers to inject IPv4 and IPv6 traffic respectively. In this case all routing decisions are performed by the kernel.

The -p and -s options are only meaningful for listener creation. -p specifies that the interface be not put in promiscuous mode when creating a listener that will listen on an interface. -s specifies the snapshot length of captured packets. These two options are equivalent to the tcpdump(8) options of the same names.

Note that if no options to create a speaker are specified, i.e. -o, -w, hexdump, stdout, -4, or -6, the default action is to only create a listener. If only a speaker is desired one of the options to create a speaker must be specified and the -nolistner option must be used.

As an example, the follow command creates a listener on interface eth0 for ARP requests for the MAC address of host 192.168.1.1:

spawn_network -i eth0 host 192.168.1.1 and {arp[6:2]} == 1
          

Note that curly braces are necessary around "arp[6:2]" because brackets have special meaning in Tcl.

system <system command> [<args>]

The system command allows to run arbitrary system commands from within a Network Expect script.

Please note that this command does no input validation at all, which means that it is very insecure. For example "system {ls;/bin/sh}" will give you a shell. If nexp is run as roon then the shell will be a root shell.

The exec command in the standard Tcl distribution is a much better alternative to the Network Expect system command. Please see the Tcl documentation for the exec command for additional information.

timeval new | delta <args>

The timeval command allows to manage variables of type timeval.

timeval new; returns a timeval variable that corresponds to the current system time.

timeval tdelta <timeval variable 1> <timeval variable 2> calculates the time delta between the timeval variable 2 and timeval variable 1.

txdelta <speaker ID>

Every time a packet is sent, the time the packet was sent is saved to the network speaker that was used to send that packet. The txdelta (short for "transmission delta") command returns the number of seconds that have elapsed since the last packet that was sent via the specified speaker ID.

The following example provides the foundation for a very simple ping program, and shows how the txdelta command can be used:

for {set id 0; set seq 0} {1} {incr seq} {
    send_network -4 -D $target -icmp-echo $id:$seq -payload "12345678901234567890"
    expect_network -timeout 1 {$icmp(type) == 0 && $icmp(id) == $id} {
        puts [format "$pdu(2,tot_len) bytes from $ip(src): icmp_seq=$seq ttl=$ip(ttl) time=%.3f ms" [expr [txdelta ip]*1000 ] ]
        sleep [expr 1.0 - [txdelta ip] ]
    }
}
          

SPECIAL VARIABLES

Special variables.

EXAMPLES

The examples directory in the Network Expect distribution contains plenty of examples that should provide a very good idea of how to use Network Expect. A few selected examples have been selected for this manual page.

1. The following code snippet performs a TCP three-way handshake. Everything is done by hand, which allows to play with TCP initial sequence numbers (ISNs), window sizes, etc. The code is a bit more complex that necessary because an effort is made to handle error coditions (timeout, remote host not listening on the destination port, strange combination of TCP flags received in response to our initial SYN.)

# Some useful constants
set SYN 0x02
set RST 0x04
set ACK 0x10
set retries 3
set isn [random]
set myip 192.168.1.2
set target 192.168.1.1
set sport [random 20000:65535]
set dport 21
set window 4096
# Spawn a listener for TCP segments coming from the FTP server to us
spawn_network -i $interface "tcp and src host $target and dst host $myip and src port $dport and dst port $sport"
# Send TCP SYN
send_network ip(src = $myip, dst = $target)/ \
             tcp(src = $sport, dst = $dport, window = $window, \
                 syn, seq = $isn, ack-seq = 0)
# Wait for response from the server
expect_network {$tcp(flags) == ($SYN | $ACK)} {
    # Got a SYN+ACK so we need to send the final segment of the
    # 3-way HS
    send_network ip(dst = $myip, dst = $target)/ \
                 tcp(dst = $tcp(dstport), dst = $tcp(srcport), \
                     window = $window, ack, seq = $tcp(ack), \
                     ack-seq = [expr $tcp(seq) + 1])
} {$tcp(flags) & $RST} {
    puts "Connection refused"
    exit 1
} {1} {
    # Any other weird combination of TCP flags we respond to
    # with a RST
    send_network ip(src = $myip, dst = $target)/ \
                 tcp(src = $tcp(dstport), dst = $tcp(srcport), \
                     rst)
    exit 1
} timeout {
    # Our SYN got lost in transit or it was filtered - perform
    # exponential backoff and retransmit the SYN...
    if {$retries > 0} {
        incr retries -1
        set timeout [expr $timeout*2]
        puts "SYN timeout, increasing timeout to $timeout"
        send_network ip(src = $myip, dst =  $target)/ \
                     tcp(src = $sport, dst = $dport, \
                         window = $window, syn, seq = $isn, \
                         ack-seq = 0)
        nexp_continue
    } else {
        puts "Connection timed out"
        exit 1
    }
}
# TCP connection has been established. Now do something...
        

2. This illustrates how to create a phantom host on the network that can respond to ICMP echo requests, and therefore, to ARP requests as well.

set interface eth0
set myip 192.168.1.1
set mymac [random mac]
# Spawn a listener for ARP requests
spawn_network -i $interface host $myip and {arp[6:2]} == 1
set arpl $listener_id
# Spawn a listener for ICMP messages sent to us
spawn_network -i $interface icmp and dst host $myip
set icmpl $listener_id
expect_network_background -i $arpl {1} {
    # Received an ARP request, send ARP reply
    send_network -o $interface \
        ether(src = $mymac, dst = $arp(sha) )/ \
        arp-reply(tha = $arp(sha), tip = $arp(sip), \
                  sha = $mymac, sip = $myip)
    nexp_continue
} -i $icmpl {$icmp(type) == 8} {
    # Received ICMP echo request, send echo reply
    send_network -o ip \
        ip(src = $myip, dst = $ip(src) )/ \
        icmp-echoreply(id =  $icmp(id), seq = $icmp(seq) )/ \
        raw($raw)
    nexp_continue
}
        

3. This example how to create a very simple traceroute program that uses TCP probes to port 80 of the target host. It uses the send_expect command.

set target "www.example.com"
set ttlrange "1:30"
set interface [outif $target]
# Spawn a listener. We don't really have to specify a filter because the
# send_expect command will intelligently match injected stimulus with
# received answers.
spawn_network -i $interface
send_expect -tries 2  -delay 0.001 \
    ip(id = random, dst = $target, ttl = $ttlrange)/ \
    tcp(src = random, dst = 80, syn)
foreach r $_(received) s $_(sent) {
    packet decode r
    set source $ip(src)
    set pdu_type $pdu(1,type)
    packet decode s
    puts [format "$ip(ttl) $source %.3f ms $pdu_type" [expr [packet tdelta r s]*1000] ]
}
        

4. This shows how to perform an ARP scan using regular send_network and expect_network commands:

set interface eth0
set network "$iface($interface,ip)/$iface($interface,netmask)"
set arprequest [pdu new -o $interface
    ether(dst = BROADCAST)/ \
    arp-request(tha = BROADCAST, tip = $network, \
                sha = $iface($interface,hw_addr), \
                sip = $iface($interface,ip) ) ]
# Spawn a listener for ARP replies
spawn_network -i $interface {arp[6:2]} == 2
for {set i 0} {$i < [pdu count arprequest]} {incr i} {
    # Send ARP request
    send_network -count 1 arprequest
    # Read ARP reply
    expect_network -timeout .05 {1} {
        puts "$arp(sip) is at $arp(sha)"
    }
}
        

5. This example shows how to do an ARP scan but in a more efficient manner using the send_expect command:

set interface eth0
set network "$iface($interface,ip)/$iface($interface,netmask)"
# Spawn a listener for ARP replies
spawn_network -i $interface {arp[6:2]} == 2
send_expect -o $interface -delay 0.001 -tries 2 \
    ether(dst = BROADCAST)/ \
    arp-request(tha = BROADCAST, tip = $network, \
                sha = $iface($interface,hw_addr),
                sip = $iface($interface,ip) )
puts "\nFound [llength $_(received)] hosts alive:\n"
foreach r $_(received) {
    packet decode r
    puts "$arp(sip) is at $arp(sha)"
}
        

BUGS

Network Expect does not run very well under the Solaris operating system because in that operating system select() does not seem to work well with packet capture file descriptors (select() returns when there is no data ready to be read.)

Network Expect does not work at all in Microsoft Windows because select() does not work at all with packet capture file descriptors (pcap_get_selectable_fd() does not exist under Microsoft Windows.)

Error-checking is almost non-existant.

These isn't input validation for the numeric specifications.

The parser of PDU definitions (and therefore the number of tokens that the parser handles) has become a big, wild beast. It's very easy to add new tokens and PDU definitions are elegant and pretty, but the parser is huge. Guess can't have everything.

VERSION

This man page is correct for version 1.0 of Network Expect.

AUTHOR

Network Expect was written by Eloy Paris <[email protected]>. However, Network Expect borrows ideas from lots of Open Source tools like Nemesis, Packit, hping, Expect, and Scapy. The Network Expect author is indebted to the authors of these tools for their contribution.

This man page was written by Eloy Paris although it borrows heavily from Expect's manual page.