]> git.proxmox.com Git - ovs.git/blame - tests/test-ovn.c
doc: Convert MAINTAINERS to rST
[ovs.git] / tests / test-ovn.c
CommitLineData
10b1662b 1/*
caff23ca 2 * Copyright (c) 2015, 2016 Nicira, Inc.
10b1662b
BP
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <config.h>
e0840f11 18#include <errno.h>
10b1662b 19#include <getopt.h>
e0840f11 20#include <sys/wait.h>
b598f214 21#include "command-line.h"
10b1662b 22#include "fatal-signal.h"
f4248336 23#include "flow.h"
b598f214 24#include "openvswitch/dynamic-string.h"
e29747e4 25#include "openvswitch/match.h"
b598f214 26#include "openvswitch/ofp-actions.h"
64c96779 27#include "openvswitch/ofpbuf.h"
b598f214 28#include "openvswitch/vlog.h"
8b2ed684
AR
29#include "ovn/actions.h"
30#include "ovn/expr.h"
31#include "ovn/lex.h"
7700eea0 32#include "ovn/lib/logical-fields.h"
42814145 33#include "ovn/lib/ovn-dhcp.h"
e0840f11 34#include "ovs-thread.h"
10b1662b 35#include "ovstest.h"
ee89ea7b 36#include "openvswitch/shash.h"
f386a8a7 37#include "simap.h"
10b1662b 38#include "util.h"
10b1662b 39
e0840f11
BP
40/* --relops: Bitmap of the relational operators to test, in exhaustive test. */
41static unsigned int test_relops;
42
9d4aecca
BP
43/* --nvars: Number of numeric variables to test, in exhaustive test. */
44static int test_nvars = 2;
45
46/* --svars: Number of string variables to test, in exhaustive test. */
47static int test_svars = 2;
e0840f11
BP
48
49/* --bits: Number of bits per variable, in exhaustive test. */
50static int test_bits = 3;
51
52/* --operation: The operation to test, in exhaustive test. */
53static enum { OP_CONVERT, OP_SIMPLIFY, OP_NORMALIZE, OP_FLOW } operation
54 = OP_FLOW;
55
56/* --parallel: Number of parallel processes to use in test. */
57static int test_parallel = 1;
58
59/* -m, --more: Message verbosity */
60static int verbosity;
61
10b1662b
BP
62static void
63compare_token(const struct lex_token *a, const struct lex_token *b)
64{
65 if (a->type != b->type) {
66 fprintf(stderr, "type differs: %d -> %d\n", a->type, b->type);
67 return;
68 }
69
70 if (!((a->s && b->s && !strcmp(a->s, b->s))
71 || (!a->s && !b->s))) {
72 fprintf(stderr, "string differs: %s -> %s\n",
73 a->s ? a->s : "(null)",
74 b->s ? b->s : "(null)");
75 return;
76 }
77
78 if (a->type == LEX_T_INTEGER || a->type == LEX_T_MASKED_INTEGER) {
79 if (memcmp(&a->value, &b->value, sizeof a->value)) {
80 fprintf(stderr, "value differs\n");
81 return;
82 }
83
84 if (a->type == LEX_T_MASKED_INTEGER
85 && memcmp(&a->mask, &b->mask, sizeof a->mask)) {
86 fprintf(stderr, "mask differs\n");
87 return;
88 }
89
90 if (a->format != b->format
91 && !(a->format == LEX_F_HEXADECIMAL
92 && b->format == LEX_F_DECIMAL
93 && a->value.integer == 0)) {
94 fprintf(stderr, "format differs: %d -> %d\n",
95 a->format, b->format);
96 }
97 }
98}
99
100static void
101test_lex(struct ovs_cmdl_context *ctx OVS_UNUSED)
102{
103 struct ds input;
104 struct ds output;
105
106 ds_init(&input);
107 ds_init(&output);
a20c96c6 108 while (!ds_get_test_line(&input, stdin)) {
10b1662b
BP
109 struct lexer lexer;
110
111 lexer_init(&lexer, ds_cstr(&input));
112 ds_clear(&output);
113 while (lexer_get(&lexer) != LEX_T_END) {
114 size_t len = output.length;
115 lex_token_format(&lexer.token, &output);
116
117 /* Check that the formatted version can really be parsed back
118 * losslessly. */
119 if (lexer.token.type != LEX_T_ERROR) {
120 const char *s = ds_cstr(&output) + len;
121 struct lexer l2;
122
123 lexer_init(&l2, s);
124 lexer_get(&l2);
125 compare_token(&lexer.token, &l2.token);
126 lexer_destroy(&l2);
127 }
128 ds_put_char(&output, ' ');
129 }
130 lexer_destroy(&lexer);
131
132 ds_chomp(&output, ' ');
133 puts(ds_cstr(&output));
134 }
135 ds_destroy(&input);
136 ds_destroy(&output);
137}
138
e0840f11
BP
139static void
140create_symtab(struct shash *symtab)
141{
7700eea0 142 ovn_init_symtab(symtab);
e0840f11
BP
143
144 /* For negative testing. */
145 expr_symtab_add_field(symtab, "bad_prereq", MFF_XREG0, "xyzzy", false);
146 expr_symtab_add_field(symtab, "self_recurse", MFF_XREG0,
147 "self_recurse != 0", false);
148 expr_symtab_add_field(symtab, "mutual_recurse_1", MFF_XREG0,
149 "mutual_recurse_2 != 0", false);
150 expr_symtab_add_field(symtab, "mutual_recurse_2", MFF_XREG0,
151 "mutual_recurse_1 != 0", false);
5ee054fb 152 expr_symtab_add_string(symtab, "big_string", MFF_XREG0, NULL);
e0840f11
BP
153}
154
42814145 155static void
01cfdb2f 156create_dhcp_opts(struct hmap *dhcp_opts, struct hmap *dhcpv6_opts)
42814145
NS
157{
158 hmap_init(dhcp_opts);
159 dhcp_opt_add(dhcp_opts, "offerip", 0, "ipv4");
160 dhcp_opt_add(dhcp_opts, "netmask", 1, "ipv4");
161 dhcp_opt_add(dhcp_opts, "router", 3, "ipv4");
162 dhcp_opt_add(dhcp_opts, "dns_server", 6, "ipv4");
163 dhcp_opt_add(dhcp_opts, "log_server", 7, "ipv4");
164 dhcp_opt_add(dhcp_opts, "lpr_server", 9, "ipv4");
165 dhcp_opt_add(dhcp_opts, "domain", 15, "str");
166 dhcp_opt_add(dhcp_opts, "swap_server", 16, "ipv4");
167 dhcp_opt_add(dhcp_opts, "policy_filter", 21, "ipv4");
168 dhcp_opt_add(dhcp_opts, "router_solicitation", 32, "ipv4");
169 dhcp_opt_add(dhcp_opts, "nis_server", 41, "ipv4");
170 dhcp_opt_add(dhcp_opts, "ntp_server", 42, "ipv4");
171 dhcp_opt_add(dhcp_opts, "server_id", 54, "ipv4");
172 dhcp_opt_add(dhcp_opts, "tftp_server", 66, "ipv4");
173 dhcp_opt_add(dhcp_opts, "classless_static_route", 121, "static_routes");
174 dhcp_opt_add(dhcp_opts, "ip_forward_enable", 19, "bool");
175 dhcp_opt_add(dhcp_opts, "router_discovery", 31, "bool");
176 dhcp_opt_add(dhcp_opts, "ethernet_encap", 36, "bool");
177 dhcp_opt_add(dhcp_opts, "default_ttl", 23, "uint8");
178 dhcp_opt_add(dhcp_opts, "tcp_ttl", 37, "uint8");
179 dhcp_opt_add(dhcp_opts, "mtu", 26, "uint16");
180 dhcp_opt_add(dhcp_opts, "lease_time", 51, "uint32");
01cfdb2f
NS
181
182 /* DHCPv6 options. */
183 hmap_init(dhcpv6_opts);
184 dhcp_opt_add(dhcpv6_opts, "server_id", 2, "mac");
185 dhcp_opt_add(dhcpv6_opts, "ia_addr", 5, "ipv6");
186 dhcp_opt_add(dhcpv6_opts, "dns_server", 23, "ipv6");
187 dhcp_opt_add(dhcpv6_opts, "domain_search", 24, "str");
42814145
NS
188}
189
2c5cbb15
RB
190static void
191create_macros(struct shash *macros)
192{
193 shash_init(macros);
194
195 static const char *const addrs1[] = {
196 "10.0.0.1", "10.0.0.2", "10.0.0.3",
197 };
198 static const char *const addrs2[] = {
199 "::1", "::2", "::3",
200 };
201 static const char *const addrs3[] = {
202 "00:00:00:00:00:01", "00:00:00:00:00:02", "00:00:00:00:00:03",
203 };
204
205 expr_macros_add(macros, "set1", addrs1, 3);
206 expr_macros_add(macros, "set2", addrs2, 3);
207 expr_macros_add(macros, "set3", addrs3, 3);
208}
209
f1c16a85
BP
210static bool
211lookup_port_cb(const void *ports_, const char *port_name, unsigned int *portp)
212{
213 const struct simap *ports = ports_;
214 const struct simap_node *node = simap_find(ports, port_name);
215 if (!node) {
216 return false;
217 }
218 *portp = node->data;
219 return true;
220}
221
e0840f11
BP
222static void
223test_parse_expr__(int steps)
224{
225 struct shash symtab;
2c5cbb15 226 struct shash macros;
f386a8a7 227 struct simap ports;
e0840f11
BP
228 struct ds input;
229
230 create_symtab(&symtab);
2c5cbb15 231 create_macros(&macros);
f386a8a7
BP
232
233 simap_init(&ports);
234 simap_put(&ports, "eth0", 5);
235 simap_put(&ports, "eth1", 6);
236 simap_put(&ports, "LOCAL", ofp_to_u16(OFPP_LOCAL));
237
e0840f11
BP
238 ds_init(&input);
239 while (!ds_get_test_line(&input, stdin)) {
240 struct expr *expr;
241 char *error;
242
2c5cbb15 243 expr = expr_parse_string(ds_cstr(&input), &symtab, &macros, &error);
e0840f11
BP
244 if (!error && steps > 0) {
245 expr = expr_annotate(expr, &symtab, &error);
246 }
247 if (!error) {
248 if (steps > 1) {
249 expr = expr_simplify(expr);
250 }
251 if (steps > 2) {
252 expr = expr_normalize(expr);
253 ovs_assert(expr_is_normalized(expr));
254 }
255 }
256 if (!error) {
f386a8a7
BP
257 if (steps > 3) {
258 struct hmap matches;
259
f1c16a85 260 expr_to_matches(expr, lookup_port_cb, &ports, &matches);
f386a8a7
BP
261 expr_matches_print(&matches, stdout);
262 expr_matches_destroy(&matches);
263 } else {
264 struct ds output = DS_EMPTY_INITIALIZER;
265 expr_format(expr, &output);
266 puts(ds_cstr(&output));
267 ds_destroy(&output);
268 }
e0840f11
BP
269 } else {
270 puts(error);
271 free(error);
272 }
273 expr_destroy(expr);
274 }
275 ds_destroy(&input);
276
f386a8a7 277 simap_destroy(&ports);
e0840f11
BP
278 expr_symtab_destroy(&symtab);
279 shash_destroy(&symtab);
2c5cbb15
RB
280 expr_macros_destroy(&macros);
281 shash_destroy(&macros);
e0840f11
BP
282}
283
284static void
285test_parse_expr(struct ovs_cmdl_context *ctx OVS_UNUSED)
286{
287 test_parse_expr__(0);
288}
289
290static void
291test_annotate_expr(struct ovs_cmdl_context *ctx OVS_UNUSED)
292{
293 test_parse_expr__(1);
294}
295
296static void
297test_simplify_expr(struct ovs_cmdl_context *ctx OVS_UNUSED)
298{
299 test_parse_expr__(2);
300}
301
302static void
303test_normalize_expr(struct ovs_cmdl_context *ctx OVS_UNUSED)
304{
305 test_parse_expr__(3);
306}
f386a8a7
BP
307
308static void
309test_expr_to_flows(struct ovs_cmdl_context *ctx OVS_UNUSED)
310{
311 test_parse_expr__(4);
312}
e0840f11 313\f
7700eea0
BP
314/* Print the symbol table. */
315
316static void
317test_dump_symtab(struct ovs_cmdl_context *ctx OVS_UNUSED)
318{
319 struct shash symtab;
320 create_symtab(&symtab);
321
322 const struct shash_node **nodes = shash_sort(&symtab);
323 for (size_t i = 0; i < shash_count(&symtab); i++) {
324 const struct expr_symbol *symbol = nodes[i]->data;
325 struct ds s = DS_EMPTY_INITIALIZER;
326 expr_symbol_format(symbol, &s);
327 puts(ds_cstr(&s));
328 ds_destroy(&s);
329 }
330
85d87d16 331 free(nodes);
7700eea0
BP
332 expr_symtab_destroy(&symtab);
333 shash_destroy(&symtab);
334}
335\f
e0840f11
BP
336/* Evaluate an expression. */
337
e0840f11 338static bool
803a6f3d
BP
339lookup_atoi_cb(const void *aux OVS_UNUSED, const char *port_name,
340 unsigned int *portp)
e0840f11 341{
803a6f3d
BP
342 *portp = atoi(port_name);
343 return true;
e0840f11
BP
344}
345
346static void
347test_evaluate_expr(struct ovs_cmdl_context *ctx)
348{
e0840f11
BP
349 struct shash symtab;
350 struct ds input;
351
d5b33279
BP
352 ovn_init_symtab(&symtab);
353
354 struct flow uflow;
355 char *error = expr_parse_microflow(ctx->argv[1], &symtab, NULL,
356 lookup_atoi_cb, NULL, &uflow);
357 if (error) {
358 ovs_fatal(0, "%s", error);
359 }
e0840f11
BP
360
361 ds_init(&input);
362 while (!ds_get_test_line(&input, stdin)) {
363 struct expr *expr;
364 char *error;
365
2c5cbb15 366 expr = expr_parse_string(ds_cstr(&input), &symtab, NULL, &error);
e0840f11
BP
367 if (!error) {
368 expr = expr_annotate(expr, &symtab, &error);
369 }
370 if (!error) {
803a6f3d 371 printf("%d\n", expr_evaluate(expr, &uflow, lookup_atoi_cb, NULL));
e0840f11
BP
372 } else {
373 puts(error);
374 free(error);
375 }
376 expr_destroy(expr);
377 }
378 ds_destroy(&input);
379
380 expr_symtab_destroy(&symtab);
381 shash_destroy(&symtab);
382}
383\f
384/* Compositions.
385 *
386 * The "compositions" of a positive integer N are all of the ways that one can
387 * add up positive integers to sum to N. For example, the compositions of 3
388 * are 3, 2+1, 1+2, and 1+1+1.
389 *
390 * We use compositions to find all the ways to break up N terms of a Boolean
391 * expression into subexpressions. Suppose we want to generate all expressions
392 * with 3 terms. The compositions of 3 (ignoring 3 itself) provide the
393 * possibilities (x && x) || x, x || (x && x), and x || x || x. (Of course one
394 * can exchange && for || in each case.) One must recursively compose the
395 * sub-expressions whose values are 3 or greater; that is what the "tree shape"
396 * concept later covers.
397 *
398 * To iterate through all compositions of, e.g., 5:
399 *
400 * unsigned int state;
401 * int s[5];
402 * int n;
403 *
404 * for (n = first_composition(ARRAY_SIZE(s), &state, s); n > 0;
405 * n = next_composition(&state, s, n)) {
406 * // Do something with composition 's' with 'n' elements.
407 * }
408 *
409 * Algorithm from D. E. Knuth, _The Art of Computer Programming, Vol. 4A:
410 * Combinatorial Algorithms, Part 1_, section 7.2.1.1, answer to exercise
411 * 12(a).
412 */
413
414/* Begins iteration through the compositions of 'n'. Initializes 's' to the
415 * number of elements in the first composition of 'n' and returns that number
416 * of elements. The first composition in fact is always 'n' itself, so the
417 * return value will be 1.
418 *
419 * Initializes '*state' to some internal state information. The caller must
420 * maintain this state (and 's') for use by next_composition().
421 *
422 * 's' must have room for at least 'n' elements. */
423static int
424first_composition(int n, unsigned int *state, int s[])
425{
426 *state = 0;
427 s[0] = n;
428 return 1;
429}
430
431/* Advances 's', with 'sn' elements, to the next composition and returns the
432 * number of elements in this new composition, or 0 if no compositions are
433 * left. 'state' is the same internal state passed to first_composition(). */
434static int
435next_composition(unsigned int *state, int s[], int sn)
436{
437 int j = sn - 1;
438 if (++*state & 1) {
439 if (s[j] > 1) {
440 s[j]--;
441 s[j + 1] = 1;
442 j++;
443 } else {
444 j--;
445 s[j]++;
446 }
447 } else {
448 if (s[j - 1] > 1) {
449 s[j - 1]--;
450 s[j + 1] = s[j];
451 s[j] = 1;
452 j++;
453 } else {
454 j--;
455 s[j] = s[j + 1];
456 s[j - 1]++;
457 if (!j) {
458 return 0;
459 }
460 }
461 }
462 return j + 1;
463}
464
465static void
466test_composition(struct ovs_cmdl_context *ctx)
467{
468 int n = atoi(ctx->argv[1]);
469 unsigned int state;
470 int s[50];
471
472 for (int sn = first_composition(n, &state, s); sn;
473 sn = next_composition(&state, s, sn)) {
474 for (int i = 0; i < sn; i++) {
475 printf("%d%c", s[i], i == sn - 1 ? '\n' : ' ');
476 }
477 }
478}
479\f
480/* Tree shapes.
481 *
482 * This code generates all possible Boolean expressions with a specified number
483 * of terms N (equivalent to the number of external nodes in a tree).
484 *
485 * See test_tree_shape() for a simple example. */
486
487/* An array of these structures describes the shape of a tree.
488 *
489 * A single element of struct tree_shape describes a single node in the tree.
490 * The node has 'sn' direct children. From left to right, for i in 0...sn-1,
491 * s[i] is 1 if the child is a leaf node, otherwise the child is a subtree and
492 * s[i] is the number of leaf nodes within that subtree. In the latter case,
493 * the subtree is described by another struct tree_shape within the enclosing
494 * array. The tree_shapes are ordered in the array in in-order.
495 */
496struct tree_shape {
497 unsigned int state;
498 int s[50];
499 int sn;
500};
501
502static int
503init_tree_shape__(struct tree_shape ts[], int n)
504{
505 if (n <= 2) {
506 return 0;
507 }
508
509 int n_tses = 1;
510 /* Skip the first composition intentionally. */
511 ts->sn = first_composition(n, &ts->state, ts->s);
512 ts->sn = next_composition(&ts->state, ts->s, ts->sn);
513 for (int i = 0; i < ts->sn; i++) {
514 n_tses += init_tree_shape__(&ts[n_tses], ts->s[i]);
515 }
516 return n_tses;
517}
518
519/* Initializes 'ts[]' as the first in the set of all of possible shapes of
520 * trees with 'n' leaves. Returns the number of "struct tree_shape"s in the
521 * first tree shape. */
522static int
523init_tree_shape(struct tree_shape ts[], int n)
524{
525 switch (n) {
526 case 1:
527 ts->sn = 1;
528 ts->s[0] = 1;
529 return 1;
530 case 2:
531 ts->sn = 2;
532 ts->s[0] = 1;
533 ts->s[1] = 1;
534 return 1;
535 default:
536 return init_tree_shape__(ts, n);
537 }
538}
539
540/* Advances 'ts', which currently has 'n_tses' elements, to the next possible
541 * tree shape with the number of leaves passed to init_tree_shape(). Returns
542 * the number of "struct tree_shape"s in the next shape, or 0 if all tree
543 * shapes have been visited. */
544static int
545next_tree_shape(struct tree_shape ts[], int n_tses)
546{
547 if (n_tses == 1 && ts->sn == 2 && ts->s[0] == 1 && ts->s[1] == 1) {
548 return 0;
549 }
550 while (n_tses > 0) {
551 struct tree_shape *p = &ts[n_tses - 1];
552 p->sn = p->sn > 1 ? next_composition(&p->state, p->s, p->sn) : 0;
553 if (p->sn) {
554 for (int i = 0; i < p->sn; i++) {
555 n_tses += init_tree_shape__(&ts[n_tses], p->s[i]);
556 }
557 break;
558 }
559 n_tses--;
560 }
561 return n_tses;
562}
563
564static void
565print_tree_shape(const struct tree_shape ts[], int n_tses)
566{
567 for (int i = 0; i < n_tses; i++) {
568 if (i) {
569 printf(", ");
570 }
571 for (int j = 0; j < ts[i].sn; j++) {
572 int k = ts[i].s[j];
573 if (k > 9) {
574 printf("(%d)", k);
575 } else {
576 printf("%d", k);
577 }
578 }
579 }
580}
581
582static void
583test_tree_shape(struct ovs_cmdl_context *ctx)
584{
585 int n = atoi(ctx->argv[1]);
586 struct tree_shape ts[50];
587 int n_tses;
588
589 for (n_tses = init_tree_shape(ts, n); n_tses;
590 n_tses = next_tree_shape(ts, n_tses)) {
591 print_tree_shape(ts, n_tses);
592 putchar('\n');
593 }
594}
595\f
596/* Iteration through all possible terminal expressions (e.g. EXPR_T_CMP and
597 * EXPR_T_BOOLEAN expressions).
598 *
599 * Given a tree shape, this allows the code to try all possible ways to plug in
600 * terms.
601 *
602 * Example use:
603 *
604 * struct expr terminal;
605 * const struct expr_symbol *vars = ...;
606 * int n_vars = ...;
607 * int n_bits = ...;
608 *
609 * init_terminal(&terminal, vars[0]);
610 * do {
611 * // Something with 'terminal'.
612 * } while (next_terminal(&terminal, vars, n_vars, n_bits));
613 */
614
615/* Sets 'expr' to the first possible terminal expression. 'var' should be the
616 * first variable in the ones to be tested. */
617static void
9d4aecca
BP
618init_terminal(struct expr *expr, int phase,
619 const struct expr_symbol *nvars[], int n_nvars,
620 const struct expr_symbol *svars[], int n_svars)
e0840f11 621{
9d4aecca
BP
622 if (phase < 1 && n_nvars) {
623 expr->type = EXPR_T_CMP;
624 expr->cmp.symbol = nvars[0];
625 expr->cmp.relop = rightmost_1bit_idx(test_relops);
626 memset(&expr->cmp.value, 0, sizeof expr->cmp.value);
627 memset(&expr->cmp.mask, 0, sizeof expr->cmp.mask);
628 expr->cmp.value.integer = htonll(0);
8c3caa2c 629 expr->cmp.mask.integer = htonll(0);
9d4aecca
BP
630 return;
631 }
632
633 if (phase < 2 && n_svars) {
634 expr->type = EXPR_T_CMP;
635 expr->cmp.symbol = svars[0];
636 expr->cmp.relop = EXPR_R_EQ;
637 expr->cmp.string = xstrdup("0");
638 return;
639 }
640
641 expr->type = EXPR_T_BOOLEAN;
642 expr->boolean = false;
e0840f11
BP
643}
644
645/* Returns 'x' with the rightmost contiguous string of 1s changed to 0s,
646 * e.g. 01011100 => 01000000. See H. S. Warren, Jr., _Hacker's Delight_, 2nd
647 * ed., section 2-1. */
648static unsigned int
649turn_off_rightmost_1s(unsigned int x)
650{
651 return ((x & -x) + x) & x;
652}
653
654static const struct expr_symbol *
655next_var(const struct expr_symbol *symbol,
656 const struct expr_symbol *vars[], int n_vars)
657{
658 for (int i = 0; i < n_vars; i++) {
659 if (symbol == vars[i]) {
660 return i + 1 >= n_vars ? NULL : vars[i + 1];
661 }
662 }
663 OVS_NOT_REACHED();
664}
665
666static enum expr_relop
667next_relop(enum expr_relop relop)
668{
669 unsigned int remaining_relops = test_relops & ~((1u << (relop + 1)) - 1);
670 return (remaining_relops
671 ? rightmost_1bit_idx(remaining_relops)
672 : rightmost_1bit_idx(test_relops));
673}
674
675/* Advances 'expr' to the next possible terminal expression within the 'n_vars'
676 * variables of 'n_bits' bits each in 'vars[]'. */
677static bool
9d4aecca
BP
678next_terminal(struct expr *expr,
679 const struct expr_symbol *nvars[], int n_nvars, int n_bits,
680 const struct expr_symbol *svars[], int n_svars)
e0840f11
BP
681{
682 if (expr->type == EXPR_T_BOOLEAN) {
683 if (expr->boolean) {
684 return false;
685 } else {
686 expr->boolean = true;
687 return true;
688 }
689 }
690
9d4aecca
BP
691 if (!expr->cmp.symbol->width) {
692 int next_value = atoi(expr->cmp.string) + 1;
693 free(expr->cmp.string);
694 if (next_value > 1) {
695 expr->cmp.symbol = next_var(expr->cmp.symbol, svars, n_svars);
696 if (!expr->cmp.symbol) {
697 init_terminal(expr, 2, nvars, n_nvars, svars, n_svars);
698 return true;
699 }
700 next_value = 0;
701 }
702 expr->cmp.string = xasprintf("%d", next_value);
703 return true;
704 }
705
e0840f11
BP
706 unsigned int next;
707
708 next = (ntohll(expr->cmp.value.integer)
709 + (ntohll(expr->cmp.mask.integer) << n_bits));
710 for (;;) {
711 next++;
712 unsigned m = next >> n_bits;
713 unsigned v = next & ((1u << n_bits) - 1);
714 if (next >= (1u << (2 * n_bits))) {
715 enum expr_relop old_relop = expr->cmp.relop;
716 expr->cmp.relop = next_relop(old_relop);
717 if (expr->cmp.relop <= old_relop) {
9d4aecca 718 expr->cmp.symbol = next_var(expr->cmp.symbol, nvars, n_nvars);
e0840f11 719 if (!expr->cmp.symbol) {
9d4aecca 720 init_terminal(expr, 1, nvars, n_nvars, svars, n_svars);
e0840f11
BP
721 return true;
722 }
723 }
8c3caa2c 724 next = UINT_MAX;
e0840f11
BP
725 } else if (v & ~m) {
726 /* Skip: 1-bits in value correspond to 0-bits in mask. */
8c3caa2c 727 } else if ((!m || turn_off_rightmost_1s(m))
e0840f11
BP
728 && (expr->cmp.relop != EXPR_R_EQ &&
729 expr->cmp.relop != EXPR_R_NE)) {
8c3caa2c 730 /* Skip: can't have discontiguous or all-0 mask for > >= < <=. */
e0840f11
BP
731 } else {
732 expr->cmp.value.integer = htonll(v);
733 expr->cmp.mask.integer = htonll(m);
734 return true;
735 }
736 }
737}
738\f
739static struct expr *
740make_terminal(struct expr ***terminalp)
741{
742 struct expr *e = expr_create_boolean(true);
743 **terminalp = e;
744 (*terminalp)++;
745 return e;
746}
747
748static struct expr *
749build_simple_tree(enum expr_type type, int n, struct expr ***terminalp)
750{
751 if (n == 2) {
752 struct expr *e = expr_create_andor(type);
753 for (int i = 0; i < 2; i++) {
754 struct expr *sub = make_terminal(terminalp);
417e7e66 755 ovs_list_push_back(&e->andor, &sub->node);
e0840f11
BP
756 }
757 return e;
758 } else if (n == 1) {
759 return make_terminal(terminalp);
760 } else {
761 OVS_NOT_REACHED();
762 }
763}
764
765static struct expr *
766build_tree_shape(enum expr_type type, const struct tree_shape **tsp,
767 struct expr ***terminalp)
768{
769 const struct tree_shape *ts = *tsp;
770 (*tsp)++;
771
772 struct expr *e = expr_create_andor(type);
773 enum expr_type t = type == EXPR_T_AND ? EXPR_T_OR : EXPR_T_AND;
774 for (int i = 0; i < ts->sn; i++) {
775 struct expr *sub = (ts->s[i] > 2
776 ? build_tree_shape(t, tsp, terminalp)
777 : build_simple_tree(t, ts->s[i], terminalp));
417e7e66 778 ovs_list_push_back(&e->andor, &sub->node);
e0840f11
BP
779 }
780 return e;
781}
782
783struct test_rule {
784 struct cls_rule cr;
785};
786
787static void
788free_rule(struct test_rule *test_rule)
789{
790 cls_rule_destroy(&test_rule->cr);
791 free(test_rule);
792}
793
794static int
795test_tree_shape_exhaustively(struct expr *expr, struct shash *symtab,
796 struct expr *terminals[], int n_terminals,
9d4aecca
BP
797 const struct expr_symbol *nvars[], int n_nvars,
798 int n_bits,
799 const struct expr_symbol *svars[], int n_svars)
e0840f11
BP
800{
801 int n_tested = 0;
802
803 const unsigned int var_mask = (1u << n_bits) - 1;
804 for (int i = 0; i < n_terminals; i++) {
9d4aecca 805 init_terminal(terminals[i], 0, nvars, n_nvars, svars, n_svars);
e0840f11
BP
806 }
807
808 struct ds s = DS_EMPTY_INITIALIZER;
809 struct flow f;
810 memset(&f, 0, sizeof f);
811 for (;;) {
812 for (int i = n_terminals - 1; ; i--) {
813 if (!i) {
814 ds_destroy(&s);
815 return n_tested;
816 }
9d4aecca
BP
817 if (next_terminal(terminals[i], nvars, n_nvars, n_bits,
818 svars, n_svars)) {
e0840f11
BP
819 break;
820 }
9d4aecca 821 init_terminal(terminals[i], 0, nvars, n_nvars, svars, n_svars);
e0840f11
BP
822 }
823 ovs_assert(expr_honors_invariants(expr));
824
825 n_tested++;
826
827 struct expr *modified;
828 if (operation == OP_CONVERT) {
829 ds_clear(&s);
830 expr_format(expr, &s);
831
832 char *error;
2c5cbb15 833 modified = expr_parse_string(ds_cstr(&s), symtab, NULL, &error);
e0840f11
BP
834 if (error) {
835 fprintf(stderr, "%s fails to parse (%s)\n",
836 ds_cstr(&s), error);
837 exit(EXIT_FAILURE);
838 }
839 } else if (operation >= OP_SIMPLIFY) {
840 modified = expr_simplify(expr_clone(expr));
841 ovs_assert(expr_honors_invariants(modified));
842
843 if (operation >= OP_NORMALIZE) {
844 modified = expr_normalize(modified);
845 ovs_assert(expr_is_normalized(modified));
846 }
847 }
848
849 struct hmap matches;
850 struct classifier cls;
851 if (operation >= OP_FLOW) {
852 struct expr_match *m;
853 struct test_rule *test_rule;
e0840f11 854
803a6f3d 855 expr_to_matches(modified, lookup_atoi_cb, NULL, &matches);
e0840f11
BP
856
857 classifier_init(&cls, NULL);
858 HMAP_FOR_EACH (m, hmap_node, &matches) {
859 test_rule = xmalloc(sizeof *test_rule);
bd53aa17 860 cls_rule_init(&test_rule->cr, &m->match, 0);
44e0c35d 861 classifier_insert(&cls, &test_rule->cr, OVS_VERSION_MIN,
bd53aa17 862 m->conjunctions, m->n);
e0840f11 863 }
e0840f11 864 }
9d4aecca
BP
865 for (int subst = 0; subst < 1 << (n_bits * n_nvars + n_svars);
866 subst++) {
803a6f3d
BP
867 for (int i = 0; i < n_nvars; i++) {
868 f.regs[i] = (subst >> (i * n_bits)) & var_mask;
869 }
870 for (int i = 0; i < n_svars; i++) {
871 f.regs[n_nvars + i] = ((subst >> (n_nvars * n_bits + i))
872 & 1);
873 }
874
875 bool expected = expr_evaluate(expr, &f, lookup_atoi_cb, NULL);
876 bool actual = expr_evaluate(modified, &f, lookup_atoi_cb, NULL);
e0840f11
BP
877 if (actual != expected) {
878 struct ds expr_s, modified_s;
879
880 ds_init(&expr_s);
881 expr_format(expr, &expr_s);
882
883 ds_init(&modified_s);
884 expr_format(modified, &modified_s);
885
886 fprintf(stderr,
887 "%s evaluates to %d, but %s evaluates to %d, for",
888 ds_cstr(&expr_s), expected,
889 ds_cstr(&modified_s), actual);
9d4aecca 890 for (int i = 0; i < n_nvars; i++) {
e0840f11
BP
891 if (i > 0) {
892 fputs(",", stderr);
893 }
9d4aecca 894 fprintf(stderr, " n%d = 0x%x", i,
e0840f11
BP
895 (subst >> (n_bits * i)) & var_mask);
896 }
9d4aecca
BP
897 for (int i = 0; i < n_svars; i++) {
898 fprintf(stderr, ", s%d = \"%d\"", i,
899 (subst >> (n_bits * n_nvars + i)) & 1);
900 }
e0840f11
BP
901 putc('\n', stderr);
902 exit(EXIT_FAILURE);
903 }
904
905 if (operation >= OP_FLOW) {
44e0c35d 906 bool found = classifier_lookup(&cls, OVS_VERSION_MIN,
03ce866e 907 &f, NULL) != NULL;
e0840f11
BP
908 if (expected != found) {
909 struct ds expr_s, modified_s;
910
911 ds_init(&expr_s);
912 expr_format(expr, &expr_s);
913
914 ds_init(&modified_s);
915 expr_format(modified, &modified_s);
916
917 fprintf(stderr,
918 "%s and %s evaluate to %d, for",
919 ds_cstr(&expr_s), ds_cstr(&modified_s), expected);
9d4aecca 920 for (int i = 0; i < n_nvars; i++) {
e0840f11
BP
921 if (i > 0) {
922 fputs(",", stderr);
923 }
9d4aecca 924 fprintf(stderr, " n%d = 0x%x", i,
e0840f11
BP
925 (subst >> (n_bits * i)) & var_mask);
926 }
9d4aecca
BP
927 for (int i = 0; i < n_svars; i++) {
928 fprintf(stderr, ", s%d = \"%d\"", i,
929 (subst >> (n_bits * n_nvars + i)) & 1);
930 }
e0840f11
BP
931 fputs(".\n", stderr);
932
933 fprintf(stderr, "Converted to classifier:\n");
f386a8a7 934 expr_matches_print(&matches, stderr);
e0840f11
BP
935 fprintf(stderr,
936 "However, %s flow was found in the classifier.\n",
937 found ? "a" : "no");
938 exit(EXIT_FAILURE);
939 }
940 }
941 }
942 if (operation >= OP_FLOW) {
e0840f11
BP
943 struct test_rule *test_rule;
944
945 CLS_FOR_EACH (test_rule, cr, &cls) {
946 classifier_remove(&cls, &test_rule->cr);
947 ovsrcu_postpone(free_rule, test_rule);
948 }
949 classifier_destroy(&cls);
950 ovsrcu_quiesce();
951
f386a8a7 952 expr_matches_destroy(&matches);
e0840f11
BP
953 }
954 expr_destroy(modified);
955 }
956}
957
958#ifndef _WIN32
959static void
960wait_pid(pid_t *pids, int *n)
961{
962 int status;
963 pid_t pid;
964
965 pid = waitpid(WAIT_ANY, &status, 0);
966 if (pid < 0) {
967 ovs_fatal(errno, "waitpid failed");
968 } else if (WIFEXITED(status)) {
969 if (WEXITSTATUS(status)) {
970 exit(WEXITSTATUS(status));
971 }
972 } else if (WIFSIGNALED(status)) {
973 raise(WTERMSIG(status));
974 exit(1);
975 } else {
976 OVS_NOT_REACHED();
977 }
978
979 for (int i = 0; i < *n; i++) {
980 if (pids[i] == pid) {
981 pids[i] = pids[--*n];
982 return;
983 }
984 }
985 ovs_fatal(0, "waitpid returned unknown child");
986}
987#endif
988
989static void
990test_exhaustive(struct ovs_cmdl_context *ctx OVS_UNUSED)
991{
992 int n_terminals = atoi(ctx->argv[1]);
993 struct tree_shape ts[50];
994 int n_tses;
995
996 struct shash symtab;
9d4aecca
BP
997 const struct expr_symbol *nvars[4];
998 const struct expr_symbol *svars[4];
e0840f11 999
9d4aecca
BP
1000 ovs_assert(test_nvars <= ARRAY_SIZE(nvars));
1001 ovs_assert(test_svars <= ARRAY_SIZE(svars));
1002 ovs_assert(test_nvars + test_svars <= FLOW_N_REGS);
e0840f11
BP
1003
1004 shash_init(&symtab);
9d4aecca
BP
1005 for (int i = 0; i < test_nvars; i++) {
1006 char *name = xasprintf("n%d", i);
1007 nvars[i] = expr_symtab_add_field(&symtab, name, MFF_REG0 + i, NULL,
1008 false);
1009 free(name);
1010 }
1011 for (int i = 0; i < test_svars; i++) {
1012 char *name = xasprintf("s%d", i);
1013 svars[i] = expr_symtab_add_string(&symtab, name,
1014 MFF_REG0 + test_nvars + i, NULL);
1015 free(name);
e0840f11
BP
1016 }
1017
1018#ifndef _WIN32
1019 pid_t *children = xmalloc(test_parallel * sizeof *children);
1020 int n_children = 0;
1021#endif
1022
1023 int n_tested = 0;
1024 for (int i = 0; i < 2; i++) {
1025 enum expr_type base_type = i ? EXPR_T_OR : EXPR_T_AND;
1026
1027 for (n_tses = init_tree_shape(ts, n_terminals); n_tses;
1028 n_tses = next_tree_shape(ts, n_tses)) {
1029 const struct tree_shape *tsp = ts;
1030 struct expr *terminals[50];
1031 struct expr **terminalp = terminals;
1032 struct expr *expr = build_tree_shape(base_type, &tsp, &terminalp);
1033 ovs_assert(terminalp == &terminals[n_terminals]);
1034
1035 if (verbosity > 0) {
1036 print_tree_shape(ts, n_tses);
1037 printf(": ");
1038 struct ds s = DS_EMPTY_INITIALIZER;
1039 expr_format(expr, &s);
1040 puts(ds_cstr(&s));
1041 ds_destroy(&s);
1042 }
1043
1044#ifndef _WIN32
1045 if (test_parallel > 1) {
1046 pid_t pid = xfork();
1047 if (!pid) {
1048 test_tree_shape_exhaustively(expr, &symtab,
1049 terminals, n_terminals,
9d4aecca
BP
1050 nvars, test_nvars, test_bits,
1051 svars, test_svars);
e0840f11
BP
1052 expr_destroy(expr);
1053 exit(0);
1054 } else {
1055 if (n_children >= test_parallel) {
1056 wait_pid(children, &n_children);
1057 }
1058 children[n_children++] = pid;
1059 }
1060 } else
1061#endif
1062 {
1063 n_tested += test_tree_shape_exhaustively(
1064 expr, &symtab, terminals, n_terminals,
9d4aecca
BP
1065 nvars, test_nvars, test_bits,
1066 svars, test_svars);
e0840f11
BP
1067 }
1068 expr_destroy(expr);
1069 }
1070 }
1071#ifndef _WIN32
1072 while (n_children > 0) {
1073 wait_pid(children, &n_children);
1074 }
1075 free(children);
1076#endif
1077
1078 printf("Tested ");
1079 switch (operation) {
1080 case OP_CONVERT:
1081 printf("converting");
1082 break;
1083 case OP_SIMPLIFY:
1084 printf("simplifying");
1085 break;
1086 case OP_NORMALIZE:
1087 printf("normalizing");
1088 break;
1089 case OP_FLOW:
1090 printf("converting to flows");
1091 break;
1092 }
1093 if (n_tested) {
1094 printf(" %d expressions of %d terminals", n_tested, n_terminals);
1095 } else {
1096 printf(" all %d-terminal expressions", n_terminals);
1097 }
9d4aecca
BP
1098 if (test_nvars || test_svars) {
1099 printf(" with");
1100 if (test_nvars) {
1101 printf(" %d numeric vars (each %d bits) in terms of operators",
1102 test_nvars, test_bits);
1103 for (unsigned int relops = test_relops; relops;
1104 relops = zero_rightmost_1bit(relops)) {
1105 enum expr_relop r = rightmost_1bit_idx(relops);
1106 printf(" %s", expr_relop_to_string(r));
1107 }
1108 }
1109 if (test_nvars && test_svars) {
1110 printf (" and");
1111 }
1112 if (test_svars) {
1113 printf(" %d string vars", test_svars);
1114 }
1115 } else {
1116 printf(" in terms of Boolean constants only");
e0840f11
BP
1117 }
1118 printf(".\n");
1119
1120 expr_symtab_destroy(&symtab);
1121 shash_destroy(&symtab);
1122}
1123\f
3b7cb7e1
BP
1124/* Actions. */
1125
1126static void
1127test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED)
1128{
1129 struct shash symtab;
42814145 1130 struct hmap dhcp_opts;
01cfdb2f 1131 struct hmap dhcpv6_opts;
78aab811 1132 struct simap ports, ct_zones;
3b7cb7e1 1133 struct ds input;
d5a76da4 1134 bool ok = true;
3b7cb7e1
BP
1135
1136 create_symtab(&symtab);
01cfdb2f 1137 create_dhcp_opts(&dhcp_opts, &dhcpv6_opts);
3b7cb7e1 1138
467085fd
GS
1139 /* Initialize group ids. */
1140 struct group_table group_table;
1141 group_table.group_ids = bitmap_allocate(MAX_OVN_GROUPS);
1142 bitmap_set1(group_table.group_ids, 0); /* Group id 0 is invalid. */
1143 hmap_init(&group_table.desired_groups);
1144 hmap_init(&group_table.existing_groups);
1145
3b7cb7e1
BP
1146 simap_init(&ports);
1147 simap_put(&ports, "eth0", 5);
1148 simap_put(&ports, "eth1", 6);
1149 simap_put(&ports, "LOCAL", ofp_to_u16(OFPP_LOCAL));
78aab811 1150 simap_init(&ct_zones);
3b7cb7e1
BP
1151
1152 ds_init(&input);
1153 while (!ds_get_test_line(&input, stdin)) {
d5a76da4 1154 struct ofpbuf ovnacts;
3b7cb7e1
BP
1155 struct expr *prereqs;
1156 char *error;
1157
d5a76da4 1158 puts(ds_cstr(&input));
1d7b2ece 1159
d5a76da4
BP
1160 ofpbuf_init(&ovnacts, 0);
1161
1162 const struct ovnact_parse_params pp = {
1d7b2ece 1163 .symtab = &symtab,
42814145 1164 .dhcp_opts = &dhcp_opts,
01cfdb2f 1165 .dhcpv6_opts = &dhcpv6_opts,
1d7b2ece 1166 .n_tables = 16,
1d7b2ece 1167 .cur_ltable = 10,
1d7b2ece 1168 };
d5a76da4 1169 error = ovnacts_parse_string(ds_cstr(&input), &pp, &ovnacts, &prereqs);
3b7cb7e1 1170 if (!error) {
d5a76da4
BP
1171 /* Convert the parsed representation back to a string and print it,
1172 * if it's different from the input. */
1173 struct ds ovnacts_s = DS_EMPTY_INITIALIZER;
1174 ovnacts_format(ovnacts.data, ovnacts.size, &ovnacts_s);
1175 if (strcmp(ds_cstr(&input), ds_cstr(&ovnacts_s))) {
1176 printf(" formats as %s\n", ds_cstr(&ovnacts_s));
1177 }
3b7cb7e1 1178
d5a76da4
BP
1179 /* Encode the actions into OpenFlow and print. */
1180 const struct ovnact_encode_params ep = {
1181 .lookup_port = lookup_port_cb,
1182 .aux = &ports,
c2e954a1 1183 .is_switch = true,
d5a76da4
BP
1184 .ct_zones = &ct_zones,
1185 .group_table = &group_table,
1186
1187 .first_ptable = 16,
1188 .output_ptable = 64,
1189 .mac_bind_ptable = 65,
1190 };
1191 struct ofpbuf ofpacts;
1192 ofpbuf_init(&ofpacts, 0);
1193 ovnacts_encode(ovnacts.data, ovnacts.size, &ep, &ofpacts);
1194 struct ds ofpacts_s = DS_EMPTY_INITIALIZER;
1195 ofpacts_format(ofpacts.data, ofpacts.size, &ofpacts_s);
1196 printf(" encodes as %s\n", ds_cstr(&ofpacts_s));
1197 ds_destroy(&ofpacts_s);
1198 ofpbuf_uninit(&ofpacts);
1199
1200 /* Print prerequisites if any. */
3b7cb7e1 1201 if (prereqs) {
d5a76da4
BP
1202 struct ds prereqs_s = DS_EMPTY_INITIALIZER;
1203 expr_format(prereqs, &prereqs_s);
1204 printf(" has prereqs %s\n", ds_cstr(&prereqs_s));
1205 ds_destroy(&prereqs_s);
1206 }
1207
1208 /* Now re-parse and re-format the string to verify that it's
1209 * round-trippable. */
1210 struct ofpbuf ovnacts2;
1211 struct expr *prereqs2;
1212 ofpbuf_init(&ovnacts2, 0);
1213 error = ovnacts_parse_string(ds_cstr(&ovnacts_s), &pp, &ovnacts2,
1214 &prereqs2);
1215 if (!error) {
1216 struct ds ovnacts2_s = DS_EMPTY_INITIALIZER;
1217 ovnacts_format(ovnacts2.data, ovnacts2.size, &ovnacts2_s);
1218 if (strcmp(ds_cstr(&ovnacts_s), ds_cstr(&ovnacts2_s))) {
1219 printf(" bad reformat: %s\n", ds_cstr(&ovnacts2_s));
1220 ok = false;
1221 }
1222 ds_destroy(&ovnacts2_s);
3b7cb7e1 1223 } else {
d5a76da4
BP
1224 printf(" reparse error: %s\n", error);
1225 free(error);
1226 ok = false;
3b7cb7e1 1227 }
d5a76da4
BP
1228 expr_destroy(prereqs2);
1229
85d87d16
RM
1230 ovnacts_free(ovnacts2.data, ovnacts2.size);
1231 ofpbuf_uninit(&ovnacts2);
d5a76da4 1232 ds_destroy(&ovnacts_s);
3b7cb7e1 1233 } else {
d5a76da4 1234 printf(" %s\n", error);
3b7cb7e1
BP
1235 free(error);
1236 }
1237
1238 expr_destroy(prereqs);
85d87d16 1239 ovnacts_free(ovnacts.data, ovnacts.size);
d5a76da4 1240 ofpbuf_uninit(&ovnacts);
3b7cb7e1
BP
1241 }
1242 ds_destroy(&input);
1243
1244 simap_destroy(&ports);
78aab811 1245 simap_destroy(&ct_zones);
3b7cb7e1
BP
1246 expr_symtab_destroy(&symtab);
1247 shash_destroy(&symtab);
01cfdb2f
NS
1248 dhcp_opts_destroy(&dhcp_opts);
1249 dhcp_opts_destroy(&dhcpv6_opts);
d5a76da4
BP
1250
1251 exit(ok ? EXIT_SUCCESS : EXIT_FAILURE);
3b7cb7e1
BP
1252}
1253\f
e0840f11
BP
1254static unsigned int
1255parse_relops(const char *s)
1256{
1257 unsigned int relops = 0;
1258 struct lexer lexer;
1259
1260 lexer_init(&lexer, s);
1261 lexer_get(&lexer);
1262 do {
1263 enum expr_relop relop;
1264
1265 if (expr_relop_from_token(lexer.token.type, &relop)) {
1266 relops |= 1u << relop;
1267 lexer_get(&lexer);
1268 } else {
1269 ovs_fatal(0, "%s: relational operator expected at `%.*s'",
1270 s, (int) (lexer.input - lexer.start), lexer.start);
1271 }
1272 lexer_match(&lexer, LEX_T_COMMA);
1273 } while (lexer.token.type != LEX_T_END);
1274 lexer_destroy(&lexer);
1275
1276 return relops;
1277}
1278
1279static void
1280usage(void)
1281{
1282 printf("\
1283%s: OVN test utility\n\
1284usage: test-ovn %s [OPTIONS] COMMAND [ARG...]\n\
1285\n\
1286lex\n\
1287 Lexically analyzes OVN input from stdin and print them back on stdout.\n\
1288\n\
1289parse-expr\n\
1290annotate-expr\n\
1291simplify-expr\n\
1292normalize-expr\n\
f386a8a7 1293expr-to-flows\n\
e0840f11
BP
1294 Parses OVN expressions from stdin and print them back on stdout after\n\
1295 differing degrees of analysis. Available fields are based on packet\n\
1296 headers.\n\
1297\n\
d5b33279
BP
1298evaluate-expr MICROFLOW\n\
1299 Parses OVN expressions from stdin and evaluates them against the flow\n\
1300 specified in MICROFLOW, which must be an expression that constrains\n\
1301 the packet, e.g. \"ip4 && tcp.src == 80\" for a TCP packet with source\n\
1302 port 80, and prints the results on stdout, either 1 for true or 0 for\n\
1303 false. Use quoted integers, e.g. \"123\", for string fields.\n\
1304\n\
1305 Example: for MICROFLOW of \"ip4 && tcp.src == 80\", \"eth.type == 0x800\"\n\
1306 evaluates to true, \"udp\" evaluates to false, and \"udp || tcp\"\n\
1307 evaluates to true.\n\
e0840f11
BP
1308\n\
1309composition N\n\
1310 Prints all the compositions of N on stdout.\n\
1311\n\
1312tree-shape N\n\
1313 Prints all the tree shapes with N terminals on stdout.\n\
1314\n\
1315exhaustive N\n\
1316 Tests that all possible Boolean expressions with N terminals are properly\n\
1317 simplified, normalized, and converted to flows. Available options:\n\
9d4aecca 1318 Overall options:\n\
e0840f11
BP
1319 --operation=OPERATION Operation to test, one of: convert, simplify,\n\
1320 normalize, flow. Default: flow. 'normalize' includes 'simplify',\n\
9d4aecca 1321 'flow' includes 'simplify' and 'normalize'.\n\
e0840f11 1322 --parallel=N Number of processes to use in parallel, default 1.\n\
9d4aecca
BP
1323 Numeric vars:\n\
1324 --nvars=N Number of numeric vars to test, in range 0...4, default 2.\n\
1325 --bits=N Number of bits per variable, in range 1...3, default 3.\n\
1326 --relops=OPERATORS Test only the specified Boolean operators.\n\
1327 OPERATORS may include == != < <= > >=, space or\n\
1328 comma separated. Default is all operators.\n\
1329 String vars:\n\
1330 --svars=N Number of string vars to test, in range 0...4, default 2.\n\
caff23ca
BP
1331\n\
1332parse-actions\n\
1333 Parses OVN actions from stdin and prints the equivalent OpenFlow actions\n\
1334 on stdout.\n\
e0840f11
BP
1335",
1336 program_name, program_name);
1337 exit(EXIT_SUCCESS);
1338}
1339
10b1662b
BP
1340static void
1341test_ovn_main(int argc, char *argv[])
1342{
9d4aecca
BP
1343 enum {
1344 OPT_RELOPS = UCHAR_MAX + 1,
1345 OPT_NVARS,
1346 OPT_SVARS,
1347 OPT_BITS,
1348 OPT_OPERATION,
1349 OPT_PARALLEL
1350 };
1351 static const struct option long_options[] = {
1352 {"relops", required_argument, NULL, OPT_RELOPS},
1353 {"nvars", required_argument, NULL, OPT_NVARS},
1354 {"svars", required_argument, NULL, OPT_SVARS},
1355 {"bits", required_argument, NULL, OPT_BITS},
1356 {"operation", required_argument, NULL, OPT_OPERATION},
1357 {"parallel", required_argument, NULL, OPT_PARALLEL},
1358 {"more", no_argument, NULL, 'm'},
1359 {"help", no_argument, NULL, 'h'},
1360 {NULL, 0, NULL, 0},
1361 };
1362 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
1363
10b1662b
BP
1364 set_program_name(argv[0]);
1365
e0840f11
BP
1366 test_relops = parse_relops("== != < <= > >=");
1367 for (;;) {
e0840f11 1368 int option_index = 0;
9d4aecca
BP
1369 int c = getopt_long (argc, argv, short_options, long_options,
1370 &option_index);
e0840f11
BP
1371
1372 if (c == -1) {
1373 break;
1374 }
1375 switch (c) {
1376 case OPT_RELOPS:
1377 test_relops = parse_relops(optarg);
1378 break;
1379
9d4aecca
BP
1380 case OPT_NVARS:
1381 test_nvars = atoi(optarg);
1382 if (test_nvars < 0 || test_nvars > 4) {
1383 ovs_fatal(0, "number of numeric variables must be "
1384 "between 0 and 4");
1385 }
1386 break;
1387
1388 case OPT_SVARS:
1389 test_svars = atoi(optarg);
1390 if (test_svars < 0 || test_svars > 4) {
1391 ovs_fatal(0, "number of string variables must be "
1392 "between 0 and 4");
e0840f11
BP
1393 }
1394 break;
1395
1396 case OPT_BITS:
1397 test_bits = atoi(optarg);
1398 if (test_bits < 1 || test_bits > 3) {
1399 ovs_fatal(0, "number of bits must be between 1 and 3");
1400 }
1401 break;
1402
1403 case OPT_OPERATION:
1404 if (!strcmp(optarg, "convert")) {
1405 operation = OP_CONVERT;
1406 } else if (!strcmp(optarg, "simplify")) {
1407 operation = OP_SIMPLIFY;
1408 } else if (!strcmp(optarg, "normalize")) {
1409 operation = OP_NORMALIZE;
1410 } else if (!strcmp(optarg, "flow")) {
1411 operation = OP_FLOW;
1412 } else {
1413 ovs_fatal(0, "%s: unknown operation", optarg);
1414 }
1415 break;
1416
1417 case OPT_PARALLEL:
1418 test_parallel = atoi(optarg);
1419 break;
1420
1421 case 'm':
1422 verbosity++;
1423 break;
1424
1425 case 'h':
1426 usage();
1427
1428 case '?':
1429 exit(1);
1430
1431 default:
1432 abort();
1433 }
1434 }
d2730f4c 1435 free(short_options);
e0840f11 1436
10b1662b 1437 static const struct ovs_cmdl_command commands[] = {
3b7cb7e1 1438 /* Lexer. */
1f4a7252 1439 {"lex", NULL, 0, 0, test_lex, OVS_RO},
3b7cb7e1 1440
7700eea0 1441 /* Symbol table. */
1f4a7252 1442 {"dump-symtab", NULL, 0, 0, test_dump_symtab, OVS_RO},
7700eea0 1443
3b7cb7e1 1444 /* Expressions. */
1f4a7252
RM
1445 {"parse-expr", NULL, 0, 0, test_parse_expr, OVS_RO},
1446 {"annotate-expr", NULL, 0, 0, test_annotate_expr, OVS_RO},
1447 {"simplify-expr", NULL, 0, 0, test_simplify_expr, OVS_RO},
1448 {"normalize-expr", NULL, 0, 0, test_normalize_expr, OVS_RO},
1449 {"expr-to-flows", NULL, 0, 0, test_expr_to_flows, OVS_RO},
1450 {"evaluate-expr", NULL, 1, 1, test_evaluate_expr, OVS_RO},
1451 {"composition", NULL, 1, 1, test_composition, OVS_RO},
1452 {"tree-shape", NULL, 1, 1, test_tree_shape, OVS_RO},
1453 {"exhaustive", NULL, 1, 1, test_exhaustive, OVS_RO},
3b7cb7e1
BP
1454
1455 /* Actions. */
1f4a7252 1456 {"parse-actions", NULL, 0, 0, test_parse_actions, OVS_RO},
3b7cb7e1 1457
1f4a7252 1458 {NULL, NULL, 0, 0, NULL, OVS_RO},
10b1662b
BP
1459 };
1460 struct ovs_cmdl_context ctx;
1461 ctx.argc = argc - optind;
1462 ctx.argv = argv + optind;
1463 ovs_cmdl_run_command(&ctx, commands);
1464}
1465
1466OVSTEST_REGISTER("test-ovn", test_ovn_main);