aboutsummaryrefslogtreecommitdiff
path: root/vswitchd
diff options
context:
space:
mode:
authorBen Pfaff <blp@nicira.com>2011-11-23 17:03:31 -0800
committerBen Pfaff <blp@nicira.com>2011-11-23 17:03:31 -0800
commit52a90c29ab472076fb8f20fba4f847350268e01e (patch)
treee88de67d71f96c5af55b41950ed6380b01b268c3 /vswitchd
parente84173dc0c6df7b1816514c3c8702bcec81fc45b (diff)
Implement new "VLAN splinters" feature.
The "VLAN splinters" feature works around buggy device drivers in old Linux versions. This feature is deprecated. When broken device drivers are no longer in widespread use, we will delete this feature. I tested earlier versions of this commit, but I have not tested this version. See ovs-vswitchd.conf.db(5) for more information.
Diffstat (limited to 'vswitchd')
-rw-r--r--vswitchd/bridge.c330
-rw-r--r--vswitchd/vswitch.xml67
2 files changed, 393 insertions, 4 deletions
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index ca106c5c..32885ebc 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -45,6 +45,7 @@
#include "timeval.h"
#include "util.h"
#include "unixctl.h"
+#include "vlandev.h"
#include "vswitchd/vswitch-idl.h"
#include "xenserver.h"
#include "vlog.h"
@@ -143,7 +144,8 @@ static unixctl_cb_func bridge_unixctl_dump_flows;
static unixctl_cb_func bridge_unixctl_reconnect;
static size_t bridge_get_controllers(const struct bridge *br,
struct ovsrec_controller ***controllersp);
-static void bridge_add_del_ports(struct bridge *);
+static void bridge_add_del_ports(struct bridge *,
+ const unsigned long int *splinter_vlans);
static void bridge_add_ofproto_ports(struct bridge *);
static void bridge_del_ofproto_ports(struct bridge *);
static void bridge_refresh_ofp_port(struct bridge *);
@@ -209,11 +211,32 @@ static void iface_refresh_cfm_stats(struct iface *);
static void iface_refresh_stats(struct iface *);
static void iface_refresh_status(struct iface *);
static bool iface_is_synthetic(const struct iface *);
+static const char *get_interface_other_config(const struct ovsrec_interface *,
+ const char *key,
+ const char *default_value);
static void shash_from_ovs_idl_map(char **keys, char **values, size_t n,
struct shash *);
static void shash_to_ovs_idl_map(struct shash *,
char ***keys, char ***values, size_t *n);
+
+/* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
+ *
+ * This is deprecated. It is only for compatibility with broken device drivers
+ * in old versions of Linux that do not properly support VLANs when VLAN
+ * devices are not used. When broken device drivers are no longer in
+ * widespread use, we will delete these interfaces. */
+
+/* True if VLAN splinters are enabled on any interface, false otherwise.*/
+static bool vlan_splinters_enabled_anywhere;
+
+static bool vlan_splinters_is_enabled(const struct ovsrec_interface *);
+static unsigned long int *collect_splinter_vlans(
+ const struct ovsrec_open_vswitch *);
+static void configure_splinter_port(struct port *);
+static void add_vlan_splinter_ports(struct bridge *,
+ const unsigned long int *splinter_vlans,
+ struct shash *ports);
/* Public functions. */
@@ -362,6 +385,7 @@ collect_in_band_managers(const struct ovsrec_open_vswitch *ovs_cfg,
static void
bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
{
+ unsigned long int *splinter_vlans;
struct sockaddr_in *managers;
struct bridge *br, *next;
int sflow_bridge_number;
@@ -376,9 +400,11 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
* This is mostly an update to bridge data structures. Very little is
* pushed down to ofproto or lower layers. */
add_del_bridges(ovs_cfg);
+ splinter_vlans = collect_splinter_vlans(ovs_cfg);
HMAP_FOR_EACH (br, node, &all_bridges) {
- bridge_add_del_ports(br);
+ bridge_add_del_ports(br, splinter_vlans);
}
+ free(splinter_vlans);
/* Delete all datapaths and datapath ports that are no longer configured.
*
@@ -489,6 +515,11 @@ port_configure(struct port *port)
struct ofproto_bundle_settings s;
struct iface *iface;
+ if (cfg->vlan_mode && !strcmp(cfg->vlan_mode, "splinter")) {
+ configure_splinter_port(port);
+ return;
+ }
+
/* Get name. */
s.name = port->name;
@@ -1094,6 +1125,12 @@ bridge_add_ofproto_ports(struct bridge *br)
VLOG_WARN("could not open network device %s (%s)",
iface->name, strerror(error));
}
+
+ if (iface->netdev
+ && port->cfg->vlan_mode
+ && !strcmp(port->cfg->vlan_mode, "splinter")) {
+ netdev_turn_flags_on(iface->netdev, NETDEV_UP, true);
+ }
} else {
error = 0;
}
@@ -1747,6 +1784,7 @@ bridge_run(void)
{
const struct ovsrec_open_vswitch *cfg;
+ bool vlan_splinters_changed;
bool datapath_destroyed;
bool database_changed;
struct bridge *br;
@@ -1794,7 +1832,19 @@ bridge_run(void)
stream_ssl_set_ca_cert_file(ssl->ca_cert, ssl->bootstrap_ca_cert);
}
- if (database_changed || datapath_destroyed) {
+ /* If VLAN splinters are in use, then we need to reconfigure if VLAN usage
+ * has changed. */
+ vlan_splinters_changed = false;
+ if (vlan_splinters_enabled_anywhere) {
+ HMAP_FOR_EACH (br, node, &all_bridges) {
+ if (ofproto_has_vlan_usage_changed(br->ofproto)) {
+ vlan_splinters_changed = true;
+ break;
+ }
+ }
+ }
+
+ if (database_changed || datapath_destroyed || vlan_splinters_changed) {
if (cfg) {
struct ovsdb_idl_txn *txn = ovsdb_idl_txn_create(idl);
@@ -2132,7 +2182,8 @@ bridge_get_controllers(const struct bridge *br,
/* Adds and deletes "struct port"s and "struct iface"s under 'br' to match
* those configured in 'br->cfg'. */
static void
-bridge_add_del_ports(struct bridge *br)
+bridge_add_del_ports(struct bridge *br,
+ const unsigned long int *splinter_vlans)
{
struct port *port, *next;
struct shash_node *node;
@@ -2165,6 +2216,10 @@ bridge_add_del_ports(struct bridge *br)
shash_add(&new_ports, br->name, &br->synth_local_port);
}
+ if (splinter_vlans) {
+ add_vlan_splinter_ports(br, splinter_vlans, &new_ports);
+ }
+
/* Get rid of deleted ports.
* Get rid of deleted interfaces on ports that still exist. */
HMAP_FOR_EACH_SAFE (port, next, hmap_node, &br->ports) {
@@ -3233,3 +3288,270 @@ mirror_configure(struct mirror *m, const struct ovsrec_mirror *cfg)
return true;
}
+
+/* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
+ *
+ * This is deprecated. It is only for compatibility with broken device drivers
+ * in old versions of Linux that do not properly support VLANs when VLAN
+ * devices are not used. When broken device drivers are no longer in
+ * widespread use, we will delete these interfaces. */
+
+static void **blocks;
+static size_t n_blocks, allocated_blocks;
+
+/* Adds 'block' to a list of blocks that have to be freed with free() when the
+ * VLAN splinters are reconfigured. */
+static void
+register_block(void *block)
+{
+ if (n_blocks >= allocated_blocks) {
+ blocks = x2nrealloc(blocks, &allocated_blocks, sizeof *blocks);
+ }
+ blocks[n_blocks++] = block;
+}
+
+/* Frees all of the blocks registered with register_block(). */
+static void
+free_registered_blocks(void)
+{
+ size_t i;
+
+ for (i = 0; i < n_blocks; i++) {
+ free(blocks[i]);
+ }
+ n_blocks = 0;
+}
+
+/* Returns true if VLAN splinters are enabled on 'iface_cfg', false
+ * otherwise. */
+static bool
+vlan_splinters_is_enabled(const struct ovsrec_interface *iface_cfg)
+{
+ const char *value;
+
+ value = get_interface_other_config(iface_cfg, "enable-vlan-splinters", "");
+ return !strcmp(value, "true");
+}
+
+/* Figures out the set of VLANs that are in use for the purpose of VLAN
+ * splinters.
+ *
+ * If VLAN splinters are enabled on at least one interface and any VLANs are in
+ * use, returns a 4096-bit bitmap with a 1-bit for each in-use VLAN (bits 0 and
+ * 4095 will not be set). The caller is responsible for freeing the bitmap,
+ * with free().
+ *
+ * If VLANs splinters are not enabled on any interface or if no VLANs are in
+ * use, returns NULL.
+ *
+ * Updates 'vlan_splinters_enabled_anywhere'. */
+static unsigned long int *
+collect_splinter_vlans(const struct ovsrec_open_vswitch *ovs_cfg)
+{
+ unsigned long int *splinter_vlans;
+ struct sset splinter_ifaces;
+ const char *real_dev_name;
+ struct shash *real_devs;
+ struct shash_node *node;
+ struct bridge *br;
+ size_t i;
+
+ splinter_vlans = NULL;
+ sset_init(&splinter_ifaces);
+ for (i = 0; i < ovs_cfg->n_bridges; i++) {
+ struct ovsrec_bridge *br_cfg = ovs_cfg->bridges[i];
+ size_t j;
+
+ for (j = 0; j < br_cfg->n_ports; j++) {
+ struct ovsrec_port *port_cfg = br_cfg->ports[j];
+ int k;
+
+ for (k = 0; k < port_cfg->n_interfaces; k++) {
+ struct ovsrec_interface *iface_cfg = port_cfg->interfaces[k];
+
+ if (vlan_splinters_is_enabled(iface_cfg)) {
+ sset_add(&splinter_ifaces, iface_cfg->name);
+
+ if (!splinter_vlans) {
+ splinter_vlans = bitmap_allocate(4096);
+ }
+ vlan_bitmap_from_array__(port_cfg->trunks,
+ port_cfg->n_trunks,
+ splinter_vlans);
+ }
+ }
+ }
+ }
+
+ vlan_splinters_enabled_anywhere = splinter_vlans != NULL;
+ if (!splinter_vlans) {
+ sset_destroy(&splinter_ifaces);
+ return NULL;
+ }
+
+ HMAP_FOR_EACH (br, node, &all_bridges) {
+ if (br->ofproto) {
+ ofproto_get_vlan_usage(br->ofproto, splinter_vlans);
+ }
+ }
+
+ /* Don't allow VLANs 0 or 4095 to be splintered. VLAN 0 should appear on
+ * the real device. VLAN 4095 is reserved and Linux doesn't allow a VLAN
+ * device to be created for it. */
+ bitmap_set0(splinter_vlans, 0);
+ bitmap_set0(splinter_vlans, 4095);
+
+ /* Delete all VLAN devices that we don't need. */
+ vlandev_refresh();
+ real_devs = vlandev_get_real_devs();
+ SHASH_FOR_EACH (node, real_devs) {
+ const struct vlan_real_dev *real_dev = node->data;
+ const struct vlan_dev *vlan_dev;
+ bool real_dev_has_splinters;
+
+ real_dev_has_splinters = sset_contains(&splinter_ifaces,
+ real_dev->name);
+ HMAP_FOR_EACH (vlan_dev, hmap_node, &real_dev->vlan_devs) {
+ if (!real_dev_has_splinters
+ || !bitmap_is_set(splinter_vlans, vlan_dev->vid)) {
+ struct netdev *netdev;
+
+ if (!netdev_open(vlan_dev->name, "system", &netdev)) {
+ if (!netdev_get_in4(netdev, NULL, NULL) ||
+ !netdev_get_in6(netdev, NULL)) {
+ vlandev_del(vlan_dev->name);
+ } else {
+ /* It has an IP address configured, so we don't own
+ * it. Don't delete it. */
+ }
+ netdev_close(netdev);
+ }
+ }
+
+ }
+ }
+
+ /* Add all VLAN devices that we need. */
+ SSET_FOR_EACH (real_dev_name, &splinter_ifaces) {
+ int vid;
+
+ BITMAP_FOR_EACH_1 (vid, 4096, splinter_vlans) {
+ if (!vlandev_get_name(real_dev_name, vid)) {
+ vlandev_add(real_dev_name, vid);
+ }
+ }
+ }
+
+ vlandev_refresh();
+
+ sset_destroy(&splinter_ifaces);
+
+ if (bitmap_scan(splinter_vlans, 0, 4096) >= 4096) {
+ free(splinter_vlans);
+ return NULL;
+ }
+ return splinter_vlans;
+}
+
+/* Pushes the configure of VLAN splinter port 'port' (e.g. eth0.9) down to
+ * ofproto. */
+static void
+configure_splinter_port(struct port *port)
+{
+ struct ofproto *ofproto = port->bridge->ofproto;
+ uint16_t realdev_ofp_port;
+ const char *realdev_name;
+ struct iface *vlandev, *realdev;
+
+ ofproto_bundle_unregister(port->bridge->ofproto, port);
+
+ vlandev = CONTAINER_OF(list_front(&port->ifaces), struct iface,
+ port_elem);
+
+ realdev_name = get_port_other_config(port->cfg, "realdev", NULL);
+ realdev = iface_lookup(port->bridge, realdev_name);
+ realdev_ofp_port = realdev ? realdev->ofp_port : 0;
+
+ ofproto_port_set_realdev(ofproto, vlandev->ofp_port, realdev_ofp_port,
+ *port->cfg->tag);
+}
+
+static struct ovsrec_port *
+synthesize_splinter_port(const char *real_dev_name,
+ const char *vlan_dev_name, int vid)
+{
+ struct ovsrec_interface *iface;
+ struct ovsrec_port *port;
+
+ iface = xzalloc(sizeof *iface);
+ iface->name = xstrdup(vlan_dev_name);
+ iface->type = "system";
+
+ port = xzalloc(sizeof *port);
+ port->interfaces = xmemdup(&iface, sizeof iface);
+ port->n_interfaces = 1;
+ port->name = xstrdup(vlan_dev_name);
+ port->vlan_mode = "splinter";
+ port->tag = xmalloc(sizeof *port->tag);
+ *port->tag = vid;
+ port->key_other_config = xmalloc(sizeof *port->key_other_config);
+ port->key_other_config[0] = "realdev";
+ port->value_other_config = xmalloc(sizeof *port->value_other_config);
+ port->value_other_config[0] = xstrdup(real_dev_name);
+ port->n_other_config = 1;
+
+ register_block(iface);
+ register_block(iface->name);
+ register_block(port);
+ register_block(port->interfaces);
+ register_block(port->name);
+ register_block(port->tag);
+ register_block(port->key_other_config);
+ register_block(port->value_other_config);
+ register_block(port->value_other_config[0]);
+
+ return port;
+}
+
+/* For each interface with 'br' that has VLAN splinters enabled, adds a
+ * corresponding ovsrec_port to 'ports' for each splinter VLAN marked with a
+ * 1-bit in the 'splinter_vlans' bitmap. */
+static void
+add_vlan_splinter_ports(struct bridge *br,
+ const unsigned long int *splinter_vlans,
+ struct shash *ports)
+{
+ size_t i;
+
+ free_registered_blocks();
+
+ /* We iterate through 'br->cfg->ports' instead of 'ports' here because
+ * we're modifying 'ports'. */
+ for (i = 0; i < br->cfg->n_ports; i++) {
+ const char *name = br->cfg->ports[i]->name;
+ struct ovsrec_port *port_cfg = shash_find_data(ports, name);
+ size_t j;
+
+ for (j = 0; j < port_cfg->n_interfaces; j++) {
+ struct ovsrec_interface *iface_cfg = port_cfg->interfaces[j];
+
+ if (vlan_splinters_is_enabled(iface_cfg)) {
+ const char *real_dev_name;
+ uint16_t vid;
+
+ real_dev_name = iface_cfg->name;
+ BITMAP_FOR_EACH_1 (vid, 4096, splinter_vlans) {
+ const char *vlan_dev_name;
+
+ vlan_dev_name = vlandev_get_name(real_dev_name, vid);
+ if (vlan_dev_name
+ && !shash_find(ports, vlan_dev_name)) {
+ shash_add(ports, vlan_dev_name,
+ synthesize_splinter_port(
+ real_dev_name, vlan_dev_name, vid));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index 8eba5cbf..a467be4e 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -1768,6 +1768,73 @@
</column>
</group>
+ <group title="VLAN Splinters">
+ <p>
+ The ``VLAN splinters'' feature increases Open vSwitch compatibility
+ with buggy network drivers in old versions of Linux that do not
+ properly support VLANs when VLAN devices are not used, at some cost
+ in memory and performance.
+ </p>
+
+ <p>
+ When VLAN splinters are enabled on a particular interface, Open vSwitch
+ creates a VLAN device for each in-use VLAN. For sending traffic tagged
+ with a VLAN on the interface, it substitutes the VLAN device. Traffic
+ received on the VLAN device is treated as if it had been received on
+ the interface on the particular VLAN.
+ </p>
+
+ <p>
+ VLAN splinters consider a VLAN to be in use if:
+ </p>
+
+ <ul>
+ <li>
+ The VLAN is listed within the <ref table="Port" column="trunks"/>
+ column of the <ref table="Port"/> record of an interface on which
+ VLAN splinters are enabled.
+
+ An empty <ref table="Port" column="trunks"/> does not influence the
+ in-use VLANs: creating 4,096 VLAN devices is impractical because it
+ will exceed the current 1,024 port per datapath limit.
+ </li>
+
+ <li>
+ An OpenFlow flow within any bridge matches the VLAN.
+ </li>
+ </ul>
+
+ <p>
+ The same set of in-use VLANs applies to every interface on which VLAN
+ splinters are enabled. That is, the set is not chosen separately for
+ each interface but selected once as the union of all in-use VLANs based
+ on the rules above.
+ </p>
+
+ <p>
+ It does not make sense to enable VLAN splinters on an interface for an
+ access port, or on an interface that is not a physical port.
+ </p>
+
+ <p>
+ VLAN splinters are deprecated. When broken device drivers are no
+ longer in widespread use, we will delete this feature.
+ </p>
+
+ <column name="other_config" key="enable-vlan-splinters"
+ type='{"type": "boolean"}'>
+ <p>
+ Set to <code>true</code> to enable VLAN splinters on this interface.
+ Defaults to <code>false</code>.
+ </p>
+
+ <p>
+ VLAN splinters increase kernel and userspace memory overhead, so do
+ not use them unless they are needed.
+ </p>
+ </column>
+ </group>
+
<group title="Common Columns">
The overall purpose of these columns is described under <code>Common
Columns</code> at the beginning of this document.