499 lines
18 KiB
ReStructuredText
499 lines
18 KiB
ReStructuredText
.. SPDX-License-Identifier: GPL-2.0-only
|
|
.. Copyright Red Hat
|
|
|
|
==============================================
|
|
BPF_MAP_TYPE_SOCKMAP and BPF_MAP_TYPE_SOCKHASH
|
|
==============================================
|
|
|
|
.. note::
|
|
- ``BPF_MAP_TYPE_SOCKMAP`` was introduced in kernel version 4.14
|
|
- ``BPF_MAP_TYPE_SOCKHASH`` was introduced in kernel version 4.18
|
|
|
|
``BPF_MAP_TYPE_SOCKMAP`` and ``BPF_MAP_TYPE_SOCKHASH`` maps can be used to
|
|
redirect skbs between sockets or to apply policy at the socket level based on
|
|
the result of a BPF (verdict) program with the help of the BPF helpers
|
|
``bpf_sk_redirect_map()``, ``bpf_sk_redirect_hash()``,
|
|
``bpf_msg_redirect_map()`` and ``bpf_msg_redirect_hash()``.
|
|
|
|
``BPF_MAP_TYPE_SOCKMAP`` is backed by an array that uses an integer key as the
|
|
index to look up a reference to a ``struct sock``. The map values are socket
|
|
descriptors. Similarly, ``BPF_MAP_TYPE_SOCKHASH`` is a hash backed BPF map that
|
|
holds references to sockets via their socket descriptors.
|
|
|
|
.. note::
|
|
The value type is either __u32 or __u64; the latter (__u64) is to support
|
|
returning socket cookies to userspace. Returning the ``struct sock *`` that
|
|
the map holds to user-space is neither safe nor useful.
|
|
|
|
These maps may have BPF programs attached to them, specifically a parser program
|
|
and a verdict program. The parser program determines how much data has been
|
|
parsed and therefore how much data needs to be queued to come to a verdict. The
|
|
verdict program is essentially the redirect program and can return a verdict
|
|
of ``__SK_DROP``, ``__SK_PASS``, or ``__SK_REDIRECT``.
|
|
|
|
When a socket is inserted into one of these maps, its socket callbacks are
|
|
replaced and a ``struct sk_psock`` is attached to it. Additionally, this
|
|
``sk_psock`` inherits the programs that are attached to the map.
|
|
|
|
A sock object may be in multiple maps, but can only inherit a single
|
|
parse or verdict program. If adding a sock object to a map would result
|
|
in having multiple parser programs the update will return an EBUSY error.
|
|
|
|
The supported programs to attach to these maps are:
|
|
|
|
.. code-block:: c
|
|
|
|
struct sk_psock_progs {
|
|
struct bpf_prog *msg_parser;
|
|
struct bpf_prog *stream_parser;
|
|
struct bpf_prog *stream_verdict;
|
|
struct bpf_prog *skb_verdict;
|
|
};
|
|
|
|
.. note::
|
|
Users are not allowed to attach ``stream_verdict`` and ``skb_verdict``
|
|
programs to the same map.
|
|
|
|
The attach types for the map programs are:
|
|
|
|
- ``msg_parser`` program - ``BPF_SK_MSG_VERDICT``.
|
|
- ``stream_parser`` program - ``BPF_SK_SKB_STREAM_PARSER``.
|
|
- ``stream_verdict`` program - ``BPF_SK_SKB_STREAM_VERDICT``.
|
|
- ``skb_verdict`` program - ``BPF_SK_SKB_VERDICT``.
|
|
|
|
There are additional helpers available to use with the parser and verdict
|
|
programs: ``bpf_msg_apply_bytes()`` and ``bpf_msg_cork_bytes()``. With
|
|
``bpf_msg_apply_bytes()`` BPF programs can tell the infrastructure how many
|
|
bytes the given verdict should apply to. The helper ``bpf_msg_cork_bytes()``
|
|
handles a different case where a BPF program cannot reach a verdict on a msg
|
|
until it receives more bytes AND the program doesn't want to forward the packet
|
|
until it is known to be good.
|
|
|
|
Finally, the helpers ``bpf_msg_pull_data()`` and ``bpf_msg_push_data()`` are
|
|
available to ``BPF_PROG_TYPE_SK_MSG`` BPF programs to pull in data and set the
|
|
start and end pointers to given values or to add metadata to the ``struct
|
|
sk_msg_buff *msg``.
|
|
|
|
All these helpers will be described in more detail below.
|
|
|
|
Usage
|
|
=====
|
|
Kernel BPF
|
|
----------
|
|
bpf_msg_redirect_map()
|
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
.. code-block:: c
|
|
|
|
long bpf_msg_redirect_map(struct sk_msg_buff *msg, struct bpf_map *map, u32 key, u64 flags)
|
|
|
|
This helper is used in programs implementing policies at the socket level. If
|
|
the message ``msg`` is allowed to pass (i.e., if the verdict BPF program
|
|
returns ``SK_PASS``), redirect it to the socket referenced by ``map`` (of type
|
|
``BPF_MAP_TYPE_SOCKMAP``) at index ``key``. Both ingress and egress interfaces
|
|
can be used for redirection. The ``BPF_F_INGRESS`` value in ``flags`` is used
|
|
to select the ingress path otherwise the egress path is selected. This is the
|
|
only flag supported for now.
|
|
|
|
Returns ``SK_PASS`` on success, or ``SK_DROP`` on error.
|
|
|
|
bpf_sk_redirect_map()
|
|
^^^^^^^^^^^^^^^^^^^^^
|
|
.. code-block:: c
|
|
|
|
long bpf_sk_redirect_map(struct sk_buff *skb, struct bpf_map *map, u32 key u64 flags)
|
|
|
|
Redirect the packet to the socket referenced by ``map`` (of type
|
|
``BPF_MAP_TYPE_SOCKMAP``) at index ``key``. Both ingress and egress interfaces
|
|
can be used for redirection. The ``BPF_F_INGRESS`` value in ``flags`` is used
|
|
to select the ingress path otherwise the egress path is selected. This is the
|
|
only flag supported for now.
|
|
|
|
Returns ``SK_PASS`` on success, or ``SK_DROP`` on error.
|
|
|
|
bpf_map_lookup_elem()
|
|
^^^^^^^^^^^^^^^^^^^^^
|
|
.. code-block:: c
|
|
|
|
void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)
|
|
|
|
socket entries of type ``struct sock *`` can be retrieved using the
|
|
``bpf_map_lookup_elem()`` helper.
|
|
|
|
bpf_sock_map_update()
|
|
^^^^^^^^^^^^^^^^^^^^^
|
|
.. code-block:: c
|
|
|
|
long bpf_sock_map_update(struct bpf_sock_ops *skops, struct bpf_map *map, void *key, u64 flags)
|
|
|
|
Add an entry to, or update a ``map`` referencing sockets. The ``skops`` is used
|
|
as a new value for the entry associated to ``key``. The ``flags`` argument can
|
|
be one of the following:
|
|
|
|
- ``BPF_ANY``: Create a new element or update an existing element.
|
|
- ``BPF_NOEXIST``: Create a new element only if it did not exist.
|
|
- ``BPF_EXIST``: Update an existing element.
|
|
|
|
If the ``map`` has BPF programs (parser and verdict), those will be inherited
|
|
by the socket being added. If the socket is already attached to BPF programs,
|
|
this results in an error.
|
|
|
|
Returns 0 on success, or a negative error in case of failure.
|
|
|
|
bpf_sock_hash_update()
|
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
.. code-block:: c
|
|
|
|
long bpf_sock_hash_update(struct bpf_sock_ops *skops, struct bpf_map *map, void *key, u64 flags)
|
|
|
|
Add an entry to, or update a sockhash ``map`` referencing sockets. The ``skops``
|
|
is used as a new value for the entry associated to ``key``.
|
|
|
|
The ``flags`` argument can be one of the following:
|
|
|
|
- ``BPF_ANY``: Create a new element or update an existing element.
|
|
- ``BPF_NOEXIST``: Create a new element only if it did not exist.
|
|
- ``BPF_EXIST``: Update an existing element.
|
|
|
|
If the ``map`` has BPF programs (parser and verdict), those will be inherited
|
|
by the socket being added. If the socket is already attached to BPF programs,
|
|
this results in an error.
|
|
|
|
Returns 0 on success, or a negative error in case of failure.
|
|
|
|
bpf_msg_redirect_hash()
|
|
^^^^^^^^^^^^^^^^^^^^^^^
|
|
.. code-block:: c
|
|
|
|
long bpf_msg_redirect_hash(struct sk_msg_buff *msg, struct bpf_map *map, void *key, u64 flags)
|
|
|
|
This helper is used in programs implementing policies at the socket level. If
|
|
the message ``msg`` is allowed to pass (i.e., if the verdict BPF program returns
|
|
``SK_PASS``), redirect it to the socket referenced by ``map`` (of type
|
|
``BPF_MAP_TYPE_SOCKHASH``) using hash ``key``. Both ingress and egress
|
|
interfaces can be used for redirection. The ``BPF_F_INGRESS`` value in
|
|
``flags`` is used to select the ingress path otherwise the egress path is
|
|
selected. This is the only flag supported for now.
|
|
|
|
Returns ``SK_PASS`` on success, or ``SK_DROP`` on error.
|
|
|
|
bpf_sk_redirect_hash()
|
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
.. code-block:: c
|
|
|
|
long bpf_sk_redirect_hash(struct sk_buff *skb, struct bpf_map *map, void *key, u64 flags)
|
|
|
|
This helper is used in programs implementing policies at the skb socket level.
|
|
If the sk_buff ``skb`` is allowed to pass (i.e., if the verdict BPF program
|
|
returns ``SK_PASS``), redirect it to the socket referenced by ``map`` (of type
|
|
``BPF_MAP_TYPE_SOCKHASH``) using hash ``key``. Both ingress and egress
|
|
interfaces can be used for redirection. The ``BPF_F_INGRESS`` value in
|
|
``flags`` is used to select the ingress path otherwise the egress path is
|
|
selected. This is the only flag supported for now.
|
|
|
|
Returns ``SK_PASS`` on success, or ``SK_DROP`` on error.
|
|
|
|
bpf_msg_apply_bytes()
|
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
.. code-block:: c
|
|
|
|
long bpf_msg_apply_bytes(struct sk_msg_buff *msg, u32 bytes)
|
|
|
|
For socket policies, apply the verdict of the BPF program to the next (number
|
|
of ``bytes``) of message ``msg``. For example, this helper can be used in the
|
|
following cases:
|
|
|
|
- A single ``sendmsg()`` or ``sendfile()`` system call contains multiple
|
|
logical messages that the BPF program is supposed to read and for which it
|
|
should apply a verdict.
|
|
- A BPF program only cares to read the first ``bytes`` of a ``msg``. If the
|
|
message has a large payload, then setting up and calling the BPF program
|
|
repeatedly for all bytes, even though the verdict is already known, would
|
|
create unnecessary overhead.
|
|
|
|
Returns 0
|
|
|
|
bpf_msg_cork_bytes()
|
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
.. code-block:: c
|
|
|
|
long bpf_msg_cork_bytes(struct sk_msg_buff *msg, u32 bytes)
|
|
|
|
For socket policies, prevent the execution of the verdict BPF program for
|
|
message ``msg`` until the number of ``bytes`` have been accumulated.
|
|
|
|
This can be used when one needs a specific number of bytes before a verdict can
|
|
be assigned, even if the data spans multiple ``sendmsg()`` or ``sendfile()``
|
|
calls.
|
|
|
|
Returns 0
|
|
|
|
bpf_msg_pull_data()
|
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
.. code-block:: c
|
|
|
|
long bpf_msg_pull_data(struct sk_msg_buff *msg, u32 start, u32 end, u64 flags)
|
|
|
|
For socket policies, pull in non-linear data from user space for ``msg`` and set
|
|
pointers ``msg->data`` and ``msg->data_end`` to ``start`` and ``end`` bytes
|
|
offsets into ``msg``, respectively.
|
|
|
|
If a program of type ``BPF_PROG_TYPE_SK_MSG`` is run on a ``msg`` it can only
|
|
parse data that the (``data``, ``data_end``) pointers have already consumed.
|
|
For ``sendmsg()`` hooks this is likely the first scatterlist element. But for
|
|
calls relying on MSG_SPLICE_PAGES (e.g., ``sendfile()``) this will be the
|
|
range (**0**, **0**) because the data is shared with user space and by default
|
|
the objective is to avoid allowing user space to modify data while (or after)
|
|
BPF verdict is being decided. This helper can be used to pull in data and to
|
|
set the start and end pointers to given values. Data will be copied if
|
|
necessary (i.e., if data was not linear and if start and end pointers do not
|
|
point to the same chunk).
|
|
|
|
A call to this helper is susceptible to change the underlying packet buffer.
|
|
Therefore, at load time, all checks on pointers previously done by the verifier
|
|
are invalidated and must be performed again, if the helper is used in
|
|
combination with direct packet access.
|
|
|
|
All values for ``flags`` are reserved for future usage, and must be left at
|
|
zero.
|
|
|
|
Returns 0 on success, or a negative error in case of failure.
|
|
|
|
bpf_map_lookup_elem()
|
|
^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
.. code-block:: c
|
|
|
|
void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)
|
|
|
|
Look up a socket entry in the sockmap or sockhash map.
|
|
|
|
Returns the socket entry associated to ``key``, or NULL if no entry was found.
|
|
|
|
bpf_map_update_elem()
|
|
^^^^^^^^^^^^^^^^^^^^^
|
|
.. code-block:: c
|
|
|
|
long bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags)
|
|
|
|
Add or update a socket entry in a sockmap or sockhash.
|
|
|
|
The flags argument can be one of the following:
|
|
|
|
- BPF_ANY: Create a new element or update an existing element.
|
|
- BPF_NOEXIST: Create a new element only if it did not exist.
|
|
- BPF_EXIST: Update an existing element.
|
|
|
|
Returns 0 on success, or a negative error in case of failure.
|
|
|
|
bpf_map_delete_elem()
|
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
.. code-block:: c
|
|
|
|
long bpf_map_delete_elem(struct bpf_map *map, const void *key)
|
|
|
|
Delete a socket entry from a sockmap or a sockhash.
|
|
|
|
Returns 0 on success, or a negative error in case of failure.
|
|
|
|
User space
|
|
----------
|
|
bpf_map_update_elem()
|
|
^^^^^^^^^^^^^^^^^^^^^
|
|
.. code-block:: c
|
|
|
|
int bpf_map_update_elem(int fd, const void *key, const void *value, __u64 flags)
|
|
|
|
Sockmap entries can be added or updated using the ``bpf_map_update_elem()``
|
|
function. The ``key`` parameter is the index value of the sockmap array. And the
|
|
``value`` parameter is the FD value of that socket.
|
|
|
|
Under the hood, the sockmap update function uses the socket FD value to
|
|
retrieve the associated socket and its attached psock.
|
|
|
|
The flags argument can be one of the following:
|
|
|
|
- BPF_ANY: Create a new element or update an existing element.
|
|
- BPF_NOEXIST: Create a new element only if it did not exist.
|
|
- BPF_EXIST: Update an existing element.
|
|
|
|
bpf_map_lookup_elem()
|
|
^^^^^^^^^^^^^^^^^^^^^
|
|
.. code-block:: c
|
|
|
|
int bpf_map_lookup_elem(int fd, const void *key, void *value)
|
|
|
|
Sockmap entries can be retrieved using the ``bpf_map_lookup_elem()`` function.
|
|
|
|
.. note::
|
|
The entry returned is a socket cookie rather than a socket itself.
|
|
|
|
bpf_map_delete_elem()
|
|
^^^^^^^^^^^^^^^^^^^^^
|
|
.. code-block:: c
|
|
|
|
int bpf_map_delete_elem(int fd, const void *key)
|
|
|
|
Sockmap entries can be deleted using the ``bpf_map_delete_elem()``
|
|
function.
|
|
|
|
Returns 0 on success, or negative error in case of failure.
|
|
|
|
Examples
|
|
========
|
|
|
|
Kernel BPF
|
|
----------
|
|
Several examples of the use of sockmap APIs can be found in:
|
|
|
|
- `tools/testing/selftests/bpf/progs/test_sockmap_kern.h`_
|
|
- `tools/testing/selftests/bpf/progs/sockmap_parse_prog.c`_
|
|
- `tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c`_
|
|
- `tools/testing/selftests/bpf/progs/test_sockmap_listen.c`_
|
|
- `tools/testing/selftests/bpf/progs/test_sockmap_update.c`_
|
|
|
|
The following code snippet shows how to declare a sockmap.
|
|
|
|
.. code-block:: c
|
|
|
|
struct {
|
|
__uint(type, BPF_MAP_TYPE_SOCKMAP);
|
|
__uint(max_entries, 1);
|
|
__type(key, __u32);
|
|
__type(value, __u64);
|
|
} sock_map_rx SEC(".maps");
|
|
|
|
The following code snippet shows a sample parser program.
|
|
|
|
.. code-block:: c
|
|
|
|
SEC("sk_skb/stream_parser")
|
|
int bpf_prog_parser(struct __sk_buff *skb)
|
|
{
|
|
return skb->len;
|
|
}
|
|
|
|
The following code snippet shows a simple verdict program that interacts with a
|
|
sockmap to redirect traffic to another socket based on the local port.
|
|
|
|
.. code-block:: c
|
|
|
|
SEC("sk_skb/stream_verdict")
|
|
int bpf_prog_verdict(struct __sk_buff *skb)
|
|
{
|
|
__u32 lport = skb->local_port;
|
|
__u32 idx = 0;
|
|
|
|
if (lport == 10000)
|
|
return bpf_sk_redirect_map(skb, &sock_map_rx, idx, 0);
|
|
|
|
return SK_PASS;
|
|
}
|
|
|
|
The following code snippet shows how to declare a sockhash map.
|
|
|
|
.. code-block:: c
|
|
|
|
struct socket_key {
|
|
__u32 src_ip;
|
|
__u32 dst_ip;
|
|
__u32 src_port;
|
|
__u32 dst_port;
|
|
};
|
|
|
|
struct {
|
|
__uint(type, BPF_MAP_TYPE_SOCKHASH);
|
|
__uint(max_entries, 1);
|
|
__type(key, struct socket_key);
|
|
__type(value, __u64);
|
|
} sock_hash_rx SEC(".maps");
|
|
|
|
The following code snippet shows a simple verdict program that interacts with a
|
|
sockhash to redirect traffic to another socket based on a hash of some of the
|
|
skb parameters.
|
|
|
|
.. code-block:: c
|
|
|
|
static inline
|
|
void extract_socket_key(struct __sk_buff *skb, struct socket_key *key)
|
|
{
|
|
key->src_ip = skb->remote_ip4;
|
|
key->dst_ip = skb->local_ip4;
|
|
key->src_port = skb->remote_port >> 16;
|
|
key->dst_port = (bpf_htonl(skb->local_port)) >> 16;
|
|
}
|
|
|
|
SEC("sk_skb/stream_verdict")
|
|
int bpf_prog_verdict(struct __sk_buff *skb)
|
|
{
|
|
struct socket_key key;
|
|
|
|
extract_socket_key(skb, &key);
|
|
|
|
return bpf_sk_redirect_hash(skb, &sock_hash_rx, &key, 0);
|
|
}
|
|
|
|
User space
|
|
----------
|
|
Several examples of the use of sockmap APIs can be found in:
|
|
|
|
- `tools/testing/selftests/bpf/prog_tests/sockmap_basic.c`_
|
|
- `tools/testing/selftests/bpf/test_sockmap.c`_
|
|
- `tools/testing/selftests/bpf/test_maps.c`_
|
|
|
|
The following code sample shows how to create a sockmap, attach a parser and
|
|
verdict program, as well as add a socket entry.
|
|
|
|
.. code-block:: c
|
|
|
|
int create_sample_sockmap(int sock, int parse_prog_fd, int verdict_prog_fd)
|
|
{
|
|
int index = 0;
|
|
int map, err;
|
|
|
|
map = bpf_map_create(BPF_MAP_TYPE_SOCKMAP, NULL, sizeof(int), sizeof(int), 1, NULL);
|
|
if (map < 0) {
|
|
fprintf(stderr, "Failed to create sockmap: %s\n", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
err = bpf_prog_attach(parse_prog_fd, map, BPF_SK_SKB_STREAM_PARSER, 0);
|
|
if (err){
|
|
fprintf(stderr, "Failed to attach_parser_prog_to_map: %s\n", strerror(errno));
|
|
goto out;
|
|
}
|
|
|
|
err = bpf_prog_attach(verdict_prog_fd, map, BPF_SK_SKB_STREAM_VERDICT, 0);
|
|
if (err){
|
|
fprintf(stderr, "Failed to attach_verdict_prog_to_map: %s\n", strerror(errno));
|
|
goto out;
|
|
}
|
|
|
|
err = bpf_map_update_elem(map, &index, &sock, BPF_NOEXIST);
|
|
if (err) {
|
|
fprintf(stderr, "Failed to update sockmap: %s\n", strerror(errno));
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
close(map);
|
|
return err;
|
|
}
|
|
|
|
References
|
|
===========
|
|
|
|
- https://github.com/jrfastab/linux-kernel-xdp/commit/c89fd73cb9d2d7f3c716c3e00836f07b1aeb261f
|
|
- https://lwn.net/Articles/731133/
|
|
- http://vger.kernel.org/lpc_net2018_talks/ktls_bpf_paper.pdf
|
|
- https://lwn.net/Articles/748628/
|
|
- https://lore.kernel.org/bpf/20200218171023.844439-7-jakub@cloudflare.com/
|
|
|
|
.. _`tools/testing/selftests/bpf/progs/test_sockmap_kern.h`: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/testing/selftests/bpf/progs/test_sockmap_kern.h
|
|
.. _`tools/testing/selftests/bpf/progs/sockmap_parse_prog.c`: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/testing/selftests/bpf/progs/sockmap_parse_prog.c
|
|
.. _`tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c`: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c
|
|
.. _`tools/testing/selftests/bpf/prog_tests/sockmap_basic.c`: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
|
|
.. _`tools/testing/selftests/bpf/test_sockmap.c`: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/testing/selftests/bpf/test_sockmap.c
|
|
.. _`tools/testing/selftests/bpf/test_maps.c`: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/testing/selftests/bpf/test_maps.c
|
|
.. _`tools/testing/selftests/bpf/progs/test_sockmap_listen.c`: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/testing/selftests/bpf/progs/test_sockmap_listen.c
|
|
.. _`tools/testing/selftests/bpf/progs/test_sockmap_update.c`: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/testing/selftests/bpf/progs/test_sockmap_update.c
|