From patchwork Wed Dec 23 08:07:47 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: niti1489@gmail.com X-Patchwork-Id: 560387 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from archives.nicira.com (li376-54.members.linode.com [96.126.127.54]) by ozlabs.org (Postfix) with ESMTP id 52106140213 for ; Wed, 23 Dec 2015 19:08:17 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=L2HyJS6b; dkim-atps=neutral Received: from archives.nicira.com (localhost [127.0.0.1]) by archives.nicira.com (Postfix) with ESMTP id 8804610B9D; Wed, 23 Dec 2015 00:08:16 -0800 (PST) X-Original-To: dev@openvswitch.org Delivered-To: dev@openvswitch.org Received: from mx3v3.cudamail.com (mx3.cudamail.com [64.34.241.5]) by archives.nicira.com (Postfix) with ESMTPS id CB8CF10B35 for ; Wed, 23 Dec 2015 00:08:15 -0800 (PST) Received: from bar4.cudamail.com (localhost [127.0.0.1]) by mx3v3.cudamail.com (Postfix) with ESMTPS id 62F801613C2 for ; Wed, 23 Dec 2015 01:08:15 -0700 (MST) X-ASG-Debug-ID: 1450858094-03dc213fee58db60001-byXFYA Received: from mx3-pf1.cudamail.com ([192.168.14.2]) by bar4.cudamail.com with ESMTP id G7E8uDlnKFd8eZ6y (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Wed, 23 Dec 2015 01:08:14 -0700 (MST) X-Barracuda-Envelope-From: niti1489@gmail.com X-Barracuda-RBL-Trusted-Forwarder: 192.168.14.2 Received: from unknown (HELO mail-pf0-f177.google.com) (209.85.192.177) by mx3-pf1.cudamail.com with ESMTPS (RC4-SHA encrypted); 23 Dec 2015 08:08:07 -0000 Received-SPF: pass (mx3-pf1.cudamail.com: SPF record at _netblocks.google.com designates 209.85.192.177 as permitted sender) X-Barracuda-RBL-Trusted-Forwarder: 209.85.192.177 Received: by mail-pf0-f177.google.com with SMTP id u7so72980843pfb.1 for ; Wed, 23 Dec 2015 00:08:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=z6M5IdkAcOo7N7Tjn6uq0W0+kY5P4TM64m0IEDdRQMs=; b=L2HyJS6btNEQxO2A5d/T3AanC3XVuqdt+dH8cSLTpk1AV2SlavrQxNdcy63VxFiLEF 75LknCNJlP0aQ/fPkX4rMV86f+iET28relaFuAGsgwYz4Qap6Ql1KLwZ2i41+jyU+37L veds8iJ10LXMs6ZRwuXbCByCAE+gzWeN57JCUzHXL1otSDiPNWxgxBd4E2q2Rv1v3DtZ Hmb4RXHIO/QnGytuvYtVIdjT1HvPTth9xuu9/dV83iQxXgktqeu6LgAqxqT3rpXU4kjD KBV/vNmKUqUaAwsNqe8ydK8yQu6eJL7krsKWytI2ra1pittIdQHy6ym0ld6dwL7qbkO8 brFQ== X-Received: by 10.98.17.3 with SMTP id z3mr23457986pfi.166.1450858087421; Wed, 23 Dec 2015 00:08:07 -0800 (PST) Received: from localhost.localdomain ([14.98.87.220]) by smtp.gmail.com with ESMTPSA id g88sm11264826pfj.91.2015.12.23.00.08.03 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 23 Dec 2015 00:08:06 -0800 (PST) X-CudaMail-Envelope-Sender: niti1489@gmail.com X-Barracuda-Apparent-Source-IP: 14.98.87.220 From: niti1489@gmail.com X-Google-Original-From: niti.rohilla@tcs.com To: dev@openvswitch.org X-CudaMail-MID: CM-V1-1222000875 X-CudaMail-DTE: 122315 X-CudaMail-Originating-IP: 209.85.192.177 Date: Wed, 23 Dec 2015 13:37:47 +0530 X-ASG-Orig-Subj: [##CM-V1-1222000875##][PATCH 2/2] ofproto: This patch adds support for egress pipeline processing Message-Id: <1450858067-16842-1-git-send-email-niti.rohilla@tcs.com> X-Mailer: git-send-email 2.5.0 X-GBUdb-Analysis: 0, 209.85.192.177, Ugly c=0.385168 p=-0.380952 Source Normal X-MessageSniffer-Rules: 0-0-0-32767-c X-Barracuda-Connect: UNKNOWN[192.168.14.2] X-Barracuda-Start-Time: 1450858094 X-Barracuda-Encrypted: DHE-RSA-AES256-SHA X-Barracuda-URL: https://web.cudamail.com:443/cgi-mod/mark.cgi X-Virus-Scanned: by bsmtpd at cudamail.com X-Barracuda-BRTS-Status: 1 X-Barracuda-Spam-Score: 0.60 X-Barracuda-Spam-Status: No, SCORE=0.60 using per-user scores of TAG_LEVEL=3.5 QUARANTINE_LEVEL=1000.0 KILL_LEVEL=4.0 tests=BSF_SC5_MJ1963, DKIM_SIGNED, NO_REAL_NAME, RDNS_NONE X-Barracuda-Spam-Report: Code version 3.2, rules version 3.2.3.25503 Rule breakdown below pts rule name description ---- ---------------------- -------------------------------------------------- 0.00 NO_REAL_NAME From: does not include a real name 0.00 DKIM_SIGNED Domain Keys Identified Mail: message has a signature 0.10 RDNS_NONE Delivered to trusted network by a host with no rDNS 0.50 BSF_SC5_MJ1963 Custom Rule MJ1963 Cc: deepankar.gupta@tcs.com, partha.datta@tcs.com Subject: [ovs-dev] [PATCH 2/2] ofproto: This patch adds support for egress pipeline processing X-BeenThere: dev@openvswitch.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dev-bounces@openvswitch.org Sender: "dev" From: Niti Rohilla 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 the ingress flow tables to direct the packets to egress flow tables via GOTO_TABLE instruction. Signed-off-by: Niti Rohilla --- lib/ofp-actions.c | 24 +++++++- lib/ofp-util.h | 1 + ofproto/ofproto-dpif-xlate.c | 136 +++++++++++++++++++++++++++++++------------ ofproto/ofproto-dpif.c | 5 ++ ofproto/ofproto-dpif.h | 2 + ofproto/ofproto-provider.h | 1 + ofproto/ofproto.c | 4 ++ tests/ofproto-dpif.at | 19 ++++++ utilities/ovs-ofctl.c | 104 ++++++++++++++++++++++++++++++--- 9 files changed, 250 insertions(+), 46 deletions(-) diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 3137861..51a6977 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -5945,7 +5945,6 @@ ofpacts_decode_for_action_set(const struct ofp_action_header *in, return OFPERR_OFPBAC_BAD_TYPE; } } - return 0; } @@ -6636,6 +6635,20 @@ 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 (egress_table_id && table_id >= egress_table_id) { + 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); } @@ -6657,6 +6670,15 @@ 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 (egress_table_id && table_id < egress_table_id) { + if (goto_table >= egress_table_id) { + return OFPERR_OFPBIC_BAD_TABLE_ID; + } + } + } return 0; } diff --git a/lib/ofp-util.h b/lib/ofp-util.h index b16b1fb..ecc7880 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -347,6 +347,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 dab64b9..65229c8 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) @@ -2890,9 +2892,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; @@ -2909,38 +2912,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; @@ -3178,6 +3149,78 @@ 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; + + 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) { @@ -4098,8 +4141,21 @@ xlate_write_actions(struct xlate_ctx *ctx, const struct ofpact *a) } } } - - ofpbuf_put(&ctx->action_set, on->actions, on_len); + /* Check to forbid adding output or group action in the egress action-set + * to prevent changing output port, if egress table is supported and + * configured. */ + if (ofproto_first_egress_table_id(ctx->xbridge->ofproto) != 0 && + (ofproto_first_egress_table_id(ctx->xbridge->ofproto) <= ctx->table_id)) { + OFPACT_FOR_EACH (inner, on->actions, on_len) { + if (inner->type == OFPACT_OUTPUT || inner->type == OFPACT_GROUP) { + continue; + } else { + ofpbuf_put(&ctx->action_set, inner, inner->len); + } + } + } else { + ofpbuf_put(&ctx->action_set, on->actions, on_len); + } ofpact_pad(&ctx->action_set); } @@ -4737,13 +4793,18 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, case OFPACT_GOTO_TABLE: { struct ofpact_goto_table *ogt = ofpact_get_GOTO_TABLE(a); - + int first_egress_table = ofproto_first_egress_table_id(ctx->xbridge->ofproto); /* Allow ctx->table_id == TBL_INTERNAL, which will be greater * than ogt->table_id. This is to allow goto_table actions that * triggered recirculation: ctx->table_id will be TBL_INTERNAL * after recirculation. */ ovs_assert(ctx->table_id == TBL_INTERNAL || ctx->table_id < ogt->table_id); + /* This check forbids the ingress flow tables to direct the packets to + * egress flow tables via GOTO_TABLE instruction. */ + if (first_egress_table && ctx->table_id < first_egress_table) { + ovs_assert(ogt->table_id < first_egress_table); + } xlate_table_action(ctx, ctx->xin->flow.in_port.ofp_port, ogt->table_id, true, true); break; @@ -5087,6 +5148,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 6561c65..ead715c 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -5724,6 +5724,11 @@ ofproto_dpif_delete_internal_flow(struct ofproto_dpif *ofproto, return 0; } +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 dab422a..f855cc7 100644 --- a/ofproto/ofproto-dpif.h +++ b/ofproto/ofproto-dpif.h @@ -178,6 +178,8 @@ int ofproto_dpif_add_internal_flow(struct ofproto_dpif *, struct rule **rulep); int ofproto_dpif_delete_internal_flow(struct ofproto_dpif *, struct match *, int priority); + +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 af5268b..a0dad9f 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -1805,6 +1805,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 99ffe2e..5760c92 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -5318,10 +5318,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); @@ -7071,11 +7073,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 4c2a995..98e34db 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -7141,3 +7141,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 e2ab612..191bde2 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -1285,6 +1285,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; + set_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) @@ -1380,6 +1435,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 @@ -1387,10 +1443,26 @@ 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); @@ -1406,10 +1478,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); }