summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJarno Rajahalme <jarno@ovn.org>2016-09-14 16:51:27 -0700
committerJarno Rajahalme <jarno@ovn.org>2016-09-14 16:51:27 -0700
commit6dd3c787f5914ea27b239b6852d8fc0815033128 (patch)
tree69fc093392b33e9b3f4043359a225eebd12a39b4
parent1f4a893366826e392722d5b1ba59e94331bfe5c9 (diff)
downloadopenvswitch-6dd3c787f5914ea27b239b6852d8fc0815033128.tar.gz
ofproto: Support packet_outs in bundles.
Add support for OFPT_PACKET_OUT messages in bundles. While ovs-ofctl already has a packet-out command, we did not have a string parser for it, as the parsing was done directly from command line arguments. This patch adds the string parser for packet-out messages, adds support for it into the 'ovs-ofctl packet-out' command, and adds a new ofctl/packet-out ovs-appctl command that can be used when ovs-ofctl is used as a flow monitor. The old 'ovs-ofctl packet-out syntax is deprecated' and will be removed in a later OVS release. The new packet-out parser is further supported with the ovs-ofctl bundle command, which allows bundles to mix flow mods, group mods and packet-out messages. Also the packet-outs in bundles are only executed if the whole bundle is successful. A failing packet-out translation may also make the whole bundle to fail. Signed-off-by: Jarno Rajahalme <jarno@ovn.org> Acked-by: Ben Pfaff <blp@ovn.org>
-rw-r--r--NEWS9
-rw-r--r--include/openvswitch/ofp-parse.h5
-rw-r--r--include/openvswitch/ofp-util.h8
-rw-r--r--lib/ofp-parse.c133
-rw-r--r--lib/ofp-util.c37
-rw-r--r--ofproto/bundles.h7
-rw-r--r--ofproto/ofproto-dpif.c23
-rw-r--r--ofproto/ofproto-provider.h7
-rw-r--r--ofproto/ofproto.c82
-rw-r--r--tests/bfd.at20
-rw-r--r--tests/cfm.at6
-rw-r--r--tests/ofp-print.at19
-rw-r--r--tests/ofproto.at144
-rw-r--r--utilities/ovs-ofctl.8.in62
-rw-r--r--utilities/ovs-ofctl.c130
15 files changed, 573 insertions, 119 deletions
diff --git a/NEWS b/NEWS
index 7f501d63b..a1ca864b6 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,15 @@ Post-v2.6.0
- OpenFlow:
* Bundles now expire after 10 seconds since the last time the
bundle was either opened, modified, or closed.
+ * OFPT_PACKET_OUT messages are now supported in bundles.
+ - ovs-ofctl:
+ * 'bundle' command now supports packet-out messages.
+ * New syntax for 'ovs-ofctl packet-out' command, which uses the
+ same string parser as the 'bundle' command. The old 'packet-out'
+ syntax is deprecated and will be removed in a later OVS
+ release.
+ * New unixctl "ofctl/packet-out" command, which can be used to
+ instruct a flow monitor to issue OpenFlow packet-out messages.
v2.6.0 - xx xxx xxxx
---------------------
diff --git a/include/openvswitch/ofp-parse.h b/include/openvswitch/ofp-parse.h
index df60b188e..e68e57b00 100644
--- a/include/openvswitch/ofp-parse.h
+++ b/include/openvswitch/ofp-parse.h
@@ -28,6 +28,7 @@
struct flow;
struct ofpbuf;
struct ofputil_flow_mod;
+struct ofputil_packet_out;
struct ofputil_flow_monitor_request;
struct ofputil_flow_stats_request;
struct ofputil_group_mod;
@@ -47,6 +48,10 @@ char *parse_ofp_flow_mod_str(struct ofputil_flow_mod *, const char *string,
enum ofputil_protocol *usable_protocols)
OVS_WARN_UNUSED_RESULT;
+char *parse_ofp_packet_out_str(struct ofputil_packet_out *po, const char *str_,
+ enum ofputil_protocol *usable_protocols)
+ OVS_WARN_UNUSED_RESULT;
+
char *parse_ofp_table_mod(struct ofputil_table_mod *,
const char *table_id, const char *flow_miss_handling,
uint32_t *usable_versions)
diff --git a/include/openvswitch/ofp-util.h b/include/openvswitch/ofp-util.h
index 450e739ac..da0aa0108 100644
--- a/include/openvswitch/ofp-util.h
+++ b/include/openvswitch/ofp-util.h
@@ -1338,18 +1338,20 @@ enum ofperr ofputil_decode_bundle_add(const struct ofp_header *,
struct ofputil_bundle_add_msg *,
enum ofptype *type);
+/* Bundle message as produced by ofp-parse. */
struct ofputil_bundle_msg {
enum ofptype type;
union {
struct ofputil_flow_mod fm;
struct ofputil_group_mod gm;
+ struct ofputil_packet_out po;
};
};
-/* Destroys 'bms'. */
-void ofputil_encode_bundle_msgs(struct ofputil_bundle_msg *bms, size_t n_bms,
- struct ovs_list *requests,
+void ofputil_encode_bundle_msgs(const struct ofputil_bundle_msg *bms,
+ size_t n_bms, struct ovs_list *requests,
enum ofputil_protocol);
+void ofputil_free_bundle_msgs(struct ofputil_bundle_msg *bms, size_t n_bms);
struct ofputil_tlv_map {
struct ovs_list list_node;
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index d3ef14049..0568fc7f6 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -22,6 +22,7 @@
#include <netinet/in.h>
#include "byte-order.h"
+#include "dp-packet.h"
#include "learn.h"
#include "multipath.h"
#include "netdev.h"
@@ -550,6 +551,107 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
return error;
}
+/* Parse a string representation of a OFPT_PACKET_OUT to '*po'. If successful,
+ * both 'po->ofpacts' and 'po->packet' must be free()d by the caller. */
+static char * OVS_WARN_UNUSED_RESULT
+parse_ofp_packet_out_str__(struct ofputil_packet_out *po, char *string,
+ enum ofputil_protocol *usable_protocols)
+{
+ enum ofputil_protocol action_usable_protocols;
+ uint64_t stub[256 / 8];
+ struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
+ struct dp_packet *packet = NULL;
+ char *act_str = NULL;
+ char *name, *value;
+ char *error = NULL;
+
+ *usable_protocols = OFPUTIL_P_ANY;
+
+ *po = (struct ofputil_packet_out) {
+ .buffer_id = UINT32_MAX,
+ .in_port = OFPP_CONTROLLER,
+ };
+
+ act_str = extract_actions(string);
+
+ while (ofputil_parse_key_value(&string, &name, &value)) {
+ if (!*value) {
+ error = xasprintf("field %s missing value", name);
+ goto out;
+ }
+
+ if (!strcmp(name, "in_port")) {
+ if (!ofputil_port_from_string(value, &po->in_port)) {
+ error = xasprintf("%s is not a valid OpenFlow port", value);
+ goto out;
+ }
+ if (po->in_port > OFPP_MAX && po->in_port != OFPP_LOCAL
+ && po->in_port != OFPP_NONE
+ && po->in_port != OFPP_CONTROLLER) {
+ error = xasprintf(
+ "%s is not a valid OpenFlow port for PACKET_OUT",
+ value);
+ goto out;
+ }
+ } else if (!strcmp(name, "packet")) {
+ const char *error_msg = eth_from_hex(value, &packet);
+ if (error_msg) {
+ error = xasprintf("%s: %s", name, error_msg);
+ goto out;
+ }
+ } else {
+ error = xasprintf("unknown keyword %s", name);
+ goto out;
+ }
+ }
+
+ if (!packet || !dp_packet_size(packet)) {
+ error = xstrdup("must specify packet");
+ goto out;
+ }
+
+ if (act_str) {
+ error = ofpacts_parse_actions(act_str, &ofpacts,
+ &action_usable_protocols);
+ *usable_protocols &= action_usable_protocols;
+ if (error) {
+ goto out;
+ }
+ }
+ po->ofpacts_len = ofpacts.size;
+ po->ofpacts = ofpbuf_steal_data(&ofpacts);
+
+ po->packet_len = dp_packet_size(packet);
+ po->packet = dp_packet_steal_data(packet);
+out:
+ ofpbuf_uninit(&ofpacts);
+ dp_packet_delete(packet);
+ return error;
+}
+
+/* Convert 'str_' (as described in the Packet-Out Syntax section of the
+ * ovs-ofctl man page) into 'po' for sending a OFPT_PACKET_OUT message to a
+ * switch. Returns the set of usable protocols in '*usable_protocols'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error. The caller is responsible for freeing the returned string. */
+char * OVS_WARN_UNUSED_RESULT
+parse_ofp_packet_out_str(struct ofputil_packet_out *po, const char *str_,
+ enum ofputil_protocol *usable_protocols)
+{
+ char *string = xstrdup(str_);
+ char *error;
+
+ error = parse_ofp_packet_out_str__(po, string, usable_protocols);
+ if (error) {
+ po->ofpacts = NULL;
+ po->ofpacts_len = 0;
+ }
+
+ free(string);
+ return error;
+}
+
static char * OVS_WARN_UNUSED_RESULT
parse_ofp_meter_mod_str__(struct ofputil_meter_mod *mm, char *string,
struct ofpbuf *bands, int command,
@@ -1709,26 +1811,6 @@ parse_ofp_group_mod_file(const char *file_name, int command,
return NULL;
}
-static void
-free_bundle_msgs(struct ofputil_bundle_msg **bms, size_t *n_bms)
-{
- for (size_t i = 0; i < *n_bms; i++) {
- switch ((int)(*bms)[i].type) {
- case OFPTYPE_FLOW_MOD:
- free(CONST_CAST(struct ofpact *, (*bms)[i].fm.ofpacts));
- break;
- case OFPTYPE_GROUP_MOD:
- ofputil_uninit_group_mod(&(*bms)[i].gm);
- break;
- default:
- break;
- }
- }
- free(*bms);
- *bms = NULL;
- *n_bms = 0;
-}
-
/* Opens file 'file_name' and reads each line as a flow_mod or a group_mod,
* depending on the first keyword on each line. Stores each flow and group
* mods in '*bms', an array allocated on the caller's behalf, and the number of
@@ -1797,6 +1879,13 @@ parse_ofp_bundle_file(const char *file_name,
break;
}
(*bms)[*n_bms].type = OFPTYPE_GROUP_MOD;
+ } else if (!strncmp(s, "packet-out", len)) {
+ s += len;
+ error = parse_ofp_packet_out_str(&(*bms)[*n_bms].po, s, &usable);
+ if (error) {
+ break;
+ }
+ (*bms)[*n_bms].type = OFPTYPE_PACKET_OUT;
} else {
error = xasprintf("Unsupported bundle message type: %.*s",
(int)len, s);
@@ -1816,7 +1905,9 @@ parse_ofp_bundle_file(const char *file_name,
char *err_msg = xasprintf("%s:%d: %s", file_name, line_number, error);
free(error);
- free_bundle_msgs(bms, n_bms);
+ ofputil_free_bundle_msgs(*bms, *n_bms);
+ *bms = NULL;
+ *n_bms = 0;
return err_msg;
}
return NULL;
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 1fa499841..c84ed1719 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -4131,6 +4131,9 @@ ofputil_packet_in_private_destroy(struct ofputil_packet_in_private *pin)
* message's actions. The caller must initialize 'ofpacts' and retains
* ownership of it. 'po->ofpacts' will point into the 'ofpacts' buffer.
*
+ * 'po->packet' refers to the packet data in 'oh', so the buffer containing
+ * 'oh' must not be destroyed while 'po' is being used.
+ *
* Returns 0 if successful, otherwise an OFPERR_* value. */
enum ofperr
ofputil_decode_packet_out(struct ofputil_packet_out *po,
@@ -9462,8 +9465,30 @@ ofputil_decode_group_mod(const struct ofp_header *oh,
/* Destroys 'bms'. */
void
-ofputil_encode_bundle_msgs(struct ofputil_bundle_msg *bms, size_t n_bms,
- struct ovs_list *requests,
+ofputil_free_bundle_msgs(struct ofputil_bundle_msg *bms, size_t n_bms)
+{
+ for (size_t i = 0; i < n_bms; i++) {
+ switch ((int)bms[i].type) {
+ case OFPTYPE_FLOW_MOD:
+ free(CONST_CAST(struct ofpact *, bms[i].fm.ofpacts));
+ break;
+ case OFPTYPE_GROUP_MOD:
+ ofputil_uninit_group_mod(&bms[i].gm);
+ break;
+ case OFPTYPE_PACKET_OUT:
+ free(bms[i].po.ofpacts);
+ free(CONST_CAST(void *, bms[i].po.packet));
+ break;
+ default:
+ break;
+ }
+ }
+ free(bms);
+}
+
+void
+ofputil_encode_bundle_msgs(const struct ofputil_bundle_msg *bms,
+ size_t n_bms, struct ovs_list *requests,
enum ofputil_protocol protocol)
{
enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
@@ -9474,11 +9499,12 @@ ofputil_encode_bundle_msgs(struct ofputil_bundle_msg *bms, size_t n_bms,
switch ((int)bms[i].type) {
case OFPTYPE_FLOW_MOD:
request = ofputil_encode_flow_mod(&bms[i].fm, protocol);
- free(CONST_CAST(struct ofpact *, bms[i].fm.ofpacts));
break;
case OFPTYPE_GROUP_MOD:
request = ofputil_encode_group_mod(version, &bms[i].gm);
- ofputil_uninit_group_mod(&bms[i].gm);
+ break;
+ case OFPTYPE_PACKET_OUT:
+ request = ofputil_encode_packet_out(&bms[i].po, protocol);
break;
default:
break;
@@ -9487,7 +9513,6 @@ ofputil_encode_bundle_msgs(struct ofputil_bundle_msg *bms, size_t n_bms,
ovs_list_push_back(requests, &request->list_node);
}
}
- free(bms);
}
/* Parse a queue status request message into 'oqsr'.
@@ -9875,13 +9900,13 @@ ofputil_is_bundlable(enum ofptype type)
case OFPTYPE_FLOW_MOD:
/* Other supported types. */
case OFPTYPE_GROUP_MOD:
+ case OFPTYPE_PACKET_OUT:
return true;
/* Nice to have later. */
case OFPTYPE_FLOW_MOD_TABLE_ID:
case OFPTYPE_TABLE_MOD:
case OFPTYPE_METER_MOD:
- case OFPTYPE_PACKET_OUT:
case OFPTYPE_NXT_TLV_TABLE_MOD:
/* Not to be bundlable. */
diff --git a/ofproto/bundles.h b/ofproto/bundles.h
index 30692242b..1818b161d 100644
--- a/ofproto/bundles.h
+++ b/ofproto/bundles.h
@@ -33,12 +33,13 @@ extern "C" {
struct ofp_bundle_entry {
struct ovs_list node;
- enum ofptype type; /* OFPTYPE_FLOW_MOD, OFPTYPE_PORT_MOD, or
- * OFPTYPE_GROUP_MOD. */
+ enum ofptype type; /* OFPTYPE_FLOW_MOD, OFPTYPE_PORT_MOD,
+ * OFPTYPE_GROUP_MOD, OFPTYPE_PACKET_OUT. */
union {
struct ofproto_flow_mod ofm;
struct ofproto_port_mod opm;
struct ofproto_group_mod ogm;
+ struct ofproto_packet_out opo;
};
/* OpenFlow header and some of the message contents for error reporting. */
@@ -106,6 +107,8 @@ ofp_bundle_entry_free(struct ofp_bundle_entry *entry)
ofproto_flow_mod_uninit(&entry->ofm);
} else if (entry->type == OFPTYPE_GROUP_MOD) {
ofputil_uninit_group_mod(&entry->ogm.gm);
+ } else if (entry->type == OFPTYPE_PACKET_OUT) {
+ ofproto_packet_out_uninit(&entry->opo);
}
free(entry);
}
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index e96aeb71b..f6f5cd11e 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -4370,6 +4370,28 @@ error_out:
return error;
}
+static void
+packet_xlate_revert(struct ofproto *ofproto OVS_UNUSED,
+ struct ofproto_packet_out *opo)
+ OVS_REQUIRES(ofproto_mutex)
+{
+ struct ofproto_dpif_packet_out *aux = opo->aux;
+ ovs_assert(aux);
+
+ /* Revert the learned flows. */
+ struct xc_entry *entry;
+ struct ofpbuf entries = aux->xcache.entries;
+
+ XC_ENTRY_FOR_EACH (entry, &entries) {
+ if (entry->type == XC_LEARN && entry->learn.ofm->learn_adds_rule) {
+ ofproto_flow_mod_learn_revert(entry->learn.ofm);
+ }
+ }
+
+ ofproto_dpif_packet_out_delete(aux);
+ opo->aux = NULL;
+}
+
/* Push stats and perform side effects of flow translation. */
static void
ofproto_dpif_xcache_execute(struct ofproto_dpif *ofproto,
@@ -5824,6 +5846,7 @@ const struct ofproto_class ofproto_dpif_class = {
rule_dealloc,
rule_get_stats,
packet_xlate,
+ packet_xlate_revert,
packet_execute,
set_frag_handling,
nxt_resume,
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index dd700d20e..be5632eb7 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -1313,6 +1313,11 @@ struct ofproto_class {
enum ofperr (*packet_xlate)(struct ofproto *,
struct ofproto_packet_out *opo);
+ /* Free resources taken by a successful packet_xlate(). If multiple
+ * packet_xlate() calls have been made in sequence, the corresponding
+ * packet_xlate_revert() calls have to be made in reverse order. */
+ void (*packet_xlate_revert)(struct ofproto *, struct ofproto_packet_out *);
+
/* Executes the datapath actions, translation side-effects, and stats as
* produced by ->packet_xlate(). The caller retains ownership of 'opo'.
*/
@@ -1907,6 +1912,8 @@ enum ofperr ofproto_flow_mod_learn(struct ofproto_flow_mod *, bool keep_ref)
enum ofperr ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm);
enum ofperr ofproto_flow_mod_learn_start(struct ofproto_flow_mod *ofm)
OVS_REQUIRES(ofproto_mutex);
+void ofproto_flow_mod_learn_revert(struct ofproto_flow_mod *ofm)
+ OVS_REQUIRES(ofproto_mutex);
void ofproto_flow_mod_learn_finish(struct ofproto_flow_mod *ofm,
struct ofproto *orig_ofproto)
OVS_REQUIRES(ofproto_mutex);
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index ae07e7e6b..90b1ffab0 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -259,6 +259,9 @@ static enum ofperr ofproto_flow_mod_init(struct ofproto *,
static enum ofperr ofproto_flow_mod_start(struct ofproto *,
struct ofproto_flow_mod *)
OVS_REQUIRES(ofproto_mutex);
+static void ofproto_flow_mod_revert(struct ofproto *,
+ struct ofproto_flow_mod *)
+ OVS_REQUIRES(ofproto_mutex);
static void ofproto_flow_mod_finish(struct ofproto *,
struct ofproto_flow_mod *,
const struct openflow_mod_requester *)
@@ -3465,6 +3468,14 @@ ofproto_packet_out_start(struct ofproto *ofproto,
}
static void
+ofproto_packet_out_revert(struct ofproto *ofproto,
+ struct ofproto_packet_out *opo)
+ OVS_REQUIRES(ofproto_mutex)
+{
+ ofproto->ofproto_class->packet_xlate_revert(ofproto, opo);
+}
+
+static void
ofproto_packet_out_finish(struct ofproto *ofproto,
struct ofproto_packet_out *opo)
OVS_REQUIRES(ofproto_mutex)
@@ -4977,6 +4988,14 @@ ofproto_flow_mod_learn_start(struct ofproto_flow_mod *ofm)
}
void
+ofproto_flow_mod_learn_revert(struct ofproto_flow_mod *ofm)
+ OVS_REQUIRES(ofproto_mutex)
+{
+ struct rule *rule = rule_collection_rules(&ofm->new_rules)[0];
+ ofproto_flow_mod_revert(rule->ofproto, ofm);
+}
+
+void
ofproto_flow_mod_learn_finish(struct ofproto_flow_mod *ofm,
struct ofproto *orig_ofproto)
OVS_REQUIRES(ofproto_mutex)
@@ -7495,6 +7514,9 @@ do_bundle_commit(struct ofconn *ofconn, uint32_t id, uint16_t flags)
* effect. */
be->ogm.version = version;
error = ofproto_group_mod_start(ofproto, &be->ogm);
+ } else if (be->type == OFPTYPE_PACKET_OUT) {
+ be->opo.version = version;
+ error = ofproto_packet_out_start(ofproto, &be->opo);
} else {
OVS_NOT_REACHED();
}
@@ -7517,8 +7539,9 @@ do_bundle_commit(struct ofconn *ofconn, uint32_t id, uint16_t flags)
ofproto_flow_mod_revert(ofproto, &be->ofm);
} else if (be->type == OFPTYPE_GROUP_MOD) {
ofproto_group_mod_revert(ofproto, &be->ogm);
+ } else if (be->type == OFPTYPE_PACKET_OUT) {
+ ofproto_packet_out_revert(ofproto, &be->opo);
}
-
/* Nothing needs to be reverted for a port mod. */
}
} else {
@@ -7533,30 +7556,30 @@ do_bundle_commit(struct ofconn *ofconn, uint32_t id, uint16_t flags)
* processing. */
port_mod_finish(ofconn, &be->opm.pm, be->opm.port);
} else {
+ version =
+ (be->type == OFPTYPE_FLOW_MOD) ? be->ofm.version :
+ (be->type == OFPTYPE_GROUP_MOD) ? be->ogm.version :
+ (be->type == OFPTYPE_PACKET_OUT) ? be->opo.version :
+ version;
+
+ /* Bump the lookup version to the one of the current
+ * message. This makes all the changes in the bundle at
+ * this version visible to lookups at once. */
+ if (ofproto->tables_version < version) {
+ ofproto->tables_version = version;
+ ofproto->ofproto_class->set_tables_version(
+ ofproto, ofproto->tables_version);
+ }
+
struct openflow_mod_requester req = { ofconn,
&be->ofp_msg };
- if (be->type == OFPTYPE_FLOW_MOD) {
- /* Bump the lookup version to the one of the current
- * message. This makes all the changes in the bundle
- * at this version visible to lookups at once. */
- if (ofproto->tables_version < be->ofm.version) {
- ofproto->tables_version = be->ofm.version;
- ofproto->ofproto_class->set_tables_version(
- ofproto, ofproto->tables_version);
- }
+ if (be->type == OFPTYPE_FLOW_MOD) {
ofproto_flow_mod_finish(ofproto, &be->ofm, &req);
} else if (be->type == OFPTYPE_GROUP_MOD) {
- /* Bump the lookup version to the one of the current
- * message. This makes all the changes in the bundle
- * at this version visible to lookups at once. */
- if (ofproto->tables_version < be->ogm.version) {
- ofproto->tables_version = be->ogm.version;
- ofproto->ofproto_class->set_tables_version(
- ofproto, ofproto->tables_version);
- }
-
ofproto_group_mod_finish(ofproto, &be->ogm, &req);
+ } else if (be->type == OFPTYPE_PACKET_OUT) {
+ ofproto_packet_out_finish(ofproto, &be->opo);
}
}
}
@@ -7646,14 +7669,15 @@ handle_bundle_add(struct ofconn *ofconn, const struct ofp_header *oh)
bmsg = ofp_bundle_entry_alloc(type, badd.msg);
+ struct ofpbuf ofpacts;
+ uint64_t ofpacts_stub[1024 / 8];
+ ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+
if (type == OFPTYPE_PORT_MOD) {
error = ofputil_decode_port_mod(badd.msg, &bmsg->opm.pm, false);
} else if (type == OFPTYPE_FLOW_MOD) {
struct ofputil_flow_mod fm;
- struct ofpbuf ofpacts;
- uint64_t ofpacts_stub[1024 / 8];
- ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
error = ofputil_decode_flow_mod(&fm, badd.msg,
ofconn_get_protocol(ofconn),
&ofpacts,
@@ -7662,13 +7686,25 @@ handle_bundle_add(struct ofconn *ofconn, const struct ofp_header *oh)
if (!error) {
error = ofproto_flow_mod_init(ofproto, &bmsg->ofm, &fm, NULL);
}
- ofpbuf_uninit(&ofpacts);
} else if (type == OFPTYPE_GROUP_MOD) {
error = ofputil_decode_group_mod(badd.msg, &bmsg->ogm.gm);
+ } else if (type == OFPTYPE_PACKET_OUT) {
+ struct ofputil_packet_out po;
+
+ COVERAGE_INC(ofproto_packet_out);
+
+ /* Decode message. */
+ error = ofputil_decode_packet_out(&po, badd.msg, &ofpacts);
+ if (!error) {
+ po.ofpacts = ofpbuf_steal_data(&ofpacts); /* Move to heap. */
+ error = ofproto_packet_out_init(ofproto, ofconn, &bmsg->opo, &po);
+ }
} else {
OVS_NOT_REACHED();
}
+ ofpbuf_uninit(&ofpacts);
+
if (!error) {
error = ofp_bundle_add_message(ofconn, badd.bundle_id, badd.flags,
bmsg, oh);
diff --git a/tests/bfd.at b/tests/bfd.at
index 8bbf00b27..46a507a77 100644
--- a/tests/bfd.at
+++ b/tests/bfd.at
@@ -304,7 +304,7 @@ BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms])
for i in `seq 0 49`
do
ovs-appctl time/warp 100
- AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"],
[0], [stdout], [])
done
# after a decay interval (3000ms), the p0 min_rx will go back to
@@ -435,7 +435,7 @@ done
for i in `seq 0 19`
do
ovs-appctl time/warp 100
- AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"],
[0], [stdout], [])
done
# the forwarding flag should be false, due to the demand_rx_bfd.
@@ -485,7 +485,7 @@ do
BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
for i in `seq 0 4`
do
- AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"],
[0], [stdout], [])
done
done
@@ -562,7 +562,7 @@ AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false], [0])
for i in `seq 0 7`
do
ovs-appctl time/warp 500
- AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"],
[0], [stdout], [])
done
BFD_CHECK([p0], [true], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
@@ -570,7 +570,7 @@ BFD_CHECK([p0], [true], [false], [none], [down], [Control Detection Time Expired
# receive packet at 1/100ms rate for 1000ms.
for i in `seq 0 9`
do
- AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"],
[0], [stdout], [])
ovs-appctl time/warp 100
# the forwarding flag should always be true during this time.
@@ -629,7 +629,7 @@ AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false], [0])
for i in `seq 0 7`
do
ovs-appctl time/warp 500
- AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"],
[0], [stdout], [])
done
BFD_CHECK([p0], [true], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
@@ -640,7 +640,7 @@ BFD_CHECK([p0], [true], [false], [none], [down], [Control Detection Time Expired
for i in `seq 0 120`
do
ovs-appctl time/warp 300
- AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"],
[0], [stdout], [])
done
BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
@@ -661,7 +661,7 @@ AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false], [0])
for i in `seq 0 120`
do
ovs-appctl time/warp 300
- AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"],
[0], [stdout], [])
done
BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
@@ -761,7 +761,7 @@ AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false], [0])
for i in `seq 0 39`
do
ovs-appctl time/warp 100
- AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"],
[0], [stdout], [])
done
BFD_CHECK([p0], [true], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
@@ -778,7 +778,7 @@ BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["6"]
for i in `seq 0 39`
do
ovs-appctl time/warp 100
- AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"],
[0], [stdout], [])
done
# forwarding should be false, since there is still no bfd control packet received.
diff --git a/tests/cfm.at b/tests/cfm.at
index 0033f3029..b3770b508 100644
--- a/tests/cfm.at
+++ b/tests/cfm.at
@@ -176,7 +176,7 @@ AT_CHECK([ovs-vsctl clear Interface p1 cfm_mpid])
for i in `seq 0 200`
do
ovs-appctl time/warp 100
- AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"],
[0], [stdout], [])
done
CFM_CHECK_EXTENDED([p0], [1], [0], [up], [up], [300ms], [2], [up])
@@ -186,7 +186,7 @@ CFM_CHECK_EXTENDED([p0], [1], [0], [up], [up], [300ms], [2], [up])
for i in `seq 0 200`
do
ovs-appctl time/warp 100
- AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"],
[0], [stdout], [])
done
CFM_CHECK_EXTENDED_FAULT([p0], [1], [recv], [0], [up], [up], [300ms])
@@ -204,7 +204,7 @@ AT_CHECK([ovs-vsctl clear Interface p1 cfm_mpid])
for i in `seq 0 400`
do
ovs-appctl time/warp 100
- AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ AT_CHECK([ovs-ofctl packet-out br1 "in_port=3 packet=90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202 actions=2"],
[0], [stdout], [])
done
CFM_CHECK_EXTENDED_FAULT([p0], [1], [recv], [0], [up], [up], [300ms])
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index 5d2040bdb..6c7433d85 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -3532,6 +3532,25 @@ OFPT_GROUP_MOD (OF1.5) (xid=0x3):
])
AT_CLEANUP
+AT_SETUP([OFPT_BUNDLE_ADD_MESSAGE - PACKET_OUT])
+AT_KEYWORDS([ofp-print bundle packet-out])
+AT_CHECK([ovs-ofctl ofp-print "\
+05 22 00 74 00 00 00 03 00 00 00 01 00 00 00 01 \
+05 0d 00 64 00 00 00 03 ff ff ff ff ff ff ff fe \
+00 10 00 00 00 00 00 00 00 00 00 10 ff ff ff fb \
+05 dc 00 00 00 00 00 00 50 54 00 00 00 05 50 54 \
+00 00 00 06 08 00 45 00 00 28 00 00 40 00 40 06 \
+b9 7c c0 a8 00 02 c0 a8 00 01 00 00 2b 60 00 00 \
+00 00 6a 4f 2b 58 50 14 00 00 6d 75 00 00 00 00 \
+00 00 00 00 \
+"], [0], [dnl
+OFPT_BUNDLE_ADD_MESSAGE (OF1.4) (xid=0x3):
+ bundle_id=0x1 flags=atomic
+OFPT_PACKET_OUT (OF1.4) (xid=0x3): in_port=LOCAL actions=FLOOD data_len=60
+tcp,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=0,tp_dst=11104,tcp_flags=rst|ack tcp_csum:6d75
+])
+AT_CLEANUP
+
AT_SETUP([NXST_IPFIX_BRIDGE - request])
AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
AT_CHECK([ovs-ofctl ofp-print "\
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 24867c761..f8af82230 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -1965,6 +1965,131 @@ AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sort], [0],
OVS_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([ofproto - bundle packet-out (OpenFlow 1.4)])
+OVS_VSWITCHD_START
+
+ovs-ofctl del-flows br0
+ovs-ofctl add-flow br0 priority=0,actions=drop
+
+# Start a monitor listening for packet-ins.
+AT_CHECK([ovs-ofctl -P standard monitor br0 --detach --no-chdir --pidfile])
+ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+AT_CAPTURE_FILE([monitor.log])
+
+# This bundle adds a group, a flow using that group and then a
+# packet-out that needs them both. Finally the bundle deletes all
+# groups, which also deletes the flow, leaving only the drop flow in
+# the table. If this works properly, the packet-out should get
+# translated and processed before the flow disappears. Also, if the
+# bundle were to fail, the packet-in should not get executed.
+AT_DATA([bundle.txt], [dnl
+group group_id=1,type=all,bucket=output:controller
+flow in_port=6 actions=group:1
+packet-out in_port=6, packet=0001020304050010203040501234 actions=table
+group delete
+])
+AT_CHECK([ovs-ofctl bundle br0 bundle.txt])
+
+# Verify that only the drop flow remains.
+AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sort], [0],
+ [ reset_counts priority=0 actions=drop
+OFPST_FLOW reply (OF1.4):
+])
+
+# Verify that the packet-in was received via controller action.
+AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0],
+ [OFPT_PACKET_IN (xid=0x0): total_len=14 in_port=6 (via action) data_len=14 (unbuffered)
+vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - bundle packet-out, failing bundle commit (OpenFlow 1.4)])
+OVS_VSWITCHD_START
+
+ovs-ofctl del-flows br0
+ovs-ofctl add-flow br0 priority=0,actions=drop
+
+# Start a monitor listening for packet-ins.
+AT_CHECK([ovs-ofctl -P standard monitor br0 --detach --no-chdir --pidfile])
+ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+AT_CAPTURE_FILE([monitor.log])
+
+# This bundle adds a flow using that group and then a packet-out that
+# needs them both. Finally the bundle adds another flow that referes
+# to a non-existing group, causing the bundle to fail, and the
+# packet-in should not get executed.
+AT_DATA([bundle.txt], [dnl
+group group_id=1,type=all,bucket=output:controller
+flow in_port=6 actions=group:1
+packet-out in_port=6, packet=0001020304050010203040501234 actions=table
+flow in_port=7 actions=group:2
+])
+AT_CHECK([ovs-ofctl bundle br0 bundle.txt 2>&1 | sed '/talking to/,$d'], [], [dnl
+Error OFPBAC_BAD_OUT_GROUP for: OFPT_FLOW_MOD (OF1.4) (xid=0x5): ADD in_port=7 actions=group:2
+Error OFPBFC_MSG_FAILED for: OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x8):
+ bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered
+])
+
+# Verify that only the drop flow remains.
+AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sort], [0],
+ [ reset_counts priority=0 actions=drop
+OFPST_FLOW reply (OF1.4):
+])
+
+# Verify that the packet-in was NOT received via controller action.
+AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0], [])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - bundle packet-out makes bundle commit to fail(OpenFlow 1.4)])
+OVS_VSWITCHD_START
+
+ovs-ofctl del-flows br0
+ovs-ofctl add-flow br0 priority=0,actions=drop
+
+# Start a monitor listening for packet-ins.
+AT_CHECK([ovs-ofctl -P standard monitor br0 --detach --no-chdir --pidfile])
+ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+AT_CAPTURE_FILE([monitor.log])
+
+# This bundle adds a flow using that group and then a packet-out that
+# needs them both. Finally the bundle adds another flow that referes
+# to a non-existing group, causing the bundle to fail, and the
+# packet-in should not get executed.
+AT_DATA([bundle.txt], [dnl
+group group_id=1,type=all,bucket=output:controller
+flow in_port=6 actions=group:1
+packet-out in_port=6, packet=0001020304050010203040501234 actions=table
+packet-out in_port=6, packet=0001020304050010203040501234 actions=group:2
+])
+AT_CHECK([ovs-ofctl bundle br0 bundle.txt 2>&1 | sed '/talking to/,$d'], [], [dnl
+Error OFPBAC_BAD_OUT_GROUP for: OFPT_PACKET_OUT (OF1.4) (xid=0x5): in_port=6 actions=group:2 data_len=14
+vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234
+Error OFPBFC_MSG_FAILED for: OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x8):
+ bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered
+])
+
+# Verify that only the drop flow remains.
+AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sort], [0],
+ [ reset_counts priority=0 actions=drop
+OFPST_FLOW reply (OF1.4):
+])
+
+# Verify that the packet-in was NOT received via controller action.
+AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0], [])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([ofproto - flow table configuration (OpenFlow 1.0)])
OVS_VSWITCHD_START
# Check the default configuration.
@@ -2979,7 +3104,7 @@ vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234
fi
# OFPT_PACKET_IN, OFPR_INVALID_TTL (controller_id=0)
- ovs-ofctl packet-out br0 controller dec_ttl '002583dfb4000026b98cb0f908004500003eb7e200000011339bac11370dac100002d7730035002b8f6d86fb0100000100000000000006626c702d7873066e696369726103636f6d00000f00'
+ ovs-ofctl packet-out br0 "in_port=controller packet=002583dfb4000026b98cb0f908004500003eb7e200000011339bac11370dac100002d7730035002b8f6d86fb0100000100000000000006626c702d7873066e696369726103636f6d00000f00 actions=dec_ttl"
if test X"$1" = X"OFPR_INVALID_TTL"; then shift;
echo >>expout "OFPT_PACKET_IN: total_len=76 in_port=CONTROLLER (via invalid_ttl) data_len=76 (unbuffered)
udp,vlan_tci=0x0000,dl_src=00:26:b9:8c:b0:f9,dl_dst=00:25:83:df:b4:00,nw_src=172.17.55.13,nw_dst=172.16.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=55155,tp_dst=53 udp_csum:8f6d"
@@ -3777,8 +3902,8 @@ ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
AT_CAPTURE_FILE([monitor.log])
# Send some packet-outs with OFPP_NONE and OFPP_CONTROLLER (65533) as in_port.
-AT_CHECK([ovs-ofctl packet-out br0 none controller,1 '0001020304050010203040501234'])
-AT_CHECK([ovs-ofctl packet-out br0 controller controller,1 '0001020304050010203040505678'])
+AT_CHECK([ovs-ofctl packet-out br0 "in_port=none packet=0001020304050010203040501234 actions=controller,1"])
+AT_CHECK([ovs-ofctl packet-out br0 "in_port=controller packet=0001020304050010203040505678 actions=controller,1"])
# Stop the monitor and check its output.
ovs-appctl -t ovs-ofctl ofctl/barrier
@@ -3844,15 +3969,16 @@ ovs-appctl -t ovs-ofctl ofctl/barrier
ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
AT_CAPTURE_FILE([monitor.log])
-# Send some packet-outs with OFPP_NONE and OFPP_CONTROLLER (65533) as in_port.
-AT_CHECK([ovs-ofctl -O OpenFlow11 packet-out br0 none controller '0001020304050010203040501234'])
-AT_CHECK([ovs-ofctl -O OpenFlow11 packet-out br0 4294967293 controller '0001020304050010203040505678'])
+# Send some packet-outs with OFPP_NONE and OFPP_CONTROLLER as in_port.
+AT_CHECK([ovs-appctl -t ovs-ofctl ofctl/packet-out "in_port=none, packet=0001020304050010203040501234 actions=controller"])
+AT_CHECK([ovs-appctl -t ovs-ofctl ofctl/packet-out "in_port=controller packet=0001020304050010203040505678 actions=controller"])
# Stop the monitor and check its output.
ovs-appctl -t ovs-ofctl ofctl/barrier
OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
-AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
+AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//
+/PACKET_OUT/d' monitor.log], [0], [dnl
OFPT_PACKET_IN (OF1.1): total_len=14 in_port=ANY (via action) data_len=14 (unbuffered)
vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234
OFPT_PACKET_IN (OF1.1): total_len=14 in_port=CONTROLLER (via action) data_len=14 (unbuffered)
@@ -3875,7 +4001,7 @@ ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
AT_CAPTURE_FILE([monitor.log])
# Send a packet-out with a load action to set some metadata, and forward to controller
-AT_CHECK([ovs-ofctl packet-out br0 controller 'load(0xfafafafa5a5a5a5a->OXM_OF_METADATA[[0..63]]), load(0xaa->NXM_NX_PKT_MARK[[]]), controller(userdata=01.02.03.04.05)' '0001020304050010203040501234'])
+AT_CHECK([ovs-ofctl packet-out br0 "in_port=controller packet=0001020304050010203040501234 actions=load(0xfafafafa5a5a5a5a->OXM_OF_METADATA[[0..63]]),load(0xaa->NXM_NX_PKT_MARK[[]]),controller(userdata=01.02.03.04.05)"])
# Stop the monitor and check its output.
ovs-appctl -t ovs-ofctl ofctl/barrier
@@ -3904,7 +4030,7 @@ ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
AT_CAPTURE_FILE([monitor.log])
# Send a packet-out with a load action to set some metadata, and forward to controller
-AT_CHECK([ovs-ofctl packet-out br0 controller 'load(0xfafafafa5a5a5a5a->OXM_OF_METADATA[[0..63]]), load(0xaa->NXM_NX_PKT_MARK[[]]), controller' '0001020304050010203040501234'])
+AT_CHECK([ovs-ofctl packet-out br0 "in_port=controller packet=0001020304050010203040501234 actions=load(0xfafafafa5a5a5a5a->OXM_OF_METADATA[[0..63]]),load(0xaa->NXM_NX_PKT_MARK[[]]),controller"])
# Stop the monitor and check its output.
ovs-appctl -t ovs-ofctl ofctl/barrier
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index b56e5b3f6..b648ab847 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -409,15 +409,10 @@ For this command, an exit status of 0 means that no differences were
found, 1 means that an error occurred, and 2 means that some
differences were found.
.
-.IP "\fBpacket\-out \fIswitch in_port actions packet\fR..."
-Connects to \fIswitch\fR and instructs it to execute the OpenFlow
-\fIactions\fR on each \fIpacket\fR. Each \fBpacket\fR is specified as a
-series of hex digits. For the purpose of executing the
-actions, the packets are considered to have arrived on \fIin_port\fR,
-which may be an OpenFlow port number or name (e.g. \fBeth0\fR), the
-keyword \fBLOCAL\fR (the preferred way to refer to the OpenFlow
-``local'' port), or the keyword \fBNONE\fR to indicate that the packet
-was generated by the switch itself.
+.IP "\fBpacket\-out \fIswitch\fR \fIpacket-out\fR"
+Connects to \fIswitch\fR and instructs it to execute the
+\fIpacket-out\fR OpenFlow message, specified as defined in
+\fBPacket\-Out Syntax\fR section.
.
.SS "OpenFlow Switch Group Table Commands"
.
@@ -473,10 +468,11 @@ buckets of the group are removed.
.
Transactional updates to both flow and group tables can be made with
the \fBbundle\fR command. \fIfile\fR is a text file that contains
-zero or more flows and groups in either \fBFlow Syntax\fR or \fBGroup
-Syntax\fR, each line preceded by either \fBflow\fR or \fBgroup\fR
-keyword. The \fBflow\fR keyword may be optionally followed by one of
-the keywords \fBadd\fR, \fBmodify\fR, \fBmodify_strict\fR,
+zero or more flow mods, group mods, or packet-outs in \fBFlow
+Syntax\fR, \fBGroup Syntax\fR, or \fBPacket\-Out Syntax\fR, each line
+preceded by \fBflow\fR, \fBgroup\fR, or \fBpacket\-out\fR keyword,
+correspondingly. The \fBflow\fR keyword may be optionally followed by
+one of the keywords \fBadd\fR, \fBmodify\fR, \fBmodify_strict\fR,
\fBdelete\fR, or \fBdelete_strict\fR, of which the \fBadd\fR is
assumed if a bare \fBflow\fR is given. Similarly, the \fBgroup\fR
keyword may be optionally followed by one of the keywords \fBadd\fR,
@@ -2756,6 +2752,38 @@ of \fBduration\fR. (This is separate from \fBduration\fR because
The integer number of seconds that have passed without any packets
passing through the flow.
.
+.SS "Packet\-Out Syntax"
+.PP
+\fBovs\-ofctl bundle\fR command accepts packet-outs to be specified in
+the bundle file. Each packet-out comprises of a series of
+\fIfield\fB=\fIvalue\fR assignments, separated by commas or white
+space. (Embedding spaces into a packet-out description normally
+requires quoting to prevent the shell from breaking the description
+into multiple arguments.). Unless noted otherwise only the last
+instance of each field is honoured. This same syntax is also
+supported by the \fBovs\-ofctl packet-out\fR command.
+.PP
+.IP \fBin_port=\fIport\fR
+The port number to be considered the in_port when processing actions.
+This can be any valid OpenFlow port number, or any of the \fBLOCAL\fR,
+\fBCONTROLLER\fR, or \fBNONE\fR.
+.
+This field is required.
+
+.IP \fBpacket=\fIhex-string\fR
+The actual packet to send, expressed as a string of hexadecimal bytes.
+.
+This field is required.
+
+.IP \fBactions=\fR[\fIaction\fR][\fB,\fIaction\fR...]\fR
+The syntax of actions are identical to the \fBactions=\fR field
+described in \fBFlow Syntax\fR above. Specifying \fBactions=\fR is
+optional, but omitting actions is interpreted as a drop, so the packet
+will not be sent anywhere from the switch.
+.
+\fBactions\fR must be specified at the end of each line, like for flow mods.
+.RE
+.
.SS "Group Syntax"
.PP
Some \fBovs\-ofctl\fR commands accept an argument that describes a group or
@@ -2897,7 +2925,7 @@ of OpenFlow are used. Open vSwitch will automatically allocate bucket
ids when they are not specified.
.IP \fBactions=\fR[\fIaction\fR][\fB,\fIaction\fR...]\fR
The syntax of actions are identical to the \fBactions=\fR field described in
-\fBFlow Syntax\fR above. Specyfing \fBactions=\fR is optional, any unknown
+\fBFlow Syntax\fR above. Specifying \fBactions=\fR is optional, any unknown
bucket parameter will be interpreted as an action.
.IP \fBweight=\fIvalue\fR
The relative weight of the bucket as an integer. This may be used by the switch
@@ -3176,6 +3204,12 @@ Sends each \fIofmsg\fR, specified as a sequence of hex digits that
express an OpenFlow message, on the OpenFlow connection. This command
is useful only when executing the \fBmonitor\fR command.
.
+.IP "\fBofctl/packet\-out \fIpacket-out\fR"
+Sends an OpenFlow PACKET_OUT message specified in \fBPacket\-Out
+Syntax\fR, on the OpenFlow connection. See \fBPacket\-Out Syntax\fR
+section for more information. This command is useful only when
+executing the \fBmonitor\fR command.
+.
.IP "\fBofctl/barrier\fR"
Sends an OpenFlow barrier request on the OpenFlow connection and waits
for a reply. This command is useful only for the \fBmonitor\fR
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 306bb78c8..4c0c70b35 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -1682,6 +1682,58 @@ ofctl_send(struct unixctl_conn *conn, int argc,
ds_destroy(&reply);
}
+static void
+unixctl_packet_out(struct unixctl_conn *conn, int OVS_UNUSED argc,
+ const char *argv[], void *vconn_)
+{
+ struct vconn *vconn = vconn_;
+ enum ofputil_protocol protocol
+ = ofputil_protocol_from_ofp_version(vconn_get_version(vconn));
+ struct ds reply = DS_EMPTY_INITIALIZER;
+ bool ok = true;
+
+ enum ofputil_protocol usable_protocols;
+ struct ofputil_packet_out po;
+ char *error_msg;
+
+ error_msg = parse_ofp_packet_out_str(&po, argv[1], &usable_protocols);
+ if (error_msg) {
+ ds_put_format(&reply, "%s\n", error_msg);
+ free(error_msg);
+ ok = false;
+ }
+
+ if (ok && !(usable_protocols & protocol)) {
+ ds_put_format(&reply, "PACKET_OUT actions are incompatible with the OpenFlow connection.\n");
+ ok = false;
+ }
+
+ if (ok) {
+ struct ofpbuf *msg = ofputil_encode_packet_out(&po, protocol);
+
+ ofp_print(stderr, msg->data, msg->size, verbosity);
+
+ int error = vconn_send_block(vconn, msg);
+ if (error) {
+ ofpbuf_delete(msg);
+ ds_put_format(&reply, "%s\n", ovs_strerror(error));
+ ok = false;
+ }
+ }
+
+ if (ok) {
+ unixctl_command_reply(conn, ds_cstr(&reply));
+ } else {
+ unixctl_command_reply_error(conn, ds_cstr(&reply));
+ }
+ ds_destroy(&reply);
+
+ if (!error_msg) {
+ free(CONST_CAST(void *, po.packet));
+ free(po.ofpacts);
+ }
+}
+
struct barrier_aux {
struct vconn *vconn; /* OpenFlow connection for sending barrier. */
struct unixctl_conn *conn; /* Connection waiting for barrier response. */
@@ -1782,6 +1834,8 @@ monitor_vconn(struct vconn *vconn, bool reply_to_echo_requests,
unixctl_command_register("exit", "", 0, 0, ofctl_exit, &exiting);
unixctl_command_register("ofctl/send", "OFMSG...", 1, INT_MAX,
ofctl_send, vconn);
+ unixctl_command_register("ofctl/packet-out", "\"in_port=<port> packet=<hex data> actions=...\"", 1, 1,
+ unixctl_packet_out, vconn);
unixctl_command_register("ofctl/barrier", "", 0, 0,
ofctl_barrier, &barrier_aux);
unixctl_command_register("ofctl/set-output-file", "FILE", 1, 1,
@@ -2041,44 +2095,63 @@ ofctl_probe(struct ovs_cmdl_context *ctx)
static void
ofctl_packet_out(struct ovs_cmdl_context *ctx)
{
+ enum ofputil_protocol usable_protocols;
enum ofputil_protocol protocol;
struct ofputil_packet_out po;
- struct ofpbuf ofpacts;
struct vconn *vconn;
+ struct ofpbuf *opo;
char *error;
- int i;
- enum ofputil_protocol usable_protocols; /* XXX: Use in proto selection */
- ofpbuf_init(&ofpacts, 64);
- error = ofpacts_parse_actions(ctx->argv[3], &ofpacts, &usable_protocols);
- if (error) {
- ovs_fatal(0, "%s", error);
- }
+ /* Use the old syntax when more than 4 arguments are given. */
+ if (ctx->argc > 4) {
+ struct ofpbuf ofpacts;
+ int i;
- po.buffer_id = UINT32_MAX;
- po.in_port = str_to_port_no(ctx->argv[1], ctx->argv[2]);
- po.ofpacts = ofpacts.data;
- po.ofpacts_len = ofpacts.size;
+ ofpbuf_init(&ofpacts, 64);
+ error = ofpacts_parse_actions(ctx->argv[3], &ofpacts,
+ &usable_protocols);
+ if (error) {
+ ovs_fatal(0, "%s", error);
+ }
- protocol = open_vconn(ctx->argv[1], &vconn);
- for (i = 4; i < ctx->argc; i++) {
- struct dp_packet *packet;
- struct ofpbuf *opo;
- const char *error_msg;
+ po.buffer_id = UINT32_MAX;
+ po.in_port = str_to_port_no(ctx->argv[1], ctx->argv[2]);
+ po.ofpacts = ofpacts.data;
+ po.ofpacts_len = ofpacts.size;
- error_msg = eth_from_hex(ctx->argv[i], &packet);
- if (error_msg) {
- ovs_fatal(0, "%s", error_msg);
- }
+ protocol = open_vconn_for_flow_mod(ctx->argv[1], &vconn,
+ usable_protocols);
+ for (i = 4; i < ctx->argc; i++) {
+ struct dp_packet *packet;
+ const char *error_msg;
+
+ error_msg = eth_from_hex(ctx->argv[i], &packet);
+ if (error_msg) {
+ ovs_fatal(0, "%s", error_msg);
+ }
- po.packet = dp_packet_data(packet);
- po.packet_len = dp_packet_size(packet);
+ po.packet = dp_packet_data(packet);
+ po.packet_len = dp_packet_size(packet);
+ opo = ofputil_encode_packet_out(&po, protocol);
+ transact_noreply(vconn, opo);
+ dp_packet_delete(packet);
+ }
+ vconn_close(vconn);
+ ofpbuf_uninit(&ofpacts);
+ } else if (ctx->argc == 3) {
+ error = parse_ofp_packet_out_str(&po, ctx->argv[2], &usable_protocols);
+ if (error) {
+ ovs_fatal(0, "%s", error);
+ }
+ protocol = open_vconn_for_flow_mod(ctx->argv[1], &vconn,
+ usable_protocols);
opo = ofputil_encode_packet_out(&po, protocol);
transact_noreply(vconn, opo);
- dp_packet_delete(packet);
+ free(CONST_CAST(void *, po.packet));
+ free(po.ofpacts);
+ } else {
+ ovs_fatal(0, "Too many arguments (%d)", ctx->argc);
}
- vconn_close(vconn);
- ofpbuf_uninit(&ofpacts);
}
static void
@@ -2781,6 +2854,7 @@ ofctl_bundle(struct ovs_cmdl_context *ctx)
ovs_list_init(&requests);
ofputil_encode_bundle_msgs(bms, n_bms, &requests, protocol);
+ ofputil_free_bundle_msgs(bms, n_bms);
bundle_transact(vconn, &requests, OFPBF_ORDERED | OFPBF_ATOMIC);
ofpbuf_list_delete(&requests);
@@ -4372,8 +4446,8 @@ static const struct ovs_cmdl_command all_commands[] = {
1, 2, ofctl_meter_stats, OVS_RO },
{ "meter-features", "switch",
1, 1, ofctl_meter_features, OVS_RO },
- { "packet-out", "switch in_port actions packet...",
- 4, INT_MAX, ofctl_packet_out, OVS_RW },
+ { "packet-out", "switch \"in_port=<port> packet=<hex data> actions=...\"",
+ 2, INT_MAX, ofctl_packet_out, OVS_RW },
{ "dump-ports", "switch [port]",
1, 2, ofctl_dump_ports, OVS_RO },
{ "dump-ports-desc", "switch [port]",