@@ -21,6 +21,7 @@ Post-v2.4.0
targets to run a new system testsuite. These tests can be run inside
a Vagrant box. See INSTALL.md for details
- Dropped support for GRE64 tunnel.
+ - Added --user option to all daemons
v2.4.0 - 20 Aug 2015
@@ -19,6 +19,8 @@
#include "daemon-private.h"
#include <errno.h>
#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
@@ -26,6 +28,9 @@
#include <sys/wait.h>
#include <sys/stat.h>
#include <unistd.h>
+#if HAVE_LIBCAPNG
+#include <cap-ng.h>
+#endif
#include "command-line.h"
#include "fatal-signal.h"
#include "dirs.h"
@@ -39,6 +44,18 @@
VLOG_DEFINE_THIS_MODULE(daemon_unix);
+#ifdef __linux__
+#define LINUX 1
+#else
+#define LINUX 0
+#endif
+
+#if HAVE_LIBCAPNG
+#define LIBCAPNG 1
+#else
+#define LIBCAPNG 0
+#endif
+
/* --detach: Should we run in the background? */
bool detach; /* Was --detach specified? */
static bool detached; /* Have we already detached? */
@@ -64,6 +81,15 @@ static int daemonize_fd = -1;
* it dies due to an error signal? */
static bool monitor;
+/* --user: Only root can use this option. Switch to new uid:gid after
+ * initially running as root. */
+static bool switch_user = false;
+static bool non_root_user = false;
+static uid_t uid;
+static gid_t gid;
+static char *user = NULL;
+static void daemon_become_new_user__(bool access_datapath);
+
static void check_already_running(void);
static int lock_pidfile(FILE *, int command);
static pid_t fork_and_clean_up(void);
@@ -409,11 +435,21 @@ monitor_daemon(pid_t daemon_pid)
* daemon_complete()) or that it failed to start up (by exiting with a nonzero
* exit code). */
void
-daemonize_start(void)
+daemonize_start(bool access_datapath)
{
assert_single_threaded();
daemonize_fd = -1;
+ if (switch_user) {
+ daemon_become_new_user__(access_datapath);
+ switch_user = false;
+ }
+
+ /* If --user is specified, make sure user switch has completed by now. */
+ if (non_root_user) {
+ ovs_assert(geteuid() && getuid());
+ }
+
if (detach) {
pid_t pid;
@@ -645,7 +681,7 @@ error:
}
/* Opens and reads a PID from 'pidfile'. Returns the positive PID if
- * successful, otherwise a negative errno value. */
+ *successful, otherwise a negative errno value. */
pid_t
read_pidfile(const char *pidfile)
{
@@ -684,3 +720,324 @@ should_service_stop(void)
{
return false;
}
+
+
+static bool
+gid_matches(gid_t expected, gid_t value)
+{
+ return expected == -1 || expected == value;
+}
+
+static bool
+gid_verify(gid_t real, gid_t effective, gid_t saved)
+{
+ gid_t r, e, s;
+
+ return (getresgid(&r, &e, &s) == 0 &&
+ gid_matches(real, r) &&
+ gid_matches(effective, e) &&
+ gid_matches(saved, s));
+}
+
+static void
+daemon_switch_group(gid_t real, gid_t effective,
+ gid_t saved)
+{
+ if ((setresgid(real, effective, saved) == -1) ||
+ !gid_verify(real, effective, saved)) {
+ VLOG_FATAL("%s: fail to switch group to gid as %d, aborting",
+ pidfile, gid);
+ }
+}
+
+static bool
+uid_matches(uid_t expected, uid_t value)
+{
+ return expected == -1 || expected == value;
+}
+
+static bool
+uid_verify(const uid_t real, const uid_t effective, const uid_t saved)
+{
+ uid_t r, e, s;
+
+ return (getresuid(&r, &e, &s) == 0 &&
+ uid_matches(real, r) &&
+ uid_matches(effective, e) &&
+ uid_matches(saved, s));
+}
+
+static void
+daemon_switch_user(const uid_t real, const uid_t effective, const uid_t saved,
+ const char *user)
+{
+ if ((setresuid(real, effective, saved) == -1) ||
+ !uid_verify(real, effective, saved)) {
+ VLOG_FATAL("%s: fail to switch user to %s, aborting",
+ pidfile, user);
+ }
+}
+
+/* Use portable Unix APIs to switch uid:gid, when datapath
+ * access is not required. On Linux systems, all capabilities
+ * will be dropped. */
+static void
+daemon_become_new_user_unix(void)
+{
+ /* "Setuid Demystified" by Hao Chen, etc outlines some caveats of
+ * around unix system call setuid() and friends. This implementation
+ * mostly follow the advice given by the paper. The paper is
+ * published in 2002, so things could have changed. */
+
+ /* Change both real and effective uid and gid will permanently
+ * drop the process' privilege. "Setuid Demystified" suggested
+ * that calling getuid() after each setuid() call to verify they
+ * are actually set, because checking return code alone is not
+ * sufficient. */
+ daemon_switch_group(gid, gid, gid);
+ if (user && initgroups(user, gid) == -1) {
+ VLOG_FATAL("%s: fail to add supplementary group gid %d, "
+ "aborting", pidfile, gid);
+ }
+ daemon_switch_user(uid, uid, uid, user);
+}
+
+/* Linux specific implementation of daemon_become_new_user()
+ * using libcap-ng. */
+#if defined __linux__ && HAVE_LIBCAPNG
+static void
+daemon_become_new_user_linux(bool access_datapath)
+{
+ int ret;
+
+ ret = capng_get_caps_process();
+
+ if (!ret) {
+ if (capng_have_capabilities(CAPNG_SELECT_CAPS) > CAPNG_NONE) {
+ unsigned int cap = CAP_IPC_LOCK | CAP_NET_BIND_SERVICE;
+
+ capng_clear(CAPNG_SELECT_BOTH);
+ if (access_datapath) {
+ cap |= CAP_NET_ADMIN | CAP_NET_RAW;
+ }
+ ret = capng_update(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED,
+ cap);
+ } else {
+ ret = -1;
+ }
+ }
+
+ if (!ret) {
+ /* CAPNG_INIT_SUPP_GRP will be a better choice than
+ * CAPNG_DROP_SUPP_GRP. However this enum value is only defined
+ * with libcap-ng higher than version 0.7.4, which is not wildly
+ * available on many Linux distributions yet. Taking a more
+ * conservative approach to make sure OVS behaves consistently.
+ *
+ * XXX We may change this for future OVS releases.
+ */
+ ret = capng_change_id(uid, gid, CAPNG_DROP_SUPP_GRP
+ | CAPNG_CLEAR_BOUNDING);
+ }
+
+ if (ret) {
+ VLOG_FATAL("%s: libcap-ng fail to switch to user and group "
+ "%d:%d, aborting", pidfile, uid, gid);
+ }
+}
+#endif
+
+static void
+daemon_become_new_user__(bool access_datapath)
+{
+ if (LINUX) {
+ if (LIBCAPNG) {
+ daemon_become_new_user_linux(access_datapath);
+ } else {
+ VLOG_FATAL("%s: fail to downgrade user using libcap-ng. "
+ "(libcap-ng is not configured at compile time), "
+ "aborting.", pidfile);
+ }
+ } else {
+ ovs_assert(!access_datapath);
+ daemon_become_new_user_unix();
+ }
+}
+
+/* Noramlly, user switch is embedded within daemonize_start().
+ * However, there in case the user switch needs to be done
+ * before daemonize_start(), the following API can be used. */
+void
+daemon_become_new_user(bool access_datapath)
+{
+ assert_single_threaded();
+ if (switch_user) {
+ daemon_become_new_user__(access_datapath);
+
+ /* Make sure daemonize_start() will not switch
+ * user again. */
+ switch_user = false;
+ }
+}
+
+/* Return the maximun suggested buffer size for both getpwname_r()
+ * and getgrnam_r().
+ *
+ * This size may still not be big enough. In case getpwname_r()
+ * and friends return ERANGE, a larger buffer should be supplied to
+ * retry. (The man page did not specify the max size to stop at, we
+ * will keep trying with doubling the buffer size for each round until
+ * the size wraps around size_t. */
+static size_t
+get_sysconf_buffer_size(void)
+{
+ size_t bufsize, pwd_bs = 0, grp_bs = 0;
+ const size_t default_bufsize = 1024;
+
+ errno = 0;
+ if ((pwd_bs = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) {
+ if (errno) {
+ VLOG_FATAL("%s: Read initial passwordd struct size "
+ "failed (%s), aborting. ", pidfile,
+ ovs_strerror(errno));
+ }
+ }
+
+ if ((grp_bs = sysconf(_SC_GETGR_R_SIZE_MAX)) == -1) {
+ if (errno) {
+ VLOG_FATAL("%s: Read initial group struct size "
+ "failed (%s), aborting. ", pidfile,
+ ovs_strerror(errno));
+ }
+ }
+
+ bufsize = MAX(pwd_bs, grp_bs);
+ return bufsize ? bufsize : default_bufsize;
+}
+
+/* Try to double the size of '*buf', return true
+ * if successful, and '*sizep' will be updated with
+ * the new size. Otherwise, return false. */
+static bool
+enlarge_buffer(char **buf, size_t *sizep)
+{
+ size_t newsize = *sizep * 2;
+
+ if (newsize > *sizep) {
+ *buf = xrealloc(*buf, newsize);
+ *sizep = newsize;
+ return true;
+ }
+
+ return false;
+}
+
+/* Parse and sanity check user_spec.
+ *
+ * If successful, set global variables 'uid' and 'gid'
+ * with the parsed results. Global variable 'user'
+ * will be pointing to a string that stores the name
+ * of the user to be switched into.
+ *
+ * Also set 'switch_to_new_user' to true, The actual
+ * user switching is done as soon as daemonize_start()
+ * is called. I/O access before calling daemonize_start()
+ * will still be with root's credential. */
+void
+daemon_set_new_user(const char *user_spec)
+{
+ char *pos = strchr(user_spec, ':');
+ size_t init_bufsize, bufsize;
+
+ init_bufsize = get_sysconf_buffer_size();
+ uid = getuid();
+ gid = getgid();
+
+ if (geteuid() || uid) {
+ VLOG_FATAL("%s: only root can use --user option", pidfile);
+ }
+
+ user_spec += strspn(user_spec, " \t\r\n");
+ size_t len = pos ? pos - user_spec : strlen(user_spec);
+ char *buf;
+ struct passwd pwd, *res;
+ int e;
+
+ bufsize = init_bufsize;
+ buf = xmalloc(bufsize);
+ if (len) {
+ user = xmemdup0(user_spec, len);
+
+ while ((e = getpwnam_r(user, &pwd, buf, bufsize, &res)) == ERANGE) {
+ if (!enlarge_buffer(&buf, &bufsize)) {
+ break;
+ }
+ }
+
+ if (e != 0) {
+ VLOG_FATAL("%s: Failed to retrive user %s's uid (%s), aborting.",
+ pidfile, user, ovs_strerror(e));
+ }
+ } else {
+ /* User name is not specified, use current user. */
+ while ((e = getpwuid_r(uid, &pwd, buf, bufsize, &res)) == ERANGE) {
+ if (!enlarge_buffer(&buf, &bufsize)) {
+ break;
+ }
+ }
+
+ if (e != 0) {
+ VLOG_FATAL("%s: Failed to retrive current user's name "
+ "(%s), aborting.", pidfile, ovs_strerror(e));
+ }
+ user = xstrdup(pwd.pw_name);
+ }
+
+ uid = pwd.pw_uid;
+ gid = pwd.pw_gid;
+ free(buf);
+
+ if (pos) {
+ char *grpstr = pos + 1;
+ grpstr += strspn(grpstr, " \t\r\n");
+
+ if (*grpstr) {
+ struct group grp, *res;
+
+ bufsize = init_bufsize;
+ buf = xmalloc(bufsize);
+ while ((e = getgrnam_r(grpstr, &grp, buf, bufsize, &res))
+ == ERANGE) {
+ if (!enlarge_buffer(&buf, &bufsize)) {
+ break;
+ }
+ }
+
+ if (e) {
+ VLOG_FATAL("%s: Failed to get group entry for %s, "
+ "(%s), aborting.", pidfile, grpstr,
+ ovs_strerror(e));
+ }
+
+ if (gid != grp.gr_gid) {
+ char **mem;
+
+ for (mem = grp.gr_mem; *mem; ++mem) {
+ if (!strcmp(*mem, user)) {
+ break;
+ }
+ }
+
+ if (!*mem) {
+ VLOG_FATAL("%s: Invalid --user option %s (user %s is "
+ "not in group %s), aborting.", pidfile,
+ user_spec, user, grpstr);
+ }
+ gid = grp.gr_gid;
+ }
+ free(buf);
+ }
+ }
+
+ switch_user = non_root_user = true;
+}
@@ -446,7 +446,8 @@ make_pidfile(void)
/* Don't close the pidfile till the process exits. */
}
-void daemonize_start(void)
+void
+daemonize_start(bool access_datapath OVS_UNUSED)
{
if (pidfile) {
make_pidfile();
@@ -473,6 +474,11 @@ daemonize_complete(void)
service_complete();
}
+void
+dameon_become_new_user(bool access_datapath OVS_UNUSED)
+{
+}
+
/* Returns the file name that would be used for a pidfile if 'name' were
* provided to set_pidfile(). The caller must free the returned string. */
char *
@@ -484,3 +490,9 @@ make_pidfile_name(const char *name)
return xasprintf("%s/%s.pid", ovs_rundir(), program_name);
}
}
+
+void
+daemon_set_new_user(const char *user_spec OVS_UNUSED)
+{
+ VLOG_FATAL("--user options is not currently supported.");
+}
@@ -41,7 +41,7 @@ get_detach(void)
void
daemonize(void)
{
- daemonize_start();
+ daemonize_start(false);
daemonize_complete();
}
@@ -42,14 +42,16 @@
OPT_NO_CHDIR, \
OPT_OVERWRITE_PIDFILE, \
OPT_PIDFILE, \
- OPT_MONITOR
+ OPT_MONITOR, \
+ OPT_USER_GROUP
-#define DAEMON_LONG_OPTIONS \
- {"detach", no_argument, NULL, OPT_DETACH}, \
- {"no-chdir", no_argument, NULL, OPT_NO_CHDIR}, \
- {"pidfile", optional_argument, NULL, OPT_PIDFILE}, \
+#define DAEMON_LONG_OPTIONS \
+ {"detach", no_argument, NULL, OPT_DETACH}, \
+ {"no-chdir", no_argument, NULL, OPT_NO_CHDIR}, \
+ {"pidfile", optional_argument, NULL, OPT_PIDFILE}, \
{"overwrite-pidfile", no_argument, NULL, OPT_OVERWRITE_PIDFILE}, \
- {"monitor", no_argument, NULL, OPT_MONITOR}
+ {"monitor", no_argument, NULL, OPT_MONITOR}, \
+ {"user", required_argument, NULL, OPT_USER_GROUP}
#define DAEMON_OPTION_HANDLERS \
case OPT_DETACH: \
@@ -70,6 +72,10 @@
\
case OPT_MONITOR: \
daemon_set_monitor(); \
+ break; \
+ \
+ case OPT_USER_GROUP: \
+ daemon_set_new_user(optarg); \
break;
void set_detach(void);
@@ -84,7 +90,8 @@ pid_t read_pidfile(const char *name);
OPT_PIDFILE, \
OPT_PIPE_HANDLE, \
OPT_SERVICE, \
- OPT_SERVICE_MONITOR
+ OPT_SERVICE_MONITOR \
+ OPT_USER_GROUP \
#define DAEMON_LONG_OPTIONS \
{"detach", no_argument, NULL, OPT_DETACH}, \
@@ -92,7 +99,8 @@ pid_t read_pidfile(const char *name);
{"pidfile", optional_argument, NULL, OPT_PIDFILE}, \
{"pipe-handle", required_argument, NULL, OPT_PIPE_HANDLE}, \
{"service", no_argument, NULL, OPT_SERVICE}, \
- {"service-monitor", no_argument, NULL, OPT_SERVICE_MONITOR}
+ {"service-monitor", no_argument, NULL, OPT_SERVICE_MONITOR} \
+ {"user", required_argument, NULL, OPT_USER_GROUP}
#define DAEMON_OPTION_HANDLERS \
case OPT_DETACH: \
@@ -113,7 +121,10 @@ pid_t read_pidfile(const char *name);
break; \
\
case OPT_SERVICE_MONITOR: \
- break;
+ break; \
+ \
+ case OPT_USER_GROUP: \
+ daemon_set_new_user(optarg); \
void control_handler(DWORD request);
void set_pipe_handle(const char *pipe_handle);
@@ -122,8 +133,10 @@ void set_pipe_handle(const char *pipe_handle);
bool get_detach(void);
void daemon_save_fd(int fd);
void daemonize(void);
-void daemonize_start(void);
+void daemonize_start(bool access_datapath);
void daemonize_complete(void);
+void daemon_set_new_user(const char * user_spec);
+void daemon_become_new_user(bool access_datapath);
void daemon_usage(void);
void service_start(int *argcp, char **argvp[]);
void service_stop(void);
@@ -50,3 +50,18 @@ core dumps into the current working directory and the root directory
is not a good directory to use.
.IP
This option has no effect when \fB\-\-detach\fR is not specified.
+.
+.TP
+\fB\-\-user\fR
+Causes \fB\*(PN\fR to run as a non root user specified in "user:group", thus
+dropping all root privileges. Short forms "user" and ":group" are also
+allowed, with current user or group are assumed respectively. Only daemons
+started by the root user accepts this argument.
+.IP
+On Linux, daemons will be granted CAP_IPC_LOCK and CAP_NET_BIND_SERVICES
+before dropping root privileges. Daemons interact with datapath,
+such as ovs-vswitchd, will be granted two additional capabilities, namely
+CAP_NET_ADMIN and CAP_NET_RAW.
+.IP
+On Windows, this option is not currently supported. For security reasons,
+Specifying this option will cause the daemon process not to start.
@@ -63,7 +63,7 @@ main(int argc, char *argv[])
parse_options(argc, argv);
fatal_ignore_sigpipe();
- daemonize_start();
+ daemonize_start(false);
retval = unixctl_server_create(NULL, &unixctl);
if (retval) {
@@ -386,7 +386,7 @@ main(int argc, char *argv[])
parse_options(argc, argv);
fatal_ignore_sigpipe();
- daemonize_start();
+ daemonize_start(false);
retval = unixctl_server_create(NULL, &unixctl);
if (retval) {
@@ -1167,7 +1167,7 @@ main(int argc, char *argv[])
vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
parse_options(argc, argv);
- daemonize_start();
+ daemonize_start(false);
retval = unixctl_server_create(NULL, &unixctl);
if (retval) {
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -91,6 +91,7 @@ main(int argc, char *argv[])
parse_options(argc, argv);
fatal_ignore_sigpipe();
+ daemon_become_new_user(false);
if (optind >= argc) {
ovs_fatal(0, "missing command name; use --help for help");
}
@@ -791,7 +792,7 @@ do_monitor(struct jsonrpc *rpc, const char *database,
size_t n_mts, allocated_mts;
daemon_save_fd(STDOUT_FILENO);
- daemonize_start();
+ daemonize_start(false);
if (get_detach()) {
int error;
@@ -221,6 +221,7 @@ main(int argc, char *argv[])
process_init();
parse_options(&argc, &argv, &remotes, &unixctl_path, &run_command);
+ daemon_become_new_user(false);
/* Create and initialize 'config_tmpfile' as a temporary file to hold
* ovsdb-server's most basic configuration, and then save our initial
@@ -248,7 +249,7 @@ main(int argc, char *argv[])
save_config__(config_tmpfile, &remotes, &db_filenames);
- daemonize_start();
+ daemonize_start(false);
/* Load the saved config. */
load_config(config_tmpfile, &remotes, &db_filenames);
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -195,7 +195,7 @@ test_netflow_main(int argc, char *argv[])
}
daemon_save_fd(STDOUT_FILENO);
- daemonize_start();
+ daemonize_start(false);
error = unixctl_server_create(NULL, &server);
if (error) {
@@ -683,7 +683,7 @@ test_sflow_main(int argc, char *argv[])
}
daemon_save_fd(STDOUT_FILENO);
- daemonize_start();
+ daemonize_start(false);
error = unixctl_server_create(NULL, &server);
if (error) {
@@ -130,6 +130,8 @@ main(int argc, char *argv[])
fatal_ignore_sigpipe();
ctx.argc = argc - optind;
ctx.argv = argv + optind;
+
+ daemon_become_new_user(false);
ovs_cmdl_run_command(&ctx, get_all_commands());
return 0;
}
@@ -1611,7 +1613,7 @@ monitor_vconn(struct vconn *vconn, bool reply_to_echo_requests)
int error;
daemon_save_fd(STDERR_FILENO);
- daemonize_start();
+ daemonize_start(false);
error = unixctl_server_create(unixctl_path, &server);
if (error) {
ovs_fatal(error, "failed to create unixctl server");
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -108,6 +108,8 @@ main(int argc, char *argv[])
parse_options(argc, argv);
fatal_ignore_sigpipe();
+ daemon_become_new_user(false);
+
if (argc - optind < 1) {
ovs_fatal(0, "at least one vconn argument required; "
"use --help for usage");
@@ -145,7 +147,7 @@ main(int argc, char *argv[])
ovs_fatal(0, "no active or passive switch connections");
}
- daemonize_start();
+ daemonize_start(false);
retval = unixctl_server_create(unixctl_path, &unixctl);
if (retval) {
@@ -69,6 +69,7 @@ main(int argc, char *argv[])
char *remote;
bool exiting;
int retval;
+ bool access_datapath;
set_program_name(argv[0]);
retval = dpdk_init(argc,argv);
@@ -85,7 +86,12 @@ main(int argc, char *argv[])
fatal_ignore_sigpipe();
ovsrec_init();
- daemonize_start();
+#ifdef __linux__
+ access_datapath = true;
+#else
+ access_datapath = false;
+#endif
+ daemonize_start(access_datapath);
if (want_mlockall) {
#ifdef HAVE_MLOCKALL
OVS daemons can now support --user option to run as a non-root user with less privileges. See the manpage patch for more descriptions. Signed-off-by: Andy Zhou <azhou@nicira.com> ---- v3->v4: calling daemon_become_new_user() from daemonize_start(), so the API works will for most daemons. In case a daemon processe needs to switch user prior to calling daemonize_start(), it daemon_become_new_user() can be called directly. Added check in daemonize_start() to make sure --user option has been executed if specified. On linux, libcap-ng is used exclusively for executing the --user option. On Windows, --user option will cause the process to die with an error message. All daemons now accepts --user option. --- NEWS | 1 + lib/daemon-unix.c | 361 +++++++++++++++++++++++++++++- lib/daemon-windows.c | 14 +- lib/daemon.c | 2 +- lib/daemon.h | 33 ++- lib/daemon.man | 15 ++ ovn/controller-vtep/ovn-controller-vtep.c | 2 +- ovn/controller/ovn-controller.c | 2 +- ovn/northd/ovn-northd.c | 2 +- ovsdb/ovsdb-client.c | 5 +- ovsdb/ovsdb-server.c | 3 +- tests/test-jsonrpc.c | 2 +- tests/test-netflow.c | 4 +- tests/test-sflow.c | 2 +- utilities/ovs-ofctl.c | 4 +- utilities/ovs-testcontroller.c | 6 +- vswitchd/ovs-vswitchd.c | 8 +- 17 files changed, 438 insertions(+), 28 deletions(-)