You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
338 lines
16 KiB
338 lines
16 KiB
.. _bsd_sockets_interface: |
|
|
|
BSD Sockets |
|
########### |
|
|
|
.. contents:: |
|
:local: |
|
:depth: 2 |
|
|
|
Overview |
|
******** |
|
|
|
Zephyr offers an implementation of a subset of the BSD Sockets API (a part |
|
of the POSIX standard). This API allows to reuse existing programming experience |
|
and port existing simple networking applications to Zephyr. |
|
|
|
Here are the key requirements and concepts which governed BSD Sockets |
|
compatible API implementation for Zephyr: |
|
|
|
* Has minimal overhead, similar to the requirement for other |
|
Zephyr subsystems. |
|
* Is namespaced by default, to avoid name conflicts with well-known |
|
names like ``close()``, which may be part of libc or other POSIX |
|
compatibility libraries. |
|
If enabled by :kconfig:option:`CONFIG_POSIX_API`, it will also |
|
expose POSIX compatible APIs. |
|
|
|
BSD Sockets compatible API is enabled using :kconfig:option:`CONFIG_NET_SOCKETS` |
|
config option and implements the following operations: ``socket()``, ``close()``, |
|
``recv()``, ``recvfrom()``, ``send()``, ``sendto()``, ``connect()``, ``bind()``, |
|
``listen()``, ``accept()``, ``fcntl()`` (to set non-blocking mode), |
|
``getsockopt()``, ``setsockopt()``, ``poll()``, ``select()``, |
|
``getaddrinfo()``, ``getnameinfo()``. |
|
|
|
Based on the namespacing requirements above, these operations are by |
|
default exposed as functions with ``zsock_`` prefix, e.g. |
|
:c:func:`zsock_socket` and :c:func:`zsock_close`. If the config option |
|
:kconfig:option:`CONFIG_POSIX_API` is defined, all the functions |
|
will be also exposed as aliases without the prefix. This includes the |
|
functions like ``close()`` and ``fcntl()`` (which may conflict with |
|
functions in libc or other libraries, for example, with the filesystem |
|
libraries). |
|
|
|
Another entailment of the design requirements above is that the Zephyr |
|
API aggressively employs the short-read/short-write property of the POSIX API |
|
whenever possible (to minimize complexity and overheads). POSIX allows |
|
for calls like ``recv()`` and ``send()`` to actually process (receive |
|
or send) less data than requested by the user (on ``SOCK_STREAM`` type |
|
sockets). For example, a call ``recv(sock, 1000, 0)`` may return 100, |
|
meaning that only 100 bytes were read (short read), and the application |
|
needs to retry call(s) to receive the remaining 900 bytes. |
|
|
|
The BSD Sockets API uses file descriptors to represent sockets. File |
|
descriptors are small integers, consecutively assigned from zero, shared |
|
among sockets, files, special devices (like stdin/stdout), etc. Internally, |
|
there is a table mapping file descriptors to internal object pointers. |
|
The file descriptor table is used by the BSD Sockets API even if the rest |
|
of the POSIX subsystem (filesystem, stdin/stdout) is not enabled. |
|
|
|
Zephyr supports multiple types of BSD sockets, the following table summarizes |
|
what socket types are available: |
|
|
|
+--------------+-------------+------------------+---------------------------------------------------------------------------+ |
|
| Family | Type | Protocol | Description | |
|
+==============+=============+==================+===========================================================================+ |
|
| AF_INET |br| | SOCK_DGRAM | IPPROTO_UDP | Enabled if :kconfig:option:`CONFIG_NET_UDP` is set. |br| | |
|
| AF_INET6 | | | Allows to send and receive UDP datagrams. | |
|
| | +------------------+---------------------------------------------------------------------------+ |
|
| | | IPPROTO_DTLS_1_x | Enabled if :kconfig:option:`CONFIG_NET_SOCKETS_ENABLE_DTLS` is set. |br| | |
|
| | | | Allows to send and receive DTLS datagrams. | |
|
| +-------------+------------------+---------------------------------------------------------------------------+ |
|
| | SOCK_STREAM | IPPROTO_TCP | Enabled if :kconfig:option:`CONFIG_NET_TCP` is set. |br| | |
|
| | | | Allows to send and receive TCP data stream. | |
|
| | +------------------+---------------------------------------------------------------------------+ |
|
| | | IPPROTO_TLS_1_x | Enabled if :kconfig:option:`CONFIG_NET_SOCKETS_SOCKOPT_TLS` is set. |br| | |
|
| | | | Allows to send and receive TLS data stream. | |
|
| +-------------+------------------+---------------------------------------------------------------------------+ |
|
| | SOCK_RAW | IPPROTO_IP |br| | Enabled if :kconfig:option:`CONFIG_NET_SOCKETS_INET_RAW` is set. |br| | |
|
| | | <proto> | Allows to send and receive IPv4/IPv6 datagrams. |br| | |
|
| | | | Packets are filtered by L4 protocol specified. | |
|
| | | | IPPROTO_IP is a wildcard protocol to receive all IP datagrams. | |
|
+--------------+-------------+------------------+---------------------------------------------------------------------------+ |
|
| AF_PACKET | SOCK_DGRAM | ETH_P_ALL |br| | Enabled if :kconfig:option:`CONFIG_NET_SOCKETS_PACKET_DGRAM` is set. |br| | |
|
| | | <proto> | Allows to send and receive packets without L2 header. |br| | |
|
| | | | Packets are filtered by L3 protocol specified. | |
|
| | | | ETH_P_ALL is a wildcard protocol to receive all packets. | |
|
| +-------------+------------------+---------------------------------------------------------------------------+ |
|
| | SOCK_RAW | ETH_P_ALL | Enabled if :kconfig:option:`CONFIG_NET_SOCKETS_PACKET` is set. |br| | |
|
| | | | Allows to send and receive packets with L2 header included. | |
|
+--------------+-------------+------------------+---------------------------------------------------------------------------+ |
|
| AF_CAN | SOCK_RAW | CAN_RAW | Enabled if :kconfig:option:`CONFIG_NET_SOCKETS_CAN` is set. |br| | |
|
| | | | Allows to send and receive CAN packets. | |
|
+--------------+-------------+------------------+---------------------------------------------------------------------------+ |
|
|
|
See :zephyr:code-sample:`sockets-echo-server` and :zephyr:code-sample:`sockets-echo-client` |
|
sample applications to learn how to create a simple server or client BSD socket based |
|
application. |
|
|
|
.. _secure_sockets_interface: |
|
|
|
Secure Sockets |
|
************** |
|
|
|
Zephyr provides an extension of standard POSIX socket API, allowing to create |
|
and configure sockets with TLS protocol types, facilitating secure |
|
communication. Secure functions for the implementation are provided by |
|
mbedTLS library. Secure sockets implementation allows use of both TLS and DTLS |
|
protocols with standard socket calls. See :c:enum:`net_ip_protocol_secure` type |
|
for supported secure protocol versions. |
|
|
|
To enable secure sockets, set the :kconfig:option:`CONFIG_NET_SOCKETS_SOCKOPT_TLS` |
|
option. To enable DTLS support, use :kconfig:option:`CONFIG_NET_SOCKETS_ENABLE_DTLS` |
|
option. |
|
|
|
.. _sockets_tls_credentials_subsys: |
|
|
|
TLS credentials subsystem |
|
========================= |
|
|
|
TLS credentials must be registered in the system before they can be used with |
|
secure sockets. See :c:func:`tls_credential_add` for more information. |
|
|
|
When a specific TLS credential is registered in the system, it is assigned with |
|
numeric value of type :c:type:`sec_tag_t`, called a tag. This value can be used |
|
later on to reference the credential during secure socket configuration with |
|
socket options. |
|
|
|
The following TLS credential types can be registered in the system: |
|
|
|
- ``TLS_CREDENTIAL_CA_CERTIFICATE`` |
|
- ``TLS_CREDENTIAL_PUBLIC_CERTIFICATE`` |
|
- ``TLS_CREDENTIAL_PRIVATE_KEY`` |
|
- ``TLS_CREDENTIAL_PSK`` |
|
- ``TLS_CREDENTIAL_PSK_ID`` |
|
|
|
An example registration of CA certificate (provided in ``ca_certificate`` |
|
array) looks like this: |
|
|
|
.. code-block:: c |
|
|
|
ret = tls_credential_add(CA_CERTIFICATE_TAG, TLS_CREDENTIAL_CA_CERTIFICATE, |
|
ca_certificate, sizeof(ca_certificate)); |
|
|
|
By default certificates in DER format are supported. PEM support can be enabled |
|
in mbedTLS settings. |
|
|
|
Secure Socket Creation |
|
====================== |
|
|
|
A secure socket can be created by specifying secure protocol type, for instance: |
|
|
|
.. code-block:: c |
|
|
|
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TLS_1_2); |
|
|
|
Once created, it can be configured with socket options. For instance, the |
|
CA certificate and hostname can be set: |
|
|
|
.. code-block:: c |
|
|
|
sec_tag_t sec_tag_opt[] = { |
|
CA_CERTIFICATE_TAG, |
|
}; |
|
|
|
ret = setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST, |
|
sec_tag_opt, sizeof(sec_tag_opt)); |
|
|
|
.. code-block:: c |
|
|
|
char host[] = "google.com"; |
|
|
|
ret = setsockopt(sock, SOL_TLS, TLS_HOSTNAME, host, sizeof(host)); |
|
|
|
Once configured, socket can be used just like a regular TCP socket. |
|
|
|
Several samples in Zephyr use secure sockets for communication. For a sample use |
|
see e.g. :zephyr:code-sample:`echo-server sample application <sockets-echo-server>` or |
|
:zephyr:code-sample:`HTTP GET sample application <sockets-http-get>`. |
|
|
|
Secure Sockets options |
|
====================== |
|
|
|
Secure sockets offer the following options for socket management: |
|
|
|
.. doxygengroup:: secure_sockets_options |
|
|
|
Socket offloading |
|
***************** |
|
|
|
Zephyr allows to register custom socket implementations (called offloaded |
|
sockets). This allows for seamless integration for devices which provide an |
|
external IP stack and expose socket-like API. |
|
|
|
Socket offloading can be enabled with :kconfig:option:`CONFIG_NET_SOCKETS_OFFLOAD` |
|
option. A network driver that wants to register a new socket implementation |
|
should use :c:macro:`NET_SOCKET_OFFLOAD_REGISTER` macro. The macro accepts the |
|
following parameters: |
|
|
|
* ``socket_name`` |
|
An arbitrary name for the socket implementation. |
|
|
|
* ``prio`` |
|
Socket implementation's priority. The higher the priority, the earlier this |
|
particular implementation will be processed when creating a new socket. |
|
Lower numeric value indicates higher priority. |
|
|
|
* ``_family`` |
|
Socket family implemented by the offloaded socket. ``AF_UNSPEC`` indicates |
|
any family. |
|
|
|
* ``_is_supported`` |
|
A filtering function, used to verify whether a particular socket family, |
|
type and protocol are supported by the offloaded socket implementation. |
|
|
|
* ``_handler`` |
|
A function compatible with :c:func:`socket` API, used to create an |
|
offloaded socket. |
|
|
|
Every offloaded socket implementation should also implement a set of socket |
|
APIs, specified in :c:struct:`socket_op_vtable` struct. |
|
|
|
The function registered for socket creation should allocate a new file |
|
descriptor using :c:func:`zvfs_reserve_fd` function. Any additional actions, |
|
specific to the creation of a particular offloaded socket implementation, |
|
should take place after the file descriptor is allocated. As a final step, |
|
if the offloaded socket was created successfully, the file descriptor should |
|
be finalized with :c:func:`zvfs_finalize_typed_fd`, or :c:func:`zvfs_finalize_fd` |
|
functions. The finalize function allows to register a |
|
:c:struct:`socket_op_vtable` structure implementing socket APIs for an |
|
offloaded socket along with an optional socket context data pointer. |
|
|
|
Finally, when an offloaded network interface is initialized, it should indicate |
|
that the interface is offloaded with :c:func:`net_if_socket_offload_set` |
|
function. The function registers the function used to create an offloaded socket |
|
(the same as the one provided in :c:macro:`NET_SOCKET_OFFLOAD_REGISTER`) at the |
|
network interface. |
|
|
|
Offloaded socket creation |
|
========================= |
|
|
|
When application creates a new socket with :c:func:`socket` function, the |
|
network stack iterates over all registered socket implementations (native and |
|
offloaded). Higher priority socket implementations are processed first. |
|
For each registered socket implementation, an address family is verified, and if |
|
it matches (or the socket was registered as ``AF_UNSPEC``), the corresponding |
|
``_is_supported`` function is called to verify the remaining socket parameters. |
|
The first implementation that fulfills the socket requirements (i. e. |
|
``_is_supported`` returns true) will create a new socket with its ``_handler`` |
|
function. |
|
|
|
The above indicates the importance of the socket priority. If multiple socket |
|
implementations support the same set of socket family/type/protocol, the first |
|
implementation processed by the system will create a socket. Therefore it's |
|
important to give the highest priority to the implementation that should be the |
|
system default. |
|
|
|
The socket priority for native socket implementation is configured with Kconfig. |
|
Use :kconfig:option:`CONFIG_NET_SOCKETS_TLS_PRIORITY` to set the priority for |
|
the native TLS sockets. |
|
Use :kconfig:option:`CONFIG_NET_SOCKETS_PRIORITY_DEFAULT` to set the priority |
|
for the remaining native sockets. |
|
|
|
Dealing with multiple offloaded interfaces |
|
========================================== |
|
|
|
As the :c:func:`socket` function does not allow to specify which network |
|
interface should be used by a socket, it's not possible to choose a specific |
|
implementation in case multiple offloaded socket implementations, supporting the |
|
same type of sockets, are available. The same problem arises when both native |
|
and offloaded sockets are available in the system. |
|
|
|
To address this problem, a special socket implementation (called socket |
|
dispatcher) was introduced. The sole reason for this module is to postpone the |
|
socket creation for until the first operation on a socket is performed. This |
|
leaves an opening to use ``SO_BINDTODEVICE`` socket option, to bind a socket to |
|
a particular network interface (and thus offloaded socket implementation). |
|
The socket dispatcher can be enabled with :kconfig:option:`CONFIG_NET_SOCKETS_OFFLOAD_DISPATCHER` |
|
Kconfig option. |
|
|
|
When enabled, the application can specify the network interface to use with |
|
:c:func:`setsockopt` function: |
|
|
|
.. code-block:: c |
|
|
|
/* A "dispatcher" socket is created */ |
|
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); |
|
|
|
struct ifreq ifreq = { |
|
.ifr_name = "SimpleLink" |
|
}; |
|
|
|
/* The socket is "dispatched" to a particular network interface |
|
* (offloaded or not). |
|
*/ |
|
setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof(ifreq)); |
|
|
|
Similarly, if TLS is supported by both native and offloaded sockets, |
|
``TLS_NATIVE`` socket option can be used to indicate that a native TLS socket |
|
should be created. The underlying socket can then be bound to a particular |
|
network interface: |
|
|
|
.. code-block:: c |
|
|
|
/* A "dispatcher" socket is created */ |
|
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TLS_1_2); |
|
|
|
int tls_native = 1; |
|
|
|
/* The socket is "dispatched" to a native TLS socket implmeentation. |
|
* The underlying socket is a "dispatcher" socket now. |
|
*/ |
|
setsockopt(sock, SOL_TLS, TLS_NATIVE, &tls_native, sizeof(tls_native)); |
|
|
|
struct ifreq ifreq = { |
|
.ifr_name = "SimpleLink" |
|
}; |
|
|
|
/* The underlying socket is "dispatched" to a particular network interface |
|
* (offloaded or not). |
|
*/ |
|
setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof(ifreq)); |
|
|
|
In case no ``SO_BINDTODEVICE`` socket option is used on a socket, the socket |
|
will be dispatched according to the default priority and filtering rules on a |
|
first socket API call. |
|
|
|
API Reference |
|
************* |
|
|
|
BSD Sockets |
|
=========== |
|
|
|
.. doxygengroup:: bsd_sockets |
|
|
|
TLS Credentials |
|
=============== |
|
|
|
.. doxygengroup:: tls_credentials
|
|
|