[ovs-dev,2/2,v3] ofproto: This patch adds support for egress pipeline processing
diff mbox

Message ID 1455173723-20062-1-git-send-email-niti.rohilla@tcs.com
State Changes Requested
Headers show

Commit Message

niti1489@gmail.com Feb. 11, 2016, 6:55 a.m. UTC
From: Niti Rohilla <niti.rohilla@tcs.com>

Following have been implemented:
- When a packet is sent to output port, it will start egress
  processing in the context of the output port.
- Added check to forbid adding output or group action in the
  egress action-set to prevent changing output port.
- Added check to forbid adding output or group action via apply-
  action instruction in the egress tables as egress mirroring is
  currently not supported.
- Added check to forbid the ingress flow tables to direct the
  packets to egress flow tables via GOTO_TABLE instruction.

Signed-off-by: Niti Rohilla <niti.rohilla@tcs.com>
---
Difference between v2->v3
- Changed ADD_OF_PORTS function and its syntax with add_of_ports
  function in tests/ofproto-dpif.at.
- Rebsaed with latest master

 lib/ofp-actions.c            |  45 +++++++++++++++++
 lib/ofp-util.h               |   1 +
 ofproto/ofproto-dpif-xlate.c | 113 ++++++++++++++++++++++++++++++-------------
 ofproto/ofproto-dpif.c       |   5 ++
 ofproto/ofproto-dpif.h       |   3 ++
 ofproto/ofproto-provider.h   |   1 +
 ofproto/ofproto.c            |   4 ++
 tests/ofproto-dpif.at        |  19 ++++++++
 utilities/ovs-ofctl.c        | 103 ++++++++++++++++++++++++++++++++++++---
 9 files changed, 252 insertions(+), 42 deletions(-)

Patch
diff mbox

diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 74a2ec1..5692dba 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -6401,6 +6401,16 @@  ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
 
     switch (a->type) {
     case OFPACT_OUTPUT:
+       /* This check forbid adding group action via apply-action instruction
+        * in the egress tables because currently egress mirroring is not
+        * supported. */
+        if (*usable_protocols & OFPUTIL_P_OF15_UP) {
+            if (ofputil_egress_table_id(0) &&
+                table_id >= ofputil_egress_table_id(0)) {
+                VLOG_WARN_RL(&rl, "disallowed action in action set");
+                return OFPERR_OFPBAC_BAD_ARGUMENT;
+            }
+        }
         return ofpact_check_output_port(ofpact_get_OUTPUT(a)->port,
                                         max_ports);
 
@@ -6622,6 +6632,21 @@  ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
          * consistency of an action set. */
         struct ofpact_nest *on = ofpact_get_WRITE_ACTIONS(a);
         enum ofputil_protocol p = *usable_protocols;
+        struct ofpact *a;
+        /* This check forbid adding output or group action in the egress
+         * action-set to prevent changing output port, if egress table is
+         * supported and configured. */
+        if (*usable_protocols & OFPUTIL_P_OF15_UP) {
+            OFPACT_FOR_EACH (a, on->actions, ofpact_nest_get_action_len(on)) {
+                if (ofputil_egress_table_id(0) &&
+                    table_id >= ofputil_egress_table_id(0)) {
+                    if (a->type == OFPACT_OUTPUT || a->type == OFPACT_GROUP) {
+                        VLOG_WARN_RL(&rl, "disallowed action in action set");
+                        return OFPERR_OFPBAC_BAD_ARGUMENT;
+                    }
+                }
+            }
+        }
         return ofpacts_check(on->actions, ofpact_nest_get_action_len(on),
                              flow, max_ports, table_id, n_tables, &p);
     }
@@ -6643,10 +6668,30 @@  ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
             || (n_tables != 255 && goto_table >= n_tables)) {
             return OFPERR_OFPBIC_BAD_TABLE_ID;
         }
+        /* This check forbids the ingress flow tables to direct the packets to
+         * egress flow tables via GOTO_TABLE instruction. */
+        if (*usable_protocols & OFPUTIL_P_OF15_UP) {
+            if (ofputil_egress_table_id(0) &&
+                table_id < ofputil_egress_table_id(0)) {
+                if (goto_table >= ofputil_egress_table_id(0)) {
+                    return OFPERR_OFPBIC_BAD_TABLE_ID;
+                }
+            }
+        }
         return 0;
     }
 
     case OFPACT_GROUP:
+        /* This check forbid adding group action via apply-action instruction
+         * in the egress tables because currently egress mirroring is not
+         * supported. */
+        if (*usable_protocols & OFPUTIL_P_OF15_UP) {
+            if (ofputil_egress_table_id(0) &&
+                table_id >= ofputil_egress_table_id(0)) {
+                VLOG_WARN_RL(&rl, "disallowed action in action set");
+                return OFPERR_OFPBAC_BAD_ARGUMENT;
+            }
+        }
         return 0;
 
     case OFPACT_UNROLL_XLATE:
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index 6e5e117..8ec061f 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -332,6 +332,7 @@  enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *,
                                     struct ofpbuf *ofpacts,
                                     ofp_port_t max_port,
                                     uint8_t max_table);
+
 struct ofpbuf *ofputil_encode_flow_mod(const struct ofputil_flow_mod *,
                                        enum ofputil_protocol);
 
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index a6ea067..281a4df 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -319,6 +319,8 @@  struct xlate_ctx {
     struct ofpbuf action_set;   /* Action set. */
 
     enum xlate_error error;     /* Translation failed. */
+    bool egress_processing;     /* Flag to check whether to start egress
+                                 * processing in  this context or not. */
 };
 
 const char *xlate_strerror(enum xlate_error error)
@@ -2905,9 +2907,10 @@  clear_conntrack(struct flow *flow)
 }
 
 static void
-compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
-                        const struct xlate_bond_recirc *xr, bool check_stp)
+compose_output__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
+                 const struct xlate_bond_recirc *xr)
 {
+
     const struct xport *xport = get_ofp_port(ctx->xbridge, ofp_port);
     struct flow_wildcards *wc = ctx->wc;
     struct flow *flow = &ctx->xin->flow;
@@ -2924,38 +2927,6 @@  compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
     BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35);
     memset(&flow_tnl, 0, sizeof flow_tnl);
 
-    if (!xport) {
-        xlate_report(ctx, "Nonexistent output port");
-        return;
-    } else if (xport->config & OFPUTIL_PC_NO_FWD) {
-        xlate_report(ctx, "OFPPC_NO_FWD set, skipping output");
-        return;
-    } else if (check_stp) {
-        if (is_stp(&ctx->base_flow)) {
-            if (!xport_stp_should_forward_bpdu(xport) &&
-                !xport_rstp_should_manage_bpdu(xport)) {
-                if (ctx->xbridge->stp != NULL) {
-                    xlate_report(ctx, "STP not in listening state, "
-                            "skipping bpdu output");
-                } else if (ctx->xbridge->rstp != NULL) {
-                    xlate_report(ctx, "RSTP not managing BPDU in this state, "
-                            "skipping bpdu output");
-                }
-                return;
-            }
-        } else if (!xport_stp_forward_state(xport) ||
-                   !xport_rstp_forward_state(xport)) {
-            if (ctx->xbridge->stp != NULL) {
-                xlate_report(ctx, "STP not in forwarding state, "
-                        "skipping output");
-            } else if (ctx->xbridge->rstp != NULL) {
-                xlate_report(ctx, "RSTP not in forwarding state, "
-                        "skipping output");
-            }
-            return;
-        }
-    }
-
     if (xport->peer) {
         const struct xport *peer = xport->peer;
         struct flow old_flow = ctx->xin->flow;
@@ -3193,6 +3164,79 @@  compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
 }
 
 static void
+compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
+                        const struct xlate_bond_recirc *xr, bool check_stp)
+{
+    const struct xport *xport = get_ofp_port(ctx->xbridge, ofp_port);
+    struct flow *flow = &ctx->xin->flow;
+
+    if (!xport) {
+        xlate_report(ctx, "Nonexistent output port");
+        return;
+    } else if (xport->config & OFPUTIL_PC_NO_FWD) {
+        xlate_report(ctx, "OFPPC_NO_FWD set, skipping output");
+        return;
+    } else if (check_stp) {
+        if (is_stp(&ctx->base_flow)) {
+            if (!xport_stp_should_forward_bpdu(xport) &&
+                !xport_rstp_should_manage_bpdu(xport)) {
+                if (ctx->xbridge->stp != NULL) {
+                    xlate_report(ctx, "STP not in listening state, "
+                            "skipping bpdu output");
+                } else if (ctx->xbridge->rstp != NULL) {
+                    xlate_report(ctx, "RSTP not managing BPDU in this state, "
+                            "skipping bpdu output");
+                }
+                return;
+            }
+        } else if (!xport_stp_forward_state(xport) ||
+                   !xport_rstp_forward_state(xport)) {
+            if (ctx->xbridge->stp != NULL) {
+                xlate_report(ctx, "STP not in forwarding state, "
+                        "skipping output");
+            } else if (ctx->xbridge->rstp != NULL) {
+                xlate_report(ctx, "RSTP not in forwarding state, "
+                        "skipping output");
+            }
+            return;
+        }
+    }
+
+    if (ofproto_first_egress_table_id(ctx->xbridge->ofproto) != 0) {
+        if (ctx->egress_processing == false) {
+            struct ofpact_output *output;
+
+            ctx->egress_processing = true;
+            output = ofpact_put_OUTPUT(&ctx->action_set);
+            output->port = ofp_port;
+            ctx->xin->flow.actset_output = ofp_port;
+
+            xlate_table_action(ctx, flow->in_port.ofp_port,
+                               ofproto_first_egress_table_id(
+                               ctx->xbridge->ofproto), true, true);
+            if (ctx->action_set.size) {
+                /* Translate action set only if not dropping the packet and
+                 * not recirculating. */
+                if (!exit_recirculates(ctx)) {
+                    xlate_action_set(ctx);
+                    ctx->egress_processing = false;
+                }
+            }
+            /* Check if need to recirculate. */
+            if (exit_recirculates(ctx)) {
+                compose_recirculate_action(ctx);
+            }
+            compose_output__(ctx, ofp_port, xr);
+        }
+    }
+    else {
+         compose_output__(ctx, ofp_port, xr);
+    }
+
+}
+
+
+static void
 compose_output_action(struct xlate_ctx *ctx, ofp_port_t ofp_port,
                       const struct xlate_bond_recirc *xr)
 {
@@ -5136,6 +5180,7 @@  xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
 
         .action_set_has_group = false,
         .action_set = OFPBUF_STUB_INITIALIZER(action_set_stub),
+        .egress_processing = false,
     };
 
     /* 'base_flow' reflects the packet as it came in, but we need it to reflect
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index adfaeb6..07b4e01 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -5724,6 +5724,11 @@  ofproto_dpif_get_uuid(const struct ofproto_dpif *ofproto)
     return &ofproto->uuid;
 }
 
+uint8_t ofproto_first_egress_table_id(struct ofproto_dpif *ofproto)
+{
+    return ofproto->up.first_egress_table_id;
+}
+
 const struct ofproto_class ofproto_dpif_class = {
     init,
     enumerate_types,
diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
index 0064178..15a64ef 100644
--- a/ofproto/ofproto-dpif.h
+++ b/ofproto/ofproto-dpif.h
@@ -180,6 +180,9 @@  int ofproto_dpif_delete_internal_flow(struct ofproto_dpif *, struct match *,
                                       int priority);
 
 const struct uuid *ofproto_dpif_get_uuid(const struct ofproto_dpif *);
+
+uint8_t ofproto_first_egress_table_id(struct ofproto_dpif *);
+
 
 /* struct rule_dpif has struct rule as it's first member. */
 #define RULE_CAST(RULE) ((struct rule *)RULE)
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 1da43b3..4f8f61c 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -1825,6 +1825,7 @@  void ofproto_flush_flows(struct ofproto *);
 enum ofperr ofproto_check_ofpacts(struct ofproto *,
                                   const struct ofpact ofpacts[],
                                   size_t ofpacts_len);
+
 
 static inline const struct rule_actions *
 rule_get_actions(const struct rule *rule)
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index dc31b7c..9a4a770 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -5305,10 +5305,12 @@  handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
     }
 
     ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+    ovs_mutex_lock(&ofproto_mutex);
     error = ofputil_decode_flow_mod(&ofm.fm, oh, ofconn_get_protocol(ofconn),
                                     &ofpacts,
                                     u16_to_ofp(ofproto->max_ports),
                                     ofproto->n_tables);
+    ovs_mutex_unlock(&ofproto_mutex);
     if (!error) {
         error = ofproto_check_ofpacts(ofproto, ofm.fm.ofpacts,
                                       ofm.fm.ofpacts_len);
@@ -7099,11 +7101,13 @@  handle_bundle_add(struct ofconn *ofconn, const struct ofp_header *oh)
         uint64_t ofpacts_stub[1024 / 8];
 
         ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+        ovs_mutex_lock(&ofproto_mutex);
         error = ofputil_decode_flow_mod(&bmsg->ofm.fm, badd.msg,
                                         ofconn_get_protocol(ofconn),
                                         &ofpacts,
                                         u16_to_ofp(ofproto->max_ports),
                                         ofproto->n_tables);
+        ovs_mutex_unlock(&ofproto_mutex);
         /* Move actions to heap. */
         bmsg->ofm.fm.ofpacts = ofpbuf_steal_data(&ofpacts);
 
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index a372d36..795bdeb 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -7194,3 +7194,22 @@  dpif_netdev|DBG|flow_add: recirc_id=0,in_port=1,vlan_tci=0xf063/0x1000,dl_type=0
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - egress table])
+OVS_VSWITCHD_START
+add_of_ports br0 1 2
+AT_CHECK([ovs-ofctl -O Openflow15 set-first-egress-table br0 200])
+AT_DATA([flows.txt], [dnl
+table=0 in_port=2,ip actions=output(1),write_actions(set_field:10.1.1.11->ip_src,output(1))
+table=0, in_port=1,actions=mod_dl_dst:00:00:00:00:08:aa,output:2
+table=200, in_port=2,actions=mod_dl_src:00:e0:4c:36:09:55
+table=200, in_port=1,actions=mod_dl_dst:00:00:00:00:08:ad
+])
+AT_CHECK([ovs-ofctl -O OpenFlow15 add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=2,dl_src=00:00:00:00:08:ad,dl_dst=00:e0:4c:36:09:5e,dl_type=0x0800,nw_src=10.1.1.20,nw_dst=10.1.1.30,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
+AT_CHECK([tail -2 stdout], [0],
+  [Megaflow: recirc_id=0,ip,in_port=2,dl_src=00:00:00:00:08:ad,nw_src=10.1.1.20,nw_frag=no
+Datapath actions: set(eth(src=00:e0:4c:36:09:55)),1,set(ipv4(src=10.1.1.11)),1
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 106fdde..4e8f296 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -1299,6 +1299,61 @@  ofctl_queue_get_config(struct ovs_cmdl_context *ctx)
     vconn_close(vconn);
 }
 
+static void
+fetch_table_features(struct vconn *vconn)
+{
+    struct ofpbuf *request;
+    ovs_be32 send_xid;
+    bool done = false;
+    bool found = false;
+    struct ofputil_table_features *tf = NULL;
+    struct ofputil_table_features tf_reply;
+
+    request = ofputil_encode_table_features_request(tf, vconn_get_version(vconn));
+
+    send_xid = ((struct ofp_header *) request->data)->xid;
+    send_openflow_buffer(vconn, request);
+    while (!done) {
+        ovs_be32 recv_xid;
+        struct ofpbuf *reply;
+
+        run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed");
+        recv_xid = ((struct ofp_header *) reply->data)->xid;
+        if (send_xid == recv_xid) {
+            struct ofp_header *oh = reply->data;
+            enum ofptype type;
+            struct ofpbuf b;
+            uint16_t flags;
+
+            ofpbuf_use_const(&b, oh, ntohs(oh->length));
+            if (ofptype_pull(&type, &b)
+                || type != OFPTYPE_TABLE_FEATURES_STATS_REPLY) {
+                ovs_fatal(0, "received bad reply: %s",
+                          ofp_to_string(reply->data, reply->size,
+                                        verbosity + 1));
+            }
+            flags = ofpmp_flags(oh);
+            done = !(flags & OFPSF_REPLY_MORE);
+            if (found) {
+                /* We've already found the table desc consisting of current
+                 * table configuration, but we need to drain the queue of
+                 * any other replies for this request. */
+                continue;
+            }
+            while (! ofputil_decode_table_features(&b, &tf_reply, true)) {
+                if ((tf_reply.features & OFPTFF_FIRST_EGRESS) != 0) {
+                    found = true;
+                    ofputil_egress_table_id(tf_reply.table_id);
+                    break;
+                }
+            }
+        } else {
+            VLOG_DBG("received reply with xid %08"PRIx32" "
+                     "!= expected %08"PRIx32, recv_xid, send_xid);
+        }
+        ofpbuf_delete(reply);
+    }
+}
 static enum ofputil_protocol
 open_vconn_for_flow_mod(const char *remote, struct vconn **vconnp,
                         enum ofputil_protocol usable_protocols)
@@ -1395,6 +1450,7 @@  ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], int command)
     struct ofputil_flow_mod *fms = NULL;
     size_t n_fms = 0;
     char *error;
+    uint32_t allowed_versions = get_allowed_ofp_versions();
 
     if (command == OFPFC_ADD) {
         /* Allow the file to specify a mix of commands.  If none specified at
@@ -1402,10 +1458,25 @@  ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], int command)
          * this is backwards compatible. */
         command = -2;
     }
-    error = parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms,
-                                    &usable_protocols);
-    if (error) {
-        ovs_fatal(0, "%s", error);
+
+    if (allowed_versions & (1u << OFP15_VERSION)) {
+
+        struct vconn *vconn;
+        open_vconn_for_flow_mod(argv[1], &vconn, OFPUTIL_P_OF15_UP);
+
+        fetch_table_features(vconn);
+        error = parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms,
+                                        &usable_protocols);
+        if (error) {
+            ovs_fatal(0, "%s", error);
+        }
+        vconn_close(vconn);
+    } else {
+        error = parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms,
+                                        &usable_protocols);
+        if (error) {
+            ovs_fatal(0, "%s", error);
+        }
     }
     ofctl_flow_mod__(argv[1], fms, n_fms, usable_protocols);
     free(fms);
@@ -1421,10 +1492,26 @@  ofctl_flow_mod(int argc, char *argv[], uint16_t command)
         char *error;
         enum ofputil_protocol usable_protocols;
 
-        error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command,
-                                       &usable_protocols);
-        if (error) {
-            ovs_fatal(0, "%s", error);
+        uint32_t allowed_versions = get_allowed_ofp_versions();
+
+        if (allowed_versions & (1u << OFP15_VERSION)) {
+
+            struct vconn *vconn;
+            open_vconn_for_flow_mod(argv[1], &vconn, OFPUTIL_P_OF15_UP);
+
+            fetch_table_features(vconn);
+            error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command,
+                                           &usable_protocols);
+            if (error) {
+                ovs_fatal(0, "%s", error);
+            }
+            vconn_close(vconn);
+        } else {
+            error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command,
+                                           &usable_protocols);
+            if (error) {
+                ovs_fatal(0, "%s", error);
+            }
         }
         ofctl_flow_mod__(argv[1], &fm, 1, usable_protocols);
     }