mavtables  0.2.1
MAVLink router and firewall.
test_ConfigParser.cpp
Go to the documentation of this file.
1 // MAVLink router and firewall.
2 // Copyright (C) 2018 Michael R. Shannon <mrshannon.aerospace@gmail.com>
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 
17 
18 #include <memory>
19 
20 #include <catch.hpp>
21 #include <fakeit.hpp>
22 #include <pegtl.hpp>
23 
24 #include "config_grammar.hpp"
25 #include "ConfigParser.hpp"
26 #include "MAVAddress.hpp"
27 #include "PacketVersion2.hpp"
28 #include "parse_tree.hpp"
29 #include "utility.hpp"
30 
31 #include "common.hpp"
32 #include "common_Packet.hpp"
33 
34 
35 TEST_CASE("'init_chains' initializes a map of chain names to empty chains"
36  "[ConfigParser]")
37 {
38  SECTION("default chain listed first")
39  {
40  tao::pegtl::string_input<> in(
41  "chain default {}\n"
42  "chain first_chain {}\n"
43  "chain second_chain {}\n"
44  "chain third_chain {}\n", "");
45  auto root = config::parse(in);
46  REQUIRE(root != nullptr);
47  auto chains = init_chains(*root);
48  REQUIRE(chains.count("default") == 0);
49  REQUIRE(chains.count("first_chain") == 1);
50  REQUIRE(chains.count("second_chain") == 1);
51  REQUIRE(chains.count("third_chain") == 1);
52  REQUIRE(chains["first_chain"] != nullptr);
53  REQUIRE(chains["second_chain"] != nullptr);
54  REQUIRE(chains["third_chain"] != nullptr);
55  REQUIRE(
56  str(*chains["first_chain"]) ==
57  "chain first_chain {\n"
58  "}");
59  REQUIRE(
60  str(*chains["second_chain"]) ==
61  "chain second_chain {\n"
62  "}");
63  REQUIRE(
64  str(*chains["third_chain"]) ==
65  "chain third_chain {\n"
66  "}");
67  }
68  SECTION("default chain listed last")
69  {
70  tao::pegtl::string_input<> in(
71  "chain first_chain {}\n"
72  "chain second_chain {}\n"
73  "chain third_chain {}\n"
74  "chain default {}\n", "");
75  auto root = config::parse(in);
76  REQUIRE(root != nullptr);
77  auto chains = init_chains(*root);
78  REQUIRE(chains.count("default") == 0);
79  REQUIRE(chains.count("first_chain") == 1);
80  REQUIRE(chains.count("second_chain") == 1);
81  REQUIRE(chains.count("third_chain") == 1);
82  REQUIRE(chains["first_chain"] != nullptr);
83  REQUIRE(chains["second_chain"] != nullptr);
84  REQUIRE(chains["third_chain"] != nullptr);
85  REQUIRE(
86  str(*chains["first_chain"]) ==
87  "chain first_chain {\n"
88  "}");
89  REQUIRE(
90  str(*chains["second_chain"]) ==
91  "chain second_chain {\n"
92  "}");
93  REQUIRE(
94  str(*chains["third_chain"]) ==
95  "chain third_chain {\n"
96  "}");
97  }
98 }
99 
100 
101 TEST_CASE("'parse_action' parses an action from the given AST node.",
102  "[ConfigParser]")
103 {
104  std::map<std::string, std::shared_ptr<Chain>> chains;
105  chains["some_chain"] = std::make_shared<Chain>("some_chain");
106  SECTION("Accept action.")
107  {
108  tao::pegtl::string_input<> in(
109  "chain default {\n"
110  " accept;\n"
111  "}\n", "");
112  auto root = config::parse(in);
113  REQUIRE(root != nullptr);
114  REQUIRE_FALSE(root->children.empty());
115  REQUIRE(root->children[0] != nullptr);
116  REQUIRE_FALSE(root->children[0]->children.empty());
117  REQUIRE(root->children[0]->children[0] != nullptr);
118  auto action = parse_action(
119  *root->children[0]->children[0], {}, {}, chains);
120  REQUIRE(str(*action) == "accept");
121  }
122  SECTION("Accept action, with priority.")
123  {
124  tao::pegtl::string_input<> in(
125  "chain default {\n"
126  " accept with priority -1;\n"
127  "}\n", "");
128  auto root = config::parse(in);
129  REQUIRE(root != nullptr);
130  REQUIRE_FALSE(root->children.empty());
131  REQUIRE(root->children[0] != nullptr);
132  REQUIRE_FALSE(root->children[0]->children.empty());
133  REQUIRE(root->children[0]->children[0] != nullptr);
134  auto action = parse_action(
135  *root->children[0]->children[0], -1, {}, chains);
136  REQUIRE(str(*action) == "accept with priority -1");
137  }
138  SECTION("Accept action, with condition.")
139  {
140  tao::pegtl::string_input<> in(
141  "chain default {\n"
142  " accept if PING from 192.168 to 127.0/8;\n"
143  "}\n", "");
144  auto root = config::parse(in);
145  REQUIRE(root != nullptr);
146  REQUIRE_FALSE(root->children.empty());
147  REQUIRE(root->children[0] != nullptr);
148  REQUIRE_FALSE(root->children[0]->children.empty());
149  REQUIRE(root->children[0]->children[0] != nullptr);
150  auto action =
151  parse_action(
152  *root->children[0]->children[0], {},
153  If().type("PING").from("192.168").to("127.0/8"), chains);
154  REQUIRE(str(*action) == "accept if PING from 192.168 to 127.0/8");
155  }
156  SECTION("Accept action, with priority and condition.")
157  {
158  tao::pegtl::string_input<> in(
159  "chain default {\n"
160  " accept with priority -1 if PING from 192.168 to 127.0/8;\n"
161  "}\n", "");
162  auto root = config::parse(in);
163  REQUIRE(root != nullptr);
164  REQUIRE_FALSE(root->children.empty());
165  REQUIRE(root->children[0] != nullptr);
166  REQUIRE_FALSE(root->children[0]->children.empty());
167  REQUIRE(root->children[0]->children[0] != nullptr);
168  auto action =
169  parse_action(
170  *root->children[0]->children[0], -1,
171  If().type("PING").from("192.168").to("127.0/8"), chains);
172  REQUIRE(
173  str(*action) ==
174  "accept with priority -1 if PING from 192.168 to 127.0/8");
175  }
176  SECTION("Reject action.")
177  {
178  tao::pegtl::string_input<> in(
179  "chain default {\n"
180  " reject;\n"
181  "}\n", "");
182  auto root = config::parse(in);
183  REQUIRE(root != nullptr);
184  REQUIRE_FALSE(root->children.empty());
185  REQUIRE(root->children[0] != nullptr);
186  REQUIRE_FALSE(root->children[0]->children.empty());
187  REQUIRE(root->children[0]->children[0] != nullptr);
188  auto action = parse_action(
189  *root->children[0]->children[0], {}, {}, chains);
190  REQUIRE(str(*action) == "reject");
191  }
192  SECTION("Reject action, with priority (priority ignored for reject).")
193  {
194  tao::pegtl::string_input<> in(
195  "chain default {\n"
196  " reject with priority -1;\n"
197  "}\n", "");
198  auto root = config::parse(in);
199  REQUIRE(root != nullptr);
200  REQUIRE_FALSE(root->children.empty());
201  REQUIRE(root->children[0] != nullptr);
202  REQUIRE_FALSE(root->children[0]->children.empty());
203  REQUIRE(root->children[0]->children[0] != nullptr);
204  auto action = parse_action(
205  *root->children[0]->children[0], -1, {}, chains);
206  REQUIRE(str(*action) == "reject");
207  }
208  SECTION("Reject action, with condition.")
209  {
210  tao::pegtl::string_input<> in(
211  "chain default {\n"
212  " reject if PING from 192.168 to 127.0/8;\n"
213  "}\n", "");
214  auto root = config::parse(in);
215  REQUIRE(root != nullptr);
216  REQUIRE_FALSE(root->children.empty());
217  REQUIRE(root->children[0] != nullptr);
218  REQUIRE_FALSE(root->children[0]->children.empty());
219  REQUIRE(root->children[0]->children[0] != nullptr);
220  auto action =
221  parse_action(
222  *root->children[0]->children[0], {},
223  If().type("PING").from("192.168").to("127.0/8"), chains);
224  REQUIRE(str(*action) == "reject if PING from 192.168 to 127.0/8");
225  }
226  SECTION("Reject action, with priority and condition "
227  "(priority ignored for reject).")
228  {
229  tao::pegtl::string_input<> in(
230  "chain default {\n"
231  " reject if PING from 192.168 to 127.0/8;\n"
232  "}\n", "");
233  auto root = config::parse(in);
234  REQUIRE(root != nullptr);
235  REQUIRE_FALSE(root->children.empty());
236  REQUIRE(root->children[0] != nullptr);
237  REQUIRE_FALSE(root->children[0]->children.empty());
238  REQUIRE(root->children[0]->children[0] != nullptr);
239  auto action =
240  parse_action(
241  *root->children[0]->children[0], -1,
242  If().type("PING").from("192.168").to("127.0/8"), chains);
243  REQUIRE(
244  str(*action) ==
245  "reject if PING from 192.168 to 127.0/8");
246  }
247  SECTION("Call action.")
248  {
249  tao::pegtl::string_input<> in(
250  "chain default {\n"
251  " call some_chain;\n"
252  "}\n", "");
253  auto root = config::parse(in);
254  REQUIRE(root != nullptr);
255  REQUIRE_FALSE(root->children.empty());
256  REQUIRE(root->children[0] != nullptr);
257  REQUIRE_FALSE(root->children[0]->children.empty());
258  REQUIRE(root->children[0]->children[0] != nullptr);
259  auto action = parse_action(
260  *root->children[0]->children[0], {}, {}, chains);
261  REQUIRE(str(*action) == "call some_chain");
262  }
263  SECTION("Call action, throws and error if calling the default chain.")
264  {
265  tao::pegtl::string_input<> in(
266  "chain some_chain {\n"
267  " call default;\n"
268  "}\n", "");
269  auto root = config::parse(in);
270  REQUIRE(root != nullptr);
271  REQUIRE_FALSE(root->children.empty());
272  REQUIRE(root->children[0] != nullptr);
273  REQUIRE_FALSE(root->children[0]->children.empty());
274  REQUIRE(root->children[0]->children[0] != nullptr);
275  REQUIRE_THROWS_AS(
276  parse_action(*root->children[0]->children[0], {}, {}, chains),
277  std::invalid_argument);
278  REQUIRE_THROWS_WITH(
279  parse_action(*root->children[0]->children[0], {}, {}, chains),
280  "cannot 'call' the default chain");
281  }
282  SECTION("Call action, with priority.")
283  {
284  tao::pegtl::string_input<> in(
285  "chain default {\n"
286  " call some_chain with priority -1;\n"
287  "}\n", "");
288  auto root = config::parse(in);
289  REQUIRE(root != nullptr);
290  REQUIRE_FALSE(root->children.empty());
291  REQUIRE(root->children[0] != nullptr);
292  REQUIRE_FALSE(root->children[0]->children.empty());
293  REQUIRE(root->children[0]->children[0] != nullptr);
294  auto action = parse_action(
295  *root->children[0]->children[0], -1, {}, chains);
296  REQUIRE(str(*action) == "call some_chain with priority -1");
297  }
298  SECTION("Call action, with condition.")
299  {
300  tao::pegtl::string_input<> in(
301  "chain default {\n"
302  " call some_chain if PING from 192.168 to 127.0/8;\n"
303  "}\n", "");
304  auto root = config::parse(in);
305  REQUIRE(root != nullptr);
306  REQUIRE_FALSE(root->children.empty());
307  REQUIRE(root->children[0] != nullptr);
308  REQUIRE_FALSE(root->children[0]->children.empty());
309  REQUIRE(root->children[0]->children[0] != nullptr);
310  auto action =
311  parse_action(
312  *root->children[0]->children[0], {},
313  If().type("PING").from("192.168").to("127.0/8"), chains);
314  REQUIRE(
315  str(*action) == "call some_chain if PING from 192.168 to 127.0/8");
316  }
317  SECTION("Call action, with priority and condition.")
318  {
319  tao::pegtl::string_input<> in(
320  "chain default {\n"
321  " call some_chain with priority -1 "
322  "if PING from 192.168 to 127.0/8;\n"
323  "}\n", "");
324  auto root = config::parse(in);
325  REQUIRE(root != nullptr);
326  REQUIRE_FALSE(root->children.empty());
327  REQUIRE(root->children[0] != nullptr);
328  REQUIRE_FALSE(root->children[0]->children.empty());
329  REQUIRE(root->children[0]->children[0] != nullptr);
330  auto action =
331  parse_action(
332  *root->children[0]->children[0], -1,
333  If().type("PING").from("192.168").to("127.0/8"), chains);
334  REQUIRE(
335  str(*action) ==
336  "call some_chain with priority -1 if PING from 192.168 to 127.0/8");
337  }
338  SECTION("GoTo action.")
339  {
340  tao::pegtl::string_input<> in(
341  "chain default {\n"
342  " goto some_chain;\n"
343  "}\n", "");
344  auto root = config::parse(in);
345  REQUIRE(root != nullptr);
346  REQUIRE_FALSE(root->children.empty());
347  REQUIRE(root->children[0] != nullptr);
348  REQUIRE_FALSE(root->children[0]->children.empty());
349  REQUIRE(root->children[0]->children[0] != nullptr);
350  auto action = parse_action(
351  *root->children[0]->children[0], {}, {}, chains);
352  REQUIRE(str(*action) == "goto some_chain");
353  }
354  SECTION("GoTo action, throws and error if going to the default chain.")
355  {
356  tao::pegtl::string_input<> in(
357  "chain some_chain {\n"
358  " goto default;\n"
359  "}\n", "");
360  auto root = config::parse(in);
361  REQUIRE(root != nullptr);
362  REQUIRE_FALSE(root->children.empty());
363  REQUIRE(root->children[0] != nullptr);
364  REQUIRE_FALSE(root->children[0]->children.empty());
365  REQUIRE(root->children[0]->children[0] != nullptr);
366  REQUIRE_THROWS_AS(
367  parse_action(*root->children[0]->children[0], {}, {}, chains),
368  std::invalid_argument);
369  REQUIRE_THROWS_WITH(
370  parse_action(*root->children[0]->children[0], {}, {}, chains),
371  "cannot 'goto' the default chain");
372  }
373  SECTION("GoTo action, with priority.")
374  {
375  tao::pegtl::string_input<> in(
376  "chain default {\n"
377  " goto some_chain with priority -1;\n"
378  "}\n", "");
379  auto root = config::parse(in);
380  REQUIRE(root != nullptr);
381  REQUIRE_FALSE(root->children.empty());
382  REQUIRE(root->children[0] != nullptr);
383  REQUIRE_FALSE(root->children[0]->children.empty());
384  REQUIRE(root->children[0]->children[0] != nullptr);
385  auto action =
386  parse_action(*root->children[0]->children[0], -1, {}, chains);
387  REQUIRE(str(*action) == "goto some_chain with priority -1");
388  }
389  SECTION("GoTo action, with condition.")
390  {
391  tao::pegtl::string_input<> in(
392  "chain default {\n"
393  " goto some_chain if PING from 192.168 to 127.0/8;\n"
394  "}\n", "");
395  auto root = config::parse(in);
396  REQUIRE(root != nullptr);
397  REQUIRE_FALSE(root->children.empty());
398  REQUIRE(root->children[0] != nullptr);
399  REQUIRE_FALSE(root->children[0]->children.empty());
400  REQUIRE(root->children[0]->children[0] != nullptr);
401  auto action =
402  parse_action(
403  *root->children[0]->children[0], {},
404  If().type("PING").from("192.168").to("127.0/8"), chains);
405  REQUIRE(
406  str(*action) == "goto some_chain if PING from 192.168 to 127.0/8");
407  }
408  SECTION("GoTo action, with priority and condition.")
409  {
410  tao::pegtl::string_input<> in(
411  "chain default {\n"
412  " goto some_chain with priority -1 "
413  "if PING from 192.168 to 127.0/8;\n"
414  "}\n", "");
415  auto root = config::parse(in);
416  REQUIRE(root != nullptr);
417  REQUIRE_FALSE(root->children.empty());
418  REQUIRE(root->children[0] != nullptr);
419  REQUIRE_FALSE(root->children[0]->children.empty());
420  REQUIRE(root->children[0]->children[0] != nullptr);
421  auto action =
422  parse_action(
423  *root->children[0]->children[0], -1,
424  If().type("PING").from("192.168").to("127.0/8"), chains);
425  REQUIRE(
426  str(*action) ==
427  "goto some_chain with priority -1 if PING from 192.168 to 127.0/8");
428  }
429 }
430 
431 
432 TEST_CASE("'parse_condition' parses a condition from the given AST node.",
433  "[ConfigParser]")
434 {
435  SECTION("Condition with type.")
436  {
437  tao::pegtl::string_input<> in(
438  "chain default {\n"
439  " accept if PING;\n"
440  "}\n", "");
441  auto root = config::parse(in);
442  REQUIRE(root != nullptr);
443  REQUIRE_FALSE(root->children.empty());
444  REQUIRE(root->children[0] != nullptr);
445  REQUIRE_FALSE(root->children[0]->children.empty());
446  REQUIRE(root->children[0]->children[0] != nullptr);
447  REQUIRE_FALSE(root->children[0]->children[0]->children.empty());
448  REQUIRE(root->children[0]->children[0]->children[0] != nullptr);
449  auto condition = parse_condition(
450  *root->children[0]->children[0]->children[0]);
451  REQUIRE(str(condition) == "if PING");
452  }
453  SECTION("Condition source.")
454  {
455  tao::pegtl::string_input<> in(
456  "chain default {\n"
457  " accept if from 192.168;\n"
458  "}\n", "");
459  auto root = config::parse(in);
460  REQUIRE(root != nullptr);
461  REQUIRE_FALSE(root->children.empty());
462  REQUIRE(root->children[0] != nullptr);
463  REQUIRE_FALSE(root->children[0]->children.empty());
464  REQUIRE(root->children[0]->children[0] != nullptr);
465  REQUIRE_FALSE(root->children[0]->children[0]->children.empty());
466  REQUIRE(root->children[0]->children[0]->children[0] != nullptr);
467  auto condition = parse_condition(
468  *root->children[0]->children[0]->children[0]);
469  REQUIRE(str(condition) == "if from 192.168");
470  }
471  SECTION("Condition destination.")
472  {
473  tao::pegtl::string_input<> in(
474  "chain default {\n"
475  " accept if to 128.0/8;\n"
476  "}\n", "");
477  auto root = config::parse(in);
478  REQUIRE(root != nullptr);
479  REQUIRE_FALSE(root->children.empty());
480  REQUIRE(root->children[0] != nullptr);
481  REQUIRE_FALSE(root->children[0]->children.empty());
482  REQUIRE(root->children[0]->children[0] != nullptr);
483  REQUIRE_FALSE(root->children[0]->children[0]->children.empty());
484  REQUIRE(root->children[0]->children[0]->children[0] != nullptr);
485  auto condition = parse_condition(
486  *root->children[0]->children[0]->children[0]);
487  REQUIRE(str(condition) == "if to 128.0/8");
488  }
489  SECTION("Condition source and destination.")
490  {
491  tao::pegtl::string_input<> in(
492  "chain default {\n"
493  " accept if from 192.168 to 127.0/8;\n"
494  "}\n", "");
495  auto root = config::parse(in);
496  REQUIRE(root != nullptr);
497  REQUIRE_FALSE(root->children.empty());
498  REQUIRE(root->children[0] != nullptr);
499  REQUIRE_FALSE(root->children[0]->children.empty());
500  REQUIRE(root->children[0]->children[0] != nullptr);
501  REQUIRE_FALSE(root->children[0]->children[0]->children.empty());
502  REQUIRE(root->children[0]->children[0]->children[0] != nullptr);
503  auto condition = parse_condition(
504  *root->children[0]->children[0]->children[0]);
505  REQUIRE(str(condition) == "if from 192.168 to 127.0/8");
506  }
507  SECTION("Condition type and source.")
508  {
509  tao::pegtl::string_input<> in(
510  "chain default {\n"
511  " accept if PING from 192.168;\n"
512  "}\n", "");
513  auto root = config::parse(in);
514  REQUIRE(root != nullptr);
515  REQUIRE_FALSE(root->children.empty());
516  REQUIRE(root->children[0] != nullptr);
517  REQUIRE_FALSE(root->children[0]->children.empty());
518  REQUIRE(root->children[0]->children[0] != nullptr);
519  REQUIRE_FALSE(root->children[0]->children[0]->children.empty());
520  REQUIRE(root->children[0]->children[0]->children[0] != nullptr);
521  auto condition = parse_condition(
522  *root->children[0]->children[0]->children[0]);
523  REQUIRE(str(condition) == "if PING from 192.168");
524  }
525  SECTION("Condition type and destination.")
526  {
527  tao::pegtl::string_input<> in(
528  "chain default {\n"
529  " accept if PING to 127.0/8;\n"
530  "}\n", "");
531  auto root = config::parse(in);
532  REQUIRE(root != nullptr);
533  REQUIRE_FALSE(root->children.empty());
534  REQUIRE(root->children[0] != nullptr);
535  REQUIRE_FALSE(root->children[0]->children.empty());
536  REQUIRE(root->children[0]->children[0] != nullptr);
537  REQUIRE_FALSE(root->children[0]->children[0]->children.empty());
538  REQUIRE(root->children[0]->children[0]->children[0] != nullptr);
539  auto condition = parse_condition(
540  *root->children[0]->children[0]->children[0]);
541  REQUIRE(str(condition) == "if PING to 127.0/8");
542  }
543  SECTION("Condition type, source, and destination.")
544  {
545  tao::pegtl::string_input<> in(
546  "chain default {\n"
547  " accept if PING from 192.168 to 127.0/8;\n"
548  "}\n", "");
549  auto root = config::parse(in);
550  REQUIRE(root != nullptr);
551  REQUIRE_FALSE(root->children.empty());
552  REQUIRE(root->children[0] != nullptr);
553  REQUIRE_FALSE(root->children[0]->children.empty());
554  REQUIRE(root->children[0]->children[0] != nullptr);
555  REQUIRE_FALSE(root->children[0]->children[0]->children.empty());
556  REQUIRE(root->children[0]->children[0]->children[0] != nullptr);
557  auto condition = parse_condition(
558  *root->children[0]->children[0]->children[0]);
559  REQUIRE(str(condition) == "if PING from 192.168 to 127.0/8");
560  }
561 }
562 
563 
564 TEST_CASE("'parse_chain' parses an action from the given AST node.",
565  "[ConfigParser]")
566 {
567  Chain default_chain("default");
568  std::map<std::string, std::shared_ptr<Chain>> chains;
569  chains["some_chain"] = std::make_shared<Chain>("some_chain");
570  SECTION("Accept action.")
571  {
572  tao::pegtl::string_input<> in(
573  "chain default {\n"
574  " accept;\n"
575  " accept with priority -1;\n"
576  " accept if PING from 192.168 to 127.0/8;\n"
577  " accept with priority -1 if PING from 192.168 to 127.0/8;\n"
578  "}\n", "");
579  auto root = config::parse(in);
580  REQUIRE(root != nullptr);
581  REQUIRE_FALSE(root->children.empty());
582  REQUIRE(root->children[0] != nullptr);
583  parse_chain(default_chain, *root->children[0], chains);
584  REQUIRE(str(default_chain) ==
585  "chain default {\n"
586  " accept;\n"
587  " accept with priority -1;\n"
588  " accept if PING from 192.168 to 127.0/8;\n"
589  " accept with priority -1 if PING from 192.168 to 127.0/8;\n"
590  "}");
591  }
592  SECTION("Reject action.")
593  {
594  tao::pegtl::string_input<> in(
595  "chain default {\n"
596  " reject;\n"
597  " reject if PING from 192.168 to 127.0/8;\n"
598  "}\n", "");
599  auto root = config::parse(in);
600  REQUIRE(root != nullptr);
601  REQUIRE_FALSE(root->children.empty());
602  REQUIRE(root->children[0] != nullptr);
603  parse_chain(default_chain, *root->children[0], chains);
604  REQUIRE(str(default_chain) ==
605  "chain default {\n"
606  " reject;\n"
607  " reject if PING from 192.168 to 127.0/8;\n"
608  "}");
609  }
610  SECTION("Call action.")
611  {
612  tao::pegtl::string_input<> in(
613  "chain default {\n"
614  " call some_chain;\n"
615  " call some_chain with priority -1;\n"
616  " call some_chain if PING from 192.168 to 127.0/8;\n"
617  " call some_chain with priority -1"
618  "if PING from 192.168 to 127.0/8;\n"
619  "}\n", "");
620  auto root = config::parse(in);
621  REQUIRE(root != nullptr);
622  REQUIRE_FALSE(root->children.empty());
623  REQUIRE(root->children[0] != nullptr);
624  parse_chain(default_chain, *root->children[0], chains);
625  REQUIRE(str(default_chain) ==
626  "chain default {\n"
627  " call some_chain;\n"
628  " call some_chain with priority -1;\n"
629  " call some_chain if PING from 192.168 to 127.0/8;\n"
630  " call some_chain with priority -1 "
631  "if PING from 192.168 to 127.0/8;\n"
632  "}");
633  }
634  SECTION("GoTo action.")
635  {
636  tao::pegtl::string_input<> in(
637  "chain default {\n"
638  " goto some_chain;\n"
639  " goto some_chain with priority -1;\n"
640  " goto some_chain if PING from 192.168 to 127.0/8;\n"
641  " goto some_chain with priority -1"
642  "if PING from 192.168 to 127.0/8;\n"
643  "}\n", "");
644  auto root = config::parse(in);
645  REQUIRE(root != nullptr);
646  REQUIRE_FALSE(root->children.empty());
647  REQUIRE(root->children[0] != nullptr);
648  parse_chain(default_chain, *root->children[0], chains);
649  REQUIRE(str(default_chain) ==
650  "chain default {\n"
651  " goto some_chain;\n"
652  " goto some_chain with priority -1;\n"
653  " goto some_chain if PING from 192.168 to 127.0/8;\n"
654  " goto some_chain with priority -1 "
655  "if PING from 192.168 to 127.0/8;\n"
656  "}");
657  }
658 }
659 
660 
661 TEST_CASE("'parse_filter' parses the filter from the given AST root node.",
662  "[ConfigParser]")
663 {
664  auto ping = packet_v2::Packet(to_vector(PingV2()));
665  auto heartbeat = packet_v2::Packet(to_vector(HeartbeatV2()));
666  auto encapsulated_data =
667  packet_v2::Packet(to_vector(EncapsulatedDataV2()));
668  SECTION("Successful parse.")
669  {
670  tao::pegtl::string_input<> in(
671  "chain default {\n"
672  " call first_chain if PING;\n"
673  " goto second_chain if HEARTBEAT;\n"
674  " reject;\n"
675  "}\n"
676  "\n"
677  "chain first_chain {\n"
678  " accept with priority 1;\n"
679  "}\n"
680  "\n"
681  "chain second_chain {\n"
682  " accept with priority 2;\n"
683  "}\n", "");
684  auto root = config::parse(in);
685  REQUIRE(root != nullptr);
686  auto filter = parse_filter(*root);
687  REQUIRE(filter != nullptr);
688  auto ping_result = filter->will_accept(ping, MAVAddress("127.1"));
689  REQUIRE(ping_result.first == true);
690  REQUIRE(ping_result.second == 1);
691  auto heartbeat_result =
692  filter->will_accept(heartbeat, MAVAddress("127.1"));
693  REQUIRE(heartbeat_result.first == true);
694  REQUIRE(heartbeat_result.second == 2);
695  auto encapsulated_result =
696  filter->will_accept(encapsulated_data, MAVAddress("127.1"));
697  REQUIRE(encapsulated_result.first == false);
698  }
699  SECTION("Default filter action is 'accept'")
700  {
701  tao::pegtl::string_input<> in(
702  "default_action accept;\n"
703  "chain default {\n"
704  "}\n", "");
705  auto root = config::parse(in);
706  REQUIRE(root != nullptr);
707  auto filter = parse_filter(*root);
708  REQUIRE(filter != nullptr);
709  auto result = filter->will_accept(ping, MAVAddress("127.1"));
710  REQUIRE(result.first == true);
711  REQUIRE(result.second == 0);
712  }
713  SECTION("Default filter action is 'reject'")
714  {
715  tao::pegtl::string_input<> in(
716  "default_action reject;\n"
717  "chain default {\n"
718  "}\n", "");
719  auto root = config::parse(in);
720  REQUIRE(root != nullptr);
721  auto filter = parse_filter(*root);
722  REQUIRE(filter != nullptr);
723  auto result = filter->will_accept(ping, MAVAddress("127.1"));
724  REQUIRE(result.first == false);
725  }
726  SECTION("The default, default filter action is 'reject'")
727  {
728  tao::pegtl::string_input<> in(
729  "chain default {\n"
730  "}\n", "");
731  auto root = config::parse(in);
732  REQUIRE(root != nullptr);
733  auto filter = parse_filter(*root);
734  REQUIRE(filter != nullptr);
735  auto result = filter->will_accept(ping, MAVAddress("127.1"));
736  REQUIRE(result.first == false);
737  }
738 }
739 
740 
741 TEST_CASE("'parse_serial' parses a serial interface from a serial interface "
742  "AST node.", "[ConfigParser]")
743 {
744  SECTION("With flow control.")
745  {
746  tao::pegtl::string_input<> in(
747  "serial {\n"
748  " device ./ttyS0;\n"
749  " baudrate 115200;\n"
750  " flow_control yes;\n"
751  "}\n", "");
752  auto root = config::parse(in);
753  REQUIRE(root != nullptr);
754  REQUIRE_FALSE(root->children.empty());
755  REQUIRE(root->children[0] != nullptr);
756  auto filter = std::make_shared<Filter>(Chain("default"));
757  auto connection_pool = std::make_shared<ConnectionPool>();
758  auto serial_port =
759  parse_serial(*root->children[0], filter, connection_pool);
760  REQUIRE(
761  str(*serial_port) ==
762  "serial {\n"
763  " device ./ttyS0;\n"
764  " baudrate 115200;\n"
765  " flow_control yes;\n"
766  "}");
767  }
768  SECTION("Without flow control.")
769  {
770  tao::pegtl::string_input<> in(
771  "serial {\n"
772  " device ./ttyS0;\n"
773  " baudrate 115200;\n"
774  " flow_control no;\n"
775  "}\n", "");
776  auto root = config::parse(in);
777  REQUIRE(root != nullptr);
778  REQUIRE_FALSE(root->children.empty());
779  REQUIRE(root->children[0] != nullptr);
780  auto filter = std::make_shared<Filter>(Chain("default"));
781  auto connection_pool = std::make_shared<ConnectionPool>();
782  auto serial_port =
783  parse_serial(*root->children[0], filter, connection_pool);
784  REQUIRE(
785  str(*serial_port) ==
786  "serial {\n"
787  " device ./ttyS0;\n"
788  " baudrate 115200;\n"
789  " flow_control no;\n"
790  "}");
791  }
792  SECTION("Default flow control is off.")
793  {
794  tao::pegtl::string_input<> in(
795  "serial {\n"
796  " device ./ttyS0;\n"
797  " baudrate 115200;\n"
798  "}\n", "");
799  auto root = config::parse(in);
800  REQUIRE(root != nullptr);
801  REQUIRE_FALSE(root->children.empty());
802  REQUIRE(root->children[0] != nullptr);
803  auto filter = std::make_shared<Filter>(Chain("default"));
804  auto connection_pool = std::make_shared<ConnectionPool>();
805  auto serial_port =
806  parse_serial(*root->children[0], filter, connection_pool);
807  REQUIRE(
808  str(*serial_port) ==
809  "serial {\n"
810  " device ./ttyS0;\n"
811  " baudrate 115200;\n"
812  " flow_control no;\n"
813  "}");
814  }
815  SECTION("Default baud rate is 9600.")
816  {
817  tao::pegtl::string_input<> in(
818  "serial {\n"
819  " device ./ttyS0;\n"
820  "}\n", "");
821  auto root = config::parse(in);
822  REQUIRE(root != nullptr);
823  REQUIRE_FALSE(root->children.empty());
824  REQUIRE(root->children[0] != nullptr);
825  auto filter = std::make_shared<Filter>(Chain("default"));
826  auto connection_pool = std::make_shared<ConnectionPool>();
827  auto serial_port =
828  parse_serial(*root->children[0], filter, connection_pool);
829  REQUIRE(
830  str(*serial_port) ==
831  "serial {\n"
832  " device ./ttyS0;\n"
833  " baudrate 9600;\n"
834  " flow_control no;\n"
835  "}");
836  }
837  SECTION("Throw error if device string is missing.")
838  {
839  tao::pegtl::string_input<> in(
840  "serial {\n"
841  "}\n", "");
842  auto root = config::parse(in);
843  REQUIRE(root != nullptr);
844  REQUIRE_FALSE(root->children.empty());
845  REQUIRE(root->children[0] != nullptr);
846  auto filter = std::make_shared<Filter>(Chain("default"));
847  auto connection_pool = std::make_shared<ConnectionPool>();
848  REQUIRE_THROWS_AS(
849  parse_serial(*root->children[0], filter, connection_pool),
850  std::invalid_argument);
851  REQUIRE_THROWS_WITH(
852  parse_serial(*root->children[0], filter, connection_pool),
853  "missing device string");
854  }
855  SECTION("With flow control.")
856  {
857  auto ping = std::make_shared<packet_v2::Packet>(to_vector(PingV2()));
858  auto set_mode = std::make_shared<packet_v2::Packet>(
859  to_vector(SetModeV2()));
860  auto param_ext_request_list =
861  std::make_shared<packet_v2::Packet>(
862  to_vector(ParamExtRequestListV2()));
863  fakeit::Mock<ConnectionPool> mock_pool;
864  std::shared_ptr<Connection> connection;
865  fakeit::When(Method(mock_pool, add)).AlwaysDo([&](auto conn)
866  {
867  connection = conn.lock();
868  });
869  tao::pegtl::string_input<> in(
870  "serial {\n"
871  " device ./ttyS0;\n"
872  " preload 127.1;\n"
873  " preload 32.64;\n"
874  "}\n", "");
875  auto root = config::parse(in);
876  REQUIRE(root != nullptr);
877  REQUIRE_FALSE(root->children.empty());
878  REQUIRE(root->children[0] != nullptr);
879  auto filter = std::make_shared<Filter>(Chain("default"), true);
880  auto connection_pool = mock_shared(mock_pool);
881  auto serial_port =
882  parse_serial(*root->children[0], filter, connection_pool);
883  REQUIRE(serial_port != nullptr);
884  fakeit::Verify(Method(mock_pool, add)).Exactly(1);
885  connection->send(ping);
886  connection->send(set_mode);
887  connection->send(param_ext_request_list);
888  // ping packet
889  auto packet = connection->next_packet();
890  REQUIRE(packet != nullptr);
891  REQUIRE(*packet == *ping);
892  // set_mode packet
893  packet = connection->next_packet();
894  REQUIRE(packet != nullptr);
895  REQUIRE(*packet == *param_ext_request_list);
896  // no more packets
897  packet = connection->next_packet();
898  REQUIRE(packet == nullptr);
899  }
900 }
901 
902 
903 TEST_CASE("'parse_udp' parses a UDP interface from a UDP interface AST node.",
904  "[ConfigParser]")
905 {
906  SECTION("Without specific IP address.")
907  {
908  tao::pegtl::string_input<> in(
909  "udp {\n"
910  " port 14500;\n"
911  "}\n", "");
912  auto root = config::parse(in);
913  REQUIRE(root != nullptr);
914  REQUIRE_FALSE(root->children.empty());
915  REQUIRE(root->children[0] != nullptr);
916  auto filter = std::make_shared<Filter>(Chain("default"));
917  auto connection_pool = std::make_shared<ConnectionPool>();
918  auto udp_socket =
919  parse_udp(*root->children[0], filter, connection_pool);
920  REQUIRE(
921  str(*udp_socket) ==
922  "udp {\n"
923  " port 14500;\n"
924  "}");
925  }
926  SECTION("With specific IP address.")
927  {
928  tao::pegtl::string_input<> in(
929  "udp {\n"
930  " port 14500;\n"
931  " address 127.0.0.1;\n"
932  "}\n", "");
933  auto root = config::parse(in);
934  REQUIRE(root != nullptr);
935  REQUIRE_FALSE(root->children.empty());
936  REQUIRE(root->children[0] != nullptr);
937  auto filter = std::make_shared<Filter>(Chain("default"));
938  auto connection_pool = std::make_shared<ConnectionPool>();
939  auto udp_socket =
940  parse_udp(*root->children[0], filter, connection_pool);
941  REQUIRE(
942  str(*udp_socket) ==
943  "udp {\n"
944  " port 14500;\n"
945  " address 127.0.0.1;\n"
946  "}");
947  }
948  SECTION("Without specific IP address (with bitrate limit).")
949  {
950  tao::pegtl::string_input<> in(
951  "udp {\n"
952  " port 14500;\n"
953  " max_bitrate 8192;\n"
954  "}\n", "");
955  auto root = config::parse(in);
956  REQUIRE(root != nullptr);
957  REQUIRE_FALSE(root->children.empty());
958  REQUIRE(root->children[0] != nullptr);
959  auto filter = std::make_shared<Filter>(Chain("default"));
960  auto connection_pool = std::make_shared<ConnectionPool>();
961  auto udp_socket =
962  parse_udp(*root->children[0], filter, connection_pool);
963  REQUIRE(
964  str(*udp_socket) ==
965  "udp {\n"
966  " port 14500;\n"
967  " max_bitrate 8192;\n"
968  "}");
969  }
970  SECTION("With specific IP address (with bitrate limit).")
971  {
972  tao::pegtl::string_input<> in(
973  "udp {\n"
974  " port 14500;\n"
975  " address 127.0.0.1;\n"
976  " max_bitrate 8192;\n"
977  "}\n", "");
978  auto root = config::parse(in);
979  REQUIRE(root != nullptr);
980  REQUIRE_FALSE(root->children.empty());
981  REQUIRE(root->children[0] != nullptr);
982  auto filter = std::make_shared<Filter>(Chain("default"));
983  auto connection_pool = std::make_shared<ConnectionPool>();
984  auto udp_socket =
985  parse_udp(*root->children[0], filter, connection_pool);
986  REQUIRE(
987  str(*udp_socket) ==
988  "udp {\n"
989  " port 14500;\n"
990  " address 127.0.0.1;\n"
991  " max_bitrate 8192;\n"
992  "}");
993  }
994 }
995 
996 
997 TEST_CASE("'parse_interfaces' parses serial port and UDP interfaces from "
998  "the root node.", "[ConfigParser]")
999 {
1000  tao::pegtl::string_input<> in(
1001  "serial {\n"
1002  " device ./ttyS0;\n"
1003  " baudrate 115200;\n"
1004  " flow_control yes;\n"
1005  "}\n"
1006  "\n"
1007  "serial {\n"
1008  " device ./ttyS1;\n"
1009  " baudrate 300;\n"
1010  " flow_control no;\n"
1011  "}\n"
1012  "\n"
1013  "udp {\n"
1014  " port 14500;\n"
1015  "}\n"
1016  "\n"
1017  "udp {\n"
1018  " port 14501;\n"
1019  " address 127.0.0.1;\n"
1020  "}\n"
1021  "\n"
1022  "udp {\n"
1023  " port 14502;\n"
1024  " max_bitrate 8192;\n"
1025  "}\n"
1026  "\n"
1027  "udp {\n"
1028  " port 14503;\n"
1029  " address 127.0.0.1;\n"
1030  " max_bitrate 8192;\n"
1031  "}\n", "");
1032  auto root = config::parse(in);
1033  REQUIRE(root != nullptr);
1034  auto filter = std::make_unique<Filter>(Chain("default"));
1035  auto interfaces = parse_interfaces(*root, std::move(filter));
1036  REQUIRE(interfaces.size() == 6);
1037  REQUIRE(interfaces[0] != nullptr);
1038  REQUIRE(
1039  str(*interfaces[0]) ==
1040  "serial {\n"
1041  " device ./ttyS0;\n"
1042  " baudrate 115200;\n"
1043  " flow_control yes;\n"
1044  "}");
1045  REQUIRE(interfaces[1] != nullptr);
1046  REQUIRE(
1047  str(*interfaces[1]) ==
1048  "serial {\n"
1049  " device ./ttyS1;\n"
1050  " baudrate 300;\n"
1051  " flow_control no;\n"
1052  "}");
1053  REQUIRE(interfaces[2] != nullptr);
1054  REQUIRE(
1055  str(*interfaces[2]) ==
1056  "udp {\n"
1057  " port 14500;\n"
1058  "}");
1059  REQUIRE(interfaces[3] != nullptr);
1060  REQUIRE(
1061  str(*interfaces[3]) ==
1062  "udp {\n"
1063  " port 14501;\n"
1064  " address 127.0.0.1;\n"
1065  "}");
1066  REQUIRE(interfaces[4] != nullptr);
1067  REQUIRE(
1068  str(*interfaces[4]) ==
1069  "udp {\n"
1070  " port 14502;\n"
1071  " max_bitrate 8192;\n"
1072  "}");
1073  REQUIRE(interfaces[5] != nullptr);
1074  REQUIRE(
1075  str(*interfaces[5]) ==
1076  "udp {\n"
1077  " port 14503;\n"
1078  " address 127.0.0.1;\n"
1079  " max_bitrate 8192;\n"
1080  "}");
1081 }
1082 
1083 
1084 TEST_CASE("ConfigParser can parse a file.", "[ConfigParser]")
1085 {
1086  SECTION("When the file exists and is valid.")
1087  {
1088  REQUIRE_NOTHROW(ConfigParser("test/mavtables.conf"));
1089  }
1090  SECTION("Throws an error if the file does not exist.")
1091  {
1092  REQUIRE_THROWS(
1093  ConfigParser("file_that_does_not_exist.conf"));
1094  }
1095 }
1096 
1097 
1098 TEST_CASE("ConfigParser are printable.", "[ConfigParser]")
1099 {
1100  ConfigParser config("test/mavtables.conf");
1101  REQUIRE(
1102  str(config) ==
1103  ":001: default_action\n"
1104  ":001: | accept\n"
1105  ":004: udp\n"
1106  ":005: | port 14500\n"
1107  ":006: | address 127.0.0.1\n"
1108  ":007: | max_bitrate 8388608\n"
1109  ":011: serial\n"
1110  ":012: | device ./ttyS0\n"
1111  ":013: | baudrate 115200\n"
1112  ":014: | flow_control yes\n"
1113  ":015: | preload 1.1\n"
1114  ":016: | preload 62.34\n"
1115  ":020: chain default\n"
1116  ":022: | call some_chain10\n"
1117  ":022: | | condition\n"
1118  ":022: | | | source 127.1\n"
1119  ":022: | | | dest 192.0\n"
1120  ":023: | reject\n"
1121  ":027: chain some_chain10\n"
1122  ":029: | accept\n"
1123  ":029: | | priority 99\n"
1124  ":029: | | condition\n"
1125  ":029: | | | dest 192.0\n"
1126  ":030: | accept\n"
1127  ":030: | | condition\n"
1128  ":030: | | | packet_type PING\n"
1129  ":031: | accept\n");
1130 }
1131 
1132 
1133 TEST_CASE("ConfigParser's 'make_app' method returns an application object.",
1134  "[ConfigParser]")
1135 {
1136  ConfigParser config("test/mavtables.conf");
1137  std::unique_ptr<App> app;
1138  REQUIRE_NOTHROW(app = config.make_app());
1139  REQUIRE(app != nullptr);
1140 }
std::string str(const T &object)
Definition: utility.hpp:128
std::unique_ptr< config::parse_tree::node > parse(Input &in)
If parse_condition(const config::parse_tree::node &root)
Definition: If.hpp:35
std::unique_ptr< Filter > parse_filter(const config::parse_tree::node &root)
std::unique_ptr< UDPInterface > parse_udp(const config::parse_tree::node &root, std::shared_ptr< Filter > filter, std::shared_ptr< ConnectionPool > pool)
std::unique_ptr< SerialInterface > parse_serial(const config::parse_tree::node &root, std::shared_ptr< Filter > filter, std::shared_ptr< ConnectionPool > pool)
def heartbeat(mav)
Definition: logger.py:42
std::vector< std::unique_ptr< Interface > > parse_interfaces(const config::parse_tree::node &root, std::unique_ptr< Filter > filter)
Definition: Chain.hpp:37
void parse_chain(Chain &chain, const config::parse_tree::node &root, const std::map< std::string, std::shared_ptr< Chain >> &chains)
std::shared_ptr< T > mock_shared(fakeit::Mock< T > &mock)
Definition: common.hpp:33
auto ping
Definition: test_Call.cpp:229
std::map< std::string, std::shared_ptr< Chain > > init_chains(const config::parse_tree::node &root)
std::unique_ptr< Rule > parse_action(const config::parse_tree::node &root, std::optional< int > priority, std::optional< If > condition, const std::map< std::string, std::shared_ptr< Chain >> &chains)
TEST_CASE("'init_chains' initializes a map of chain names to empty chains" "[ConfigParser]")