From d4842ec9f83760d8bc171bc8d4e5ddd046517ba5 Mon Sep 17 00:00:00 2001 From: Justin Pettit Date: Mon, 30 Mar 2009 01:17:49 -0700 Subject: [PATCH] Break bridge compatibility daemon into separate process. The user-space bridge compatibility code formally was part of the vswitchd process. For better separation, the bridge compatibility code has been broken into a separate process, brcompatd. --- datapath/brcompat.c | 29 +- datapath/dp_dev.c | 1 + lib/cfg.c | 30 +- lib/cfg.h | 2 +- lib/vlog-modules.def | 2 +- vswitchd/.gitignore | 3 + vswitchd/automake.mk | 23 +- vswitchd/brcompat.c | 390 ---------------------- vswitchd/brcompat.h | 45 --- vswitchd/brcompatd.8.in | 50 +++ vswitchd/brcompatd.c | 709 ++++++++++++++++++++++++++++++++++++++++ vswitchd/bridge.c | 4 +- vswitchd/mgmt.c | 2 +- vswitchd/vswitchd.8.in | 18 +- vswitchd/vswitchd.c | 18 +- 15 files changed, 833 insertions(+), 493 deletions(-) delete mode 100644 vswitchd/brcompat.c delete mode 100644 vswitchd/brcompat.h create mode 100644 vswitchd/brcompatd.8.in create mode 100644 vswitchd/brcompatd.c diff --git a/datapath/brcompat.c b/datapath/brcompat.c index a22998a6..926e8d35 100644 --- a/datapath/brcompat.c +++ b/datapath/brcompat.c @@ -308,11 +308,25 @@ static struct genl_ops brc_genl_ops_dp_result = { .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) @@ -344,7 +358,18 @@ int brc_send_dp_add_del(const char *dp_name, int add) 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: diff --git a/datapath/dp_dev.c b/datapath/dp_dev.c index bc6cb03d..d450996b 100644 --- a/datapath/dp_dev.c +++ b/datapath/dp_dev.c @@ -212,3 +212,4 @@ int is_dp_dev(struct net_device *netdev) { return netdev->open == dp_dev_open; } +EXPORT_SYMBOL(is_dp_dev); diff --git a/lib/cfg.c b/lib/cfg.c index 66a5aca5..4fbaa9ca 100644 --- a/lib/cfg.c +++ b/lib/cfg.c @@ -41,6 +41,7 @@ #include "ofpbuf.h" #include "packets.h" #include "svec.h" +#include "timeval.h" #include "util.h" #define THIS_MODULE VLM_cfg @@ -182,7 +183,7 @@ cfg_read(void) 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) { @@ -260,20 +261,29 @@ cfg_unlock(void) } } -/* 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; diff --git a/lib/cfg.h b/lib/cfg.h index cf937f2e..c2f7d17f 100644 --- a/lib/cfg.h +++ b/lib/cfg.h @@ -39,7 +39,7 @@ struct ofpbuf; 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); diff --git a/lib/vlog-modules.def b/lib/vlog-modules.def index 9ce1e822..db100e88 100644 --- a/lib/vlog-modules.def +++ b/lib/vlog-modules.def @@ -1,6 +1,6 @@ /* Modules that can emit log messages. */ VLOG_MODULE(backtrace) -VLOG_MODULE(brcompat) +VLOG_MODULE(brcompatd) VLOG_MODULE(bridge) VLOG_MODULE(chain) VLOG_MODULE(cfg) diff --git a/vswitchd/.gitignore b/vswitchd/.gitignore index 42dcf728..3c5880b3 100644 --- a/vswitchd/.gitignore +++ b/vswitchd/.gitignore @@ -1,4 +1,7 @@ /Makefile /Makefile.in +/brcompatd +/brcompatd.8 /vswitchd /vswitchd.8 +/vswitchd.conf.5 diff --git a/vswitchd/automake.mk b/vswitchd/automake.mk index 143d4fcc..0b6c1b6f 100644 --- a/vswitchd/automake.mk +++ b/vswitchd/automake.mk @@ -1,10 +1,12 @@ -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 \ @@ -18,4 +20,13 @@ vswitchd_vswitchd_LDADD = \ $(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 diff --git a/vswitchd/brcompat.c b/vswitchd/brcompat.c deleted file mode 100644 index 5a1be8b7..00000000 --- a/vswitchd/brcompat.c +++ /dev/null @@ -1,390 +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 . - * - * 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 - -#include "brcompat.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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."); - } -} diff --git a/vswitchd/brcompat.h b/vswitchd/brcompat.h deleted file mode 100644 index 2827207c..00000000 --- a/vswitchd/brcompat.h +++ /dev/null @@ -1,45 +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 . - * - * 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 */ diff --git a/vswitchd/brcompatd.8.in b/vswitchd/brcompatd.8.in new file mode 100644 index 00000000..73047371 --- /dev/null +++ b/vswitchd/brcompatd.8.in @@ -0,0 +1,50 @@ +.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. diff --git a/vswitchd/brcompatd.c b/vswitchd/brcompatd.c new file mode 100644 index 00000000..c74e4f10 --- /dev/null +++ b/vswitchd/brcompatd.c @@ -0,0 +1,709 @@ +/* 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 . + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; inlmsg_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..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(); +} + diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index e202ceec..c1218494 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -42,7 +42,6 @@ #include #include #include "bitmap.h" -#include "brcompat.h" #include "cfg.h" #include "dirs.h" #include "dpif.h" @@ -1734,12 +1733,11 @@ bridge_port_changed_ofhook_cb(enum ofp_port_reason reason, 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) { diff --git a/vswitchd/mgmt.c b/vswitchd/mgmt.c index 1e8a05f4..02313bda 100644 --- a/vswitchd/mgmt.c +++ b/vswitchd/mgmt.c @@ -494,7 +494,7 @@ recv_ofmp_config_update(uint32_t xid, const struct ofmp_header *ofmph) /* 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. */ diff --git a/vswitchd/vswitchd.8.in b/vswitchd/vswitchd.8.in index ccf8b550..38b3158e 100644 --- a/vswitchd/vswitchd.8.in +++ b/vswitchd/vswitchd.8.in @@ -6,7 +6,6 @@ vswitchd \- OpenFlow-based virtual switch daemon . .SH SYNOPSIS .B vswitchd -[\fI--brcompat\fR]\& \fIconfig\fR . .SH DESCRIPTION @@ -64,19 +63,6 @@ An OpenFlow datapath kernel module must be loaded for \fBvswitchd\fR 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 @@ -85,9 +71,6 @@ loaded. . .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 @@ -95,5 +78,6 @@ Only Linux 2.6.\fIx\fR is currently supported. . .SH "SEE ALSO" .BR vswitchd.conf (5), +.BR brcompatd (8), .BR vlogconf (8), \fBINSTALL\fR in the OpenFlow distribution. diff --git a/vswitchd/vswitchd.c b/vswitchd/vswitchd.c index 113555a7..ce0d68e0 100644 --- a/vswitchd/vswitchd.c +++ b/vswitchd/vswitchd.c @@ -34,7 +34,6 @@ #include #include -#include "brcompat.h" #include "bridge.h" #include "cfg.h" #include "command-line.h" @@ -60,7 +59,6 @@ static void parse_options(int argc, char *argv[]); static void usage(void) NO_RETURN; -bool brc_enabled = false; char *config_file; int @@ -104,11 +102,6 @@ main(int argc, char *argv[]) need_reconfigure = true; } - if (brc_enabled) { - brc_run(); - brc_wait(); - } - signal_wait(sighup); mgmt_wait(); bridge_wait(); @@ -130,13 +123,11 @@ static void 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, @@ -160,11 +151,6 @@ parse_options(int argc, char *argv[]) } switch (c) { - case OPT_BRCOMPAT: - brc_enabled = true; - brc_init(); - break; - case 'H': case 'h': usage(); @@ -217,8 +203,6 @@ usage(void) "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" -- 2.30.2