Rename brcompatd to ovs-brcompatd, for consistency.
authorBen Pfaff <blp@nicira.com>
Sat, 16 May 2009 00:19:35 +0000 (17:19 -0700)
committerBen Pfaff <blp@nicira.com>
Thu, 21 May 2009 19:47:24 +0000 (12:47 -0700)
14 files changed:
INSTALL
datapath/brcompat.c
vswitchd/.gitignore
vswitchd/automake.mk
vswitchd/brcompatd.8.in [deleted file]
vswitchd/brcompatd.c [deleted file]
vswitchd/ovs-brcompatd.8.in [new file with mode: 0644]
vswitchd/ovs-brcompatd.c [new file with mode: 0644]
vswitchd/vswitchd.conf.5.in
xenserver/etc_init.d_vswitch
xenserver/etc_init.d_vswitch-xapi-update
xenserver/etc_sysconfig_vswitch.example
xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py
xenserver/vswitch-xen.spec

diff --git a/INSTALL b/INSTALL
index e6dde2e749d8ee162d0ccc0ee239797ae3659e52..68efbdb0cba7b906f58188991d008e783c9207de 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -148,7 +148,7 @@ distribution in the ordinary way using "configure" and "make".
 
       - Virtual switch daemon: vswitchd/vswitchd
 
-      - Bridge compatibility daemon: vswitchd/brcompatd
+      - Bridge compatibility daemon: vswitchd/ovs-brcompatd
 
       - Datapath administration utility: utilities/ovs-dpctl.
 
index 4175ad1cc1cf898ca53cdae5bb98e34e83d03359..4cbce7812cde881d4ac52cac5ac008bf199bc96f 100644 (file)
@@ -22,7 +22,7 @@ static struct genl_multicast_group brc_mc_group;
 /* Time to wait for vswitchd to respond to a datapath action, in jiffies. */
 #define BRC_TIMEOUT (HZ * 5)
 
-/* Mutex to serialize brcompatd callbacks.  (Some callbacks naturally hold
+/* Mutex to serialize ovs-brcompatd callbacks.  (Some callbacks naturally hold
  * br_ioctl_mutex, others hold rtnl_lock, but we can't take the former
  * ourselves and we don't want to hold the latter over a potentially long
  * period of time.) */
index 3c5880b3fa912f6bb43c221e87498fb97f25f108..27233bcc1d05e0fdfc6ace97d4d4b192ec8deeb4 100644 (file)
@@ -1,7 +1,7 @@
 /Makefile
 /Makefile.in
-/brcompatd
-/brcompatd.8
+/ovs-brcompatd
+/ovs-brcompatd.8
 /vswitchd
 /vswitchd.8
 /vswitchd.conf.5
index e16d9578d1c0261e10a0cbf59fb03eb4649fb1bc..71516b4a93e3fb40b4dac1d24f74cea7d01a65ca 100644 (file)
@@ -1,10 +1,10 @@
-sbin_PROGRAMS += vswitchd/vswitchd vswitchd/brcompatd
+sbin_PROGRAMS += vswitchd/vswitchd vswitchd/ovs-brcompatd
 man_MANS += vswitchd/vswitchd.conf.5 \
        vswitchd/vswitchd.8 \
-       vswitchd/brcompatd.8
+       vswitchd/ovs-brcompatd.8
 DISTCLEANFILES += vswitchd/vswitchd.conf.5 \
     vswitchd/vswitchd.8 \
-    vswitchd/brcompatd.8
+    vswitchd/ovs-brcompatd.8
 
 vswitchd_vswitchd_SOURCES = \
        vswitchd/bridge.c \
@@ -25,13 +25,13 @@ vswitchd_vswitchd_LDADD = \
        $(FAULT_LIBS) \
        $(SSL_LIBS)
 
-vswitchd_brcompatd_SOURCES = \
-       vswitchd/brcompatd.c
+vswitchd_ovs_brcompatd_SOURCES = \
+       vswitchd/ovs-brcompatd.c
 
-vswitchd_brcompatd_LDADD = \
+vswitchd_ovs_brcompatd_LDADD = \
        lib/libopenvswitch.a \
        $(FAULT_LIBS) 
 
 EXTRA_DIST += vswitchd/vswitchd.conf.5.in \
        vswitchd/vswitchd.8.in \
-       vswitchd/brcompatd.8.in
+       vswitchd/ovs-brcompatd.8.in
diff --git a/vswitchd/brcompatd.8.in b/vswitchd/brcompatd.8.in
deleted file mode 100644 (file)
index 8f47525..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-.TH brcompatd 8 "March 2009" "Open vSwitch" "Open vSwitch Manual"
-.ds PN brcompatd
-.
-.SH NAME
-brcompatd \- Bridge compatibility front-end for vswitchd
-.
-.SH SYNOPSIS
-.B brcompatd
-[\fIoptions\fR] \fIconfig\fR
-.
-.SH DESCRIPTION
-A daemon that provides a legacy bridge front-end for \fBvswitchd\fR.  It 
-does this by listening for bridge ioctl commands (e.g., those generated by 
-the \fBbrctl\fR program) to add or remove datapaths and the interfaces 
-that attach to them.  It modifies \fIconfig\fR and forces
-\fBvswitchd\fR to reload its configuration file.
-.PP
-.SH OPTIONS
-.IP "\fB--reload-command=\fIcommand\fR"
-Sets the command that \fBbrcompatd\fR runs to force \fBvswitchd\fR to
-reload its configuration file to \fIcommand\fR.  The command is run in
-a subshell, so it may contain arbitrary shell metacharacters, etc.
-The \fB--help\fR option displays the default reload command.
-.TP
-\fB--prune-timeout=\fIsecs\fR
-.
-Sets the maximum time between pruning port entries to \fIsecs\fR seconds.
-Pruning ports is the process of removing port entries from \fIconfig\fR 
-that no longer exist.  If \fIsecs\fR is zero, then entries are never
-pruned.  The default prune timeout is 5 seconds.
-.TP
-\fB--lock-timeout=\fImsecs\fR
-.
-Sets the maximum time to wait for \fIconfig\fR to become unlocked to 
-\fImsecs\fR milliseconds.  The default lock timeout is 500 milliseconds.
-.
-.so lib/daemon.man
-.so lib/vlog.man
-.so lib/common.man
-.so lib/leak-checker.man
-.
-.SH NOTES
-\fBbrcompatd\fR requires the \fBbrcompat_mod.ko\fR kernel module to be
-loaded.
-.SH "SEE ALSO"
-.BR ovs-appctl (8),
-.BR vswitchd (8),
-.BR vswitchd.conf (5),
-\fBINSTALL\fR in the Open vSwitch distribution.
diff --git a/vswitchd/brcompatd.c b/vswitchd/brcompatd.c
deleted file mode 100644 (file)
index fbd063d..0000000
+++ /dev/null
@@ -1,770 +0,0 @@
-/* Copyright (c) 2008, 2009  Nicira Networks
- * 
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <config.h>
-
-#include <assert.h>
-#include <errno.h>
-#include <getopt.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <net/if.h>
-#include <linux/genetlink.h>
-#include <linux/rtnetlink.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "cfg.h"
-#include "command-line.h"
-#include "daemon.h"
-#include "dirs.h"
-#include "dpif.h"
-#include "fatal-signal.h"
-#include "fault.h"
-#include "leak-checker.h"
-#include "netdev.h"
-#include "netlink.h"
-#include "ofpbuf.h"
-#include "openvswitch/brcompat-netlink.h"
-#include "poll-loop.h"
-#include "process.h"
-#include "signals.h"
-#include "svec.h"
-#include "timeval.h"
-#include "unixctl.h"
-#include "util.h"
-
-#include "vlog.h"
-#define THIS_MODULE VLM_brcompatd
-
-
-/* xxx Just hangs if datapath is rmmod/insmod.  Learn to reconnect? */
-
-/* Actions to modify bridge compatibility configuration. */
-enum bmc_action {
-    BMC_ADD_DP,
-    BMC_DEL_DP,
-    BMC_ADD_PORT,
-    BMC_DEL_PORT
-};
-
-static void release_lock(void *aux UNUSED);
-static void parse_options(int argc, char *argv[]);
-static void usage(void) NO_RETURN;
-
-static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 60);
-
-/* Maximum number of milliseconds to wait for the config file to be
- * unlocked.  If set to zero, no waiting will occur. */
-static int lock_timeout = 500;
-
-/* Maximum number of milliseconds to wait before pruning port entries that 
- * no longer exist.  If set to zero, ports are never pruned. */
-static int prune_timeout = 5000;
-
-/* Config file shared with vswitchd (usually vswitchd.conf). */
-static char *config_file;
-
-/* Command to run (via system()) to reload the vswitchd configuration file. */
-static char *reload_command;
-
-/* Netlink socket to listen for interface changes. */
-static struct nl_sock *rtnl_sock;
-
-/* Netlink socket to bridge compatibility kernel module. */
-static struct nl_sock *brc_sock;
-
-/* The Generic Netlink family number used for bridge compatibility. */
-static int brc_family;
-
-static const struct nl_policy brc_multicast_policy[] = {
-    [BRC_GENL_A_MC_GROUP] = {.type = NL_A_U32 }
-};
-
-static const struct nl_policy rtnlgrp_link_policy[] = {
-    [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false },
-    [IFLA_MASTER] = { .type = NL_A_U32, .optional = true },
-};
-
-static int
-lookup_brc_multicast_group(int *multicast_group)
-{
-    struct nl_sock *sock;
-    struct ofpbuf request, *reply;
-    struct nlattr *attrs[ARRAY_SIZE(brc_multicast_policy)];
-    int retval;
-
-    retval = nl_sock_create(NETLINK_GENERIC, 0, 0, 0, &sock);
-    if (retval) {
-        return retval;
-    }
-    ofpbuf_init(&request, 0);
-    nl_msg_put_genlmsghdr(&request, sock, 0, brc_family,
-            NLM_F_REQUEST, BRC_GENL_C_QUERY_MC, 1);
-    retval = nl_sock_transact(sock, &request, &reply);
-    ofpbuf_uninit(&request);
-    if (retval) {
-        nl_sock_destroy(sock);
-        return retval;
-    }
-    if (!nl_policy_parse(reply, NLMSG_HDRLEN + GENL_HDRLEN,
-                         brc_multicast_policy, attrs,
-                         ARRAY_SIZE(brc_multicast_policy))) {
-        nl_sock_destroy(sock);
-        ofpbuf_delete(reply);
-        return EPROTO;
-    }
-    *multicast_group = nl_attr_get_u32(attrs[BRC_GENL_A_MC_GROUP]);
-    nl_sock_destroy(sock);
-    ofpbuf_delete(reply);
-
-    return 0;
-}
-
-/* Opens a socket for brcompat notifications.  Returns 0 if successful,
- * otherwise a positive errno value. */
-static int
-brc_open(struct nl_sock **sock)
-{
-    int multicast_group = 0;
-    int retval;
-
-    retval = nl_lookup_genl_family(BRC_GENL_FAMILY_NAME, &brc_family);
-    if (retval) {
-        return retval;
-    }
-
-    retval = lookup_brc_multicast_group(&multicast_group);
-    if (retval) {
-        return retval;
-    }
-
-    retval = nl_sock_create(NETLINK_GENERIC, multicast_group, 0, 0, sock);
-    if (retval) {
-        return retval;
-    }
-
-    return 0;
-}
-
-static const struct nl_policy brc_dp_policy[] = {
-    [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING },
-};
-
-static bool
-bridge_exists(const char *name)
-{
-    return cfg_has_section("bridge.%s", name);
-}
-
-static int
-rewrite_and_reload_config(void)
-{
-    if (cfg_is_dirty()) {
-        int error1 = cfg_write();
-        int error2 = cfg_read();
-        int error3 = system(reload_command);
-        if (error3 == -1) {
-            VLOG_ERR_RL(&rl, "failed to execute reload command: %s",
-                        strerror(errno));
-        } else if (error3 != 0) {
-            char *msg = process_status_msg(error3);
-            VLOG_ERR_RL(&rl, "reload command exited with error (%s)", msg);
-            free(msg);
-        }
-        return error1 ? error1 : error2 ? error2 : error3 ? ECHILD : 0;
-    }
-    return 0;
-}
-
-/* Go through the configuration file and remove any ports that no longer
- * exist associated with a bridge. */
-static void
-prune_ports(void)
-{
-    int i, j;
-    int error;
-    struct svec bridges, delete;
-
-    if (cfg_lock(NULL, 0)) {
-        /* Couldn't lock config file. */
-        return;
-    }
-
-    svec_init(&bridges);
-    svec_init(&delete);
-    cfg_get_subsections(&bridges, "bridge");
-    for (i=0; i<bridges.n; i++) {
-        const char *br_name = bridges.names[i];
-        struct svec ports, ifaces;
-
-        svec_init(&ports);
-
-        /* Get all the interfaces for the given bridge, breaking bonded
-         * interfaces down into their constituent parts. */
-        svec_init(&ifaces);
-        cfg_get_all_keys(&ports, "bridge.%s.port", br_name);
-        for (j=0; j<ports.n; j++) {
-            const char *port_name = ports.names[j];
-            if (cfg_has_section("bonding.%s", port_name)) {
-                struct svec slaves;
-                svec_init(&slaves);
-                cfg_get_all_keys(&slaves, "bonding.%s.slave", port_name);
-                svec_append(&ifaces, &slaves);
-                svec_destroy(&slaves);
-            } else {
-                svec_add(&ifaces, port_name);
-            }
-        }
-        svec_destroy(&ports);
-
-        /* Check that the interfaces exist. */
-        for (j = 0; j < ifaces.n; j++) {
-            const char *iface_name = ifaces.names[j];
-            enum netdev_flags flags;
-
-            /* The local port and internal ports are created and destroyed by
-             * vswitchd itself, so don't bother checking for them at all.  In
-             * practice, they might not exist if vswitchd hasn't finished
-             * reloading since the configuration file was updated. */
-            if (!strcmp(iface_name, br_name)
-                || cfg_get_bool(0, "iface.%s.internal", iface_name)) {
-                continue;
-            }
-
-            error = netdev_nodev_get_flags(iface_name, &flags);
-            if (error == ENODEV) {
-                VLOG_DBG_RL(&rl, "removing dead interface %s from %s", 
-                        iface_name, br_name);
-                svec_add(&delete, iface_name);
-            } else if (error) {
-                VLOG_DBG_RL(&rl, "unknown error %d on interface %s from %s", 
-                        error, iface_name, br_name);
-            }
-        }
-        svec_destroy(&ifaces);
-    }
-    svec_destroy(&bridges);
-
-    if (delete.n) {
-        size_t i;
-
-        for (i = 0; i < delete.n; i++) {
-            cfg_del_match("bridge.*.port=%s", delete.names[i]);
-            cfg_del_match("bonding.*.slave=%s", delete.names[i]);
-        }
-        rewrite_and_reload_config();
-        cfg_unlock();
-    } else {
-        cfg_unlock();
-    }
-    svec_destroy(&delete);
-}
-
-
-/* Checks whether a network device named 'name' exists and returns true if so,
- * false otherwise.
- *
- * XXX it is possible that this doesn't entirely accomplish what we want in
- * context, since vswitchd.conf may cause vswitchd to create or destroy network
- * devices based on iface.*.internal settings.
- *
- * XXX may want to move this to lib/netdev. */
-static bool
-netdev_exists(const char *name)
-{
-    struct stat s;
-    char *filename;
-    int error;
-
-    filename = xasprintf("/sys/class/net/%s", name);
-    error = stat(filename, &s);
-    free(filename);
-    return !error;
-}
-
-static int
-add_bridge(const char *br_name)
-{
-    if (bridge_exists(br_name)) {
-        VLOG_WARN("addbr %s: bridge %s exists", br_name, br_name);
-        return EEXIST;
-    } else if (netdev_exists(br_name)) {
-        if (cfg_get_bool(0, "iface.%s.fake-bridge", br_name)) {
-            VLOG_WARN("addbr %s: %s exists as a fake bridge",
-                      br_name, br_name);
-            return 0;
-        } else {
-            VLOG_WARN("addbr %s: cannot create bridge %s because a network "
-                      "device named %s already exists",
-                      br_name, br_name, br_name);
-            return EEXIST;
-        }
-    }
-
-    cfg_add_entry("bridge.%s.port=%s", br_name, br_name);
-    VLOG_INFO("addbr %s: success", br_name);
-
-    return 0;
-}
-
-static int 
-del_bridge(const char *br_name)
-{
-    if (!bridge_exists(br_name)) {
-        VLOG_WARN("delbr %s: no bridge named %s", br_name, br_name);
-        return ENXIO;
-    }
-
-    cfg_del_section("bridge.%s", br_name);
-    VLOG_INFO("delbr %s: success", br_name);
-
-    return 0;
-}
-
-static int
-parse_command(struct ofpbuf *buffer, uint32_t *seq, const char **br_name,
-              const char **port_name)
-{
-    static const struct nl_policy policy[] = {
-        [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING },
-        [BRC_GENL_A_PORT_NAME] = { .type = NL_A_STRING, .optional = true },
-    };
-    struct nlattr *attrs[ARRAY_SIZE(policy)];
-
-    if (!nl_policy_parse(buffer, NLMSG_HDRLEN + GENL_HDRLEN, policy,
-                         attrs, ARRAY_SIZE(policy))
-        || (port_name && !attrs[BRC_GENL_A_PORT_NAME])) {
-        return EINVAL;
-    }
-
-    *seq = ((struct nlmsghdr *) buffer->data)->nlmsg_seq;
-    *br_name = nl_attr_get_string(attrs[BRC_GENL_A_DP_NAME]);
-    if (port_name) {
-        *port_name = nl_attr_get_string(attrs[BRC_GENL_A_PORT_NAME]);
-    }
-    return 0;
-}
-
-static void
-send_reply(uint32_t seq, int error)
-{
-    struct ofpbuf msg;
-    int retval;
-
-    /* Compose reply. */
-    ofpbuf_init(&msg, 0);
-    nl_msg_put_genlmsghdr(&msg, brc_sock, 32, brc_family, NLM_F_REQUEST,
-                          BRC_GENL_C_DP_RESULT, 1);
-    ((struct nlmsghdr *) msg.data)->nlmsg_seq = seq;
-    nl_msg_put_u32(&msg, BRC_GENL_A_ERR_CODE, error);
-
-    /* Send reply. */
-    retval = nl_sock_send(brc_sock, &msg, false);
-    if (retval) {
-        VLOG_WARN_RL(&rl, "replying to brcompat request: %s",
-                     strerror(retval));
-    }
-    ofpbuf_uninit(&msg);
-}
-
-static int
-handle_bridge_cmd(struct ofpbuf *buffer, bool add)
-{
-    const char *br_name;
-    uint32_t seq;
-    int error;
-
-    error = parse_command(buffer, &seq, &br_name, NULL);
-    if (!error) {
-        error = add ? add_bridge(br_name) : del_bridge(br_name);
-        if (!error) {
-            error = rewrite_and_reload_config();
-        }
-        send_reply(seq, error);
-    }
-    return error;
-}
-
-static const struct nl_policy brc_port_policy[] = {
-    [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING },
-    [BRC_GENL_A_PORT_NAME] = { .type = NL_A_STRING },
-};
-
-static void
-del_port(const char *br_name, const char *port_name)
-{
-    cfg_del_entry("bridge.%s.port=%s", br_name, port_name);
-    cfg_del_match("bonding.*.slave=%s", port_name);
-    cfg_del_match("vlan.%s.*", port_name);
-}
-
-static int
-handle_port_cmd(struct ofpbuf *buffer, bool add)
-{
-    const char *cmd_name = add ? "add-if" : "del-if";
-    const char *br_name, *port_name;
-    uint32_t seq;
-    int error;
-
-    error = parse_command(buffer, &seq, &br_name, &port_name);
-    if (!error) {
-        if (!bridge_exists(br_name)) {
-            VLOG_WARN("%s %s %s: no bridge named %s",
-                      cmd_name, br_name, port_name, br_name);
-            error = EINVAL;
-        } else if (!netdev_exists(port_name)) {
-            VLOG_WARN("%s %s %s: no network device named %s",
-                      cmd_name, br_name, port_name, port_name);
-            error = EINVAL;
-        } else {
-            if (add) {
-                cfg_add_entry("bridge.%s.port=%s", br_name, port_name);
-            } else {
-                del_port(br_name, port_name);
-            }
-            VLOG_INFO("%s %s %s: success", cmd_name, br_name, port_name);
-            error = rewrite_and_reload_config();
-        }
-        send_reply(seq, error);
-    }
-
-    return error;
-}
-
-static int
-brc_recv_update(void)
-{
-    int retval;
-    struct ofpbuf *buffer;
-    struct genlmsghdr *genlmsghdr;
-
-
-    buffer = NULL;
-    do {
-        ofpbuf_delete(buffer);
-        retval = nl_sock_recv(brc_sock, &buffer, false);
-    } while (retval == ENOBUFS
-            || (!retval
-                && (nl_msg_nlmsgerr(buffer, NULL)
-                    || nl_msg_nlmsghdr(buffer)->nlmsg_type == NLMSG_DONE)));
-    if (retval) {
-        if (retval != EAGAIN) {
-            VLOG_WARN_RL(&rl, "brc_recv_update: %s", strerror(retval));
-        }
-        return retval;
-    }
-
-    genlmsghdr = nl_msg_genlmsghdr(buffer);
-    if (!genlmsghdr) {
-        VLOG_WARN_RL(&rl, "received packet too short for generic NetLink");
-        goto error;
-    }
-
-    if (nl_msg_nlmsghdr(buffer)->nlmsg_type != brc_family) {
-        VLOG_DBG_RL(&rl, "received type (%"PRIu16") != brcompat family (%d)",
-                nl_msg_nlmsghdr(buffer)->nlmsg_type, brc_family);
-        goto error;
-    }
-
-    if (cfg_lock(NULL, lock_timeout)) {
-        /* Couldn't lock config file. */
-        retval = EAGAIN;
-        goto error;
-    }
-
-    switch (genlmsghdr->cmd) {
-    case BRC_GENL_C_DP_ADD:
-        retval = handle_bridge_cmd(buffer, true);
-        break;
-
-    case BRC_GENL_C_DP_DEL:
-        retval = handle_bridge_cmd(buffer, false);
-        break;
-
-    case BRC_GENL_C_PORT_ADD:
-        retval = handle_port_cmd(buffer, true);
-        break;
-
-    case BRC_GENL_C_PORT_DEL:
-        retval = handle_port_cmd(buffer, false);
-        break;
-
-    default:
-        retval = EPROTO;
-    }
-
-    cfg_unlock();
-
-error:
-    ofpbuf_delete(buffer);
-    return retval;
-}
-
-/* Check for interface configuration changes announced through RTNL. */
-static void
-rtnl_recv_update(void)
-{
-    struct ofpbuf *buf;
-
-    int error = nl_sock_recv(rtnl_sock, &buf, false);
-    if (error == EAGAIN) {
-        /* Nothing to do. */
-    } else if (error == ENOBUFS) {
-        VLOG_WARN_RL(&rl, "network monitor socket overflowed");
-    } else if (error) {
-        VLOG_WARN_RL(&rl, "error on network monitor socket: %s", 
-                strerror(error));
-    } else {
-        struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)];
-        struct nlmsghdr *nlh;
-        struct ifinfomsg *iim;
-
-        nlh = ofpbuf_at(buf, 0, NLMSG_HDRLEN);
-        iim = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *iim);
-        if (!iim) {
-            VLOG_WARN_RL(&rl, "received bad rtnl message (no ifinfomsg)");
-            ofpbuf_delete(buf);
-            return;
-        } 
-    
-        if (!nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
-                             rtnlgrp_link_policy,
-                             attrs, ARRAY_SIZE(rtnlgrp_link_policy))) {
-            VLOG_WARN_RL(&rl,"received bad rtnl message (policy)");
-            ofpbuf_delete(buf);
-            return;
-        }
-        if (nlh->nlmsg_type == RTM_DELLINK && attrs[IFLA_MASTER]) {
-            const char *port_name = nl_attr_get_string(attrs[IFLA_IFNAME]);
-            char br_name[IFNAMSIZ];
-            uint32_t br_idx = nl_attr_get_u32(attrs[IFLA_MASTER]);
-            struct svec ports;
-
-            if (!if_indextoname(br_idx, br_name)) {
-                ofpbuf_delete(buf);
-                return;
-            }
-
-            if (cfg_lock(NULL, lock_timeout)) {
-                /* Couldn't lock config file. */
-                /* xxx this should try again and print error msg. */
-                ofpbuf_delete(buf);
-                return;
-            }
-
-            svec_init(&ports);
-            cfg_get_all_keys(&ports, "bridge.%s.port", br_name);
-            svec_sort(&ports);
-            if (svec_contains(&ports, port_name)) {
-                del_port(br_name, port_name);
-                rewrite_and_reload_config();
-            }
-            cfg_unlock();
-        }
-        ofpbuf_delete(buf);
-    }
-}
-
-int
-main(int argc, char *argv[])
-{
-    struct unixctl_server *unixctl;
-    int retval;
-
-    set_program_name(argv[0]);
-    register_fault_handlers();
-    time_init();
-    vlog_init();
-    parse_options(argc, argv);
-    signal(SIGPIPE, SIG_IGN);
-    process_init();
-
-    die_if_already_running();
-    daemonize();
-
-    fatal_signal_add_hook(release_lock, NULL, true);
-
-    retval = unixctl_server_create(NULL, &unixctl);
-    if (retval) {
-        ovs_fatal(retval, "could not listen for vlog connections");
-    }
-
-    if (brc_open(&brc_sock)) {
-        ovs_fatal(0, "could not open brcompat socket.  Check "
-                "\"brcompat\" kernel module.");
-    }
-
-    if (prune_timeout) {
-        if (nl_sock_create(NETLINK_ROUTE, RTNLGRP_LINK, 0, 0, &rtnl_sock)) {
-            ovs_fatal(0, "could not create rtnetlink socket");
-        }
-    }
-
-    cfg_read();
-
-    for (;;) {
-        unixctl_server_run(unixctl);
-        brc_recv_update();
-
-        /* If 'prune_timeout' is non-zero, we actively prune from the
-         * config file any 'bridge.<br_name>.port' entries that are no 
-         * longer valid.  We use two methods: 
-         *
-         *   1) The kernel explicitly notifies us of removed ports
-         *      through the RTNL messages.
-         *
-         *   2) We periodically check all ports associated with bridges
-         *      to see if they no longer exist.
-         */
-        if (prune_timeout) {
-            rtnl_recv_update();
-            prune_ports();
-
-            nl_sock_wait(rtnl_sock, POLLIN);
-            poll_timer_wait(prune_timeout);
-        }
-
-        nl_sock_wait(brc_sock, POLLIN);
-        unixctl_server_wait(unixctl);
-        poll_block();
-    }
-
-    return 0;
-}
-
-static void
-parse_options(int argc, char *argv[])
-{
-    enum {
-        OPT_LOCK_TIMEOUT = UCHAR_MAX + 1,
-        OPT_PRUNE_TIMEOUT,
-        OPT_RELOAD_COMMAND,
-        VLOG_OPTION_ENUMS,
-        LEAK_CHECKER_OPTION_ENUMS
-    };
-    static struct option long_options[] = {
-        {"help",             no_argument, 0, 'h'},
-        {"version",          no_argument, 0, 'V'},
-        {"lock-timeout",     required_argument, 0, OPT_LOCK_TIMEOUT},
-        {"prune-timeout",    required_argument, 0, OPT_PRUNE_TIMEOUT},
-        {"reload-command",   required_argument, 0, OPT_RELOAD_COMMAND},
-        DAEMON_LONG_OPTIONS,
-        VLOG_LONG_OPTIONS,
-        LEAK_CHECKER_LONG_OPTIONS,
-        {0, 0, 0, 0},
-    };
-    char *short_options = long_options_to_short_options(long_options);
-    int error;
-
-    reload_command = xasprintf("%s/ovs-appctl "
-                               "-t %s/vswitchd.`cat %s/vswitchd.pid`.ctl "
-                               "-e vswitchd/reload 2>&1 "
-                               "| /usr/bin/logger -t brcompatd-reload",
-                               ovs_bindir, ovs_rundir, ovs_rundir);
-    for (;;) {
-        int c;
-
-        c = getopt_long(argc, argv, short_options, long_options, NULL);
-        if (c == -1) {
-            break;
-        }
-
-        switch (c) {
-        case 'H':
-        case 'h':
-            usage();
-
-        case 'V':
-            OVS_PRINT_VERSION(0, 0);
-            exit(EXIT_SUCCESS);
-
-        case OPT_LOCK_TIMEOUT:
-            lock_timeout = atoi(optarg);
-            break;
-
-        case OPT_PRUNE_TIMEOUT:
-            prune_timeout = atoi(optarg) * 1000;
-            break;
-
-        case OPT_RELOAD_COMMAND:
-            reload_command = optarg;
-            break;
-
-        VLOG_OPTION_HANDLERS
-        DAEMON_OPTION_HANDLERS
-        LEAK_CHECKER_OPTION_HANDLERS
-
-        case '?':
-            exit(EXIT_FAILURE);
-
-        default:
-            abort();
-        }
-    }
-    free(short_options);
-
-    argc -= optind;
-    argv += optind;
-
-    if (argc != 1) {
-        ovs_fatal(0, "exactly one non-option argument required; "
-                "use --help for usage");
-    }
-
-    config_file = argv[0];
-    error = cfg_set_file(config_file);
-    if (error) {
-        ovs_fatal(error, "failed to add configuration file \"%s\"", 
-                config_file);
-    }
-}
-
-static void
-usage(void)
-{
-    printf("%s: bridge compatibility front-end for vswitchd\n"
-           "usage: %s [OPTIONS] CONFIG\n"
-           "CONFIG is the configuration file used by vswitchd.\n",
-           program_name, program_name);
-    printf("\nConfiguration options:\n"
-           "  --reload-command=COMMAND  command to run to reload vswitchd\n"
-           "  --prune-timeout=SECS    wait at most SECS before pruning ports\n"
-           "  --lock-timeout=MSECS    wait at most MSECS for CONFIG to unlock\n"
-          );
-    daemon_usage();
-    vlog_usage();
-    printf("\nOther options:\n"
-           "  -h, --help              display this help message\n"
-           "  -V, --version           display version information\n");
-    leak_checker_usage();
-    printf("\nThe default reload command is:\n%s\n", reload_command);
-    exit(EXIT_SUCCESS);
-}
-
-/* Callback to cleanup when process is dying. */
-static void
-release_lock(void *aux UNUSED)
-{
-    cfg_unlock();
-}
-
diff --git a/vswitchd/ovs-brcompatd.8.in b/vswitchd/ovs-brcompatd.8.in
new file mode 100644 (file)
index 0000000..15c3051
--- /dev/null
@@ -0,0 +1,49 @@
+.TH ovs\-brcompatd 8 "March 2009" "Open vSwitch" "Open vSwitch Manual"
+.ds PN ovs\-brcompatd
+.
+.SH NAME
+ovs\-brcompatd \- Bridge compatibility front-end for vswitchd
+.
+.SH SYNOPSIS
+.B ovs\-brcompatd
+[\fIoptions\fR] \fIconfig\fR
+.
+.SH DESCRIPTION
+A daemon that provides a legacy bridge front-end for \fBvswitchd\fR.  It 
+does this by listening for bridge ioctl commands (e.g., those generated by 
+the \fBbrctl\fR program) to add or remove datapaths and the interfaces 
+that attach to them.  It modifies \fIconfig\fR and forces
+\fBvswitchd\fR to reload its configuration file.
+.PP
+.SH OPTIONS
+.IP "\fB--reload-command=\fIcommand\fR"
+Sets the command that \fBovs\-brcompatd\fR runs to force \fBvswitchd\fR to
+reload its configuration file to \fIcommand\fR.  The command is run in
+a subshell, so it may contain arbitrary shell metacharacters, etc.
+The \fB--help\fR option displays the default reload command.
+.TP
+\fB--prune-timeout=\fIsecs\fR
+.
+Sets the maximum time between pruning port entries to \fIsecs\fR seconds.
+Pruning ports is the process of removing port entries from \fIconfig\fR 
+that no longer exist.  If \fIsecs\fR is zero, then entries are never
+pruned.  The default prune timeout is 5 seconds.
+.TP
+\fB--lock-timeout=\fImsecs\fR
+.
+Sets the maximum time to wait for \fIconfig\fR to become unlocked to 
+\fImsecs\fR milliseconds.  The default lock timeout is 500 milliseconds.
+.
+.so lib/daemon.man
+.so lib/vlog.man
+.so lib/common.man
+.so lib/leak-checker.man
+.
+.SH NOTES
+\fBovs\-brcompatd\fR requires the \fBbrcompat_mod.ko\fR kernel module to be
+loaded.
+.SH "SEE ALSO"
+.BR ovs-appctl (8),
+.BR vswitchd (8),
+.BR vswitchd.conf (5),
+\fBINSTALL\fR in the Open vSwitch distribution.
diff --git a/vswitchd/ovs-brcompatd.c b/vswitchd/ovs-brcompatd.c
new file mode 100644 (file)
index 0000000..fbd063d
--- /dev/null
@@ -0,0 +1,770 @@
+/* Copyright (c) 2008, 2009  Nicira Networks
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <net/if.h>
+#include <linux/genetlink.h>
+#include <linux/rtnetlink.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "cfg.h"
+#include "command-line.h"
+#include "daemon.h"
+#include "dirs.h"
+#include "dpif.h"
+#include "fatal-signal.h"
+#include "fault.h"
+#include "leak-checker.h"
+#include "netdev.h"
+#include "netlink.h"
+#include "ofpbuf.h"
+#include "openvswitch/brcompat-netlink.h"
+#include "poll-loop.h"
+#include "process.h"
+#include "signals.h"
+#include "svec.h"
+#include "timeval.h"
+#include "unixctl.h"
+#include "util.h"
+
+#include "vlog.h"
+#define THIS_MODULE VLM_brcompatd
+
+
+/* xxx Just hangs if datapath is rmmod/insmod.  Learn to reconnect? */
+
+/* Actions to modify bridge compatibility configuration. */
+enum bmc_action {
+    BMC_ADD_DP,
+    BMC_DEL_DP,
+    BMC_ADD_PORT,
+    BMC_DEL_PORT
+};
+
+static void release_lock(void *aux UNUSED);
+static void parse_options(int argc, char *argv[]);
+static void usage(void) NO_RETURN;
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 60);
+
+/* Maximum number of milliseconds to wait for the config file to be
+ * unlocked.  If set to zero, no waiting will occur. */
+static int lock_timeout = 500;
+
+/* Maximum number of milliseconds to wait before pruning port entries that 
+ * no longer exist.  If set to zero, ports are never pruned. */
+static int prune_timeout = 5000;
+
+/* Config file shared with vswitchd (usually vswitchd.conf). */
+static char *config_file;
+
+/* Command to run (via system()) to reload the vswitchd configuration file. */
+static char *reload_command;
+
+/* Netlink socket to listen for interface changes. */
+static struct nl_sock *rtnl_sock;
+
+/* Netlink socket to bridge compatibility kernel module. */
+static struct nl_sock *brc_sock;
+
+/* The Generic Netlink family number used for bridge compatibility. */
+static int brc_family;
+
+static const struct nl_policy brc_multicast_policy[] = {
+    [BRC_GENL_A_MC_GROUP] = {.type = NL_A_U32 }
+};
+
+static const struct nl_policy rtnlgrp_link_policy[] = {
+    [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false },
+    [IFLA_MASTER] = { .type = NL_A_U32, .optional = true },
+};
+
+static int
+lookup_brc_multicast_group(int *multicast_group)
+{
+    struct nl_sock *sock;
+    struct ofpbuf request, *reply;
+    struct nlattr *attrs[ARRAY_SIZE(brc_multicast_policy)];
+    int retval;
+
+    retval = nl_sock_create(NETLINK_GENERIC, 0, 0, 0, &sock);
+    if (retval) {
+        return retval;
+    }
+    ofpbuf_init(&request, 0);
+    nl_msg_put_genlmsghdr(&request, sock, 0, brc_family,
+            NLM_F_REQUEST, BRC_GENL_C_QUERY_MC, 1);
+    retval = nl_sock_transact(sock, &request, &reply);
+    ofpbuf_uninit(&request);
+    if (retval) {
+        nl_sock_destroy(sock);
+        return retval;
+    }
+    if (!nl_policy_parse(reply, NLMSG_HDRLEN + GENL_HDRLEN,
+                         brc_multicast_policy, attrs,
+                         ARRAY_SIZE(brc_multicast_policy))) {
+        nl_sock_destroy(sock);
+        ofpbuf_delete(reply);
+        return EPROTO;
+    }
+    *multicast_group = nl_attr_get_u32(attrs[BRC_GENL_A_MC_GROUP]);
+    nl_sock_destroy(sock);
+    ofpbuf_delete(reply);
+
+    return 0;
+}
+
+/* Opens a socket for brcompat notifications.  Returns 0 if successful,
+ * otherwise a positive errno value. */
+static int
+brc_open(struct nl_sock **sock)
+{
+    int multicast_group = 0;
+    int retval;
+
+    retval = nl_lookup_genl_family(BRC_GENL_FAMILY_NAME, &brc_family);
+    if (retval) {
+        return retval;
+    }
+
+    retval = lookup_brc_multicast_group(&multicast_group);
+    if (retval) {
+        return retval;
+    }
+
+    retval = nl_sock_create(NETLINK_GENERIC, multicast_group, 0, 0, sock);
+    if (retval) {
+        return retval;
+    }
+
+    return 0;
+}
+
+static const struct nl_policy brc_dp_policy[] = {
+    [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING },
+};
+
+static bool
+bridge_exists(const char *name)
+{
+    return cfg_has_section("bridge.%s", name);
+}
+
+static int
+rewrite_and_reload_config(void)
+{
+    if (cfg_is_dirty()) {
+        int error1 = cfg_write();
+        int error2 = cfg_read();
+        int error3 = system(reload_command);
+        if (error3 == -1) {
+            VLOG_ERR_RL(&rl, "failed to execute reload command: %s",
+                        strerror(errno));
+        } else if (error3 != 0) {
+            char *msg = process_status_msg(error3);
+            VLOG_ERR_RL(&rl, "reload command exited with error (%s)", msg);
+            free(msg);
+        }
+        return error1 ? error1 : error2 ? error2 : error3 ? ECHILD : 0;
+    }
+    return 0;
+}
+
+/* Go through the configuration file and remove any ports that no longer
+ * exist associated with a bridge. */
+static void
+prune_ports(void)
+{
+    int i, j;
+    int error;
+    struct svec bridges, delete;
+
+    if (cfg_lock(NULL, 0)) {
+        /* Couldn't lock config file. */
+        return;
+    }
+
+    svec_init(&bridges);
+    svec_init(&delete);
+    cfg_get_subsections(&bridges, "bridge");
+    for (i=0; i<bridges.n; i++) {
+        const char *br_name = bridges.names[i];
+        struct svec ports, ifaces;
+
+        svec_init(&ports);
+
+        /* Get all the interfaces for the given bridge, breaking bonded
+         * interfaces down into their constituent parts. */
+        svec_init(&ifaces);
+        cfg_get_all_keys(&ports, "bridge.%s.port", br_name);
+        for (j=0; j<ports.n; j++) {
+            const char *port_name = ports.names[j];
+            if (cfg_has_section("bonding.%s", port_name)) {
+                struct svec slaves;
+                svec_init(&slaves);
+                cfg_get_all_keys(&slaves, "bonding.%s.slave", port_name);
+                svec_append(&ifaces, &slaves);
+                svec_destroy(&slaves);
+            } else {
+                svec_add(&ifaces, port_name);
+            }
+        }
+        svec_destroy(&ports);
+
+        /* Check that the interfaces exist. */
+        for (j = 0; j < ifaces.n; j++) {
+            const char *iface_name = ifaces.names[j];
+            enum netdev_flags flags;
+
+            /* The local port and internal ports are created and destroyed by
+             * vswitchd itself, so don't bother checking for them at all.  In
+             * practice, they might not exist if vswitchd hasn't finished
+             * reloading since the configuration file was updated. */
+            if (!strcmp(iface_name, br_name)
+                || cfg_get_bool(0, "iface.%s.internal", iface_name)) {
+                continue;
+            }
+
+            error = netdev_nodev_get_flags(iface_name, &flags);
+            if (error == ENODEV) {
+                VLOG_DBG_RL(&rl, "removing dead interface %s from %s", 
+                        iface_name, br_name);
+                svec_add(&delete, iface_name);
+            } else if (error) {
+                VLOG_DBG_RL(&rl, "unknown error %d on interface %s from %s", 
+                        error, iface_name, br_name);
+            }
+        }
+        svec_destroy(&ifaces);
+    }
+    svec_destroy(&bridges);
+
+    if (delete.n) {
+        size_t i;
+
+        for (i = 0; i < delete.n; i++) {
+            cfg_del_match("bridge.*.port=%s", delete.names[i]);
+            cfg_del_match("bonding.*.slave=%s", delete.names[i]);
+        }
+        rewrite_and_reload_config();
+        cfg_unlock();
+    } else {
+        cfg_unlock();
+    }
+    svec_destroy(&delete);
+}
+
+
+/* Checks whether a network device named 'name' exists and returns true if so,
+ * false otherwise.
+ *
+ * XXX it is possible that this doesn't entirely accomplish what we want in
+ * context, since vswitchd.conf may cause vswitchd to create or destroy network
+ * devices based on iface.*.internal settings.
+ *
+ * XXX may want to move this to lib/netdev. */
+static bool
+netdev_exists(const char *name)
+{
+    struct stat s;
+    char *filename;
+    int error;
+
+    filename = xasprintf("/sys/class/net/%s", name);
+    error = stat(filename, &s);
+    free(filename);
+    return !error;
+}
+
+static int
+add_bridge(const char *br_name)
+{
+    if (bridge_exists(br_name)) {
+        VLOG_WARN("addbr %s: bridge %s exists", br_name, br_name);
+        return EEXIST;
+    } else if (netdev_exists(br_name)) {
+        if (cfg_get_bool(0, "iface.%s.fake-bridge", br_name)) {
+            VLOG_WARN("addbr %s: %s exists as a fake bridge",
+                      br_name, br_name);
+            return 0;
+        } else {
+            VLOG_WARN("addbr %s: cannot create bridge %s because a network "
+                      "device named %s already exists",
+                      br_name, br_name, br_name);
+            return EEXIST;
+        }
+    }
+
+    cfg_add_entry("bridge.%s.port=%s", br_name, br_name);
+    VLOG_INFO("addbr %s: success", br_name);
+
+    return 0;
+}
+
+static int 
+del_bridge(const char *br_name)
+{
+    if (!bridge_exists(br_name)) {
+        VLOG_WARN("delbr %s: no bridge named %s", br_name, br_name);
+        return ENXIO;
+    }
+
+    cfg_del_section("bridge.%s", br_name);
+    VLOG_INFO("delbr %s: success", br_name);
+
+    return 0;
+}
+
+static int
+parse_command(struct ofpbuf *buffer, uint32_t *seq, const char **br_name,
+              const char **port_name)
+{
+    static const struct nl_policy policy[] = {
+        [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING },
+        [BRC_GENL_A_PORT_NAME] = { .type = NL_A_STRING, .optional = true },
+    };
+    struct nlattr *attrs[ARRAY_SIZE(policy)];
+
+    if (!nl_policy_parse(buffer, NLMSG_HDRLEN + GENL_HDRLEN, policy,
+                         attrs, ARRAY_SIZE(policy))
+        || (port_name && !attrs[BRC_GENL_A_PORT_NAME])) {
+        return EINVAL;
+    }
+
+    *seq = ((struct nlmsghdr *) buffer->data)->nlmsg_seq;
+    *br_name = nl_attr_get_string(attrs[BRC_GENL_A_DP_NAME]);
+    if (port_name) {
+        *port_name = nl_attr_get_string(attrs[BRC_GENL_A_PORT_NAME]);
+    }
+    return 0;
+}
+
+static void
+send_reply(uint32_t seq, int error)
+{
+    struct ofpbuf msg;
+    int retval;
+
+    /* Compose reply. */
+    ofpbuf_init(&msg, 0);
+    nl_msg_put_genlmsghdr(&msg, brc_sock, 32, brc_family, NLM_F_REQUEST,
+                          BRC_GENL_C_DP_RESULT, 1);
+    ((struct nlmsghdr *) msg.data)->nlmsg_seq = seq;
+    nl_msg_put_u32(&msg, BRC_GENL_A_ERR_CODE, error);
+
+    /* Send reply. */
+    retval = nl_sock_send(brc_sock, &msg, false);
+    if (retval) {
+        VLOG_WARN_RL(&rl, "replying to brcompat request: %s",
+                     strerror(retval));
+    }
+    ofpbuf_uninit(&msg);
+}
+
+static int
+handle_bridge_cmd(struct ofpbuf *buffer, bool add)
+{
+    const char *br_name;
+    uint32_t seq;
+    int error;
+
+    error = parse_command(buffer, &seq, &br_name, NULL);
+    if (!error) {
+        error = add ? add_bridge(br_name) : del_bridge(br_name);
+        if (!error) {
+            error = rewrite_and_reload_config();
+        }
+        send_reply(seq, error);
+    }
+    return error;
+}
+
+static const struct nl_policy brc_port_policy[] = {
+    [BRC_GENL_A_DP_NAME] = { .type = NL_A_STRING },
+    [BRC_GENL_A_PORT_NAME] = { .type = NL_A_STRING },
+};
+
+static void
+del_port(const char *br_name, const char *port_name)
+{
+    cfg_del_entry("bridge.%s.port=%s", br_name, port_name);
+    cfg_del_match("bonding.*.slave=%s", port_name);
+    cfg_del_match("vlan.%s.*", port_name);
+}
+
+static int
+handle_port_cmd(struct ofpbuf *buffer, bool add)
+{
+    const char *cmd_name = add ? "add-if" : "del-if";
+    const char *br_name, *port_name;
+    uint32_t seq;
+    int error;
+
+    error = parse_command(buffer, &seq, &br_name, &port_name);
+    if (!error) {
+        if (!bridge_exists(br_name)) {
+            VLOG_WARN("%s %s %s: no bridge named %s",
+                      cmd_name, br_name, port_name, br_name);
+            error = EINVAL;
+        } else if (!netdev_exists(port_name)) {
+            VLOG_WARN("%s %s %s: no network device named %s",
+                      cmd_name, br_name, port_name, port_name);
+            error = EINVAL;
+        } else {
+            if (add) {
+                cfg_add_entry("bridge.%s.port=%s", br_name, port_name);
+            } else {
+                del_port(br_name, port_name);
+            }
+            VLOG_INFO("%s %s %s: success", cmd_name, br_name, port_name);
+            error = rewrite_and_reload_config();
+        }
+        send_reply(seq, error);
+    }
+
+    return error;
+}
+
+static int
+brc_recv_update(void)
+{
+    int retval;
+    struct ofpbuf *buffer;
+    struct genlmsghdr *genlmsghdr;
+
+
+    buffer = NULL;
+    do {
+        ofpbuf_delete(buffer);
+        retval = nl_sock_recv(brc_sock, &buffer, false);
+    } while (retval == ENOBUFS
+            || (!retval
+                && (nl_msg_nlmsgerr(buffer, NULL)
+                    || nl_msg_nlmsghdr(buffer)->nlmsg_type == NLMSG_DONE)));
+    if (retval) {
+        if (retval != EAGAIN) {
+            VLOG_WARN_RL(&rl, "brc_recv_update: %s", strerror(retval));
+        }
+        return retval;
+    }
+
+    genlmsghdr = nl_msg_genlmsghdr(buffer);
+    if (!genlmsghdr) {
+        VLOG_WARN_RL(&rl, "received packet too short for generic NetLink");
+        goto error;
+    }
+
+    if (nl_msg_nlmsghdr(buffer)->nlmsg_type != brc_family) {
+        VLOG_DBG_RL(&rl, "received type (%"PRIu16") != brcompat family (%d)",
+                nl_msg_nlmsghdr(buffer)->nlmsg_type, brc_family);
+        goto error;
+    }
+
+    if (cfg_lock(NULL, lock_timeout)) {
+        /* Couldn't lock config file. */
+        retval = EAGAIN;
+        goto error;
+    }
+
+    switch (genlmsghdr->cmd) {
+    case BRC_GENL_C_DP_ADD:
+        retval = handle_bridge_cmd(buffer, true);
+        break;
+
+    case BRC_GENL_C_DP_DEL:
+        retval = handle_bridge_cmd(buffer, false);
+        break;
+
+    case BRC_GENL_C_PORT_ADD:
+        retval = handle_port_cmd(buffer, true);
+        break;
+
+    case BRC_GENL_C_PORT_DEL:
+        retval = handle_port_cmd(buffer, false);
+        break;
+
+    default:
+        retval = EPROTO;
+    }
+
+    cfg_unlock();
+
+error:
+    ofpbuf_delete(buffer);
+    return retval;
+}
+
+/* Check for interface configuration changes announced through RTNL. */
+static void
+rtnl_recv_update(void)
+{
+    struct ofpbuf *buf;
+
+    int error = nl_sock_recv(rtnl_sock, &buf, false);
+    if (error == EAGAIN) {
+        /* Nothing to do. */
+    } else if (error == ENOBUFS) {
+        VLOG_WARN_RL(&rl, "network monitor socket overflowed");
+    } else if (error) {
+        VLOG_WARN_RL(&rl, "error on network monitor socket: %s", 
+                strerror(error));
+    } else {
+        struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)];
+        struct nlmsghdr *nlh;
+        struct ifinfomsg *iim;
+
+        nlh = ofpbuf_at(buf, 0, NLMSG_HDRLEN);
+        iim = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *iim);
+        if (!iim) {
+            VLOG_WARN_RL(&rl, "received bad rtnl message (no ifinfomsg)");
+            ofpbuf_delete(buf);
+            return;
+        } 
+    
+        if (!nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
+                             rtnlgrp_link_policy,
+                             attrs, ARRAY_SIZE(rtnlgrp_link_policy))) {
+            VLOG_WARN_RL(&rl,"received bad rtnl message (policy)");
+            ofpbuf_delete(buf);
+            return;
+        }
+        if (nlh->nlmsg_type == RTM_DELLINK && attrs[IFLA_MASTER]) {
+            const char *port_name = nl_attr_get_string(attrs[IFLA_IFNAME]);
+            char br_name[IFNAMSIZ];
+            uint32_t br_idx = nl_attr_get_u32(attrs[IFLA_MASTER]);
+            struct svec ports;
+
+            if (!if_indextoname(br_idx, br_name)) {
+                ofpbuf_delete(buf);
+                return;
+            }
+
+            if (cfg_lock(NULL, lock_timeout)) {
+                /* Couldn't lock config file. */
+                /* xxx this should try again and print error msg. */
+                ofpbuf_delete(buf);
+                return;
+            }
+
+            svec_init(&ports);
+            cfg_get_all_keys(&ports, "bridge.%s.port", br_name);
+            svec_sort(&ports);
+            if (svec_contains(&ports, port_name)) {
+                del_port(br_name, port_name);
+                rewrite_and_reload_config();
+            }
+            cfg_unlock();
+        }
+        ofpbuf_delete(buf);
+    }
+}
+
+int
+main(int argc, char *argv[])
+{
+    struct unixctl_server *unixctl;
+    int retval;
+
+    set_program_name(argv[0]);
+    register_fault_handlers();
+    time_init();
+    vlog_init();
+    parse_options(argc, argv);
+    signal(SIGPIPE, SIG_IGN);
+    process_init();
+
+    die_if_already_running();
+    daemonize();
+
+    fatal_signal_add_hook(release_lock, NULL, true);
+
+    retval = unixctl_server_create(NULL, &unixctl);
+    if (retval) {
+        ovs_fatal(retval, "could not listen for vlog connections");
+    }
+
+    if (brc_open(&brc_sock)) {
+        ovs_fatal(0, "could not open brcompat socket.  Check "
+                "\"brcompat\" kernel module.");
+    }
+
+    if (prune_timeout) {
+        if (nl_sock_create(NETLINK_ROUTE, RTNLGRP_LINK, 0, 0, &rtnl_sock)) {
+            ovs_fatal(0, "could not create rtnetlink socket");
+        }
+    }
+
+    cfg_read();
+
+    for (;;) {
+        unixctl_server_run(unixctl);
+        brc_recv_update();
+
+        /* If 'prune_timeout' is non-zero, we actively prune from the
+         * config file any 'bridge.<br_name>.port' entries that are no 
+         * longer valid.  We use two methods: 
+         *
+         *   1) The kernel explicitly notifies us of removed ports
+         *      through the RTNL messages.
+         *
+         *   2) We periodically check all ports associated with bridges
+         *      to see if they no longer exist.
+         */
+        if (prune_timeout) {
+            rtnl_recv_update();
+            prune_ports();
+
+            nl_sock_wait(rtnl_sock, POLLIN);
+            poll_timer_wait(prune_timeout);
+        }
+
+        nl_sock_wait(brc_sock, POLLIN);
+        unixctl_server_wait(unixctl);
+        poll_block();
+    }
+
+    return 0;
+}
+
+static void
+parse_options(int argc, char *argv[])
+{
+    enum {
+        OPT_LOCK_TIMEOUT = UCHAR_MAX + 1,
+        OPT_PRUNE_TIMEOUT,
+        OPT_RELOAD_COMMAND,
+        VLOG_OPTION_ENUMS,
+        LEAK_CHECKER_OPTION_ENUMS
+    };
+    static struct option long_options[] = {
+        {"help",             no_argument, 0, 'h'},
+        {"version",          no_argument, 0, 'V'},
+        {"lock-timeout",     required_argument, 0, OPT_LOCK_TIMEOUT},
+        {"prune-timeout",    required_argument, 0, OPT_PRUNE_TIMEOUT},
+        {"reload-command",   required_argument, 0, OPT_RELOAD_COMMAND},
+        DAEMON_LONG_OPTIONS,
+        VLOG_LONG_OPTIONS,
+        LEAK_CHECKER_LONG_OPTIONS,
+        {0, 0, 0, 0},
+    };
+    char *short_options = long_options_to_short_options(long_options);
+    int error;
+
+    reload_command = xasprintf("%s/ovs-appctl "
+                               "-t %s/vswitchd.`cat %s/vswitchd.pid`.ctl "
+                               "-e vswitchd/reload 2>&1 "
+                               "| /usr/bin/logger -t brcompatd-reload",
+                               ovs_bindir, ovs_rundir, ovs_rundir);
+    for (;;) {
+        int c;
+
+        c = getopt_long(argc, argv, short_options, long_options, NULL);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+        case 'H':
+        case 'h':
+            usage();
+
+        case 'V':
+            OVS_PRINT_VERSION(0, 0);
+            exit(EXIT_SUCCESS);
+
+        case OPT_LOCK_TIMEOUT:
+            lock_timeout = atoi(optarg);
+            break;
+
+        case OPT_PRUNE_TIMEOUT:
+            prune_timeout = atoi(optarg) * 1000;
+            break;
+
+        case OPT_RELOAD_COMMAND:
+            reload_command = optarg;
+            break;
+
+        VLOG_OPTION_HANDLERS
+        DAEMON_OPTION_HANDLERS
+        LEAK_CHECKER_OPTION_HANDLERS
+
+        case '?':
+            exit(EXIT_FAILURE);
+
+        default:
+            abort();
+        }
+    }
+    free(short_options);
+
+    argc -= optind;
+    argv += optind;
+
+    if (argc != 1) {
+        ovs_fatal(0, "exactly one non-option argument required; "
+                "use --help for usage");
+    }
+
+    config_file = argv[0];
+    error = cfg_set_file(config_file);
+    if (error) {
+        ovs_fatal(error, "failed to add configuration file \"%s\"", 
+                config_file);
+    }
+}
+
+static void
+usage(void)
+{
+    printf("%s: bridge compatibility front-end for vswitchd\n"
+           "usage: %s [OPTIONS] CONFIG\n"
+           "CONFIG is the configuration file used by vswitchd.\n",
+           program_name, program_name);
+    printf("\nConfiguration options:\n"
+           "  --reload-command=COMMAND  command to run to reload vswitchd\n"
+           "  --prune-timeout=SECS    wait at most SECS before pruning ports\n"
+           "  --lock-timeout=MSECS    wait at most MSECS for CONFIG to unlock\n"
+          );
+    daemon_usage();
+    vlog_usage();
+    printf("\nOther options:\n"
+           "  -h, --help              display this help message\n"
+           "  -V, --version           display version information\n");
+    leak_checker_usage();
+    printf("\nThe default reload command is:\n%s\n", reload_command);
+    exit(EXIT_SUCCESS);
+}
+
+/* Callback to cleanup when process is dying. */
+static void
+release_lock(void *aux UNUSED)
+{
+    cfg_unlock();
+}
+
index 25b2eeeb8e9259cf22c1968f1f33f1961a467a53..5f1171c1debbf9b77d90fbd52f8cb1a8cfdccd23 100644 (file)
@@ -41,7 +41,8 @@ within a configuration file is not significant.
 Blank lines, lines that consist only of white space, and lines that
 begin with \fB#\fR (optionally preceded by white space) are ignored.
 Keep in mind that programs that modify the configuration file, such as 
-\fBbrcompatd\fR and \fBovs-cfg-mod\fR, may alter the order of elements and 
+\fBovs\-brcompatd\fR and \fBovs-cfg-mod\fR, may alter the order of
+elements and 
 strip comments and blank lines.
 .PP
 The following subsections describe how key-value pairs are used to
@@ -637,5 +638,5 @@ To entirely disable controller connection snooping, set
 \fBnone\fR.
 .SH "SEE ALSO"
 .BR vswitchd (8),
-.BR brcompatd (8),
-.BR ovs-cfg-mod (8).
+.BR ovs\-brcompatd (8),
+.BR ovs\-cfg\-mod (8).
index bf52c921f06160a96215cb7b25b23cb4d807a9cc..61b415269d4a43bd0ca272f3b988fa2d62688eb3 100755 (executable)
@@ -44,8 +44,8 @@ VSWITCHD_STRACE_OPT="${VSWITCHD_STRACE_OPT:-}"
 VSWITCHD_VALGRIND_LOG="${VSWITCHD_VALGRIND_LOG:-}"
 VSWITCHD_VALGRIND_OPT="${VSWITCHD_VALGRIND_OPT:-}"
 
-# Config variables specific brcompatd
-BRCOMPATD_PIDFILE="${BRCOMPATD_PIDFILE:-/var/run/brcompatd.pid}"
+# Config variables specific to ovs-brcompatd
+BRCOMPATD_PIDFILE="${BRCOMPATD_PIDFILE:-/var/run/ovs-brcompatd.pid}"
 BRCOMPATD_PRIORITY="${BRCOMPATD_PRIORITY:--5}"
 BRCOMPATD_LOGFILE="${BRCOMPATD_LOGFILE:-/var/log/brcompatd.log}"
 BRCOMPATD_FILE_LOGLEVEL="${BRCOMPATD_FILE_LOGLEVEL:-}"
@@ -61,7 +61,7 @@ BRCOMPATD_VALGRIND_OPT="${BRCOMPATD_VALGRIND_OPT:-}"
 
 # Full paths to executables & modules
 vswitchd="$VSWITCH_BASE/sbin/vswitchd"
-brcompatd="$VSWITCH_BASE/sbin/brcompatd"
+brcompatd="$VSWITCH_BASE/sbin/ovs-brcompatd"
 dpctl="$VSWITCH_BASE/bin/ovs-dpctl"
 appctl="$VSWITCH_BASE/bin/ovs-appctl"
 ofctl="$VSWITCH_BASE/bin/ovs-ofctl"
@@ -245,10 +245,10 @@ function start_brcompatd {
     reload_cmd='/root/vswitch/bin/ovs-appctl -t /var/run/vswitchd.`cat /var/run/vswitchd.pid`.ctl -e vswitchd/reload 2>&1 | /usr/bin/logger -t brcompatd-reload'
     if [ "$daemonize" != "y" ]; then
         # Start in background and force a "success" message
-        action "Starting brcompatd ($strace_opt$valgrind_opt)" true
+        action "Starting ovs-brcompatd ($strace_opt$valgrind_opt)" true
         (nice -n "$VSWITCHD_PRIORITY" $strace_opt $valgrind_opt "$brcompatd" --reload-command="$reload_cmd" -P$BRCOMPATD_PIDFILE -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF") &
     else
-        action "Starting brcompatd" nice -n "$BRCOMPATD_PRIORITY" $strace_opt $valgrind_opt "$brcompatd" --reload-command="$reload_cmd" -P$BRCOMPATD_PIDFILE -D -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF"
+        action "Starting ovs-brcompatd" nice -n "$BRCOMPATD_PRIORITY" $strace_opt $valgrind_opt "$brcompatd" --reload-command="$reload_cmd" -P$BRCOMPATD_PIDFILE -D -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF"
     fi
 }
 
@@ -263,7 +263,7 @@ function stop_vswitchd {
 function stop_brcompatd {
     if [ -f "$BRCOMPATD_PIDFILE" ]; then
         local pid=$(cat "$BRCOMPATD_PIDFILE")
-        action "Killing brcompatd ($pid)" kill -TERM $pid
+        action "Killing ovs-brcompatd ($pid)" kill -TERM $pid
         rm -f "$BRCOMPATD_PIDFILE"
     fi
 }
@@ -362,11 +362,11 @@ case "$1" in
         ;;
     status)
         status -p vswitchd.pid vswitchd
-        status -p brcompatd.pid brcompatd
+        status -p brcompatd.pid ovs-brcompatd
         ;;
     version)
         "$VSWITCH_BASE"/sbin/vswitchd -V
-        "$VSWITCH_BASE"/sbin/brcompatd -V
+        "$VSWITCH_BASE"/sbin/ovs-brcompatd -V
         ;;
     help)
         printf "vswitch [start|stop|restart|reload|unload|status|version]\n"
index cd6e7d1fba310c57a23555b910b4ecb91c79b255..25003d3859b707d41405351db78da862f55e2c1d 100755 (executable)
@@ -31,9 +31,9 @@ VSWITCHD_LOGFILE="${VSWITCHD_LOGFILE:-/var/log/vswitchd.log}"
 VSWITCHD_FILE_LOGLEVEL="${VSWITCHD_FILE_LOGLEVEL:-}"
 VSWITCHD_SYSLOG_LOGLEVEL="${VSWITCHD_SYSLOG_LOGLEVEL:-WARN}"
 VSWITCHD_MEMLEAK_LOGFILE="${VSWITCHD_MEMLEAK_LOGFILE:-}"
-BRCOMPATD_PIDFILE="${BRCOMPATD_PIDFILE:-/var/run/brcompatd.pid}"
+BRCOMPATD_PIDFILE="${BRCOMPATD_PIDFILE:-/var/run/ovs-brcompatd.pid}"
 BRCOMPATD_PRIORITY="${BRCOMPATD_PRIORITY:--5}"
-BRCOMPATD_LOGFILE="${BRCOMPATD_LOGFILE:-/var/log/brcompatd.log}"
+BRCOMPATD_LOGFILE="${BRCOMPATD_LOGFILE:-/var/log/ovs-brcompatd.log}"
 BRCOMPATD_FILE_LOGLEVEL="${BRCOMPATD_FILE_LOGLEVEL:-}"
 BRCOMPATD_SYSLOG_LOGLEVEL="${BRCOMPATD_SYSLOG_LOGLEVEL:-WARN}"
 BRCOMPATD_MEMLEAK_LOGFILE="${BRCOMPATD_MEMLEAK_LOGFILE:-}"
index bdebf84fca038c421753beedb91f99946851fddf..8de056ebcef178828fcdbb253f7d4146d9edeada 100644 (file)
@@ -11,7 +11,7 @@
 # VSWITCH_BASE=/root/vswitch/openvswitch/build
 
 # ENABLE_BRCOMPAT: If 'y' than emulate linux bridging interfaces
-#    using the brcompat kernel module and brcompatd daemon
+#    using the brcompat kernel module and ovs-brcompatd daemon
 # ENABLE_BRCOMPAT=y
 
 # ENABLE_FAKE_PROC_NET: If 'y' then emulate linux bonding and vlan
 # VSWITCHD_SYSLOG_LOGLEVEL="WARN"
 
 # BRCOMPATD_PIDFILE: File in which to store the pid of the running
-#     brcompatd (the Linux bridge compatibility daemon for vswitchd).
-#     If this is the empty string, brcompatd will not be started and
+#     ovs-brcompatd (the Linux bridge compatibility daemon for vswitchd).
+#     If this is the empty string, ovs-brcompatd will not be started and
 #     the brcompat_mod kernel module will not be inserted.  Note that
 #     the default is to use brcompat!
-# BRCOMPATD_PIDFILE=/var/run/brcompatd.pid
+# BRCOMPATD_PIDFILE=/var/run/ovs-brcompatd.pid
 
 # BRCOMPATD_PRIORITY: "nice" priority at which to run vswitchd and related
 #     processes.
 # BRCOMPATD_PRIORITY=-5
 
 # BRCOMPATD_LOGFILE: File to send the FILE_LOGLEVEL log messages to.
-# BRCOMPATD_LOGFILE=/var/log/brcompatd.log
+# BRCOMPATD_LOGFILE=/var/log/ovs-brcompatd.log
 
 # BRCOMPATD_FILE_LOGLEVEL: Log level at which to log into the
 #     BRCOMPATD_LOG file.  If this is null or not set the logfile will
index 1a88b2b6bed7aad25141cb91b752b3d239af9855..78a6654e3366261ce0b42755ccf696c66358db1f 100644 (file)
@@ -101,7 +101,7 @@ class VSwitchControllerDialogue(Dialogue):
                       lambda: self.syncController()),
 #             ChoiceDef(Lang("Restart vswitchd"),
 #                       lambda: self.restartService("vswitch")),
-#             ChoiceDef(Lang("Restart brcompatd"),
+#             ChoiceDef(Lang("Restart ovs-brcompatd"),
 #                       lambda: self.restartService("vswitch-brcompatd"))
             ]
         self.menu = Menu(self, None, Lang("Configure vSwitch"), choiceDefs)
@@ -267,8 +267,8 @@ class XSFeatureVSwitch:
         inPane.NewLine()
         inPane.AddStatusField(Lang("vswitchd status", 20),
                               VSwitchService.Inst("vswitch", "vswitchd").status())
-        inPane.AddStatusField(Lang("brcompatd status", 20),
-                              VSwitchService.Inst("vswitch", "brcompatd").status())
+        inPane.AddStatusField(Lang("ovs-brcompatd status", 20),
+                              VSwitchService.Inst("vswitch", "ovs-brcompatd").status())
 
         inPane.AddKeyHelpField( {
             Lang("<Enter>") : Lang("Reconfigure"),
index df69356a23c7814980ff88784535e7d57cb39bf0..01e84b4a1b27e9606d007d19d867579168b486a8 100644 (file)
@@ -257,14 +257,14 @@ fi
 # include them.
 /root/vswitch/scripts/XSFeatureVSwitch.pyc
 /root/vswitch/scripts/XSFeatureVSwitch.pyo
-/root/vswitch/sbin/brcompatd
+/root/vswitch/sbin/ovs-brcompatd
 /root/vswitch/sbin/vswitchd
 /root/vswitch/bin/ovs-appctl
 /root/vswitch/bin/ovs-cfg-mod
 /root/vswitch/bin/ovs-dpctl
 /root/vswitch/bin/ovs-ofctl
 /root/vswitch/share/man/man5/vswitchd.conf.5
-/root/vswitch/share/man/man8/brcompatd.8
+/root/vswitch/share/man/man8/ovs-brcompatd.8
 /root/vswitch/share/man/man8/ovs-appctl.8
 /root/vswitch/share/man/man8/ovs-cfg-mod.8
 /root/vswitch/share/man/man8/ovs-dpctl.8