@@ -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;
}
@@ -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);
@@ -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
@@ -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,
@@ -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)
@@ -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)
@@ -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);
@@ -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
@@ -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);
}