post-v1.3.0
------------------------
+ - New "VLAN splinters" feature to work 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.) See ovs-vswitchd.conf.db(5) for more
+ information.
- OpenFlow:
- Added ability to match on IPv6 flow label through NXM.
- Added ability to match on ECN bits in IPv4 and IPv6 through NXM.
uint32_t bond_stable_id; /* stable_id to use as bond slave, or 0. */
bool may_enable; /* May be enabled in bonds. */
+ /* Spanning tree. */
struct stp_port *stp_port; /* Spanning Tree Protocol, if any. */
enum stp_state stp_state; /* Always STP_DISABLED if STP not in use. */
long long int stp_state_entered;
struct hmap priorities; /* Map of attached 'priority_to_dscp's. */
+
+ /* 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. */
+ uint16_t realdev_ofp_port;
+ int vlandev_vid;
};
/* Node in 'ofport_dpif''s 'priorities' map. Used to maintain a map from
uint8_t dscp; /* DSCP bits to mark outgoing traffic with. */
};
+/* 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. */
+struct vlan_splinter {
+ struct hmap_node realdev_vid_node;
+ struct hmap_node vlandev_node;
+ uint16_t realdev_ofp_port;
+ uint16_t vlandev_ofp_port;
+ int vid;
+};
+
+static uint32_t vsp_realdev_to_vlandev(const struct ofproto_dpif *,
+ uint32_t realdev, ovs_be16 vlan_tci);
+static uint16_t vsp_vlandev_to_realdev(const struct ofproto_dpif *,
+ uint16_t vlandev, int *vid);
+static void vsp_remove(struct ofport_dpif *);
+static void vsp_add(struct ofport_dpif *, uint16_t realdev_ofp_port, int vid);
+
static struct ofport_dpif *
ofport_dpif_cast(const struct ofport *ofport)
{
/* Spanning tree. */
struct stp *stp;
long long int stp_last_tick;
+
+ /* VLAN splinters. */
+ struct hmap realdev_vid_map; /* (realdev,vid) -> vlandev. */
+ struct hmap vlandev_map; /* vlandev -> (realdev,vid). */
};
/* Defer flow mod completion until "ovs-appctl ofproto/unclog"? (Useful only
static void send_netflow_active_timeouts(struct ofproto_dpif *);
/* Utilities. */
-static int send_packet(const struct ofport_dpif *,
- const struct ofpbuf *packet);
+static int send_packet(const struct ofport_dpif *, struct ofpbuf *packet);
static size_t
compose_sflow_action(const struct ofproto_dpif *, struct ofpbuf *odp_actions,
const struct flow *, uint32_t odp_port);
ofproto->has_bundle_action = false;
+ hmap_init(&ofproto->vlandev_map);
+ hmap_init(&ofproto->realdev_vid_map);
+
*n_tablesp = N_TABLES;
return 0;
}
hmap_destroy(&ofproto->facets);
hmap_destroy(&ofproto->subfacets);
+ hmap_destroy(&ofproto->vlandev_map);
+ hmap_destroy(&ofproto->realdev_vid_map);
+
dpif_close(ofproto->dpif);
}
port->stp_port = NULL;
port->stp_state = STP_DISABLED;
hmap_init(&port->priorities);
+ port->realdev_ofp_port = 0;
+ port->vlandev_vid = 0;
if (ofproto->sflow) {
dpif_sflow_add_port(ofproto->sflow, port->odp_port,
}
static enum odp_key_fitness
-ofproto_dpif_extract_flow_key(const struct ofproto_dpif *ofproto OVS_UNUSED,
+ofproto_dpif_extract_flow_key(const struct ofproto_dpif *ofproto,
const struct nlattr *key, size_t key_len,
struct flow *flow, ovs_be16 *initial_tci)
{
enum odp_key_fitness fitness;
+ uint16_t realdev;
+ int vid;
fitness = odp_flow_key_to_flow(key, key_len, flow);
if (fitness == ODP_FIT_ERROR) {
}
*initial_tci = flow->vlan_tci;
+ realdev = vsp_vlandev_to_realdev(ofproto, flow->in_port, &vid);
+ if (realdev) {
+ /* Cause the flow to be processed as if it came in on the real device
+ * with the VLAN device's VLAN ID. */
+ flow->in_port = realdev;
+ flow->vlan_tci = htons((vid & VLAN_VID_MASK) | VLAN_CFI);
+
+ /* Let the caller know that we can't reproduce 'key' from 'flow'. */
+ if (fitness == ODP_FIT_PERFECT) {
+ fitness = ODP_FIT_TOO_MUCH;
+ }
+ }
+
return fitness;
}
}
\f
/* Sends 'packet' out 'ofport'.
+ * May modify 'packet'.
* Returns 0 if successful, otherwise a positive errno value. */
static int
-send_packet(const struct ofport_dpif *ofport, const struct ofpbuf *packet)
+send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
{
const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
- uint16_t odp_port = ofport->odp_port;
struct ofpbuf key, odp_actions;
struct odputil_keybuf keybuf;
+ uint16_t odp_port;
struct flow flow;
int error;
flow_extract((struct ofpbuf *) packet, 0, 0, 0, &flow);
+ odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port,
+ flow.vlan_tci);
+ if (odp_port != ofport->odp_port) {
+ eth_pop_vlan(packet);
+ flow.vlan_tci = htons(0);
+ }
+
ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
odp_flow_key_from_flow(&key, &flow);
{
const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
uint16_t odp_port = ofp_port_to_odp_port(ofp_port);
+ ovs_be16 flow_vlan_tci = ctx->flow.vlan_tci;
uint8_t flow_nw_tos = ctx->flow.nw_tos;
+ uint16_t out_port;
if (ofport) {
struct priority_to_dscp *pdscp;
* later and we're pre-populating the flow table. */
}
+ out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port,
+ ctx->flow.vlan_tci);
+ if (out_port != odp_port) {
+ ctx->flow.vlan_tci = htons(0);
+ }
commit_odp_actions(ctx);
- nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port);
+ nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, out_port);
+
ctx->sflow_odp_port = odp_port;
ctx->sflow_n_outputs++;
ctx->nf_output_iface = ofp_port;
+ ctx->flow.vlan_tci = flow_vlan_tci;
ctx->flow.nw_tos = flow_nw_tos;
}
unixctl_command_register("ofproto/unclog", "", ofproto_dpif_unclog, NULL);
}
\f
+/* 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 int
+set_realdev(struct ofport *ofport_, uint16_t realdev_ofp_port, int vid)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport_->ofproto);
+ struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+
+ if (realdev_ofp_port == ofport->realdev_ofp_port
+ && vid == ofport->vlandev_vid) {
+ return 0;
+ }
+
+ ofproto->need_revalidate = true;
+
+ if (ofport->realdev_ofp_port) {
+ vsp_remove(ofport);
+ }
+ if (realdev_ofp_port && ofport->bundle) {
+ /* vlandevs are enslaved to their realdevs, so they are not allowed to
+ * themselves be part of a bundle. */
+ bundle_set(ofport->up.ofproto, ofport->bundle, NULL);
+ }
+
+ ofport->realdev_ofp_port = realdev_ofp_port;
+ ofport->vlandev_vid = vid;
+
+ if (realdev_ofp_port) {
+ vsp_add(ofport, realdev_ofp_port, vid);
+ }
+
+ return 0;
+}
+
+static uint32_t
+hash_realdev_vid(uint16_t realdev_ofp_port, int vid)
+{
+ return hash_2words(realdev_ofp_port, vid);
+}
+
+static uint32_t
+vsp_realdev_to_vlandev(const struct ofproto_dpif *ofproto,
+ uint32_t realdev_odp_port, ovs_be16 vlan_tci)
+{
+ if (!hmap_is_empty(&ofproto->realdev_vid_map)) {
+ uint16_t realdev_ofp_port = odp_port_to_ofp_port(realdev_odp_port);
+ int vid = vlan_tci_to_vid(vlan_tci);
+ const struct vlan_splinter *vsp;
+
+ HMAP_FOR_EACH_WITH_HASH (vsp, realdev_vid_node,
+ hash_realdev_vid(realdev_ofp_port, vid),
+ &ofproto->realdev_vid_map) {
+ if (vsp->realdev_ofp_port == realdev_ofp_port
+ && vsp->vid == vid) {
+ return ofp_port_to_odp_port(vsp->vlandev_ofp_port);
+ }
+ }
+ }
+ return realdev_odp_port;
+}
+
+static struct vlan_splinter *
+vlandev_find(const struct ofproto_dpif *ofproto, uint16_t vlandev_ofp_port)
+{
+ struct vlan_splinter *vsp;
+
+ HMAP_FOR_EACH_WITH_HASH (vsp, vlandev_node, hash_int(vlandev_ofp_port, 0),
+ &ofproto->vlandev_map) {
+ if (vsp->vlandev_ofp_port == vlandev_ofp_port) {
+ return vsp;
+ }
+ }
+
+ return NULL;
+}
+
+static uint16_t
+vsp_vlandev_to_realdev(const struct ofproto_dpif *ofproto,
+ uint16_t vlandev_ofp_port, int *vid)
+{
+ if (!hmap_is_empty(&ofproto->vlandev_map)) {
+ const struct vlan_splinter *vsp;
+
+ vsp = vlandev_find(ofproto, vlandev_ofp_port);
+ if (vsp) {
+ if (vid) {
+ *vid = vsp->vid;
+ }
+ return vsp->realdev_ofp_port;
+ }
+ }
+ return 0;
+}
+
+static void
+vsp_remove(struct ofport_dpif *port)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
+ struct vlan_splinter *vsp;
+
+ vsp = vlandev_find(ofproto, port->up.ofp_port);
+ if (vsp) {
+ hmap_remove(&ofproto->vlandev_map, &vsp->vlandev_node);
+ hmap_remove(&ofproto->realdev_vid_map, &vsp->realdev_vid_node);
+ free(vsp);
+
+ port->realdev_ofp_port = 0;
+ } else {
+ VLOG_ERR("missing vlan device record");
+ }
+}
+
+static void
+vsp_add(struct ofport_dpif *port, uint16_t realdev_ofp_port, int vid)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
+
+ if (!vsp_vlandev_to_realdev(ofproto, port->up.ofp_port, NULL)
+ && (vsp_realdev_to_vlandev(ofproto, realdev_ofp_port, htons(vid))
+ == realdev_ofp_port)) {
+ struct vlan_splinter *vsp;
+
+ vsp = xmalloc(sizeof *vsp);
+ hmap_insert(&ofproto->vlandev_map, &vsp->vlandev_node,
+ hash_int(port->up.ofp_port, 0));
+ hmap_insert(&ofproto->realdev_vid_map, &vsp->realdev_vid_node,
+ hash_realdev_vid(realdev_ofp_port, vid));
+ vsp->realdev_ofp_port = realdev_ofp_port;
+ vsp->vlandev_ofp_port = port->up.ofp_port;
+ vsp->vid = vid;
+
+ port->realdev_ofp_port = realdev_ofp_port;
+ } else {
+ VLOG_ERR("duplicate vlan device record");
+ }
+}
+\f
const struct ofproto_class ofproto_dpif_class = {
enumerate_types,
enumerate_names,
set_flood_vlans,
is_mirror_output_bundle,
forward_bpdu_changed,
+ set_realdev,
};
struct list pending; /* List of "struct ofopgroup"s. */
unsigned int n_pending; /* list_size(&pending). */
struct hmap deletions; /* All OFOPERATION_DELETE "ofoperation"s. */
+
+ /* 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. */
+ unsigned long int *vlan_bitmap; /* 4096-bit bitmap of in-use VLANs. */
+ bool vlans_changed; /* True if new VLANs are in use. */
};
struct ofproto *ofproto_lookup(const char *name);
/* When the configuration option of forward_bpdu changes, this function
* will be invoked. */
void (*forward_bpdu_changed)(struct ofproto *ofproto);
+
+/* 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. */
+
+ /* If 'realdev_ofp_port' is nonzero, then this function configures 'ofport'
+ * as a VLAN splinter port for VLAN 'vid', associated with the real device
+ * that has OpenFlow port number 'realdev_ofp_port'.
+ *
+ * If 'realdev_ofp_port' is zero, then this function deconfigures 'ofport'
+ * as a VLAN splinter port.
+ *
+ * This function should be NULL if a an implementation does not support
+ * it. */
+ int (*set_realdev)(struct ofport *ofport,
+ uint16_t realdev_ofp_port, int vid);
};
extern const struct ofproto_class ofproto_dpif_class;
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
+#include "bitmap.h"
#include "byte-order.h"
#include "classifier.h"
#include "connmgr.h"
list_init(&ofproto->pending);
ofproto->n_pending = 0;
hmap_init(&ofproto->deletions);
+ ofproto->vlan_bitmap = NULL;
+ ofproto->vlans_changed = false;
error = ofproto->ofproto_class->construct(ofproto, &n_tables);
if (error) {
hmap_destroy(&ofproto->deletions);
+ free(ofproto->vlan_bitmap);
+
ofproto->ofproto_class->dealloc(ofproto);
}
{
struct ofport *port = ofproto_get_port(ofproto, ofp_port);
if (port) {
+ if (port->ofproto->ofproto_class->set_realdev) {
+ port->ofproto->ofproto_class->set_realdev(port, 0, 0);
+ }
if (port->ofproto->ofproto_class->set_stp_port) {
port->ofproto->ofproto_class->set_stp_port(port, NULL);
}
{
struct ofopgroup *group = op->group;
struct rule *rule = op->rule;
- struct classifier *table = &rule->ofproto->tables[rule->table_id];
+ struct ofproto *ofproto = rule->ofproto;
+ struct classifier *table = &ofproto->tables[rule->table_id];
assert(rule->pending == op);
assert(op->status < 0);
if (op->victim) {
ofproto_rule_destroy__(op->victim);
}
+ if (!(rule->cr.wc.vlan_tci_mask & htons(VLAN_VID_MASK))
+ && ofproto->vlan_bitmap) {
+ uint16_t vid = vlan_tci_to_vid(rule->cr.flow.vlan_tci);
+
+ if (!bitmap_is_set(ofproto->vlan_bitmap, vid)) {
+ bitmap_set1(ofproto->vlan_bitmap, vid);
+ ofproto->vlans_changed = true;
+ }
+ }
} else {
if (op->victim) {
classifier_replace(table, &op->victim->cr);
unixctl_command_register("ofproto/list", "", ofproto_unixctl_list, NULL);
}
+\f
+/* 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. */
+
+/* Sets a 1-bit in the 4096-bit 'vlan_bitmap' for each VLAN ID that is matched
+ * (exactly) by an OpenFlow rule in 'ofproto'. */
+void
+ofproto_get_vlan_usage(struct ofproto *ofproto, unsigned long int *vlan_bitmap)
+{
+ const struct classifier *cls;
+
+ free(ofproto->vlan_bitmap);
+ ofproto->vlan_bitmap = bitmap_allocate(4096);
+ ofproto->vlans_changed = false;
+
+ OFPROTO_FOR_EACH_TABLE (cls, ofproto) {
+ const struct cls_table *table;
+
+ HMAP_FOR_EACH (table, hmap_node, &cls->tables) {
+ if (!(table->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) {
+ const struct cls_rule *rule;
+
+ HMAP_FOR_EACH (rule, hmap_node, &table->rules) {
+ uint16_t vid = vlan_tci_to_vid(rule->flow.vlan_tci);
+ bitmap_set1(vlan_bitmap, vid);
+ bitmap_set1(ofproto->vlan_bitmap, vid);
+ }
+ }
+ }
+ }
+}
+
+/* Returns true if new VLANs have come into use by the flow table since the
+ * last call to ofproto_get_vlan_usage().
+ *
+ * We don't track when old VLANs stop being used. */
+bool
+ofproto_has_vlan_usage_changed(const struct ofproto *ofproto)
+{
+ return ofproto->vlans_changed;
+}
+
+/* Configures a VLAN splinter binding between the ports identified by OpenFlow
+ * port numbers 'vlandev_ofp_port' and 'realdev_ofp_port'. If
+ * 'realdev_ofp_port' is nonzero, then the VLAN device is enslaved to the real
+ * device as a VLAN splinter for VLAN ID 'vid'. If 'realdev_ofp_port' is zero,
+ * then the VLAN device is un-enslaved. */
+int
+ofproto_port_set_realdev(struct ofproto *ofproto, uint16_t vlandev_ofp_port,
+ uint16_t realdev_ofp_port, int vid)
+{
+ struct ofport *ofport;
+ int error;
+
+ assert(vlandev_ofp_port != realdev_ofp_port);
+
+ ofport = ofproto_get_port(ofproto, vlandev_ofp_port);
+ if (!ofport) {
+ VLOG_WARN("%s: cannot set realdev on nonexistent port %"PRIu16,
+ ofproto->name, vlandev_ofp_port);
+ return EINVAL;
+ }
+
+ if (!ofproto->ofproto_class->set_realdev) {
+ if (!vlandev_ofp_port) {
+ return 0;
+ }
+ VLOG_WARN("%s: vlan splinters not supported", ofproto->name);
+ return EOPNOTSUPP;
+ }
+
+ error = ofproto->ofproto_class->set_realdev(ofport, realdev_ofp_port, vid);
+ if (error) {
+ VLOG_WARN("%s: setting realdev on port %"PRIu16" (%s) failed (%s)",
+ ofproto->name, vlandev_ofp_port,
+ netdev_get_name(ofport->netdev), strerror(error));
+ }
+ return error;
+}
struct lacp_settings *lacp; /* Nonnull to enable LACP. */
struct lacp_slave_settings *lacp_slaves; /* Array of n_slaves elements. */
+
+ /* 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. */
+ uint16_t realdev_ofp_port; /* OpenFlow port number of real device. */
};
int ofproto_bundle_register(struct ofproto *, void *aux,
void ofproto_get_ofproto_controller_info(const struct ofproto *, struct shash *);
void ofproto_free_ofproto_controller_info(struct shash *);
+\f
+/* 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. */
+
+void ofproto_get_vlan_usage(struct ofproto *, unsigned long int *vlan_bitmap);
+bool ofproto_has_vlan_usage_changed(const struct ofproto *);
+int ofproto_port_set_realdev(struct ofproto *, uint16_t vlandev_ofp_port,
+ uint16_t realdev_ofp_port, int vid);
#ifdef __cplusplus
}
#include "timeval.h"
#include "util.h"
#include "unixctl.h"
+#include "vlandev.h"
#include "vswitchd/vswitch-idl.h"
#include "xenserver.h"
#include "vlog.h"
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 *);
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);
\f
/* Public functions. */
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;
* 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.
*
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;
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;
}
{
const struct ovsrec_open_vswitch *cfg;
+ bool vlan_splinters_changed;
bool datapath_destroyed;
bool database_changed;
struct bridge *br;
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);
/* 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;
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) {
return true;
}
+\f
+/* 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));
+ }
+ }
+ }
+ }
+ }
+}
</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.