Line data Source code
1 : /* SPDX-License-Identifier: GPL-2.0-only */
2 : /*
3 : * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
4 : */
5 :
6 : #include "bpfilter/cgen/matcher/meta.h"
7 :
8 : #include <linux/bpf.h>
9 : #include <linux/bpf_common.h>
10 : #include <linux/in.h> // NOLINT
11 : #include <linux/tcp.h>
12 : #include <linux/udp.h>
13 :
14 : #include <endian.h>
15 : #include <errno.h>
16 : #include <stddef.h>
17 : #include <stdint.h>
18 :
19 : #include "bpfilter/cgen/program.h"
20 : #include "bpfilter/cgen/swich.h"
21 : #include "core/logger.h"
22 : #include "core/matcher.h"
23 :
24 : #include "external/filter.h"
25 :
26 0 : static int _bf_matcher_generate_meta_ifindex(struct bf_program *program,
27 : const struct bf_matcher *matcher)
28 : {
29 0 : EMIT(program,
30 : BPF_LDX_MEM(BPF_H, BPF_REG_1, BPF_REG_10, BF_PROG_CTX_OFF(ifindex)));
31 0 : EMIT_FIXUP_JMP_NEXT_RULE(
32 : program,
33 : BPF_JMP_IMM(BPF_JNE, BPF_REG_1, *(uint32_t *)&matcher->payload, 0));
34 :
35 0 : return 0;
36 : }
37 :
38 0 : static int _bf_matcher_generate_meta_l3_proto(struct bf_program *program,
39 : const struct bf_matcher *matcher)
40 : {
41 0 : EMIT_FIXUP_JMP_NEXT_RULE(
42 : program, BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
43 : htobe16(*(uint16_t *)&matcher->payload), 0));
44 :
45 0 : return 0;
46 : }
47 :
48 0 : static int _bf_matcher_generate_meta_l4_proto(struct bf_program *program,
49 : const struct bf_matcher *matcher)
50 : {
51 0 : EMIT_FIXUP_JMP_NEXT_RULE(
52 : program,
53 : BPF_JMP_IMM(BPF_JNE, BPF_REG_8, *(uint8_t *)&matcher->payload, 0));
54 :
55 0 : return 0;
56 : }
57 :
58 : static int
59 0 : _bf_matcher_generate_meta_probability(struct bf_program *program,
60 : const struct bf_matcher *matcher)
61 : {
62 0 : uint8_t proba = *(uint8_t *)matcher->payload;
63 :
64 0 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32));
65 0 : EMIT_FIXUP_JMP_NEXT_RULE(
66 : program, BPF_JMP_IMM(BPF_JGT, BPF_REG_0,
67 : (int)(UINT32_MAX * (proba / 100.0)), 0));
68 :
69 0 : return 0;
70 : }
71 :
72 0 : static int _bf_matcher_generate_meta_port(struct bf_program *program,
73 : const struct bf_matcher *matcher)
74 : {
75 0 : _clean_bf_swich_ struct bf_swich swich;
76 : uint16_t *port = (uint16_t *)&matcher->payload;
77 : int r;
78 :
79 : // Load L4 header address into r6
80 0 : EMIT(program,
81 : BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, BF_PROG_CTX_OFF(l4_hdr)));
82 :
83 : // Get the packet's port into r1
84 0 : swich = bf_swich_get(program, BPF_REG_8);
85 0 : EMIT_SWICH_OPTION(&swich, IPPROTO_TCP,
86 : BPF_LDX_MEM(BPF_H, BPF_REG_1, BPF_REG_6,
87 : matcher->type == BF_MATCHER_META_SPORT ?
88 : offsetof(struct tcphdr, source) :
89 : offsetof(struct tcphdr, dest)));
90 0 : EMIT_SWICH_OPTION(&swich, IPPROTO_UDP,
91 : BPF_LDX_MEM(BPF_H, BPF_REG_1, BPF_REG_6,
92 : matcher->type == BF_MATCHER_META_SPORT ?
93 : offsetof(struct udphdr, source) :
94 : offsetof(struct udphdr, dest)));
95 0 : EMIT_SWICH_DEFAULT(&swich, BPF_MOV64_IMM(BPF_REG_1, 0));
96 :
97 0 : r = bf_swich_generate(&swich);
98 0 : if (r)
99 0 : return bf_err_r(r, "failed to generate swich for meta.(s|d)port");
100 :
101 : // If r1 == 0: no TCP nor UDP header found, jump to the next rule
102 0 : EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 0));
103 :
104 0 : switch (matcher->op) {
105 0 : case BF_MATCHER_EQ:
106 0 : EMIT_FIXUP_JMP_NEXT_RULE(
107 : program, BPF_JMP_IMM(BPF_JNE, BPF_REG_1, htobe16(*port), 0));
108 0 : break;
109 0 : case BF_MATCHER_NE:
110 0 : EMIT_FIXUP_JMP_NEXT_RULE(
111 : program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, htobe16(*port), 0));
112 0 : break;
113 0 : case BF_MATCHER_RANGE:
114 : /* Convert the big-endian value stored in the packet into a
115 : * little-endian value for x86 and arm before comparing it to the
116 : * reference value. This is a JLT/JGT comparison, we need to have the
117 : * MSB where the machine expects then. */
118 0 : EMIT(program, BPF_BSWAP(BPF_REG_1, 16));
119 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
120 : BPF_JMP_IMM(BPF_JLT, BPF_REG_1, port[0], 0));
121 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
122 : BPF_JMP_IMM(BPF_JGT, BPF_REG_1, port[1], 0));
123 0 : break;
124 0 : default:
125 0 : return bf_err_r(-EINVAL, "unknown matcher operator '%s' (%d)",
126 : bf_matcher_op_to_str(matcher->op), matcher->op);
127 : }
128 :
129 : return 0;
130 : }
131 :
132 0 : int bf_matcher_generate_meta(struct bf_program *program,
133 : const struct bf_matcher *matcher)
134 : {
135 : int r;
136 :
137 0 : switch (matcher->type) {
138 0 : case BF_MATCHER_META_IFINDEX:
139 0 : r = _bf_matcher_generate_meta_ifindex(program, matcher);
140 0 : break;
141 0 : case BF_MATCHER_META_L3_PROTO:
142 0 : r = _bf_matcher_generate_meta_l3_proto(program, matcher);
143 0 : break;
144 0 : case BF_MATCHER_META_L4_PROTO:
145 0 : r = _bf_matcher_generate_meta_l4_proto(program, matcher);
146 0 : break;
147 0 : case BF_MATCHER_META_PROBABILITY:
148 0 : r = _bf_matcher_generate_meta_probability(program, matcher);
149 0 : break;
150 0 : case BF_MATCHER_META_SPORT:
151 : case BF_MATCHER_META_DPORT:
152 0 : r = _bf_matcher_generate_meta_port(program, matcher);
153 0 : break;
154 0 : default:
155 0 : return bf_err_r(-EINVAL, "unknown matcher type %d", matcher->type);
156 : };
157 :
158 : if (r)
159 : return r;
160 :
161 : return 0;
162 : }
|