This commit adds a new command "ovs-appctl log/controller" to ovs. This command allows user to log OpenFlow messages between bridge and its controllers into a specified file.
Signed-off-by: Alex Wang <al...@nicira.com> --- lib/automake.mk | 2 ++ lib/ovs-snoop.c | 71 +++++++++++++++++++++++++++++++++++++++ lib/ovs-snoop.h | 28 +++++++++++++++ lib/rconn.c | 26 ++++++++++++++ lib/rconn.h | 3 ++ ofproto/automake.mk | 3 +- ofproto/connmgr.c | 63 ++++++++++++++++++++++++++++++++++ ofproto/connmgr.h | 4 +++ ofproto/ofproto-dpif.c | 66 ++++++++++++++++++++++++++++++++++++ ofproto/ofproto-log-unixctl.man | 37 ++++++++++++++++++++ tests/ofproto-dpif.at | 53 +++++++++++++++++++++++++++++ 11 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 lib/ovs-snoop.c create mode 100644 lib/ovs-snoop.h create mode 100644 ofproto/ofproto-log-unixctl.man diff --git a/lib/automake.mk b/lib/automake.mk index b46a868..f422422 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -131,6 +131,8 @@ lib_libopenvswitch_a_SOURCES = \ lib/ovs-atomic-pthreads.c \ lib/ovs-atomic-pthreads.h \ lib/ovs-atomic.h \ + lib/ovs-snoop.c \ + lib/ovs-snoop.h \ lib/ovs-thread.c \ lib/ovs-thread.h \ lib/ovsdb-data.c \ diff --git a/lib/ovs-snoop.c b/lib/ovs-snoop.c new file mode 100644 index 0000000..54e6bc1 --- /dev/null +++ b/lib/ovs-snoop.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#include "ovs-snoop.h" + +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include "dirs.h" +#include "stream.h" +#include "stream-fd.h" +#include "vlog.h" + +VLOG_DEFINE_THIS_MODULE(ovs_snoop); + +/* Opens a snoop file of name 'file_name'. Uses default file name if + * 'file_name' is null. Then creates a fd stream, stores the pointer of + * the stream in 'streamp'. Returns 0 if successful, otherwise a + * positive errno value. */ +int +snoop_open(struct stream **streamp, const char *file_name) +{ + char *snoop_file_name; + int retval, new_fd; + + snoop_file_name = (file_name + ? xstrdup(file_name) + : xasprintf("%s/ovs-snoop.log", ovs_logdir())); + + /* Open new snoop file. */ + new_fd = open(snoop_file_name, O_WRONLY | O_CREAT | O_APPEND, 0666); + if (new_fd < 0) { + VLOG_WARN("failed to open %s for snoop logging: %s", + snoop_file_name, ovs_strerror(errno)); + retval = errno; + goto exit; + } + retval = new_fd_stream(snoop_file_name, new_fd, 0, streamp); +exit: + free(snoop_file_name); + return retval; +} + +/* Closes snoop file. */ +void +snoop_close(struct stream *stream) +{ + stream_close(stream); +} + +/* Writes 'message' to the snoop log. */ +void +snoop_log(struct stream *stream, const void *msg, size_t size) +{ + stream_send(stream, msg, size); + stream_send(stream, "\n", 1); +} diff --git a/lib/ovs-snoop.h b/lib/ovs-snoop.h new file mode 100644 index 0000000..bd5e37d --- /dev/null +++ b/lib/ovs-snoop.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OVS_SNOOP_H +#define OVS_SNOOP_H 1 + +#include <stddef.h> + +struct stream; + +int snoop_open(struct stream **, const char *file_name); +void snoop_close(struct stream *); +void snoop_log(struct stream *, const void *msg, size_t size); + +#endif /* ovs-snoop.h */ diff --git a/lib/rconn.c b/lib/rconn.c index 39a12c9..2d36d75 100644 --- a/lib/rconn.c +++ b/lib/rconn.c @@ -25,6 +25,7 @@ #include "ofp-util.h" #include "ofpbuf.h" #include "openflow/openflow.h" +#include "ovs-snoop.h" #include "poll-loop.h" #include "sat-math.h" #include "timeval.h" @@ -131,6 +132,9 @@ struct rconn { struct vconn *monitors[8]; size_t n_monitors; + /* Controller logging. */ + struct stream *snoop_stream; + uint32_t allowed_versions; }; @@ -220,6 +224,8 @@ rconn_create(int probe_interval, int max_backoff, uint8_t dscp, rc->n_monitors = 0; rc->allowed_versions = allowed_versions; + rc->snoop_stream = NULL; + return rc; } @@ -580,6 +586,9 @@ rconn_recv(struct rconn *rc) int error = vconn_recv(rc->vconn, &buffer); if (!error) { copy_to_monitor(rc, buffer); + if (rc->snoop_stream) { + snoop_log(rc->snoop_stream, buffer->data, buffer->size); + } if (rc->probably_admitted || is_admitted_msg(buffer) || time_now() - rc->last_connected >= 30) { rc->probably_admitted = true; @@ -628,6 +637,9 @@ rconn_send(struct rconn *rc, struct ofpbuf *b, if (rconn_is_connected(rc)) { COVERAGE_INC(rconn_queued); copy_to_monitor(rc, b); + if (rc->snoop_stream) { + snoop_log(rc->snoop_stream, b->data, b->size); + } b->private_p = counter; if (counter) { rconn_packet_counter_inc(counter, b->size); @@ -697,6 +709,20 @@ rconn_add_monitor(struct rconn *rc, struct vconn *vconn) } } +/* Enables writing to snoop log of 'rc'. */ +void +rconn_set_snoop(struct rconn *rc, struct stream *stream) +{ + rc->snoop_stream = stream; +} + +/* Disables writing to snoop log of 'rc'. */ +void +rconn_clear_snoop(struct rconn *rc) +{ + rc->snoop_stream = NULL; +} + /* Returns 'rc''s name. This is a name for human consumption, appropriate for * use in log messages. It is not necessarily a name that may be passed * directly to, e.g., vconn_open(). */ diff --git a/lib/rconn.h b/lib/rconn.h index aa30238..4dd7a9c 100644 --- a/lib/rconn.h +++ b/lib/rconn.h @@ -36,6 +36,7 @@ struct vconn; struct rconn_packet_counter; +struct stream; struct rconn *rconn_create(int inactivity_probe_interval, int max_backoff, uint8_t dscp, @@ -47,6 +48,8 @@ void rconn_set_max_backoff(struct rconn *, int max_backoff); int rconn_get_max_backoff(const struct rconn *); void rconn_set_probe_interval(struct rconn *, int inactivity_probe_interval); int rconn_get_probe_interval(const struct rconn *); +void rconn_set_snoop(struct rconn *, struct stream *); +void rconn_clear_snoop(struct rconn *); void rconn_connect(struct rconn *, const char *target, const char *name); void rconn_connect_unreliably(struct rconn *, diff --git a/ofproto/automake.mk b/ofproto/automake.mk index af9a12a..8b1066f 100644 --- a/ofproto/automake.mk +++ b/ofproto/automake.mk @@ -48,7 +48,8 @@ BUILT_SOURCES += ofproto/ipfix-entities.def CLEANFILES += ofproto/ipfix-entities.def -MAN_FRAGMENTS += ofproto/ofproto-unixctl.man ofproto/ofproto-dpif-unixctl.man +MAN_FRAGMENTS += ofproto/ofproto-unixctl.man ofproto/ofproto-dpif-unixctl.man \ + ofproto/ofproto-log-unixctl.man # IPFIX entity definition macros generation from IANA's XML definition. EXTRA_DIST += ofproto/ipfix.xml diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c index 4264934..3defbe3 100644 --- a/ofproto/connmgr.c +++ b/ofproto/connmgr.c @@ -30,6 +30,7 @@ #include "ofp-util.h" #include "ofpbuf.h" #include "ofproto-provider.h" +#include "ovs-snoop.h" #include "pinsched.h" #include "poll-loop.h" #include "pktbuf.h" @@ -172,6 +173,9 @@ struct connmgr { struct sockaddr_in *extra_in_band_remotes; size_t n_extra_remotes; int in_band_queue; + + /* Controller logging. */ + struct stream *snoop_stream; }; static void update_in_band_remotes(struct connmgr *); @@ -211,6 +215,7 @@ connmgr_create(struct ofproto *ofproto, mgr->n_extra_remotes = 0; mgr->in_band_queue = -1; + mgr->snoop_stream = NULL; return mgr; } @@ -814,6 +819,64 @@ add_snooper(struct connmgr *mgr, struct vconn *vconn) vconn_close(vconn); } } + +/* Sets all controller rconns of mgr to snoop OpenFlow packets. */ +void +connmgr_snoop_set_all(struct connmgr *mgr) +{ + struct ofconn *ofconn; + + ovs_assert(mgr->snoop_stream); + /* Snoop all controller rconns. */ + HMAP_FOR_EACH (ofconn, hmap_node, &mgr->controllers) { + rconn_set_snoop(ofconn->rconn, mgr->snoop_stream); + } +} + +/* Checks the existence of controller matching the given 'target'. + * If a controller is found, set its rconn to snoop OpenFlow packets. */ +int +connmgr_snoop_set_one(struct connmgr *mgr, const char *target) +{ + struct ofconn *ofconn; + + ovs_assert(mgr->snoop_stream); + ofconn = find_controller_by_target(mgr, target); + if (ofconn) { + rconn_set_snoop(ofconn->rconn, mgr->snoop_stream); + return 0; + } + return -1; +} + +/* Clears the snooping of all controller rconns of mgr. */ +static void +connmgr_snoop_clear_all(struct connmgr *mgr) +{ + struct ofconn *ofconn; + + HMAP_FOR_EACH (ofconn, hmap_node, &mgr->controllers) { + rconn_clear_snoop(ofconn->rconn); + } +} + +/* Opens the snoop file for this bridge. */ +int +connmgr_log_open(struct connmgr *mgr, const char *name) +{ + ovs_assert(!mgr->snoop_stream); + return snoop_open(&mgr->snoop_stream, name); +} + +/* Closes the snoop file for this bridge. */ +void +connmgr_log_close(struct connmgr *mgr) +{ + snoop_close(mgr->snoop_stream); + connmgr_snoop_clear_all(mgr); + mgr->snoop_stream = NULL; +} + /* Public ofconn functions. */ diff --git a/ofproto/connmgr.h b/ofproto/connmgr.h index f92a523..1a7a61a 100644 --- a/ofproto/connmgr.h +++ b/ofproto/connmgr.h @@ -92,6 +92,10 @@ void connmgr_reconnect(const struct connmgr *); int connmgr_set_snoops(struct connmgr *, const struct sset *snoops); bool connmgr_has_snoops(const struct connmgr *); void connmgr_get_snoops(const struct connmgr *, struct sset *snoops); +void connmgr_snoop_set_all(struct connmgr *); +int connmgr_snoop_set_one(struct connmgr *, const char *target); +int connmgr_log_open(struct connmgr *, const char *name); +void connmgr_log_close(struct connmgr *); /* Individual connections to OpenFlow controllers. */ enum ofconn_type ofconn_get_type(const struct ofconn *); diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 7425855..f7a4778 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -501,6 +501,9 @@ struct ofproto_dpif { struct ovs_mutex flow_mod_mutex; struct list flow_mods OVS_GUARDED; size_t n_flow_mods OVS_GUARDED; + + /* Controller logging. */ + bool ctrl_logging; }; /* Defer flow mod completion until "ovs-appctl ofproto/unclog"? (Useful only @@ -1318,6 +1321,7 @@ construct(struct ofproto *ofproto_) ofproto->n_hit = 0; ofproto->n_missed = 0; + ofproto->ctrl_logging = false; return error; } @@ -5599,6 +5603,64 @@ ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED, ds_destroy(&ds); } +static void +ofproto_unixctl_log_controller(struct unixctl_conn *conn, int argc, + const char *argv[], void *aux OVS_UNUSED) +{ + struct ofproto_dpif *ofproto; + + ofproto = ofproto_dpif_lookup(argv[1]); + if (!ofproto) { + unixctl_command_reply_error(conn, "Unknown bridge name"); + return; + } + + /* Stops snooping when nothing is specified in the arguments. */ + if (argc == 2) { + connmgr_log_close(ofproto->up.connmgr); + ofproto->ctrl_logging = false; + } else { + int error; + + /* If bridge is currently logging controller, return. */ + if (ofproto->ctrl_logging) { + unixctl_command_reply_error(conn, "Bridge already in logging " + "controllers"); + return; + } + + /* Opens the log file. If name is 'default', use the default path. + * Otherwise, use the specified name. */ + error = connmgr_log_open(ofproto->up.connmgr, + !strcmp(argv[2], "default") + ? NULL : argv[2]); + if (error) { + unixctl_command_reply_error(conn, "Open log file failed"); + return; + } + + /* If nothing is specified, log all controllers. */ + if (argc == 3) { + connmgr_snoop_set_all(ofproto->up.connmgr); + } else { + size_t i; + + /* Log only the specified controllers. If there is a + * incorrect name, clear all configurations. */ + for (i = 3; i < argc; i++) { + if(connmgr_snoop_set_one(ofproto->up.connmgr, argv[i])) { + connmgr_log_close(ofproto->up.connmgr); + unixctl_command_reply_error(conn, "Incorrect controller " + "name, reconfigure needed"); + return; + } + } + } + ofproto->ctrl_logging = true; + } + unixctl_command_reply(conn, NULL); +} + struct trace_ctx { struct xlate_out xout; struct xlate_in xin; @@ -6341,6 +6403,10 @@ ofproto_dpif_unixctl_init(void) ofproto_unixctl_dpif_disable_megaflows, NULL); unixctl_command_register("dpif/enable-megaflows", "", 0, 0, ofproto_unixctl_dpif_enable_megaflows, NULL); + unixctl_command_register("log/controller", + "bridge file|default [controllers]", + 1, INT_MAX, ofproto_unixctl_log_controller, NULL); + } /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) diff --git a/ofproto/ofproto-log-unixctl.man b/ofproto/ofproto-log-unixctl.man new file mode 100644 index 0000000..4357982 --- /dev/null +++ b/ofproto/ofproto-log-unixctl.man @@ -0,0 +1,37 @@ +.SS "LOG COMMANDS" +These commands manage the logging of OpenFlow messages. +. +.IP "\fBofproto/trace\fR \fIbridge\fR \fIfile|default\fR [\fIcontrollers\fR]" +Logs the OpenFlow messages exchanged between \fIbridge\fR and +\fIcontroller(s)\fR into \fIfile\fR. After setting the log, +you must use \fBofproto/trace\fR \fIbridge\fR before reset the +bridge to another set of controllers. The arguments are: +. +.RS +.IP "\fIbridge\fR" +\fIbridge\fR is the name of the target bridge. +. +.IP "\fIfile|default\fR" +\fIfile\fR can be an absolute or relative path of the log file +to be created. If there is already a file with same name, the +OpenFLow messages are appended to the log file. If \fIdefault\fR +is specified, the log file will be named as "ovs-snoop.log" and +be created at same directory as \fBvlog\fR. +. +.IP "Incomplete information." +If nothing is specified after the \fIfile|default\fR, Open vSwitch +will log all OpenFlow messages between \fIbridge\fR and all its +controller(s). Alternatively, you can specify a set of controllers +to log using the [\fIcontrollers\fR]. +.RS +.IP \fIcontrollers\fR +\fIcontrollers\fR can be one or more controller name strings in the +same form as the output of "\fBovs-vsctl get-controller\fR" command. +If there is an incorrect controller name, the command will configure +nothing and report error. +.RE +.RE +.IP "\fBofproto/trace\fR \fIbridge\fR" +Resets the logging of OpenFlow messages exchanged between \fIbridge\fR +and controller(s). After this command, the \fIbridge\fR can be reset +to log another set of controllers. diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index 2c14af6..3b9ecf9 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -2682,3 +2682,56 @@ AT_CHECK([tail -1 stdout], [0], [Datapath actions: 5 ]) OVS_VSWITCHD_STOP AT_CLEANUP + +AT_SETUP([ofproto-dpif - log/controller]) +OVS_VSWITCHD_START() + +# Test the creation of default (ovs-snoop.log) file. +AT_CHECK([ovs-appctl log/controller br0 default], [0]) +AT_CHECK([ls ovs-snoop.log], [0], [dnl +ovs-snoop.log +]) + +# Test the error message of set log/controller when is already in +# logging. +AT_CHECK([ovs-appctl log/controller br0 default], [2], [], [dnl +Bridge already in logging controllers +ovs-appctl: ovs-vswitchd: server returned an error +]) + +# Test the reset of log/controller. +AT_CHECK([ovs-appctl log/controller br0], [0]) + +# Test the creation of specified file (./log_controller.log) file. +AT_CHECK([ovs-appctl log/controller br0 "./log_controller.log"], [0]) +AT_CHECK([ls log_controller.log], [0], [dnl +log_controller.log +]) + +# Test the reset of log/controller. +AT_CHECK([ovs-appctl log/controller br0], [0]) + +# Test the error report of a wrong controller name. +AT_CHECK([ovs-appctl log/controller br0 default tcp:1.2.3.4:8000], [2], [], [dnl +Incorrect controller name, reconfigure needed +ovs-appctl: ovs-vswitchd: server returned an error +]) +# Should still be able to log/controller br0. +AT_CHECK([ovs-appctl log/controller br0 default], [0]) +AT_CHECK([ovs-appctl log/controller br0], [0]) + +# Test the use of incorrect file name. +AT_CHECK([ovs-appctl log/controller br0 "./non_exist_dir/log_controller.log"], [2], [], [dnl +Open log file failed +ovs-appctl: ovs-vswitchd: server returned an error +]) + +# Detect the warning log message +AT_CHECK([sed -n "s/^.*\(|ovs_snoop|WARN|.*\)$/\1/p" ovs-vswitchd.log], [0], [dnl +|ovs_snoop|WARN|failed to open ./non_exist_dir/log_controller.log for snoop logging: No such file or directory +]) +# Delete the warning log message +AT_CHECK([sed "/|ovs_snoop|WARN|/d" ovs-vswitchd.log > ovs-vswitchd.log], [0]) + +OVS_VSWITCHD_STOP +AT_CLEANUP -- 1.7.9.5 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev