ovn: Add stateful ACL support.
[cascardo/ovs.git] / ovn / lib / actions.c
index 28be688..aebe5ce 100644 (file)
 #include "lex.h"
 #include "ofp-actions.h"
 #include "ofpbuf.h"
+#include "simap.h"
 
 /* Context maintained during actions_parse(). */
 struct action_context {
-    /* Input. */
+/* Input. */
+
     struct lexer *lexer;        /* Lexer for pulling more tokens. */
-    const struct shash *symtab; /* Symbol table. */
-    uint8_t next_table_id;      /* OpenFlow table for 'next' to resubmit. */
     const struct simap *ports;  /* Map from port name to number. */
+    const struct shash *symtab; /* Symbol table. */
 
-    /* State. */
+    /* OVN maps each logical flow table (ltable), one-to-one, onto a physical
+     * OpenFlow flow table (ptable).  These members describe the mapping and
+     * data related to flow tables. */
+    uint8_t n_tables;           /* Number of flow tables. */
+    uint8_t first_ptable;       /* First OpenFlow table. */
+    uint8_t cur_ltable;         /* 0 <= cur_ltable < n_tables. */
+    uint8_t output_ptable;      /* OpenFlow table for 'output' to resubmit. */
+    const struct simap *ct_zones; /* Map from port name to conntrack zone. */
+
+/* State. */
     char *error;                /* Error, if any, otherwise NULL. */
 
-    /* Output. */
+/* Output. */
     struct ofpbuf *ofpacts;     /* Actions. */
     struct expr *prereqs;       /* Prerequisites to apply to match. */
 };
@@ -104,6 +114,7 @@ action_syntax_error(struct action_context *ctx, const char *message, ...)
     ctx->error = ds_steal_cstr(&s);
 }
 
+/* Parses an assignment or exchange action. */
 static void
 parse_set_action(struct action_context *ctx)
 {
@@ -129,6 +140,83 @@ emit_resubmit(struct action_context *ctx, uint8_t table_id)
     resubmit->table_id = table_id;
 }
 
+static bool
+action_get_int(struct action_context *ctx, int *value)
+{
+    bool ok = lexer_get_int(ctx->lexer, value);
+    if (!ok) {
+        action_syntax_error(ctx, "expecting small integer");
+    }
+    return ok;
+}
+
+static void
+parse_next_action(struct action_context *ctx)
+{
+    if (!ctx->n_tables) {
+        action_error(ctx, "\"next\" action not allowed here.");
+    } else if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
+        int ltable;
+
+        if (!action_get_int(ctx, &ltable)) {
+            return;
+        }
+        if (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
+            action_syntax_error(ctx, "expecting `)'");
+            return;
+        }
+
+        if (ltable >= ctx->n_tables) {
+            action_error(ctx, "\"next\" argument must be in range 0 to %d.",
+                         ctx->n_tables - 1);
+            return;
+        }
+
+        emit_resubmit(ctx, ctx->first_ptable + ltable);
+    } else {
+        if (ctx->cur_ltable < ctx->n_tables) {
+            emit_resubmit(ctx, ctx->first_ptable + ctx->cur_ltable + 1);
+        } else {
+            action_error(ctx, "\"next\" action not allowed in last table.");
+        }
+    }
+}
+
+static void
+emit_ct(struct action_context *ctx, bool recirc_next, bool commit)
+{
+    struct ofpact_conntrack *ct = ofpact_put_CT(ctx->ofpacts);
+    ct->flags |= commit ? NX_CT_F_COMMIT : 0;
+
+    /* If "recirc" is set, we automatically go to the next table. */
+    if (recirc_next) {
+        if (ctx->cur_ltable < ctx->n_tables) {
+            ct->recirc_table = ctx->first_ptable + ctx->cur_ltable + 1;
+        } else {
+            action_error(ctx, "\"ct_next\" action not allowed in last table.");
+            return;
+        }
+    } else {
+        ct->recirc_table = NX_CT_RECIRC_NONE;
+    }
+
+    /* xxx Should remove hard-coding reg5 if we refactor library. */
+    ct->zone_src.field = mf_from_id(MFF_REG5);
+    ct->zone_src.ofs = 0;
+    ct->zone_src.n_bits = 16;
+
+    /* We do not support ALGs yet. */
+    ct->alg = 0;
+
+    /* CT only works with IP, so set up a prerequisite. */
+    struct expr *expr;
+    char *error;
+
+    expr = expr_parse_string("ip", ctx->symtab, &error);
+    ovs_assert(!error);
+    ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, expr);
+}
+
 static void
 parse_actions(struct action_context *ctx)
 {
@@ -152,17 +240,26 @@ parse_actions(struct action_context *ctx)
         }
 
         enum lex_type lookahead = lexer_lookahead(ctx->lexer);
-        if (lookahead == LEX_T_EQUALS || lookahead == LEX_T_LSQUARE) {
+        if (lookahead == LEX_T_EQUALS || lookahead == LEX_T_EXCHANGE
+            || lookahead == LEX_T_LSQUARE) {
             parse_set_action(ctx);
         } else if (lexer_match_id(ctx->lexer, "next")) {
-            if (ctx->next_table_id) {
-                emit_resubmit(ctx, ctx->next_table_id);
+            parse_next_action(ctx);
+        } else if (lexer_match_id(ctx->lexer, "output")) {
+            emit_resubmit(ctx, ctx->output_ptable);
+        } else if (lexer_match_id(ctx->lexer, "ip4.ttl")) {
+            if (lexer_match(ctx->lexer, LEX_T_DECREMENT)) {
+                struct expr *e = expr_parse_string("ip4", ctx->symtab,
+                                                   &ctx->error);
+                ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, e);
+                ofpact_put_DEC_TTL(ctx->ofpacts);
             } else {
-                action_error(ctx, "\"next\" action not allowed here.");
+                action_syntax_error(ctx, "expecting `--'");
             }
-        } else if (lexer_match_id(ctx->lexer, "output")) {
-            /* Table 64 does logical-to-physical translation. */
-            emit_resubmit(ctx, 64);
+        } else if (lexer_match_id(ctx->lexer, "ct_next")) {
+            emit_ct(ctx, true, false);
+        } else if (lexer_match_id(ctx->lexer, "ct_commit")) {
+            emit_ct(ctx, false, true);
         } else {
             action_syntax_error(ctx, "expecting action");
         }
@@ -176,8 +273,8 @@ parse_actions(struct action_context *ctx)
 }
 
 /* Parses OVN actions, in the format described for the "actions" column in the
- * Pipeline table in ovn-sb(5), and appends the parsed versions of the actions
- * to 'ofpacts' as "struct ofpact"s.
+ * Logical_Flow table in ovn-sb(5), and appends the parsed versions of the
+ * actions to 'ofpacts' as "struct ofpact"s.
  *
  * 'symtab' provides a table of "struct expr_symbol"s to support (as one would
  * provide to expr_parse()).
@@ -186,9 +283,29 @@ parse_actions(struct action_context *ctx)
  * (as one would provide to expr_to_matches()).  Strings used in the actions
  * that are not in 'ports' are translated to zero.
  *
+ * 'ct_zones' provides a map from a port name to its connection tracking zone.
+ *
+ * OVN maps each logical flow table (ltable), one-to-one, onto a physical
+ * OpenFlow flow table (ptable).  A number of parameters describe this mapping
+ * and data related to flow tables:
+ *
+ *     - 'first_ptable' and 'n_tables' define the range of OpenFlow tables to
+ *       which the logical "next" action should be able to jump.  Logical table
+ *       0 maps to OpenFlow table 'first_ptable', logical table 1 to
+ *       'first_ptable + 1', and so on.  If 'n_tables' is 0 then "next" is
+ *       disallowed entirely.
+ *
+ *     - 'cur_ltable' is an offset from 'first_ptable' (e.g. 0 <= cur_ltable <
+ *       n_ptables) of the logical flow that contains the actions.  If
+ *       cur_ltable + 1 < n_tables, then this defines the default table that
+ *       "next" will jump to.
+ *
  * 'next_table_id' should be the OpenFlow table to which the "next" action will
  * resubmit, or 0 to disable "next".
  *
+ *     - 'output_ptable' should be the OpenFlow table to which the logical
+ *       "output" action will resubmit
+ *
  * Some actions add extra requirements (prerequisites) to the flow's match.  If
  * so, this function sets '*prereqsp' to the actions' prerequisites; otherwise,
  * it sets '*prereqsp' to NULL.  The caller owns '*prereqsp' and must
@@ -201,8 +318,10 @@ parse_actions(struct action_context *ctx)
   */
 char * OVS_WARN_UNUSED_RESULT
 actions_parse(struct lexer *lexer, const struct shash *symtab,
-              const struct simap *ports, uint8_t next_table_id,
-              struct ofpbuf *ofpacts, struct expr **prereqsp)
+              const struct simap *ports, const struct simap *ct_zones,
+              uint8_t first_ptable, uint8_t n_tables, uint8_t cur_ltable,
+              uint8_t output_ptable, struct ofpbuf *ofpacts,
+              struct expr **prereqsp)
 {
     size_t ofpacts_start = ofpacts->size;
 
@@ -210,7 +329,11 @@ actions_parse(struct lexer *lexer, const struct shash *symtab,
     ctx.lexer = lexer;
     ctx.symtab = symtab;
     ctx.ports = ports;
-    ctx.next_table_id = next_table_id;
+    ctx.ct_zones = ct_zones;
+    ctx.first_ptable = first_ptable;
+    ctx.n_tables = n_tables;
+    ctx.cur_ltable = cur_ltable;
+    ctx.output_ptable = output_ptable;
     ctx.error = NULL;
     ctx.ofpacts = ofpacts;
     ctx.prereqs = NULL;
@@ -231,16 +354,19 @@ actions_parse(struct lexer *lexer, const struct shash *symtab,
 /* Like actions_parse(), but the actions are taken from 's'. */
 char * OVS_WARN_UNUSED_RESULT
 actions_parse_string(const char *s, const struct shash *symtab,
-                     const struct simap *ports, uint8_t next_table_id,
-                     struct ofpbuf *ofpacts, struct expr **prereqsp)
+                     const struct simap *ports, const struct simap *ct_zones,
+                     uint8_t first_table, uint8_t n_tables, uint8_t cur_table,
+                     uint8_t output_table, struct ofpbuf *ofpacts,
+                     struct expr **prereqsp)
 {
     struct lexer lexer;
     char *error;
 
     lexer_init(&lexer, s);
     lexer_get(&lexer);
-    error = actions_parse(&lexer, symtab, ports, next_table_id,
-                          ofpacts, prereqsp);
+    error = actions_parse(&lexer, symtab, ports, ct_zones, first_table,
+                          n_tables, cur_table, output_table, ofpacts,
+                          prereqsp);
     lexer_destroy(&lexer);
 
     return error;