static struct genl_family brc_genl_family;
static struct genl_multicast_group brc_mc_group;
-/* Completion for vswitchd to notify the ioctl that the operation
- * completed. */
-static DECLARE_COMPLETION(dp_act_done);
-
-/* Time to wait for vswitchd to respond to a datapath action (in
- * milliseconds) */
-#define DP_ACT_TIMEOUT 5000
+/* Time to wait for vswitchd to respond to a datapath action, in jiffies. */
+#define BRC_TIMEOUT (HZ * 5)
-/* Positive errno as a result of a datapath action. Calls that make
- * use of this variable are serialized by the br_ioctl_mutex. */
-static int dp_act_err;
+/* Mutex to serialize 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.) */
+static DEFINE_MUTEX(brc_serial);
-int brc_send_dp_add_del(const char *dp_name, int add);
-int brc_send_port_add_del(struct net_device *dev, struct net_device *port,
- int add);
+/* Userspace communication. */
+static DEFINE_SPINLOCK(brc_lock); /* Ensure atomic access to these vars. */
+static DECLARE_COMPLETION(brc_done); /* Userspace signaled operation done? */
+static int brc_err; /* Error code from userspace. */
+static u32 brc_seq; /* Sequence number for current op. */
+static int brc_send_command(const char *bridge, const char *port, int op);
static int
get_dp_ifindices(int *indices, int num)
rcu_read_unlock();
}
-/* Legacy deviceless bridge ioctl's. Called with br_ioctl_mutex. */
-static int
-old_deviceless(void __user *uarg)
+static int brc_add_del_bridge(char __user *uname, int add)
{
- unsigned long args[3];
+ char name[IFNAMSIZ];
- if (copy_from_user(args, uarg, sizeof(args)))
+ if (copy_from_user(name, uname, IFNAMSIZ))
return -EFAULT;
- switch (args[0]) {
- case BRCTL_GET_BRIDGES: {
- int *indices;
- int ret = 0;
+ name[IFNAMSIZ - 1] = 0;
+ return brc_send_command(name, NULL,
+ add ? BRC_GENL_C_DP_ADD : BRC_GENL_C_DP_DEL);
+}
- if (args[2] >= 2048)
- return -ENOMEM;
+static int brc_get_bridges(int __user *uindices, int n)
+{
+ int *indices;
+ int ret;
- indices = kcalloc(args[2], sizeof(int), GFP_KERNEL);
- if (indices == NULL)
- return -ENOMEM;
+ if (n >= 2048)
+ return -ENOMEM;
- args[2] = get_dp_ifindices(indices, args[2]);
+ indices = kcalloc(n, sizeof(int), GFP_KERNEL);
+ if (indices == NULL)
+ return -ENOMEM;
- ret = copy_to_user((void __user *)args[1], indices,
- args[2]*sizeof(int)) ? -EFAULT : args[2];
+ n = get_dp_ifindices(indices, n);
- kfree(indices);
- return ret;
- }
+ ret = copy_to_user(uindices, indices, n * sizeof(int)) ? -EFAULT : n;
- case BRCTL_DEL_BRIDGE:
- case BRCTL_ADD_BRIDGE: {
- char dp_name[IFNAMSIZ];
+ kfree(indices);
+ return ret;
+}
- if (copy_from_user(dp_name, (void __user *)args[1], IFNAMSIZ))
- return -EFAULT;
+/* Legacy deviceless bridge ioctl's. Called with br_ioctl_mutex. */
+static int
+old_deviceless(void __user *uarg)
+{
+ unsigned long args[3];
- dp_name[IFNAMSIZ-1] = 0;
- if (args[0] == BRCTL_ADD_BRIDGE)
- return brc_send_dp_add_del(dp_name, 1);
+ if (copy_from_user(args, uarg, sizeof(args)))
+ return -EFAULT;
- return brc_send_dp_add_del(dp_name, 0);
- }
+ switch (args[0]) {
+ case BRCTL_GET_BRIDGES:
+ return brc_get_bridges((int __user *)args[1], args[2]);
+
+ case BRCTL_ADD_BRIDGE:
+ return brc_add_del_bridge((void __user *)args[1], 1);
+ case BRCTL_DEL_BRIDGE:
+ return brc_add_del_bridge((void __user *)args[1], 0);
}
return -EOPNOTSUPP;
{
switch (cmd) {
case SIOCGIFBR:
- case SIOCSIFBR:
+ case SIOCSIFBR:
return old_deviceless(uarg);
case SIOCBRADDBR:
- case SIOCBRDELBR: {
- char dp_name[IFNAMSIZ];
-
- if (copy_from_user(dp_name, uarg, IFNAMSIZ))
- return -EFAULT;
-
- dp_name[IFNAMSIZ-1] = 0;
- return brc_send_dp_add_del(dp_name, cmd == SIOCBRADDBR);
- }
+ return brc_add_del_bridge(uarg, 1);
+ case SIOCBRDELBR:
+ return brc_add_del_bridge(uarg, 0);
}
return -EOPNOTSUPP;
}
-/* Legacy ioctl's through SIOCDEVPRIVATE. Called with rtnl_lock. */
static int
-old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+brc_add_del_port(struct net_device *dev, int port_ifindex, int add)
{
- struct dp_dev *dp_dev = netdev_priv(dev);
- struct datapath *dp = dp_dev->dp;
- unsigned long args[4];
+ struct net_device *port;
+ int err;
- if (copy_from_user(args, rq->ifr_data, sizeof(args)))
+ port = __dev_get_by_index(&init_net, port_ifindex);
+ if (!port)
+ return -EINVAL;
+
+ rtnl_unlock();
+ err = brc_send_command(dev->name, port->name,
+ add ? BRC_GENL_C_PORT_ADD : BRC_GENL_C_PORT_DEL);
+ rtnl_lock();
+
+ return err;
+}
+
+static int
+brc_get_bridge_info(struct net_device *dev, struct __bridge_info __user *ub)
+{
+ struct __bridge_info b;
+ u64 id = 0;
+ int i;
+
+ memset(&b, 0, sizeof(struct __bridge_info));
+
+ for (i=0; i<ETH_ALEN; i++)
+ id |= (u64)dev->dev_addr[i] << (8*(ETH_ALEN-1 - i));
+ b.bridge_id = cpu_to_be64(id);
+ b.stp_enabled = 0;
+
+ if (copy_to_user(ub, &b, sizeof(struct __bridge_info)))
return -EFAULT;
- switch (args[0]) {
- case BRCTL_ADD_IF:
- case BRCTL_DEL_IF: {
- struct net_device *port;
- int err;
+ return 0;
+}
- port = __dev_get_by_index(&init_net, args[1]);
- if (!port)
- return -EINVAL;
+static int
+brc_get_port_list(struct net_device *dev, int __user *uindices, int num)
+{
+ struct dp_dev *dp_dev = netdev_priv(dev);
+ struct datapath *dp = dp_dev->dp;
+ int *indices;
- err = brc_send_port_add_del(dev, port, args[0] == BRCTL_ADD_IF);
- return err;
- }
+ if (num < 0)
+ return -EINVAL;
+ if (num == 0)
+ num = 256;
+ if (num > DP_MAX_PORTS)
+ num = DP_MAX_PORTS;
- case BRCTL_GET_BRIDGE_INFO: {
- struct __bridge_info b;
- u64 id = 0;
- int i;
+ indices = kcalloc(num, sizeof(int), GFP_KERNEL);
+ if (indices == NULL)
+ return -ENOMEM;
- memset(&b, 0, sizeof(struct __bridge_info));
+ get_port_ifindices(dp, indices, num);
+ if (copy_to_user(uindices, indices, num * sizeof(int)))
+ num = -EFAULT;
+ kfree(indices);
+ return num;
+}
- for (i=0; i<ETH_ALEN; i++)
- id |= (u64)dev->dev_addr[i] << (8*(ETH_ALEN-1 - i));
- b.bridge_id = cpu_to_be64(id);
- b.stp_enabled = 0;
+/* Legacy ioctl's through SIOCDEVPRIVATE. Called with rtnl_lock. */
+static int
+old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ unsigned long args[4];
- if (copy_to_user((void __user *)args[1], &b, sizeof(b)))
- return -EFAULT;
+ if (copy_from_user(args, rq->ifr_data, sizeof(args)))
+ return -EFAULT;
- return 0;
- }
+ switch (args[0]) {
+ case BRCTL_ADD_IF:
+ return brc_add_del_port(dev, args[1], 1);
+ case BRCTL_DEL_IF:
+ return brc_add_del_port(dev, args[1], 0);
- case BRCTL_GET_PORT_LIST: {
- int num, *indices;
-
- num = args[2];
- if (num < 0)
- return -EINVAL;
- if (num == 0)
- num = 256;
- if (num > DP_MAX_PORTS)
- num = DP_MAX_PORTS;
-
- indices = kcalloc(num, sizeof(int), GFP_KERNEL);
- if (indices == NULL)
- return -ENOMEM;
-
- get_port_ifindices(dp, indices, num);
- if (copy_to_user((void __user *)args[1], indices, num*sizeof(int)))
- num = -EFAULT;
- kfree(indices);
- return num;
- }
+ case BRCTL_GET_BRIDGE_INFO:
+ return brc_get_bridge_info(dev, (struct __bridge_info __user *)args[1]);
+
+ case BRCTL_GET_PORT_LIST:
+ return brc_get_port_list(dev, (int __user *)args[1], args[2]);
}
return -EOPNOTSUPP;
break;
case SIOCBRADDIF:
- case SIOCBRDELIF: {
- struct net_device *port;
-
- port = __dev_get_by_index(&init_net, rq->ifr_ifindex);
- if (!port)
- return -EINVAL;
-
- err = brc_send_port_add_del(dev, port, cmd == SIOCBRADDIF);
- break;
- }
+ return brc_add_del_port(dev, rq->ifr_ifindex, 1);
+ case SIOCBRDELIF:
+ return brc_add_del_port(dev, rq->ifr_ifindex, 0);
default:
err = -EOPNOTSUPP;
static int
brc_genl_dp_result(struct sk_buff *skb, struct genl_info *info)
{
+ unsigned long int flags;
+ int err;
+
if (!info->attrs[BRC_GENL_A_ERR_CODE])
return -EINVAL;
-
- dp_act_err = nla_get_u32(info->attrs[BRC_GENL_A_ERR_CODE]);
- complete(&dp_act_done);
- return 0;
+ spin_lock_irqsave(&brc_lock, flags);
+ if (brc_seq == info->snd_seq) {
+ brc_err = nla_get_u32(info->attrs[BRC_GENL_A_ERR_CODE]);
+ complete(&brc_done);
+ err = 0;
+ } else {
+ err = -ESTALE;
+ }
+ spin_unlock_irqrestore(&brc_lock, flags);
+
+ return err;
}
static struct genl_ops brc_genl_ops_dp_result = {
.dumpit = NULL
};
-int dp_exists(const char *dp_name)
-{
- struct net_device *dev;
- int retval;
-
- dev = dev_get_by_name(&init_net, dp_name);
- if (!dev)
- return 0;
-
- retval = is_dp_dev(dev);
- dev_put(dev);
- return retval;
-}
-
-int brc_send_dp_add_del(const char *dp_name, int add)
+static int brc_send_command(const char *bridge, const char *port, int op)
{
+ unsigned long int flags;
struct sk_buff *skb;
void *data;
- int i, retval;
-
- skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
- if (skb == NULL)
- return -ENOMEM;
-
- if (add)
- data = genlmsg_put(skb, 0, 0, &brc_genl_family, 0,
- BRC_GENL_C_DP_ADD);
- else
- data = genlmsg_put(skb, 0, 0, &brc_genl_family, 0,
- BRC_GENL_C_DP_DEL);
- if (!data)
- goto err;
-
- NLA_PUT_STRING(skb, BRC_GENL_A_DP_NAME, dp_name);
-
- init_completion(&dp_act_done);
-
- genlmsg_end(skb, data);
- retval = genlmsg_multicast(skb, 0, brc_mc_group.id, GFP_KERNEL);
- if (retval < 0)
- return retval;
-
- if (!wait_for_completion_timeout(&dp_act_done,
- msecs_to_jiffies(DP_ACT_TIMEOUT)))
- return -EIO;
-
- /* The value is returned as a positive errno, so make it negative */
- if (dp_act_err)
- return -dp_act_err;
-
- /* 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++) {
- int exists = dp_exists(dp_name);
- if ((add && exists) || (!add && !exists))
- return 0;
-
- msleep(100);
- }
+ int error;
- return -EINVAL;
+ mutex_lock(&brc_serial);
-nla_put_failure:
-err:
- kfree_skb(skb);
- return -ENOMEM;
-}
-
-int brc_send_port_add_del(struct net_device *dev, struct net_device *port,
- int add)
-{
- struct dp_dev *dp_dev = netdev_priv(dev);
- struct datapath *dp = dp_dev->dp;
- struct sk_buff *skb;
- int error;
- void *data;
- int i;
+ /* Increment sequence number first, so that we ignore any replies
+ * to stale requests. */
+ spin_lock_irqsave(&brc_lock, flags);
+ brc_seq++;
+ INIT_COMPLETION(brc_done);
+ spin_unlock_irqrestore(&brc_lock, flags);
+ /* Compose message. */
skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ error = -ENOMEM;
if (skb == NULL)
- return -ENOMEM;
-
- if (add) {
- /* Only add the port if it's not attached to a datapath. */
- if (port->br_port != NULL) {
- kfree_skb(skb);
- return -EBUSY;
- }
- data = genlmsg_put(skb, 0, 0, &brc_genl_family, 0,
- BRC_GENL_C_PORT_ADD);
- } else {
- /* Only delete the port if it's attached to this datapath. */
- if (port->br_port == NULL || port->br_port->dp != dp) {
- kfree_skb(skb);
- return -ENOENT;
- }
- data = genlmsg_put(skb, 0, 0, &brc_genl_family, 0,
- BRC_GENL_C_PORT_DEL);
- }
- if (!data)
- goto err;
+ goto exit_unlock;
+ data = genlmsg_put(skb, 0, brc_seq, &brc_genl_family, 0, op);
- NLA_PUT_STRING(skb, BRC_GENL_A_DP_NAME, dev->name);
- NLA_PUT_STRING(skb, BRC_GENL_A_PORT_NAME, port->name);
+ NLA_PUT_STRING(skb, BRC_GENL_A_DP_NAME, bridge);
+ if (port)
+ NLA_PUT_STRING(skb, BRC_GENL_A_PORT_NAME, port);
genlmsg_end(skb, data);
+
+ /* Send message. */
error = genlmsg_multicast(skb, 0, brc_mc_group.id, GFP_KERNEL);
- if (error)
- return error;
-
- for (i = 0; i < 50; i++) {
- int dev_ifindex = dev->ifindex;
- int port_ifindex = port->ifindex;
-
- rtnl_unlock();
- msleep(100);
- rtnl_lock();
-
- /* Since we released the RTNL lock, we have to make sure that
- * our devices still exist. (But as long as they still exist,
- * there's no point in refreshing 'dp' or 'dp_dev', since
- * ifindexes are reused very slowly if ever.) */
- dev = __dev_get_by_index(&init_net, dev_ifindex);
- port = __dev_get_by_index(&init_net, port_ifindex);
- if (!dev || !port)
- return -ENODEV;
-
- if (add) {
- if (port->br_port)
- return port->br_port->dp == dp ? 0 : -EBUSY;
- } else {
- if (!port->br_port || port->br_port->dp != dp)
- return 0;
- }
- }
- return -ETIMEDOUT;
+ if (error < 0)
+ goto exit_unlock;
+
+ /* Wait for reply. */
+ error = -ETIMEDOUT;
+ if (!wait_for_completion_timeout(&brc_done, BRC_TIMEOUT))
+ goto exit_unlock;
+
+ error = -brc_err;
+ goto exit_unlock;
nla_put_failure:
-err:
kfree_skb(skb);
- return -EINVAL;
+exit_unlock:
+ mutex_unlock(&brc_serial);
+ return error;
}
int brc_add_dp(struct datapath *dp)
dp_del_if_hook = brc_sysfs_del_if;
#endif
+ /* Randomize the initial sequence number. This is not a security
+ * feature; it only helps avoid crossed wires between userspace and
+ * the kernel when the module is unloaded and reloaded. */
+ brc_seq = net_random();
+
/* Register generic netlink family to communicate changes to
* userspace. */
err = genl_register_family(&brc_genl_family);
\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.
+.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
.
#include "cfg.h"
#include "command-line.h"
#include "daemon.h"
+#include "dirs.h"
#include "dpif.h"
#include "fatal-signal.h"
#include "fault.h"
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);
/* Config file shared with vswitchd (usually vswitchd.conf). */
static char *config_file;
-/* Filename containing vswitchd pid. */
-static char *vswitchd_pidfile;
+/* 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;
return cfg_has_section("bridge.%s", name);
}
-/* Tell vswitchd reconfigure itself. */
-static void
-force_reconfigure(void)
+static int
+rewrite_and_reload_config(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;
+ 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
cfg_del_match("bridge.*.port=%s", delete.names[i]);
cfg_del_match("bonding.*.slave=%s", delete.names[i]);
}
- cfg_write();
- cfg_read();
-
+ rewrite_and_reload_config();
cfg_unlock();
-
- /* Ask vswitchd to reconfigure itself. */
- force_reconfigure();
} else {
cfg_unlock();
}
svec_destroy(&delete);
}
-/* 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)
-{
- int error1, error2;
- 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);
- cfg_del_match("bonding.*.slave=%s", port_name);
- cfg_del_match("vlan.%s.*", port_name);
- break;
- }
-
- error1 = cfg_write();
- error2 = cfg_read();
+/* 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;
- return error1 ? error1 : error2;
+ filename = xasprintf("/sys/class/net/%s", name);
+ error = stat(filename, &s);
+ free(filename);
+ return !error;
}
-static int
+static int
add_bridge(const char *br_name)
{
- int retval;
-
if (bridge_exists(br_name)) {
VLOG_WARN("addbr %s: bridge %s exists", br_name, br_name);
return EEXIST;
+ } else if (netdev_exists(br_name)) {
+ VLOG_WARN("addbr %s: cannot create bridge %s because a network device "
+ "named %s already exists", br_name, br_name, br_name);
+ return EEXIST;
}
- retval = modify_config(br_name, NULL, BMC_ADD_DP);
- if (retval) {
- VLOG_WARN("addbr %s: config file update failed: %s",
- br_name, strerror(retval));
- return retval;
- }
-
+ cfg_add_entry("bridge.%s.port=%s", br_name, br_name);
VLOG_INFO("addbr %s: success", br_name);
- /* Ask vswitchd to reconfigure itself. */
- force_reconfigure();
-
return 0;
}
static int
del_bridge(const char *br_name)
{
- int retval;
-
if (!bridge_exists(br_name)) {
VLOG_WARN("delbr %s: no bridge named %s", br_name, br_name);
return ENXIO;
}
- retval = modify_config(br_name, NULL, BMC_DEL_DP);
- if (retval) {
- VLOG_WARN("delbr %s: config file update failed: %s",
- br_name, strerror(retval));
- return retval;
- }
-
- /* Ask vswitchd to reconfigure itself. */
- force_reconfigure();
-
+ cfg_del_section("bridge.%s", br_name);
VLOG_INFO("delbr %s: success", br_name);
return 0;
}
-static int
-handle_bridge_cmd(struct ofpbuf *buffer, bool add)
+static int
+parse_command(struct ofpbuf *buffer, uint32_t *seq, const char **br_name,
+ const char **port_name)
{
- int br_act_err;
- int retval;
- struct nlattr *attrs[ARRAY_SIZE(brc_dp_policy)];
- const char *br_name;
- struct ofpbuf msg;
+ 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, brc_dp_policy,
- attrs, ARRAY_SIZE(brc_dp_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;
}
- 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);
+ *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;
+}
- /* Notify the brcompat kernel module of the result. */
+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);
- nl_msg_put_u32(&msg, BRC_GENL_A_ERR_CODE, br_act_err);
+ 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, "handle_bridge_cmd: %s", strerror(retval));
+ VLOG_WARN_RL(&rl, "replying to brcompat request: %s",
+ strerror(retval));
}
ofpbuf_uninit(&msg);
+}
- return br_act_err;
+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_PORT_NAME] = { .type = NL_A_STRING },
};
-static int
+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";
- 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)) {
- VLOG_WARN("%s %s %s: no bridge named %s",
- cmd_name, br_name, port_name, br_name);
- return EINVAL;
- }
+ uint32_t seq;
+ int error;
- 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) {
- VLOG_WARN("%s %s %s: config file update failed: %s",
- cmd_name, br_name, port_name, strerror(retval));
- return retval;
+ 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);
}
- VLOG_INFO("%s %s %s: success", cmd_name, br_name, port_name);
-
- /* Ask vswitchd to reconfigure itself. */
- force_reconfigure();
-
- return 0;
+ return error;
}
-static int
+static int
brc_recv_update(void)
{
int retval;
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);
+ del_port(br_name, port_name);
+ rewrite_and_reload_config();
}
cfg_unlock();
}
}
}
- /* 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 (;;) {
enum {
OPT_LOCK_TIMEOUT = UCHAR_MAX + 1,
OPT_PRUNE_TIMEOUT,
- OPT_VSWITCHD_PIDFILE,
+ OPT_RELOAD_COMMAND,
VLOG_OPTION_ENUMS,
LEAK_CHECKER_OPTION_ENUMS
};
{"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},
+ {"reload-command", required_argument, 0, OPT_RELOAD_COMMAND},
DAEMON_LONG_OPTIONS,
VLOG_LONG_OPTIONS,
LEAK_CHECKER_LONG_OPTIONS,
char *short_options = long_options_to_short_options(long_options);
int error;
+ reload_command = xasprintf("ovs-appctl "
+ "-t %s/vswitchd.`cat %s/vswitchd.pid`.ctl "
+ "-e vswitchd/reload 2>&1 "
+ "| /usr/bin/logger -t brcompatd-reload",
+ ovs_rundir, ovs_rundir);
for (;;) {
int c;
prune_timeout = atoi(optarg) * 1000;
break;
- case OPT_VSWITCHD_PIDFILE:
- vswitchd_pidfile = optarg;
+ case OPT_RELOAD_COMMAND:
+ reload_command = optarg;
break;
VLOG_OPTION_HANDLERS
"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"
+ " --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("Other options:\n"
+ 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);
}
action "Starting brcompatd ($strace_opt$valgrind_opt)" true
(nice -n "$VSWITCHD_PRIORITY" $strace_opt $valgrind_opt "$brcompatd" -P$BRCOMPATD_PIDFILE --vswitchd-pidfile=$VSWITCHD_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" -P$BRCOMPATD_PIDFILE --vswitchd-pidfile=$VSWITCHD_PIDFILE -D -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF"
+ action "Starting brcompatd" nice -n "$BRCOMPATD_PRIORITY" $strace_opt $valgrind_opt "$brcompatd" -P$BRCOMPATD_PIDFILE -D -vANY:CONSOLE:EMER $syslog_opt $logfile_level_opt $logfile_file_opt $leak_opt "$VSWITCHD_CONF"
fi
}