Branch data Line data Source code
1 : : /* SPDX-License-Identifier: GPL-2.0-only */
2 : : /*
3 : : * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
4 : : */
5 : :
6 : : #include "cgen/program.h"
7 : :
8 : : #include <linux/bpf.h>
9 : : #include <linux/bpf_common.h>
10 : : #include <linux/limits.h>
11 : :
12 : : #include <errno.h>
13 : : #include <fcntl.h>
14 : : #include <limits.h>
15 : : #include <stddef.h>
16 : : #include <stdint.h>
17 : : #include <stdio.h>
18 : : #include <stdlib.h>
19 : : #include <string.h>
20 : : #include <unistd.h>
21 : :
22 : : #include <bpfilter/bpf.h>
23 : : #include <bpfilter/btf.h>
24 : : #include <bpfilter/chain.h>
25 : : #include <bpfilter/core/list.h>
26 : : #include <bpfilter/counter.h>
27 : : #include <bpfilter/ctx.h>
28 : : #include <bpfilter/dump.h>
29 : : #include <bpfilter/flavor.h>
30 : : #include <bpfilter/helper.h>
31 : : #include <bpfilter/hook.h>
32 : : #include <bpfilter/io.h>
33 : : #include <bpfilter/logger.h>
34 : : #include <bpfilter/matcher.h>
35 : : #include <bpfilter/pack.h>
36 : : #include <bpfilter/rule.h>
37 : : #include <bpfilter/set.h>
38 : : #include <bpfilter/verdict.h>
39 : :
40 : : #include "cgen/cgroup_skb.h"
41 : : #include "cgen/cgroup_sock_addr.h"
42 : : #include "cgen/dump.h"
43 : : #include "cgen/fixup.h"
44 : : #include "cgen/handle.h"
45 : : #include "cgen/jmp.h"
46 : : #include "cgen/nf.h"
47 : : #include "cgen/printer.h"
48 : : #include "cgen/prog/link.h"
49 : : #include "cgen/prog/map.h"
50 : : #include "cgen/stub.h"
51 : : #include "cgen/tc.h"
52 : : #include "cgen/xdp.h"
53 : : #include "filter.h"
54 : :
55 : : #define _BF_LOG_BUF_SIZE \
56 : : (UINT32_MAX >> 8) /* verifier maximum in kernels <= 5.1 */
57 : : #define _BF_LOG_MAP_N_ENTRIES 1000
58 : : #define _BF_LOG_MAP_SIZE \
59 : : _bf_round_next_power_of_2(sizeof(struct bf_log) * _BF_LOG_MAP_N_ENTRIES)
60 : : #define _BF_SET_MAP_PREFIX "bf_set_"
61 : : #define _BF_COUNTER_MAP_NAME "bf_cmap"
62 : : #define _BF_PRINTER_MAP_NAME "bf_pmap"
63 : : #define _BF_LOG_MAP_NAME "bf_lmap"
64 : :
65 : : static inline size_t _bf_round_next_power_of_2(size_t value)
66 : : {
67 : : if (value == 0)
68 : : return 1;
69 : :
70 : : value--;
71 : : value |= value >> 1;
72 : : value |= value >> 2;
73 : : value |= value >> 4;
74 : : value |= value >> 8;
75 : : value |= value >> 16;
76 : : #if SIZE_MAX > 0xFFFFFFFFU
77 : : value |= value >> 32;
78 : : #endif
79 : :
80 : : return ++value;
81 : : }
82 : :
83 : : /**
84 : : * @brief Sets sharing the same key format, collapsed to a single BPF map.
85 : : *
86 : : * For hash-keyed sets, one `bf_set_group` exists for every unique key
87 : : * format among a chain's non-empty sets. The sets list preserves insertion
88 : : * order; a set's position is its bit index within the group's bitmask
89 : : * value.
90 : : *
91 : : * LPM trie sets are never grouped together: each non-empty trie set gets
92 : : * its own group of size 1. See `_bf_program_build_set_groups()` for the
93 : : * rationale.
94 : : */
95 : : struct bf_set_group
96 : : {
97 : : /** Non-empty sets that map to the same BPF map. For hash-keyed groups,
98 : : * all sets share the same key format; LPM trie groups always hold a
99 : : * single set. Non-owning pointers into the chain's `bf_set` list.
100 : : * Never empty. */
101 : : bf_list sets;
102 : :
103 : : /** Backing BPF map. Populated during `bf_program_load()`; the map is
104 : : * owned by `bf_program->handle->sets`. */
105 : : struct bf_map *map;
106 : : };
107 : :
108 : : #define _free_bf_set_group_ __attribute__((__cleanup__(_bf_set_group_free)))
109 : :
110 : 903 : static void _bf_set_group_free(struct bf_set_group **group)
111 : : {
112 : : assert(group);
113 : :
114 [ + + ]: 903 : if (!*group)
115 : : return;
116 : :
117 : 301 : bf_list_clean(&(*group)->sets);
118 : : freep((void *)group);
119 : : }
120 : :
121 : 301 : static int _bf_set_group_new(struct bf_set_group **group)
122 : : {
123 : 301 : _free_bf_set_group_ struct bf_set_group *_group = NULL;
124 : :
125 : : assert(group);
126 : :
127 : 301 : _group = calloc(1, sizeof(*_group));
128 [ + - ]: 301 : if (!_group)
129 : : return -ENOMEM;
130 : :
131 : : /* The list holds non-owning const struct bf_set * pointers; no free
132 : : * callback. Groups are temporary (not serialized), so no pack callback
133 : : * either. */
134 : 301 : _group->sets = bf_list_default(NULL, NULL);
135 : :
136 : 301 : *group = TAKE_PTR(_group);
137 : :
138 : 301 : return 0;
139 : : }
140 : :
141 : : static struct bf_set_group *
142 : 275 : _bf_program_find_set_group(const struct bf_program *program,
143 : : const struct bf_set *set)
144 : : {
145 : : assert(program);
146 : :
147 [ - + ]: 275 : if (!set)
148 : : return NULL;
149 : :
150 [ + - + - : 752 : bf_list_foreach (&program->set_groups, group_node) {
- + ]
151 : : struct bf_set_group *group = bf_list_node_get_data(group_node);
152 [ + - + + : 943 : bf_list_foreach (&group->sets, set_node) {
+ + ]
153 [ + + ]: 421 : if (bf_list_node_get_data(set_node) == set)
154 : : return group;
155 : : }
156 : : }
157 : :
158 : : return NULL;
159 : : }
160 : :
161 : 1310 : static int _bf_program_build_set_groups(struct bf_program *program)
162 : : {
163 : : assert(program);
164 : :
165 : 1310 : bf_list_clean(&program->set_groups);
166 : 1310 : program->set_groups = bf_list_default(_bf_set_group_free, NULL);
167 : :
168 [ + + + + : 3260 : bf_list_foreach (&program->runtime.chain->sets, set_node) {
+ + ]
169 : : struct bf_set *set = bf_list_node_get_data(set_node);
170 : : struct bf_set_group *match = NULL;
171 : : int r;
172 : :
173 [ + + ]: 320 : if (bf_hashset_is_empty(&set->elems))
174 : 4 : continue;
175 : :
176 : : /* LPM trie sets are not grouped: BPF LPM trie lookup always
177 : : * returns the longest-prefix match, so the read-modify-write
178 : : * step in _bf_program_load_sets_maps() can't preserve the
179 : : * per-set bitmask when prefixes overlap. */
180 [ + + ]: 316 : if (!set->use_trie) {
181 [ + + + + : 706 : bf_list_foreach (&program->set_groups, group_node) {
+ + ]
182 : : struct bf_set_group *group = bf_list_node_get_data(group_node);
183 : : const struct bf_set *head =
184 : : bf_list_node_get_data(bf_list_get_head(&group->sets));
185 : :
186 [ + + ]: 116 : if (bf_set_same_key(set, head)) {
187 : : match = group;
188 : : break;
189 : : }
190 : : }
191 : : }
192 : :
193 [ + + ]: 252 : if (match) {
194 : 15 : r = bf_list_add_tail(&match->sets, set);
195 [ - + ]: 15 : if (r)
196 [ # # ]: 0 : return bf_err_r(r, "failed to add set to existing group");
197 : : } else {
198 : 301 : _free_bf_set_group_ struct bf_set_group *new_group = NULL;
199 : :
200 : 301 : r = _bf_set_group_new(&new_group);
201 [ - + ]: 301 : if (r)
202 [ # # ]: 0 : return bf_err_r(r, "failed to allocate set group");
203 : :
204 : 301 : r = bf_list_add_tail(&new_group->sets, set);
205 [ - + ]: 301 : if (r)
206 [ # # ]: 0 : return bf_err_r(r, "failed to seed set group");
207 : :
208 : 301 : r = bf_list_push(&program->set_groups, (void **)&new_group);
209 [ - + ]: 301 : if (r)
210 [ # # ]: 0 : return bf_err_r(r, "failed to register set group");
211 : : }
212 : : }
213 : :
214 : : return 0;
215 : : }
216 : :
217 : 275 : int bf_program_set_bit_index(const struct bf_program *program,
218 : : const struct bf_set *set, size_t *bit_index)
219 : : {
220 : : assert(program);
221 : : assert(set);
222 : : assert(bit_index);
223 : :
224 [ + - + - : 752 : bf_list_foreach (&program->set_groups, group_node) {
+ - ]
225 : : struct bf_set_group *group = bf_list_node_get_data(group_node);
226 : : size_t i = 0;
227 : :
228 [ + - + + ]: 943 : bf_list_foreach (&group->sets, set_node) {
229 [ + + ]: 421 : if (bf_list_node_get_data(set_node) == set) {
230 : 275 : *bit_index = i;
231 : 275 : return 0;
232 : : }
233 [ + + ]: 146 : ++i;
234 : : }
235 : : }
236 : :
237 : : return -ENOENT;
238 : : }
239 : :
240 : : static const struct bf_flavor_ops *bf_flavor_ops_get(enum bf_flavor flavor)
241 : : {
242 : : static const struct bf_flavor_ops *flavor_ops[] = {
243 : : [BF_FLAVOR_TC] = &bf_flavor_ops_tc,
244 : : [BF_FLAVOR_NF] = &bf_flavor_ops_nf,
245 : : [BF_FLAVOR_XDP] = &bf_flavor_ops_xdp,
246 : : [BF_FLAVOR_CGROUP_SKB] = &bf_flavor_ops_cgroup_skb,
247 : : [BF_FLAVOR_CGROUP_SOCK_ADDR] = &bf_flavor_ops_cgroup_sock_addr,
248 : : };
249 : :
250 : : static_assert_enum_mapping(flavor_ops, _BF_FLAVOR_MAX);
251 : :
252 : 1310 : return flavor_ops[flavor];
253 : : }
254 : :
255 : 1310 : int bf_program_new(struct bf_program **program, const struct bf_chain *chain,
256 : : struct bf_handle *handle)
257 : : {
258 : 1310 : _free_bf_program_ struct bf_program *_program = NULL;
259 : : int r;
260 : :
261 : : assert(program);
262 : : assert(chain);
263 : : assert(handle);
264 : :
265 : 1310 : _program = calloc(1, sizeof(*_program));
266 [ + - ]: 1310 : if (!_program)
267 : : return -ENOMEM;
268 : :
269 : 1310 : _program->flavor = bf_hook_to_flavor(chain->hook);
270 : 1310 : _program->runtime.ops = bf_flavor_ops_get(_program->flavor);
271 : 1310 : _program->runtime.chain = chain;
272 : 1310 : _program->img = bf_vector_default(sizeof(struct bpf_insn));
273 : 1310 : _program->fixups = bf_list_default(bf_fixup_free, NULL);
274 : 1310 : _program->set_groups = bf_list_default(_bf_set_group_free, NULL);
275 : 1310 : _program->handle = handle;
276 : :
277 : 1310 : r = bf_vector_reserve(&_program->img, 512);
278 [ + - ]: 1310 : if (r)
279 : : return r;
280 : :
281 : 1310 : r = bf_printer_new(&_program->printer);
282 [ + - ]: 1310 : if (r)
283 : : return r;
284 : :
285 : 1310 : *program = TAKE_PTR(_program);
286 : :
287 : 1310 : return 0;
288 : : }
289 : :
290 : 2620 : void bf_program_free(struct bf_program **program)
291 : : {
292 [ + + ]: 2620 : if (!*program)
293 : : return;
294 : :
295 : 1310 : bf_list_clean(&(*program)->fixups);
296 : 1310 : bf_list_clean(&(*program)->set_groups);
297 : 1310 : bf_vector_clean(&(*program)->img);
298 : :
299 : 1310 : bf_printer_free(&(*program)->printer);
300 : :
301 : 1310 : free(*program);
302 : 1310 : *program = NULL;
303 : : }
304 : :
305 : 0 : void bf_program_dump(const struct bf_program *program, prefix_t *prefix)
306 : : {
307 : : assert(program);
308 : : assert(prefix);
309 : :
310 [ # # ]: 0 : DUMP(prefix, "struct bf_program at %p", program);
311 : :
312 : 0 : bf_dump_prefix_push(prefix);
313 : :
314 [ # # ]: 0 : DUMP(prefix, "handle: struct bf_handle *");
315 : 0 : bf_dump_prefix_push(prefix);
316 : 0 : bf_handle_dump(program->handle, bf_dump_prefix_last(prefix));
317 : 0 : bf_dump_prefix_pop(prefix);
318 : :
319 [ # # ]: 0 : DUMP(prefix, "printer: struct bf_printer *");
320 : 0 : bf_dump_prefix_push(prefix);
321 : 0 : bf_printer_dump(program->printer, prefix);
322 : 0 : bf_dump_prefix_pop(prefix);
323 : :
324 [ # # ]: 0 : DUMP(prefix, "img: %p", program->img.data);
325 [ # # ]: 0 : DUMP(prefix, "img.size: %lu", program->img.size);
326 [ # # ]: 0 : DUMP(prefix, "img.cap: %lu", program->img.cap);
327 : :
328 [ # # ]: 0 : DUMP(prefix, "fixups: bf_list<struct bf_fixup>[%lu]",
329 : : bf_list_size(&program->fixups));
330 : 0 : bf_dump_prefix_push(prefix);
331 [ # # # # : 0 : bf_list_foreach (&program->fixups, fixup_node) {
# # ]
332 : : struct bf_fixup *fixup = bf_list_node_get_data(fixup_node);
333 : :
334 [ # # ]: 0 : if (bf_list_is_tail(&program->fixups, fixup_node))
335 : 0 : bf_dump_prefix_last(prefix);
336 : :
337 : 0 : bf_fixup_dump(fixup, prefix);
338 : : }
339 : 0 : bf_dump_prefix_pop(prefix);
340 : :
341 [ # # ]: 0 : DUMP(prefix, "groups: bf_list<struct bf_set_group>[%lu]",
342 : : bf_list_size(&program->set_groups));
343 : 0 : bf_dump_prefix_push(prefix);
344 [ # # # # : 0 : bf_list_foreach (&program->set_groups, group_node) {
# # ]
345 : : const struct bf_set_group *group = bf_list_node_get_data(group_node);
346 : : size_t i = 0;
347 : :
348 [ # # ]: 0 : if (bf_list_is_tail(&program->set_groups, group_node))
349 : 0 : bf_dump_prefix_last(prefix);
350 : :
351 [ # # ]: 0 : DUMP(prefix, "struct bf_set_group at %p (%lu set(s), map=%p)", group,
352 : : bf_list_size(&group->sets), (void *)group->map);
353 : 0 : bf_dump_prefix_push(prefix);
354 [ # # # # ]: 0 : bf_list_foreach (&group->sets, set_node) {
355 : : const struct bf_set *set = bf_list_node_get_data(set_node);
356 : :
357 [ # # ]: 0 : if (bf_list_is_tail(&group->sets, set_node))
358 : 0 : bf_dump_prefix_last(prefix);
359 [ # # # # ]: 0 : DUMP(prefix, "bit %lu: bf_set '%s' (%lu element(s))", i,
360 : : set->name ?: "<anonymous>", bf_hashset_size(&set->elems));
361 [ # # ]: 0 : ++i;
362 : : }
363 : 0 : bf_dump_prefix_pop(prefix);
364 : : }
365 : 0 : bf_dump_prefix_pop(prefix);
366 : :
367 [ # # ]: 0 : DUMP(bf_dump_prefix_last(prefix), "runtime: <anonymous>");
368 : 0 : bf_dump_prefix_push(prefix);
369 [ # # ]: 0 : DUMP(bf_dump_prefix_last(prefix), "ops: %p", program->runtime.ops);
370 : 0 : bf_dump_prefix_pop(prefix);
371 : :
372 : 0 : bf_dump_prefix_pop(prefix);
373 : 0 : }
374 : :
375 : 21613 : static void _bf_program_fixup_insn(struct bpf_insn *insn,
376 : : enum bf_fixup_insn type, int32_t value)
377 : : {
378 [ + + - ]: 21613 : switch (type) {
379 : 4672 : case BF_FIXUP_INSN_OFF:
380 : : assert(!insn->off);
381 : : assert(value < SHRT_MAX);
382 : 4672 : insn->off = (int16_t)value;
383 : 4672 : break;
384 : 16941 : case BF_FIXUP_INSN_IMM:
385 : : assert(!insn->imm);
386 : 16941 : insn->imm = value;
387 : 16941 : break;
388 : 0 : default:
389 [ # # ]: 0 : bf_abort(
390 : : "unsupported fixup instruction type, this should not happen: %d",
391 : : type);
392 : : break;
393 : : }
394 : 21613 : }
395 : :
396 : 6821 : static int _bf_program_fixup(struct bf_program *program,
397 : : enum bf_fixup_type type)
398 : : {
399 : : assert(program);
400 : : assert(type >= 0 && type < _BF_FIXUP_TYPE_MAX);
401 : :
402 [ + - + + : 138834 : bf_list_foreach (&program->fixups, fixup_node) {
+ + ]
403 : : enum bf_fixup_insn insn_type = _BF_FIXUP_INSN_MAX;
404 : : int32_t value;
405 : : size_t offset;
406 : : struct bf_fixup *fixup = bf_list_node_get_data(fixup_node);
407 : : struct bpf_insn *insn;
408 : :
409 [ + + ]: 62596 : if (type != fixup->type)
410 : 40983 : continue;
411 : :
412 : 21613 : insn = bf_vector_get(&program->img, fixup->insn);
413 [ - + ]: 21613 : if (!insn) {
414 [ # # ]: 0 : return bf_err_r(-EINVAL,
415 : : "fixup references invalid instruction index %lu",
416 : : fixup->insn);
417 : : }
418 : :
419 [ + + + + : 21613 : switch (type) {
+ + - ]
420 : 4672 : case BF_FIXUP_TYPE_JMP_NEXT_RULE:
421 : : insn_type = BF_FIXUP_INSN_OFF;
422 : 4672 : value = (int)(program->img.size - fixup->insn - 1U);
423 : 4672 : break;
424 : 6994 : case BF_FIXUP_TYPE_COUNTERS_MAP_FD:
425 : : insn_type = BF_FIXUP_INSN_IMM;
426 : 6994 : value = program->handle->cmap->fd;
427 : 6994 : break;
428 : 1337 : case BF_FIXUP_TYPE_PRINTER_MAP_FD:
429 : : insn_type = BF_FIXUP_INSN_IMM;
430 : 1337 : value = program->handle->pmap->fd;
431 : 1337 : break;
432 : 30 : case BF_FIXUP_TYPE_LOG_MAP_FD:
433 : : insn_type = BF_FIXUP_INSN_IMM;
434 : 30 : value = program->handle->lmap->fd;
435 : 30 : break;
436 : 275 : case BF_FIXUP_TYPE_SET_MAP_FD: {
437 : : const struct bf_set_group *group =
438 : 275 : _bf_program_find_set_group(program, fixup->attr.set_ptr);
439 [ + - - + ]: 275 : if (!group || !group->map) {
440 [ # # # # : 0 : return bf_err_r(
# # ]
441 : : -ENOENT, "set map fixup: set '%s' not in any loaded group",
442 : : fixup->attr.set_ptr ?
443 : : (fixup->attr.set_ptr->name ?: "<anonymous>") :
444 : : "(null)");
445 : : }
446 : : insn_type = BF_FIXUP_INSN_IMM;
447 : 275 : value = group->map->fd;
448 : 275 : break;
449 : : }
450 : 8305 : case BF_FIXUP_ELFSTUB_CALL:
451 : : insn_type = BF_FIXUP_INSN_IMM;
452 : 8305 : offset = program->elfstubs_location[fixup->attr.elfstub_id] -
453 : 8305 : fixup->insn - 1;
454 [ - + ]: 8305 : if (offset >= INT_MAX)
455 [ # # ]: 0 : return bf_err_r(-EINVAL, "invalid ELF stub call offset");
456 : 8305 : value = (int32_t)offset;
457 : 8305 : break;
458 : 0 : default:
459 [ # # ]: 0 : bf_abort("unsupported fixup type, this should not happen: %d",
460 : : type);
461 : : break;
462 : : }
463 : :
464 : 21613 : _bf_program_fixup_insn(insn, insn_type, value);
465 : 21613 : bf_list_delete(&program->fixups, fixup_node);
466 : : }
467 : :
468 : : return 0;
469 : : }
470 : :
471 : 2767 : static int _bf_program_check_proto(struct bf_program *program,
472 : : enum bf_matcher_type type,
473 : : uint32_t *checked_layers)
474 : : {
475 : : const struct bf_matcher_meta *meta;
476 : :
477 : : assert(program);
478 : : assert(checked_layers);
479 : :
480 : 2767 : meta = bf_matcher_get_meta(type);
481 [ - + ]: 2767 : if (!meta)
482 [ # # ]: 0 : return bf_err_r(-EINVAL, "missing meta for matcher type %d", type);
483 : :
484 [ + + ]: 2767 : if (*checked_layers & BF_FLAG(meta->layer))
485 : : return 0;
486 : :
487 : 1875 : if (meta->layer == BF_MATCHER_LAYER_2 ||
488 [ + + ]: 1875 : meta->layer == BF_MATCHER_LAYER_3 ||
489 : : meta->layer == BF_MATCHER_LAYER_4) {
490 : 1228 : int r = bf_stub_rule_check_protocol(program, meta);
491 [ + - ]: 1228 : if (r)
492 : : return r;
493 : 1228 : *checked_layers |= BF_FLAG(meta->layer);
494 : : }
495 : :
496 : : return 0;
497 : : }
498 : :
499 : 1579 : static int _bf_program_generate_rule(struct bf_program *program,
500 : : struct bf_rule *rule)
501 : : {
502 : 1579 : uint32_t checked_layers = 0;
503 : : int ret_code;
504 : : int r = 0;
505 : :
506 : : assert(program);
507 : : assert(rule);
508 : : assert(program->runtime.ops->gen_inline_matcher);
509 : : assert(program->runtime.ops->gen_inline_log);
510 : :
511 [ + + ]: 1579 : if (rule->disabled)
512 : : return 0;
513 : :
514 [ + - + + : 8648 : bf_list_foreach (&rule->matchers, matcher_node) {
+ + ]
515 : : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
516 : :
517 [ + + ]: 2758 : if (bf_matcher_get_type(matcher) == BF_MATCHER_SET) {
518 : : const struct bf_set *set =
519 : 275 : bf_chain_get_set_for_matcher(program->runtime.chain, matcher);
520 : :
521 [ - + ]: 275 : if (!set) {
522 [ # # ]: 0 : return bf_err_r(-ENOENT, "rule %u references non-existent set",
523 : : rule->index);
524 : : }
525 : :
526 [ + + + - ]: 559 : for (size_t i = 0; i < set->n_comps && !r; ++i) {
527 : 284 : r = _bf_program_check_proto(program, set->key[i],
528 : : &checked_layers);
529 : : }
530 : : } else {
531 : 2483 : r = _bf_program_check_proto(program, bf_matcher_get_type(matcher),
532 : : &checked_layers);
533 : : }
534 : :
535 [ + - ]: 2758 : if (r)
536 : : return r;
537 : : }
538 : :
539 [ + - + + : 8648 : bf_list_foreach (&rule->matchers, matcher_node) {
+ + ]
540 : : struct bf_matcher *matcher = bf_list_node_get_data(matcher_node);
541 : :
542 : 2758 : r = program->runtime.ops->gen_inline_matcher(program, matcher);
543 [ + - ]: 2758 : if (r)
544 : : return r;
545 : : }
546 : :
547 [ + + ]: 1566 : if (bf_rule_mark_is_set(rule)) {
548 [ - + ]: 10 : if (!program->runtime.ops->gen_inline_set_mark) {
549 [ # # ]: 0 : return bf_err_r(-ENOTSUP, "set mark is not supported by %s",
550 : : program->runtime.chain->name);
551 : : }
552 : :
553 : 10 : r = program->runtime.ops->gen_inline_set_mark(program,
554 : : bf_rule_mark_get(rule));
555 [ - + ]: 10 : if (r) {
556 [ # # ]: 0 : return bf_err_r(r,
557 : : "failed to generate bytecode to set mark for '%s'",
558 : : program->runtime.chain->name);
559 : : }
560 : : }
561 : :
562 [ + + ]: 1566 : if (rule->log) {
563 : 42 : r = program->runtime.ops->gen_inline_log(program, rule);
564 [ + - ]: 42 : if (r)
565 : : return r;
566 : : }
567 : :
568 [ + + ]: 1566 : if (rule->has_counters) {
569 [ + - ]: 1510 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
570 [ + - ]: 1510 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, BF_PROG_CTX_OFF(arg)));
571 [ + - + - ]: 3020 : EMIT_LOAD_COUNTERS_FD_FIXUP(program, BPF_REG_2);
572 [ + - ]: 1510 : EMIT(program, BPF_MOV32_IMM(BPF_REG_3, rule->index));
573 [ + - ]: 1510 : EMIT_FIXUP_ELFSTUB(program, BF_ELFSTUB_UPDATE_COUNTERS);
574 : : }
575 : :
576 [ + + - + ]: 1566 : switch (rule->verdict) {
577 : 1514 : case BF_VERDICT_ACCEPT:
578 : : case BF_VERDICT_DROP:
579 : : case BF_VERDICT_NEXT:
580 : 1514 : r = program->runtime.ops->get_verdict(rule->verdict, &ret_code);
581 [ + - ]: 1514 : if (r)
582 : : return r;
583 [ + - ]: 1514 : EMIT(program, BPF_MOV64_IMM(BPF_REG_0, ret_code));
584 [ + - ]: 1514 : EMIT(program, BPF_EXIT_INSN());
585 : : break;
586 : 12 : case BF_VERDICT_REDIRECT:
587 [ + + ]: 12 : if (!program->runtime.ops->gen_inline_redirect) {
588 [ + - ]: 2 : return bf_err_r(-ENOTSUP, "redirect is not supported by %s hook",
589 : : bf_hook_to_str(program->runtime.chain->hook));
590 : : }
591 : 10 : r = program->runtime.ops->gen_inline_redirect(
592 : : program, rule->redirect_ifindex, rule->redirect_dir);
593 [ + + ]: 10 : if (r)
594 : : return r;
595 : : break;
596 : : case BF_VERDICT_CONTINUE:
597 : : // Fall through to next rule or default chain policy.
598 : : break;
599 : 0 : default:
600 [ # # ]: 0 : bf_abort("unsupported verdict, this should not happen: %d",
601 : : rule->verdict);
602 : : break;
603 : : }
604 : :
605 : 1563 : r = _bf_program_fixup(program, BF_FIXUP_TYPE_JMP_NEXT_RULE);
606 [ + - ]: 1563 : if (r)
607 [ # # ]: 0 : return bf_err_r(r, "failed to generate next rule fixups");
608 : :
609 : : return 0;
610 : : }
611 : :
612 : 1307 : static int _bf_program_generate_elfstubs(struct bf_program *program)
613 : : {
614 : : const struct bf_elfstub *elfstub;
615 : : size_t start_at;
616 : : int r;
617 : :
618 : : assert(program);
619 : :
620 [ + - + + : 36488 : bf_list_foreach (&program->fixups, fixup_node) {
+ + ]
621 : : struct bf_fixup *fixup = bf_list_node_get_data(fixup_node);
622 : 16937 : size_t off = program->img.size;
623 : :
624 [ + + ]: 16937 : if (fixup->type != BF_FIXUP_ELFSTUB_CALL)
625 : 8632 : continue;
626 : :
627 : : // Only generate each ELF stub once
628 [ + + ]: 8305 : if (program->elfstubs_location[fixup->attr.elfstub_id])
629 : 5699 : continue;
630 : :
631 [ - + ]: 2606 : bf_dbg("generate ELF stub for ID %d", fixup->attr.elfstub_id);
632 : :
633 : 2606 : elfstub = bf_ctx_get_elfstub(fixup->attr.elfstub_id);
634 [ - + ]: 2606 : if (!elfstub) {
635 [ # # ]: 0 : return bf_err_r(-ENOENT, "no ELF stub found for ID %d",
636 : : fixup->attr.elfstub_id);
637 : : }
638 : :
639 : 2606 : start_at = program->img.size;
640 : :
641 [ + + ]: 98598 : for (size_t i = 0; i < elfstub->ninsns; ++i) {
642 : 95992 : r = bf_program_emit(program, elfstub->insns[i]);
643 [ - + ]: 95992 : if (r)
644 [ # # ]: 0 : return bf_err_r(r, "failed to insert ELF stub instruction");
645 : : }
646 : :
647 [ + + - + : 7886 : bf_list_foreach (&elfstub->strs, pstr_node) {
+ + ]
648 : 1337 : _free_bf_fixup_ struct bf_fixup *fixup = NULL;
649 : : struct bf_printk_str *pstr = bf_list_node_get_data(pstr_node);
650 : 1337 : size_t insn_idx = start_at + pstr->insn_idx;
651 : : const struct bf_printer_msg *msg =
652 : 1337 : bf_printer_add_msg(program->printer, pstr->str);
653 : 1337 : struct bpf_insn ld_insn[2] = {
654 : : BPF_LD_MAP_FD(BPF_REG_1, 0),
655 : : };
656 : :
657 : 1337 : ld_insn[0].src_reg = BPF_PSEUDO_MAP_VALUE;
658 : 1337 : ld_insn[1].imm = (int)bf_printer_msg_offset(msg);
659 : :
660 : 1337 : r = bf_vector_set(&program->img, insn_idx, &ld_insn[0]);
661 [ - + ]: 1337 : if (r) {
662 [ # # ]: 0 : return bf_err_r(
663 : : r, "failed to set ELF stub instruction at index %lu",
664 : : insn_idx);
665 : : }
666 : 1337 : r = bf_vector_set(&program->img, insn_idx + 1, &ld_insn[1]);
667 [ - + ]: 1337 : if (r) {
668 [ # # ]: 0 : return bf_err_r(
669 : : r, "failed to set ELF stub instruction at index %lu",
670 : : insn_idx + 1);
671 : : }
672 : :
673 : 1337 : r = bf_fixup_new(&fixup, BF_FIXUP_TYPE_PRINTER_MAP_FD, insn_idx,
674 : : NULL);
675 [ + - ]: 1337 : if (r)
676 : : return r;
677 : :
678 : 1337 : r = bf_list_add_tail(&program->fixups, fixup);
679 [ + - ]: 1337 : if (r)
680 : : return r;
681 : :
682 : 1337 : TAKE_PTR(fixup);
683 : : }
684 : :
685 : 2606 : program->elfstubs_location[fixup->attr.elfstub_id] = off;
686 : : }
687 : :
688 : : return 0;
689 : : }
690 : :
691 : 4187 : int bf_program_emit_kfunc_call(struct bf_program *program, const char *name)
692 : : {
693 : : int r;
694 : :
695 : : assert(program);
696 : : assert(name);
697 : :
698 : 4187 : r = bf_btf_get_id(name);
699 [ + - ]: 4187 : if (r < 0)
700 : : return r;
701 : :
702 : 4187 : EMIT(program, ((struct bpf_insn) {.code = BPF_JMP | BPF_CALL,
703 : : .dst_reg = 0,
704 : : .src_reg = BPF_PSEUDO_KFUNC_CALL,
705 : : .off = 0,
706 : : .imm = r}));
707 : :
708 : : return 0;
709 : : }
710 : :
711 : 11987 : int bf_program_emit_fixup(struct bf_program *program, enum bf_fixup_type type,
712 : : struct bpf_insn insn, const union bf_fixup_attr *attr)
713 : : {
714 : 23974 : _free_bf_fixup_ struct bf_fixup *fixup = NULL;
715 : : int r;
716 : :
717 : : assert(program);
718 : :
719 [ + - ]: 11987 : EMIT(program, insn);
720 : :
721 : 11987 : r = bf_fixup_new(&fixup, type, program->img.size - 1, attr);
722 [ + - ]: 11987 : if (r)
723 : : return r;
724 : :
725 : 11987 : r = bf_list_add_tail(&program->fixups, fixup);
726 [ + - ]: 11987 : if (r)
727 : : return r;
728 : :
729 : 11987 : TAKE_PTR(fixup);
730 : :
731 : 11987 : return 0;
732 : : }
733 : :
734 : 8318 : int bf_program_emit_fixup_elfstub(struct bf_program *program,
735 : : enum bf_elfstub_id id)
736 : : {
737 : 16636 : _free_bf_fixup_ struct bf_fixup *fixup = NULL;
738 : : int r;
739 : :
740 : : assert(program);
741 : :
742 [ + - ]: 8318 : EMIT(program, BPF_CALL_REL(0));
743 : :
744 : 8318 : r = bf_fixup_new(&fixup, BF_FIXUP_ELFSTUB_CALL, program->img.size - 1,
745 : : NULL);
746 [ + - ]: 8318 : if (r)
747 : : return r;
748 : :
749 : 8318 : fixup->attr.elfstub_id = id;
750 : :
751 : 8318 : r = bf_list_add_tail(&program->fixups, fixup);
752 [ + - ]: 8318 : if (r)
753 : : return r;
754 : :
755 : 8318 : TAKE_PTR(fixup);
756 : :
757 : 8318 : return 0;
758 : : }
759 : :
760 : 1310 : int bf_program_generate(struct bf_program *program)
761 : : {
762 : 1310 : const struct bf_chain *chain = program->runtime.chain;
763 : : int ret_code;
764 : : int r;
765 : :
766 : 1310 : r = _bf_program_build_set_groups(program);
767 [ - + ]: 1310 : if (r)
768 [ # # ]: 0 : return bf_err_r(r, "failed to build set groups");
769 : :
770 : : // Save the program's argument into the context.
771 [ + - ]: 1310 : EMIT(program,
772 : : BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, BF_PROG_CTX_OFF(arg)));
773 : :
774 : : // Reset the protocol ID registers
775 [ + - ]: 1310 : EMIT(program, BPF_MOV64_IMM(BPF_REG_7, 0));
776 [ + - ]: 1310 : EMIT(program, BPF_MOV64_IMM(BPF_REG_8, 0));
777 : :
778 : : // If at least one rule logs the matched packets, populate ctx->log_map
779 [ + + ]: 1310 : if (program->runtime.chain->flags & BF_FLAG(BF_CHAIN_LOG)) {
780 [ + - + - ]: 60 : EMIT_LOAD_LOG_FD_FIXUP(program, BPF_REG_2);
781 [ + - ]: 30 : EMIT(program, BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2,
782 : : BF_PROG_CTX_OFF(log_map)));
783 : : }
784 : :
785 : : // Zeroing IPv6 extension headers
786 [ + + ]: 1310 : if (program->runtime.chain->flags & BF_FLAG(BF_CHAIN_STORE_NEXTHDR)) {
787 [ + - ]: 30 : EMIT(program, BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_7,
788 : : BF_PROG_CTX_OFF(ipv6_eh)));
789 : : }
790 : :
791 : 1310 : r = program->runtime.ops->gen_inline_prologue(program);
792 [ + - ]: 1310 : if (r)
793 : : return r;
794 : :
795 [ + + + + : 5772 : bf_list_foreach (&chain->rules, rule_node) {
+ + ]
796 : 1579 : r = _bf_program_generate_rule(program,
797 : : bf_list_node_get_data(rule_node));
798 [ + + ]: 1579 : if (r)
799 : : return r;
800 : : }
801 : :
802 : 1307 : r = program->runtime.ops->gen_inline_epilogue(program);
803 [ + - ]: 1307 : if (r)
804 : : return r;
805 : :
806 : : // Call the update counters function
807 : : /// @todo Allow chains to have no counters at all.
808 [ + - ]: 1307 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
809 [ + - ]: 1307 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, BF_PROG_CTX_OFF(arg)));
810 [ + - + - ]: 2614 : EMIT_LOAD_COUNTERS_FD_FIXUP(program, BPF_REG_2);
811 [ + - ]: 1307 : EMIT(program,
812 : : BPF_MOV32_IMM(BPF_REG_3, bf_program_chain_counter_idx(program)));
813 [ + - ]: 1307 : EMIT_FIXUP_ELFSTUB(program, BF_ELFSTUB_UPDATE_COUNTERS);
814 : :
815 : 1307 : r = program->runtime.ops->get_verdict(chain->policy, &ret_code);
816 [ + - ]: 1307 : if (r)
817 : : return r;
818 [ + - ]: 1307 : EMIT(program, BPF_MOV64_IMM(BPF_REG_0, ret_code));
819 [ + - ]: 1307 : EMIT(program, BPF_EXIT_INSN());
820 : :
821 : 1307 : r = _bf_program_generate_elfstubs(program);
822 [ + - ]: 1307 : if (r)
823 : : return r;
824 : :
825 : 1307 : r = _bf_program_fixup(program, BF_FIXUP_ELFSTUB_CALL);
826 [ - + ]: 1307 : if (r)
827 [ # # ]: 0 : return bf_err_r(r, "failed to generate ELF stub call fixups");
828 : :
829 : : return 0;
830 : : }
831 : :
832 : 1307 : static int _bf_program_load_printer_map(struct bf_program *program)
833 : : {
834 : 1307 : _cleanup_free_ void *pstr = NULL;
835 : : size_t pstr_len;
836 : 1307 : uint32_t key = 0;
837 : : int r;
838 : :
839 : : assert(program);
840 : :
841 : 1307 : r = bf_printer_assemble(program->printer, &pstr, &pstr_len);
842 [ - + ]: 1307 : if (r)
843 [ # # ]: 0 : return bf_err_r(r, "failed to assemble printer map string");
844 : :
845 : 1307 : r = bf_map_new(&program->handle->pmap, _BF_PRINTER_MAP_NAME,
846 : : BF_MAP_TYPE_PRINTER, sizeof(uint32_t), pstr_len, 1);
847 [ - + ]: 1307 : if (r)
848 [ # # ]: 0 : return bf_err_r(r, "failed to create the printer bf_map object");
849 : :
850 : 1307 : r = bf_map_set_elem(program->handle->pmap, &key, pstr);
851 [ - + ]: 1307 : if (r)
852 [ # # ]: 0 : return bf_err_r(r, "failed to set print map elem");
853 : :
854 : 1307 : r = _bf_program_fixup(program, BF_FIXUP_TYPE_PRINTER_MAP_FD);
855 [ - + ]: 1307 : if (r)
856 [ # # ]: 0 : return bf_err_r(r, "failed to fixup printer map FD");
857 : :
858 : : return 0;
859 : : }
860 : :
861 : 1307 : static int _bf_program_load_counters_map(struct bf_program *program)
862 : : {
863 : : int r;
864 : :
865 : : assert(program);
866 : :
867 : 1307 : r = bf_map_new(&program->handle->cmap, _BF_COUNTER_MAP_NAME,
868 : : BF_MAP_TYPE_COUNTERS, sizeof(uint32_t),
869 : : sizeof(struct bf_counter),
870 : 1307 : bf_list_size(&program->runtime.chain->rules) + 2);
871 [ - + ]: 1307 : if (r)
872 [ # # ]: 0 : return bf_err_r(r, "failed to create the counters bf_map object");
873 : :
874 : 1307 : r = _bf_program_fixup(program, BF_FIXUP_TYPE_COUNTERS_MAP_FD);
875 [ - + ]: 1307 : if (r)
876 [ # # ]: 0 : return bf_err_r(r, "failed to fixup counters map FD");
877 : :
878 : : return 0;
879 : : }
880 : :
881 : 1307 : static int _bf_program_load_log_map(struct bf_program *program)
882 : : {
883 : : int r;
884 : :
885 : : assert(program);
886 : :
887 : : // Do not create a log map if it's unused in the chain
888 [ + + ]: 1307 : if (!(program->runtime.chain->flags & BF_FLAG(BF_CHAIN_LOG)))
889 : : return 0;
890 : :
891 : 30 : r = bf_map_new(&program->handle->lmap, _BF_LOG_MAP_NAME, BF_MAP_TYPE_LOG, 0,
892 : : 0, _BF_LOG_MAP_SIZE);
893 [ - + ]: 30 : if (r)
894 [ # # ]: 0 : return bf_err_r(r, "failed to create the log bf_map object");
895 : :
896 : 30 : r = _bf_program_fixup(program, BF_FIXUP_TYPE_LOG_MAP_FD);
897 [ + - ]: 30 : if (r)
898 [ # # ]: 0 : return bf_err_r(r, "failed to fixup log map FD");
899 : :
900 : : return 0;
901 : : }
902 : :
903 : : /**
904 : : * @brief Load set maps, one BPF map per `bf_set_group`.
905 : : *
906 : : * Hash-keyed sets that share the same key format have already been
907 : : * grouped by `_bf_program_build_set_groups()`; LPM trie sets each occupy
908 : : * their own single-set group. Each group collapses to one BPF map whose
909 : : * value is a bitmask: bit `i` of byte `i / CHAR_BIT` identifies the `i`-th
910 : : * set in the group.
911 : : *
912 : : * Group ownership of the created maps is transferred to `handle->sets`;
913 : : * the `bf_set_group::map` pointer is a non-owning back-reference used by
914 : : * `_bf_program_fixup()` when resolving `BF_FIXUP_TYPE_SET_MAP_FD` fixups.
915 : : */
916 : 1307 : static int _bf_program_load_sets_maps(struct bf_program *new_prog)
917 : : {
918 : : char name[BPF_OBJ_NAME_LEN];
919 : : size_t map_idx = 0;
920 : : int r;
921 : :
922 : : assert(new_prog);
923 : :
924 [ + + + + : 3216 : bf_list_foreach (&new_prog->set_groups, group_node) {
+ + ]
925 : : struct bf_set_group *group = bf_list_node_get_data(group_node);
926 : : const struct bf_set *key_set =
927 : : bf_list_node_get_data(bf_list_get_head(&group->sets));
928 : : size_t n_sets = bf_list_size(&group->sets);
929 : 301 : size_t value_size = (n_sets + CHAR_BIT - 1) / CHAR_BIT;
930 : : size_t total_elems = 0;
931 : : size_t bit_idx = 0;
932 : 301 : _free_bf_map_ struct bf_map *new_map = NULL;
933 : : _cleanup_free_ uint8_t *value = NULL;
934 : : struct bf_map *map_ref;
935 : :
936 : : /* Keys present in multiple sets will share a single map entry, so
937 : : * the actual entry count may be smaller, but we can err on the
938 : : * safe side. */
939 [ + + ]: 632 : bf_list_foreach (&group->sets, set_node) {
940 : : const struct bf_set *set = bf_list_node_get_data(set_node);
941 [ + + ]: 316 : total_elems += bf_hashset_size(&set->elems);
942 : : }
943 : :
944 : 301 : (void)snprintf(name, BPF_OBJ_NAME_LEN, _BF_SET_MAP_PREFIX "%04x",
945 : 301 : (uint16_t)map_idx++);
946 : :
947 : 301 : r = bf_map_new_from_set(&new_map, name, key_set, total_elems,
948 : : value_size);
949 [ + - ]: 301 : if (r)
950 : : return r;
951 : :
952 : 301 : value = malloc(value_size);
953 [ + - ]: 301 : if (!value)
954 : : return -ENOMEM;
955 : :
956 [ + - + + ]: 933 : bf_list_foreach (&group->sets, set_node) {
957 : : const struct bf_set *set = bf_list_node_get_data(set_node);
958 : :
959 [ + - + + : 1974 : bf_hashset_foreach (&set->elems, elem) {
+ + ]
960 : 671 : memset(value, 0, value_size);
961 : 671 : (void)bf_bpf_map_lookup_elem(new_map->fd, elem->data, value);
962 : 671 : value[bit_idx / CHAR_BIT] |=
963 : 671 : (uint8_t)(1U << (bit_idx % CHAR_BIT));
964 : 671 : r = bf_map_set_elem(new_map, elem->data, value);
965 [ - + ]: 671 : if (r)
966 [ # # ]: 0 : return bf_err_r(r, "failed to add set element to the map");
967 : : }
968 [ + + ]: 316 : ++bit_idx;
969 : : }
970 : :
971 : 301 : map_ref = new_map;
972 : 301 : r = bf_list_push(&new_prog->handle->sets, (void **)&new_map);
973 [ + - ]: 301 : if (r)
974 : : return r;
975 : 301 : group->map = map_ref;
976 : : }
977 : :
978 : 1307 : return _bf_program_fixup(new_prog, BF_FIXUP_TYPE_SET_MAP_FD);
979 : : }
980 : :
981 : 1307 : int bf_program_load(struct bf_program *prog)
982 : : {
983 : : _cleanup_free_ char *log_buf = NULL;
984 : : int r;
985 : :
986 : : assert(prog);
987 : :
988 : 1307 : r = _bf_program_load_sets_maps(prog);
989 [ - + ]: 1307 : if (r)
990 [ # # ]: 0 : return bf_err_r(r, "failed to load the sets map");
991 : :
992 : 1307 : r = _bf_program_load_counters_map(prog);
993 [ - + ]: 1307 : if (r)
994 [ # # ]: 0 : return bf_err_r(r, "failed to load the counter map");
995 : :
996 : 1307 : r = _bf_program_load_printer_map(prog);
997 [ - + ]: 1307 : if (r)
998 [ # # ]: 0 : return bf_err_r(r, "failed to load the printer map");
999 : :
1000 : 1307 : r = _bf_program_load_log_map(prog);
1001 [ - + ]: 1307 : if (r)
1002 [ # # ]: 0 : return bf_err_r(r, "failed to load the log map");
1003 : :
1004 [ + + ]: 1307 : if (bf_ctx_is_verbose(BF_VERBOSE_DEBUG)) {
1005 : 2 : log_buf = malloc(_BF_LOG_BUF_SIZE);
1006 [ - + ]: 2 : if (!log_buf) {
1007 [ # # ]: 0 : return bf_err_r(-ENOMEM,
1008 : : "failed to allocate BPF_PROG_LOAD logs buffer");
1009 : : }
1010 : : }
1011 : :
1012 [ - + ]: 1307 : if (bf_ctx_is_verbose(BF_VERBOSE_BYTECODE))
1013 : 0 : bf_program_dump_bytecode(prog);
1014 : :
1015 [ + + ]: 2614 : r = bf_bpf_prog_load(prog->handle->prog_name,
1016 : 1307 : bf_hook_to_bpf_prog_type(prog->runtime.chain->hook),
1017 : : prog->img.data, prog->img.size,
1018 : 1307 : bf_hook_to_bpf_attach_type(prog->runtime.chain->hook),
1019 : : log_buf, log_buf ? _BF_LOG_BUF_SIZE : 0,
1020 : 1307 : bf_ctx_token(), &prog->handle->prog_fd);
1021 [ - + ]: 1307 : if (r) {
1022 [ # # # # ]: 0 : return bf_err_r(r, "failed to load bf_program (%lu insns):\n%s\nerrno:",
1023 : : prog->img.size, log_buf ? log_buf : "<NO LOG BUFFER>");
1024 : : }
1025 : :
1026 : : return r;
1027 : : }
1028 : :
1029 : 0 : int bf_program_get_counter(const struct bf_program *program,
1030 : : uint32_t counter_idx, struct bf_counter *counter)
1031 : : {
1032 : : assert(program);
1033 : : assert(counter);
1034 : :
1035 : 0 : return bf_handle_get_counter(program->handle, counter_idx, counter);
1036 : : }
1037 : :
1038 : 0 : int bf_cgen_set_counters(struct bf_program *program,
1039 : : const struct bf_counter *counters)
1040 : : {
1041 : : (void)program;
1042 : : (void)counters;
1043 : :
1044 : 0 : return -ENOTSUP;
1045 : : }
1046 : :
1047 : 1307 : size_t bf_program_chain_counter_idx(const struct bf_program *program)
1048 : : {
1049 : 1307 : return bf_list_size(&program->runtime.chain->rules);
1050 : : }
1051 : :
1052 : 4187 : size_t bf_program_error_counter_idx(const struct bf_program *program)
1053 : : {
1054 : 4187 : return bf_list_size(&program->runtime.chain->rules) + 1;
1055 : : }
|