Stack
Stack Organization
Overview
Protocol layers are wired together to form a stack. Click on the image to the right to see the stack organization diagram of the OpenWSN protocol stack. Send functions are used to push packets down the stack; Receive functions to pull them up. The bytes of a packet live in the OpenQueue component. A packet is a variable of type OpenQueueEntry_t and is defined in openwsn.h; components pass a pointer to an OpenQueueEntry_t variables in the Send and Receive functions.
OpenQueueEntry_t and metadata
An OpenQueueEntry_t consists of the actual bytes in the packet, plus a number of metadata fields. Metadata is used for an upper (resp. lower) layer to set parameters a lower (resp. upper) layer will need, when sending a packet down (resp. up) the stack. One example is that the MAC layer sets l1_channel to instruct the driver about which channel it needs to transmit the packet on. Another example is that RPL layer specifies l2_nextORpreviousHop, indicating to the MAC layer to which neighbor this packet needs to go.
No parameters are sent in the Send interfaces other than the pointer to the OpenQueueEntry_t. All Send interfaces have the same two functions:
error_t send (OpenQueueEntry_t* msg);
void sendDone(OpenQueueEntry_t* msg, error_t error);
Similarly, when receiving:
void receive(OpenQueueEntry_t* msg);
Instead of adding and adding parameters in the interface, they go as metadata as are attached to the OpenQueueEntry_t variable. A packet is thereby self-contained: a component should never have to maintain state linked to a particular packet.
Metadata fields have names which indicates which layer should use them, from l7_ for the application to l1_ for the radio driver. Some field are used for administration by all the layers:
creatorindicates the component which created this packet, i.e. which requested an unusedOpenQueueEntry_tto theOpenQueuecomponent. When sending a packet down the stack, the convention is that only the creator of a packet can free that packet, i.e. instruct theOpenQueuecomponent it doesn't need it anymore. Typically,OpenQueueEntry_tvariables will be created by all application-layer component when sending a packet, or by the drivers when receiving.ownerindicates the components which currently holds the packet. The convention is that a component can only change the content of anOpenQueueEntry_tif it is currently the owner.The
packetpart of theOpenQueueEntry_tholds the actual bytes of the packet. Because OpenWSN does not allow for dynamic memory allocation,packetis the maximal allowed size.payloadis a pointer to the first used by inpacket;lengthindicates how many bytes are used.
Adding/removing protocol headers
When a component requests an unused OpenQueueEntry_t from the OpenQueue component, payload points to the end packet and length is zero. If a components needs to add n bytes of data to the packet pkt, it calls:
packetfunctions_reserveHeaderSize(OpenQueueEntry_t* pkt, uint8_t header_length)
This function modifies the payload and length fields of the OpenQueueEntry_t to make space for a header of header_length bytes. After this function is called, you can write a header starting at the byte pointed at by pkt->payload, of exactly header_length bytes.
Similarly, when a components needs to remove n bytes of data from the packet pkt, it does:
void packetfunctions_tossHeader(OpenQueueEntry_t* pkt, uint8_t header_length)
In practice, fixed-length header might be defined in a header file. The TCP header, for example, is defined in tcp.h as
typedef struct {
uint16_t source_port;
uint16_t destination_port;
uint32_t sequence_number;
uint32_t ack_number;
uint8_t data_offset;
uint8_t control_bits;
uint16_t window_size;
uint16_t checksum;
uint16_t urgent_pointer;
} tcp_ht;
tcp.c, which implements the TCP protocol, reserves space for the header by calling:
packetfunctions_reserveHeaderSize(msg,sizeof(tcp_ht));
Story of a packet
This section tells the story about what each layer does in the OpenWSN implementation, including which layers read and write metadata. There are two parts: one which details what happens when a packet is generated at the application layer, and travels down the stack until the drivers; one the other way.
Each of these sections end with a table stating which layer write (w) or read (r) which metadata.
Down the stack*
Application
gets a free
OpenQueueEntry_tfills in the following metadata:
creator(to itself)owner(to itself)l4_source_port(to be used by self to route to appropriate L4)l4_destination_port(to be used by L4 to build header)l3_destinationORsourceADDR_128B (to be used by RPL to find next hop)
fills in the payload according to its own format
sends to L4
L4(TCP or UDP)
fills in the following metadata:
owner(to itself)l4_payload(to remember where the l4 payload starts, in case needs to resend)l4_length(to remember where the l4 payload starts, in case needs to resend)
adds transport header based on:
l4_source_port(filled in by Application)l4_destination_port(filled in by Application)
sends to RPL
RPL
fills in the following metadata:
owner(to itself)l2_nextORpreviousHop(which is obtains by calling getNextHop)
sends to IPHC
IPHC
fills in the following metadata:
owner(to itself)
add 6LoWPAN header
sends to RES
RES
fills in the following metadata:
owner(to itself)l2_frameType(to data if from above, to beacon if from Neighbors)
sends to MAC
MAC(IEEE802154E or stupidMAC)
fills in the following metadata:
owner(to itself)l2_retriesLeftl1_txPowerl1_channel
adds the MAC header based on:
l2_nextORpreviousHop(filled in by RPL)l2_frameType(filled in by RES)
sends to drivers
drivers
does not put owner to itself to allow MAC to retransmit (in case something goes wrong in the drivers)
configures the radio according to:
l1_txPower(set by MAC)l1_channel(set by MAC)
| App | L4 | RPL | IPHC | RES | MAC | drivers |
creator | w |
|
|
|
|
|
|
owner | w | w | w | w | w | w |
|
l4_protocol | w/r |
|
|
|
|
|
|
l4_source_port | w | r |
|
|
|
|
|
l4_destination_port | w | r |
|
|
|
|
|
l4_payload |
| w/r |
|
|
|
|
|
l4_length |
| w/r |
|
|
|
|
|
l3_destinationORsource | w |
| r | r |
|
|
|
l2_nextORpreviousHop |
|
| w |
|
| r |
|
l2_frameType |
|
|
|
| w | r |
|
l2_retriesLeft |
|
|
|
|
| w/r |
|
l1_txPower |
|
|
|
|
| w | r |
l1_channel |
|
|
|
|
| w | r |
l1_rssi |
|
|
|
|
|
|
|
l1_lqi |
|
|
|
|
|
|
|
l1_crc |
|
|
|
|
|
|
|
l1_rxTimestamp |
|
|
|
|
|
|
|
Up the stack*
drivers
gets a free an
OpenQueueEntry_tfills in the following metadata:
creator(to itself)owner(to itself)l1_rxTimestamp(for the MAC layer to possibly resynchronize)l1_crc(in fact always true as in the current drivers a packet with a false CRC is discarded)l1_lqi(not used in upper layers, for the moment)l1_rssi(used by theNeighborscomponents to select stable neighbors)
sends to MAC
MAC(IEEE802154E or stupidMAC)
fills in the following metadata:
owner(to itself)l2_frameTypel2_nextORpreviousHopto the 64b/16b address of the previous hop
if IEEE802.15.4E, resynchronizes using
l1_rxTimestampfilled in by the driversremoves the MAC header
sends to RES
RES
fills in the following metadata:
owner(to itself)
filters using
l2_frameTypefilled in by MACsends to IPHC
IPHC
fills in the following metadata:
owner(to itself)
inflates the IPv6 header
removes the 6LoWPAN header
sends the message with the inflated IPv6 header to RPL
RPL
fills in the following metadata:
owner(to itself)l4_protocol(according to thenext_headerfield in the inflated IPv6 header provided by theIPHCcomponnent)
sends to the appropriate L4 according to
l4_protocol
L4(TCP or UDP)
fills in the following metadata:
owner(to itself)l4_source_portl4_destination_port
remove the L4 header
sends to App
Application
discards the packet
| drivers | MAC | RES | IPHC | RPL |