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, and adds a new ofctl/packet-out ovs-appctl command that can be used when ovs-ofctl is used as a flow monitor. 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 <ja...@ovn.org> --- NEWS | 3 +- include/openvswitch/ofp-parse.h | 5 ++ include/openvswitch/ofp-util.h | 1 + lib/ofp-parse.c | 108 ++++++++++++++++++++++++++++++++ lib/ofp-util.c | 7 ++- ofproto/bundles.h | 7 ++- ofproto/ofproto.c | 82 +++++++++++++++++------- tests/ofproto.at | 134 ++++++++++++++++++++++++++++++++++++++-- utilities/ovs-ofctl.8.in | 46 ++++++++++++-- utilities/ovs-ofctl.c | 55 +++++++++++++++++ 10 files changed, 412 insertions(+), 36 deletions(-) diff --git a/NEWS b/NEWS index 4c4785d..e9b7bd2 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,7 @@ Post-v2.6.0 --------------------- - + - OpenFlow: + * OFPT_PACKET_OUT messages are now supported in bundles. v2.6.0 - xx xxx xxxx --------------------- diff --git a/include/openvswitch/ofp-parse.h b/include/openvswitch/ofp-parse.h index df60b18..e68e57b 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 450e739..9283e13 100644 --- a/include/openvswitch/ofp-util.h +++ b/include/openvswitch/ofp-util.h @@ -1343,6 +1343,7 @@ struct ofputil_bundle_msg { union { struct ofputil_flow_mod fm; struct ofputil_group_mod gm; + struct ofputil_packet_out po; }; }; diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index d3ef140..7fccb48 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,106 @@ 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); + } + + if (!strcmp(name, "in_port")) { + if (!ofputil_port_from_string(value, &po->in_port)) { + error = xasprintf("%s is not a valid OpenFlow port", value); + } + 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); + } + } else if (!strcmp(name, "packet")) { + const char *error_msg = eth_from_hex(value, &packet); + if (error_msg) { + error = xasprintf("%s: %s", name, error_msg); + } + } else { + error = xasprintf("unknown keyword %s", name); + } + + if (error) { + 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, @@ -1797,6 +1898,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); diff --git a/lib/ofp-util.c b/lib/ofp-util.c index a9087a5..83bced6 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -9489,6 +9489,11 @@ ofputil_encode_bundle_msgs(struct ofputil_bundle_msg *bms, size_t n_bms, 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); + free(bms[i].po.ofpacts); + free(CONST_CAST(void *, bms[i].po.packet)); + break; default: break; } @@ -9884,13 +9889,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 802de77..b1c23e6 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. */ @@ -93,6 +94,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.c b/ofproto/ofproto.c index e6f9b19..51941a6 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -3479,6 +3479,25 @@ ofproto_packet_out_start(struct ofproto *ofproto, } static void +ofproto_packet_out_revert(struct ofproto_packet_out *opo) + OVS_REQUIRES(ofproto_mutex) +{ + /* Revert the learned flows. */ + struct xc_entry *entry; + struct ofpbuf entries = opo->xcache->entries; + + XC_ENTRY_FOR_EACH (entry, &entries) { + if (entry->type == XC_LEARN) { + struct ofproto_flow_mod *ofm = entry->u.learn.ofm; + struct rule *rule = rule_collection_rules(&ofm->new_rules)[0]; + ovs_assert(rule); + + ofproto_flow_mod_revert(rule->ofproto, ofm); + } + } +} + +static void ofproto_packet_out_finish(struct ofproto *ofproto, struct ofproto_packet_out *opo) 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(&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); diff --git a/tests/ofproto.at b/tests/ofproto.at index a849c43..dd57751 100644 --- a/tests/ofproto.at +++ b/tests/ofproto.at @@ -1964,6 +1964,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. @@ -3843,15 +3968,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) diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index b56e5b3..76b3aa3 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -473,10 +473,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 +2757,35 @@ 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. +.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 +2927,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 +3206,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 \fBin_port=\fIport\fR \fBpacket=\fIhex\-string\fR \fBactions=\fIactions\fR" +Sends an OpenFlow PACKET_OUT message specified as a sequence of hex +digits that express an Ethernet frame, 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 6fd3818..e6e5c7e 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -1662,6 +1662,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. */ @@ -1762,6 +1814,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>] [buffer_id=<buffer>] [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, @@ -2760,6 +2814,7 @@ ofctl_bundle(struct ovs_cmdl_context *ctx) protocol = open_vconn_for_flow_mod(ctx->argv[1], &vconn, usable_protocols); ovs_list_init(&requests); + /* Takes ownership of 'bms'. */ ofputil_encode_bundle_msgs(bms, n_bms, &requests, protocol); bundle_transact(vconn, &requests, OFPBF_ORDERED | OFPBF_ATOMIC); ofpbuf_list_delete(&requests); -- 2.1.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev