bmv2
Designing your own switch target with bmv2
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 
40 namespace bm {
41 
42 class Packet;
43 
44 class BoolExpression;
45 class ArithExpression;
46 class ActionFn;
47 
48 class Checksum;
49 
50 struct 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 
60 struct 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 
71 static_assert(std::is_pod<ParserLookAhead>::value,
72  "ParserLookAhead is used in union and we assume it is POD data");
73 
74 struct 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
81 template <typename T>
82 struct 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 
93 class 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 
140 class ParseState;
141 
142 class 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 
163 class 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.
169 class 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 
207 class 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 
231 class 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 
324 class 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_
bm::NamedP4Object
Definition: named_p4object.h:39
bm::Parser::operator=
Parser & operator=(const Parser &other)=delete
Deleted copy assignment operator.
named_p4object.h
bm::Packet
Definition: packet.h:98
bm::ErrorCode
Definition: parser_error.h:35
stateful.h
parser_error.h
bm::Parser::parse
void parse(Packet *pkt) const
bm::Parser
Implements a P4 parser.
Definition: parser.h:324
bm::ErrorCodeMap
A bi-directional map between error codes and their P4 names.
Definition: parser_error.h:66