bmv2
Designing your own switch target with bmv2
Loading...
Searching...
No Matches
parser.h
Go to the documentation of this file.
1/* Copyright 2013-present Barefoot Networks, Inc.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16/*
17 * Antonin Bas (antonin@barefootnetworks.com)
18 *
19 */
20
22
23#ifndef BM_BM_SIM_PARSER_H_
24#define BM_BM_SIM_PARSER_H_
25
26#include <memory>
27#include <mutex>
28#include <string>
29#include <type_traits>
30#include <utility>
31#include <vector>
32
33#include <cassert>
34
35#include "phv_forward.h"
36#include "named_p4object.h"
37#include "stateful.h"
38#include "parser_error.h"
39
40namespace bm {
41
42class Packet;
43
44class BoolExpression;
45class ArithExpression;
46class ActionFn;
47
48class Checksum;
49
50struct field_t {
51 header_id_t header;
52 int offset;
53
54 static field_t make(header_id_t header, int offset) {
55 field_t field = {header, offset};
56 return field;
57 }
58};
59
60struct ParserLookAhead {
61 int byte_offset;
62 int bit_offset;
63 int bitwidth;
64 size_t nbytes;
65
66 static ParserLookAhead make(int offset, int bitwidth);
67
68 void peek(const char *data, ByteContainer *res) const;
69};
70
71static_assert(std::is_pod<ParserLookAhead>::value,
72 "ParserLookAhead is used in union and we assume it is POD data");
73
74struct ParserOp {
75 virtual ~ParserOp() {}
76 virtual void operator()(Packet *pkt, const char *data,
77 size_t *bytes_parsed) const = 0;
78};
79
80// need to be in the header because of unit tests
81template <typename T>
82struct ParserOpSet : ParserOp {
83 field_t dst;
84 T src;
85
86 ParserOpSet(header_id_t header, int offset, const T &src)
87 : dst({header, offset}), src(src) { }
88
89 void operator()(Packet *pkt, const char *data,
90 size_t *bytes_parsed) const override;
91};
92
93class ParseSwitchKeyBuilder {
94 public:
95 void push_back_field(header_id_t header, int field_offset, int bitwidth);
96
97 void push_back_stack_field(header_stack_id_t header_stack, int field_offset,
98 int bitwidth);
99
100 void push_back_union_stack_field(header_union_stack_id_t header_union_stack,
101 size_t header_offset, int field_offset,
102 int bitwidth);
103
104 void push_back_lookahead(int offset, int bitwidth);
105
106 std::vector<int> get_bitwidths() const;
107
108 void operator()(const PHV &phv, const char *data, ByteContainer *key) const;
109
110 private:
111 struct Entry {
112 enum {FIELD, LOOKAHEAD, STACK_FIELD, UNION_STACK_FIELD} tag{};
113 // I made sure ParserLookAhead was POD data so that it is easy to use in the
114 // union
115 union {
116 field_t field;
117 struct {
118 header_union_stack_id_t header_union_stack;
119 size_t header_offset;
120 int offset;
121 } union_stack_field;
122 ParserLookAhead lookahead;
123 };
124
125 static Entry make_field(header_id_t header, int offset);
126
127 static Entry make_stack_field(header_stack_id_t header_stack, int offset);
128
129 static Entry make_union_stack_field(
130 header_union_stack_id_t header_union_stack, size_t header_offset,
131 int offset);
132
133 static Entry make_lookahead(int offset, int bitwidth);
134 };
135
136 std::vector<Entry> entries{};
137 std::vector<int> bitwidths{};
138};
139
140class ParseState;
141
142class ParseVSetIface {
143 public:
144 enum ErrorCode {
145 SUCCESS = 0,
146 INVALID_PARSE_VSET_NAME,
147 ERROR
148 };
149
150 virtual ~ParseVSetIface() { }
151
152 virtual void add(const ByteContainer &v) = 0;
153
154 virtual void remove(const ByteContainer &v) = 0;
155
156 virtual bool contains(const ByteContainer &v) const = 0;
157
158 virtual void clear() = 0;
159
160 virtual size_t size() const = 0;
161};
162
163class ParseVSetBase; // forward declaration
164
165// Note that as of today, all parse states using a vset have to be configured
166// before any value can be added to a vset (otherwise the value won't be present
167// in the shadow copies). This can be easily changed if the need arises in the
168// future.
169class ParseVSet : public NamedP4Object, public ParseVSetIface {
170 template <typename P> friend class ParseSwitchCaseVSet;
171 using Lock = std::unique_lock<std::mutex>;
172
173 public:
174 using ErrorCode = ParseVSetIface::ErrorCode;
175
176 ParseVSet(const std::string &name, p4object_id_t id,
177 size_t compressed_bitwidth);
178
179 ~ParseVSet();
180
181 void add(const ByteContainer &v) override;
182
183 void remove(const ByteContainer &v) override;
184
185 bool contains(const ByteContainer &v) const override;
186
187 void clear() override;
188
189 size_t size() const override;
190
191 size_t get_compressed_bitwidth() const;
192
193 std::vector<ByteContainer> get() const;
194
195 private:
196 void add_shadow(ParseVSetIface *shadow);
197
198 size_t compressed_bitwidth;
199 // if no mutex we can have inconsistent results accross shadow copies (some
200 // have a value, others don't. This mutex is never requested by the dataplane
201 // (each shadow has its own).
202 mutable std::mutex shadows_mutex{};
203 std::vector<ParseVSetIface *> shadows{};
204 std::unique_ptr<ParseVSetBase> base;
205};
206
207class ParseSwitchCaseIface {
208 public:
209 virtual ~ParseSwitchCaseIface() { }
210
211 virtual bool match(const ByteContainer &input,
212 const ParseState **state) const = 0;
213
214 static std::unique_ptr<ParseSwitchCaseIface>
215 make_case(const ByteContainer &key, const ParseState *next_state);
216
217 static std::unique_ptr<ParseSwitchCaseIface>
218 make_case_with_mask(const ByteContainer &key, const ByteContainer &mask,
219 const ParseState *next_state);
220
221 static std::unique_ptr<ParseSwitchCaseIface>
222 make_case_vset(ParseVSet *vset, std::vector<int> bitwidths,
223 const ParseState *next_state);
224
225 static std::unique_ptr<ParseSwitchCaseIface>
226 make_case_vset_with_mask(ParseVSet *vset, const ByteContainer &mask,
227 std::vector<int> bitwidths,
228 const ParseState *next_state);
229};
230
231class ParseState : public NamedP4Object {
232 public:
233 ParseState(const std::string &name, p4object_id_t id);
234
235 void add_extract(header_id_t header);
236 void add_extract_VL(header_id_t header,
237 const ArithExpression &field_length_expr,
238 size_t max_header_bytes);
239 void add_extract_to_stack(header_stack_id_t header_stack);
240 void add_extract_to_stack_VL(header_stack_id_t header_stack,
241 const ArithExpression &field_length_expr,
242 size_t max_header_bytes);
243 void add_extract_to_union_stack(header_union_stack_id_t header_union_stack,
244 size_t header_offset);
245 void add_extract_to_union_stack_VL(header_union_stack_id_t header_union_stack,
246 size_t header_offset,
247 const ArithExpression &field_length_expr,
248 size_t max_header_bytes);
249
250 void add_set_from_field(header_id_t dst_header, int dst_offset,
251 header_id_t src_header, int src_offset);
252
253 void add_set_from_data(header_id_t dst_header, int dst_offset,
254 const Data &src);
255
256 void add_set_from_lookahead(header_id_t dst_header, int dst_offset,
257 int src_offset, int src_bitwidth);
258
259 void add_set_from_expression(header_id_t dst_header, int dst_offset,
260 const ArithExpression &expr);
261
262 void add_verify(const BoolExpression &condition,
263 const ArithExpression &error_expr);
264
265 void add_method_call(ActionFn *action_fn);
266
267 void add_shift(size_t shift_bytes);
268
269 void add_advance_from_data(const Data &shift_bits);
270 void add_advance_from_expression(const ArithExpression &shift_bits);
271 void add_advance_from_field(header_id_t shift_header, int shift_offset);
272
273 void set_key_builder(const ParseSwitchKeyBuilder &builder);
274
275 void add_switch_case(const ByteContainer &key, const ParseState *next_state);
276 void add_switch_case(int nbytes_key, const char *key,
277 const ParseState *next_state);
278
279 void add_switch_case_with_mask(const ByteContainer &key,
280 const ByteContainer &mask,
281 const ParseState *next_state);
282 void add_switch_case_with_mask(int nbytes_key, const char *key,
283 const char *mask,
284 const ParseState *next_state);
285
286 void add_switch_case_vset(ParseVSet *vset, const ParseState *next_state);
287
288 void add_switch_case_vset_with_mask(ParseVSet *vset,
289 const ByteContainer &mask,
290 const ParseState *next_state);
291
292 void set_default_switch_case(const ParseState *default_next);
293
294 int expected_switch_case_key_size() const;
295
296 // Copy constructor
297 ParseState(const ParseState& other) = delete;
298
299 // Copy assignment operator
300 ParseState &operator =(const ParseState& other) = delete;
301
302 // Move constructor
303 ParseState(ParseState&& other)= default;
304
305 // Move assignment operator
306 ParseState &operator =(ParseState &&other) = default;
307
308 const ParseState *operator()(Packet *pkt, const char *data,
309 size_t *bytes_parsed) const;
310
311 private:
312 const ParseState *find_next_state(Packet *pkt, const char *data,
313 size_t *bytes_parsed) const;
314
315 std::vector<std::unique_ptr<ParserOp> > parser_ops{};
316 RegisterSync register_sync{};
317 bool has_switch;
318 ParseSwitchKeyBuilder key_builder{};
319 std::vector<std::unique_ptr<ParseSwitchCaseIface> > parser_switch{};
320 const ParseState *default_next_state{nullptr};
321};
322
324class Parser : public NamedP4Object {
325 public:
326 Parser(const std::string &name, p4object_id_t id,
327 const ErrorCodeMap *error_codes);
328
329 void set_init_state(const ParseState *state);
330
331 void add_checksum(const Checksum *checksum);
332
338 void parse(Packet *pkt) const;
339
341 Parser(const Parser &other) = delete;
343 Parser &operator=(const Parser &other) = delete;
344
346 Parser(Parser &&other) = delete;
348 Parser &operator=(Parser &&other) /*noexcept*/ = delete;
349
350 private:
351 void verify_checksums(Packet *pkt) const;
352
353 const ParseState *init_state;
354 const ErrorCodeMap *error_codes;
355 const ErrorCode no_error;
356 std::vector<const Checksum *> checksums{};
357};
358
359} // namespace bm
360
361#endif // BM_BM_SIM_PARSER_H_
A bi-directional map between error codes and their P4 names.
Definition parser_error.h:66
Definition parser_error.h:35
Definition named_p4object.h:39
Definition packet.h:98
Implements a P4 parser.
Definition parser.h:324
void parse(Packet *pkt) const
Parser(Parser &&other)=delete
Deleted move constructor (const member variables)
Parser(const Parser &other)=delete
Deleted copy constructor.
Parser & operator=(const Parser &other)=delete
Deleted copy assignment operator.
Parser & operator=(Parser &&other)=delete
Deleted move assignment operator (const member variables)