> On May 15, 2015, at 10:03 AM, Romain Lenglet <romain.leng...@oracle.com> > wrote: > > On May 15, 2015 at 7:45:31 AM, Jarno Rajahalme (jrajaha...@nicira.com > <mailto:jrajaha...@nicira.com>(mailto:jrajaha...@nicira.com > <mailto:jrajaha...@nicira.com>)) wrote: > >> >>> On May 14, 2015, at 23:44, Romain Lenglet wrote: >>> >>> Thanks Jarno! >>> >>>> On May 14, 2015 at 2:11:50 PM, Jarno Rajahalme >>>> (jrajaha...@nicira.com(mailto:jrajaha...@nicira.com)) wrote: >>>> The new ovs-ofctl 'bundle' command accepts files similar to >>>> 'add-flows', but each line can optionally start with 'add', 'modify', >>>> 'delete', 'modify_strict', or 'delete_strict' keyword, so that >>>> arbitrary flow table modifications may be specified in a single file. >>>> The modifications in the file are executed as a single transaction >>>> using a OpenFlow 1.4 bundle. >>> >>> I would suggest a different design for the ovs-ofctl support: add a >>> --bundle option to the ovs-ofctl’s add-flows, del-flows and replace-flows, >>> so that all the flow mods generated by each execution would be sent in a >>> bundle. This option would be accepted only when OF1.4 or later is used. >>> >> >> Unfortunately this does not work, as bundles are controller (connection) >> specific, and each ovs-ofctl invocation is seen as a separate controller. > > Yes, that’s what I had in mind: one bundle per invocation of add-flows, > del-flows, diff-flows, or replace-flows.
Ah, OK. Jarno > >> >> This said, it could be possible to come up with an extension that would >> circumvent this restriction between ovs-ofctl and OVS. >> >>> This design would allow using the current flows file format unmodified, and >>> seems much more useful to me. I understand that this design wouldn’t >>> support port mods, but adding support for bundles to replace-flows would be >>> extremely useful. >>> >> >> This should be doable, as replace-flows runs as a single ovs-ofctl >> invocation. I'll look into this. > > Thanks, I’m looking forward to this! > -- > Romain Lenglet > >> >> Jarno >> >>>> OpenFlow 1.4 requires bundles to support at least flow and port mods. >>>> This implementation does not yet support port mods in bundles. >>>> >>>> Another restriction is that the 'atomic' keyword is not yet supported. >>>> >>>> Signed-off-by: Jarno Rajahalme >>>> --- >>>> NEWS | 10 +- >>>> include/openvswitch/vconn.h | 6 +- >>>> lib/ofp-parse.c | 95 +++++++++++++++++- >>>> lib/ofp-parse.h | 12 ++- >>>> lib/ofp-util.c | 30 ++++++ >>>> lib/ofp-util.h | 2 + >>>> lib/vconn.c | 27 ++++-- >>>> tests/ofproto.at | 110 +++++++++++++++++++++ >>>> utilities/ovs-ofctl.8.in | 32 +++++++ >>>> utilities/ovs-ofctl.c | 222 +++++++++++++++++++++++++++++++++++++++++-- >>>> 10 files changed, 523 insertions(+), 23 deletions(-) >>>> >>>> diff --git a/NEWS b/NEWS >>>> index a480607..a7f9369 100644 >>>> --- a/NEWS >>>> +++ b/NEWS >>>> @@ -1,6 +1,6 @@ >>>> Post-v2.3.0 >>>> --------------------- >>>> - - Added support for SFQ, FQ_CoDel and CoDel qdiscs. >>>> + - Added support for SFQ, FQ_CoDel and CoDel qdiscs. >>>> - Add bash command-line completion support for ovs-vsctl Please check >>>> utilities/ovs-command-compgen.INSTALL.md for how to use. >>>> - The MAC learning feature now includes per-port fairness to mitigate >>>> @@ -27,6 +27,11 @@ Post-v2.3.0 >>>> commands are now redundant and will be removed in a future >>>> release. See ovs-vswitchd(8) for details. >>>> - OpenFlow: >>>> + * OpenFlow 1.4 bundles are now supported, but for flow mod >>>> + messages only. 'atomic' bundles are not yet supported, and >>>> + 'ordered' bundles are trivially supported, as all bundled >>>> + messages are executed in the order they were added to the >>>> + bundle regardless of the presence of the 'ordered' flag. >>>> * IPv6 flow label and neighbor discovery fields are now modifiable. >>>> * OpenFlow 1.5 extended registers are now supported. >>>> * The OpenFlow 1.5 actset_output field is now supported. >>>> @@ -41,6 +46,9 @@ Post-v2.3.0 >>>> * A new Netronome extension to OpenFlow 1.5+ allows control over the >>>> fields hashed for OpenFlow select groups. See "selection_method" and >>>> related options in ovs-ofctl(8) for details. >>>> + - ovs-ofctl has a new 'bundle' command that accepts a file of >>>> + arbitrary flow mods as an input that are executed as a single >>>> + transaction using the new OpenFlow 1.4 bundles support. >>>> - ovs-pki: Changed message digest algorithm from MD5 to SHA-1 because >>>> MD5 is no longer secure and some operating systems have started to disable >>>> it in OpenSSL. >>>> diff --git a/include/openvswitch/vconn.h b/include/openvswitch/vconn.h >>>> index 3b157e1..a8d7297 100644 >>>> --- a/include/openvswitch/vconn.h >>>> +++ b/include/openvswitch/vconn.h >>>> @@ -50,8 +50,10 @@ void vconn_set_recv_any_version(struct vconn *); >>>> int vconn_connect(struct vconn *); >>>> int vconn_recv(struct vconn *, struct ofpbuf **); >>>> int vconn_send(struct vconn *, struct ofpbuf *); >>>> -int vconn_recv_xid(struct vconn *, ovs_be32 xid, struct ofpbuf **); >>>> -int vconn_transact(struct vconn *, struct ofpbuf *, struct ofpbuf **); >>>> +int vconn_recv_xid(struct vconn *, ovs_be32 xid, struct ofpbuf **, >>>> + void (*error_reporter)(const struct ofp_header *)); >>>> +int vconn_transact(struct vconn *, struct ofpbuf *, struct ofpbuf **, >>>> + void (*error_reporter)(const struct ofp_header *)); >>>> int vconn_transact_noreply(struct vconn *, struct ofpbuf *, struct ofpbuf >>>> **); >>>> int vconn_transact_multiple_noreply(struct vconn *, struct ovs_list >>>> *requests, >>>> struct ofpbuf **replyp); >>>> diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c >>>> index 126980c..3e746ea 100644 >>>> --- a/lib/ofp-parse.c >>>> +++ b/lib/ofp-parse.c >>>> @@ -257,6 +257,29 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int >>>> command, char *string, >>>> >>>> *usable_protocols = OFPUTIL_P_ANY; >>>> >>>> + if (command == -2) { >>>> + size_t len; >>>> + >>>> + string += strspn(string, " \t\r\n"); /* Skip white space. */ >>>> + len = strcspn(string, ", \t\r\n"); /* Get length of the first token. */ >>>> + >>>> + if (!strncmp(string, "add", len)) { >>>> + command = OFPFC_ADD; >>>> + } else if (!strncmp(string, "delete", len)) { >>>> + command = OFPFC_DELETE; >>>> + } else if (!strncmp(string, "delete_strict", len)) { >>>> + command = OFPFC_DELETE_STRICT; >>>> + } else if (!strncmp(string, "modify", len)) { >>>> + command = OFPFC_MODIFY; >>>> + } else if (!strncmp(string, "modify_strict", len)) { >>>> + command = OFPFC_MODIFY_STRICT; >>>> + } else { >>>> + len = 0; >>>> + command = OFPFC_ADD; >>>> + } >>>> + string += len; >>>> + } >>>> + >>>> switch (command) { >>>> case -1: >>>> fields = 0; >>>> @@ -485,6 +508,10 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int >>>> command, char *string, >>>> * constant for 'command'. To parse syntax for an OFPST_FLOW or >>>> * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. >>>> * >>>> + * if 'command' is given as -2, 'str_' may begin with a command name >>>> ("add", >>>> + * "modify", "delete", "modify_strict", or "delete_strict"). A missing >>>> command >>>> + * name is treated as "add". >>>> + * >>>> * 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 >>>> @@ -817,14 +844,19 @@ parse_flow_monitor_request(struct >>>> ofputil_flow_monitor_request *fmr, >>>> /* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command >>>> 'command' >>>> * (one of OFPFC_*) into 'fm'. >>>> * >>>> + * if 'command' is given as -2, 'string' may begin with a command name >>>> ("add", >>>> + * "modify", "delete", "modify_strict", or "delete_strict"). A missing >>>> command >>>> + * name is treated as "add". >>>> + * >>>> * 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_flow_mod_str(struct ofputil_flow_mod *fm, const char *string, >>>> - uint16_t command, >>>> + int command, >>>> enum ofputil_protocol *usable_protocols) >>>> { >>>> char *error = parse_ofp_str(fm, command, string, usable_protocols); >>>> + >>>> if (!error) { >>>> /* Normalize a copy of the match. This ensures that non-normalized >>>> * flows get logged but doesn't affect what gets sent to the switch, so >>>> @@ -882,10 +914,14 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, >>>> const char *table_id, >>>> * type (one of OFPFC_*). Stores each flow_mod in '*fm', an array allocated >>>> * on the caller's behalf, and the number of flow_mods in '*n_fms'. >>>> * >>>> + * if 'command' is given as -2, each line may start with a command name >>>> + * ("add", "modify", "delete", "modify_strict", or "delete_strict"). A >>>> missing >>>> + * command name is treated as "add". >>>> + * >>>> * 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_flow_mod_file(const char *file_name, uint16_t command, >>>> +parse_ofp_flow_mod_file(const char *file_name, int command, >>>> struct ofputil_flow_mod **fms, size_t *n_fms, >>>> enum ofputil_protocol *usable_protocols) >>>> { >>>> @@ -1547,3 +1583,58 @@ parse_ofp_group_mod_file(const char *file_name, >>>> uint16_t command, >>>> } >>>> return NULL; >>>> } >>>> + >>>> +static char * OVS_WARN_UNUSED_RESULT >>>> +parse_ofp_bundle_control_str__(struct ofputil_bundle_ctrl_msg *bc, >>>> + char *string, >>>> + enum ofputil_protocol *usable_protocols) >>>> +{ >>>> + char *save_ptr = NULL; >>>> + char *name; >>>> + >>>> + /* Bundles require at least OF 1.4. */ >>>> + *usable_protocols = OFPUTIL_P_OF14_UP; >>>> + >>>> + memset(bc, 0, sizeof *bc); >>>> + >>>> + for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; >>>> + name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { >>>> + >>>> + if (!strcmp(name, "atomic")) { >>>> + bc->flags |= OFPBF_ATOMIC; >>>> + } else if (!strcmp(name, "ordered")) { >>>> + bc->flags |= OFPBF_ORDERED; >>>> + } else { >>>> + char *value; >>>> + >>>> + value = strtok_r(NULL, ", \t\r\n", &save_ptr); >>>> + if (!value) { >>>> + return xasprintf("field %s missing value", name); >>>> + } >>>> + >>>> + if (!strcmp(name, "bundle")) { >>>> + char *error = str_to_u32(value, &bc->bundle_id); >>>> + >>>> + if (error) { >>>> + return error; >>>> + } >>>> + } else { >>>> + return xasprintf("unknown keyword %s", name); >>>> + } >>>> + } >>>> + } >>>> + >>>> + return NULL; >>>> +} >>>> + >>>> +char * OVS_WARN_UNUSED_RESULT >>>> +parse_ofp_bundle_control_str(struct ofputil_bundle_ctrl_msg *bc, >>>> + const char *str_, >>>> + enum ofputil_protocol *usable_protocols) >>>> +{ >>>> + char *string = xstrdup(str_); >>>> + char *error = parse_ofp_bundle_control_str__(bc, string, >>>> usable_protocols); >>>> + >>>> + free(string); >>>> + return error; >>>> +} >>>> diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h >>>> index db30f43..f39c323 100644 >>>> --- a/lib/ofp-parse.h >>>> +++ b/lib/ofp-parse.h >>>> @@ -1,5 +1,5 @@ >>>> /* >>>> - * Copyright (c) 2010, 2011, 2012, 2013, 2014 Nicira, Inc. >>>> + * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. >>>> * >>>> * Licensed under the Apache License, Version 2.0 (the "License"); >>>> * you may not use this file except in compliance with the License. >>>> @@ -32,6 +32,7 @@ struct ofputil_flow_mod; >>>> struct ofputil_flow_monitor_request; >>>> struct ofputil_flow_stats_request; >>>> struct ofputil_group_mod; >>>> +struct ofputil_bundle_ctrl_msg; >>>> struct ofputil_meter_mod; >>>> struct ofputil_table_mod; >>>> struct simap; >>>> @@ -42,7 +43,7 @@ char *parse_ofp_str(struct ofputil_flow_mod *, int >>>> command, const char *str_, >>>> OVS_WARN_UNUSED_RESULT; >>>> >>>> char *parse_ofp_flow_mod_str(struct ofputil_flow_mod *, const char *string, >>>> - uint16_t command, >>>> + int command, >>>> enum ofputil_protocol *usable_protocols) >>>> OVS_WARN_UNUSED_RESULT; >>>> >>>> @@ -51,7 +52,7 @@ char *parse_ofp_table_mod(struct ofputil_table_mod *, >>>> enum ofputil_protocol *usable_protocols) >>>> OVS_WARN_UNUSED_RESULT; >>>> >>>> -char *parse_ofp_flow_mod_file(const char *file_name, uint16_t command, >>>> +char *parse_ofp_flow_mod_file(const char *file_name, int command, >>>> struct ofputil_flow_mod **fms, size_t *n_fms, >>>> enum ofputil_protocol *usable_protocols) >>>> OVS_WARN_UNUSED_RESULT; >>>> @@ -84,6 +85,11 @@ char *parse_ofp_group_mod_str(struct ofputil_group_mod >>>> *, uint16_t command, >>>> enum ofputil_protocol *usable_protocols) >>>> OVS_WARN_UNUSED_RESULT; >>>> >>>> +char *parse_ofp_bundle_control_str(struct ofputil_bundle_ctrl_msg *, >>>> + const char *string, >>>> + enum ofputil_protocol *usable_protocols) >>>> + OVS_WARN_UNUSED_RESULT; >>>> + >>>> char *str_to_u8(const char *str, const char *name, uint8_t *valuep) >>>> OVS_WARN_UNUSED_RESULT; >>>> char *str_to_u16(const char *str, const char *name, uint16_t *valuep) >>>> diff --git a/lib/ofp-util.c b/lib/ofp-util.c >>>> index 17a0c41..d3251ed 100644 >>>> --- a/lib/ofp-util.c >>>> +++ b/lib/ofp-util.c >>>> @@ -8748,6 +8748,36 @@ ofputil_decode_bundle_ctrl(const struct ofp_header >>>> *oh, >>>> } >>>> >>>> struct ofpbuf * >>>> +ofputil_encode_bundle_ctrl_request(enum ofp_version ofp_version, >>>> + struct ofputil_bundle_ctrl_msg *bc) >>>> +{ >>>> + struct ofpbuf *request; >>>> + struct ofp14_bundle_ctrl_msg *m; >>>> + >>>> + switch (ofp_version) { >>>> + case OFP10_VERSION: >>>> + case OFP11_VERSION: >>>> + case OFP12_VERSION: >>>> + case OFP13_VERSION: >>>> + ovs_fatal(0, "bundles need OpenFlow 1.4 or later " >>>> + "(\'-O OpenFlow14\')"); >>>> + case OFP14_VERSION: >>>> + case OFP15_VERSION: >>>> + request = ofpraw_alloc(OFPRAW_OFPT14_BUNDLE_CONTROL, ofp_version, 0); >>>> + m = ofpbuf_put_zeros(request, sizeof *m); >>>> + >>>> + m->bundle_id = htonl(bc->bundle_id); >>>> + m->type = htons(bc->type); >>>> + m->flags = htons(bc->flags); >>>> + break; >>>> + default: >>>> + OVS_NOT_REACHED(); >>>> + } >>>> + >>>> + return request; >>>> +} >>>> + >>>> +struct ofpbuf * >>>> ofputil_encode_bundle_ctrl_reply(const struct ofp_header *oh, >>>> struct ofputil_bundle_ctrl_msg *msg) >>>> { >>>> diff --git a/lib/ofp-util.h b/lib/ofp-util.h >>>> index efb5b18..644a679 100644 >>>> --- a/lib/ofp-util.h >>>> +++ b/lib/ofp-util.h >>>> @@ -1114,6 +1114,8 @@ enum ofptype; >>>> enum ofperr ofputil_decode_bundle_ctrl(const struct ofp_header *, >>>> struct ofputil_bundle_ctrl_msg *); >>>> >>>> +struct ofpbuf *ofputil_encode_bundle_ctrl_request(enum ofp_version, >>>> + struct ofputil_bundle_ctrl_msg *); >>>> struct ofpbuf *ofputil_encode_bundle_ctrl_reply(const struct ofp_header *, >>>> struct ofputil_bundle_ctrl_msg *); >>>> >>>> diff --git a/lib/vconn.c b/lib/vconn.c >>>> index c033b48..e0329ec 100644 >>>> --- a/lib/vconn.c >>>> +++ b/lib/vconn.c >>>> @@ -1,5 +1,5 @@ >>>> /* >>>> - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. >>>> + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, >>>> Inc. >>>> * >>>> * Licensed under the Apache License, Version 2.0 (the "License"); >>>> * you may not use this file except in compliance with the License. >>>> @@ -751,11 +751,14 @@ vconn_recv_block(struct vconn *vconn, struct ofpbuf >>>> **msgp) >>>> * >>>> * 'request' is always destroyed, regardless of the return value. */ >>>> int >>>> -vconn_recv_xid(struct vconn *vconn, ovs_be32 xid, struct ofpbuf **replyp) >>>> +vconn_recv_xid(struct vconn *vconn, ovs_be32 xid, struct ofpbuf **replyp, >>>> + void (*error_reporter)(const struct ofp_header *)) >>>> { >>>> for (;;) { >>>> ovs_be32 recv_xid; >>>> struct ofpbuf *reply; >>>> + const struct ofp_header *oh; >>>> + enum ofptype type; >>>> int error; >>>> >>>> error = vconn_recv_block(vconn, &reply); >>>> @@ -763,15 +766,21 @@ vconn_recv_xid(struct vconn *vconn, ovs_be32 xid, >>>> struct ofpbuf **replyp) >>>> *replyp = NULL; >>>> return error; >>>> } >>>> - recv_xid = ((struct ofp_header *) reply->data)->xid; >>>> + oh = reply->data; >>>> + recv_xid = oh->xid; >>>> if (xid == recv_xid) { >>>> *replyp = reply; >>>> return 0; >>>> } >>>> >>>> - VLOG_DBG_RL(&bad_ofmsg_rl, "%s: received reply with xid %08"PRIx32 >>>> - " != expected %08"PRIx32, >>>> - vconn->name, ntohl(recv_xid), ntohl(xid)); >>>> + error = ofptype_decode(&type, oh); >>>> + if (!error && type == OFPTYPE_ERROR && error_reporter) { >>>> + error_reporter(oh); >>>> + } else { >>>> + VLOG_DBG_RL(&bad_ofmsg_rl, "%s: received reply with xid %08"PRIx32 >>>> + " != expected %08"PRIx32, >>>> + vconn->name, ntohl(recv_xid), ntohl(xid)); >>>> + } >>>> ofpbuf_delete(reply); >>>> } >>>> } >>>> @@ -788,7 +797,8 @@ vconn_recv_xid(struct vconn *vconn, ovs_be32 xid, >>>> struct ofpbuf **replyp) >>>> * 'request' is always destroyed, regardless of the return value. */ >>>> int >>>> vconn_transact(struct vconn *vconn, struct ofpbuf *request, >>>> - struct ofpbuf **replyp) >>>> + struct ofpbuf **replyp, >>>> + void (*error_reporter)(const struct ofp_header *)) >>>> { >>>> ovs_be32 send_xid = ((struct ofp_header *) request->data)->xid; >>>> int error; >>>> @@ -798,7 +808,8 @@ vconn_transact(struct vconn *vconn, struct ofpbuf >>>> *request, >>>> if (error) { >>>> ofpbuf_delete(request); >>>> } >>>> - return error ? error : vconn_recv_xid(vconn, send_xid, replyp); >>>> + return error ? error : vconn_recv_xid(vconn, send_xid, replyp, >>>> + error_reporter); >>>> } >>>> >>>> /* Sends 'request' followed by a barrier request to 'vconn', then blocks >>>> until >>>> diff --git a/tests/ofproto.at b/tests/ofproto.at >>>> index 9729a7c..20bf5d6 100644 >>>> --- a/tests/ofproto.at >>>> +++ b/tests/ofproto.at >>>> @@ -3450,3 +3450,113 @@ OFPT_BARRIER_REPLY (OF1.4): >>>> >>>> OVS_VSWITCHD_STOP >>>> AT_CLEANUP >>>> + >>>> + >>>> +AT_SETUP([ofproto - bundle with multiple flow mods (OpenFlow 1.4)]) >>>> +AT_KEYWORDS([monitor]) >>>> +OVS_VSWITCHD_START >>>> + >>>> +AT_CHECK([ovs-ofctl -O OpenFlow14 del-flows br0]) >>>> + >>>> +AT_DATA([flows.txt], [dnl >>>> +add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=1 >>>> +add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=2 >>>> +add idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=3 >>>> +add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=4 >>>> +delete >>>> +add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=5 >>>> +add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=6 >>>> +add idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=7 >>>> +delete in_port=2 dl_src=00:88:99:aa:bb:cc >>>> +]) >>>> + >>>> +AT_CHECK([ovs-ofctl -O OpenFlow14 bundle br0 flows.txt]) >>>> + >>>> +AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sort], >>>> [0], [dnl >>>> + idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=output:5 >>>> + idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:6 >>>> +OFPST_FLOW reply (OF1.4): >>>> +]) >>>> + >>>> +AT_DATA([flows.txt], [dnl >>>> +modify actions=drop >>>> +modify_strict in_port=2 dl_src=00:77:88:99:aa:bb actions=7 >>>> +]) >>>> + >>>> +AT_CHECK([ovs-ofctl -O OpenFlow14 bundle br0 flows.txt]) >>>> + >>>> +AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sort], >>>> [0], [dnl >>>> + idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=drop >>>> + idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:7 >>>> +OFPST_FLOW reply (OF1.4): >>>> +]) >>>> + >>>> +# Adding an existing flow acts as a modify, and delete_strict also works. >>>> +AT_DATA([flows.txt], [dnl >>>> +add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=8 >>>> +delete_strict in_port=2 dl_src=00:66:77:88:99:aa >>>> +add in_port=2 dl_src=00:66:77:88:99:aa actions=drop >>>> +]) >>>> + >>>> +AT_CHECK([ovs-ofctl -O OpenFlow14 bundle br0 flows.txt]) >>>> + >>>> +AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sort], >>>> [0], [dnl >>>> + idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:8 >>>> + in_port=2,dl_src=00:66:77:88:99:aa actions=drop >>>> +OFPST_FLOW reply (OF1.4): >>>> +]) >>>> + >>>> +OVS_VSWITCHD_STOP >>>> +AT_CLEANUP >>>> + >>>> + >>>> +AT_SETUP([ofproto - failing bundle commit (OpenFlow 1.4)]) >>>> +AT_KEYWORDS([monitor]) >>>> +OVS_VSWITCHD_START >>>> + >>>> +AT_CHECK([ovs-ofctl -O OpenFlow14 del-flows br0]) >>>> + >>>> +ovs-ofctl add-flows br0 - <> +idle_timeout=50 in_port=2 >>>> dl_src=00:66:77:88:99:aa actions=11 >>>> +idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=22 >>>> +idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=33 >>>> +EOF >>>> +AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sort], >>>> [0], [dnl >>>> + idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=output:11 >>>> + idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:22 >>>> + idle_timeout=70, in_port=2,dl_src=00:88:99:aa:bb:cc actions=output:33 >>>> +OFPST_FLOW reply (OF1.4): >>>> +]) >>>> + >>>> +# last line uses illegal table number (OVS internal table) >>>> +AT_DATA([flows.txt], [dnl >>>> +add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=1 >>>> +add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=2 >>>> +add idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=3 >>>> +modify idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=4 >>>> +delete >>>> +add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=5 >>>> +add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=6 >>>> +add idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=7 >>>> +delete in_port=2 dl_src=00:88:99:aa:bb:cc >>>> +add table=254 actions=drop >>>> +]) >>>> + >>>> +AT_CHECK([ovs-ofctl -O OpenFlow14 bundle br0 flows.txt], [1], [], [dnl >>>> +OFPT_ERROR (OF1.4) (xid=0x15): OFPBRC_EPERM >>>> +OFPT_FLOW_MOD (OF1.4) (xid=0x15): ADD table:254 actions=drop >>>> +OFPT_ERROR (OF1.4) (xid=0x17): OFPBFC_MSG_FAILED >>>> +OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x17): >>>> + bundle_id=0 type=COMMIT_REQUEST flags=0 >>>> +ovs-ofctl: Bundle commit failed (OFPBFC_MSG_FAILED). >>>> +]) >>>> + >>>> +AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sort], >>>> [0], [dnl >>>> + idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=output:11 >>>> + idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:22 >>>> + idle_timeout=70, in_port=2,dl_src=00:88:99:aa:bb:cc actions=output:33 >>>> +OFPST_FLOW reply (OF1.4): >>>> +]) >>>> + >>>> +OVS_VSWITCHD_STOP >>>> +AT_CLEANUP >>>> diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in >>>> index c667aa4..95341ac 100644 >>>> --- a/utilities/ovs-ofctl.8.in >>>> +++ b/utilities/ovs-ofctl.8.in >>>> @@ -318,6 +318,38 @@ Deletes entries from \fIswitch\fR's flow table. With >>>> only a >>>> entries that match the specified flows. With \fB\-\-strict\fR, >>>> wildcards are not treated as active for matching purposes. >>>> . >>>> +.IQ "\fBbundle \fIswitch [\fBatomic\fR] [\fBordered\fR] \fIfile\fR" >>>> +.IQ "\fBbundle \fIswitch [\fBatomic\fR] [\fBordered\fR] \fB\- < >>>> \fIfile\fR" >>>> +Adds, modifies, and/or deletes entries from \fIswitch\fR's flow table. >>>> +\fIfile\fR is a text file where each line contains a flow in the same >>>> +syntax as with \fBadd\-flows\fR, \fBmod\-flows\fR, and \fBdel\-flows\fR, >>>> +except that the line may start with \fBadd\fR, \fBmodify\fR, >>>> +\fBdelete\fR, \fBmodify_strict\fR, or \fBdelete_strict\fR keyword to >>>> specify >>>> +whether a flow is to be added, modified, or deleted, and whether the >>>> +modify or delete is strict or not. A line without one of these >>>> +keywords is treated as a flow add. >>>> +. >>>> +.IP >>>> +All flow mods are processed as a single transaction, meaning that if >>>> +one of them fails, the whole transaction fails and none of the changes >>>> +are made to the \fIswitch\fR's flow table. The \fIswitch\fR may >>>> +perform resource management, such as evicting flows, that may not be >>>> +unrolled even when the transaction fails. >>>> +. >>>> +.IP >>>> +If an optional \fBatomic\fR keyword is given, the switch is requested >>>> +to make the modifications so that no datapath packet sees an >>>> +intermediate configuration of the flow tables. The optional >>>> +\fBordered\fR keyword requests the modifications to be performed in >>>> +the order given in the file. Without this keyword the switch may >>>> +apply the modifications in any order. >>>> +. >>>> +.IP >>>> +\fBbundle\fR command requires OpenFlow 1.4 or higher. You may need to >>>> +supply an explicit \fB-O OpenFlow14\fR option, and you may need to enable >>>> +OVS OpenFlow 1.4 support by setting the OVSDB \fIprotocols\fR column >>>> +in the \fIbridge\fR table. >>>> +. >>>> .IP "[\fB\-\-readd\fR] \fBreplace\-flows \fIswitch file\fR" >>>> Reads flow entries from \fIfile\fR (or \fBstdin\fR if \fIfile\fR is >>>> \fB\-\fR) and queries the flow table from \fIswitch\fR. Then it fixes >>>> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c >>>> index 54a5bb8..20abed7 100644 >>>> --- a/utilities/ovs-ofctl.c >>>> +++ b/utilities/ovs-ofctl.c >>>> @@ -506,7 +506,7 @@ dump_transaction(struct vconn *vconn, struct ofpbuf >>>> *request) >>>> struct ofpbuf *reply; >>>> >>>> ofpmsg_update_length(request); >>>> - run(vconn_transact(vconn, request, &reply), "talking to %s", >>>> + run(vconn_transact(vconn, request, &reply, NULL), "talking to %s", >>>> vconn_get_name(vconn)); >>>> ofp_print(stdout, reply->data, reply->size, verbosity + 1); >>>> ofpbuf_delete(reply); >>>> @@ -627,7 +627,7 @@ fetch_switch_config(struct vconn *vconn, struct >>>> ofp_switch_config *config_) >>>> >>>> request = ofpraw_alloc(OFPRAW_OFPT_GET_CONFIG_REQUEST, >>>> vconn_get_version(vconn), 0); >>>> - run(vconn_transact(vconn, request, &reply), >>>> + run(vconn_transact(vconn, request, &reply, NULL), >>>> "talking to %s", vconn_get_name(vconn)); >>>> >>>> if (ofptype_pull(&type, reply) || type != OFPTYPE_GET_CONFIG_REPLY) { >>>> @@ -664,7 +664,8 @@ ofctl_show(struct ovs_cmdl_context *ctx) >>>> open_vconn(vconn_name, &vconn); >>>> version = vconn_get_version(vconn); >>>> request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, version, 0); >>>> - run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name); >>>> + run(vconn_transact(vconn, request, &reply, NULL), "talking to %s", >>>> + vconn_name); >>>> >>>> has_ports = ofputil_switch_features_has_ports(reply); >>>> ofp_print(stdout, reply->data, reply->size, verbosity + 1); >>>> @@ -731,7 +732,7 @@ fetch_port_by_features(struct vconn *vconn, >>>> /* Fetch the switch's ofp_switch_features. */ >>>> request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, >>>> vconn_get_version(vconn), 0); >>>> - run(vconn_transact(vconn, request, &reply), >>>> + run(vconn_transact(vconn, request, &reply, NULL), >>>> "talking to %s", vconn_get_name(vconn)); >>>> >>>> oh = reply->data; >>>> @@ -1667,7 +1668,8 @@ ofctl_probe(struct ovs_cmdl_context *ctx) >>>> >>>> open_vconn(ctx->argv[1], &vconn); >>>> request = make_echo_request(vconn_get_version(vconn)); >>>> - run(vconn_transact(vconn, request, &reply), "talking to %s", >>>> ctx->argv[1]); >>>> + run(vconn_transact(vconn, request, &reply, NULL), "talking to %s", >>>> + ctx->argv[1]); >>>> if (reply->size != sizeof(struct ofp_header)) { >>>> ovs_fatal(0, "reply does not match request"); >>>> } >>>> @@ -2022,7 +2024,8 @@ ofctl_ping(struct ovs_cmdl_context *ctx) >>>> random_bytes(ofpbuf_put_uninit(request, payload), payload); >>>> >>>> xgettimeofday(&start); >>>> - run(vconn_transact(vconn, ofpbuf_clone(request), &reply), "transact"); >>>> + run(vconn_transact(vconn, ofpbuf_clone(request), &reply, NULL), >>>> + "transact"); >>>> xgettimeofday(&end); >>>> >>>> rpy_hdr = reply->data; >>>> @@ -2075,7 +2078,7 @@ ofctl_benchmark(struct ovs_cmdl_context *ctx) >>>> request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST, >>>> vconn_get_version(vconn), payload_size); >>>> ofpbuf_put_zeros(request, payload_size); >>>> - run(vconn_transact(vconn, request, &reply), "transact"); >>>> + run(vconn_transact(vconn, request, &reply, NULL), "transact"); >>>> ofpbuf_delete(reply); >>>> } >>>> xgettimeofday(&end); >>>> @@ -2261,6 +2264,210 @@ ofctl_dump_group_features(struct ovs_cmdl_context >>>> *ctx) >>>> vconn_close(vconn); >>>> } >>>> >>>> +static enum ofperr >>>> +bundle_reply_validate(struct ofpbuf *reply, enum ofp_version version, >>>> + struct ofputil_bundle_ctrl_msg *request) >>>> +{ >>>> + const struct ofp_header *oh; >>>> + enum ofptype type; >>>> + enum ofperr error; >>>> + struct ofputil_bundle_ctrl_msg rbc; >>>> + >>>> + oh = reply->data; >>>> + if (oh->version != version) { >>>> + return OFPERR_OFPBRC_BAD_VERSION; >>>> + } >>>> + >>>> + error = ofptype_decode(&type, oh); >>>> + if (error) { >>>> + return error; >>>> + } >>>> + >>>> + if (type == OFPTYPE_ERROR) { >>>> + ofp_print(stderr, reply->data, reply->size, verbosity + 1); >>>> + fflush(stderr); >>>> + error = ofperr_decode_msg(oh, NULL); >>>> + return error; >>>> + } >>>> + if (type != OFPTYPE_BUNDLE_CONTROL) { >>>> + ofp_print(stderr, reply->data, reply->size, verbosity + 1); >>>> + fflush(stderr); >>>> + return OFPERR_OFPBRC_BAD_TYPE; >>>> + } >>>> + >>>> + error = ofputil_decode_bundle_ctrl(oh, &rbc); >>>> + if (error) { >>>> + return error; >>>> + } >>>> + >>>> + if (rbc.bundle_id != request->bundle_id) { >>>> + ofp_print(stderr, reply->data, reply->size, verbosity + 1); >>>> + fflush(stderr); >>>> + return OFPERR_OFPBFC_BAD_ID; >>>> + } >>>> + >>>> + if (rbc.type != request->type + 1) { >>>> + ofp_print(stderr, reply->data, reply->size, verbosity + 1); >>>> + fflush(stderr); >>>> + return OFPERR_OFPBFC_BAD_TYPE; >>>> + } >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static void >>>> +bundle_error_reporter(const struct ofp_header *oh) >>>> +{ >>>> + ofp_print(stderr, oh, ntohs(oh->length), verbosity + 1); >>>> + fflush(stderr); >>>> +} >>>> + >>>> +static enum ofperr >>>> +bundle_control_transact(struct vconn *vconn, enum ofp_version version, >>>> + struct ofputil_bundle_ctrl_msg *bc, >>>> + uint16_t type) >>>> +{ >>>> + struct ofpbuf *request, *reply; >>>> + enum ofperr error; >>>> + >>>> + bc->type = type; >>>> + request = ofputil_encode_bundle_ctrl_request(version, bc); >>>> + ofpmsg_update_length(request); >>>> + run(vconn_transact(vconn, request, &reply, bundle_error_reporter), >>>> + "talking to %s", vconn_get_name(vconn)); >>>> + >>>> + error = bundle_reply_validate(reply, version, bc); >>>> + ofpbuf_delete(reply); >>>> + >>>> + return error; >>>> +} >>>> + >>>> +static enum ofperr >>>> +bundle_add_msg(struct vconn *vconn, enum ofp_version version, >>>> + struct ofputil_bundle_ctrl_msg *bc, struct ofpbuf *msg) >>>> +{ >>>> + struct ofputil_bundle_add_msg bam; >>>> + struct ofpbuf *request, *reply; >>>> + >>>> + ofpmsg_update_length(msg); >>>> + >>>> + bam.bundle_id = bc->bundle_id; >>>> + bam.flags = bc->flags; >>>> + bam.msg = msg->data; >>>> + >>>> + request = ofputil_encode_bundle_add(version, &bam); >>>> + ofpbuf_delete(msg); >>>> + ofpmsg_update_length(request); >>>> + >>>> + run(vconn_transact_noreply(vconn, request, &reply), "talking to %s", >>>> + vconn_get_name(vconn)); >>>> + >>>> + if (reply) { >>>> + const struct ofp_header *oh; >>>> + enum ofptype type; >>>> + enum ofperr error; >>>> + >>>> + oh = reply->data; >>>> + error = ofptype_decode(&type, oh); >>>> + if (error) { >>>> + goto error_out; >>>> + } >>>> + >>>> + if (type == OFPTYPE_ERROR) { >>>> + ofp_print(stderr, reply->data, reply->size, verbosity + 1); >>>> + fflush(stderr); >>>> + error = ofperr_decode_msg(oh, NULL); >>>> + goto error_out; >>>> + } >>>> + error = OFPERR_OFPBFC_UNKNOWN; >>>> +error_out: >>>> + ofpbuf_delete(reply); >>>> + return error; >>>> + } >>>> + return 0; >>>> +} >>>> + >>>> +static void >>>> +ofctl_bundle(struct ovs_cmdl_context *ctx) >>>> +{ >>>> + enum ofputil_protocol usable_protocols, usable_protocols2; >>>> + enum ofputil_protocol protocol; >>>> + enum ofp_version version; >>>> + struct vconn *vconn; >>>> + const char *file; >>>> + struct ofputil_flow_mod *fms = NULL; >>>> + size_t n_fms = 0; >>>> + struct ofputil_bundle_ctrl_msg bc; >>>> + char *error; >>>> + enum ofperr ofperr; >>>> + size_t i; >>>> + >>>> + /* Parse bundle options, if any. */ >>>> + if (ctx->argc > 3) { >>>> + error = parse_ofp_bundle_control_str(&bc, ctx->argv[2], >>>> + &usable_protocols); >>>> + if (error) { >>>> + ovs_fatal(0, "%s", error); >>>> + } >>>> + file = ctx->argv[3]; /* File name after the options. */ >>>> + } else { >>>> + memset(&bc, 0, sizeof bc); >>>> + file = ctx->argv[2]; >>>> + usable_protocols = OFPUTIL_P_OF14_UP; >>>> + } >>>> + >>>> + /* Parse flow mods from the file. */ >>>> + error = parse_ofp_flow_mod_file(file, -2, &fms, &n_fms, >>>> + &usable_protocols2); >>>> + if (error) { >>>> + ovs_fatal(0, "%s", error); >>>> + } >>>> + >>>> + usable_protocols &= usable_protocols2; >>>> + >>>> + protocol = open_vconn_for_flow_mod(ctx->argv[1], &vconn, >>>> usable_protocols); >>>> + version = ofputil_protocol_to_ofp_version(protocol); >>>> + >>>> + ofperr = bundle_control_transact(vconn, version, &bc, >>>> OFPBCT_OPEN_REQUEST); >>>> + if (ofperr) { >>>> + ovs_fatal(0, "Bundle open failed (%s).", ofperr_to_string(ofperr)); >>>> + } >>>> + >>>> + for (i = 0; i < n_fms; i++) { >>>> + struct ofputil_flow_mod *fm = &fms[i]; >>>> + >>>> + if (!ofperr) { >>>> + ofperr = bundle_add_msg(vconn, version, &bc, >>>> + ofputil_encode_flow_mod(fm, protocol)); >>>> + if (ofperr) { >>>> + fprintf(stderr, "Bundle flow mod #%"PRIuSIZE >>>> + " failed (%s), discarding bundle.", >>>> + i, ofperr_to_string(ofperr)); >>>> + fflush(stderr); >>>> + } >>>> + } >>>> + free(CONST_CAST(struct ofpact *, fm->ofpacts)); >>>> + } >>>> + free(fms); >>>> + >>>> + if (!ofperr) { >>>> + ofperr = bundle_control_transact(vconn, version, &bc, >>>> + OFPBCT_COMMIT_REQUEST); >>>> + if (ofperr) { >>>> + ovs_fatal(0, "Bundle commit failed (%s).", >>>> + ofperr_to_string(ofperr)); >>>> + } >>>> + } else { >>>> + ofperr = bundle_control_transact(vconn, version, &bc, >>>> + OFPBCT_DISCARD_REQUEST); >>>> + if (ofperr) { >>>> + ovs_fatal(0, "Bundle discard failed (%s).", >>>> + ofperr_to_string(ofperr)); >>>> + } >>>> + } >>>> + vconn_close(vconn); >>>> +} >>>> + >>>> static void >>>> ofctl_help(struct ovs_cmdl_context *ctx OVS_UNUSED) >>>> { >>>> @@ -3578,6 +3785,7 @@ static const struct ovs_cmdl_command all_commands[] >>>> = { >>>> 1, 2, ofctl_dump_group_stats }, >>>> { "dump-group-features", "switch", >>>> 1, 1, ofctl_dump_group_features }, >>>> + { "bundle", "switch [options] file", 2, 3, ofctl_bundle }, >>>> { "help", NULL, 0, INT_MAX, ofctl_help }, >>>> { "list-commands", NULL, 0, INT_MAX, ofctl_list_commands }, >>>> >>>> -- >>>> 1.7.10.4 >>> >>> -- >>> Romain Lenglet _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev