diff options
author | Jarno Rajahalme <jarno@ovn.org> | 2016-09-14 16:51:27 -0700 |
---|---|---|
committer | Jarno Rajahalme <jarno@ovn.org> | 2016-09-14 16:51:27 -0700 |
commit | 6dd3c787f5914ea27b239b6852d8fc0815033128 (patch) | |
tree | 69fc093392b33e9b3f4043359a225eebd12a39b4 | |
parent | 1f4a893366826e392722d5b1ba59e94331bfe5c9 (diff) | |
download | openvswitch-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-- | NEWS | 9 | ||||
-rw-r--r-- | include/openvswitch/ofp-parse.h | 5 | ||||
-rw-r--r-- | include/openvswitch/ofp-util.h | 8 | ||||
-rw-r--r-- | lib/ofp-parse.c | 133 | ||||
-rw-r--r-- | lib/ofp-util.c | 37 | ||||
-rw-r--r-- | ofproto/bundles.h | 7 | ||||
-rw-r--r-- | ofproto/ofproto-dpif.c | 23 | ||||
-rw-r--r-- | ofproto/ofproto-provider.h | 7 | ||||
-rw-r--r-- | ofproto/ofproto.c | 82 | ||||
-rw-r--r-- | tests/bfd.at | 20 | ||||
-rw-r--r-- | tests/cfm.at | 6 | ||||
-rw-r--r-- | tests/ofp-print.at | 19 | ||||
-rw-r--r-- | tests/ofproto.at | 144 | ||||
-rw-r--r-- | utilities/ovs-ofctl.8.in | 62 | ||||
-rw-r--r-- | utilities/ovs-ofctl.c | 130 |
15 files changed, 573 insertions, 119 deletions
@@ -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]", |