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 "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/logger.h>
20 : #include <bpfilter/matcher.h>
21 :
22 : #include "cgen/program.h"
23 : #include "cgen/swich.h"
24 : #include "filter.h"
25 :
26 : /** @todo Add support for input and output interface filtering based on the
27 : * program's hook. */
28 0 : static int _bf_matcher_generate_meta_iface(struct bf_program *program,
29 : const struct bf_matcher *matcher)
30 : {
31 0 : EMIT(program,
32 : BPF_LDX_MEM(BPF_H, BPF_REG_1, BPF_REG_10, BF_PROG_CTX_OFF(ifindex)));
33 0 : EMIT_FIXUP_JMP_NEXT_RULE(
34 : program, BPF_JMP_IMM(BPF_JNE, BPF_REG_1,
35 : *(uint32_t *)bf_matcher_payload(matcher), 0));
36 :
37 0 : return 0;
38 : }
39 :
40 0 : static int _bf_matcher_generate_meta_l3_proto(struct bf_program *program,
41 : const struct bf_matcher *matcher)
42 : {
43 0 : EMIT_FIXUP_JMP_NEXT_RULE(
44 : program,
45 : BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
46 : htobe16(*(uint16_t *)bf_matcher_payload(matcher)), 0));
47 :
48 0 : return 0;
49 : }
50 :
51 0 : static int _bf_matcher_generate_meta_l4_proto(struct bf_program *program,
52 : const struct bf_matcher *matcher)
53 : {
54 0 : EMIT_FIXUP_JMP_NEXT_RULE(
55 : program,
56 : BPF_JMP_IMM(bf_matcher_get_op(matcher) == BF_MATCHER_EQ ? BPF_JNE :
57 : BPF_JEQ,
58 : BPF_REG_8, *(uint8_t *)bf_matcher_payload(matcher), 0));
59 :
60 0 : return 0;
61 : }
62 :
63 : static int
64 0 : _bf_matcher_generate_meta_probability(struct bf_program *program,
65 : const struct bf_matcher *matcher)
66 : {
67 0 : uint8_t proba = *(uint8_t *)bf_matcher_payload(matcher);
68 :
69 0 : EMIT(program, BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32));
70 0 : EMIT_FIXUP_JMP_NEXT_RULE(
71 : program,
72 : BPF_JMP_IMM(BPF_JGT, BPF_REG_0,
73 : (uint32_t)((uint64_t)UINT32_MAX * (proba / 100.0)), 0));
74 :
75 0 : return 0;
76 : }
77 :
78 0 : static int _bf_matcher_generate_meta_port(struct bf_program *program,
79 : const struct bf_matcher *matcher)
80 : {
81 0 : _clean_bf_swich_ struct bf_swich swich;
82 0 : uint16_t *port = (uint16_t *)bf_matcher_payload(matcher);
83 : int r;
84 :
85 : // Load L4 header address into r6
86 0 : EMIT(program,
87 : BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, BF_PROG_CTX_OFF(l4_hdr)));
88 :
89 : // Get the packet's port into r1
90 0 : swich = bf_swich_get(program, BPF_REG_8);
91 0 : EMIT_SWICH_OPTION(
92 : &swich, IPPROTO_TCP,
93 : BPF_LDX_MEM(BPF_H, BPF_REG_1, BPF_REG_6,
94 : bf_matcher_get_type(matcher) == BF_MATCHER_META_SPORT ?
95 : offsetof(struct tcphdr, source) :
96 : offsetof(struct tcphdr, dest)));
97 0 : EMIT_SWICH_OPTION(
98 : &swich, IPPROTO_UDP,
99 : BPF_LDX_MEM(BPF_H, BPF_REG_1, BPF_REG_6,
100 : bf_matcher_get_type(matcher) == BF_MATCHER_META_SPORT ?
101 : offsetof(struct udphdr, source) :
102 : offsetof(struct udphdr, dest)));
103 0 : EMIT_SWICH_DEFAULT(&swich, BPF_MOV64_IMM(BPF_REG_1, 0));
104 :
105 0 : r = bf_swich_generate(&swich);
106 0 : if (r)
107 0 : return bf_err_r(r, "failed to generate swich for meta.(s|d)port");
108 :
109 : // If r1 == 0: no TCP nor UDP header found, jump to the next rule
110 0 : EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 0));
111 :
112 0 : switch (bf_matcher_get_op(matcher)) {
113 0 : case BF_MATCHER_EQ:
114 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
115 : BPF_JMP_IMM(BPF_JNE, BPF_REG_1, *port, 0));
116 0 : break;
117 0 : case BF_MATCHER_NE:
118 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
119 : BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, *port, 0));
120 0 : break;
121 0 : case BF_MATCHER_RANGE:
122 : /* Convert the big-endian value stored in the packet into a
123 : * little-endian value for x86 and arm before comparing it to the
124 : * reference value. This is a JLT/JGT comparison, we need to have the
125 : * MSB where the machine expects then. */
126 0 : EMIT(program, BPF_BSWAP(BPF_REG_1, 16));
127 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
128 : BPF_JMP_IMM(BPF_JLT, BPF_REG_1, port[0], 0));
129 0 : EMIT_FIXUP_JMP_NEXT_RULE(program,
130 : BPF_JMP_IMM(BPF_JGT, BPF_REG_1, port[1], 0));
131 0 : break;
132 0 : default:
133 0 : return bf_err_r(-EINVAL, "unknown matcher operator '%s' (%d)",
134 : bf_matcher_op_to_str(bf_matcher_get_op(matcher)),
135 : bf_matcher_get_op(matcher));
136 : }
137 :
138 : return 0;
139 : }
140 :
141 0 : int bf_matcher_generate_meta(struct bf_program *program,
142 : const struct bf_matcher *matcher)
143 : {
144 : int r;
145 :
146 0 : switch (bf_matcher_get_type(matcher)) {
147 0 : case BF_MATCHER_META_IFACE:
148 0 : r = _bf_matcher_generate_meta_iface(program, matcher);
149 0 : break;
150 0 : case BF_MATCHER_META_L3_PROTO:
151 0 : r = _bf_matcher_generate_meta_l3_proto(program, matcher);
152 0 : break;
153 0 : case BF_MATCHER_META_L4_PROTO:
154 0 : r = _bf_matcher_generate_meta_l4_proto(program, matcher);
155 0 : break;
156 0 : case BF_MATCHER_META_PROBABILITY:
157 0 : r = _bf_matcher_generate_meta_probability(program, matcher);
158 0 : break;
159 0 : case BF_MATCHER_META_SPORT:
160 : case BF_MATCHER_META_DPORT:
161 0 : r = _bf_matcher_generate_meta_port(program, matcher);
162 0 : break;
163 0 : default:
164 0 : return bf_err_r(-EINVAL, "unknown matcher type %d",
165 : bf_matcher_get_type(matcher));
166 : };
167 :
168 : if (r)
169 : return r;
170 :
171 : return 0;
172 : }
|