summaryrefslogtreecommitdiff
path: root/include/ovn/expr.h
blob: 569c524c5ffcba19e96735db719bd80df1611409 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
/*
 * Copyright (c) 2015, 2016 Nicira, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef OVN_EXPR_H
#define OVN_EXPR_H 1

/* OVN matching expression tree
 * ============================
 *
 * The data structures here form an abstract expression tree for matching
 * expressions in OVN.
 *
 * The abstract syntax tree representation of a matching expression is one of:
 *
 *    - A Boolean literal ("true" or "false").
 *
 *    - A comparison of a field (or part of a field) against a constant
 *      with one of the operators == != < <= > >=.
 *
 *    - The logical AND or OR of two or more matching expressions.
 *
 * Literals and comparisons are called "terminal" nodes, logical AND and OR
 * nodes are "nonterminal" nodes.
 *
 * The syntax for expressions includes a few other concepts that are not part
 * of the abstract syntax tree.  In these examples, x is a field, a, b, and c
 * are constants, and e1 and e2 are arbitrary expressions:
 *
 *    - Logical NOT.  The parser implements NOT by inverting the sense of the
 *      operand: !(x == a) becomes x != a, !(e1 && e2) becomes !e1 || !e2, and
 *      so on.
 *
 *    - Set membership.  The parser translates x == {a, b, c} into
 *      x == a || x == b || x == c.
 *
 *    - Reversed comparisons.  The parser translates a < x into x > a.
 *
 *    - Range expressions.  The parser translates a < x < b into
 *      x > a && x < b.
 */

#include "classifier.h"
#include "lex.h"
#include "openvswitch/hmap.h"
#include "openvswitch/list.h"
#include "openvswitch/match.h"
#include "openvswitch/meta-flow.h"

struct ds;
struct ofpbuf;
struct shash;
struct simap;

/* "Measurement level" of a field.  See "Level of Measurement" in the large
 * comment on struct expr_symbol below for more information. */
enum expr_level {
    EXPR_L_NOMINAL,

    /* Boolean values are nominal, however because of their simple nature OVN
     * can allow both equality and inequality tests on them. */
    EXPR_L_BOOLEAN,

    /* Ordinal values can at least be ordered on a scale.  OVN allows equality
     * and inequality and relational tests on ordinal values.  These are the
     * fields on which OVS allows bitwise matching. */
    EXPR_L_ORDINAL
};

const char *expr_level_to_string(enum expr_level);

/* A symbol.
 *
 *
 * Name
 * ====
 *
 * Every symbol must have a name.  To be useful, the name must satisfy the
 * lexer's syntax for an identifier.
 *
 *
 * Width
 * =====
 *
 * Every symbol has a width.  For integer symbols, this is the number of bits
 * in the value; for string symbols, this is 0.
 *
 *
 * Types
 * =====
 *
 * There are three kinds of symbols:
 *
 *   Fields:
 *
 *     One might, for example, define a field named "vlan.tci" to refer to
 *     MFF_VLAN_TCI.  For integer fields, 'field' specifies the referent; for
 *     string fields, 'field' is NULL.
 *
 *     'expansion' is NULL.
 *
 *     Integer fields can be nominal or ordinal (see below).  String fields are
 *     always nominal.
 *
 *   Subfields:
 *
 *     'expansion' is a string that specifies a subfield of some larger field,
 *     e.g. "vlan.tci[0..11]" for a field that represents a VLAN VID.
 *
 *     'field' is NULL.
 *
 *     Only ordinal fields (see below) may have subfields, and subfields are
 *     always ordinal.
 *
 *   Predicates:
 *
 *     A predicate is an arbitrary Boolean expression that can be used in an
 *     expression much like a 1-bit field.  'expansion' specifies the Boolean
 *     expression, e.g. "ip4" might expand to "eth.type == 0x800".  The
 *     expansion of a predicate might refer to other predicates, e.g. "icmp4"
 *     might expand to "ip4 && ip4.proto == 1".
 *
 *     'field' is NULL.
 *
 *     A predicate whose expansion refers to any nominal field or predicate
 *     (see below) is nominal; other predicates have Boolean level of
 *     measurement.
 *
 *
 * Level of Measurement
 * ====================
 *
 * See http://en.wikipedia.org/wiki/Level_of_measurement for the statistical
 * concept on which this classification is based.  There are three levels:
 *
 *   Ordinal:
 *
 *     In statistics, ordinal values can be ordered on a scale.  Here, we
 *     consider a field (or subfield) to be ordinal if its bits can be examined
 *     individually.  This is true for the OpenFlow fields that OpenFlow or
 *     Open vSwitch makes "maskable".
 *
 *     OVN supports all the usual arithmetic relations (== != < <= > >=) on
 *     ordinal fields and their subfields, because all of these can be
 *     implemented as collections of bitwise tests.
 *
 *   Nominal:
 *
 *     In statistics, nominal values cannot be usefully compared except for
 *     equality.  This is true of OpenFlow port numbers, Ethernet types, and IP
 *     protocols are examples: all of these are just identifiers assigned
 *     arbitrarily with no deeper meaning.  In OpenFlow and Open vSwitch, bits
 *     in these fields generally aren't individually addressable.
 *
 *     OVN only supports arithmetic tests for equality on nominal fields,
 *     because OpenFlow and Open vSwitch provide no way for a flow to
 *     efficiently implement other comparisons on them.  (A test for inequality
 *     can be sort of built out of two flows with different priorities, but OVN
 *     matching expressions always generate flows with a single priority.)
 *
 *     String fields are always nominal.
 *
 *   Boolean:
 *
 *     A nominal field that has only two values, 0 and 1, is somewhat
 *     exceptional, since it is easy to support both equality and inequality
 *     tests on such a field: either one can be implemented as a test for 0 or
 *     1.
 *
 *     Only predicates (see above) have a Boolean level of measurement.
 *
 *     This isn't a standard level of measurement.
 *
 *
 * Prerequisites
 * =============
 *
 * Any symbol can have prerequisites, which are specified as a string giving an
 * additional expression that must be true whenever the symbol is referenced.
 * For example, the "icmp4.type" symbol might have prerequisite "icmp4", which
 * would cause an expression "icmp4.type == 0" to be interpreted as "icmp4.type
 * == 0 && icmp4", which would in turn expand to "icmp4.type == 0 && eth.type
 * == 0x800 && ip4.proto == 1" (assuming "icmp4" is a predicate defined as
 * suggested under "Types" above).
 *
 *
 * Crossproducting
 * ===============
 *
 * Ordinarily OVN is willing to consider using any field as a dimension in the
 * Open vSwitch "conjunctive match" extension (see ovs-ofctl(8)).  However,
 * some fields can't actually be used that way because they are necessary as
 * prerequisites.  For example, from an expression like "tcp.src == {1,2,3}
 * && tcp.dst == {4,5,6}", OVN might naturally generate flows like this:
 *
 *     conj_id=1,actions=...
 *     ip,actions=conjunction(1,1/3)
 *     ip6,actions=conjunction(1,1/3)
 *     tp_src=1,actions=conjunction(1,2/3)
 *     tp_src=2,actions=conjunction(1,2/3)
 *     tp_src=3,actions=conjunction(1,2/3)
 *     tp_dst=4,actions=conjunction(1,3/3)
 *     tp_dst=5,actions=conjunction(1,3/3)
 *     tp_dst=6,actions=conjunction(1,3/3)
 *
 * but that's not valid because any flow that matches on tp_src or tp_dst must
 * also match on either ip or ip6.  Thus, one would mark eth.type as "must
 * crossproduct", to force generating flows like this:
 *
 *     conj_id=1,actions=...
 *     ip,tp_src=1,actions=conjunction(1,1/2)
 *     ip,tp_src=2,actions=conjunction(1,1/2)
 *     ip,tp_src=3,actions=conjunction(1,1/2)
 *     ip6,tp_src=1,actions=conjunction(1,1/2)
 *     ip6,tp_src=2,actions=conjunction(1,1/2)
 *     ip6,tp_src=3,actions=conjunction(1,1/2)
 *     ip,tp_dst=4,actions=conjunction(1,2/2)
 *     ip,tp_dst=5,actions=conjunction(1,2/2)
 *     ip,tp_dst=6,actions=conjunction(1,2/2)
 *     ip6,tp_dst=4,actions=conjunction(1,2/2)
 *     ip6,tp_dst=5,actions=conjunction(1,2/2)
 *     ip6,tp_dst=6,actions=conjunction(1,2/2)
 *
 * which are acceptable.
 */
struct expr_symbol {
    char *name;
    int width;

    const struct mf_field *field;
    char *expansion;

    enum expr_level level;

    char *prereqs;
    bool must_crossproduct;
    bool rw;
};

/* A reference to a symbol or a subfield of a symbol.
 *
 * For string fields, ofs and n_bits are 0. */
struct expr_field {
    const struct expr_symbol *symbol; /* The symbol. */
    int ofs;                          /* Starting bit offset. */
    int n_bits;                       /* Number of bits. */
};

struct expr_symbol *expr_symtab_add_field(struct shash *symtab,
                                          const char *name, enum mf_field_id,
                                          const char *prereqs,
                                          bool must_crossproduct);
struct expr_symbol *expr_symtab_add_subfield(struct shash *symtab,
                                             const char *name,
                                             const char *prereqs,
                                             const char *subfield);
struct expr_symbol *expr_symtab_add_string(struct shash *symtab,
                                           const char *name, enum mf_field_id,
                                           const char *prereqs);
struct expr_symbol *expr_symtab_add_predicate(struct shash *symtab,
                                              const char *name,
                                              const char *expansion);
void expr_symtab_destroy(struct shash *symtab);

/* Expression type. */
enum expr_type {
    EXPR_T_CMP,                 /* Compare symbol with constant. */
    EXPR_T_AND,                 /* Logical AND of 2 or more subexpressions. */
    EXPR_T_OR,                  /* Logical OR of 2 or more subexpressions. */
    EXPR_T_BOOLEAN,             /* True or false constant. */
};

/* Relational operator. */
enum expr_relop {
    EXPR_R_EQ,                  /* == */
    EXPR_R_NE,                  /* != */
    EXPR_R_LT,                  /* < */
    EXPR_R_LE,                  /* <= */
    EXPR_R_GT,                  /* > */
    EXPR_R_GE,                  /* >= */
};
const char *expr_relop_to_string(enum expr_relop);
bool expr_relop_from_token(enum lex_type type, enum expr_relop *relop);

/* An abstract syntax tree for a matching expression.
 *
 * The expression code maintains and relies on a few important invariants:
 *
 *     - An EXPR_T_AND or EXPR_T_OR node never has a child of the same type.
 *       (Any such children could be merged into their parent.)  A node may
 *       have grandchildren of its own type.
 *
 *       As a consequence, every nonterminal node at the same distance from the
 *       root has the same type.
 *
 *     - EXPR_T_AND and EXPR_T_OR nodes must have at least two children.
 *
 *     - An EXPR_T_CMP node always has a nonzero mask, and never has a 1-bit
 *       in its value in a position where the mask is a 0-bit.
 *
 * The expr_honors_invariants() function can check invariants. */
struct expr {
    struct ovs_list node;       /* In parent EXPR_T_AND or EXPR_T_OR if any. */
    enum expr_type type;        /* Expression type. */

    union {
        /* EXPR_T_CMP.
         *
         * The symbol is on the left, e.g. "field < constant". */
        struct {
            const struct expr_symbol *symbol;
            enum expr_relop relop;

            union {
                char *string;
                struct {
                    union mf_subvalue value;
                    union mf_subvalue mask;
                };
            };
        } cmp;

        /* EXPR_T_AND, EXPR_T_OR. */
        struct ovs_list andor;

        /* EXPR_T_BOOLEAN. */
        bool boolean;
    };
};

struct expr *expr_create_boolean(bool b);
struct expr *expr_create_andor(enum expr_type);
struct expr *expr_combine(enum expr_type, struct expr *a, struct expr *b);

static inline struct expr *
expr_from_node(const struct ovs_list *node)
{
    return CONTAINER_OF(node, struct expr, node);
}

void expr_format(const struct expr *, struct ds *);
void expr_print(const struct expr *);
struct expr *expr_parse(struct lexer *, const struct shash *symtab,
                        const struct shash *macros,
                        char **errorp);
struct expr *expr_parse_string(const char *, const struct shash *symtab,
                               const struct shash *macros,
                               char **errorp);

struct expr *expr_clone(struct expr *);
void expr_destroy(struct expr *);

struct expr *expr_annotate(struct expr *, const struct shash *symtab,
                           char **errorp);
struct expr *expr_simplify(struct expr *);
struct expr *expr_normalize(struct expr *);

bool expr_honors_invariants(const struct expr *);
bool expr_is_simplified(const struct expr *);
bool expr_is_normalized(const struct expr *);

/* Converting expressions to OpenFlow flows. */

/* An OpenFlow match generated from a Boolean expression.  See
 * expr_to_matches() for more information. */
struct expr_match {
    struct hmap_node hmap_node;
    struct match match;
    struct cls_conjunction *conjunctions;
    size_t n, allocated;
};

uint32_t expr_to_matches(const struct expr *,
                         bool (*lookup_port)(const void *aux,
                                             const char *port_name,
                                             unsigned int *portp),
                         const void *aux,
                         struct hmap *matches);
void expr_matches_destroy(struct hmap *matches);
void expr_matches_print(const struct hmap *matches, FILE *);

/* Action parsing helper. */

char *expr_parse_assignment(struct lexer *lexer, struct expr_field *dst,
                            const struct shash *symtab,
                            bool (*lookup_port)(const void *aux,
                                                const char *port_name,
                                                unsigned int *portp),
                            const void *aux,
                            struct ofpbuf *ofpacts, struct expr **prereqsp)
    OVS_WARN_UNUSED_RESULT;
char *expr_parse_exchange(struct lexer *lexer, struct expr_field *dst,
                          const struct shash *symtab,
                          bool (*lookup_port)(const void *aux,
                                              const char *port_name,
                                              unsigned int *portp),
                          const void *aux,
                          struct ofpbuf *ofpacts, struct expr **prereqsp)
    OVS_WARN_UNUSED_RESULT;
char *expr_parse_field(struct lexer *lexer, const struct shash *symtab,
                       struct expr_field *field)
    OVS_WARN_UNUSED_RESULT;
char *expr_expand_field(struct lexer *lexer, const struct shash *symtab,
                        const struct expr_field *orig_field,
                        int n_bits, bool rw,
                        struct mf_subfield *sf, struct expr **prereqsp)
    OVS_WARN_UNUSED_RESULT;

/* Type of a "union expr_constant" or "struct expr_constant_set". */
enum expr_constant_type {
    EXPR_C_INTEGER,
    EXPR_C_STRING
};

/* A string or integer constant (one must know which from context). */
union expr_constant {
    /* Integer constant.
     *
     * The width of a constant isn't always clear, e.g. if you write "1",
     * there's no way to tell whether you mean for that to be a 1-bit constant
     * or a 128-bit constant or somewhere in between. */
    struct {
        union mf_subvalue value;
        union mf_subvalue mask; /* Only initialized if 'masked'. */
        bool masked;

        enum lex_format format; /* From the constant's lex_token. */
    };

    /* Null-terminated string constant. */
    char *string;
};

/* A collection of "union expr_constant"s of the same type. */
struct expr_constant_set {
    union expr_constant *values;  /* Constants. */
    size_t n_values;              /* Number of constants. */
    enum expr_constant_type type; /* Type of the constants. */
    bool in_curlies;              /* Whether the constants were in {}. */
};

char *expr_parse_constant_set(struct lexer *, const struct shash *symtab,
                              struct expr_constant_set *cs)
    OVS_WARN_UNUSED_RESULT;
void expr_constant_set_destroy(struct expr_constant_set *cs);


/* Address sets, aka "macros".
 *
 * Instead of referring to a set of value as:
 *    {addr1, addr2, ..., addrN}
 * You can register a set of values and refer to them as:
 *    $name
 * The macros should all have integer/masked-integer values.
 * The values that don't qualify are ignored.
 */

void expr_macros_add(struct shash *macros, const char *name,
                     const char * const *values, size_t n_values);
void expr_macros_remove(struct shash *macros, const char *name);
void expr_macros_destroy(struct shash *macros);

#endif /* ovn/expr.h */