Chapter 158. Sequential API

Table of Contents

158.1. Overview
158.2. Comparison with BSD sockets
158.2.1. BSD API Restrictions
158.3. Netbufs
158.4. TCP/IP thread
158.5. Usage
158.5.1. API declarations
158.5.2. Types
158.6. API reference
netbuf_new() — Allocate a netbuf structure
netbuf_delete() — Deallocate a netbuf structure
netbuf_alloc() — Allocate space in a netbuf
netbuf_free() — Deallocate buffer memory associated with a netbuf
netbuf_ref() — Associate a data pointer with a netbuf
netbuf_len() — Obtain the total length of a netbuf
netbuf_data() — Obtain a pointer to netbuf data
netbuf_next() — Traverse internal fragments in a netbuf
netbuf_first() — Reset fragment pointer to start of netbuf
netbuf_copy() — Copy all netbuf data to memory pointer
netbuf_copy_partial() — Copy some netbuf data to memory pointer
netbuf_chain() — Chain two netbufs together
netbuf_fromaddr() — Obtain the sender's IPv4 address for a netbuf
netbuf_fromaddr_ip6() — Obtain the sender's IPv6 address for a netbuf
netbuf_fromport() — Obtain the sender's port number for a netbuf
netconn_new() — Create a new connection structure
netconn_new_with_callback() — Create a new connection structure with a callback
netconn_new_with_proto_and_callback() — Create a new connection structure with a callback for a specific protocol
netconn_delete() — Deallocate a netconn
netconn_type() — Obtain the type of netconn
netconn_peer() — Obtain the remote host IP address/port of a netconn
netconn_addr() — Obtain the local host IPv4 address/port of a netconn
netconn_bind() — Set local IP address/port of a netconn
netconn_bind_ip6() — Set local IPv6 address/port of a netconn
netconn_connect() — Connect netconn to remote peer
netconn_connect_ip6() — Connect netconn to remote peer
netconn_disconnect() — Disconnect UDP connection
netconn_listen() — Make a listening TCP netconn
netconn_accept() — Wait for incoming connections
netconn_recv() — Wait for data
netconn_recv_tcp_pbuf() — Wait for data
netconn_recved() — Update receive window
netconn_write() — Send data on TCP connection
netconn_send() — Send data on UDP connection
netconn_close() — Close a connection
netconn_shutdown() — Shutdown a connection
netconn_set_noautorecved() — Set the connection no-auto-recved state
netconn_get_noautorecved() — Get the connection no-auto-recved state
netconn_err() — Obtain connection error status

158.1. Overview

As described earlier, the lwIP sequential API provides a straightforward and easy-to-use method of interfacing to the stack. Unlike the raw API, which requires event-driven callbacks, an application can simply call the API functions as needed to perform stack operations such as sending data, receiving data, or manipulating packet buffers or connections. While the raw API may allow for more efficient operation, the sequential API typically allows for simpler application design.

158.2. Comparison with BSD sockets

In design, it is not unlike the BSD sockets API. Some of the terminology differs however: in the sequential API, the term connection is used for any communication link between network peers, and the handle for a connection is termed a netconn. A netconn can be considered analogous to a socket, albeit specific to networking - BSD sockets traditionally represent both network connections and files.

The main reason for superiority over the socket API occurs with buffer management. The BSD socket API was designed to manage the fact that the user and the operating system kernel operate in different address spaces and data must always be copied regardless. This results in not only decreased performance, but also increased footprint as buffers must be allocated to hold the copied data.

158.2.1. BSD API Restrictions

By default the eCos lwIP configurations enable the option CYGFUN_LWIP_COMPAT_SOCKETS which means normally you cannot include both the lwIP headers lwip/sockets.h or network.h, and the CYGPKG_IO_FILEIO package (i.e. POSIX-alike) headers from the same file.

158.3. Netbufs

Instead of the BSD approach of generic buffers, the sequential API uses netbufs, which are based on pbufs. This allows users to manage buffers directly, including even allowing data to come from ROM. Since pbufs, and hence netbufs, can be chained, this also allows the application and lwIP to avoid the need for large regions of entirely contiguous memory in order to hold data. Instead data can be constructed in chunks, and chained together.

When the application wishes to send data, it can send a netbuf directly with UDP. TCP is different as it is intrinsically a buffering, streaming protocol, which requires data to be kept aside to allow for retransmissions. As a result data is sent using just a pointer to memory and a length. However since TCP data can also reside in ROM, it is possible to indicate that the data does not need copying, and so will persist even if the stack needs to queue the data. This can lead to huge savings of memory. For example, static web page content can reside in ROM, and never need to be copied to RAM.

For both TCP and UDP, incoming data is passed to the application as netbufs. The application can use API functions to extract the data from the netbufs - care must be taken as the received data may in fact be a chain. A convenience function exists to copy out the entirety of data across the whole chain into a single contiguous region of memory. Otherwise the application can process data in each netbuf in the chain in turn. The functions netbuf_first() and netbuf_next() can be used to iterate throught the chain.

158.4. TCP/IP thread

When interacting with the network stack using the sequential API, all operations are not handled by the calling thread, but instead are passed to the lwIP network processing (TCP/IP) thread. Inter-thread communication is used inside lwIP to ensure that at the point the API function returns, operation is either complete, or for asynchronous operations, under way.

For example, to register a timeout callback the tcpip_timeout() function can be used from client threads to cross the thread boundary into the sequential TCP/IP thread. However, since the actual timeout callback handler registered will be executed within the sequential TCP/IP thread context, it can subsequently directly call the lwIP internal sys_timeout() if it needs to re-schedule its callback.

158.5. Usage

158.5.1. API declarations

Declarations for all sequential API types and functions may be obtained by including the lwip/api.h header file:

#include lwip/api.h

158.5.2. Types

Objects of type struct netconn and struct netbuf are intended to be used as opaque types and the structure contents are intended to be maintained and viewed only by lwIP itself. User applications accessing internal members do so at their own risk, and future API compatibility is not guaranteed, nor is thread synchronization since lwIP is entitled to change structure contents at any time.

158.5.2.1. IP address representation

Depending on the lwIP configuration some API functions take an IP address, which can either be an IPv4 or an IPv6 address.

The IPv4 type struct ip_addr may be accessed as if it has the following structure:

struct ip_addr {
  u32_t addr;
};

The IPv6 type struct ip6_addr may be accessed as if it has the following structure:

struct ip6_addr {
  u32_t addr[4];
};
[Caution]Caution

API users must use the declarations of these structures from the header file lwip/ip_addr.h which is included implicitly by lwip/api.h. These types must not be declared by the application itself.

To make it easier to work with either IPv4 or IPv6 addresses the type ipX_addr_t is provided. This is a union of the IPv4 and IPv6 address structures, and may be accessed as if it has the following structure:

typedef union {
  ip_addr_t ip4;
  ip6_addr_t ip6;
} ipX_addr_t;

See Section 158.5.2.1.3, “ipX Helpers” for an overview of the IP version neutral address support. As with the caveat regarding the declarations of the specific IPv4 and IPv6 address structures, the ipX declarations should be accessed via including the lwip/ip_addr.h header file.

158.5.2.1.1. IPv4 Addresses

For convenience, predefined struct ip_addr instances are provided for the special cases of "any" IP address (0.0.0.0), and the global broadcast address (255.255.255.255). These instances can be accessed with the macro defines IP_ADDR_ANY and IP_ADDR_BROADCAST which return values of type struct ip_addr *.

The addr field is a 32-bit integral value representing the IP address in network byte order (not host byte order).

A variety of convenience function-like macros exist for manipulation or evaluation of IP addresses:

IP_ADDR_ANY
This macro evaluates to an expression of type struct ip_addr * identifying an IP address structure which can be used to represent the special "any" IP address 0.0.0.0.
IP_ADDR_BROADCAST
This macro evaluates to an expression of type struct ip_addr * identifying an IP address structure which can be used to represent the special global IP address 255.255.255.255.
IN_CLASSA(a)
An expression which evaluates to non-zero if a (of type u32_t and in host byte order) is a class A internet address.
IN_CLASSB(a)
An expression which evaluates to non-zero if a (of type u32_t and in host byte order) is a class B internet address.
IN_CLASSC(a)
An expression which evaluates to non-zero if a (of type u32_t and in host byte order) is a class C internet address.
IN_CLASSD(a)
An expression which evaluates to non-zero if a (of type u32_t and in host byte order) is a class D internet address.
IP4_ADDR(ipaddr, a, b, c, d)

Sets ipaddr (of type struct ip_addr *) to the internet address a.b.c.d. For example:

struct ip_addr host;
    …
    IP4_ADDR(host, 192, 168, 1, 1);
ip_addr_cmp(addr1, addr2)
Returns non-zero if the arguments addr1 and addr2, both of type struct ip_addr * are identical. Zero if they differ.
ip_addr_netcmp(addr1, addr2, mask)
Returns non-zero if the arguments addr1 and addr2, both of type struct ip_addr * are on the same network, as indicated by the network mask mask which is itself also of type struct ip_addr *. Zero if they are on different networks.
htons(s)
Portably converts s of type u16_t from host byte order to a u16_t in network byte order.
ntohs(s)
Portably converts s of type u16_t from network byte order to a u16_t in host byte order.
htonl(l)
Portably converts l of type u32_t from host byte order to a u32_t in network byte order.
ntohl(l)
Portably converts l of type u32_t from network byte order to a u32_t in host byte order.

Some further potentially useful macro definitions can be viewed in lwip/ip_addr.h.

158.5.2.1.2. IPv6 Addresses

The header file lwip/ip6_addr.h (which is included by default from lwip/api.h) contains definitions for many IPv6 address convenience function-like macros, as well as utility function prototypes.

The following is not an exhaustive list, so the reader is recommended to inspect the header file to get a complete overview of the IPv6 address support macros and functions.

IP6_ADDR_ANY
This macro evaluates to an expression of type struct ip6_addr * identifying an IP address structure which can be used to represent the special "any" IPv6 address ::/128. It actually just returns the address of the exported ip6_addr_any variable.
ip6_addr_copy(dest, src)
This implements a fast (no NULL check) address copy.
ip6_addr_set(dest, src)
Set the dest address from the supplied src. If src is NULL the destination is written with zeroes.
ip6_addr_set_zero(ip6addr)
Sets the ip6addr address to all zeroes.
ip6_addr_set_any(ip6addr)
This explicitly sets the IP6_ADDR_ANY address value.
ip6_addr_set_loopback(ip6addr)
This sets the destination ip6addr parameters to the ::1 loopback address.
ip6_addr_set_hton(dest, src)
Copy the src address to the dest address converting from host to network byte order.
ip6_addr_netcmp(addr1, addr2)
An expression which evaluates to non-zero if the supplied addr1 and addr2 parameters are on the same network, by comparing the most-significant 64-bits of the addresses.
ip6_addr_cmp(addr1, addr2)
An expression which evaluates to non-zero if there is an exact match between the supplied addr1 and addr2 parameters.
ip6_get_subnet_id(ip6addr)
This returns in host byte order the 16-bit subnet identifier.
ip6_addr_isany(ip6addr)
An expression which evaluates to non-zero if ip6addr matches the IP6_ADDR_ANY address (all zeroes).
ip6_addr_isglobal(ip6addr)
An expression which evaluates to non-zero if the supplied ip6addr is a valid global address.
ip6_addr_islinklocal(ip6addr)
An expression which evaluates to non-zero if the ip6addr parameter is a valid link-local address.
ip6_addr_issitelocal(ip6addr)
An expression which evaluates to non-zero if the ip6addr parameter is a valid site-local address.
ip6_addr_isuniquelocal(ip6addr)
An expression which evaluates to non-zero if the ip6addr parameter is a valid unique link-local address.
ip6_addr_ismulticast(ip6addr)

An expression which evaluates to non-zero if the supplied ip6addr is a valid multicast address.

There are various other function-like macros provided to further decode whether the multicast address is a loopback, link-local, admin-local, global, etc. address. The definitions for these variants can be found by inspecting the lwip/ip6_addr.h header file.

ip6_addr_isallnodes_iflocal(ip6addr)
An expression which evaluates to non-zero if the supplied ip6addr matches the IPv6 ff01::1 loopback "all nodes" address.
ip6_addr_isallnodes_linklocal(ip6addr)
An expression which evaluates to non-zero if the supplied ip6addr matches the IPv6 link-local "all nodes" address.
ip6_addr_isallrouters_linklocal(ip6addr)
An expression which evaluates to non-zero if the supplied ip6addr matches the IPv6 link-local "all routers" address.
ip6_addr_set_allnodes_linklocal(ip6addr)
Sets the given ip6addr to the ff02::1 link-local "all nodes" multicast address.
ip6_addr_set_allrouters_linklocal(ip6addr)
Sets the given ip6addr to the ff02::2 link-local "all routers" multicast address.
ip6_addr_isinvalid(addr_state)
An expression which evaluates to non-zero if the supplied addr_state parameter is IP6_ADDR_INVALID.
ip6_addr_isvalid(addr_state)
An expression which evaluates to non-zero if the supplied addr_state parameter denotes a valid address state, where the IP6_ADDR_VALID bitmask is set.
ip6_addr_istentative(addr_state)
An expression which evaluates to non-zero if the supplied addr_state parameter has the IP6_ADDR_TENTATIVE bitmask set.
ip6_addr_ispreferred(addr_state)
An expression which evaluates to non-zero if the supplied addr_state parameter is IP6_ADDR_PREFERRED.
ip6_addr_isdeprecated(addr_state)
An expression which evaluates to non-zero if the supplied addr_state parameter is IP6_ADDR_DEPRECATED.
int ip6addr_aton(const char *cp, ip6_addr_t *addr)
Checks whether cp is a valid ASCII representation of an IPv6 address, and if valid converts it to the binary IPv6 address destination addr. The function returns 1 on successful conversion, or 0 on failure.
char *ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen)

Converts the binary IPv6 address addr into an ASCII representation written into the supplied buf buffer of buflen bytes.

Returns the buf parameter on success if the buffer has been updated to hold the ASCII address representation, or NULL if the buffer was too small.

158.5.2.1.3. ipX Helpers

Instead of directly referencing the IPv4 or IPv6 versions of the utility routines, applications should ideally use the common ipX_* variants. These functions, and function-like macros, take as their first parameter a boolean is_ipv6 value denoting when zero that the referenced addresses are IPv4 structures, or when non-zero that the referenced addresses are IPv6 structures. Note: If IPv6 support is not enabled for lwIP then the ipX_* implementations default to only using IPv4 addresses.

ipX_addr_copy(is_ipv6, dest, src)
This implements a fast (no NULL check) address copy.
ipX_addr_set(is_ipv6, dest, src)
Set the dest address from the supplied src. If src is NULL the destination is written with zeroes.
ipX_addr_set_ipaddr(is_ipv6, dest, src)
Sets the dest address parameter from the specified src. If src is NULL the destination is written with zeroes.
ipX_addr_set_zero(is_ipv6, ipaddr)

Sets the address to all zeroes. This is normally the ANY address.

ipX_addr_set_any(is_ipv6, ipaddr)
This explicitly sets the ANY address.
ipX_addr_set_loopback(is_ipv6, ipaddr)
This sets the destination ipaddr to the respective loopback interface address. For IPv4 this is 127.0.0.1 and ::1 for IPv6.
ipX_addr_set_hton(is_ipv6, dest, src)
Copy the src address to the dest address converting from host to network byte order.
ipX_addr_cmp(is_ipv6, addr1, addr2)
An expression which evaluates to non-zero if there is an exact match between the supplied addr1 and addr2 parameters.
ipX_addr_isany(is_ipv6, ipaddr)
An expression which evaluates to non-zero if ipaddr is NULL or points at the ANY address value.
ipX_addr_ismulticast(is_ipv6, ipaddr)
An expression which evaluates to non-zero if ipaddr references a valid multicast address value.
ipX_addr_debug_print(is_ipv6, debug, ipaddr)
This debugging helper macro will output the raw IPv4 or IPv6 address via the printf-alike debug support calls if the relevant lwIP debugging option specified by the debug parameter is enabled.

158.5.2.2. Error codes

While the BSD sockets API uses POSIX standard error codes (ENOMEM, EINVAL, etc.) the lwIP sequential API has its own separate set of error code definitions.

These error definitions are used by any API function that returns a value of type err_t. The following table indicates possible error code values and their meaning:

Table 158.1. lwIP sequential API error codes

CodeMeaning
ERR_OK No error, operation successful.
ERR_MEM Out of memory error.
ERR_BUF Buffer error.
ERR_ABRT Connection aborted.
ERR_RST Connection reset.
ERR_CLSD Connection closed.
ERR_CONN Not connected.
ERR_VAL Illegal value.
ERR_ARG Illegal argument.
ERR_RTE Routing problem.
ERR_USE Address in use.
ERR_IF Low-level network interface error.
ERR_ISCONN Already connected.
ERR_TIMEOUT Timeout.
ERR_INPROGRESS Operation in progress.
ERR_WOULDBLOCK Operation would block.

158.6. API reference