X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=utilities%2Fovs-ofctl.c;h=fd0829eaa42922b098a2b41f690d380d011a8293;hb=67e96a5dca90225358936b1392bba8b3207805b1;hp=9b1a1b24e786ea2dd59941b963973bb1f58dfac7;hpb=7d0c5973d5165f3cc6099de326ad0dfc326bac33;p=openvswitch diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index 9b1a1b24..fd0829ea 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -45,6 +46,7 @@ #include "ofproto/ofproto.h" #include "openflow/nicira-ext.h" #include "openflow/openflow.h" +#include "packets.h" #include "poll-loop.h" #include "random.h" #include "stream-ssl.h" @@ -77,6 +79,10 @@ static int preferred_packet_in_format = -1; /* -m, --more: Additional verbosity for ofp-print functions. */ static int verbosity; +/* --timestamp: Print a timestamp before each received packet on "monitor" and + * "snoop" command? */ +static bool timestamp; + static const struct command all_commands[]; static void usage(void) NO_RETURN; @@ -98,6 +104,7 @@ parse_options(int argc, char *argv[]) enum { OPT_STRICT = UCHAR_MAX + 1, OPT_READD, + OPT_TIMESTAMP, DAEMON_OPTION_ENUMS, VLOG_OPTION_ENUMS }; @@ -108,6 +115,7 @@ parse_options(int argc, char *argv[]) {"flow-format", required_argument, NULL, 'F'}, {"packet-in-format", required_argument, NULL, 'P'}, {"more", no_argument, NULL, 'm'}, + {"timestamp", no_argument, NULL, OPT_TIMESTAMP}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, DAEMON_LONG_OPTIONS, @@ -171,6 +179,10 @@ parse_options(int argc, char *argv[]) readd = true; break; + case OPT_TIMESTAMP: + timestamp = true; + break; + DAEMON_OPTION_HANDLERS VLOG_OPTION_HANDLERS STREAM_SSL_OPTION_HANDLERS @@ -209,6 +221,8 @@ usage(void) " del-flows SWITCH [FLOW] delete matching FLOWs\n" " replace-flows SWITCH FILE replace flows with those in FILE\n" " diff-flows SOURCE1 SOURCE2 compare flows from two sources\n" + " packet-out SWITCH IN_PORT ACTIONS PACKET...\n" + " execute ACTIONS on PACKET\n" " monitor SWITCH [MISSLEN] [invalid_ttl]\n" " print packets received from SWITCH\n" " snoop SWITCH snoop on SWITCH and its controller\n" @@ -227,6 +241,7 @@ usage(void) " -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" + " --timestamp (monitor, snoop) print timestamps\n" " -t, --timeout=SECS give up after SECS seconds\n" " -h, --help display this help message\n" " -V, --version display version information\n"); @@ -239,7 +254,7 @@ ofctl_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, { bool *exiting = exiting_; *exiting = true; - unixctl_command_reply(conn, 200, ""); + unixctl_command_reply(conn, NULL); } static void run(int retval, const char *message, ...) @@ -447,6 +462,8 @@ fetch_switch_config(struct vconn *vconn, struct ofp_switch_config *config_) config = reply->data; *config_ = *config; + + ofpbuf_delete(reply); } static void @@ -826,9 +843,135 @@ monitor_set_invalid_ttl_to_controller(struct vconn *vconn) return 0; } +/* Converts hex digits in 'hex' to an OpenFlow message in '*msgp'. The + * caller must free '*msgp'. On success, returns NULL. On failure, returns + * an error message and stores NULL in '*msgp'. */ +static const char * +openflow_from_hex(const char *hex, struct ofpbuf **msgp) +{ + struct ofp_header *oh; + struct ofpbuf *msg; + + msg = ofpbuf_new(strlen(hex) / 2); + *msgp = NULL; + + if (ofpbuf_put_hex(msg, hex, NULL)[0] != '\0') { + ofpbuf_delete(msg); + return "Trailing garbage in hex data"; + } + + if (msg->size < sizeof(struct ofp_header)) { + ofpbuf_delete(msg); + return "Message too short for OpenFlow"; + } + + oh = msg->data; + if (msg->size != ntohs(oh->length)) { + ofpbuf_delete(msg); + return "Message size does not match length in OpenFlow header"; + } + + *msgp = msg; + return NULL; +} + +static void +ofctl_send(struct unixctl_conn *conn, int argc, + const char *argv[], void *vconn_) +{ + struct vconn *vconn = vconn_; + struct ds reply; + bool ok; + int i; + + ok = true; + ds_init(&reply); + for (i = 1; i < argc; i++) { + const char *error_msg; + struct ofpbuf *msg; + int error; + + error_msg = openflow_from_hex(argv[i], &msg); + if (error_msg) { + ds_put_format(&reply, "%s\n", error_msg); + ok = false; + continue; + } + + fprintf(stderr, "send: "); + ofp_print(stderr, msg->data, msg->size, verbosity); + + error = vconn_send_block(vconn, msg); + if (error) { + ofpbuf_delete(msg); + ds_put_format(&reply, "%s\n", strerror(error)); + ok = false; + } else { + ds_put_cstr(&reply, "sent\n"); + } + } + + if (ok) { + unixctl_command_reply(conn, ds_cstr(&reply)); + } else { + unixctl_command_reply_error(conn, ds_cstr(&reply)); + } + ds_destroy(&reply); +} + +struct barrier_aux { + struct vconn *vconn; /* OpenFlow connection for sending barrier. */ + struct unixctl_conn *conn; /* Connection waiting for barrier response. */ +}; + +static void +ofctl_barrier(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[] OVS_UNUSED, void *aux_) +{ + struct barrier_aux *aux = aux_; + struct ofpbuf *msg; + int error; + + if (aux->conn) { + unixctl_command_reply_error(conn, "already waiting for barrier reply"); + return; + } + + msg = ofputil_encode_barrier_request(); + fprintf(stderr, "send: "); + ofp_print(stderr, msg->data, msg->size, verbosity); + + error = vconn_send_block(aux->vconn, msg); + if (error) { + ofpbuf_delete(msg); + unixctl_command_reply_error(conn, strerror(error)); + } else { + aux->conn = conn; + } +} + +static void +ofctl_set_output_file(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[], void *aux OVS_UNUSED) +{ + int fd; + + fd = open(argv[1], O_CREAT | O_TRUNC | O_WRONLY, 0666); + if (fd < 0) { + unixctl_command_reply_error(conn, strerror(errno)); + return; + } + + fflush(stderr); + dup2(fd, STDERR_FILENO); + close(fd); + unixctl_command_reply(conn, NULL); +} + static void monitor_vconn(struct vconn *vconn) { + struct barrier_aux barrier_aux = { vconn, NULL }; struct unixctl_server *server; bool exiting = false; int error; @@ -840,6 +983,12 @@ monitor_vconn(struct vconn *vconn) ovs_fatal(error, "failed to create unixctl server"); } 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/barrier", "", 0, 0, + ofctl_barrier, &barrier_aux); + unixctl_command_register("ofctl/set-output-file", "FILE", 1, 1, + ofctl_set_output_file, NULL); daemonize_complete(); for (;;) { @@ -849,14 +998,29 @@ monitor_vconn(struct vconn *vconn) unixctl_server_run(server); for (;;) { + uint8_t msg_type; + retval = vconn_recv(vconn, &b); if (retval == EAGAIN) { break; } + msg_type = ((const struct ofp_header *) b->data)->type; run(retval, "vconn_recv"); + if (timestamp) { + time_t now = time_wall(); + char s[32]; + + strftime(s, sizeof s, "%Y-%m-%d %H:%M:%S: ", localtime(&now)); + fputs(s, stderr); + } ofp_print(stderr, b->data, b->size, verbosity + 2); ofpbuf_delete(b); + + if (barrier_aux.conn && msg_type == OFPT_BARRIER_REPLY) { + unixctl_command_reply(barrier_aux.conn, NULL); + barrier_aux.conn = NULL; + } } if (exiting) { @@ -869,6 +1033,8 @@ monitor_vconn(struct vconn *vconn) unixctl_server_wait(server); poll_block(); } + vconn_close(vconn); + unixctl_server_destroy(server); } static void @@ -949,6 +1115,43 @@ do_probe(int argc OVS_UNUSED, char *argv[]) vconn_close(vconn); } +static void +do_packet_out(int argc, char *argv[]) +{ + struct ofputil_packet_out po; + struct ofpbuf actions; + struct vconn *vconn; + int i; + + ofpbuf_init(&actions, sizeof(union ofp_action)); + parse_ofp_actions(argv[3], &actions); + + po.buffer_id = UINT32_MAX; + po.in_port = (!strcasecmp(argv[2], "none") ? OFPP_NONE + : !strcasecmp(argv[2], "local") ? OFPP_LOCAL + : str_to_port_no(argv[1], argv[2])); + po.actions = actions.data; + po.n_actions = actions.size / sizeof(union ofp_action); + + open_vconn(argv[1], &vconn); + for (i = 4; i < argc; i++) { + struct ofpbuf *packet, *opo; + const char *error_msg; + + error_msg = eth_from_hex(argv[i], &packet); + if (error_msg) { + ovs_fatal(0, "%s", error_msg); + } + + po.packet = packet->data; + po.packet_len = packet->size; + opo = ofputil_encode_packet_out(&po); + transact_noreply(vconn, opo); + ofpbuf_delete(packet); + } + vconn_close(vconn); +} + static void do_mod_port(int argc OVS_UNUSED, char *argv[]) { @@ -1350,7 +1553,7 @@ read_flows_from_switch(struct vconn *vconn, enum nx_flow_format flow_format, struct ofputil_flow_stats fs; int retval; - retval = ofputil_decode_flow_stats_reply(&fs, reply); + retval = ofputil_decode_flow_stats_reply(&fs, reply, false); if (retval) { if (retval != EOF) { ovs_fatal(0, "parse error in reply"); @@ -1678,6 +1881,7 @@ static const struct command all_commands[] = { { "del-flows", 1, 2, do_del_flows }, { "replace-flows", 2, 2, do_replace_flows }, { "diff-flows", 2, 2, do_diff_flows }, + { "packet-out", 4, INT_MAX, do_packet_out }, { "dump-ports", 1, 2, do_dump_ports }, { "mod-port", 3, 3, do_mod_port }, { "get-frags", 1, 1, do_get_frags },