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

Linux 6.18.37 · BPF

BPF_MAP_TYPE_LPM_TRIE

LPM trie의 prefix key 형식과 byte order, longest-prefix CRUD API, userspace 순회 순서와 IPv4 예제를 설명합니다.

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

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

1. 요약·해설

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

요약과 해설

map_lpm_trie.rst:1-197

`BPF_MAP_TYPE_LPM_TRIE`는 IP address와 같은 bit sequence를 저장된 prefix 중 가장 길게 일치하는 entry에 연결합니다. Key data는 big endian이며 `prefixlen`이 유효 bit 수를 정합니다.

Map 생성에는 `BPF_F_NO_PREALLOC`이 필수입니다. Lookup key의 `prefixlen`을 maximum length로 설정해야 longest-prefix 검색이 올바르게 수행됩니다.

Userspace 순회는 구체적인 prefix를 덜 구체적인 prefix보다 먼저 반환합니다. IPv4에는 4-byte data와 32-bit lookup prefix를, IPv6에는 16-byte data를 사용합니다.

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_LPM_TRIE
6 =====================
7
8 .. note::
9 - ``BPF_MAP_TYPE_LPM_TRIE`` was introduced in kernel version 4.11
10
11 ``BPF_MAP_TYPE_LPM_TRIE`` provides a longest prefix match algorithm that
12 can be used to match IP addresses to a stored set of prefixes.
13 Internally, data is stored in an unbalanced trie of nodes that uses
14 ``prefixlen,data`` pairs as its keys. The ``data`` is interpreted in
15 network byte order, i.e. big endian, so ``data[0]`` stores the most
16 significant byte.
17
18 LPM tries may be created with a maximum prefix length that is a multiple
19 of 8, in the range from 8 to 2048. The key used for lookup and update
20 operations is a ``struct bpf_lpm_trie_key_u8``, extended by
21 ``max_prefixlen/8`` bytes.
22
23 - For IPv4 addresses the data length is 4 bytes
24 - For IPv6 addresses the data length is 16 bytes
25
26 The value type stored in the LPM trie can be any user defined type.
27
28 .. note::
29 When creating a map of type ``BPF_MAP_TYPE_LPM_TRIE`` you must set the
30 ``BPF_F_NO_PREALLOC`` flag.
31
32 Usage
33 =====
34
35 Kernel BPF
36 ----------
37
38 bpf_map_lookup_elem()
39 ~~~~~~~~~~~~~~~~~~~~~
40
41 .. code-block:: c
42
43 void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)
44
45 The longest prefix entry for a given data value can be found using the
46 ``bpf_map_lookup_elem()`` helper. This helper returns a pointer to the
47 value associated with the longest matching ``key``, or ``NULL`` if no
48 entry was found.
49
50 The ``key`` should have ``prefixlen`` set to ``max_prefixlen`` when
51 performing longest prefix lookups. For example, when searching for the
52 longest prefix match for an IPv4 address, ``prefixlen`` should be set to
53 ``32``.
54
55 bpf_map_update_elem()
56 ~~~~~~~~~~~~~~~~~~~~~
57
58 .. code-block:: c
59
60 long bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags)
61
62 Prefix entries can be added or updated using the ``bpf_map_update_elem()``
63 helper. This helper replaces existing elements atomically.
64
65 ``bpf_map_update_elem()`` returns ``0`` on success, or negative error in
66 case of failure.
67
68 .. note::
69 The flags parameter must be one of BPF_ANY, BPF_NOEXIST or BPF_EXIST,
70 but the value is ignored, giving BPF_ANY semantics.
71
72 bpf_map_delete_elem()
73 ~~~~~~~~~~~~~~~~~~~~~
74
75 .. code-block:: c
76
77 long bpf_map_delete_elem(struct bpf_map *map, const void *key)
78
79 Prefix entries can be deleted using the ``bpf_map_delete_elem()``
80 helper. This helper will return 0 on success, or negative error in case
81 of failure.
82
83 Userspace
84 ---------
85
86 Access from userspace uses libbpf APIs with the same names as above, with
87 the map identified by ``fd``.
88
89 bpf_map_get_next_key()
90 ~~~~~~~~~~~~~~~~~~~~~~
91
92 .. code-block:: c
93
94 int bpf_map_get_next_key (int fd, const void *cur_key, void *next_key)
95
96 A userspace program can iterate through the entries in an LPM trie using
97 libbpf's ``bpf_map_get_next_key()`` function. The first key can be
98 fetched by calling ``bpf_map_get_next_key()`` with ``cur_key`` set to
99 ``NULL``. Subsequent calls will fetch the next key that follows the
100 current key. ``bpf_map_get_next_key()`` returns ``0`` on success,
101 ``-ENOENT`` if ``cur_key`` is the last key in the trie, or negative
102 error in case of failure.
103
104 ``bpf_map_get_next_key()`` will iterate through the LPM trie elements
105 from leftmost leaf first. This means that iteration will return more
106 specific keys before less specific ones.
107
108 Examples
109 ========
110
111 Please see ``tools/testing/selftests/bpf/test_lpm_map.c`` for examples
112 of LPM trie usage from userspace. The code snippets below demonstrate
113 API usage.
114
115 Kernel BPF
116 ----------
117
118 The following BPF code snippet shows how to declare a new LPM trie for IPv4
119 address prefixes:
120
121 .. code-block:: c
122
123 #include <linux/bpf.h>
124 #include <bpf/bpf_helpers.h>
125
126 struct ipv4_lpm_key {
127 __u32 prefixlen;
128 __u32 data;
129 };
130
131 struct {
132 __uint(type, BPF_MAP_TYPE_LPM_TRIE);
133 __type(key, struct ipv4_lpm_key);
134 __type(value, __u32);
135 __uint(map_flags, BPF_F_NO_PREALLOC);
136 __uint(max_entries, 255);
137 } ipv4_lpm_map SEC(".maps");
138
139 The following BPF code snippet shows how to lookup by IPv4 address:
140
141 .. code-block:: c
142
143 void *lookup(__u32 ipaddr)
144 {
145 struct ipv4_lpm_key key = {
146 .prefixlen = 32,
147 .data = ipaddr
148 };
149
150 return bpf_map_lookup_elem(&ipv4_lpm_map, &key);
151 }
152
153 Userspace
154 ---------
155
156 The following snippet shows how to insert an IPv4 prefix entry into an
157 LPM trie:
158
159 .. code-block:: c
160
161 int add_prefix_entry(int lpm_fd, __u32 addr, __u32 prefixlen, struct value *value)
162 {
163 struct ipv4_lpm_key ipv4_key = {
164 .prefixlen = prefixlen,
165 .data = addr
166 };
167 return bpf_map_update_elem(lpm_fd, &ipv4_key, value, BPF_ANY);
168 }
169
170 The following snippet shows a userspace program walking through the entries
171 of an LPM trie:
172
173
174 .. code-block:: c
175
176 #include <bpf/libbpf.h>
177 #include <bpf/bpf.h>
178
179 void iterate_lpm_trie(int map_fd)
180 {
181 struct ipv4_lpm_key *cur_key = NULL;
182 struct ipv4_lpm_key next_key;
183 struct value value;
184 int err;
185
186 for (;;) {
187 err = bpf_map_get_next_key(map_fd, cur_key, &next_key);
188 if (err)
189 break;
190
191 bpf_map_lookup_elem(map_fd, &next_key, &value);
192
193 /* Use key and value here */
194
195 cur_key = &next_key;
196 }
197 }
198

3. 한국어 전문 번역

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

LPM trie key와 prefix 특성

1-31

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

`BPF_MAP_TYPE_LPM_TRIE`는 `kernel version 4.11`에 도입되었습니다.

`BPF_MAP_TYPE_LPM_TRIE`는 IP address를 저장된 prefix set과 비교할 수 있는 longest prefix match algorithm을 제공합니다.

내부 data는 `prefixlen,data` pair를 key로 사용하는 unbalanced trie node에 저장됩니다. `data`는 network byte order, 즉 big endian으로 해석되므로 `data[0]`에 most significant byte가 저장됩니다.

LPM trie의 maximum prefix length는 8부터 2048까지 범위에서 8의 배수로 지정할 수 있습니다. Lookup과 update에 사용하는 key는 `struct bpf_lpm_trie_key_u8` 뒤에 `max_prefixlen/8` byte를 확장한 형태입니다.

  • IPv4 address의 data length는 4 byte입니다.
  • IPv6 address의 data length는 16 byte입니다.

LPM trie에 저장하는 value type은 user가 정의한 어떤 type도 사용할 수 있습니다.

`BPF_MAP_TYPE_LPM_TRIE` map을 만들 때는 반드시 `BPF_F_NO_PREALLOC` flag를 설정해야 합니다.

Kernel BPF lookup, update, delete

32-82

주어진 data value에 대한 longest prefix entry는 다음 `bpf_map_lookup_elem()` helper로 찾습니다.

void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)

Helper는 가장 길게 일치하는 `key`에 연결된 value pointer를 반환하며 일치하는 entry가 없으면 `NULL`을 반환합니다.

Longest prefix lookup을 수행할 때 key의 `prefixlen`은 `max_prefixlen`으로 설정해야 합니다. 예를 들어 IPv4 address의 longest prefix match를 찾을 때는 `prefixlen = 32`로 지정합니다.

Prefix entry를 추가하거나 갱신할 때는 다음 `bpf_map_update_elem()` helper를 사용합니다. Existing element는 atomically 교체됩니다.

long bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags)

`bpf_map_update_elem()`은 성공하면 0, 실패하면 negative error를 반환합니다.

`flags` parameter는 `BPF_ANY`, `BPF_NOEXIST`, `BPF_EXIST` 중 하나여야 하지만 실제 값은 무시되며 항상 `BPF_ANY` semantics가 적용됩니다.

Prefix entry를 삭제할 때는 다음 `bpf_map_delete_elem()` helper를 사용합니다.

long bpf_map_delete_elem(struct bpf_map *map, const void *key)

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

Userspace 접근과 trie 순회 순서

83-107

Userspace에서는 위와 같은 이름의 libbpf API를 사용하며 map은 `fd`로 식별합니다. LPM trie entry를 순회할 때는 다음 `bpf_map_get_next_key()` function을 사용합니다.

int bpf_map_get_next_key (int fd, const void *cur_key, void *next_key)

첫 key는 `cur_key = NULL`로 호출해 가져오고 이후에는 current key 다음의 key를 가져옵니다. 성공하면 0, current key가 마지막이면 `-ENOENT`, 그 밖의 실패에는 negative error를 반환합니다.

`bpf_map_get_next_key()`는 LPM trie의 leftmost leaf부터 순회합니다. 따라서 덜 구체적인 key보다 더 구체적인 key가 먼저 반환됩니다.

IPv4 LPM trie 선언과 lookup

108-152

Userspace LPM trie example은 `tools/testing/selftests/bpf/test_lpm_map.c`에 있습니다. 다음 BPF code는 IPv4 address prefix를 저장할 새 LPM trie를 선언합니다.

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

struct ipv4_lpm_key {
        __u32 prefixlen;
        __u32 data;
};

struct {
        __uint(type, BPF_MAP_TYPE_LPM_TRIE);
        __type(key, struct ipv4_lpm_key);
        __type(value, __u32);
        __uint(map_flags, BPF_F_NO_PREALLOC);
        __uint(max_entries, 255);
} ipv4_lpm_map SEC(".maps");

`struct ipv4_lpm_key`는 `prefixlen`과 32-bit address `data`를 담습니다. Map은 `BPF_MAP_TYPE_LPM_TRIE`, `BPF_F_NO_PREALLOC`, 최대 255개 entry로 선언됩니다.

다음 lookup function은 IPv4 address 전체 32 bit로 longest prefix match를 수행하기 위해 `prefixlen = 32`인 key를 만들고 `ipv4_lpm_map`을 조회합니다.

void *lookup(__u32 ipaddr)
{
        struct ipv4_lpm_key key = {
                .prefixlen = 32,
                .data = ipaddr
        };

        return bpf_map_lookup_elem(&ipv4_lpm_map, &key);
}

Userspace prefix 삽입과 순회

153-197

다음 userspace code는 address와 prefix length로 `ipv4_lpm_key`를 구성하고 `BPF_ANY` semantics로 IPv4 prefix entry를 LPM trie에 넣습니다.

int add_prefix_entry(int lpm_fd, __u32 addr, __u32 prefixlen, struct value *value)
{
        struct ipv4_lpm_key ipv4_key = {
                .prefixlen = prefixlen,
                .data = addr
        };
        return bpf_map_update_elem(lpm_fd, &ipv4_key, value, BPF_ANY);
}

다음 program은 LPM trie entry를 순회합니다. `cur_key = NULL`에서 시작해 next key를 얻고 해당 value를 lookup한 뒤 current key pointer를 갱신합니다.

#include <bpf/libbpf.h>
#include <bpf/bpf.h>

void iterate_lpm_trie(int map_fd)
{
        struct ipv4_lpm_key *cur_key = NULL;
        struct ipv4_lpm_key next_key;
        struct value value;
        int err;

        for (;;) {
                err = bpf_map_get_next_key(map_fd, cur_key, &next_key);
                if (err)
                        break;

                bpf_map_lookup_elem(map_fd, &next_key, &value);

                /* Use key and value here */

                cur_key = &next_key;
        }
}