Generic (GNRC) network stack

RIOT's modular default IP network stack. More...

Detailed Description

RIOT's modular default IP network stack.

See also
Martine Lenders' master thesis about GNRC's design and evaluation and the slide set of its defense.

About

This module is currently the default network stack for RIOT and includes many components ranging from a Network interface API through a fully-featured IPv6 implementation with 6LowPAN extensions to an UDP implementation and RPL.

A list of all features contained in the Generic (GNRC) network stack is available in the Modules section above.

Integration into RIOT

From the application layer the Generic (GNRC) network stack can be accessed through the Sock API, while the interface to the Netdev - Network Device Driver API is defined by the Network interface API.

Architecture

Each layer of the network stack runs in its own thread and each lower layer thread has a higher priority than any upper layer thread. In this regard, the thread of the MAC layer implementation has the highest priority and threads on the application layer have the lowest priority. The communication between threads is handled by the kernel's Messaging / IPC functionality and by the GNRC communication interface. Most of the times IPC will take place between threads of neighboring layers for packets that traverse the network stack up or down.

Due to the design of GNRC and the nature of inter-process communication, it is crucial for a new module that introduces a new thread to follow a certain programming construct if it desires to interact with other threads without blocking the system: Utilizing an event loop.

Hence, a thread for GNRC will usually consist of four basic steps.

  1. Initialize a message queue (note that its size must be a power of two, see msg_init_queue())
  2. register for a Protocol type
  3. wait for a message
  4. react appropriately to a message and return to 3.
void *_event_loop(void *arg)
{
static msg_t _msg_q[Q_SZ];
(void)arg;
msg_init_queue(_msg_q, Q_SZ);
while (1) {
msg_receive(&msg);
switch (msg.type) {
case TYPE1:
callback1();
break;
...
}
}
return NULL;
}
void msg_init_queue(msg_t *array, int num)
Initialize the current thread's message queue.
int msg_receive(msg_t *m)
Receive a message.
static kernel_pid_t thread_getpid(void)
Returns the process ID of the currently running thread.
Definition: thread.h:380
#define GNRC_NETREG_DEMUX_CTX_ALL
Demux context value to get all packets of a certain type.
Definition: netreg.h:80
#define GNRC_NETREG_ENTRY_INIT_PID(demux_ctx, pid)
Initializes a netreg entry statically with PID.
Definition: netreg.h:101
int gnrc_netreg_register(gnrc_nettype_t type, gnrc_netreg_entry_t *entry)
Registers a thread to the registry.
@ GNRC_NETTYPE_IPV6
Protocol is IPv6.
Definition: nettype.h:105
Entry to the Network protocol registry.
Definition: netreg.h:167
Describes a message object which can be sent between threads.
Definition: msg.h:185
Note
As an example have a look at the event loops of IPv6 and RPL

Receiving / Transmitting Packets

Packets can be received or transmitted by interacting with the GNRC communication interface.

Receiving Packets

The reception of a Packet from another thread is handled by the GNRC communication interface module. In order to receive a Packet of a specific type, it is necessary to register for the appropriate Protocol type first. Your thread will then be able to receive certain commands defined in the GNRC communication interface module (e.g. net_gnrc_netapi::GNRC_NETAPI_MSG_TYPE_RCV) for all Packets that your thread registered for.

The following example will sketch how to receive incoming and outgoing UDP traffic on port 80.

void *_event_loop(void *arg)
{
static msg_t _msg_q[Q_SZ];
msg_t msg, reply;
reply.content.value = -ENOTSUP;
msg_init_queue(_msg_q, Q_SZ);
gnrc_pktsnip_t *pkt = NULL;
gnrc_netreg_entry me_reg = { .demux_ctx = 80, .pid = thread_getpid() };
while (1) {
msg_receive(&msg);
switch (msg.type) {
pkt = msg.content.ptr;
_handle_incoming_pkt(pkt);
break;
pkt = msg.content.ptr;
_handle_outgoing_pkt(pkt);
break;
msg_reply(&msg, &reply);
break;
default:
break;
}
}
return NULL;
}
int msg_reply(msg_t *m, msg_t *reply)
Replies to a message.
#define ENOTSUP
Not supported (may be the same value as [EOPNOTSUPP]).
Definition: errno.h:130
#define GNRC_NETAPI_MSG_TYPE_RCV
Messaging / IPC type for passing a Packet up the network stack
Definition: netapi.h:74
#define GNRC_NETAPI_MSG_TYPE_ACK
Messaging / IPC type for replying to get and set option messages
Definition: netapi.h:94
#define GNRC_NETAPI_MSG_TYPE_SET
Messaging / IPC type for setting options of network modules
Definition: netapi.h:84
#define GNRC_NETAPI_MSG_TYPE_GET
Messaging / IPC type for getting options from network modules
Definition: netapi.h:89
#define GNRC_NETAPI_MSG_TYPE_SND
Messaging / IPC type for passing a Packet down the network stack
Definition: netapi.h:79
@ GNRC_NETTYPE_UDP
Protocol is UDP.
Definition: nettype.h:125
uint32_t demux_ctx
The demultiplexing context for the registering thread.
Definition: netreg.h:182
Type to represent parts (either headers or payload) of a packet, called snips.
Definition: pkt.h:108
uint16_t type
Type field.
Definition: msg.h:188
union msg_t::@2 content
Content of the message.
void * ptr
Pointer content field.
Definition: msg.h:190
uint32_t value
Value content field.
Definition: msg.h:191
Note
When receiving a message of type GNRC_NETAPI_MSG_TYPE_SET or GNRC_NETAPI_MSG_TYPE_GET, it is necessary to acknowledge it by calling msg_reply() with a message of type GNRC_NETAPI_MSG_TYPE_ACK which contains the actual size of the GET message's content on success or an error code otherwise.
Do not forget to unregister with gnrc_netreg_unregister() if you leave the function context

Transmitting Packets

A packet is transmitted by relaying it to threads interested in handling (and dispatching) packets of its type. To do this, the GNRC communication interface offers dispatch helper functions called gnrc_netapi_dispatch_send() and gnrc_netapi_dispatch_receive().

The following example sketches the usage and assumes a valid Packet named pkt.

pkt = gnrc_pktbuf_add(NULL, data, size, GNRC_NETTYPE_UNDEF);
if (pkt == NULL) {
puts("Error: unable to copy data to packet buffer\n");
return;
}
puts("Error: no thread is interested");
return;
}
static int gnrc_netapi_dispatch_send(gnrc_nettype_t type, uint32_t demux_ctx, gnrc_pktsnip_t *pkt)
Sends a GNRC_NETAPI_MSG_TYPE_SND command to all subscribers to (type, demux_ctx).
Definition: netapi.h:181
@ GNRC_NETTYPE_UNDEF
Protocol is undefined.
Definition: nettype.h:61
gnrc_pktsnip_t * gnrc_pktbuf_add(gnrc_pktsnip_t *next, const void *data, size_t size, gnrc_nettype_t type)
Adds a new gnrc_pktsnip_t and its packet to the packet buffer.
static void gnrc_pktbuf_release(gnrc_pktsnip_t *pkt)
Decreases gnrc_pktsnip_t::users of pkt atomically and removes it if it reaches 0 and reports GNRC_NET...
Definition: pktbuf.h:179

First, the data to be sent is added to the packet buffer. This ensures its intactness during the sending process. After the data to be sent has been added to the packet buffer, its parent data structure can safely be freed or re-used.

Then, the pkt will be sent to all threads that registered for GNRC_NETTYPE_UDP and the demux context 80. Every registered thread will receive a GNRC_NETAPI_MSG_TYPE_SND command and can access the Packet. Note that at this point, the threads receiving pkt act as its owners, so please don't modify pkt after calling any dispatch function.

If gnrc_netapi_dispatch_send() is replaced by gnrc_netapi_dispatch_receive() then threads will receive the GNRC_NETAPI_MSG_TYPE_RCV command instead, again with access to the Packet.

Note
If the data to be sent requires extra headers to be added for successful transmission (in the example, this would be IP and UDP headers), these have to be built manually before calling gnrc_netapi_dispatch_send(). In the interest of conciseness, this is omitted in this tutorial; please refer to gnrc_udp_hdr_build(), gnrc_ipv6_hdr_build() etc. for more information.
GNRC is implemented according to the respective standards. So please note, that sending to a IPv6 link-local address always requires you by definition to also provide the interface you want to send to, otherwise address resolution might fail.

How To Use

Generic (GNRC) network stack is highly modular and can be adjusted to include only the desired features. In the following several of the available modules will be stated that you can include in your application's Makefile.

Modules

 6LoWPAN
 GNRC's 6LoWPAN implementation.
 
 Common MAC module
 A MAC module for providing common MAC parameters and helper functions.
 
 Dump Network Packets
 Dump network packets to STDOUT for debugging.
 
 Error reporting
 Allows for asynchronous error reporting in the network stack.
 
 GNRC LoRaWAN
 GNRC LoRaWAN stack implementation.
 
 GNRC communication interface
 Generic interface for IPC communication between GNRC modules.
 
 GNRC-specific implementation of the sock API
 Provides an implementation of the Sock API by the Generic (GNRC) network stack.
 
 GoMacH
 A traffic-adaptive multi-channel MAC.
 
 Helpers for synchronizing with transmission.
 This allows upper layers to wait for a transmission to complete (or fail) and for passing up data about the transmission.
 
 IPv6
 GNRC's IPv6 implementation.
 
 LWMAC
 A Lightweight duty-cycling 802.15.4 MAC protocol.
 
 Network interface API
 Abstraction layer for GNRC's network interfaces.
 
 Network protocol registry
 Registry to receive messages of a specified protocol type by GNRC.
 
 Packet buffer
 A global network packet buffer.
 
 Packet queue
 gnrc_pktsnip_t queue
 
 Priority packet queue for GNRC
 Wrapper for priority_queue that holds gnrc_pktsnip_t*.
 
 Protocol type
 Protocol type definitions and helper functions.
 
 RPL
 RPL implementation for GNRC.
 
 TCP
 RIOT's TCP implementation for the GNRC network stack.
 
 UDP
 GNRC's implementation of the UDP protocol.
 

Files

file  gnrc.h
 Includes all essential GNRC network stack base modules.