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

Linux 6.18.37 · BPF

BPF Graph Data Structures

BPF linked list·rbtree의 lock, ownership, non-owning reference, pointer alias invalidation semantics를 설명합니다.

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

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

1. 요약·해설

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

요약과 해설

graph_ds_impl.rst:1-267

새 방식의 BPF `linked_list`와 `rbtree`는 intrusive node, root와 같은 map value에 놓인 `bpf_spin_lock`, kfunc 기반 API를 공유합니다. Verifier가 lock 연결을 정적으로 검사하므로 runtime lock-check 비용은 없습니다.

Node를 graph에 넘기면 owning reference가 non-owning reference로 바뀝니다. Lock critical section과 pointer stability 덕분에 ownership 이전 뒤에도 안전하게 node를 읽고 쓸 수 있지만, `spin_unlock`이나 remove type kfunc에서는 alias가 stale reference와 double-free를 만들지 않도록 모든 non-owning reference를 invalidate해야 합니다.

이 API와 semantics는 현재 상태를 설명할 뿐 안정성을 보장하지 않으며, kfunc 특성상 필요하면 하위 호환성을 깨뜨리는 방향으로 바뀔 수 있습니다.

2. 영어 원문 전체

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

원문 전체 펼치기
1 =========================
2 BPF Graph Data Structures
3 =========================
4
5 This document describes implementation details of new-style "graph" data
6 structures (linked_list, rbtree), with particular focus on the verifier's
7 implementation of semantics specific to those data structures.
8
9 Although no specific verifier code is referred to in this document, the document
10 assumes that the reader has general knowledge of BPF verifier internals, BPF
11 maps, and BPF program writing.
12
13 Note that the intent of this document is to describe the current state of
14 these graph data structures. **No guarantees** of stability for either
15 semantics or APIs are made or implied here.
16
17 .. contents::
18 :local:
19 :depth: 2
20
21 Introduction
22 ------------
23
24 The BPF map API has historically been the main way to expose data structures
25 of various types for use within BPF programs. Some data structures fit naturally
26 with the map API (HASH, ARRAY), others less so. Consequently, programs
27 interacting with the latter group of data structures can be hard to parse
28 for kernel programmers without previous BPF experience.
29
30 Luckily, some restrictions which necessitated the use of BPF map semantics are
31 no longer relevant. With the introduction of kfuncs, kptrs, and the any-context
32 BPF allocator, it is now possible to implement BPF data structures whose API
33 and semantics more closely match those exposed to the rest of the kernel.
34
35 Two such data structures - linked_list and rbtree - have many verification
36 details in common. Because both have "root"s ("head" for linked_list) and
37 "node"s, the verifier code and this document refer to common functionality
38 as "graph_api", "graph_root", "graph_node", etc.
39
40 Unless otherwise stated, examples and semantics below apply to both graph data
41 structures.
42
43 Unstable API
44 ------------
45
46 Data structures implemented using the BPF map API have historically used BPF
47 helper functions - either standard map API helpers like ``bpf_map_update_elem``
48 or map-specific helpers. The new-style graph data structures instead use kfuncs
49 to define their manipulation helpers. Because there are no stability guarantees
50 for kfuncs, the API and semantics for these data structures can be evolved in
51 a way that breaks backwards compatibility if necessary.
52
53 Root and node types for the new data structures are opaquely defined in the
54 ``uapi/linux/bpf.h`` header.
55
56 Locking
57 -------
58
59 The new-style data structures are intrusive and are defined similarly to their
60 vanilla kernel counterparts:
61
62 .. code-block:: c
63
64 struct node_data {
65 long key;
66 long data;
67 struct bpf_rb_node node;
68 };
69
70 struct bpf_spin_lock glock;
71 struct bpf_rb_root groot __contains(node_data, node);
72
73 The "root" type for both linked_list and rbtree expects to be in a map_value
74 which also contains a ``bpf_spin_lock`` - in the above example both global
75 variables are placed in a single-value arraymap. The verifier considers this
76 spin_lock to be associated with the ``bpf_rb_root`` by virtue of both being in
77 the same map_value and will enforce that the correct lock is held when
78 verifying BPF programs that manipulate the tree. Since this lock checking
79 happens at verification time, there is no runtime penalty.
80
81 Non-owning references
82 ---------------------
83
84 **Motivation**
85
86 Consider the following BPF code:
87
88 .. code-block:: c
89
90 struct node_data *n = bpf_obj_new(typeof(*n)); /* ACQUIRED */
91
92 bpf_spin_lock(&lock);
93
94 bpf_rbtree_add(&tree, n); /* PASSED */
95
96 bpf_spin_unlock(&lock);
97
98 From the verifier's perspective, the pointer ``n`` returned from ``bpf_obj_new``
99 has type ``PTR_TO_BTF_ID | MEM_ALLOC``, with a ``btf_id`` of
100 ``struct node_data`` and a nonzero ``ref_obj_id``. Because it holds ``n``, the
101 program has ownership of the pointee's (object pointed to by ``n``) lifetime.
102 The BPF program must pass off ownership before exiting - either via
103 ``bpf_obj_drop``, which ``free``'s the object, or by adding it to ``tree`` with
104 ``bpf_rbtree_add``.
105
106 (``ACQUIRED`` and ``PASSED`` comments in the example denote statements where
107 "ownership is acquired" and "ownership is passed", respectively)
108
109 What should the verifier do with ``n`` after ownership is passed off? If the
110 object was ``free``'d with ``bpf_obj_drop`` the answer is obvious: the verifier
111 should reject programs which attempt to access ``n`` after ``bpf_obj_drop`` as
112 the object is no longer valid. The underlying memory may have been reused for
113 some other allocation, unmapped, etc.
114
115 When ownership is passed to ``tree`` via ``bpf_rbtree_add`` the answer is less
116 obvious. The verifier could enforce the same semantics as for ``bpf_obj_drop``,
117 but that would result in programs with useful, common coding patterns being
118 rejected, e.g.:
119
120 .. code-block:: c
121
122 int x;
123 struct node_data *n = bpf_obj_new(typeof(*n)); /* ACQUIRED */
124
125 bpf_spin_lock(&lock);
126
127 bpf_rbtree_add(&tree, n); /* PASSED */
128 x = n->data;
129 n->data = 42;
130
131 bpf_spin_unlock(&lock);
132
133 Both the read from and write to ``n->data`` would be rejected. The verifier
134 can do better, though, by taking advantage of two details:
135
136 * Graph data structure APIs can only be used when the ``bpf_spin_lock``
137 associated with the graph root is held
138
139 * Both graph data structures have pointer stability
140
141 * Because graph nodes are allocated with ``bpf_obj_new`` and
142 adding / removing from the root involves fiddling with the
143 ``bpf_{list,rb}_node`` field of the node struct, a graph node will
144 remain at the same address after either operation.
145
146 Because the associated ``bpf_spin_lock`` must be held by any program adding
147 or removing, if we're in the critical section bounded by that lock, we know
148 that no other program can add or remove until the end of the critical section.
149 This combined with pointer stability means that, until the critical section
150 ends, we can safely access the graph node through ``n`` even after it was used
151 to pass ownership.
152
153 The verifier considers such a reference a *non-owning reference*. The ref
154 returned by ``bpf_obj_new`` is accordingly considered an *owning reference*.
155 Both terms currently only have meaning in the context of graph nodes and API.
156
157 **Details**
158
159 Let's enumerate the properties of both types of references.
160
161 *owning reference*
162
163 * This reference controls the lifetime of the pointee
164
165 * Ownership of pointee must be 'released' by passing it to some graph API
166 kfunc, or via ``bpf_obj_drop``, which ``free``'s the pointee
167
168 * If not released before program ends, verifier considers program invalid
169
170 * Access to the pointee's memory will not page fault
171
172 *non-owning reference*
173
174 * This reference does not own the pointee
175
176 * It cannot be used to add the graph node to a graph root, nor ``free``'d via
177 ``bpf_obj_drop``
178
179 * No explicit control of lifetime, but can infer valid lifetime based on
180 non-owning ref existence (see explanation below)
181
182 * Access to the pointee's memory will not page fault
183
184 From verifier's perspective non-owning references can only exist
185 between spin_lock and spin_unlock. Why? After spin_unlock another program
186 can do arbitrary operations on the data structure like removing and ``free``-ing
187 via bpf_obj_drop. A non-owning ref to some chunk of memory that was remove'd,
188 ``free``'d, and reused via bpf_obj_new would point to an entirely different thing.
189 Or the memory could go away.
190
191 To prevent this logic violation all non-owning references are invalidated by the
192 verifier after a critical section ends. This is necessary to ensure the "will
193 not page fault" property of non-owning references. So if the verifier hasn't
194 invalidated a non-owning ref, accessing it will not page fault.
195
196 Currently ``bpf_obj_drop`` is not allowed in the critical section, so
197 if there's a valid non-owning ref, we must be in a critical section, and can
198 conclude that the ref's memory hasn't been dropped-and- ``free``'d or
199 dropped-and-reused.
200
201 Any reference to a node that is in an rbtree _must_ be non-owning, since
202 the tree has control of the pointee's lifetime. Similarly, any ref to a node
203 that isn't in rbtree _must_ be owning. This results in a nice property:
204 graph API add / remove implementations don't need to check if a node
205 has already been added (or already removed), as the ownership model
206 allows the verifier to prevent such a state from being valid by simply checking
207 types.
208
209 However, pointer aliasing poses an issue for the above "nice property".
210 Consider the following example:
211
212 .. code-block:: c
213
214 struct node_data *n, *m, *o, *p;
215 n = bpf_obj_new(typeof(*n)); /* 1 */
216
217 bpf_spin_lock(&lock);
218
219 bpf_rbtree_add(&tree, n); /* 2 */
220 m = bpf_rbtree_first(&tree); /* 3 */
221
222 o = bpf_rbtree_remove(&tree, n); /* 4 */
223 p = bpf_rbtree_remove(&tree, m); /* 5 */
224
225 bpf_spin_unlock(&lock);
226
227 bpf_obj_drop(o);
228 bpf_obj_drop(p); /* 6 */
229
230 Assume the tree is empty before this program runs. If we track verifier state
231 changes here using numbers in above comments:
232
233 1) n is an owning reference
234
235 2) n is a non-owning reference, it's been added to the tree
236
237 3) n and m are non-owning references, they both point to the same node
238
239 4) o is an owning reference, n and m non-owning, all point to same node
240
241 5) o and p are owning, n and m non-owning, all point to the same node
242
243 6) a double-free has occurred, since o and p point to same node and o was
244 ``free``'d in previous statement
245
246 States 4 and 5 violate our "nice property", as there are non-owning refs to
247 a node which is not in an rbtree. Statement 5 will try to remove a node which
248 has already been removed as a result of this violation. State 6 is a dangerous
249 double-free.
250
251 At a minimum we should prevent state 6 from being possible. If we can't also
252 prevent state 5 then we must abandon our "nice property" and check whether a
253 node has already been removed at runtime.
254
255 We prevent both by generalizing the "invalidate non-owning references" behavior
256 of ``bpf_spin_unlock`` and doing similar invalidation after
257 ``bpf_rbtree_remove``. The logic here being that any graph API kfunc which:
258
259 * takes an arbitrary node argument
260
261 * removes it from the data structure
262
263 * returns an owning reference to the removed node
264
265 May result in a state where some other non-owning reference points to the same
266 node. So ``remove``-type kfuncs must be considered a non-owning reference
267 invalidation point as well.
268

3. 한국어 전문 번역

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

BPF graph data structure 구현

1-20

`BPF Graph Data Structures` 문서는 새 방식의 "graph" data structure인 `linked_list`와 `rbtree`의 구현 세부 사항을 설명하며, 특히 이들 data structure에 고유한 semantics를 verifier가 어떻게 구현하는지에 초점을 맞춥니다.

특정 verifier code를 직접 지칭하지는 않지만, 독자가 BPF verifier 내부 구조, BPF map, BPF program 작성에 관한 일반 지식을 갖추었다고 가정합니다.

이 문서의 목적은 graph data structure의 현재 상태를 설명하는 것입니다. 여기에서 semantics나 API의 안정성을 보장하거나 암시하지 않습니다.

문서에는 depth 2의 local contents 목록이 포함됩니다.

Map API에서 graph API로

21-42

역사적으로 BPF map API는 BPF program 안에서 여러 종류의 data structure를 노출하는 주된 방법이었습니다. `HASH`와 `ARRAY` 같은 일부 data structure는 map API에 자연스럽게 들어맞지만, 그렇지 않은 것도 있습니다. 따라서 후자와 상호 작용하는 program은 BPF 경험이 없는 kernel programmer가 해석하기 어려울 수 있습니다.

다행히 BPF map semantics를 사용해야 했던 일부 제약은 더 이상 유효하지 않습니다. kfunc, kptr, any-context BPF allocator가 도입되면서 이제 kernel의 나머지 부분에 노출되는 API와 semantics에 더 가까운 BPF data structure를 구현할 수 있습니다.

그러한 data structure인 `linked_list`와 `rbtree`는 많은 verification 세부 사항을 공유합니다. 둘 다 "root"(`linked_list`에서는 "head")와 "node"를 가지므로 verifier code와 이 문서는 공통 기능을 `graph_api`, `graph_root`, `graph_node` 등으로 부릅니다.

달리 명시하지 않는 한 아래의 예제와 semantics는 두 graph data structure 모두에 적용됩니다.

안정성을 보장하지 않는 API

43-55

BPF map API로 구현한 data structure는 역사적으로 표준 map API helper인 `bpf_map_update_elem` 또는 map별 helper 같은 BPF helper function을 사용했습니다. 새 방식의 graph data structure는 그 대신 조작 helper를 kfunc로 정의합니다.

kfunc에는 안정성 보장이 없으므로 필요하다면 이 data structure의 API와 semantics를 하위 호환성을 깨뜨리는 방식으로 발전시킬 수 있습니다.

새 data structure의 root와 node type은 `uapi/linux/bpf.h` header에 opaque하게 정의되어 있습니다.

Intrusive node와 root별 spin lock

56-80

새 방식의 data structure는 intrusive 구조이며 일반 kernel counterpart와 비슷하게 정의합니다.

struct node_data {
  long key;
  long data;
  struct bpf_rb_node node;
};

struct bpf_spin_lock glock;
struct bpf_rb_root groot __contains(node_data, node);

`linked_list`와 `rbtree`의 "root" type은 `bpf_spin_lock`도 함께 들어 있는 map value 안에 위치해야 합니다. 위 예제에서는 두 global variable을 single-value array map 하나에 둡니다.

Verifier는 `bpf_spin_lock`과 `bpf_rb_root`가 같은 map value에 있다는 사실을 근거로 둘을 연결하고, tree를 조작하는 BPF program을 검증할 때 올바른 lock을 보유했는지 강제합니다. 이 lock 검사는 verification 시점에 수행되므로 runtime penalty가 없습니다.

Ownership을 획득하고 graph에 넘기는 과정

81-108

다음 BPF code를 살펴봅니다.

struct node_data *n = bpf_obj_new(typeof(*n)); /* ACQUIRED */

bpf_spin_lock(&lock);

bpf_rbtree_add(&tree, n); /* PASSED */

bpf_spin_unlock(&lock);

Verifier 관점에서 `bpf_obj_new`가 반환한 pointer `n`의 type은 `PTR_TO_BTF_ID | MEM_ALLOC`이고, `btf_id`는 `struct node_data`이며 `ref_obj_id`는 0이 아닙니다. Program이 `n`을 보유하므로 `n`이 가리키는 object의 lifetime ownership도 program에 있습니다.

BPF program은 종료하기 전에 ownership을 넘겨야 합니다. Object를 `free`하는 `bpf_obj_drop`에 넘기거나, `bpf_rbtree_add`로 `tree`에 추가할 수 있습니다.

예제의 `ACQUIRED`와 `PASSED` comment는 각각 "ownership을 획득한" statement와 "ownership을 넘긴" statement를 나타냅니다.

Ownership 이전 뒤에도 유용한 접근

109-132

Ownership을 넘긴 뒤 verifier가 `n`을 어떻게 처리해야 하는지 생각해 볼 수 있습니다. `bpf_obj_drop`으로 object를 `free`했다면 답은 분명합니다. Object가 더 이상 유효하지 않으므로 `bpf_obj_drop` 뒤에 `n`에 접근하려는 program은 reject해야 합니다. Underlying memory가 다른 allocation에 재사용되거나 unmap되는 등 여러 일이 생길 수 있습니다.

`bpf_rbtree_add`를 통해 ownership을 `tree`에 넘긴 경우에는 답이 덜 분명합니다. Verifier가 `bpf_obj_drop`과 같은 semantics를 강제할 수도 있지만, 그러면 다음과 같이 유용하고 흔한 coding pattern을 사용하는 program이 reject됩니다.

int x;
struct node_data *n = bpf_obj_new(typeof(*n)); /* ACQUIRED */

bpf_spin_lock(&lock);

bpf_rbtree_add(&tree, n); /* PASSED */
x = n->data;
n->data = 42;

bpf_spin_unlock(&lock);

이 경우 `n->data`를 읽는 동작과 쓰는 동작이 모두 reject될 것입니다.

Lock과 pointer stability가 허용하는 안전한 접근

133-156

Verifier는 다음 두 가지 세부 사항을 활용해 더 나은 결과를 낼 수 있습니다.

  • Graph data structure API는 graph root에 연결된 `bpf_spin_lock`을 보유하고 있을 때만 사용할 수 있습니다.
  • 두 graph data structure 모두 pointer stability를 가집니다. Graph node는 `bpf_obj_new`로 allocate되고 root에 추가하거나 root에서 제거할 때 node struct의 `bpf_{list,rb}_node` field만 조작하므로, 두 operation 뒤에도 graph node의 address는 바뀌지 않습니다.

추가 또는 제거를 수행하는 program은 연결된 `bpf_spin_lock`을 반드시 보유해야 합니다. 따라서 그 lock이 경계를 이루는 critical section 안에서는 section이 끝날 때까지 다른 program이 node를 추가하거나 제거할 수 없다는 것을 압니다.

이 사실을 pointer stability와 결합하면 ownership을 넘기는 데 `n`을 사용한 뒤에도 critical section이 끝날 때까지 `n`을 통해 graph node에 안전하게 접근할 수 있습니다.

Verifier는 이러한 reference를 *non-owning reference*라고 합니다. 이에 따라 `bpf_obj_new`가 반환한 reference는 *owning reference*입니다. 현재 두 용어는 graph node와 graph API 맥락에서만 의미가 있습니다.

Owning reference의 속성

157-171

두 reference type의 속성을 열거하면 다음과 같습니다.

  • Owning reference는 pointee의 lifetime을 제어합니다.
  • Pointee의 ownership은 graph API kfunc에 넘기거나 pointee를 `free`하는 `bpf_obj_drop`을 통해 반드시 "release"해야 합니다. Program이 끝나기 전에 release하지 않으면 verifier는 program을 invalid로 판단합니다.
  • Pointee memory에 접근해도 page fault가 발생하지 않습니다.

Non-owning reference의 속성

172-183
  • Non-owning reference는 pointee를 소유하지 않습니다. 따라서 graph node를 graph root에 추가하는 데 사용할 수 없고 `bpf_obj_drop`으로 `free`할 수도 없습니다.
  • Lifetime을 명시적으로 제어하지 않지만 non-owning reference가 존재한다는 사실을 바탕으로 유효한 lifetime을 추론할 수 있습니다.
  • Pointee memory에 접근해도 page fault가 발생하지 않습니다.

Critical section에 한정된 유효 기간

184-200

Verifier 관점에서 non-owning reference는 `spin_lock`과 `spin_unlock` 사이에만 존재할 수 있습니다. `spin_unlock` 뒤에는 다른 program이 data structure에서 node를 제거하고 `bpf_obj_drop`으로 `free`하는 등 임의의 operation을 수행할 수 있기 때문입니다.

어떤 memory chunk가 제거되고 `free`된 뒤 `bpf_obj_new`로 재사용되면 그곳을 가리키던 non-owning reference는 완전히 다른 대상을 가리키게 됩니다. Memory 자체가 사라질 수도 있습니다.

이 논리 위반을 막기 위해 verifier는 critical section이 끝난 뒤 모든 non-owning reference를 invalidate합니다. 이는 non-owning reference가 "page fault를 일으키지 않는다"는 속성을 보장하는 데 필요합니다. 따라서 verifier가 invalidate하지 않은 non-owning reference에는 page fault 없이 접근할 수 있습니다.

현재 critical section 안에서는 `bpf_obj_drop`이 허용되지 않습니다. 그러므로 유효한 non-owning reference가 있다면 critical section 안에 있는 것이고, reference의 memory가 drop 후 `free`되거나 drop 후 재사용되지 않았다고 결론 내릴 수 있습니다.

Reference type으로 강제하는 node 상태

201-208

Rbtree 안에 있는 node를 가리키는 모든 reference는 non-owning이어야 합니다. Tree가 pointee의 lifetime을 제어하기 때문입니다. 마찬가지로 rbtree에 들어 있지 않은 node의 reference는 모두 owning이어야 합니다.

이로써 유용한 속성이 생깁니다. Graph API의 add/remove 구현은 node가 이미 추가됐거나 이미 제거됐는지 확인할 필요가 없습니다. Verifier가 type만 확인해 그런 상태가 유효해지는 것을 ownership model 차원에서 막을 수 있기 때문입니다.

Pointer aliasing이 만드는 반례

209-229

그러나 pointer aliasing은 위의 유용한 속성에 문제를 일으킵니다. 다음 예제를 살펴봅니다.

struct node_data *n, *m, *o, *p;
n = bpf_obj_new(typeof(*n));     /* 1 */

bpf_spin_lock(&lock);

bpf_rbtree_add(&tree, n);        /* 2 */
m = bpf_rbtree_first(&tree);     /* 3 */

o = bpf_rbtree_remove(&tree, n); /* 4 */
p = bpf_rbtree_remove(&tree, m); /* 5 */

bpf_spin_unlock(&lock);

bpf_obj_drop(o);
bpf_obj_drop(p); /* 6 */

Aliased reference의 상태 변화

230-245

Program 실행 전에 tree가 비어 있다고 가정하고, 위 comment의 번호에 따라 verifier state 변화를 추적하면 다음과 같습니다.

  • 1) `n`은 owning reference입니다.
  • 2) `n`은 tree에 추가되었으므로 non-owning reference입니다.
  • 3) `n`과 `m`은 non-owning reference이고 둘 다 같은 node를 가리킵니다.
  • 4) `o`는 owning reference이고 `n`과 `m`은 non-owning reference이며, 셋 모두 같은 node를 가리킵니다.
  • 5) `o`와 `p`는 owning이고 `n`과 `m`은 non-owning이며, 넷 모두 같은 node를 가리킵니다.
  • 6) `o`와 `p`가 같은 node를 가리키고 이전 statement에서 `o`를 `free`했으므로 double-free가 발생했습니다.

Remove kfunc도 non-owning reference를 invalidate해야 하는 이유

246-267

상태 4와 5에는 rbtree 안에 있지 않은 node를 가리키는 non-owning reference가 있으므로 앞서 말한 유용한 속성을 위반합니다. 이 위반 때문에 statement 5는 이미 제거된 node를 다시 제거하려고 하고, 상태 6에서는 위험한 double-free가 발생합니다.

최소한 상태 6은 발생할 수 없도록 해야 합니다. 상태 5까지 막을 수 없다면 유용한 속성을 포기하고 node가 이미 제거됐는지 runtime에 확인해야 합니다.

두 상태 모두 막기 위해 `bpf_spin_unlock`의 "non-owning reference invalidate" 동작을 일반화하고 `bpf_rbtree_remove` 뒤에도 비슷한 invalidation을 수행합니다.

그 근거는 다음 조건을 모두 만족하는 graph API kfunc가

  • 임의의 node argument를 받고,
  • 그 node를 data structure에서 제거하며,
  • 제거한 node의 owning reference를 반환하면,

같은 node를 가리키는 다른 non-owning reference가 남아 있는 상태를 만들 수 있다는 것입니다. 따라서 `remove` type kfunc도 non-owning reference invalidation point로 간주해야 합니다.