cnet v2.0.5

simulation model

topology files
cmdline options
the API



cnet's Physical Layer

The Physical Layer (either the internal default version or one provided with the -R option) has the responsibility of delivering data frames between nodes. Frames are delivered along either point-to-point links or Ethernet segments. Each physical link is numbered within each node from 1 to its total number of links. As a special case, link 0 represents a loopback link, and is provided to simply copy a frame immediately from a node's output to input. In general, the Physical Layer will randomly corrupt and drop data frames on all point-to-point links, but not on the loopback link or Ethernet segments.

When your protocols wish to transmit a data frame along a link, they write that frame to the Physical Layer. On calling the CNET_write_physical function, you indicate the length of the frame to be written and on return CNET_write_physical indicates how many bytes were accepted. A typical sequence for a network of just 2 nodes, connected by a single point-to-point link is:

int  length;

 ...   /* prepare frame contents for transmission */
length = ... ;
result = CNET_write_physical(1, myframe, &length);

Each link is also constrained in how many bytes may be written to it in each call to CNET_write_physical. This limit may be specified in the topology file, using the link attribute transmitbufsize, and may be detemined at run-time from the C variable linkinfo[linkno].transmitbufsize. Setting transmitbufsize to a small value, say 1KB, will require your protocols to fragment large messages before sending them. The default value of transmitbufsize (9KB), is sufficient to handle the largest message generated by the Application Layer (8KB), and so fragmentation is not required in elementary protocols.


When cnet informs the destination node that a frame has arrived, the handler for EV_PHYSICALREADY should read that frame. On return from a successful call to CNET_read_physical, your protocol is informed on which link the frame arrived and how long it was.

int  link, length;

length = sizeof(myframe);
result = CNET_read_physical(&link, myframe, &length);
 ...   /* process frame contents */

Of course, in a simple network with just one point-to-point link or one Ethernet segment, all frames will be transmitted and will arrive on link number 1. Point-to-point links impose no particular format on the frames written to it; unless corrupted or lost, whatever is written to a point-to-point link will arrive unmodified, and without interpretation, at the other end of the link.

As an aid to debugging protocols, 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. An errant 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. If the frame limit is exceeded, CNET_write_physical() will return -1 and set cnet_errno to ER_TOOBUSY.


To provide some sense of realism, frames (or packets) written to Ethernet links are expected to carry the address of their destination Network Interface Card (NIC) at the very beginning of the frame. cnet provides the data type CnetNicaddr to represent the addresses of its NICs, as an array of LEN_NICADDR (=6) unsigned characters. cnet interprets the leading LEN_NICADDR bytes of each frame on an Ethernet segment to be an address. The special address, whose string representation is ff:ff:ff:ff:ff:ff, is interpreted as the Ethernet broadcast address. Any frame carrying the broadcast address as its destination address will be delivered to all NICs on the Ethernet segment, except the sender. cnet does not support multicast or group addressing.

Consider the following example function, used to write data to an Ethernet segment:

typedef struct {
    CnetNicaddr    dest;
    CnetNicaddr    src;
    char           type[2];
    char           data[ETH_MAXDATA];

#define LEN_ETHERHEADER (2*sizeof(CnetNicaddr) + 2)

static void write_to_ethernet(CnetNicaddr dest, int link, char *buf, int len)
    ETHERPACKET packet;
    short int   twobytes;

    memcpy(packet.dest, dest,                   sizeof(CnetNicaddr));
    memcpy(packet.src,  linkinfo[link].nicaddr, sizeof(CnetNicaddr));

    twobytes = len;              /* type carries the data's true length */
    memcpy(packet.type, &twobytes, 2);
    memcpy(, buf, len);

    len  += LEN_ETHERHEADER;
    if(len < ETH_MINPACKET)      /* pad short packets to minimum length */
        len = ETH_MINPACKET;
    CHECK(CNET_write_physical(link, (char *)&packet, &len));

This function assumes that the data's length is not too long for Ethernet (<= ETH_MAXDATA (=1500) bytes). The required destination's NIC address is first copied to the destination address field, and then the address of the local NIC used copied to the source address field. Notice that because the CnetNicaddr type is actually an array of characters, we do not use the & operator in the calls to memcpy. The data's true length is copied into the packet's two-byte type field, the provided data copied to the packet's data. After ensuring that the packet to be written is at least ETH_MINPACKET (=64) bytes long, the packet is written to the link. Again, cnet does not enforce (nor understand) the use of our ETHERPACKET data type, but does assume that the first LEN_NICADDR bytes of each packet provides the destination NIC address.

Two additional Physical Layer functions are provided to assist in the debugging of multi-layered protocols. CNET_write_physical_reliable is identical to CNET_write_physical except that frames sent using it will not be subject to frame corruption or loss. It can be considered as a ``perfect'' Data Link Layer if you just want to implement higher-layered protocols. The function CNET_write_direct also bypasses all Physical Layer errors and instructs a message to be sent directly to the node whose address is specified as a parameter. It thus provides perfect a Data Link Layer and Network Layer.


Physical Layer functions

int CNET_write_physical(int link, char *frame, int *len);

Passes a number of bytes, pointed to by frame ``down to'' the Physical Layer which will attempt to deliver them on the indicated link (wire). Each node has a fixed number of links, the first available link is number 1, the second is number 2, and so on. As a special case, a node may reliably transmit a frame to itself by requesting the LOOPBACK(=0) link. On invocation, len must point to an integer indicating the number of bytes to be taken from frame. On return, len will point to an integer now indicating the number of bytes accepted by the Physical Layer.


int CNET_write_physical_reliable(int link, char *frame, int *len);

Identical to CNET_write_physical though the transmission is guaranteed to be error free (providing a reliable data-link layer).


int CNET_write_direct(CnetAddr destaddr, char *msg, int *len);

Similar to CNET_write_physical_reliable but the network address of the required destination node may be specified (providing a reliable network/routing layer for asynchronous message passing). Messages transmitted using CNET_write_direct are considered to be transmitted on, and arrive on, link number 1. The special destination address BROADCAST may be used to transmit a message to all nodes except the sender. Regardless of the number of hops, propagation delays, and bandwidth, all frames sent via CNET_write_direct reach their destination in just 1msec.


int CNET_read_physical(int *link, char *frame, int *len);

Accepts the specified maximum number of bytes from the Physical Layer, placing them in the address pointed to by frame. On invocation, len must point to an integer indicating the maximum number of bytes that may be copied into frame. On return, len will point to an integer now indicating the number of bytes taken from the Physical Layer and link will point to an integer indicating on which link they were received.


int CNET_set_promiscuous(int link, int Boolean_value);

This function places (or removes) the Network Interface Card (NIC) of the indicated physical link into promiscuous mode. When in promiscuous mode, the NIC will receive a copy of all frames on the segment, even if they are neither broadcast frames or explicity addressed to the NIC. Promiscuous mode may only be set for links of type LT_ETHERNET, not for LT_LOOPBACK (link=0) or LT_POINT2POINT links.

Possible errors: ER_BADLINK.

int CNET_set_nicaddr(int link, CnetNicaddr new_nicaddr);

This function sets the address recognized by the Network Interface Card (NIC) of the indicated physical link. The new address may not be the zero address, 00:00:00:00:00:00, or the broadcast address, ff:ff:ff:ff:ff:ff. No check is made to ensure that the new address is unique within the network (thereby enabling some snooping/sniffing protocols to be developed). NIC addresses may only be set for links of type LT_ETHERNET, not for LT_LOOPBACK (link=0) or LT_POINT2POINT links.

Possible errors: ER_BADARG, ER_BADLINK.

int CNET_parse_nicaddr(CnetNicaddr nicaddr, char *string);

This function accepts a character string of the form ab:cd:ef:gh:ij:kl, where each letter is a valid hexadecimal character, and converts this string representation to the ``internal'' form consisting of an array of 6 unsigned characters. Any valid Network Interface Card (NIC) address is accepted.

Possible errors: ER_BADARG.

int CNET_format_nicaddr(char *buffer, CnetNicaddr nicaddr);

This function accepts a Network Interface Card (NIC) address and formats its `internal'' form to a string of characters of the form ab:cd:ef:gh:ij:kl, where each letter will be a hexadecimal character.

Possible errors: ER_BADARG.

cnet was written and is maintained by Chris McDonald (