--- /dev/null
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <net/genetlink.h>
+#include "openflow/brcompat-netlink.h"
+
+/* This code implements a Generic Netlink command BRC_GENL_C_SET_PROC that can
+ * be used to add, modify, and delete arbitrary files in selected
+ * subdirectories of /proc. It's a horrible kluge prompted by the need to
+ * simulate certain /proc/net/vlan and /proc/net/bonding files for software
+ * that wants to read them, and with any luck it will go away eventually.
+ *
+ * The implementation is a kluge too. In particular, we want to release the
+ * strings copied into the 'data' members of proc_dir_entry when the
+ * proc_dir_entry structures are freed, but there doesn't appear to be a way to
+ * hook that, so instead we have to rely on being the only entity modifying the
+ * directories in question.
+ */
+
+static int brc_seq_show(struct seq_file *seq, void *unused)
+{
+ seq_puts(seq, seq->private);
+ return 0;
+}
+
+static int brc_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, brc_seq_show, PDE(inode)->data);
+}
+
+static struct file_operations brc_fops = {
+ .owner = THIS_MODULE,
+ .open = brc_seq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct proc_dir_entry *proc_vlan_dir;
+static struct proc_dir_entry *proc_bonding_dir;
+
+struct proc_dir_entry *brc_lookup_entry(struct proc_dir_entry *de, const char *name)
+{
+ int namelen = strlen(name);
+ for (de = de->subdir; de; de = de->next) {
+ if (de->namelen != namelen)
+ continue;
+ if (!memcmp(name, de->name, de->namelen))
+ return de;
+ }
+ return NULL;
+}
+
+static struct proc_dir_entry *brc_open_dir(const char *dir_name,
+ struct proc_dir_entry *parent,
+ struct proc_dir_entry **dirp)
+{
+ if (!*dirp) {
+ struct proc_dir_entry *dir;
+ if (brc_lookup_entry(parent, dir_name)) {
+ printk(KERN_WARNING "%s proc directory exists, can't "
+ "simulate--probably its real module is "
+ "loaded\n", dir_name);
+ return NULL;
+ }
+ dir = *dirp = proc_mkdir(dir_name, parent);
+ }
+ return *dirp;
+}
+
+int brc_genl_set_proc(struct sk_buff *skb, struct genl_info *info)
+{
+ struct proc_dir_entry *dir, *entry;
+ const char *dir_name, *name;
+ char *data;
+
+ /* The following would be much simpler if we could depend on supporting
+ * NLA_NUL_STRING and the .len member in Generic Netlink policy, but
+ * upstream 2.6.18 does not have either. */
+ if (!info->attrs[BRC_GENL_A_PROC_DIR] ||
+ VERIFY_NUL_STRING(info->attrs[BRC_GENL_A_PROC_DIR]) ||
+ !info->attrs[BRC_GENL_A_PROC_NAME] ||
+ VERIFY_NUL_STRING(info->attrs[BRC_GENL_A_PROC_NAME]) ||
+ (info->attrs[BRC_GENL_A_PROC_DATA] &&
+ VERIFY_NUL_STRING(info->attrs[BRC_GENL_A_PROC_DATA])))
+ return -EINVAL;
+
+ dir_name = nla_data(info->attrs[BRC_GENL_A_PROC_DIR]);
+ name = nla_data(info->attrs[BRC_GENL_A_PROC_NAME]);
+ if (strlen(dir_name) > 32 || strlen(name) > 32)
+ return -EINVAL;
+
+ if (!strcmp(dir_name, "net/vlan"))
+ dir = brc_open_dir("vlan", proc_net, &proc_vlan_dir);
+ else if (!strcmp(dir_name, "net/bonding"))
+ dir = brc_open_dir("bonding", proc_net, &proc_bonding_dir);
+ else
+ return -EINVAL;
+ if (!dir) {
+ /* Probably failed because the module that really implements
+ * the function in question is loaded and already owns the
+ * directory in question.*/
+ return -EBUSY;
+ }
+
+ entry = brc_lookup_entry(dir, name);
+ if (!info->attrs[BRC_GENL_A_PROC_DATA]) {
+ if (!entry)
+ return -ENOENT;
+
+ data = entry->data;
+ remove_proc_entry(name, dir);
+ if (brc_lookup_entry(dir, name))
+ return -EBUSY; /* Shouldn't happen */
+
+ kfree(data);
+ } else {
+ data = kstrdup(nla_data(info->attrs[BRC_GENL_A_PROC_DATA]),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (entry) {
+ char *old_data = entry->data;
+ entry->data = data;
+ kfree(old_data);
+ return 0;
+ }
+
+ entry = create_proc_entry(name, S_IFREG|S_IRUSR|S_IWUSR, dir);
+ if (!entry) {
+ kfree(data);
+ return -ENOBUFS;
+ }
+ entry->proc_fops = &brc_fops;
+ entry->data = data;
+ }
+ return 0;
+}
+
+static void kill_proc_dir(const char *dir_name,
+ struct proc_dir_entry *parent,
+ struct proc_dir_entry *dir)
+{
+ if (!dir)
+ return;
+ for (;;) {
+ struct proc_dir_entry *e;
+ char *data;
+ char name[33];
+
+ e = dir->subdir;
+ if (!e)
+ break;
+
+ if (e->namelen >= sizeof name) {
+ /* Can't happen: we prevent adding names this long by
+ * limiting the BRC_GENL_A_PROC_NAME string to 32
+ * bytes. */
+ WARN_ON(1);
+ break;
+ }
+ strcpy(name, e->name);
+
+ data = e->data;
+ e->data = NULL;
+ kfree(data);
+
+ remove_proc_entry(name, dir);
+ }
+ remove_proc_entry(dir_name, parent);
+}
+
+void brc_procfs_exit(void)
+{
+ kill_proc_dir("vlan", proc_net, proc_vlan_dir);
+ kill_proc_dir("bonding", proc_net, proc_bonding_dir);
+}
--- /dev/null
+#ifndef BRC_PROCFS_H
+#define BRC_PROCFS_H 1
+
+struct sk_buff;
+struct genl_info;
+
+void brc_procfs_exit(void);
+int brc_genl_set_proc(struct sk_buff *skb, struct genl_info *info);
+
+#endif /* brc_procfs.h */
+
#include "compat.h"
#include "openflow/openflow-netlink.h"
#include "openflow/brcompat-netlink.h"
+#include "brc_procfs.h"
#include "brc_sysfs.h"
#include "datapath.h"
#include "dp_dev.h"
/* Attribute policy: what each attribute may contain. */
static struct nla_policy brc_genl_policy[BRC_GENL_A_MAX + 1] = {
- [BRC_GENL_A_ERR_CODE] = { .type = NLA_U32 }
+ [BRC_GENL_A_ERR_CODE] = { .type = NLA_U32 },
+ [BRC_GENL_A_PROC_DIR] = { .type = NLA_NUL_STRING },
+ [BRC_GENL_A_PROC_NAME] = { .type = NLA_NUL_STRING },
+ [BRC_GENL_A_PROC_DATA] = { .type = NLA_NUL_STRING },
};
static int
.dumpit = NULL
};
+static struct genl_ops brc_genl_ops_set_proc = {
+ .cmd = BRC_GENL_C_SET_PROC,
+ .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privelege. */
+ .policy = brc_genl_policy,
+ .doit = brc_genl_set_proc,
+ .dumpit = NULL
+};
+
int dp_exists(const char *dp_name)
{
struct net_device *dev;
if (err != 0)
goto err_unregister;
+ err = genl_register_ops(&brc_genl_family, &brc_genl_ops_set_proc);
+ if (err != 0)
+ goto err_unregister;
+
strcpy(brc_mc_group.name, "brcompat");
err = genl_register_mc_group(&brc_genl_family, &brc_mc_group);
if (err < 0)
brioctl_set(NULL);
genl_unregister_family(&brc_genl_family);
+ brc_procfs_exit();
}
module_init(brc_init);
brcompat_sources = \
linux-2.6/compat-2.6/genetlink-brcompat.c \
brcompat.c \
+ brc_procfs.c \
brc_sysfs_dp.c \
brc_sysfs_if.c
brcompat_headers = \
+ brc_procfs.h \
brc_sysfs.h
dist_modules += veth
}
#endif /* linux kernel < 2.6.26 */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+#define proc_net init_net.proc_net
+#endif
+
#ifndef for_each_netdev
/* Linux before 2.6.22 didn't have for_each_netdev at all. */
#define for_each_netdev(net, d) for (d = dev_base; d; d = d->next)
#define for_each_netdev(net,d) list_for_each_entry(d, &dev_base_head, dev_list)
#endif
+
+
#endif
BRC_GENL_A_PORT_NAME, /* Interface name. */
BRC_GENL_A_ERR_CODE, /* Positive error code. */
BRC_GENL_A_MC_GROUP, /* Generic netlink multicast group. */
+ BRC_GENL_A_PROC_DIR, /* Name of subdirectory in /proc. */
+ BRC_GENL_A_PROC_NAME, /* Name of file in /proc. */
+ BRC_GENL_A_PROC_DATA, /* Contents of file in /proc. */
__BRC_GENL_A_MAX,
BRC_GENL_A_MAX = __BRC_GENL_A_MAX - 1
BRC_GENL_C_PORT_ADD, /* K: Port added to datapath. */
BRC_GENL_C_PORT_DEL, /* K: Port removed from datapath. */
BRC_GENL_C_QUERY_MC, /* U: Get multicast group for brcompat. */
+ BRC_GENL_C_SET_PROC, /* U: Set contents of file in /proc. */
__BRC_GENL_C_MAX,
BRC_GENL_C_MAX = __BRC_GENL_C_MAX - 1
VLOG_MODULE(pcap)
VLOG_MODULE(poll_loop)
VLOG_MODULE(port_watcher)
+VLOG_MODULE(proc_net_compat)
VLOG_MODULE(process)
VLOG_MODULE(secchan)
VLOG_MODULE(rconn)
vswitchd/mgmt.h \
vswitchd/port.c \
vswitchd/port.h \
+ vswitchd/proc-net-compat.c \
vswitchd/vswitchd.c \
vswitchd/vswitchd.h \
vswitchd/xenserver.c \
#include "ofpbuf.h"
#include "poll-loop.h"
#include "port-array.h"
+#include "proc-net-compat.h"
#include "process.h"
#include "secchan/ofproto.h"
#include "socket-util.h"
static struct port *port_lookup(const struct bridge *, const char *name);
static struct port *port_from_dp_ifidx(const struct bridge *,
uint16_t dp_ifidx);
+static void port_update_bond_compat(struct port *);
+static void port_update_vlan_compat(struct port *);
static void mirror_create(struct bridge *, const char *name);
static void mirror_destroy(struct mirror *);
i++;
}
}
+ LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
+ for (i = 0; i < br->n_ports; i++) {
+ struct port *port = br->ports[i];
+ port_update_vlan_compat(port);
+ }
+ }
LIST_FOR_EACH (br, struct bridge, node, &all_bridges) {
brstp_reconfigure(br);
}
bridge_flush(br);
} else {
+ memcpy(iface->mac, opp->hw_addr, ETH_ADDR_LEN);
if (port->n_ifaces > 1) {
bool up = !(opp->state & OFPPS_LINK_DOWN);
bond_link_status_update(iface, up);
+ port_update_bond_compat(port);
}
- memcpy(iface->mac, opp->hw_addr, ETH_ADDR_LEN);
}
}
struct port *del;
size_t i;
+ proc_net_compat_update_vlan(port->name, NULL, 0);
+
for (i = 0; i < MAX_MIRRORS; i++) {
struct mirror *m = br->mirrors[i];
if (m && m->out_port == port) {
{
if (port->n_ifaces < 2) {
/* Not a bonded port. */
- free(port->bond_hash);
- port->bond_hash = NULL;
+ if (port->bond_hash) {
+ free(port->bond_hash);
+ port->bond_hash = NULL;
+ proc_net_compat_update_bond(port->name, NULL);
+ }
} else {
if (!port->bond_hash) {
size_t i;
port->no_ifaces_tag = tag_create_random();
bond_choose_active_iface(port);
}
+ port_update_bond_compat(port);
+ }
+}
+
+static void
+port_update_bond_compat(struct port *port)
+{
+ struct compat_bond bond;
+ size_t i;
+
+ if (port->n_ifaces < 2) {
+ return;
+ }
+
+ bond.up = false;
+ bond.updelay = port->updelay;
+ bond.downdelay = port->downdelay;
+ bond.n_slaves = port->n_ifaces;
+ bond.slaves = xmalloc(port->n_ifaces * sizeof *bond.slaves);
+ for (i = 0; i < port->n_ifaces; i++) {
+ struct iface *iface = port->ifaces[i];
+ struct compat_bond_slave *slave = &bond.slaves[i];
+ slave->name = iface->name;
+ slave->up = ((iface->enabled && iface->delay_expires == LLONG_MAX) ||
+ (!iface->enabled && iface->delay_expires != LLONG_MAX));
+ if (slave->up) {
+ bond.up = true;
+ }
+ memcpy(slave->mac, iface->mac, ETH_ADDR_LEN);
+ }
+ proc_net_compat_update_bond(port->name, &bond);
+ free(bond.slaves);
+}
+
+static void
+port_update_vlan_compat(struct port *port)
+{
+ struct bridge *br = port->bridge;
+ char *vlandev_name = NULL;
+
+ if (port->vlan > 0) {
+ /* Figure out the name that the VLAN device should actually have, if it
+ * existed. This takes some work because the VLAN device would not
+ * have port->name in its name; rather, it would have the trunk port's
+ * name, and 'port' would be attached to a bridge that also had the
+ * VLAN device one of its ports. So we need to find a trunk port that
+ * includes port->vlan.
+ *
+ * There might be more than one candidate. This doesn't happen on
+ * XenServer, so if it happens we just pick the first choice in
+ * alphabetical order instead of creating multiple VLAN devices. */
+ size_t i;
+ for (i = 0; i < br->n_ports; i++) {
+ struct port *p = br->ports[i];
+ if (port_trunks_vlan(p, port->vlan)
+ && p->n_ifaces
+ && (!vlandev_name || strcmp(p->name, vlandev_name) <= 0))
+ {
+ const uint8_t *ea = p->ifaces[0]->mac;
+ if (!eth_addr_is_multicast(ea) &&
+ !eth_addr_is_reserved(ea) &&
+ !eth_addr_is_zero(ea)) {
+ vlandev_name = p->name;
+ }
+ }
+ }
}
+ proc_net_compat_update_vlan(port->name, vlandev_name, port->vlan);
}
\f
/* Interface functions. */
--- /dev/null
+/* Copyright (c) 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 "proc-net-compat.h"
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+#include "dynamic-string.h"
+#include "hash.h"
+#include "netlink-protocol.h"
+#include "netlink.h"
+#include "ofpbuf.h"
+#include "openflow/brcompat-netlink.h"
+#include "hmap.h"
+#include "shash.h"
+#include "svec.h"
+
+#define THIS_MODULE VLM_proc_net_compat
+#include "vlog.h"
+
+/* Netlink socket to bridge compatibility kernel module. */
+static struct nl_sock *brc_sock;
+
+/* The Generic Netlink family number used for bridge compatibility. */
+static int brc_family = 0;
+
+/* Rate limiting for log messages. */
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
+
+static void flush_dir(const char *dir);
+static int set_proc_file(const char *dir, const char *file, const char *data);
+
+/* Initializes the /proc/net compatibility layer. Returns 0 if successful,
+ * otherwise a positive errno value. */
+int
+proc_net_compat_init(void)
+{
+ if (!brc_sock) {
+ int retval = nl_lookup_genl_family(BRC_GENL_FAMILY_NAME, &brc_family);
+ if (retval) {
+ return retval;
+ }
+
+ retval = nl_sock_create(NETLINK_GENERIC, 0, 0, 0, &brc_sock);
+ if (retval) {
+ return retval;
+ }
+
+ flush_dir("/proc/net/vlan");
+ flush_dir("/proc/net/bonding");
+ }
+ return 0;
+}
+
+static int
+set_proc_file(const char *dir, const char *file, const char *data)
+{
+ struct ofpbuf request, *reply;
+ int retval;
+
+ ofpbuf_init(&request, 0);
+ nl_msg_put_genlmsghdr(&request, brc_sock, 1024, brc_family, NLM_F_REQUEST,
+ BRC_GENL_C_SET_PROC, 1);
+ nl_msg_put_string(&request, BRC_GENL_A_PROC_DIR, dir);
+ nl_msg_put_string(&request, BRC_GENL_A_PROC_NAME, file);
+ if (data) {
+ nl_msg_put_string(&request, BRC_GENL_A_PROC_DATA, data);
+ }
+
+ retval = nl_sock_transact(brc_sock, &request, &reply);
+ ofpbuf_uninit(&request);
+ ofpbuf_delete(reply);
+ if (retval) {
+ VLOG_WARN_RL(&rl, "failed to %s /proc/%s/%s (%s)",
+ data ? "update" : "remove", dir, file, strerror(retval));
+ }
+ return retval;
+}
+
+static void
+flush_dir(const char *dir)
+{
+ const char *subdir;
+ struct dirent *de;
+ DIR *stream;
+
+ assert(!memcmp(dir, "/proc/", 6));
+ subdir = dir + 6;
+
+ stream = opendir(dir);
+ if (!stream) {
+ if (errno != ENOENT) {
+ VLOG_WARN_RL(&rl, "%s: open failed (%s)", dir, strerror(errno));
+ }
+ return;
+ }
+
+ while ((de = readdir(stream)) != NULL) {
+ if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
+ set_proc_file(subdir, de->d_name, NULL);
+ }
+ }
+ closedir(stream);
+}
+\f
+/* If 'bond' is nonnull, creates a file in /proc/net/bonding for a bond with
+ * the given 'name' and the details in 'bond'. If 'bond' is null, deletes
+ * the /proc/net/bonding file with the given 'name'.
+ *
+ * This function has no effect unless proc_net_compat_init() has been
+ * called. */
+void
+proc_net_compat_update_bond(const char *name, const struct compat_bond *bond)
+{
+ struct ds ds;
+ int i;
+
+ if (!brc_sock) {
+ return;
+ }
+
+ if (!bond) {
+ set_proc_file("net/bonding", name, NULL);
+ return;
+ }
+
+ ds_init(&ds);
+ ds_put_format(
+ &ds,
+ "Ethernet Channel Bonding Driver: vswitchd "
+ VERSION BUILDNR" ("__DATE__" "__TIME__")\n"
+ "Bonding Mode: source load balancing\n"
+ "Primary Slave: None\n"
+ "Currently Active Slave: None\n"
+ "MII Status: %s\n"
+ "MII Polling Interval (ms): 100\n"
+ "Up Delay (ms): %d\n"
+ "Down Delay (ms): %d\n"
+ "\n"
+ "Source load balancing info:\n",
+ bond->up ? "up" : "down", bond->updelay, bond->downdelay);
+ for (i = 0; i < bond->n_slaves; i++) {
+ const struct compat_bond_slave *slave = &bond->slaves[i];
+ ds_put_format(
+ &ds,
+ "\n"
+ "Slave Interface: %s\n"
+ "MII Status: %s\n"
+ "Link Failure Count: 0\n"
+ "Permanent HW addr: "ETH_ADDR_FMT"\n",
+ slave->name, slave->up ? "up" : "down",
+ ETH_ADDR_ARGS(slave->mac));
+ }
+ set_proc_file("net/bonding", name, ds_cstr(&ds));
+ ds_destroy(&ds);
+}
+\f
+/* /proc/net/vlan compatibility.
+ *
+ * This is much more complex than I expected it to be. */
+
+struct compat_vlan {
+ /* Hash key. */
+ struct hmap_node trunk_node; /* Hash map node. */
+ char *trunk_dev; /* Name of trunk network device. */
+ int vid; /* VLAN number. */
+
+ /* Auxiliary data. */
+ char *vlan_dev; /* sprintf("%s.%d", trunk_dev, vid); */
+ struct svec tagged_devs; /* Name of tagged network device(s). */
+};
+
+/* Current set of VLAN devices, indexed two different ways. */
+static struct hmap vlans_by_trunk = HMAP_INITIALIZER(&vlans_by_trunk);
+static struct shash vlans_by_tagged = SHASH_INITIALIZER(&vlans_by_tagged);
+
+static bool remove_tagged_dev(struct shash_node *, const char *tagged_dev);
+static void update_vlan_config(void);
+static void set_vlan_proc_file(const struct compat_vlan *);
+static uint32_t hash_vlan(const char *trunk_dev, uint32_t vid);
+
+/* Updates the /proc/net/vlan compatibility layer's idea of what trunk device
+ * and VLAN the given 'tagged_dev' is associated with. If 'tagged_dev' has an
+ * implicit VLAN tag, then 'trunk_dev' should be the name of a network device
+ * on the same bridge that trunks that VLAN, and 'vid' should be the VLAN tag
+ * number. If 'tagged_dev' does not have an implicit VLAN tag, then
+ * 'trunk_dev' should be NULL and 'vid' should be -1.
+ *
+ * This function has no effect unless proc_net_compat_init() has been
+ * called. */
+void
+proc_net_compat_update_vlan(const char *tagged_dev, const char *trunk_dev,
+ int vid)
+{
+ struct compat_vlan *vlan;
+ struct shash_node *node;
+
+ if (!brc_sock) {
+ return;
+ }
+
+ /* Find the compat_vlan that we currently have for 'tagged_dev' (if
+ * any). */
+ node = shash_find(&vlans_by_tagged, tagged_dev);
+ vlan = node ? node->data : NULL;
+ if (vid <= 0 || !trunk_dev) {
+ if (vlan) {
+ if (remove_tagged_dev(node, tagged_dev)) {
+ update_vlan_config();
+ }
+ }
+ } else {
+ if (vlan) {
+ if (!strcmp(trunk_dev, vlan->trunk_dev) && vid == vlan->vid) {
+ /* No change. */
+ return;
+ } else {
+ /* 'tagged_dev' is attached to the wrong compat_vlan. Start
+ * by removing it from that one. */
+ remove_tagged_dev(node, tagged_dev);
+ node = NULL;
+ vlan = NULL;
+ }
+ }
+
+ /* 'tagged_dev' is not attached to any compat_vlan. Find the
+ * compat_vlan corresponding to (trunk_dev,vid) to attach it to, or
+ * create a new compat_vlan if none exists for (trunk_dev,vid). */
+ HMAP_FOR_EACH_WITH_HASH (vlan, struct compat_vlan, trunk_node,
+ hash_vlan(trunk_dev, vid),
+ &vlans_by_trunk) {
+ if (!strcmp(trunk_dev, vlan->trunk_dev) && vid == vlan->vid) {
+ break;
+ }
+ }
+ if (!vlan) {
+ /* Create a new compat_vlan for (trunk_dev,vid). */
+ vlan = xcalloc(1, sizeof *vlan);
+ vlan->trunk_dev = xstrdup(trunk_dev);
+ vlan->vid = vid;
+ vlan->vlan_dev = xasprintf("%s.%d", trunk_dev, vid);
+ svec_init(&vlan->tagged_devs);
+ hmap_insert(&vlans_by_trunk, &vlan->trunk_node,
+ hash_vlan(trunk_dev, vid));
+ set_vlan_proc_file(vlan);
+ }
+
+ /* Attach 'tagged_dev' to 'vlan'. */
+ svec_add(&vlan->tagged_devs, tagged_dev);
+ shash_add(&vlans_by_tagged, tagged_dev, vlan);
+ svec_sort(&vlan->tagged_devs);
+ update_vlan_config();
+ }
+}
+
+/* Remove 'tagged_dev' from the compat_vlan in 'node'. If that causes the
+ * compat_vlan to have no tagged_devs left, destroy the compat_vlan too. */
+static bool
+remove_tagged_dev(struct shash_node *node, const char *tagged_dev)
+{
+ struct compat_vlan *vlan = node->data;
+
+ svec_del(&vlan->tagged_devs, tagged_dev);
+ shash_delete(&vlans_by_tagged, node);
+ if (!vlan->tagged_devs.n) {
+ set_proc_file("net/vlan", vlan->vlan_dev, NULL);
+
+ hmap_remove(&vlans_by_trunk, &vlan->trunk_node);
+ svec_destroy(&vlan->tagged_devs);
+ free(vlan->trunk_dev);
+ free(vlan->vlan_dev);
+ free(vlan);
+ return true;
+ }
+ return false;
+}
+
+/* Returns a hash value for (trunk_dev,vid). */
+static uint32_t
+hash_vlan(const char *trunk_dev, uint32_t vid)
+{
+ return hash_words(&vid, 1, hash_bytes(trunk_dev, strlen(trunk_dev), 0));
+}
+
+/* Update /proc/net/vlan/<vlan_dev> for 'vlan'. */
+static void
+set_vlan_proc_file(const struct compat_vlan *vlan)
+{
+ struct ds ds;
+
+ ds_init(&ds);
+ ds_put_format(
+ &ds,
+ "%s VID: %d REORDER_HDR: 1 dev->priv_flags: 81\n"
+ " total frames received 0\n"
+ " total bytes received 0\n"
+ " Broadcast/Multicast Rcvd 0\n"
+ "\n"
+ " total frames transmitted 0\n"
+ " total bytes transmitted 0\n"
+ " total headroom inc 0\n"
+ " total encap on xmit 0\n"
+ "Device: %s\n"
+ "INGRESS priority mappings: 0:0 1:0 2:0 3:0 4:0 5:0 6:0 7:0\n"
+ "EGRESSS priority Mappings: \n",
+ vlan->vlan_dev, vlan->vid, vlan->trunk_dev);
+ set_proc_file("net/vlan", vlan->vlan_dev, ds_cstr(&ds));
+ ds_destroy(&ds);
+}
+
+/* Update /proc/net/vlan/config. */
+static void
+update_vlan_config(void)
+{
+ struct compat_vlan *vlan;
+ struct ds ds;
+
+ ds_init(&ds);
+ ds_put_cstr(&ds, "VLAN Dev name | VLAN ID\n"
+ "Name-Type: VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD\n");
+ HMAP_FOR_EACH (vlan, struct compat_vlan, trunk_node, &vlans_by_trunk) {
+ ds_put_format(&ds, "%-15s| %d | %s\n",
+ vlan->vlan_dev, vlan->vid, vlan->trunk_dev);
+ }
+ set_proc_file("net/vlan", "config", ds_cstr(&ds));
+ ds_destroy(&ds);
+}
--- /dev/null
+/* Copyright (c) 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_PROC_NET_COMPAT_H
+#define VSWITCHD_PROC_NET_COMPAT_H 1
+
+#include "packets.h"
+
+struct compat_bond {
+ bool up;
+ int updelay;
+ int downdelay;
+ int n_slaves;
+ struct compat_bond_slave *slaves;
+};
+
+struct compat_bond_slave {
+ const char *name;
+ bool up;
+ uint8_t mac[ETH_ADDR_LEN];
+};
+
+int proc_net_compat_init(void);
+void proc_net_compat_update_bond(const char *name, const struct compat_bond *);
+void proc_net_compat_update_vlan(const char *dev, const char *vlandev,
+ int vlan);
+
+#endif /* vswitchd/proc-net-compat.h */
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
+.IP "\fB--fake-proc-net\fR"
+Causes \fBvswitchd\fR to simulate some files in \fB/proc/net/vlan\fR
+and \fB/proc/net/bonding\fR that some legacy software expects to
+exist. This option should only be used if such legacy software is
+actually in use. It requires the \fBbrcompat_mod.ko\fR kernel module
+to be loaded.
.
.so lib/daemon.man
.so lib/vlog.man
#include "mgmt.h"
#include "poll-loop.h"
#include "port.h"
+#include "proc-net-compat.h"
#include "process.h"
#include "signals.h"
#include "svec.h"
{
enum {
OPT_PEER_CA_CERT = UCHAR_MAX + 1,
+ OPT_FAKE_PROC_NET,
VLOG_OPTION_ENUMS,
LEAK_CHECKER_OPTION_ENUMS
};
static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'},
+ {"fake-proc-net", no_argument, 0, OPT_FAKE_PROC_NET},
DAEMON_LONG_OPTIONS,
VLOG_LONG_OPTIONS,
LEAK_CHECKER_LONG_OPTIONS,
argv[0]);
exit(EXIT_SUCCESS);
+ case OPT_FAKE_PROC_NET:
+ error = proc_net_compat_init();
+ if (error) {
+ ofp_fatal(error, "failed to initialize /proc/net "
+ "compatibility");
+ }
+ break;
+
VLOG_OPTION_HANDLERS
DAEMON_OPTION_HANDLERS
VCONN_SSL_OPTION_HANDLERS
program_name, program_name);
daemon_usage();
vlog_usage();
- printf("\nOther options:\n"
+ printf("\nLegacy compatibility options:\n"
+ " --fake-proc-net simulate some files in /proc/net\n"
+ "\nOther options:\n"
" -h, --help display this help message\n"
" -V, --version display version information\n");
leak_checker_usage();