aboutsummaryrefslogtreecommitdiff
path: root/ofproto
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 /ofproto
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 'ofproto')
-rw-r--r--ofproto/ofproto-dpif.c229
-rw-r--r--ofproto/ofproto-provider.h28
-rw-r--r--ofproto/ofproto.c103
-rw-r--r--ofproto/ofproto.h20
4 files changed, 373 insertions, 7 deletions
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 241dbd3a..96bd764d 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -392,11 +392,21 @@ struct ofport_dpif {
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
@@ -409,6 +419,27 @@ struct priority_to_dscp {
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)
{
@@ -473,6 +504,10 @@ struct ofproto_dpif {
/* 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
@@ -511,8 +546,7 @@ static int expire(struct ofproto_dpif *);
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);
@@ -623,6 +657,9 @@ construct(struct ofproto *ofproto_, int *n_tablesp)
ofproto->has_bundle_action = false;
+ hmap_init(&ofproto->vlandev_map);
+ hmap_init(&ofproto->realdev_vid_map);
+
*n_tablesp = N_TABLES;
return 0;
}
@@ -670,6 +707,9 @@ destruct(struct ofproto *ofproto_)
hmap_destroy(&ofproto->facets);
hmap_destroy(&ofproto->subfacets);
+ hmap_destroy(&ofproto->vlandev_map);
+ hmap_destroy(&ofproto->realdev_vid_map);
+
dpif_close(ofproto->dpif);
}
@@ -881,6 +921,8 @@ port_construct(struct ofport *port_)
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,
@@ -2435,11 +2477,13 @@ handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss,
}
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) {
@@ -2447,6 +2491,19 @@ ofproto_dpif_extract_flow_key(const struct ofproto_dpif *ofproto OVS_UNUSED,
}
*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;
}
@@ -3762,18 +3819,26 @@ rule_modify_actions(struct rule *rule_)
}
/* 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);
@@ -4066,7 +4131,9 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
{
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;
@@ -4087,11 +4154,18 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
* 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;
}
@@ -5696,6 +5770,148 @@ ofproto_dpif_unixctl_init(void)
unixctl_command_register("ofproto/unclog", "", ofproto_dpif_unclog, NULL);
}
+/* 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");
+ }
+}
+
const struct ofproto_class ofproto_dpif_class = {
enumerate_types,
enumerate_names,
@@ -5751,4 +5967,5 @@ const struct ofproto_class ofproto_dpif_class = {
set_flood_vlans,
is_mirror_output_bundle,
forward_bpdu_changed,
+ set_realdev,
};
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 2a7324ac..558b8719 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -69,6 +69,15 @@ struct ofproto {
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);
@@ -1035,6 +1044,25 @@ struct ofproto_class {
/* 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;
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index af02a0e3..1a200979 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -21,6 +21,7 @@
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
+#include "bitmap.h"
#include "byte-order.h"
#include "classifier.h"
#include "connmgr.h"
@@ -337,6 +338,8 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
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) {
@@ -834,6 +837,8 @@ ofproto_destroy__(struct ofproto *ofproto)
hmap_destroy(&ofproto->deletions);
+ free(ofproto->vlan_bitmap);
+
ofproto->ofproto_class->dealloc(ofproto);
}
@@ -1394,6 +1399,9 @@ ofproto_port_unregister(struct ofproto *ofproto, uint16_t ofp_port)
{
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);
}
@@ -3098,7 +3106,8 @@ ofoperation_complete(struct ofoperation *op, int error)
{
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);
@@ -3130,6 +3139,15 @@ ofoperation_complete(struct ofoperation *op, int error)
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);
@@ -3242,3 +3260,86 @@ ofproto_unixctl_init(void)
unixctl_command_register("ofproto/list", "", ofproto_unixctl_list, NULL);
}
+
+/* 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;
+}
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 74b3dece..4999c828 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -266,6 +266,14 @@ struct ofproto_bundle_settings {
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,
@@ -313,6 +321,18 @@ int ofproto_port_get_cfm_remote_mpids(const struct ofproto *,
void ofproto_get_ofproto_controller_info(const struct ofproto *, struct shash *);
void ofproto_free_ofproto_controller_info(struct shash *);
+
+/* 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
}