X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=utilities%2Fovs-ofctl.c;h=6ec715146f82d20500da253e3c78680b7a0a1f90;hb=898dcef1ccbe39efdc3c32a6b368c403c34bd3d1;hp=3d61c4b8a6071f702faef901156d280befd3aefa;hpb=40cb2c3229a586cbc3c9792efe2a81f62dc52d95;p=cascardo%2Fovs.git diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index 3d61c4b8a..6ec715146 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -67,6 +67,12 @@ VLOG_DEFINE_THIS_MODULE(ofctl); +/* --bundle: Use OpenFlow 1.4 bundle for making the flow table change atomic. + * NOTE: Also the flow mod will use OpenFlow 1.4, so the semantics may be + * different (see the comment in parse_options() for details). + */ +static bool bundle = false; + /* --strict: Use strict matching for flow mod commands? Additionally governs * use of nx_pull_match() instead of nx_pull_match_loose() in parse-nx-match. */ @@ -159,6 +165,7 @@ parse_options(int argc, char *argv[]) OPT_SORT, OPT_RSORT, OPT_UNIXCTL, + OPT_BUNDLE, DAEMON_OPTION_ENUMS, OFP_VERSION_OPTION_ENUMS, VLOG_OPTION_ENUMS @@ -176,6 +183,7 @@ parse_options(int argc, char *argv[]) {"unixctl", required_argument, NULL, OPT_UNIXCTL}, {"help", no_argument, NULL, 'h'}, {"option", no_argument, NULL, 'o'}, + {"bundle", no_argument, NULL, OPT_BUNDLE}, DAEMON_LONG_OPTIONS, OFP_VERSION_LONG_OPTIONS, VLOG_LONG_OPTIONS, @@ -249,6 +257,10 @@ parse_options(int argc, char *argv[]) ovs_cmdl_print_options(long_options); exit(EXIT_SUCCESS); + case OPT_BUNDLE: + bundle = true; + break; + case OPT_STRICT: strict = true; break; @@ -293,6 +305,12 @@ parse_options(int argc, char *argv[]) free(short_options); + /* Implicit OpenFlow 1.4 with the '--bundle' option. */ + if (bundle) { + /* Add implicit allowance for OpenFlow 1.4. */ + add_allowed_ofp_versions(ofputil_protocols_to_version_bitmap( + OFPUTIL_P_OF14_OXM)); + } versions = get_allowed_ofp_versions(); version_protocols = ofputil_protocols_from_version_bitmap(versions); if (!(allowed_protocols & version_protocols)) { @@ -320,10 +338,14 @@ usage(void) " dump-desc SWITCH print switch description\n" " dump-tables SWITCH print table stats\n" " dump-table-features SWITCH print table features\n" + " dump-table-desc SWITCH print table description (OF1.4+)\n" " mod-port SWITCH IFACE ACT modify port behavior\n" " mod-table SWITCH MOD modify flow table behavior\n" + " OF1.1/1.2 MOD: controller, continue, drop\n" + " OF1.4+ MOD: evict, noevict\n" " get-frags SWITCH print fragment handling behavior\n" " set-frags SWITCH FRAG_MODE set fragment handling behavior\n" + " FRAG_MODE: normal, drop, reassemble, nx-match\n" " dump-ports SWITCH [PORT] print port statistics\n" " dump-ports-desc SWITCH [PORT] print port descriptions\n" " dump-flows SWITCH print all flow entries\n" @@ -360,6 +382,9 @@ usage(void) " dump-meters SWITCH print all meter configuration\n" " meter-stats SWITCH [METER] print meter statistics\n" " meter-features SWITCH print meter features\n" + " add-geneve-map SWITCH MAP add Geneve option MAPpings\n" + " del-geneve-map SWITCH [MAP] delete Geneve option MAPpings\n" + " dump-geneve-map SWITCH print Geneve option mappings\n" "\nFor OpenFlow switches and controllers:\n" " probe TARGET probe whether TARGET is up\n" " ping TARGET [N] latency of N-byte echos\n" @@ -496,7 +521,6 @@ open_vconn(const char *name, struct vconn **vconnp) static void send_openflow_buffer(struct vconn *vconn, struct ofpbuf *buffer) { - ofpmsg_update_length(buffer); run(vconn_send_block(vconn, buffer), "failed to send packet to switch"); } @@ -505,7 +529,6 @@ dump_transaction(struct vconn *vconn, struct ofpbuf *request) { struct ofpbuf *reply; - ofpmsg_update_length(request); run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_get_name(vconn)); ofp_print(stdout, reply->data, reply->size, verbosity + 1); @@ -587,11 +610,7 @@ dump_trivial_stats_transaction(const char *vconn_name, enum ofpraw raw) static void transact_multiple_noreply(struct vconn *vconn, struct ovs_list *requests) { - struct ofpbuf *request, *reply; - - LIST_FOR_EACH (request, list_node, requests) { - ofpmsg_update_length(request); - } + struct ofpbuf *reply; run(vconn_transact_multiple_noreply(vconn, requests, &reply), "talking to %s", vconn_get_name(vconn)); @@ -602,6 +621,20 @@ transact_multiple_noreply(struct vconn *vconn, struct ovs_list *requests) ofpbuf_delete(reply); } +static void +bundle_error_reporter(const struct ofp_header *oh) +{ + ofp_print(stderr, oh, ntohs(oh->length), verbosity + 1); + fflush(stderr); +} + +static void +bundle_transact(struct vconn *vconn, struct ovs_list *requests, uint16_t flags) +{ + run(vconn_bundle_transact(vconn, requests, flags, bundle_error_reporter), + "talking to %s", vconn_get_name(vconn)); +} + /* Sends 'request', which should be a request that only has a reply if an error * occurs, and waits for it to succeed or fail. If an error does occur, prints * it and exits with an error. @@ -698,6 +731,85 @@ ofctl_dump_table_features(struct ovs_cmdl_context *ctx) open_vconn(ctx->argv[1], &vconn); request = ofputil_encode_table_features_request(vconn_get_version(vconn)); + + /* The following is similar to dump_trivial_stats_transaction(), but it + * maintains the previous 'ofputil_table_features' from one stats reply + * message to the next, which allows duplication to be eliminated in the + * output across messages. Otherwise the output is much larger and harder + * to read, because only 17 or so ofputil_table_features elements fit in a + * single 64 kB OpenFlow message and therefore you get a ton of repetition + * (every 17th element is printed in full instead of abbreviated). */ + + const struct ofp_header *request_oh = request->data; + ovs_be32 send_xid = request_oh->xid; + bool done = false; + + struct ofputil_table_features prev; + int n = 0; + + send_openflow_buffer(vconn, request); + while (!done) { + ovs_be32 recv_xid; + struct ofpbuf *reply; + + run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed"); + recv_xid = ((struct ofp_header *) reply->data)->xid; + if (send_xid == recv_xid) { + enum ofptype type; + enum ofperr error; + error = ofptype_decode(&type, reply->data); + if (error) { + ovs_fatal(0, "decode error: %s", ofperr_get_name(error)); + } else if (type == OFPTYPE_ERROR) { + ofp_print(stdout, reply->data, reply->size, verbosity + 1); + done = true; + } else if (type == OFPTYPE_TABLE_FEATURES_STATS_REPLY) { + done = !ofpmp_more(reply->data); + for (;;) { + struct ofputil_table_features tf; + int retval; + + retval = ofputil_decode_table_features(reply, &tf, true); + if (retval) { + if (retval != EOF) { + ovs_fatal(0, "decode error: %s", + ofperr_get_name(retval)); + } + break; + } + + struct ds s = DS_EMPTY_INITIALIZER; + ofp_print_table_features(&s, &tf, n ? &prev : NULL, + NULL, NULL); + puts(ds_cstr(&s)); + ds_destroy(&s); + + prev = tf; + n++; + } + } else { + ovs_fatal(0, "received bad reply: %s", + ofp_to_string(reply->data, reply->size, + verbosity + 1)); + } + } else { + VLOG_DBG("received reply with xid %08"PRIx32" " + "!= expected %08"PRIx32, recv_xid, send_xid); + } + ofpbuf_delete(reply); + } + + vconn_close(vconn); +} + +static void +ofctl_dump_table_desc(struct ovs_cmdl_context *ctx) +{ + struct ofpbuf *request; + struct vconn *vconn; + + open_vconn(ctx->argv[1], &vconn); + request = ofputil_encode_table_desc_request(vconn_get_version(vconn)); if (request) { dump_stats_transaction(vconn, request); } @@ -705,6 +817,7 @@ ofctl_dump_table_features(struct ovs_cmdl_context *ctx) vconn_close(vconn); } + static bool fetch_port_by_stats(struct vconn *, const char *port_name, ofp_port_t port_no, struct ofputil_phy_port *); @@ -1174,6 +1287,33 @@ open_vconn_for_flow_mod(const char *remote, struct vconn **vconnp, "formats (%s)", usable_s); } +static void +bundle_flow_mod__(const char *remote, struct ofputil_flow_mod *fms, + size_t n_fms, enum ofputil_protocol usable_protocols) +{ + enum ofputil_protocol protocol; + struct vconn *vconn; + struct ovs_list requests; + size_t i; + + list_init(&requests); + + /* Bundles need OpenFlow 1.4+. */ + usable_protocols &= OFPUTIL_P_OF14_UP; + protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols); + + for (i = 0; i < n_fms; i++) { + struct ofputil_flow_mod *fm = &fms[i]; + struct ofpbuf *request = ofputil_encode_flow_mod(fm, protocol); + + list_push_back(&requests, &request->list_node); + free(CONST_CAST(struct ofpact *, fm->ofpacts)); + } + + bundle_transact(vconn, &requests, OFPBF_ORDERED | OFPBF_ATOMIC); + vconn_close(vconn); +} + static void ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms, size_t n_fms, enum ofputil_protocol usable_protocols) @@ -1182,6 +1322,11 @@ ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms, struct vconn *vconn; size_t i; + if (bundle) { + bundle_flow_mod__(remote, fms, n_fms, usable_protocols); + return; + } + protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols); for (i = 0; i < n_fms; i++) { @@ -1194,13 +1339,19 @@ ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms, } static void -ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command) +ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], int command) { enum ofputil_protocol usable_protocols; struct ofputil_flow_mod *fms = NULL; size_t n_fms = 0; char *error; + if (command == OFPFC_ADD) { + /* Allow the file to specify a mix of commands. If none specified at + * the beginning of any given line, then the default is OFPFC_ADD, so + * this is backwards compatible. */ + command = -2; + } error = parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms, &usable_protocols); if (error) { @@ -1781,35 +1932,28 @@ found: static void ofctl_mod_table(struct ovs_cmdl_context *ctx) { - enum ofputil_protocol protocol, usable_protocols; + uint32_t usable_versions; struct ofputil_table_mod tm; struct vconn *vconn; char *error; - int i; - error = parse_ofp_table_mod(&tm, ctx->argv[2], ctx->argv[3], &usable_protocols); + error = parse_ofp_table_mod(&tm, ctx->argv[2], ctx->argv[3], + &usable_versions); if (error) { ovs_fatal(0, "%s", error); } - protocol = open_vconn(ctx->argv[1], &vconn); - if (!(protocol & usable_protocols)) { - for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) { - enum ofputil_protocol f = 1 << i; - if (f != protocol - && f & usable_protocols - && try_set_protocol(vconn, f, &protocol)) { - protocol = f; - break; - } - } - } - - if (!(protocol & usable_protocols)) { - char *usable_s = ofputil_protocols_to_string(usable_protocols); - ovs_fatal(0, "Switch does not support table mod message(%s)", usable_s); + uint32_t allowed_versions = get_allowed_ofp_versions(); + if (!(allowed_versions & usable_versions)) { + struct ds versions = DS_EMPTY_INITIALIZER; + ofputil_format_version_bitmap_names(&versions, allowed_versions); + ovs_fatal(0, "table_mod '%s' requires one of the OpenFlow " + "versions %s but none is enabled (use -O)", + ctx->argv[3], ds_cstr(&versions)); } + mask_allowed_ofp_versions(usable_versions); + enum ofputil_protocol protocol = open_vconn(ctx->argv[1], &vconn); transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol)); vconn_close(vconn); } @@ -1951,7 +2095,7 @@ ofctl_ofp_parse_pcap(struct ovs_cmdl_context *ctx) if (error) { break; } - packet->md = PKT_METADATA_INITIALIZER(ODPP_NONE); + pkt_metadata_init(&packet->md, ODPP_NONE); flow_extract(packet, &flow); if (flow.dl_type == htons(ETH_TYPE_IP) && flow.nw_proto == IPPROTO_TCP @@ -2261,6 +2405,54 @@ ofctl_dump_group_features(struct ovs_cmdl_context *ctx) vconn_close(vconn); } +static void +ofctl_geneve_mod(struct ovs_cmdl_context *ctx, uint16_t command) +{ + enum ofputil_protocol usable_protocols; + enum ofputil_protocol protocol; + struct ofputil_geneve_table_mod gtm; + char *error; + enum ofp_version version; + struct ofpbuf *request; + struct vconn *vconn; + + error = parse_ofp_geneve_table_mod_str(>m, command, ctx->argc > 2 ? + ctx->argv[2] : "", + &usable_protocols); + if (error) { + ovs_fatal(0, "%s", error); + } + + protocol = open_vconn_for_flow_mod(ctx->argv[1], &vconn, usable_protocols); + version = ofputil_protocol_to_ofp_version(protocol); + + request = ofputil_encode_geneve_table_mod(version, >m); + if (request) { + transact_noreply(vconn, request); + } + + vconn_close(vconn); + ofputil_uninit_geneve_table(>m.mappings); +} + +static void +ofctl_add_geneve_map(struct ovs_cmdl_context *ctx) +{ + ofctl_geneve_mod(ctx, NXGTMC_ADD); +} + +static void +ofctl_del_geneve_map(struct ovs_cmdl_context *ctx) +{ + ofctl_geneve_mod(ctx, ctx->argc > 2 ? NXGTMC_DELETE : NXGTMC_CLEAR); +} + +static void +ofctl_dump_geneve_map(struct ovs_cmdl_context *ctx) +{ + dump_trivial_transaction(ctx->argv[1], OFPRAW_NXT_GENEVE_TABLE_REQUEST); +} + static void ofctl_help(struct ovs_cmdl_context *ctx OVS_UNUSED) { @@ -2396,7 +2588,8 @@ fte_insert(struct classifier *cls, const struct match *match, cls_rule_init(&fte->rule, match, priority); fte->versions[index] = version; - old = fte_from_cls_rule(classifier_replace(cls, &fte->rule, NULL, 0)); + old = fte_from_cls_rule(classifier_replace(cls, &fte->rule, + CLS_MIN_VERSION, NULL, 0)); if (old) { fte->versions[!index] = old->versions[!index]; old->versions[!index] = NULL; @@ -2635,7 +2828,11 @@ ofctl_replace_flows(struct ovs_cmdl_context *ctx) fte_make_flow_mod(fte, FILE_IDX, OFPFC_ADD, protocol, &requests); } } - transact_multiple_noreply(vconn, &requests); + if (bundle) { + bundle_transact(vconn, &requests, OFPBF_ORDERED | OFPBF_ATOMIC); + } else { + transact_multiple_noreply(vconn, &requests); + } vconn_close(vconn); fte_free_all(&cls); @@ -3255,7 +3452,7 @@ ofctl_parse_pcap(struct ovs_cmdl_context *ctx) ovs_fatal(error, "%s: read failed", ctx->argv[1]); } - packet->md = PKT_METADATA_INITIALIZER(ODPP_NONE); + pkt_metadata_init(&packet->md, ODPP_NONE); flow_extract(packet, &flow); flow_print(stdout, &flow); putchar('\n'); @@ -3497,6 +3694,8 @@ static const struct ovs_cmdl_command all_commands[] = { 1, 1, ofctl_dump_tables }, { "dump-table-features", "switch", 1, 1, ofctl_dump_table_features }, + { "dump-table-desc", "switch", + 1, 1, ofctl_dump_table_desc }, { "dump-flows", "switch", 1, 2, ofctl_dump_flows }, { "dump-aggregate", "switch", @@ -3577,6 +3776,12 @@ static const struct ovs_cmdl_command all_commands[] = { 1, 2, ofctl_dump_group_stats }, { "dump-group-features", "switch", 1, 1, ofctl_dump_group_features }, + { "add-geneve-map", "switch map", + 2, 2, ofctl_add_geneve_map }, + { "del-geneve-map", "switch [map]", + 1, 2, ofctl_del_geneve_map }, + { "dump-geneve-map", "switch", + 1, 1, ofctl_dump_geneve_map }, { "help", NULL, 0, INT_MAX, ofctl_help }, { "list-commands", NULL, 0, INT_MAX, ofctl_list_commands },