Raw IPv4/IPv6 sock API¶
Sock submodule for raw IPv4/IPv6.
How To Use¶
First you need to a module that implements this API in your application’s Makefile. For example the implementation for GNRC is called gnrc_sock_ip
.
A Simple IPv6 Server¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #include <stdio.h>
#include "net/protnum.h"
#include "net/sock/ip.h"
uint8_t buf[128];
int main(void)
{
sock_ip_ep_t local = SOCK_IPV6_EP_ANY;
sock_ip_t sock;
if (sock_ip_create(&sock, &local, NULL, PROTNUM_IPV6_NONXT, 0) < 0) {
puts("Error creating raw IP sock");
return 1;
}
while (1) {
sock_ip_ep_t remote;
ssize_t res;
if ((res = sock_ip_recv(&sock, buf, sizeof(buf), SOCK_NO_TIMEOUT,
&remote)) >= 0) {
puts("Received a message");
if (sock_ip_send(&sock, buf, res, 0, &remote) < 0) {
puts("Error sending reply");
}
}
}
return 0;
}
|
Above you see a simple IPv6 server. Don’t forget to also the IPv6 module of your networking implementation (e.g. gnrc_ipv6_default
for GNRC) and at least one network device.
After including header files for the address families, protocol numbers and the raw `sock`s themselves, we create some buffer space buf
to store the data received by the server:
1 2 3 4 5 | #include "net/af.h"
#include "net/protnum.h"
#include "net/sock/ip.h"
uint8_t buf[128];
|
To be able to listen for incoming packets we bind the sock
by setting a local end point (even if we just state here, that we just want to bind it to any IPv6 address).
We then proceed to create the sock
. It is bound to local
and listens for IPv6 packets with ipv6/hdr.h::ipv6_hdr_t::nh
protnum.h::PROTNUM_IPV6_NONXT
. Since we don’t need any further configuration we set the flags to 0. In case of an error we stop the program:
1 2 3 4 5 6 7 | sock_ip_ep_t local = SOCK_IPV6_EP_ANY;
sock_ip_t sock;
if (sock_ip_create(&sock, &local, NULL, PROTNUM_IPV6_NONXT, 0) < 0) {
puts("Error creating raw IP sock");
return 1;
}
|
The application then waits indefinitely for an incoming message in buf
from remote
. If we want to timeout this wait period we could alternatively set the timeout
parameter of ip.h::sock_ip_recv()
to a value != SOCK_NO_TIMEOUT
. If an error occurs on receive we just ignore it and continue looping.
If we receive a message we use its remote
to reply. Note since the proto
was already set during ip.h::sock_ip_create()
we can just leave proto
for the ip.h::sock_ip_send()
set to 0 (it is ignored by that function in that case anyway). In case of an error on send we print an according message:
1 2 3 4 5 6 7 8 9 10 11 12 | while (1) {
sock_ip_ep_t remote;
ssize_t res;
if ((res = sock_ip_recv(&sock, buf, sizeof(buf), SOCK_NO_TIMEOUT,
&remote)) >= 0) {
puts("Received a message");
if (sock_ip_send(&sock, buf, res, 0, &remote) < 0) {
puts("Error sending reply");
}
}
}
|
A Simple IPv6 Client¶
There are two kinds of clients. Those that do expect a reply and those who don’t. A client that does not require a reply is very simple to implement in one line:
1 | res = sock_ip_send(NULL, data, data_len, PROTNUM, &remote);
|
With data
being the data sent, data_len
the length of data
, PROTNUM
the next header number for the sent packet and remote
the remote end point the packet that is to be sent.
To see some other capabilities we look at a more complex example in form of the counter of the echo server above:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | #include <stdio.h>
#include "net/af.h"
#include "net/protnum.h"
#include "net/ipv6/addr.h"
#include "net/sock/ip.h"
#include "xtimer.h"
uint8_t buf[7];
int main(void)
{
sock_ip_ep_t local = SOCK_IPV6_EP_ANY;
sock_ip_t sock;
if (sock_ip_create(&sock, &local, NULL, PROTNUM_IPV6_NONXT, 0) < 0) {
puts("Error creating raw IP sock");
return 1;
}
while (1) {
sock_ip_ep_t remote = { .family = AF_INET6 };
ssize_t res;
ipv6_addr_set_all_nodes_multicast((ipv6_addr_t *)&remote.addr.ipv6,
IPV6_ADDR_MCAST_SCP_LINK_LOCAL);
if (sock_ip_send(&sock, "Hello!", sizeof("Hello!"), 0, &remote) < 0) {
puts("Error sending message");
sock_ip_close(&sock);
return 1;
}
if ((res = sock_ip_recv(&sock, buf, sizeof(buf), 1 * US_PER_SEC,
NULL)) < 0) {
if (res == -ETIMEDOUT) {
puts("Timed out");
}
else {
puts("Error receiving message");
}
}
else {
printf("Received message: \"");
for (int i = 0; i < res; i++) {
printf("%c", buf[i]);
}
printf("\"\n");
}
xtimer_sleep(1);
}
return 0;
}
|
Again: Don’t forget to also the IPv6 module of your networking implementation (e.g. gnrc_ipv6_default
for GNRC) and at least one network device.
We first create again a sock
with a local end point bound to any IPv6 address. Note that we also could specify the remote end point here and not use it with ip.h::sock_ip_send()
.
1 2 3 4 5 6 7 | sock_ip_ep_t local = SOCK_IPV6_EP_ANY;
sock_ip_t sock;
if (sock_ip_create(&sock, &local, NULL, PROTNUM_IPV6_NONXT, 0) < 0) {
puts("Error creating raw IP sock");
return 1;
}
|
We then create a remote end point for the link-local all nodes multicast address (ff02::1
) and send a “Hello!” message to that end point.
1 2 3 4 5 6 7 8 9 10 11 | sock_ip_ep_t remote = { .family = AF_INET6 };
ssize_t res;
ipv6_addr_set_all_nodes_multicast((ipv6_addr_t *)&remote.addr.ipv6,
IPV6_ADDR_MCAST_SCP_LINK_LOCAL);
if (sock_ip_send(&sock, "Hello!", sizeof("Hello!"), 0, &remote) < 0) {
puts("Error sending message");
sock_ip_close(&sock);
return 1;
}
|
We then wait a second for a reply and print it when it is received.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | if ((res = sock_ip_recv(&sock, buf, sizeof(buf), 1 * US_PER_SEC,
NULL)) < 0) {
if (res == -ETIMEDOUT) {
puts("Timed out");
}
else {
puts("Error receiving message");
}
}
else {
printf("Received message: \"");
for (int i = 0; i < res; i++) {
printf("%c", buf[i]);
}
printf("\"\n");
}
|
Finally, we wait a second before sending out the next “Hello!” with xtimer_sleep(1)
.
A Simple IPv6 Server¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #include <stdio.h>
#include "net/protnum.h"
#include "net/sock/ip.h"
uint8_t buf[128];
int main(void)
{
sock_ip_ep_t local = SOCK_IPV6_EP_ANY;
sock_ip_t sock;
if (sock_ip_create(&sock, &local, NULL, PROTNUM_IPV6_NONXT, 0) < 0) {
puts("Error creating raw IP sock");
return 1;
}
while (1) {
sock_ip_ep_t remote;
ssize_t res;
if ((res = sock_ip_recv(&sock, buf, sizeof(buf), SOCK_NO_TIMEOUT,
&remote)) >= 0) {
puts("Received a message");
if (sock_ip_send(&sock, buf, res, 0, &remote) < 0) {
puts("Error sending reply");
}
}
}
return 0;
}
|
Above you see a simple IPv6 server. Don’t forget to also the IPv6 module of your networking implementation (e.g. gnrc_ipv6_default
for GNRC) and at least one network device.
After including header files for the address families, protocol numbers and the raw `sock`s themselves, we create some buffer space buf
to store the data received by the server:
1 2 3 4 5 | #include "net/af.h"
#include "net/protnum.h"
#include "net/sock/ip.h"
uint8_t buf[128];
|
To be able to listen for incoming packets we bind the sock
by setting a local end point (even if we just state here, that we just want to bind it to any IPv6 address).
We then proceed to create the sock
. It is bound to local
and listens for IPv6 packets with ipv6/hdr.h::ipv6_hdr_t::nh
protnum.h::PROTNUM_IPV6_NONXT
. Since we don’t need any further configuration we set the flags to 0. In case of an error we stop the program:
1 2 3 4 5 6 7 | sock_ip_ep_t local = SOCK_IPV6_EP_ANY;
sock_ip_t sock;
if (sock_ip_create(&sock, &local, NULL, PROTNUM_IPV6_NONXT, 0) < 0) {
puts("Error creating raw IP sock");
return 1;
}
|
The application then waits indefinitely for an incoming message in buf
from remote
. If we want to timeout this wait period we could alternatively set the timeout
parameter of ip.h::sock_ip_recv()
to a value != SOCK_NO_TIMEOUT
. If an error occurs on receive we just ignore it and continue looping.
If we receive a message we use its remote
to reply. Note since the proto
was already set during ip.h::sock_ip_create()
we can just leave proto
for the ip.h::sock_ip_send()
set to 0 (it is ignored by that function in that case anyway). In case of an error on send we print an according message:
1 2 3 4 5 6 7 8 9 10 11 12 | while (1) {
sock_ip_ep_t remote;
ssize_t res;
if ((res = sock_ip_recv(&sock, buf, sizeof(buf), SOCK_NO_TIMEOUT,
&remote)) >= 0) {
puts("Received a message");
if (sock_ip_send(&sock, buf, res, 0, &remote) < 0) {
puts("Error sending reply");
}
}
}
|
A Simple IPv6 Client¶
There are two kinds of clients. Those that do expect a reply and those who don’t. A client that does not require a reply is very simple to implement in one line:
1 | res = sock_ip_send(NULL, data, data_len, PROTNUM, &remote);
|
With data
being the data sent, data_len
the length of data
, PROTNUM
the next header number for the sent packet and remote
the remote end point the packet that is to be sent.
To see some other capabilities we look at a more complex example in form of the counter of the echo server above:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | #include <stdio.h>
#include "net/af.h"
#include "net/protnum.h"
#include "net/ipv6/addr.h"
#include "net/sock/ip.h"
#include "xtimer.h"
uint8_t buf[7];
int main(void)
{
sock_ip_ep_t local = SOCK_IPV6_EP_ANY;
sock_ip_t sock;
if (sock_ip_create(&sock, &local, NULL, PROTNUM_IPV6_NONXT, 0) < 0) {
puts("Error creating raw IP sock");
return 1;
}
while (1) {
sock_ip_ep_t remote = { .family = AF_INET6 };
ssize_t res;
ipv6_addr_set_all_nodes_multicast((ipv6_addr_t *)&remote.addr.ipv6,
IPV6_ADDR_MCAST_SCP_LINK_LOCAL);
if (sock_ip_send(&sock, "Hello!", sizeof("Hello!"), 0, &remote) < 0) {
puts("Error sending message");
sock_ip_close(&sock);
return 1;
}
if ((res = sock_ip_recv(&sock, buf, sizeof(buf), 1 * US_PER_SEC,
NULL)) < 0) {
if (res == -ETIMEDOUT) {
puts("Timed out");
}
else {
puts("Error receiving message");
}
}
else {
printf("Received message: \"");
for (int i = 0; i < res; i++) {
printf("%c", buf[i]);
}
printf("\"\n");
}
xtimer_sleep(1);
}
return 0;
}
|
Again: Don’t forget to also the IPv6 module of your networking implementation (e.g. gnrc_ipv6_default
for GNRC) and at least one network device.
We first create again a sock
with a local end point bound to any IPv6 address. Note that we also could specify the remote end point here and not use it with ip.h::sock_ip_send()
.
1 2 3 4 5 6 7 | sock_ip_ep_t local = SOCK_IPV6_EP_ANY;
sock_ip_t sock;
if (sock_ip_create(&sock, &local, NULL, PROTNUM_IPV6_NONXT, 0) < 0) {
puts("Error creating raw IP sock");
return 1;
}
|
We then create a remote end point for the link-local all nodes multicast address (ff02::1
) and send a “Hello!” message to that end point.
1 2 3 4 5 6 7 8 9 10 11 | sock_ip_ep_t remote = { .family = AF_INET6 };
ssize_t res;
ipv6_addr_set_all_nodes_multicast((ipv6_addr_t *)&remote.addr.ipv6,
IPV6_ADDR_MCAST_SCP_LINK_LOCAL);
if (sock_ip_send(&sock, "Hello!", sizeof("Hello!"), 0, &remote) < 0) {
puts("Error sending message");
sock_ip_close(&sock);
return 1;
}
|
We then wait a second for a reply and print it when it is received.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | if ((res = sock_ip_recv(&sock, buf, sizeof(buf), 1 * US_PER_SEC,
NULL)) < 0) {
if (res == -ETIMEDOUT) {
puts("Timed out");
}
else {
puts("Error receiving message");
}
}
else {
printf("Received message: \"");
for (int i = 0; i < res; i++) {
printf("%c", buf[i]);
}
printf("\"\n");
}
|
Finally, we wait a second before sending out the next “Hello!” with xtimer_sleep(1)
.
-
struct sock_ip
sock_ip_t
¶ Type for a raw IPv4/IPv6 sock object.
Note
API implementors:
struct sock_ip
needs to be defined by implementation-specificsock_types.h
.
-
int
sock_ip_create
(ip.h::sock_ip_t
* sock, const sock_ip_ep_t * local, const sock_ip_ep_t * remote, uint8_t proto, uint16_t flags)¶ Creates a new raw IPv4/IPv6 sock object.
Parameters
sock: The resulting sock object. local: Local end point for the sock object. May be NULL. sock.h::sock_ip_ep_t::netif
must either besock.h::SOCK_ADDR_ANY_NETIF
or equal tosock.h::sock_ip_ep_t::netif
ofremote
ifremote != NULL
. If NULLip.h::sock_ip_send()
may bind implicitly.remote: Remote end point for the sock object. May be NULL
but then theremote
parameter ofip.h::sock_ip_send()
may not beNULL
or it will always error with return value -ENOTCONN. sock_ip_ep_t::port may not be 0 ifremote != NULL
.sock.h::sock_ip_ep_t::netif
must either besock.h::SOCK_ADDR_ANY_NETIF
or equal tosock.h::sock_ip_ep_t::netif
oflocal
iflocal != NULL
.proto: Protocol to use in the raw IPv4/IPv6 sock object (the protocol
header field in IPv4 and thenext_header
field in IPv6).flags: Flags for the sock object. See also [sock flags](net_sock_flags). May be 0. Return values
- 0 on success.
- -EADDRINUSE, if
local != NULL
andlocal
is already used elsewhere - -EAFNOSUPPORT, if
local != NULL
orremote != NULL
andsock.h::sock_ip_ep_t::family
oflocal
orremote
is not supported. - -EINVAL, if
sock.h::sock_ip_ep_t::addr
ofremote
is an invalid address. - -EINVAL, if
sock.h::sock_ip_ep_t::netif
oflocal
orremote
are not valid interfaces or contradict each other (i.e. `(local->netif != remote->netif) && ((local->netif != SOCK_ADDR_ANY_NETIF) || (remote->netif != SOCK_ADDR_ANY_NETIF))if neither is
NULL). @return -ENOMEM, if not enough resources can be provided for
sock` to be created. - -EPROTONOSUPPORT, if
local != NULL
orremote != NULL
and proto is not supported bysock.h::sock_ip_ep_t::family
oflocal
orremote
.
-
void
sock_ip_close
(ip.h::sock_ip_t
* sock)¶ Closes a raw IPv4/IPv6 sock object.
Parameters
sock: A raw IPv4/IPv6 sock object.
-
int
sock_ip_get_local
(ip.h::sock_ip_t
* sock, sock_ip_ep_t * ep)¶ Gets the local end point of a raw IPv4/IPv6 sock object.
This gets the local end point of a raw IPv4/IPv6 sock object. Note that this might not be the same end point you added in
ip.h::sock_ip_create()
, but an end point more suitable for the implementation. Examples for this might be that ifsock.h::sock_ip_ep_t::netif
is given inip.h::sock_ip_create()
, the implementation might choose to return the address on this interface thesock
is bound to inep's
sock.h::sock_ip_ep_t::addr
.Parameters
sock: A raw IPv4/IPv6 sock object. ep: The local end point. Return values
- 0 on success.
- -EADDRNOTAVAIL, when
sock
has no end point bound to it.
-
int
sock_ip_get_remote
(ip.h::sock_ip_t
* sock, sock_ip_ep_t * ep)¶ Gets the remote end point of a UDP sock object.
This gets the remote end point of a raw IPv4/IPv6 sock object. Note that this might not be the same end point you added in
ip.h::sock_ip_create()
, but an end point more suitable for the implementation. Examples for this might be that ifsock.h::sock_ip_ep_t::netif
is given inip.h::sock_ip_create()
, the implementation might choose to return the address on this interface thesock
is bound to inep's
sock.h::sock_ip_ep_t::addr
.Parameters
sock: A UDP sock object. ep: The remote end point. Return values
- 0 on success.
- -ENOTCONN, when
sock
has no remote end point bound to it.
-
msp430_types.h::ssize_t
sock_ip_recv
(ip.h::sock_ip_t
* sock, void * data,msp430_types.h::size_t
max_len, uint32_t timeout, sock_ip_ep_t * remote)¶ Receives a message over IPv4/IPv6 from remote end point.
Parameters
sock: A raw IPv4/IPv6 sock object. data: Pointer where the received data should be stored. max_len: Maximum space available at data
.timeout: Timeout for receive in microseconds. If 0 and no data is available, the function returns immediately. May be sock.h::SOCK_NO_TIMEOUT
for no timeout (wait until data is available).remote: Remote end point of the received data. May be NULL, if it is not required by the application. Note
Function blocks if no packet is currently waiting.
Return values
- The number of bytes received on success.
- 0, if no received data is available, but everything is in order.
- -EADDRNOTAVAIL, if local of
sock
is not given. - -EAGAIN, if
timeout
is0
and no data is available. - -EINVAL, if
remote
is invalid orsock
is not properly initialized (or closed whileip.h::sock_ip_recv()
blocks). - -ENOBUFS, if buffer space is not large enough to store received data.
- -ENOMEM, if no memory was available to receive
data
. - -EPROTO, if source address of received packet did not equal the remote of
sock
. - -ETIMEDOUT, if
timeout
expired.
-
msp430_types.h::ssize_t
sock_ip_send
(ip.h::sock_ip_t
* sock, const void * data,msp430_types.h::size_t
len, uint8_t proto, const sock_ip_ep_t * remote)¶ Sends a message over IPv4/IPv6 to remote end point.
Parameters
sock: A raw IPv4/IPv6 sock object. May be NULL. A sensible local end point should be selected by the implementation in that case. data: Pointer where the received data should be stored. May be NULL
iflen == 0
.len: Maximum space available at data
.proto: Protocol to use in the packet sent, in case sock == NULL
. Ifsock != NULL
this parameter will be ignored.remote: Remote end point for the sent data. May be NULL
, ifsock
has a remote end point.sock.h::sock_ip_ep_t::family
may be AF_UNSPEC, if local end point ofsock
provides this information.Return values
- The number of bytes sent on success.
- -EAFNOSUPPORT, if
remote != NULL
andsock.h::sock_ip_ep_t::family
ofremote
is != AF_UNSPEC and not supported. - -EINVAL, if
sock.h::sock_ip_ep_t::addr
ofremote
is an invalid address. - -EINVAL, if
sock.h::sock_ip_ep_t::netif
ofremote
is not a valid interface or contradicts the local interface ofsock
. - -EHOSTUNREACH, if
remote
or remote end point ofsock
is not reachable. - -ENOMEM, if no memory was available to send
data
. - -ENOTCONN, if
remote == NULL
, butsock
has no remote end point. - -EPROTOTYPE, if
sock == NULL
andproto
is not bysock.h::sock_ip_ep_t::family
ofremote
.