cnet v2.0.5

home
introduction
simulation model

topology files
cmdline options
the API

FAQ

download

cnet Frequently Asked Questions

This page contains a number of Frequently Asked/Answered Questions (FAQs) about the cnet network protocol simulator. Please read this page first to ensure that you fully understand what is happening and then be able to anticipate any errors that you may confront.

Please appreciate that there are thousands of students worldwide using cnet. I am unable to respond to individual questions about cnet, unless they are from students enrolled in a course that I'm presenting. In particular, I will not answer homework or assignment questions. Please ask your professor or instructor.

Getting started

Developing protocols

Managing time and periodic activity

Hidden meanings

Getting some real protocol simulation done

Please email chris@csse.uwa.edu.au if you find any errors here, or think that some additional material should be added.

line

How do I get started with cnet?

There are a number of on-line WWW pages to help you to get started with cnet. Allow about an hour to read all of these:

If your copy of cnet has been installed (correctly) by someone else, you should read the Linux/Unix manual entry for cnet. You can read this by issuing the command:

                       man cnet

on your machine, taking note of any ``local conditions'' such as the location of cnet's files and examples. The manual entry briefly outlines the capabilities of cnet and lists the many command-line options available.

Having read the manual entry, read the cnet specific header file (typically installed at /usr/local/include/cnet.h). All cnet programs must include the line

                       #include <cnet.h>

to include the contents of this file. In particular, it is important that you understand the CnetNodeinfo and CnetLinkinfo structures, the CnetEvent and CnetError enumerated types and all function prototypes. These are all described in much greater detail in another set of web-pages:

line

How do I compile my cnet protocol source files?

You do not need to compile your protocol files yourself. Simply executing

cnet TOPOLOGY

will cause cnet to locate and compile your ANSI-C files and produce a shared object file in your working directory (e.g. protocol.cnet). This shared object is then dynamically linked with the cnet simulator at run-time.

The system's standard C compiler is used, preferably GNU's gcc. All C error messages are reported by the compiler (not cnet). All fatal and warning messages should be eliminated before your protocol code can be considered syntactically correct. You will probably receive many more error messages than you've experienced before - the reason being that the compiler is invoked with extra compilation switches to be very pedantic (this is good for your soul and in fact is how you should always compile C code). If you are concerned about any ``black magic'' destroying your code, observe what happens by invoking cnet as:

cnet -d TOPOLOGY

line

Why does cnet terminate after 3 minutes?

Errors in your protocols which prevent an event handler from returning when expected, prevent the cnet scheduler from performing correctly. In particular, the scheduler cannot service events from the windowing system - for example your requests to kill cnet itself when you detect a problem. To overcome this major problem, cnet itself times-out after 3 minutes just in case you have written incorrect protocols which have become 'stuck'. Once you are confident that your protocols are working as expected you can easily extend this 3 minute period with, for example,
cnet -m 10 TOPOLOGY

where the command line option indicates the required number of minutes.

line

How can I develop my cnet protocols in multiple files?

As cnet projects become larger, it's naturally wise to develop protocols in a number of different source files. A natural method to partition the files is based on their responsibilities. C (and of course many other programming languages) allow you to place relatively independent sections of source code in separate files, compile each source file individually, and to then link the resulting object files to form a single executable file.

cnet also allows you to do this, but simplifies the activity. In your topology file, replace a line such as

        compile = "protocol.c"
with	compile = "dll.c nl.c routing.c queueing.c fragments.c"
(or whatever) and cnet will quietly compile and link all of the pieces. Only one of the C source files needs to have a reboot_node() function.

cnet handles the compilation and linking quite happily, unless it is interrupted. If individual files appear to be not being compiled, just remove all object files with rm *.o and re-run cnet. If you're interested in what's going on, invoke cnet with its -v switch to see the executed compilation and linking commands.

line

How can I debug my cnet programs?

Because many things appear to be happening simultaneously in cnet, debugging cnet protocols can be difficult. However, it is far easier than debugging protocols on different computers in different geographic locations! All output to C's implicit standard output stream appears on each node's output window. Output to C's standard error stream will appear on the invoking shell window (tty or pty).

Each node's standard output stream can be copied to an individual file using the -o option to cnet. For example, if running a two node network with

cnet -o debug TOPOLOGY

all output will be copied (typically) to the files debug.node0 and debug.node1.

Most importantly, most cnet functions return an integer indicating their success or failure (0 for success, -1 for failure). IT IS ESSENTIAL that you examine the function's return value to ensure that it performed as expected. If you ignore this return value your protocols may fail at a much later stage in their execution and it will be extremely difficult to track down your error. If a function detects an error (and returns -1) it will also set the node-specific variable cnet_errno to reflect what went wrong. The most recent error detected by cnet may then be printed from each node (to stderr) with the function cnet_perror or you may construct your own error messages using the error descriptions in *cnet_errname[] or *cnet_errstr[].

It is also helpful to trace your protocols to see the exact ordering and arguments of cnet function calls. Tracing may be selected with the -t command line option, setting the trace node attribute to true for all or individual nodes in the topology file or by selecting the trace checkbox on either the default or specific node panels under the windowing system. Tracing will appear on the trace stream of cnet (either the separate Tcl/Tk trace window or the shell's tty) and shows each node's event handling functions being invoked (and returned from) and, within each handler, all function calls, their arguments and the function return values. Any function arguments that are modified by the functions (arguments passed by reference) are also shown after the function return values. If any errors are detected by the functions themselves, these will be reported within the trace. See Tracing cnet's execution.

As a special case, networks of only 2-nodes may request that all data frames traversing the Physical Layer be drawn in a special window. Drawing frames requires a small addition to the protocol's topology file, and a special event handler to describe how the frames are to be drawn. A careful choice of colours and frame (field) lengths can assist in the debugging of Data Link layer protocols. See Drawing data frames in cnet.

line

Are there any simple tricks that can help my understanding?

Many people get confused with cnet's apparent ability to manage multiple nodes simultaneously within a single process (which is, in fact, one of its unique features). In addition, it can be initially confusing to understand how a single protocol can act as both a sender and receiver simultaneously. A simple trick to ease this confusion is to only allow one node to transmit and the other to receive (in a network of just 2 nodes). As nodes have nodenumbers of 0, 1, 2, ... adding the lines

    if(nodeinfo.nodenumber == 0)
	(void)CNET_enable_application(ALLNODES);

to your handler for EV_REBOOT, typically reboot_node(), will only permit node #0 to generate messages for delivery.

line

What is the CHECK function that appears in most examples?

CHECK is actually not a function provided by cnet (or UNIX) but a C macro defined in the cnet header file.

Most of cnet's library (builtin) functions return 0 on success and something else, typically -1, on failure. In fact, if any of these functions fail, it probably indicates a serious error in a protocol (there are a few exceptions to this generalization, such as cancelling a timer that has already expired). Moreover, all functions will set the global error variable cnet_errno on failure and this may be used as a index into the globally accessible array of error strings, cnet_errstr. This is similar to the use of errno and sys_errlist in ANSI-C.

By enveloping most calls to cnet's library routines we can get an accurate and immediate report on the location (source file + line number + nodename) and type of each error. If using the GNU C compiler, we can also determine the function name in which the error occurred. These helpful values are passed to cnet's function CNET_exit which, if able, pops up a window highlighting the file and line number of the runtime error. Looking at the definition of CHECK in <cnet.h> may expose the "black magic":


#if	defined(__GNUC__)
#define CHECK(call)   if((call) != 0) { \
                        CNET_exit(__FILE__,__FUNCTION__,__LINE__);}
#else
#define CHECK(call)   if((call) != 0) { \
                        CNET_exit(__FILE__,(char *)NULL,__LINE__);}
#endif

CHECK may not strictly belong in cnet's header file, but it's such a useful macro, it saves everyone from re-inventing the wheel.

line

What is the difference between a node's number and its node address?

Nodes have both a number and an address - node numbers (available in nodeinfo.nodenumber) range from 0,1,2,.....NNODES-1, whereas each node's address (available in nodeinfo.nodeaddress) can be any unique non-negative value. By default node numbers and node addresses are the same (0,1,2,....).

Setting a node address attribute in the topology file, as with

    host Perth {
        address     = 351
        ....
    }
should reveal a problem if your protocols are assuming that node numbers and node addresses are always the same. In particular, the destination node addresses provided by CNET_read_application and expected by CNET_write_direct are node addresses and not node numbers.

line

Can cnet display my protocol's data frames?

You're in luck! As of version 1.7, cnet can present a limited visualization of data frames traversing the Physical Layer. Using just colours and lengths, it is possible to display both data and acknowledgment frames, and the contents of some of their fields. In combination, these features may be used to debug implementations of Data Link Layer protocols. See Drawing data frames in cnet.

line

How do I determine the current time?

Do not attempt to use Unix's time or gettimeofday functions.

In cnet each node's "system-time" is provided in the global integer variable nodeinfo.time_in_usec, which measures the time, in microseconds, since the node was last rebooted. This value will (usually) have increased between calls to event handlers, but its value will not change during the execution of an event handler.

The current time of day, i.e. the "wall clock time", of each node is available via the structure nodeinfo.time_of_day, i.e. in nodeinfo.time_of_day.sec and nodeinfo.time_of_day.usec. The integer value in nodeinfo.time_of_day.sec represents the number of seconds elapsed since 00:00:00 on January 1, 1970, and can thus be used as an argument to standard Unix functions such as ctime().

Unless cnet is invoked with the -c option, the wall clock time of all nodes is initially different on each node. If -c is specified, the clocks in all nodes will initially be synchronized. Protocols may be developed, which call CNET_set_time_of_day to synchronize the clocks.

cnet provides no support for timezones.

line

What are timers and CnetTimers?

The event-driven nature of cnet means that your protocols cannot simply 'wait' for something to happen. The cnet scheduler will inform your protocols when important things need doing (messages to deliver, frames to receive, etc). In particular, your protocols cannot simply wait a nominated period of time and then do something appropriate after that time.

YOUR PROTOCOLS SHOULD NOT CALL sleep() or any similar functions. Instead, cnet provides timers so that the scheduler may inform your protocol when a nominated period of time has elapsed. You may have an almost unlimited number timers quietly ``ticking away'' - they are each uniquely identified by a CnetTimer.

When you create a new timer you must indicate one of 10 timer events EV_TIMER1..EV_TIMER10 and a period of time (in microseconds) in the future. The function CNET_start_timer will return to you a CnetTimer so that you may keep track of which timer has expired when your timer event handler is invoked. For example:

    CnetInt64  when;
    CnetTimer  saved_timer;

    int64_I2L(when, 1000000);
    saved_timer = CNET_start_timer(EV_TIMER1, when, 0);

will cause the event handler for EV_TIMER1 to be called in 1 second. The value of saved_timer will be passed as the second parameter to the handler so that you can see which timer expired. You can have as many outstanding timers on the EV_TIMER1 queue as you want. PLEASE NOTE: there are not only 10 timers available - however, each timer must be tagged with one of the 10 available timer events.

If you decide that you no longer want to be informed when a timer expires, you should call CNET_stop_timer with the CnetTimer in which you are no longer interested. For example,

    (void)CNET_stop_timer(saved_timer);

If the cnet scheduler invokes your timer handler, then you do not need to cancel the corresponding timer (it will be done for you). However, if you wish a timer event to be raised periodically, then you'll need to start a new timer in the handler of an expiring timer, i.e. timers only expire once, not repeatedly.

line

Why does cnet provide 10 distinct timer queues?

When writing protocols in multiple layers, it's a nice separation of concerns to use different timers in each layer. For example, in a Data-Link layer protocol, we could use EV_TIMER1 for a retransmission timer, and EV_TIMER2 for a piggyback timer. At the same time, the Network layer may use EV_TIMER3 to flush any queued messages if it uses a leaky bucket for congestion control, and EV_TIMER4 to periodically exchange routing table information with neighbours. Using a distinct timer queue for each activity allows us to use a separate handler to manage the requirements of each activity, and to ``hide'' the handler in the protocol layer or source file of concern.

line

What is the third parameter to CNET_start_timer ever used for?

Any value passed as the third parameter to CNET_start_timer is remembered, internally, along with the timer. When the timer expires, this saved value is passed as the third parameter to the handler for the timer's event. This parameter is of type CnetData (a long integer in C) which allows it to store integral values or a pointer to a variable or dynamically allocated data structure. Typical uses for this parameter are to pass a sequence number used in a protocol, or perhaps a pointer to a list or tree structure, to the timer event's handler.

If the parameter is used to store a pointer, care must be taken to ensure that the pointer is still valid at the time the timer's event handler is called. In particular, the parameter should never be used to store the address of any variable or structure on C's runtime stack. It is reasonable to pass a pointer to dynamically allocated storage to CNET_start_timer (i.e. allocated with malloc), and then have the timer's event handler deallocate this storage (i.e. deallocated with free).

If you need to call CNET_stop_timer before a timer expires, take care to first deallocate any dynamic storage associated with the timer as a CnetData value. You can ``recover'' the CnetData value by calling the function CNET_timer_data.

line

Can I add my own CnetEvent events?

No, not unless you wish to change and recompile the source code of cnet itself. However, there are a few ``spare'' standard EV_TIMER events that could be re-used or ``renamed'' to suit your purpose. For example, if you'd like a new event for the Data Link Layer to signal the Network Layer, you could (ab)use the C-preprocessor and say:


#define  EV_DLL_2_NL                 EV_TIMER8
#define  raise_dll_2_nl_event(data)  CNET_start_timer(EV_DLL_2_NL, 1LL, data)

and then simply call raise_dll_2_nl_event() with an instance of CnetData when you want to raise the pseudo-event.

line

What is the meaning of "spelling mistake on line 83 of protocol.c"?

There is a spelling mistake on line 83 of protocol.c

line

What is the meaning of "caught signal number <??> while (last) handling Perth.EV_APPLICATIONREADY"?

Old tricks for young players.

Fatal error messages of this form generally indicate a major problem with your protocols. The number (typically 2, 10 or 11) refers to a Unix signal number intercepted by cnet (see /usr/include/signal.h). For example, signal number 2 will be caught and reported by cnet if you interrupt cnet from the shell level (signal 2 = SIGINT). The other common signals, 10 and 11, reveal significant flaws in the implementation of your protocols.

Signal 10 (SIGBUS, a bus error) occurs when the CPU attempts to execute an instruction not on an instruction boundary (on many architectures, you've requested to execute an instruction whose address is not a multiple of 4). This error will generally occur when your programming corrupts your program's stack and, in particular, you corrupt the return address of the currently executing function. When the current function attempts to return (to a now incorrect address) and then fetches an instruction whose address is invalid, signal 10 will result.

Signal 11 (SIGSEGV, segmentation violation) occurs when your program attempts to address memory that has not been mapped into your address space. Typically, by accessing a pointer that has not been correctly initialized or has been modified/overwritten incorrectly, that pointer will point to memory that you do not ``own'', it being owned by either the operating system or another (person's) process. When attempting to access outside of your memory segment, you will get a segmentation violation. Operating systems that do not provide memory protection (segmentation), for example DOS, will not report this error as the (single) process on those operating systems "own" all of the address space. Your program there will still (maybe!) exhibit errors but these may not be reported to you. Unix is in fact doing you a favour.

Signals 10 and 11 spell disaster for your programs - there is obviously something seriously wrong with your program if they happen. Both forms of error most frequently occur when you are incorrectly managing pointers and/or dynamic memory.

Such problems are very difficult to diagnose - your first action should be to check your programming logic. By their nature, errors which often *cause* signals 10 and 11 to be reported, do not necessarily raise the signal immediately. You may do the wrong thing many instructions or even seconds before the signal is reported. For this reason, the best cnet can do is state which event handler it was executing (or it was most recently executing) when the signal occurs. This does not necessarily indicate that your programming error is in that event handler though experience shows that this is likely.

line

What is the meaning of the statistic ``Efficiency (bytes AL/PL)''?

Here, AL stands for Application Layer, and PL for Physical Layer. This statistic divides the number of bytes generated by the Application Layers, by the number of bytes traversing the Physical Layer. Our protocols will require headers for their frames and packets, re-transmit data frames, and generate acknowledgments and other control packets, and so this ratio is expected to be less than 100% (the price we pay for reliable message delivery). The statistic is not updated until a message is successfully written ``up'' to an Application Layer.

Keep in mind that this ratio is not the only desirable measure of protocol efficiency (but retains this name for historical reasons). Protocols may also strive to minimize average delivery time, or the total (monetary) cost of delivering frames.

line

What is the meaning of the error ``Function is too busy/congested to handle request''?

The function CNET_write_physical() will 'trap' the situation when a large number of frames have been written to the Physical Layer, and when the receiving node has not read any of them off. This trap is currently set at the large value of 1000, which surely indicates an error in a protocol.

Your protocol may have some unbounded loop, or a very short timeout-and-retransmission sequence, resulting in many calls to CNET_write_physical() at the sender, before any EV_PHYSICALREADY events are handled at the receiver.

line

How can I speed up cnet?

  1. Don't print out volumes of debug information to each node's output window. The printing of large amounts of text and scrolling these windows obviously slows down cnet. Print out bad news, not good news.
  2. Disable all printing, altogether - invoke cnet with the -q option.
  3. If you think your protocol works ``for a few minutes'' but then dies a death, change a few attributes to speed up the cnet world. For example:
    
        messagerate      = 3ms
        propagationdelay = 1ms
    
    
    should make your protocol ``work for a few seconds''. Hmmmm, much better.
  4. If you'd rather not wait a full second for cnet to complete one second of network activity, run with the -T option to force events to be scheduled immediately (nodeinfo.time_in_usec is updated as you'd hope).
  5. You don't always need to work with cnet's graphical display; it'll run ``within'' an ASCII terminal window without its display if you invoke cnet with its -W option. You can also use the -o option or explicitly send output to stdout, stderr or a file rather than expecting it to appear in its own stdout window. After a while the gimmick of the ``network map'' should've worn off and you should only be debugging bad news, ala cnet_errno and cnet_perror().

line

How do I collate cnet statistics for plotting?

cnet centrally collates statistics on behalf of all nodes, and displays these on the 'Statistics' popup window or at the end of a simulation run if cnet is invoked with the -s option (or the -z option to also get zero-valued statistics).

We can also print statistics more frequently (periodically) with the correct choice of command line options. These are:

-W no need for the windowing interface
-T run the simulation as fast as possible
-M 5 run for 5 minutes of simulation time
-s yes, we want statistics
-f 10 print statistics with a frequency (period) of 10 seconds

This will produce volumes of output to cnet's standard output stream, so we need to both capture this and probably filter only what we need. So, to capture the Efficiency measure (bytes AL/PL) every second (in the hope that it improves), we issue:


  #!/bin/sh
  #
  cnet -W -T -M 5 -s -f 1 topologyfile     | \
  grep Efficiency                          | \
  cut -d: -f 2                             | \
  cat -n              > result.file

The last line takes its input (a column of 300 efficiencies) and places a line number at the beginning of each line. This is fine if we really want statistics every single second, but slowly adapting protocols may take several minutes to reach their optimum. We could develop a shellscript which accepts arguments indicating the topology file and the frequency of collection:


  #!/bin/sh
  #
  TOPFILE=$1
  FREQ=$2 
  #
  cnet -W -T -M 5 -s -f $FREQ $TOPFILE      | \
  grep Efficiency                           | \
  cut -d: -f 2                              | \
  awk '{ printf("%d %s\n", n+=freq, $0); }' freq=$FREQ  > result.file

Of course, other shellscript arguments could indicate the required statistic, resultfile, etc.

cnet was written and is maintained by Chris McDonald (chris@csse.uwa.edu.au)