← Documents Documentation/bpf/map_sk_storage.rst GitHub 원문 ↗

Linux 6.18.37 · BPF

BPF_MAP_TYPE_SK_STORAGE

Socket-local storage의 lifetime과 map 설정, kernel helper, userspace fd 기반 CRUD와 sockops 예제를 설명합니다.

Source pathDocumentation/bpf/map_sk_storage.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약과 해설

map_sk_storage.rst:1-159

`BPF_MAP_TYPE_SK_STORAGE`는 value를 map이 아닌 각 socket에 연결합니다. Kernel은 요청 시 storage를 만들고 socket이나 map이 사라질 때 자동으로 해제합니다.

Map key는 `int`, `max_entries`는 0이어야 하며 `BPF_F_NO_PREALLOC`이 필수입니다. Kernel program에서는 socket pointer를, userspace에서는 socket fd를 key로 사용합니다.

`BPF_LOCAL_STORAGE_GET_F_CREATE`는 lookup과 allocation을 결합합니다. Initial value를 전달하지 않으면 새 storage가 zero-initialize되므로 반환 pointer와 초기화 semantics를 함께 확인해야 합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0-only
2 .. Copyright (C) 2022 Red Hat, Inc.
3
4 =======================
5 BPF_MAP_TYPE_SK_STORAGE
6 =======================
7
8 .. note::
9 - ``BPF_MAP_TYPE_SK_STORAGE`` was introduced in kernel version 5.2
10
11 ``BPF_MAP_TYPE_SK_STORAGE`` is used to provide socket-local storage for BPF
12 programs. A map of type ``BPF_MAP_TYPE_SK_STORAGE`` declares the type of storage
13 to be provided and acts as the handle for accessing the socket-local
14 storage. The values for maps of type ``BPF_MAP_TYPE_SK_STORAGE`` are stored
15 locally with each socket instead of with the map. The kernel is responsible for
16 allocating storage for a socket when requested and for freeing the storage when
17 either the map or the socket is deleted.
18
19 .. note::
20 - The key type must be ``int`` and ``max_entries`` must be set to ``0``.
21 - The ``BPF_F_NO_PREALLOC`` flag must be used when creating a map for
22 socket-local storage.
23
24 Usage
25 =====
26
27 Kernel BPF
28 ----------
29
30 bpf_sk_storage_get()
31 ~~~~~~~~~~~~~~~~~~~~
32
33 .. code-block:: c
34
35 void *bpf_sk_storage_get(struct bpf_map *map, void *sk, void *value, u64 flags)
36
37 Socket-local storage for ``map`` can be retrieved from socket ``sk`` using the
38 ``bpf_sk_storage_get()`` helper. If the ``BPF_LOCAL_STORAGE_GET_F_CREATE``
39 flag is used then ``bpf_sk_storage_get()`` will create the storage for ``sk``
40 if it does not already exist. ``value`` can be used together with
41 ``BPF_LOCAL_STORAGE_GET_F_CREATE`` to initialize the storage value, otherwise
42 it will be zero initialized. Returns a pointer to the storage on success, or
43 ``NULL`` in case of failure.
44
45 .. note::
46 - ``sk`` is a kernel ``struct sock`` pointer for LSM or tracing programs.
47 - ``sk`` is a ``struct bpf_sock`` pointer for other program types.
48
49 bpf_sk_storage_delete()
50 ~~~~~~~~~~~~~~~~~~~~~~~
51
52 .. code-block:: c
53
54 long bpf_sk_storage_delete(struct bpf_map *map, void *sk)
55
56 Socket-local storage for ``map`` can be deleted from socket ``sk`` using the
57 ``bpf_sk_storage_delete()`` helper. Returns ``0`` on success, or negative
58 error in case of failure.
59
60 User space
61 ----------
62
63 bpf_map_update_elem()
64 ~~~~~~~~~~~~~~~~~~~~~
65
66 .. code-block:: c
67
68 int bpf_map_update_elem(int map_fd, const void *key, const void *value, __u64 flags)
69
70 Socket-local storage for map ``map_fd`` can be added or updated locally to a
71 socket using the ``bpf_map_update_elem()`` libbpf function. The socket is
72 identified by a `socket` ``fd`` stored in the pointer ``key``. The pointer
73 ``value`` has the data to be added or updated to the socket ``fd``. The type
74 and size of ``value`` should be the same as the value type of the map
75 definition.
76
77 The ``flags`` parameter can be used to control the update behaviour:
78
79 - ``BPF_ANY`` will create storage for `socket` ``fd`` or update existing storage.
80 - ``BPF_NOEXIST`` will create storage for `socket` ``fd`` only if it did not
81 already exist, otherwise the call will fail with ``-EEXIST``.
82 - ``BPF_EXIST`` will update existing storage for `socket` ``fd`` if it already
83 exists, otherwise the call will fail with ``-ENOENT``.
84
85 Returns ``0`` on success, or negative error in case of failure.
86
87 bpf_map_lookup_elem()
88 ~~~~~~~~~~~~~~~~~~~~~
89
90 .. code-block:: c
91
92 int bpf_map_lookup_elem(int map_fd, const void *key, void *value)
93
94 Socket-local storage for map ``map_fd`` can be retrieved from a socket using
95 the ``bpf_map_lookup_elem()`` libbpf function. The storage is retrieved from
96 the socket identified by a `socket` ``fd`` stored in the pointer
97 ``key``. Returns ``0`` on success, or negative error in case of failure.
98
99 bpf_map_delete_elem()
100 ~~~~~~~~~~~~~~~~~~~~~
101
102 .. code-block:: c
103
104 int bpf_map_delete_elem(int map_fd, const void *key)
105
106 Socket-local storage for map ``map_fd`` can be deleted from a socket using the
107 ``bpf_map_delete_elem()`` libbpf function. The storage is deleted from the
108 socket identified by a `socket` ``fd`` stored in the pointer ``key``. Returns
109 ``0`` on success, or negative error in case of failure.
110
111 Examples
112 ========
113
114 Kernel BPF
115 ----------
116
117 This snippet shows how to declare socket-local storage in a BPF program:
118
119 .. code-block:: c
120
121 struct {
122 __uint(type, BPF_MAP_TYPE_SK_STORAGE);
123 __uint(map_flags, BPF_F_NO_PREALLOC);
124 __type(key, int);
125 __type(value, struct my_storage);
126 } socket_storage SEC(".maps");
127
128 This snippet shows how to retrieve socket-local storage in a BPF program:
129
130 .. code-block:: c
131
132 SEC("sockops")
133 int _sockops(struct bpf_sock_ops *ctx)
134 {
135 struct my_storage *storage;
136 struct bpf_sock *sk;
137
138 sk = ctx->sk;
139 if (!sk)
140 return 1;
141
142 storage = bpf_sk_storage_get(&socket_storage, sk, 0,
143 BPF_LOCAL_STORAGE_GET_F_CREATE);
144 if (!storage)
145 return 1;
146
147 /* Use 'storage' here */
148
149 return 1;
150 }
151
152
153 Please see the ``tools/testing/selftests/bpf`` directory for functional
154 examples.
155
156 References
157 ==========
158
159 https://lwn.net/ml/netdev/20190426171103.61892-1-kafai@fb.com/
160

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

Socket-local storage lifetime과 map 설정

1-23

`BPF_MAP_TYPE_SK_STORAGE` 문서는 `GPL-2.0-only` 라이선스와 `Copyright (C) 2022 Red Hat, Inc.`를 명시합니다.

`BPF_MAP_TYPE_SK_STORAGE`는 `kernel version 5.2`에 도입되었습니다.

`BPF_MAP_TYPE_SK_STORAGE`는 BPF program에 socket-local storage를 제공합니다. 이 type의 map은 제공할 storage type을 선언하고 socket-local storage에 접근하는 handle 역할을 합니다.

Map value는 map 자체가 아니라 각 socket에 local하게 저장됩니다. Kernel은 요청 시 socket storage를 할당하고 map 또는 socket이 삭제될 때 storage를 해제합니다.

Key type은 반드시 `int`, `max_entries`는 반드시 0이어야 합니다. Socket-local storage map을 만들 때는 `BPF_F_NO_PREALLOC` flag를 사용해야 합니다.

Kernel BPF storage get과 delete

24-59

Socket `sk`에서 `map`의 socket-local storage를 가져올 때는 다음 `bpf_sk_storage_get()` helper를 사용합니다.

void *bpf_sk_storage_get(struct bpf_map *map, void *sk, void *value, u64 flags)

`BPF_LOCAL_STORAGE_GET_F_CREATE` flag를 지정하면 storage가 없을 때 새로 만듭니다. 이 flag와 함께 `value`를 전달하면 initial value로 사용하며, value가 없으면 zero-initialize됩니다. 성공하면 storage pointer, 실패하면 `NULL`을 반환합니다.

LSM 또는 tracing program에서 `sk`는 kernel `struct sock` pointer입니다. 그 밖의 program type에서는 `struct bpf_sock` pointer입니다.

Socket `sk`에서 `map`의 local storage를 삭제할 때는 다음 `bpf_sk_storage_delete()` helper를 사용합니다.

long bpf_sk_storage_delete(struct bpf_map *map, void *sk)

Delete는 성공하면 0, 실패하면 negative error를 반환합니다.

Userspace update, lookup, delete

60-110

Userspace에서 socket의 local storage를 추가하거나 갱신할 때는 다음 libbpf `bpf_map_update_elem()` function을 사용합니다.

int bpf_map_update_elem(int map_fd, const void *key, const void *value, __u64 flags)

Socket은 `key` pointer에 저장한 socket `fd`로 식별합니다. `value` pointer는 해당 socket에 추가하거나 갱신할 data를 가리키며 type과 size는 map definition의 value type과 같아야 합니다.

`flags` parameter는 update behavior를 다음과 같이 제어합니다.

  • `BPF_ANY`: socket fd에 storage를 만들거나 existing storage를 갱신합니다.
  • `BPF_NOEXIST`: storage가 없을 때만 만들며 이미 있으면 `-EEXIST`로 실패합니다.
  • `BPF_EXIST`: storage가 있을 때만 갱신하며 없으면 `-ENOENT`로 실패합니다.

Update는 성공하면 0, 실패하면 negative error를 반환합니다.

Userspace에서 socket-local storage를 가져올 때는 다음 `bpf_map_lookup_elem()` function을 사용합니다.

int bpf_map_lookup_elem(int map_fd, const void *key, void *value)

`key` pointer의 socket fd로 socket을 식별합니다. Lookup은 성공하면 0, 실패하면 negative error를 반환합니다.

Userspace에서 socket-local storage를 삭제할 때는 다음 `bpf_map_delete_elem()` function을 사용합니다.

int bpf_map_delete_elem(int map_fd, const void *key)

`key` pointer의 socket fd가 삭제 대상 socket을 식별합니다. Delete는 성공하면 0, 실패하면 negative error를 반환합니다.

Map 선언과 sockops lookup 예제

111-159

다음 code는 `BPF_F_NO_PREALLOC`, `int` key, `struct my_storage` value를 사용하는 `BPF_MAP_TYPE_SK_STORAGE` map을 선언합니다.

struct {
        __uint(type, BPF_MAP_TYPE_SK_STORAGE);
        __uint(map_flags, BPF_F_NO_PREALLOC);
        __type(key, int);
        __type(value, struct my_storage);
} socket_storage SEC(".maps");

다음 `sockops` program은 context에서 `struct bpf_sock`을 얻은 뒤 socket storage를 lookup하거나 생성합니다.

SEC("sockops")
int _sockops(struct bpf_sock_ops *ctx)
{
        struct my_storage *storage;
        struct bpf_sock *sk;

        sk = ctx->sk;
        if (!sk)
                return 1;

        storage = bpf_sk_storage_get(&socket_storage, sk, 0,
                                     BPF_LOCAL_STORAGE_GET_F_CREATE);
        if (!storage)
                return 1;

        /* Use 'storage' here */

        return 1;
}

`ctx->sk` 또는 `bpf_sk_storage_get()` 결과가 NULL이면 즉시 반환합니다. 유효한 storage를 얻으면 program-specific data를 그 위치에서 사용할 수 있습니다.

Functional example은 `tools/testing/selftests/bpf` directory에서 확인할 수 있습니다.

[Socket-local storage patch discussion](https://lwn.net/ml/netdev/20190426171103.61892-1-kafai@fb.com/)에서 도입 배경을 확인할 수 있습니다.