.dumpit = NULL
};
+bool dp_exists(const char *dp_name)
+{
+ struct net_device *dev;
+ bool retval;
+
+ dev = dev_get_by_name(&init_net, dp_name);
+ if (!dev)
+ return false;
+
+ retval = is_dp_dev(dev);
+ dev_put(dev);
+ return retval;
+}
+
int brc_send_dp_add_del(const char *dp_name, int add)
{
struct sk_buff *skb;
void *data;
- int retval;
+ int i, retval;
skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (skb == NULL)
if (dp_act_err)
return -dp_act_err;
- return 0;
+ /* The user-space brcompatd can only suggest to vswitchd that it
+ * reconfigure itself. We need to make sure the changes actually
+ * worked. */
+ for (i = 0; i < 50; i++) {
+ bool exists = dp_exists(dp_name);
+ if ((add && exists) || (!add && !exists))
+ return 0;
+
+ msleep(100);
+ }
+
+ return -EINVAL;
nla_put_failure:
err:
{
return netdev->open == dp_dev_open;
}
+EXPORT_SYMBOL(is_dp_dev);
#include "ofpbuf.h"
#include "packets.h"
#include "svec.h"
+#include "timeval.h"
#include "util.h"
#define THIS_MODULE VLM_cfg
svec_clear(&cfg);
/* Read new configuration. */
- VLOG_INFO("reading configuration from %s", cfg_name);
+ VLOG_DBG("reading configuration from %s", cfg_name);
file = fopen(cfg_name, "r");
if (!file) {
}
}
-/* Config may change, so caller responsible for notifying others if it
- * did. Returns positive errno. */
+/* Config may change, so caller is responsible for notifying others if it
+ * did. The 'timeout' specifies the maximum number of milliseconds to
+ * wait for the config file to become free. Returns positive errno. */
int
-cfg_lock(uint8_t *cookie)
+cfg_lock(uint8_t *cookie, int timeout)
{
+ long long int start = time_msec();
int fd;
uint8_t curr_cookie[CFG_COOKIE_LEN];
- /* Put the lock file in the same directory as cfg_name, so that
- * they are guaranteed to be in the same file system. */
- fd = create_lockfile(lock_name);
- if (fd < 0) {
- /* Couldn't lock config file */
- return -fd;
+ for (;;) {
+ /* Put the lock file in the same directory as cfg_name, so that
+ * they are guaranteed to be in the same file system. */
+ fd = create_lockfile(lock_name);
+ if (fd < 0 && (timeout <= (time_msec() - start))) {
+ /* Wait a millisecond before trying again. */
+ usleep(1000);
+ } else if (fd < 0) {
+ /* Couldn't lock config file in time. */
+ return -fd;
+ } else {
+ break;
+ }
}
lock_fd = fd;
int cfg_set_file(const char *file_name);
void cfg_read(void);
-int cfg_lock(uint8_t *cookie);
+int cfg_lock(uint8_t *cookie, int timeout);
void cfg_unlock(void);
int cfg_write(void);
int cfg_write_data(uint8_t *data, size_t len);
/* Modules that can emit log messages. */
VLOG_MODULE(backtrace)
-VLOG_MODULE(brcompat)
+VLOG_MODULE(brcompatd)
VLOG_MODULE(bridge)
VLOG_MODULE(chain)
VLOG_MODULE(cfg)
/Makefile
/Makefile.in
+/brcompatd
+/brcompatd.8
/vswitchd
/vswitchd.8
+/vswitchd.conf.5
-bin_PROGRAMS += vswitchd/vswitchd
-man_MANS += vswitchd/vswitchd.conf.5 vswitchd/vswitchd.8
-DISTCLEANFILES += vswitchd/vswitchd.conf.5 vswitchd/vswitchd.8
+bin_PROGRAMS += vswitchd/vswitchd vswitchd/brcompatd
+man_MANS += vswitchd/vswitchd.conf.5 \
+ vswitchd/vswitchd.8 \
+ vswitchd/brcompatd.8
+DISTCLEANFILES += vswitchd/vswitchd.conf.5 \
+ vswitchd/vswitchd.8 \
+ vswitchd/brcompatd.8
vswitchd_vswitchd_SOURCES = \
- vswitchd/brcompat.c \
- vswitchd/brcompat.h \
vswitchd/bridge.c \
vswitchd/bridge.h \
vswitchd/mgmt.c \
$(FAULT_LIBS) \
$(SSL_LIBS)
-EXTRA_DIST += vswitchd/vswitchd.conf.5.in vswitchd/vswitchd.8.in
+vswitchd_brcompatd_SOURCES = \
+ vswitchd/brcompatd.c
+
+vswitchd_brcompatd_LDADD = \
+ lib/libopenflow.a \
+ $(FAULT_LIBS)
+
+EXTRA_DIST += vswitchd/vswitchd.conf.5.in \
+ vswitchd/vswitchd.8.in
+ vswitchd/brcompatd.8.in
+++ /dev/null
-/* 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/>.
- *
- * In addition, as a special exception, Nicira Networks gives permission
- * to link the code of its release of vswitchd with the OpenSSL project's
- * "OpenSSL" library (or with modified versions of it that use the same
- * license as the "OpenSSL" library), and distribute the linked
- * executables. You must obey the GNU General Public License in all
- * respects for all of the code used other than "OpenSSL". If you modify
- * this file, you may extend this exception to your version of the file,
- * but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version.
- *
- */
-
-#include <config.h>
-
-#include "brcompat.h"
-
-#include <assert.h>
-#include <errno.h>
-#include <getopt.h>
-#include <inttypes.h>
-#include <limits.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 "openflow/brcompat-netlink.h"
-#include "bridge.h"
-#include "cfg.h"
-#include "command-line.h"
-#include "daemon.h"
-#include "dpif.h"
-#include "fault.h"
-#include "netlink.h"
-#include "netlink-protocol.h"
-#include "ofpbuf.h"
-#include "poll-loop.h"
-#include "process.h"
-#include "svec.h"
-#include "timeval.h"
-#include "util.h"
-#include "vlog-socket.h"
-#include "vswitchd.h"
-
-#include "vlog.h"
-#define THIS_MODULE VLM_brcompat
-
-extern bool brc_enabled;
-
-static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 60);
-
-/* Netlink socket to kernel datapath */
-struct nl_sock *nl_sock;
-
-/* xxx Just hangs if datapath is rmmod/insmod. Learn to reconnect? */
-
-/* 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 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 },
-};
-
-/* Modify the existing configuration according to 'act'. The configuration
- * file will be modified to reflect these changes. The caller is
- * responsible for causing vswitchd to actually re-read its configuration.
- * Returns 0 on success, otherwise a postive errno. */
-int
-brc_modify_config(const char *dp_name, const char *port_name,
- enum bmc_action act)
-{
- if (!brc_enabled) {
- return 0;
- }
-
- if (cfg_lock(NULL)) {
- /* Couldn't lock config file. */
- return EAGAIN;
- }
-
- switch (act) {
-
- case BMC_ADD_DP:
- cfg_add_entry("bridge.%s.port=%s", dp_name, dp_name);
- break;
-
- case BMC_ADD_PORT:
- cfg_add_entry("bridge.%s.port=%s", dp_name, port_name);
- break;
-
- case BMC_DEL_DP:
- cfg_del_section("bridge.%s", dp_name);
- break;
-
- case BMC_DEL_PORT:
- cfg_del_entry("bridge.%s.port=%s", dp_name, port_name);
- break;
- }
-
- cfg_write();
- cfg_unlock();
-
- return 0;
-}
-
-static int
-brc_add_dp(const char *dp_name)
-{
- int retval;
-
- if (bridge_exists(dp_name)) {
- return EEXIST;
- }
-
- retval = brc_modify_config(dp_name, NULL, BMC_ADD_DP);
- if (retval) {
- return retval;
- }
-
- reconfigure();
-
- if (!bridge_exists(dp_name)) {
- return EINVAL;
- }
-
- return 0;
-}
-
-static int
-brc_del_dp(const char *dp_name)
-{
- int retval;
-
- if (!bridge_exists(dp_name)) {
- return ENXIO;
- }
-
- retval = brc_modify_config(dp_name, NULL, BMC_DEL_DP);
- if (retval) {
- return retval;
- }
-
- reconfigure();
-
- if (bridge_exists(dp_name)) {
- return EINVAL;
- }
-
- return 0;
-}
-
-static int
-brc_handle_dp_cmd(struct ofpbuf *buffer, bool add)
-{
- int dp_act_err;
- int retval;
- struct nlattr *attrs[ARRAY_SIZE(brc_dp_policy)];
- const char *dp_name;
- struct ofpbuf msg;
-
- if (!nl_policy_parse(buffer, NLMSG_HDRLEN + GENL_HDRLEN, brc_dp_policy,
- attrs, ARRAY_SIZE(brc_dp_policy))) {
- return EINVAL;
- }
-
- dp_name = nl_attr_get(attrs[BRC_GENL_A_DP_NAME]);
-
- if (add) {
- dp_act_err = brc_add_dp(dp_name);
- } else {
- dp_act_err = brc_del_dp(dp_name);
- }
-
- /* Notify the brcompat kernel module of the result. */
- ofpbuf_init(&msg, 0);
- nl_msg_put_genlmsghdr(&msg, nl_sock, 32, brc_family, NLM_F_REQUEST,
- BRC_GENL_C_DP_RESULT, 1);
- nl_msg_put_u32(&msg, BRC_GENL_A_ERR_CODE, dp_act_err);
- retval = nl_sock_send(nl_sock, &msg, false);
- if (retval) {
- VLOG_WARN_RL(&rl, "brc_handle_dp_cmd: %s", strerror(retval));
- }
- ofpbuf_uninit(&msg);
-
- return dp_act_err;
-}
-
-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 int
-brc_handle_port_cmd(struct ofpbuf *buffer, bool add)
-{
- struct nlattr *attrs[ARRAY_SIZE(brc_port_policy)];
- const char *dp_name, *port_name;
- int retval;
-
- if (!nl_policy_parse(buffer, NLMSG_HDRLEN + GENL_HDRLEN, brc_port_policy,
- attrs, ARRAY_SIZE(brc_port_policy))) {
- return EINVAL;
- }
-
- dp_name = nl_attr_get(attrs[BRC_GENL_A_DP_NAME]);
- port_name = nl_attr_get(attrs[BRC_GENL_A_PORT_NAME]);
-
- if (!bridge_exists(dp_name)) {
- return EINVAL;
- }
-
- if (add) {
- retval = brc_modify_config(dp_name, port_name, BMC_ADD_PORT);
- } else {
- retval = brc_modify_config(dp_name, port_name, BMC_DEL_PORT);
- }
- if (retval) {
- return retval;
- }
-
- /* Force vswitchd to reconfigure itself. */
- reconfigure();
-
- return 0;
-}
-
-static int
-brc_recv_update(void)
-{
- int retval;
- struct ofpbuf *buffer;
- struct genlmsghdr *genlmsghdr;
-
-
- buffer = NULL;
- do {
- ofpbuf_delete(buffer);
- retval = nl_sock_recv(nl_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;
- }
-
- switch (genlmsghdr->cmd) {
- case BRC_GENL_C_DP_ADD:
- retval = brc_handle_dp_cmd(buffer, true);
- break;
-
- case BRC_GENL_C_DP_DEL:
- retval = brc_handle_dp_cmd(buffer, false);
- break;
-
- case BRC_GENL_C_PORT_ADD:
- retval = brc_handle_port_cmd(buffer, true);
- break;
-
- case BRC_GENL_C_PORT_DEL:
- retval = brc_handle_port_cmd(buffer, false);
- break;
-
- default:
- retval = EPROTO;
- }
-
-error:
- ofpbuf_delete(buffer);
- return retval;
-}
-
-void
-brc_run(void)
-{
- brc_recv_update();
-}
-
-void
-brc_wait(void)
-{
- nl_sock_wait(nl_sock, POLLIN);
-}
-
-void
-brc_init(void)
-{
- if (brc_open(&nl_sock)) {
- ofp_fatal(0, "could not open brcompat socket. Check "
- "\"brcompat\" kernel module.");
- }
-}
+++ /dev/null
-/* 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/>.
- *
- * In addition, as a special exception, Nicira Networks gives permission
- * to link the code of its release of vswitchd with the OpenSSL project's
- * "OpenSSL" library (or with modified versions of it that use the same
- * license as the "OpenSSL" library), and distribute the linked
- * executables. You must obey the GNU General Public License in all
- * respects for all of the code used other than "OpenSSL". If you modify
- * this file, you may extend this exception to your version of the file,
- * but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version.
- */
-
-#ifndef VSWITCHD_BRCOMPAT_H
-#define VSWITCHD_BRCOMPAT_H 1
-
-
-/* Actions to modify bridge compatibility configuration. */
-enum bmc_action {
- BMC_ADD_DP,
- BMC_DEL_DP,
- BMC_ADD_PORT,
- BMC_DEL_PORT
-};
-
-void brc_init(void);
-void brc_wait(void);
-void brc_run(void);
-int brc_modify_config(const char *dp_name, const char *port_name,
- enum bmc_action act);
-
-#endif /* brcompat.h */
--- /dev/null
+.TH brcompatd 8 "March 2009" "OpenFlow" "OpenFlow 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 modifies \fIconfig\fR and forces
+\fBvswitchd\fR to reload its configuration file.
+.PP
+.SH OPTIONS
+.TP
+\fB--vswitchd-pidfile=\fIpidfile\fR
+.
+If the \fB--vswitchd-pidfile\fR flag is provided, the required
+\fIpidfile\fR argument is used as the pidfile location instead of the
+default one.
+.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 vswitchd (8),
+.BR vswitchd.conf (5),
+.BR vlogconf (8),
+\fBINSTALL\fR in the OpenFlow distribution.
--- /dev/null
+/* 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 "openflow/brcompat-netlink.h"
+#include "cfg.h"
+#include "command-line.h"
+#include "daemon.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 "poll-loop.h"
+#include "process.h"
+#include "signals.h"
+#include "svec.h"
+#include "timeval.h"
+#include "util.h"
+#include "vlog-socket.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 int modify_config(const char *br_name, const char *port_name,
+ enum bmc_action act);
+
+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. */
+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. */
+int prune_timeout = 5000;
+
+/* Config file shared with vswitchd (usually vswitchd.conf). */
+char *config_file;
+
+/* Filename containing vswitchd pid. */
+char *vswitchd_pidfile;
+
+/* Netlink socket to listen for interface changes. */
+struct nl_sock *rtnl_sock;
+
+/* Netlink socket to bridge compatibility kernel module. */
+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);
+}
+
+/* Tell vswitchd reconfigure itself. */
+static void
+force_reconfigure(void)
+{
+ pid_t pid;
+
+ pid = read_pidfile(vswitchd_pidfile);
+ if (pid < 0) {
+ VLOG_ERR_RL(&rl, "problem read vswitchd pidfile: %s",
+ strerror(errno));
+ return;
+ }
+ if (kill(pid, SIGHUP) < 0) {
+ VLOG_ERR_RL(&rl, "problem sending HUP to vswitchd: %s",
+ strerror(errno));
+ return;
+ }
+}
+
+/* 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;
+ bool pruned = false;
+
+ if (cfg_lock(NULL, 0)) {
+ /* Couldn't lock config file. */
+ return;
+ }
+
+ svec_init(&bridges);
+ cfg_get_subsections(&bridges, "bridge");
+
+ for (i=0; i<bridges.n; i++) {
+ const char *br_name = bridges.names[i];
+ struct svec ports;
+ svec_init(&ports);
+
+ cfg_get_all_keys(&ports, "bridge.%s.port", br_name);
+ for (j=0; j<ports.n; j++) {
+ const char *port_name = ports.names[j];
+ enum netdev_flags flags;
+
+ error = netdev_nodev_get_flags(port_name, &flags);
+ if (error == ENODEV) {
+ VLOG_DBG_RL(&rl, "removing dead port %s from %s",
+ port_name, br_name);
+ modify_config(br_name, port_name, BMC_DEL_PORT);
+ pruned = true;
+ } else if (error) {
+ VLOG_DBG_RL(&rl, "unknown error %d on port %s from %s",
+ error, port_name, br_name);
+ }
+ }
+ }
+
+ cfg_unlock();
+
+ if (pruned) {
+ /* Ask vswitchd to reconfigure itself. */
+ force_reconfigure();
+ }
+}
+
+/* Modify the existing configuration according to 'act'. The configuration
+ * file will be modified to reflect these changes. The caller is
+ * responsible for causing vswitchd to actually re-read its configuration.
+ * Returns 0 on success, otherwise a postive errno. */
+static int
+modify_config(const char *br_name, const char *port_name, enum bmc_action act)
+{
+ switch (act) {
+
+ case BMC_ADD_DP:
+ cfg_add_entry("bridge.%s.port=%s", br_name, br_name);
+ break;
+
+ case BMC_ADD_PORT:
+ cfg_add_entry("bridge.%s.port=%s", br_name, port_name);
+ break;
+
+ case BMC_DEL_DP:
+ cfg_del_section("bridge.%s", br_name);
+ break;
+
+ case BMC_DEL_PORT:
+ cfg_del_entry("bridge.%s.port=%s", br_name, port_name);
+ break;
+ }
+
+ cfg_write();
+ cfg_read();
+
+ return 0;
+}
+
+static int
+add_bridge(const char *br_name)
+{
+ int retval;
+
+ if (bridge_exists(br_name)) {
+ return EEXIST;
+ }
+
+ retval = modify_config(br_name, NULL, BMC_ADD_DP);
+ if (retval) {
+ return retval;
+ }
+
+ /* Ask vswitchd to reconfigure itself. */
+ force_reconfigure();
+
+ return 0;
+}
+
+static int
+del_bridge(const char *br_name)
+{
+ int retval;
+
+ if (!bridge_exists(br_name)) {
+ return ENXIO;
+ }
+
+ retval = modify_config(br_name, NULL, BMC_DEL_DP);
+ if (retval) {
+ return retval;
+ }
+
+ /* Ask vswitchd to reconfigure itself. */
+ force_reconfigure();
+
+ return 0;
+}
+
+static int
+handle_bridge_cmd(struct ofpbuf *buffer, bool add)
+{
+ int br_act_err;
+ int retval;
+ struct nlattr *attrs[ARRAY_SIZE(brc_dp_policy)];
+ const char *br_name;
+ struct ofpbuf msg;
+
+ if (!nl_policy_parse(buffer, NLMSG_HDRLEN + GENL_HDRLEN, brc_dp_policy,
+ attrs, ARRAY_SIZE(brc_dp_policy))) {
+ return EINVAL;
+ }
+
+ br_name = nl_attr_get(attrs[BRC_GENL_A_DP_NAME]);
+
+ if (add) {
+ br_act_err = add_bridge(br_name);
+ } else {
+ br_act_err = del_bridge(br_name);
+ }
+
+ /* Notify the brcompat kernel module of the result. */
+ ofpbuf_init(&msg, 0);
+ nl_msg_put_genlmsghdr(&msg, brc_sock, 32, brc_family, NLM_F_REQUEST,
+ BRC_GENL_C_DP_RESULT, 1);
+ nl_msg_put_u32(&msg, BRC_GENL_A_ERR_CODE, br_act_err);
+ retval = nl_sock_send(brc_sock, &msg, false);
+ if (retval) {
+ VLOG_WARN_RL(&rl, "handle_bridge_cmd: %s", strerror(retval));
+ }
+ ofpbuf_uninit(&msg);
+
+ return br_act_err;
+}
+
+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 int
+handle_port_cmd(struct ofpbuf *buffer, bool add)
+{
+ struct nlattr *attrs[ARRAY_SIZE(brc_port_policy)];
+ const char *br_name, *port_name;
+ int retval;
+
+ if (!nl_policy_parse(buffer, NLMSG_HDRLEN + GENL_HDRLEN, brc_port_policy,
+ attrs, ARRAY_SIZE(brc_port_policy))) {
+ return EINVAL;
+ }
+
+ br_name = nl_attr_get(attrs[BRC_GENL_A_DP_NAME]);
+ port_name = nl_attr_get(attrs[BRC_GENL_A_PORT_NAME]);
+
+ if (!bridge_exists(br_name)) {
+ return EINVAL;
+ }
+
+ if (add) {
+ retval = modify_config(br_name, port_name, BMC_ADD_PORT);
+ } else {
+ retval = modify_config(br_name, port_name, BMC_DEL_PORT);
+ }
+ if (retval) {
+ return retval;
+ }
+
+ /* Ask vswitchd to reconfigure itself. */
+ force_reconfigure();
+
+ return 0;
+}
+
+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)) {
+ modify_config(br_name, port_name, BMC_DEL_PORT);
+ }
+ cfg_unlock();
+ }
+ ofpbuf_delete(buf);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ 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 = vlog_server_listen(NULL, NULL);
+ if (retval) {
+ ofp_fatal(retval, "could not listen for vlog connections");
+ }
+
+ if (brc_open(&brc_sock)) {
+ ofp_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)) {
+ ofp_fatal(0, "could not create rtnetlink socket");
+ }
+ }
+
+ /* If a vswitchd pidfile was not explicitly set, assume the default
+ * location. */
+ if (!vswitchd_pidfile) {
+ vswitchd_pidfile = make_pidfile_name("vswitchd.pid");
+ }
+
+ cfg_read();
+
+ for (;;) {
+ 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);
+
+ poll_block();
+ }
+
+ return 0;
+}
+
+static void
+parse_options(int argc, char *argv[])
+{
+ enum {
+ OPT_LOCK_TIMEOUT = UCHAR_MAX + 1,
+ OPT_PRUNE_TIMEOUT,
+ OPT_VSWITCHD_PIDFILE,
+ 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},
+ {"vswitchd-pidfile", required_argument, 0, OPT_VSWITCHD_PIDFILE},
+ 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;
+
+ 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':
+ printf("%s "VERSION BUILDNR" compiled "__DATE__" "__TIME__"\n",
+ argv[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_VSWITCHD_PIDFILE:
+ vswitchd_pidfile = 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) {
+ ofp_fatal(0, "config file is only non-option argument; "
+ "use --help for usage");
+ }
+
+ config_file = argv[0];
+ error = cfg_set_file(config_file);
+ if (error) {
+ ofp_fatal(error, "failed to add configuration file \"%s\"",
+ optarg);
+ }
+}
+
+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"
+ " --vswitchd-pidfile=FILE use FILE as pid file for 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("Other options:\n"
+ " -h, --help display this help message\n"
+ " -V, --version display version information\n");
+ leak_checker_usage();
+ exit(EXIT_SUCCESS);
+}
+
+/* Callback to cleanup when process is dying. */
+static void
+release_lock(void *aux UNUSED)
+{
+ cfg_unlock();
+}
+
#include <sys/socket.h>
#include <unistd.h>
#include "bitmap.h"
-#include "brcompat.h"
#include "cfg.h"
#include "dirs.h"
#include "dpif.h"
br->name, iface->name);
iface_destroy(iface);
if (!port->n_ifaces) {
- VLOG_WARN("bridge %s: port %s has no interfaces, deleting",
+ VLOG_WARN("bridge %s: port %s has no interfaces, dropping",
br->name, port->name);
port_destroy(port);
}
- brc_modify_config(br->name, (const char *)opp->name, BMC_DEL_PORT);
bridge_flush(br);
} else {
if (port->n_ifaces > 1) {
/* Check if the supplied cookie matches our current understanding of
* it. If they don't match, tell the controller and let it sort
* things out. */
- if (cfg_lock(ofmpcu->cookie)) {
+ if (cfg_lock(ofmpcu->cookie, 0)) {
/* xxx cfg_lock can fail for other reasons, such as being
* xxx locked... */
/* xxx This error message should probably send diff info. */
.
.SH SYNOPSIS
.B vswitchd
-[\fI--brcompat\fR]\&
\fIconfig\fR
.
.SH DESCRIPTION
to be useful. Please refer to the \fBINSTALL\fR file included in the
OpenFlow source distribution for instructions on how to build and load
the OpenFlow kernel module.
-.PP
-.SH OPTIONS
-.TP
-\fB--brcompat\fR
-.
-The \fB--brcompat\fR option puts \fBvswitchd\fR into bridge compatibility
-mode. This means it listens 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.
-.
-.IP ""
-This option requires the \fBbrcompat_mod.ko\fR kernel module to be
-loaded.
.
.so lib/daemon.man
.so lib/vlog.man
.
.SH "BUGS"
.
-The \fB--brcompat\fR kluge should not be necessary. It will be
-removed in the future.
-.PP
Only OpenFlow kernel-based datapaths are currently supported. In the
future, this restriction may be lifted.
.PP
.
.SH "SEE ALSO"
.BR vswitchd.conf (5),
+.BR brcompatd (8),
.BR vlogconf (8),
\fBINSTALL\fR in the OpenFlow distribution.
#include <stdlib.h>
#include <string.h>
-#include "brcompat.h"
#include "bridge.h"
#include "cfg.h"
#include "command-line.h"
static void parse_options(int argc, char *argv[]);
static void usage(void) NO_RETURN;
-bool brc_enabled = false;
char *config_file;
int
need_reconfigure = true;
}
- if (brc_enabled) {
- brc_run();
- brc_wait();
- }
-
signal_wait(sighup);
mgmt_wait();
bridge_wait();
parse_options(int argc, char *argv[])
{
enum {
- OPT_BRCOMPAT = UCHAR_MAX + 1,
- OPT_PEER_CA_CERT,
+ OPT_PEER_CA_CERT = UCHAR_MAX + 1,
VLOG_OPTION_ENUMS,
LEAK_CHECKER_OPTION_ENUMS
};
static struct option long_options[] = {
- {"brcompat", no_argument, 0, OPT_BRCOMPAT},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'},
DAEMON_LONG_OPTIONS,
}
switch (c) {
- case OPT_BRCOMPAT:
- brc_enabled = true;
- brc_init();
- break;
-
case 'H':
case 'h':
usage();
"usage: %s [OPTIONS] CONFIG\n"
"CONFIG is a configuration file in vswitchd.conf(5) format.\n",
program_name, program_name);
- printf("\nCompatibility options:\n"
- " --brcompat run with bridge compatibility hooks\n");
daemon_usage();
vlog_usage();
printf("\nOther options:\n"