Branch data Line data Source code
1 : : /* SPDX-License-Identifier: GPL-2.0-only */
2 : : /*
3 : : * Copyright (c) Meta Platforms, Inc. and affiliates.
4 : : */
5 : :
6 : : #include "cgen/packet.h"
7 : :
8 : : #include <linux/bpf.h>
9 : : #include <linux/bpf_common.h>
10 : : #include <linux/in.h> // NOLINT
11 : : #include <linux/ipv6.h>
12 : :
13 : : #include <assert.h>
14 : : #include <errno.h>
15 : : #include <stdint.h>
16 : :
17 : : #include <bpfilter/chain.h>
18 : : #include <bpfilter/elfstub.h>
19 : : #include <bpfilter/helper.h>
20 : : #include <bpfilter/logger.h>
21 : : #include <bpfilter/matcher.h>
22 : : #include <bpfilter/rule.h>
23 : : #include <bpfilter/set.h>
24 : :
25 : : #include "cgen/matcher/cmp.h"
26 : : #include "cgen/matcher/meta.h"
27 : : #include "cgen/matcher/set.h"
28 : : #include "cgen/program.h"
29 : : #include "cgen/runtime.h"
30 : : #include "cgen/stub.h"
31 : : #include "filter.h"
32 : :
33 : : /**
34 : : * Packet matcher codegen follows a three-stage pipeline:
35 : : *
36 : : * 1. Protocol check: `_bf_program_generate_rule()` (in program.c) emits
37 : : * deduplicated guards for protocol-specific matchers before the matcher
38 : : * loop, so each protocol-specific guard is emitted at most once per rule.
39 : : *
40 : : * 2. Header + field load: the header base address is loaded into `R6`, then
41 : : * `_bf_matcher_pkt_load_field()` reads the target field into the specified
42 : : * register (and `reg+1` for 128-bit values such as IPv6 addresses).
43 : : *
44 : : * 3. Comparison: A `bf_cmp_*` function compares the value in the
45 : : * specified register against the matcher's reference payload.
46 : : */
47 : :
48 : : #define BF_IPV6_EH_HOPOPTS(x) ((x) << 0)
49 : : #define BF_IPV6_EH_ROUTING(x) ((x) << 1)
50 : : #define BF_IPV6_EH_FRAGMENT(x) ((x) << 2)
51 : : #define BF_IPV6_EH_AH(x) ((x) << 3)
52 : : #define BF_IPV6_EH_DSTOPTS(x) ((x) << 4)
53 : : #define BF_IPV6_EH_MH(x) ((x) << 5)
54 : :
55 : : /**
56 : : * @brief Load a packet field from the header into the specified register.
57 : : *
58 : : * `R6` must already point to the header base. For 128-bit fields (IPv6
59 : : * addresses), the low 8 bytes are loaded into `reg` and the high 8 bytes into
60 : : * `reg + 1`.
61 : : *
62 : : * @param program Program to emit into. Can't be NULL.
63 : : * @param meta Matcher metadata describing field offset and size. Can't be NULL.
64 : : * @param reg BPF register to load the value into.
65 : : * @return 0 on success, negative errno on error.
66 : : */
67 : 1911 : static int _bf_matcher_pkt_load_field(struct bf_program *program,
68 : : const struct bf_matcher_meta *meta,
69 : : int reg)
70 : : {
71 [ + + + - : 1911 : switch (meta->hdr_payload_size) {
+ - ]
72 : 604 : case 1:
73 : 604 : EMIT(program,
74 : : BPF_LDX_MEM(BPF_B, reg, BPF_REG_6, meta->hdr_payload_offset));
75 : : break;
76 : 698 : case 2:
77 : 698 : EMIT(program,
78 : : BPF_LDX_MEM(BPF_H, reg, BPF_REG_6, meta->hdr_payload_offset));
79 : : break;
80 : 269 : case 4:
81 : 269 : EMIT(program,
82 : : BPF_LDX_MEM(BPF_W, reg, BPF_REG_6, meta->hdr_payload_offset));
83 : : break;
84 : 0 : case 8:
85 : 0 : EMIT(program,
86 : : BPF_LDX_MEM(BPF_DW, reg, BPF_REG_6, meta->hdr_payload_offset));
87 : : break;
88 : 340 : case 16:
89 [ + - ]: 340 : EMIT(program,
90 : : BPF_LDX_MEM(BPF_DW, reg, BPF_REG_6, meta->hdr_payload_offset));
91 : 340 : EMIT(program, BPF_LDX_MEM(BPF_DW, reg + 1, BPF_REG_6,
92 : : meta->hdr_payload_offset + 8));
93 : : break;
94 : : default:
95 : : return -EINVAL;
96 : : }
97 : :
98 : : return 0;
99 : : }
100 : :
101 : 999 : static int _bf_matcher_pkt_prepare_header(struct bf_program *program,
102 : : enum bf_matcher_type type,
103 : : const struct bf_matcher_meta *meta)
104 : : {
105 : : assert(program);
106 : : assert(meta);
107 : :
108 [ + + ]: 999 : switch (type) {
109 : 278 : case BF_MATCHER_META_SPORT:
110 : : case BF_MATCHER_META_DPORT:
111 [ + - ]: 278 : EMIT(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_8, IPPROTO_TCP, 2));
112 [ + - ]: 278 : EMIT(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_8, IPPROTO_UDP, 1));
113 [ - + ]: 278 : EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_A(0));
114 : :
115 : 278 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10,
116 : : BF_PROG_CTX_OFF(l4_hdr)));
117 : : return 0;
118 : 721 : default:
119 : 721 : return bf_stub_load_header(program, meta, BPF_REG_6);
120 : : }
121 : : }
122 : :
123 : 1213 : static int _bf_matcher_pkt_load(struct bf_program *program,
124 : : const struct bf_matcher_meta *meta, int reg)
125 : : {
126 : : int r;
127 : :
128 : 1213 : r = bf_stub_load_header(program, meta, BPF_REG_6);
129 [ + - ]: 1213 : if (r)
130 : : return r;
131 : :
132 : 1213 : return _bf_matcher_pkt_load_field(program, meta, reg);
133 : : }
134 : :
135 : : /**
136 : : * @brief Generic load + value compare for matchers whose field size and offset
137 : : * are fully described by `_bf_matcher_metas`.
138 : : *
139 : : * Emits: header load -> field load -> `bf_cmp_value`.
140 : : *
141 : : * @param program Program to generate bytecode into. Can't be NULL.
142 : : * @param matcher Matcher to generate bytecode for. Can't be NULL.
143 : : * @param meta Matcher metadata describing field size and offset. Can't be NULL.
144 : : * @return 0 on success, negative errno on error.
145 : : */
146 : 653 : static int _bf_matcher_pkt_load_and_cmp(struct bf_program *program,
147 : : const struct bf_matcher *matcher,
148 : : const struct bf_matcher_meta *meta)
149 : : {
150 : : int r;
151 : :
152 : 653 : r = _bf_matcher_pkt_load(program, meta, BPF_REG_1);
153 [ + - ]: 653 : if (r)
154 : : return r;
155 : :
156 : 653 : return bf_cmp_value(program, matcher, bf_matcher_payload(matcher),
157 : 653 : meta->hdr_payload_size, BPF_REG_1);
158 : : }
159 : :
160 : 380 : static int _bf_matcher_pkt_generate_net(struct bf_program *program,
161 : : const struct bf_matcher *matcher,
162 : : const struct bf_matcher_meta *meta)
163 : : {
164 : 380 : const uint32_t prefixlen = *(const uint32_t *)bf_matcher_payload(matcher);
165 : 380 : const void *data =
166 : 380 : (const uint8_t *)bf_matcher_payload(matcher) + sizeof(uint32_t);
167 : : int r;
168 : :
169 : 380 : r = _bf_matcher_pkt_load(program, meta, BPF_REG_1);
170 [ + - ]: 380 : if (r)
171 : : return r;
172 : :
173 : 380 : return bf_cmp_masked_value(program, matcher, data, prefixlen,
174 : 380 : meta->hdr_payload_size, BPF_REG_1);
175 : : }
176 : :
177 : 698 : static int _bf_matcher_pkt_generate_port(struct bf_program *program,
178 : : const struct bf_matcher *matcher,
179 : : const struct bf_matcher_meta *meta)
180 : : {
181 : : int r;
182 : :
183 : 698 : r = _bf_matcher_pkt_prepare_header(program, bf_matcher_get_type(matcher),
184 : : meta);
185 [ + - ]: 698 : if (r)
186 : : return r;
187 : :
188 : 698 : r = _bf_matcher_pkt_load_field(program, meta, BPF_REG_1);
189 [ + - ]: 698 : if (r)
190 : : return r;
191 : :
192 [ + + ]: 698 : if (bf_matcher_get_op(matcher) == BF_MATCHER_RANGE) {
193 : 190 : uint16_t *ports = (uint16_t *)bf_matcher_payload(matcher);
194 : : /* Convert the big-endian value stored in the packet into a
195 : : * little-endian value for x86 and arm before comparing it to the
196 : : * reference value. This is a JLT/JGT comparison, we need to have the
197 : : * MSB where the machine expects then. */
198 [ + - ]: 190 : EMIT(program, BPF_BSWAP(BPF_REG_1, 16));
199 : 190 : return bf_cmp_range(program, matcher, ports[0], ports[1], BPF_REG_1);
200 : : }
201 : :
202 : 508 : return bf_cmp_value(program, matcher, bf_matcher_payload(matcher),
203 : 508 : meta->hdr_payload_size, BPF_REG_1);
204 : : }
205 : :
206 : : static int
207 : 160 : _bf_matcher_pkt_generate_tcp_flags(struct bf_program *program,
208 : : const struct bf_matcher *matcher,
209 : : const struct bf_matcher_meta *meta)
210 : : {
211 : : int r;
212 : :
213 : 160 : r = _bf_matcher_pkt_load(program, meta, BPF_REG_1);
214 [ + - ]: 160 : if (r)
215 : : return r;
216 : :
217 [ + + - ]: 160 : switch (bf_matcher_get_op(matcher)) {
218 : 100 : case BF_MATCHER_ANY:
219 : : case BF_MATCHER_ALL:
220 : 100 : return bf_cmp_bitfield(program, matcher,
221 : 100 : *(uint8_t *)bf_matcher_payload(matcher),
222 : : BPF_REG_1);
223 : 60 : case BF_MATCHER_EQ:
224 : 60 : return bf_cmp_value(program, matcher, bf_matcher_payload(matcher),
225 : 60 : meta->hdr_payload_size, BPF_REG_1);
226 : 0 : default:
227 [ # # ]: 0 : return bf_err_r(-EINVAL, "unsupported operator %d",
228 : : bf_matcher_get_op(matcher));
229 : : }
230 : : }
231 : :
232 : : static int
233 : 100 : _bf_matcher_pkt_generate_ip6_nexthdr(struct bf_program *program,
234 : : const struct bf_matcher *matcher)
235 : : {
236 : 100 : const uint8_t ehdr = *(uint8_t *)bf_matcher_payload(matcher);
237 : : uint8_t eh_mask;
238 : : uint8_t jmp_op;
239 : :
240 : 100 : jmp_op = bf_cmp_get_jmp_ins(matcher);
241 : :
242 [ + + ]: 100 : switch (ehdr) {
243 : 20 : case IPPROTO_HOPOPTS:
244 : : case IPPROTO_ROUTING:
245 : : case IPPROTO_DSTOPTS:
246 : : case IPPROTO_FRAGMENT:
247 : : case IPPROTO_AH:
248 : : case IPPROTO_MH:
249 [ + + ]: 20 : eh_mask = (BF_IPV6_EH_HOPOPTS(ehdr == IPPROTO_HOPOPTS) |
250 [ + - ]: 10 : BF_IPV6_EH_ROUTING(ehdr == IPPROTO_ROUTING) |
251 [ - + ]: 20 : BF_IPV6_EH_FRAGMENT(ehdr == IPPROTO_FRAGMENT) |
252 [ - + ]: 20 : BF_IPV6_EH_AH(ehdr == IPPROTO_AH) |
253 [ + - ]: 40 : BF_IPV6_EH_DSTOPTS(ehdr == IPPROTO_DSTOPTS) |
254 : : BF_IPV6_EH_MH(ehdr == IPPROTO_MH));
255 [ + - ]: 20 : EMIT(program, BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10,
256 : : BF_PROG_CTX_OFF(ipv6_eh)));
257 [ + - ]: 20 : EMIT(program, BPF_ALU64_IMM(BPF_AND, BPF_REG_1, eh_mask));
258 : :
259 : : /* Extension header check: after AND with eh_mask, a non-zero
260 : : * result means the header is present. For EQ (jmp_op=JNE),
261 : : * we want to skip if zero (not present), so we use the
262 : : * *opposite* opcode here. */
263 [ - + - + ]: 20 : EMIT_FIXUP_JMP_NEXT_RULE(
264 : : program, BPF_JMP_IMM(jmp_op == BPF_JNE ? BPF_JEQ : BPF_JNE,
265 : : BPF_REG_1, 0, 0));
266 : 20 : break;
267 : 80 : default:
268 : : /* check l4 protocols using `BPF_REG_8` */
269 [ - + ]: 80 : EMIT_FIXUP_JMP_NEXT_RULE(program,
270 : : BPF_JMP_IMM(jmp_op, BPF_REG_8, ehdr, 0));
271 : 80 : break;
272 : : }
273 : :
274 : : return 0;
275 : : }
276 : :
277 : 20 : static int _bf_matcher_pkt_generate_ip4_dscp(struct bf_program *program,
278 : : const struct bf_matcher *matcher,
279 : : const struct bf_matcher_meta *meta)
280 : : {
281 : : uint8_t dscp;
282 : : int r;
283 : :
284 : 20 : r = _bf_matcher_pkt_load(program, meta, BPF_REG_1);
285 [ + - ]: 20 : if (r)
286 : : return r;
287 : :
288 : 20 : dscp = *(uint8_t *)bf_matcher_payload(matcher);
289 : :
290 : : /* IPv4 TOS byte: [DSCP 6b] [ECN 2b]. Mask with 0xfc to isolate
291 : : * the 6-bit DSCP field, then compare against dscp << 2. */
292 [ + - ]: 20 : EMIT(program, BPF_ALU64_IMM(BPF_AND, BPF_REG_1, 0xfc));
293 : :
294 [ - + ]: 20 : EMIT_FIXUP_JMP_NEXT_RULE(program,
295 : : BPF_JMP_IMM(bf_cmp_get_jmp_ins(matcher), BPF_REG_1,
296 : : (uint8_t)dscp << 2, 0));
297 : :
298 : 20 : return 0;
299 : : }
300 : :
301 : 20 : static int _bf_matcher_pkt_generate_ip6_dscp(struct bf_program *program,
302 : : const struct bf_matcher *matcher,
303 : : const struct bf_matcher_meta *meta)
304 : : {
305 : : uint8_t dscp;
306 : : int r;
307 : :
308 : 20 : r = bf_stub_load_header(program, meta, BPF_REG_6);
309 [ + - ]: 20 : if (r)
310 : : return r;
311 : :
312 : 20 : dscp = *(uint8_t *)bf_matcher_payload(matcher);
313 : :
314 : : /* IPv6 DSCP occupies bits 6-11 of the header (big-endian view):
315 : : * [version 4b] [DSCP 6b] [ECN 2b] [flow label (high 4b)]
316 : : * Load 2 bytes, convert to big-endian, mask with 0x0fc0 to isolate
317 : : * the 6-bit DSCP field, then compare against dscp << 6. */
318 [ + - ]: 20 : EMIT(program, BPF_LDX_MEM(BPF_H, BPF_REG_1, BPF_REG_6, 0));
319 [ + - ]: 20 : EMIT(program, BPF_ENDIAN(BPF_TO_BE, BPF_REG_1, 16));
320 [ + - ]: 20 : EMIT(program, BPF_ALU64_IMM(BPF_AND, BPF_REG_1, 0x0fc0));
321 : :
322 [ - + ]: 20 : EMIT_FIXUP_JMP_NEXT_RULE(program,
323 : : BPF_JMP_IMM(bf_cmp_get_jmp_ins(matcher), BPF_REG_1,
324 : : (uint16_t)dscp << 6, 0));
325 : :
326 : 20 : return 0;
327 : : }
328 : :
329 : 307 : static int _bf_matcher_pkt_generate_set(struct bf_program *program,
330 : : const struct bf_matcher *matcher)
331 : : {
332 : : const struct bf_set *set;
333 : : size_t offset = 0;
334 : : int r;
335 : :
336 : : assert(program);
337 : : assert(matcher);
338 : :
339 : 307 : set = bf_chain_get_set_for_matcher(program->runtime.chain, matcher);
340 [ - + ]: 307 : if (!set) {
341 [ # # ]: 0 : return bf_err_r(-ENOENT, "set #%u not found in %s",
342 : : *(uint32_t *)bf_matcher_payload(matcher),
343 : : program->runtime.chain->name);
344 : : }
345 : :
346 [ + + ]: 307 : if (set->use_trie) {
347 : 41 : const struct bf_matcher_meta *meta = bf_matcher_get_meta(set->key[0]);
348 : :
349 [ - + ]: 41 : if (!meta) {
350 [ # # ]: 0 : return bf_err_r(-EINVAL, "missing meta for '%s'",
351 : : bf_matcher_type_to_str(set->key[0]));
352 : : }
353 : :
354 : 41 : r = bf_stub_load_header(program, meta, BPF_REG_6);
355 [ - + ]: 41 : if (r)
356 [ # # ]: 0 : return bf_err_r(r, "failed to load protocol header into BPF_REG_6");
357 : :
358 : 41 : return bf_set_generate_trie_lookup(
359 : 41 : program, matcher, meta->hdr_payload_offset, meta->hdr_payload_size);
360 : : }
361 : :
362 [ + + ]: 567 : for (size_t i = 0; i < set->n_comps; ++i) {
363 : 301 : enum bf_matcher_type type = set->key[i];
364 : 301 : const struct bf_matcher_meta *meta = bf_matcher_get_meta(type);
365 : :
366 [ - + ]: 301 : if (!meta) {
367 [ # # ]: 0 : return bf_err_r(-EINVAL, "missing meta for '%s'",
368 : : bf_matcher_type_to_str(type));
369 : : }
370 : :
371 : 301 : r = _bf_matcher_pkt_prepare_header(program, type, meta);
372 [ - + ]: 301 : if (r)
373 [ # # ]: 0 : return bf_err_r(r, "failed to load protocol header into BPF_REG_6");
374 : :
375 : 301 : r = bf_stub_stx_payload(program, meta, offset);
376 [ - + ]: 301 : if (r) {
377 [ # # ]: 0 : return bf_err_r(r,
378 : : "failed to generate bytecode to load packet data");
379 : : }
380 : :
381 : 301 : offset += meta->hdr_payload_size;
382 : : }
383 : :
384 : 266 : return bf_set_generate_map_lookup(program, matcher, BF_PROG_SCR_OFF(0));
385 : : }
386 : :
387 : 2661 : int bf_packet_gen_inline_matcher(struct bf_program *program,
388 : : const struct bf_matcher *matcher)
389 : : {
390 : : const struct bf_matcher_meta *meta;
391 : :
392 : : assert(program);
393 : : assert(matcher);
394 : :
395 : 2661 : meta = bf_matcher_get_meta(bf_matcher_get_type(matcher));
396 : :
397 [ + - + + : 2661 : switch (bf_matcher_get_type(matcher)) {
+ + + + +
+ - ]
398 : 323 : case BF_MATCHER_META_IFACE:
399 : : case BF_MATCHER_META_L3_PROTO:
400 : : case BF_MATCHER_META_L4_PROTO:
401 : : case BF_MATCHER_META_PROBABILITY:
402 : : case BF_MATCHER_META_FLOW_PROBABILITY:
403 : 323 : return bf_matcher_generate_meta(program, matcher);
404 : 0 : case BF_MATCHER_META_MARK:
405 : : case BF_MATCHER_META_FLOW_HASH:
406 : : case BF_MATCHER_META_PID:
407 [ # # ]: 0 : return bf_err_r(-ENOTSUP,
408 : : "matcher '%s' is not supported by this flavor",
409 : : bf_matcher_type_to_str(bf_matcher_get_type(matcher)));
410 : 20 : case BF_MATCHER_IP4_DSCP:
411 : 20 : return _bf_matcher_pkt_generate_ip4_dscp(program, matcher, meta);
412 : 653 : case BF_MATCHER_IP4_SADDR:
413 : : case BF_MATCHER_IP4_DADDR:
414 : : case BF_MATCHER_IP4_PROTO:
415 : : case BF_MATCHER_IP6_SADDR:
416 : : case BF_MATCHER_IP6_DADDR:
417 : : case BF_MATCHER_ICMP_TYPE:
418 : : case BF_MATCHER_ICMP_CODE:
419 : : case BF_MATCHER_ICMPV6_TYPE:
420 : : case BF_MATCHER_ICMPV6_CODE:
421 : 653 : return _bf_matcher_pkt_load_and_cmp(program, matcher, meta);
422 : 380 : case BF_MATCHER_IP4_SNET:
423 : : case BF_MATCHER_IP4_DNET:
424 : : case BF_MATCHER_IP6_SNET:
425 : : case BF_MATCHER_IP6_DNET:
426 : 380 : return _bf_matcher_pkt_generate_net(program, matcher, meta);
427 : 698 : case BF_MATCHER_TCP_SPORT:
428 : : case BF_MATCHER_TCP_DPORT:
429 : : case BF_MATCHER_UDP_SPORT:
430 : : case BF_MATCHER_UDP_DPORT:
431 : : case BF_MATCHER_META_SPORT:
432 : : case BF_MATCHER_META_DPORT:
433 : 698 : return _bf_matcher_pkt_generate_port(program, matcher, meta);
434 : 160 : case BF_MATCHER_TCP_FLAGS:
435 : 160 : return _bf_matcher_pkt_generate_tcp_flags(program, matcher, meta);
436 : 100 : case BF_MATCHER_IP6_NEXTHDR:
437 : 100 : return _bf_matcher_pkt_generate_ip6_nexthdr(program, matcher);
438 : 20 : case BF_MATCHER_IP6_DSCP:
439 : 20 : return _bf_matcher_pkt_generate_ip6_dscp(program, matcher, meta);
440 : 307 : case BF_MATCHER_SET:
441 : 307 : return _bf_matcher_pkt_generate_set(program, matcher);
442 : 0 : default:
443 [ # # ]: 0 : return bf_err_r(-EINVAL, "unknown matcher type %d",
444 : : bf_matcher_get_type(matcher));
445 : : }
446 : : }
447 : :
448 : 41 : int bf_packet_gen_inline_log(struct bf_program *program,
449 : : const struct bf_rule *rule)
450 : : {
451 : : assert(program);
452 : : assert(rule);
453 : :
454 [ + - ]: 41 : EMIT(program, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10));
455 [ + - ]: 41 : EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, BF_PROG_CTX_OFF(arg)));
456 [ + - ]: 41 : EMIT(program, BPF_MOV64_IMM(BPF_REG_2, rule->index));
457 : :
458 [ + + ]: 41 : if (rule->log == BF_FLAG(BF_LOG_OPT_5_TUPLE)) {
459 [ + - ]: 2 : EMIT(program, BPF_MOV64_IMM(BPF_REG_3, rule->verdict));
460 : :
461 : : // Pack l3_proto and l4_proto
462 [ + - ]: 2 : EMIT(program, BPF_MOV64_REG(BPF_REG_4, BPF_REG_7));
463 [ + - ]: 2 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_4, 16));
464 [ + - ]: 2 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_4, BPF_REG_8));
465 : :
466 : 2 : EMIT_FIXUP_ELFSTUB(program, BF_ELFSTUB_PKT_5_TUPLE_LOG);
467 : : } else {
468 [ + + + - ]: 76 : EMIT(program, BPF_MOV64_IMM(BPF_REG_3, rule->log == BF_LOG_OPT_DEFAULT ?
469 : : BF_LOG_PACKET_HEADERS :
470 : : rule->log));
471 [ + - ]: 39 : EMIT(program, BPF_MOV64_IMM(BPF_REG_4, rule->verdict));
472 : :
473 : : // Pack l3_proto and l4_proto
474 [ + - ]: 39 : EMIT(program, BPF_MOV64_REG(BPF_REG_5, BPF_REG_7));
475 [ + - ]: 39 : EMIT(program, BPF_ALU64_IMM(BPF_LSH, BPF_REG_5, 16));
476 [ + - ]: 39 : EMIT(program, BPF_ALU64_REG(BPF_OR, BPF_REG_5, BPF_REG_8));
477 : :
478 : 39 : EMIT_FIXUP_ELFSTUB(program, BF_ELFSTUB_PKT_LOG);
479 : : }
480 : :
481 : : return 0;
482 : : }
|