[ovs-dev] [PATCH 09/10] actions: Make "next" action able to jump from egress to ingress pipeline.

Ben Pfaff blp at ovn.org
Fri Jan 20 17:16:31 UTC 2017


This feature is useful for centralized gateways.

Signed-off-by: Ben Pfaff <blp at ovn.org>
---
 include/ovn/actions.h     | 63 +++++++++++++++++++++++------------------
 ovn/controller/lflow.c    |  7 +++--
 ovn/lib/actions.c         | 72 ++++++++++++++++++++++++++++++++++++-----------
 ovn/ovn-sb.xml            | 12 ++++++--
 ovn/utilities/ovn-trace.c |  3 ++
 tests/ovn.at              | 22 ++++++++++++++-
 tests/test-ovn.c          |  6 ++--
 7 files changed, 134 insertions(+), 51 deletions(-)

diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index f392d03..6691116 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -151,13 +151,15 @@ struct ovnact_next {
     struct ovnact ovnact;
 
     /* Arguments. */
-    uint8_t ltable;             /* Logical table ID of next table. */
+    uint8_t ltable;                /* Logical table ID of next table. */
+    enum ovnact_pipeline pipeline; /* Pipeline of next table. */
 
     /* Information about the flow that the action is in.  This does not affect
      * behavior, since the implementation of "next" doesn't depend on the
      * source table or pipeline.  It does affect how ovnacts_format() prints
      * the action. */
-    uint8_t src_ltable;            /* Logical table ID of source table. */
+    uint8_t src_ltable;                /* Logical table ID of source table. */
+    enum ovnact_pipeline src_pipeline; /* Pipeline of source table. */
 };
 
 /* OVNACT_LOAD. */
@@ -402,22 +404,26 @@ struct ovnact_parse_params {
     /* hmap of 'struct dhcp_opts_map'  to support 'put_dhcpv6_opts' action */
     const struct hmap *dhcpv6_opts;
 
-    /* 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:
+    /* Each OVN flow exists in a logical table within a logical pipeline.
+     * These parameters express this context for a set of OVN actions being
+     * parsed:
      *
-     *     - '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.
+     *     - 'n_tables' is the number of tables in the logical ingress and
+     *        egress pipelines, that is, "next" may specify a table less than
+     *        or equal to 'n_tables'.  If 'n_tables' is 0 then "next" is
+     *        disallowed entirely.
      *
-     *     - 'cur_ltable' is an offset from 'first_ptable' (e.g. 0 <=
-     *       cur_ltable < n_tables) 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. */
-    uint8_t n_tables;           /* Number of flow tables. */
-    uint8_t cur_ltable;         /* 0 <= cur_ltable < n_tables. */
+     *     - 'cur_ltable' is the logical table of the current flow, within
+     *       'pipeline'.  If cur_ltable + 1 < n_tables, then this defines the
+     *       default table that "next" will jump to.
+     *
+     *     - 'pipeline' is the logical pipeline.  It is the default pipeline to
+     *       which 'next' will jump.  If 'pipeline' is OVNACT_P_EGRESS, then
+     *       'next' will also be able to jump into the ingress pipeline, but
+     *       the reverse is not true. */
+    enum ovnact_pipeline pipeline; /* Logical pipeline. */
+    uint8_t n_tables;              /* Number of logical flow tables. */
+    uint8_t cur_ltable;            /* 0 <= cur_ltable < n_tables. */
 };
 
 bool ovnacts_parse(struct lexer *, const struct ovnact_parse_params *,
@@ -448,20 +454,23 @@ struct ovnact_encode_params {
      * 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.
+     *     - 'pipeline' is the logical pipeline in which the actions are
+     *       executing.
      *
-     *     - '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.
+     *     - 'ingress_ptable' is the OpenFlow table that corresponds to OVN
+     *       ingress table 0.
+     *
+     *     - 'egress_ptable' is the OpenFlow table that corresponds to OVN
+     *       egress table 0.
      *
      *     - 'output_ptable' should be the OpenFlow table to which the logical
-     *       "output" action will resubmit. */
-    uint8_t first_ptable;       /* First OpenFlow table. */
+     *       "output" action will resubmit.
+     *
+     *     - 'mac_bind_ptable' should be the OpenFlow table used to track MAC
+     *       bindings. */
+    enum ovnact_pipeline pipeline; /* Logical pipeline. */
+    uint8_t ingress_ptable;     /* First OpenFlow ingress table. */
+    uint8_t egress_ptable;      /* First OpenFlow egress table. */
     uint8_t output_ptable;      /* OpenFlow table for 'output' to resubmit. */
     uint8_t mac_bind_ptable;    /* OpenFlow table for 'get_arp'/'get_nd' to
                                    resubmit. */
diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
index 3d7633e..2d9213b 100644
--- a/ovn/controller/lflow.c
+++ b/ovn/controller/lflow.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, 2016 Nicira, Inc.
+/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -191,6 +191,7 @@ consider_logical_flow(const struct lport_index *lports,
         .dhcp_opts = dhcp_opts,
         .dhcpv6_opts = dhcpv6_opts,
 
+        .pipeline = ingress ? OVNACT_P_INGRESS : OVNACT_P_EGRESS,
         .n_tables = LOG_PIPELINE_LEN,
         .cur_ltable = lflow->table_id,
     };
@@ -223,7 +224,9 @@ consider_logical_flow(const struct lport_index *lports,
         .ct_zones = ct_zones,
         .group_table = group_table,
 
-        .first_ptable = first_ptable,
+        .pipeline = ingress ? OVNACT_P_INGRESS : OVNACT_P_EGRESS,
+        .ingress_ptable = OFTABLE_LOG_INGRESS_PIPELINE,
+        .egress_ptable = OFTABLE_LOG_EGRESS_PIPELINE,
         .output_ptable = output_ptable,
         .mac_bind_ptable = OFTABLE_MAC_BINDING,
     };
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
index 5548234..5aef9f9 100644
--- a/ovn/lib/actions.c
+++ b/ovn/lib/actions.c
@@ -170,6 +170,15 @@ put_load(uint64_t value, enum mf_field_id dst, int ofs, int n_bits,
     bitwise_copy(&n_value, 8, 0, sf->value, sf->field->n_bytes, ofs, n_bits);
     bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, ofs, n_bits);
 }
+
+static uint8_t
+first_ptable(const struct ovnact_encode_params *ep,
+             enum ovnact_pipeline pipeline)
+{
+    return (pipeline == OVNACT_P_INGRESS
+            ? ep->ingress_ptable
+            : ep->egress_ptable);
+}
 
 /* Context maintained during ovnacts_parse(). */
 struct action_context {
@@ -262,38 +271,69 @@ parse_NEXT(struct action_context *ctx)
         return;
     }
 
+    int pipeline = ctx->pp->pipeline;
     int table = ctx->pp->cur_ltable + 1;
     if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
-        int ltable;
-
-        if (!lexer_force_int(ctx->lexer, &ltable) ||
-            !lexer_force_match(ctx->lexer, LEX_T_RPAREN)) {
-            return;
+        if (lexer_is_int(ctx->lexer)) {
+            lexer_get_int(ctx->lexer, &table);
+        } else {
+            do {
+                if (lexer_match_id(ctx->lexer, "pipeline")) {
+                    if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
+                        return;
+                    }
+                    if (lexer_match_id(ctx->lexer, "ingress")) {
+                        pipeline = OVNACT_P_INGRESS;
+                    } else if (lexer_match_id(ctx->lexer, "egress")) {
+                        pipeline = OVNACT_P_EGRESS;
+                    } else {
+                        lexer_syntax_error(
+                            ctx->lexer, "expecting \"ingress\" or \"egress\"");
+                        return;
+                    }
+                } else if (lexer_match_id(ctx->lexer, "table")) {
+                    if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS) ||
+                        !lexer_force_int(ctx->lexer, &table)) {
+                        return;
+                    }
+                } else {
+                    lexer_syntax_error(ctx->lexer,
+                                       "expecting \"pipeline\" or \"table\"");
+                    return;
+                }
+            } while (lexer_match(ctx->lexer, LEX_T_COMMA));
         }
-
-        if (ltable >= ctx->pp->n_tables) {
-            lexer_error(ctx->lexer,
-                        "\"next\" argument must be in range 0 to %d.",
-                         ctx->pp->n_tables - 1);
+        if (!lexer_force_match(ctx->lexer, LEX_T_RPAREN)) {
             return;
         }
     }
 
-    if (table >= ctx->pp->n_tables) {
+    if (pipeline == OVNACT_P_EGRESS && ctx->pp->pipeline == OVNACT_P_INGRESS) {
+        lexer_error(ctx->lexer,
+                    "\"next\" action cannot advance from ingress to egress "
+                    "pipeline (use \"output\" action instead)");
+    } else if (table >= ctx->pp->n_tables) {
         lexer_error(ctx->lexer,
                     "\"next\" action cannot advance beyond table %d.",
                     ctx->pp->n_tables - 1);
     }
 
     struct ovnact_next *next = ovnact_put_NEXT(ctx->ovnacts);
+    next->pipeline = pipeline;
     next->ltable = table;
+    next->src_pipeline = ctx->pp->pipeline;
     next->src_ltable = ctx->pp->cur_ltable;
 }
 
 static void
 format_NEXT(const struct ovnact_next *next, struct ds *s)
 {
-    if (next->ltable != next->src_ltable + 1) {
+    if (next->pipeline != next->src_pipeline) {
+        ds_put_format(s, "next(pipeline=%s, table=%d);",
+                      (next->pipeline == OVNACT_P_INGRESS
+                       ? "ingress" : "egress"),
+                      next->ltable);
+    } else if (next->ltable != next->src_ltable + 1) {
         ds_put_format(s, "next(%d);", next->ltable);
     } else {
         ds_put_cstr(s, "next;");
@@ -305,7 +345,7 @@ encode_NEXT(const struct ovnact_next *next,
             const struct ovnact_encode_params *ep,
             struct ofpbuf *ofpacts)
 {
-    emit_resubmit(ofpacts, ep->first_ptable + next->ltable);
+    emit_resubmit(ofpacts, first_ptable(ep, next->pipeline) + next->ltable);
 }
 
 static void
@@ -550,7 +590,7 @@ encode_CT_NEXT(const struct ovnact_ct_next *ct_next,
                 struct ofpbuf *ofpacts)
 {
     struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts);
-    ct->recirc_table = ep->first_ptable + ct_next->ltable;
+    ct->recirc_table = first_ptable(ep, ep->pipeline) + ct_next->ltable;
     ct->zone_src.field = ep->is_switch ? mf_from_id(MFF_LOG_CT_ZONE)
                             : mf_from_id(MFF_LOG_DNAT_ZONE);
     ct->zone_src.ofs = 0;
@@ -754,7 +794,7 @@ encode_ct_nat(const struct ovnact_ct_nat *cn,
     ofpbuf_pull(ofpacts, ct_offset);
 
     struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts);
-    ct->recirc_table = cn->ltable + ep->first_ptable;
+    ct->recirc_table = cn->ltable + first_ptable(ep, ep->pipeline);
     if (snat) {
         ct->zone_src.field = mf_from_id(MFF_LOG_SNAT_ZONE);
     } else {
@@ -898,7 +938,7 @@ encode_CT_LB(const struct ovnact_ct_lb *cl,
              const struct ovnact_encode_params *ep,
              struct ofpbuf *ofpacts)
 {
-    uint8_t recirc_table = cl->ltable + ep->first_ptable;
+    uint8_t recirc_table = cl->ltable + first_ptable(ep, ep->pipeline);
     if (!cl->n_dsts) {
         /* ct_lb without any destinations means that this is an established
          * connection and we just need to do a NAT. */
diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
index bca82e0..d457f9e 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -956,10 +956,16 @@
 
         <dt><code>next;</code></dt>
         <dt><code>next(<var>table</var>);</code></dt>
+        <dt><code>next(pipeline=<var>pipeline</var>, table=<var>table</var>);</code></dt>
         <dd>
-          Executes another logical datapath table as a subroutine.  By default,
-          the table after the current one is executed.  Specify
-          <var>table</var> to jump to a specific table in the same pipeline.
+          Executes the given logical datapath <var>table</var> in
+          <var>pipeline</var> as a subroutine.  The default <var>table</var> is
+          just after the current one.  If <var>pipeline</var> is specified, it
+          may be <code>ingress</code> or <code>egress</code>; the default
+          <var>pipeline</var> is the one currently executing.  Actions in the
+          ingress pipeline may not use <code>next</code> to jump into the
+          egress pipeline (use the <code>output</code> instead), but
+          transitions in the opposite direction are allowed.
         </dd>
 
         <dt><code><var>field</var> = <var>constant</var>;</code></dt>
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
index 654b8d0..35d0645 100644
--- a/ovn/utilities/ovn-trace.c
+++ b/ovn/utilities/ovn-trace.c
@@ -673,6 +673,9 @@ read_flows(void)
             .symtab = &symtab,
             .dhcp_opts = &dhcp_opts,
             .dhcpv6_opts = &dhcpv6_opts,
+            .pipeline = (!strcmp(sblf->pipeline, "ingress")
+                         ? OVNACT_P_INGRESS
+                         : OVNACT_P_EGRESS),
             .n_tables = 16,
             .cur_ltable = sblf->table_id,
         };
diff --git a/tests/ovn.at b/tests/ovn.at
index f71a4af..ecdb2be 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -653,12 +653,32 @@ next(15);
     encodes as resubmit(,31)
 
 next();
-    Syntax error at `)' expecting small integer.
+    Syntax error at `)' expecting "pipeline" or "table".
 next(10;
     Syntax error at `;' expecting `)'.
 next(16);
     "next" action cannot advance beyond table 15.
 
+next(table=11);
+    formats as next;
+    encodes as resubmit(,27)
+next(pipeline=ingress);
+    formats as next;
+    encodes as resubmit(,27)
+next(table=11, pipeline=ingress);
+    formats as next;
+    encodes as resubmit(,27)
+next(pipeline=ingress, table=11);
+    formats as next;
+    encodes as resubmit(,27)
+
+next(pipeline=egress);
+    "next" action cannot advance from ingress to egress pipeline (use "output" action instead)
+
+next(table=10);
+    formats as next(10);
+    encodes as resubmit(,26)
+
 # Loading a constant value.
 tcp.dst=80;
     formats as tcp.dst = 80;
diff --git a/tests/test-ovn.c b/tests/test-ovn.c
index e64f12f..ea7c45a 100644
--- a/tests/test-ovn.c
+++ b/tests/test-ovn.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016 Nicira, Inc.
+ * Copyright (c) 2015, 2016, 2017 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -1246,7 +1246,9 @@ test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED)
                 .ct_zones = &ct_zones,
                 .group_table = &group_table,
 
-                .first_ptable = 16,
+                .pipeline = OVNACT_P_INGRESS,
+                .ingress_ptable = 16,
+                .egress_ptable = 48,
                 .output_ptable = 64,
                 .mac_bind_ptable = 65,
             };
-- 
2.10.2



More information about the dev mailing list