Really hope to hear any comment about my implementation,
I'm not sure if this is a good way to do it (e.g using fd stream for file
io).

There is a dilemma between implementing it as ofctl command or appctl
command:

Keith mentioned that he may want to be able to both output the snooped
message to stdout
and write it to a log file. But I couldn't find a way to make both happen.

*If it is implemented as a appctl command,*
pro:
- I can specify the exact set of controllers to snoop,
con:
- but there is no way to write snoop output to stdout,
- and it requires second patch "ofctl parse-ofpraw" to parse the raw
messages

*If it is implemented as a ofctl command,*
pro:
- it can output the snoop to stdout,
- all printed messages are parsed (human readable).
con:
- I haven't found a way to pass arguments (e.g. file name/controller names)
via ofctl command.
  And I don't think ofctl is designed for doing that.

Kind Regards,
Alex Wang,


On Sun, Aug 4, 2013 at 8:14 PM, Alex Wang <al...@nicira.com> wrote:

> 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

Reply via email to