X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=utilities%2Fovs-ofctl.c;h=d9d1d193fee52894a4e5ec7dca023fbe02efd088;hb=522f8f6ff0af5eb56197adb309cf56b4800756f8;hp=fef7fdacc33d00a9318ab6b20ac4ae6cb24b579d;hpb=eaa6eb2afd76819de5df0ae9ed753627504ed7e4;p=sliver-openvswitch.git diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index fef7fdacc..d9d1d193f 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks. + * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ #include "byte-order.h" #include "classifier.h" #include "command-line.h" +#include "daemon.h" #include "compiler.h" #include "dirs.h" #include "dynamic-string.h" @@ -43,16 +44,20 @@ #include "ofproto/ofproto.h" #include "openflow/nicira-ext.h" #include "openflow/openflow.h" +#include "poll-loop.h" #include "random.h" #include "stream-ssl.h" #include "timeval.h" +#include "unixctl.h" #include "util.h" #include "vconn.h" #include "vlog.h" VLOG_DEFINE_THIS_MODULE(ofctl); -/* --strict: Use strict matching for flow mod commands? */ +/* --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. + */ static bool strict; /* --readd: If ture, on replace-flows, re-add even flows that have not changed @@ -63,6 +68,11 @@ static bool readd; * particular flow format or -1 to let ovs-ofctl choose intelligently. */ static int preferred_flow_format = -1; +/* -P, --packet-in-format: Packet IN format to use in monitor and snoop + * commands. Either one of NXPIF_* to force a particular packet_in format, or + * -1 to let ovs-ofctl choose the default. */ +static int preferred_packet_in_format = -1; + /* -m, --more: Additional verbosity for ofp-print functions. */ static int verbosity; @@ -87,6 +97,7 @@ parse_options(int argc, char *argv[]) enum { OPT_STRICT = UCHAR_MAX + 1, OPT_READD, + DAEMON_OPTION_ENUMS, VLOG_OPTION_ENUMS }; static struct option long_options[] = { @@ -94,9 +105,11 @@ parse_options(int argc, char *argv[]) {"strict", no_argument, NULL, OPT_STRICT}, {"readd", no_argument, NULL, OPT_READD}, {"flow-format", required_argument, NULL, 'F'}, + {"packet-in-format", required_argument, NULL, 'P'}, {"more", no_argument, NULL, 'm'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, + DAEMON_LONG_OPTIONS, VLOG_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {NULL, 0, NULL, 0}, @@ -130,6 +143,14 @@ parse_options(int argc, char *argv[]) } break; + case 'P': + preferred_packet_in_format = + ofputil_packet_in_format_from_string(optarg); + if (preferred_packet_in_format < 0) { + ovs_fatal(0, "unknown packet-in format `%s'", optarg); + } + break; + case 'm': verbosity++; break; @@ -138,7 +159,7 @@ parse_options(int argc, char *argv[]) usage(); case 'V': - OVS_PRINT_VERSION(OFP_VERSION, OFP_VERSION); + ovs_print_version(OFP_VERSION, OFP_VERSION); exit(EXIT_SUCCESS); case OPT_STRICT: @@ -149,6 +170,7 @@ parse_options(int argc, char *argv[]) readd = true; break; + DAEMON_OPTION_HANDLERS VLOG_OPTION_HANDLERS STREAM_SSL_OPTION_HANDLERS @@ -172,6 +194,8 @@ usage(void) " dump-desc SWITCH print switch description\n" " dump-tables SWITCH print table stats\n" " mod-port SWITCH IFACE ACT modify port behavior\n" + " get-frags SWITCH print fragment handling behavior\n" + " set-frags SWITCH FRAG_MODE set fragment handling behavior\n" " dump-ports SWITCH [PORT] print port statistics\n" " dump-flows SWITCH print all flow entries\n" " dump-flows SWITCH FLOW print matching FLOWs\n" @@ -185,17 +209,19 @@ usage(void) " replace-flows SWITCH FILE replace flows with those in FILE\n" " monitor SWITCH [MISSLEN] print packets received from SWITCH\n" "\nFor OpenFlow switches and controllers:\n" - " probe VCONN probe whether VCONN is up\n" - " ping VCONN [N] latency of N-byte echos\n" - " benchmark VCONN N COUNT bandwidth of COUNT N-byte echos\n" - "where each SWITCH is an active OpenFlow connection method.\n", + " probe TARGET probe whether TARGET is up\n" + " ping TARGET [N] latency of N-byte echos\n" + " benchmark TARGET N COUNT bandwidth of COUNT N-byte echos\n" + "where SWITCH or TARGET is an active OpenFlow connection method.\n", program_name, program_name); vconn_usage(true, false, false); + daemon_usage(); vlog_usage(); printf("\nOther options:\n" " --strict use strict match for flow commands\n" " --readd replace flows that haven't changed\n" " -F, --flow-format=FORMAT force particular flow format\n" + " -P, --packet-in-format=FRMT force particular packet in format\n" " -m, --more be more verbose printing OpenFlow\n" " -t, --timeout=SECS give up after SECS seconds\n" " -h, --help display this help message\n" @@ -203,6 +229,15 @@ usage(void) exit(EXIT_SUCCESS); } +static void +ofctl_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[] OVS_UNUSED, void *exiting_) +{ + bool *exiting = exiting_; + *exiting = true; + unixctl_command_reply(conn, 200, ""); +} + static void run(int retval, const char *message, ...) PRINTF_FORMAT(2, 3); @@ -351,7 +386,9 @@ dump_trivial_stats_transaction(const char *vconn_name, uint8_t stats_type) /* 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. */ + * it and exits with an error. + * + * Destroys all of the 'requests'. */ static void transact_multiple_noreply(struct vconn *vconn, struct list *requests) { @@ -372,7 +409,9 @@ transact_multiple_noreply(struct vconn *vconn, struct list *requests) /* 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. */ + * it and exits with an error. + * + * Destroys 'request'. */ static void transact_noreply(struct vconn *vconn, struct ofpbuf *request) { @@ -383,6 +422,44 @@ transact_noreply(struct vconn *vconn, struct ofpbuf *request) transact_multiple_noreply(vconn, &requests); } +static void +fetch_switch_config(struct vconn *vconn, struct ofp_switch_config *config_) +{ + struct ofp_switch_config *config; + struct ofp_header *header; + struct ofpbuf *request; + struct ofpbuf *reply; + + make_openflow(sizeof(struct ofp_header), OFPT_GET_CONFIG_REQUEST, + &request); + run(vconn_transact(vconn, request, &reply), + "talking to %s", vconn_get_name(vconn)); + + header = reply->data; + if (header->type != OFPT_GET_CONFIG_REPLY || + header->length != htons(sizeof *config)) { + ovs_fatal(0, "%s: bad reply to config request", vconn_get_name(vconn)); + } + + config = reply->data; + *config_ = *config; +} + +static void +set_switch_config(struct vconn *vconn, struct ofp_switch_config *config_) +{ + struct ofp_switch_config *config; + struct ofp_header save_header; + struct ofpbuf *request; + + config = make_openflow(sizeof *config, OFPT_SET_CONFIG, &request); + save_header = config->header; + *config = *config_; + config->header = save_header; + + transact_noreply(vconn, request); +} + static void do_show(int argc OVS_UNUSED, char *argv[]) { @@ -534,7 +611,7 @@ static void do_dump_flows__(int argc, char *argv[], bool aggregate) { enum nx_flow_format min_flow_format, flow_format; - struct flow_stats_request fsr; + struct ofputil_flow_stats_request fsr; struct ofpbuf *request; struct vconn *vconn; @@ -542,6 +619,9 @@ do_dump_flows__(int argc, char *argv[], bool aggregate) open_vconn(argv[1], &vconn); min_flow_format = ofputil_min_flow_format(&fsr.match); + if (fsr.cookie_mask != htonll(0)) { + min_flow_format = NXFF_NXM; + } flow_format = negotiate_highest_flow_format(vconn, min_flow_format); request = ofputil_encode_flow_stats_request(&fsr, flow_format); dump_stats_transaction(argv[1], request); @@ -702,14 +782,65 @@ do_del_flows(int argc, char *argv[]) do_flow_mod__(argc, argv, strict ? OFPFC_DELETE_STRICT : OFPFC_DELETE); } +static void +set_packet_in_format(struct vconn *vconn, + enum nx_packet_in_format packet_in_format) +{ + struct ofpbuf *spif = ofputil_make_set_packet_in_format(packet_in_format); + transact_noreply(vconn, spif); + VLOG_DBG("%s: using user-specified packet in format %s", + vconn_get_name(vconn), + ofputil_packet_in_format_to_string(packet_in_format)); +} + static void monitor_vconn(struct vconn *vconn) { + struct unixctl_server *server; + bool exiting = false; + int error, fd; + + /* Daemonization will close stderr but we really want to keep it, so make a + * copy. */ + fd = dup(STDERR_FILENO); + + daemonize_start(); + error = unixctl_server_create(NULL, &server); + if (error) { + ovs_fatal(error, "failed to create unixctl server"); + } + unixctl_command_register("exit", "", 0, 0, ofctl_exit, &exiting); + daemonize_complete(); + + /* Now get stderr back. */ + dup2(fd, STDERR_FILENO); + for (;;) { struct ofpbuf *b; - run(vconn_recv_block(vconn, &b), "vconn_recv"); - ofp_print(stderr, b->data, b->size, verbosity + 2); - ofpbuf_delete(b); + int retval; + + unixctl_server_run(server); + + for (;;) { + retval = vconn_recv(vconn, &b); + if (retval == EAGAIN) { + break; + } + + run(retval, "vconn_recv"); + ofp_print(stderr, b->data, b->size, verbosity + 2); + ofpbuf_delete(b); + } + + if (exiting) { + break; + } + + vconn_run(vconn); + vconn_run_wait(vconn); + vconn_recv_wait(vconn); + unixctl_server_wait(server); + poll_block(); } } @@ -720,14 +851,30 @@ do_monitor(int argc, char *argv[]) open_vconn(argv[1], &vconn); if (argc > 2) { - int miss_send_len = atoi(argv[2]); - struct ofp_switch_config *osc; - struct ofpbuf *buf; + struct ofp_switch_config config; - osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &buf); - osc->miss_send_len = htons(miss_send_len); - transact_noreply(vconn, buf); + fetch_switch_config(vconn, &config); + config.miss_send_len = htons(atoi(argv[2])); + set_switch_config(vconn, &config); } + if (preferred_packet_in_format >= 0) { + set_packet_in_format(vconn, preferred_packet_in_format); + } else { + struct ofpbuf *spif, *reply; + + spif = ofputil_make_set_packet_in_format(NXPIF_NXM); + run(vconn_transact_noreply(vconn, spif, &reply), + "talking to %s", vconn_get_name(vconn)); + if (reply) { + char *s = ofp_to_string(reply->data, reply->size, 2); + VLOG_DBG("%s: failed to set packet in format to nxm, controller" + " replied: %s. Falling back to the switch default.", + vconn_get_name(vconn), s); + free(s); + ofpbuf_delete(reply); + } + } + monitor_vconn(vconn); } @@ -797,6 +944,11 @@ do_mod_port(int argc OVS_UNUSED, char *argv[]) } else if (!strcasecmp(argv[3], "noflood")) { opm->mask |= htonl(OFPPC_NO_FLOOD); opm->config |= htonl(OFPPC_NO_FLOOD); + } else if (!strcasecmp(argv[3], "forward")) { + opm->mask |= htonl(OFPPC_NO_FWD); + } else if (!strcasecmp(argv[3], "noforward")) { + opm->mask |= htonl(OFPPC_NO_FWD); + opm->config |= htonl(OFPPC_NO_FWD); } else { ovs_fatal(0, "unknown mod-port command '%s'", argv[3]); } @@ -806,6 +958,51 @@ do_mod_port(int argc OVS_UNUSED, char *argv[]) vconn_close(vconn); } +static void +do_get_frags(int argc OVS_UNUSED, char *argv[]) +{ + struct ofp_switch_config config; + struct vconn *vconn; + + open_vconn(argv[1], &vconn); + fetch_switch_config(vconn, &config); + puts(ofputil_frag_handling_to_string(ntohs(config.flags))); + vconn_close(vconn); +} + +static void +do_set_frags(int argc OVS_UNUSED, char *argv[]) +{ + struct ofp_switch_config config; + enum ofp_config_flags mode; + struct vconn *vconn; + ovs_be16 flags; + + if (!ofputil_frag_handling_from_string(argv[2], &mode)) { + ovs_fatal(0, "%s: unknown fragment handling mode", argv[2]); + } + + open_vconn(argv[1], &vconn); + fetch_switch_config(vconn, &config); + flags = htons(mode) | (config.flags & htons(~OFPC_FRAG_MASK)); + if (flags != config.flags) { + /* Set the configuration. */ + config.flags = flags; + set_switch_config(vconn, &config); + + /* Then retrieve the configuration to see if it really took. OpenFlow + * doesn't define error reporting for bad modes, so this is all we can + * do. */ + fetch_switch_config(vconn, &config); + if (flags != config.flags) { + ovs_fatal(0, "%s: setting fragment handling mode failed (this " + "switch probably doesn't support mode \"%s\")", + argv[1], ofputil_frag_handling_to_string(mode)); + } + } + vconn_close(vconn); +} + static void do_ping(int argc, char *argv[]) { @@ -1041,11 +1238,9 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index) min_flow_format = NXFF_OPENFLOW10; while (!ds_get_preprocessed_line(&s, file)) { struct fte_version *version; + struct ofputil_flow_mod fm; enum nx_flow_format min_ff; - struct ofpbuf actions; - struct flow_mod fm; - ofpbuf_init(&actions, 64); parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), true); version = xmalloc(sizeof *version); @@ -1053,8 +1248,8 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index) version->idle_timeout = fm.idle_timeout; version->hard_timeout = fm.hard_timeout; version->flags = fm.flags & (OFPFF_SEND_FLOW_REM | OFPFF_EMERG); - version->n_actions = actions.size / sizeof *version->actions; - version->actions = ofpbuf_steal_data(&actions); + version->actions = fm.actions; + version->n_actions = fm.n_actions; min_ff = ofputil_min_flow_format(&fm.cr); min_flow_format = MAX(min_flow_format, min_ff); @@ -1078,7 +1273,7 @@ static void read_flows_from_switch(struct vconn *vconn, enum nx_flow_format flow_format, struct classifier *cls, int index) { - struct flow_stats_request fsr; + struct ofputil_flow_stats_request fsr; struct ofpbuf *request; ovs_be32 send_xid; bool done; @@ -1087,6 +1282,7 @@ read_flows_from_switch(struct vconn *vconn, enum nx_flow_format flow_format, cls_rule_init_catchall(&fsr.match, 0); fsr.out_port = OFPP_NONE; fsr.table_id = 0xff; + fsr.cookie = fsr.cookie_mask = htonll(0); request = ofputil_encode_flow_stats_request(&fsr, flow_format); send_xid = ((struct ofp_header *) request->data)->xid; send_openflow_buffer(vconn, request); @@ -1154,7 +1350,7 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command, enum nx_flow_format flow_format, struct list *packets) { const struct fte_version *version = fte->versions[index]; - struct flow_mod fm; + struct ofputil_flow_mod fm; struct ofpbuf *ofm; fm.cr = fte->rule; @@ -1336,7 +1532,7 @@ do_parse_flows(int argc OVS_UNUSED, char *argv[]) file = fopen(argv[1], "r"); if (file == NULL) { - ovs_fatal(errno, "%s: open", argv[2]); + ovs_fatal(errno, "%s: open", argv[1]); } flow_format = NXFF_OPENFLOW10; @@ -1365,6 +1561,7 @@ do_parse_nx_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) while (!ds_get_line(&in, stdin)) { struct ofpbuf nx_match; struct cls_rule rule; + ovs_be64 cookie, cookie_mask; int match_len; int error; char *s; @@ -1388,21 +1585,29 @@ do_parse_nx_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) match_len = nx_match_from_string(ds_cstr(&in), &nx_match); /* Convert nx_match to cls_rule. */ - error = nx_pull_match(&nx_match, match_len, 0, &rule); + if (strict) { + error = nx_pull_match(&nx_match, match_len, 0, &rule, + &cookie, &cookie_mask); + } else { + error = nx_pull_match_loose(&nx_match, match_len, 0, &rule, + &cookie, &cookie_mask); + } + if (!error) { char *out; /* Convert cls_rule back to nx_match. */ ofpbuf_uninit(&nx_match); ofpbuf_init(&nx_match, 0); - match_len = nx_put_match(&nx_match, &rule); + match_len = nx_put_match(&nx_match, &rule, cookie, cookie_mask); /* Convert nx_match to string. */ out = nx_match_to_string(nx_match.data, match_len); puts(out); free(out); } else { - printf("nx_pull_match() returned error %x\n", error); + printf("nx_pull_match() returned error %x (%s)\n", error, + ofputil_error_to_string(error)); } ofpbuf_uninit(&nx_match); @@ -1443,6 +1648,8 @@ static const struct command all_commands[] = { { "diff-flows", 2, 2, do_diff_flows }, { "dump-ports", 1, 2, do_dump_ports }, { "mod-port", 3, 3, do_mod_port }, + { "get-frags", 1, 1, do_get_frags }, + { "set-frags", 2, 2, do_set_frags }, { "probe", 1, 1, do_probe }, { "ping", 1, 2, do_ping }, { "benchmark", 3, 3, do_benchmark },