noinst_LIBRARIES += lib/libopenflow.a
lib_libopenflow_a_SOURCES = \
+ lib/bitmap.c \
+ lib/bitmap.h \
lib/command-line.c \
lib/command-line.h \
lib/compiler.h \
--- /dev/null
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#include <config.h>
+#include "bitmap.h"
+#include <string.h>
+
+/* Sets 'count' consecutive bits in 'bitmap', starting at bit offset 'start',
+ * to 'value'. */
+void
+bitmap_set_multiple(unsigned long *bitmap, size_t start, size_t count,
+ bool value)
+{
+ for (; count && start % BITMAP_ULONG_BITS; count--) {
+ bitmap_set(bitmap, start++, value);
+ }
+ for (; count >= BITMAP_ULONG_BITS; count -= BITMAP_ULONG_BITS) {
+ *bitmap_unit__(bitmap, start) = -(unsigned long) value;
+ start += BITMAP_ULONG_BITS;
+ }
+ for (; count; count--) {
+ bitmap_set(bitmap, start++, value);
+ }
+}
+
+/* Compares the 'n' bits in bitmaps 'a' and 'b'. Returns true if all bits are
+ * equal, false otherwise. */
+bool
+bitmap_equal(const unsigned long *a, const unsigned long *b, size_t n)
+{
+ size_t i;
+
+ if (memcmp(a, b, n / BITMAP_ULONG_BITS * sizeof(unsigned long))) {
+ return false;
+ }
+ for (i = ROUND_DOWN(n, BITMAP_ULONG_BITS); i < n; i++) {
+ if (bitmap_is_set(a, i) != bitmap_is_set(b, i)) {
+ return false;
+ }
+ }
+ return true;
+}
--- /dev/null
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#ifndef BITMAP_H
+#define BITMAP_H 1
+
+#include <limits.h>
+#include <stdlib.h>
+#include "util.h"
+
+#define BITMAP_ULONG_BITS (sizeof(unsigned long) * CHAR_BIT)
+
+static inline unsigned long *
+bitmap_unit__(const unsigned long *bitmap, size_t offset)
+{
+ return (unsigned long *) &bitmap[offset / BITMAP_ULONG_BITS];
+}
+
+static inline unsigned long
+bitmap_bit__(size_t offset)
+{
+ return 1UL << (offset % BITMAP_ULONG_BITS);
+}
+
+static inline unsigned long *
+bitmap_allocate(size_t n_bits)
+{
+ return xcalloc(1, ROUND_UP(n_bits, BITMAP_ULONG_BITS));
+}
+
+static inline void
+bitmap_free(unsigned long *bitmap)
+{
+ free(bitmap);
+}
+
+static inline bool
+bitmap_is_set(const unsigned long *bitmap, size_t offset)
+{
+ return (*bitmap_unit__(bitmap, offset) & bitmap_bit__(offset)) != 0;
+}
+
+static inline void
+bitmap_set1(unsigned long *bitmap, size_t offset)
+{
+ *bitmap_unit__(bitmap, offset) |= bitmap_bit__(offset);
+}
+
+static inline void
+bitmap_set0(unsigned long *bitmap, size_t offset)
+{
+ *bitmap_unit__(bitmap, offset) &= ~bitmap_bit__(offset);
+}
+
+static inline void
+bitmap_set(unsigned long *bitmap, size_t offset, bool value)
+{
+ if (value) {
+ bitmap_set1(bitmap, offset);
+ } else {
+ bitmap_set0(bitmap, offset);
+ }
+}
+
+void bitmap_set_multiple(unsigned long *, size_t start, size_t count,
+ bool value);
+bool bitmap_equal(const unsigned long *, const unsigned long *, size_t n);
+
+#endif /* bitmap.h */
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>
+#include "bitmap.h"
#include "cfg.h"
#include "dirs.h"
#include "dpif.h"
struct bridge *bridge;
size_t port_idx;
int vlan; /* 0=trunk port, otherwise a 12-bit VLAN ID. */
+ unsigned long *trunks; /* Trunked VLANs, if 'vlan' == 0. */
const char *name;
/* An ordinary bridge port has 1 interface.
return false;
}
+static bool
+port_trunks_vlan(const struct port *port, uint16_t vlan)
+{
+ return !port->vlan && bitmap_is_set(port->trunks, vlan);
+}
+
+static bool
+port_includes_vlan(const struct port *port, uint16_t vlan)
+{
+ return vlan == port->vlan || port_trunks_vlan(port, vlan);
+}
+
static size_t
compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
const struct port *in_port, const struct port *out_port,
for (i = 0; i < br->n_ports; i++) {
struct port *port = br->ports[i];
- if (port != in_port && (!port->vlan || vlan == port->vlan)
+ if (port != in_port && port_includes_vlan(port, vlan)
&& set_dst(dst, flow, in_port, port, tags)) {
mirrors |= port->dst_mirrors;
dst++;
goto done;
}
vlan = in_port->vlan;
+ } else {
+ if (!port_includes_vlan(in_port, vlan)) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged "
+ "packet received on port %s not configured for "
+ "trunking VLAN %d",
+ br->name, vlan, in_port->name, vlan);
+ goto done;
+ }
}
/* Drop multicast and broadcast packets on inactive bonded interfaces, to
port->bridge = br;
port->port_idx = br->n_ports;
port->vlan = 0;
+ port->trunks = NULL;
port->name = xstrdup(name);
port->active_iface = -1;
{
bool bonded = cfg_has_section("bonding.%s", port->name);
struct svec old_ifaces, new_ifaces;
+ unsigned long *trunks;
int vlan;
size_t i;
vlan = 0;
if (cfg_has("vlan.%s.tag", port->name)) {
if (!bonded) {
- vlan = cfg_get_int(0, "vlan.%s.tag", port->name);
+ vlan = cfg_get_vlan(0, "vlan.%s.tag", port->name);
if (vlan >= 0 && vlan <= 4095) {
VLOG_DBG("port %s: assigning VLAN tag %d", port->name, vlan);
} else {
- VLOG_WARN("port %s: ignoring VLAN tag %d because it is not in "
- "the valid range from 0 to 4095", port->name, vlan);
vlan = 0;
}
} else {
port->vlan = vlan;
bridge_flush(port->bridge);
}
+
+ /* Get trunked VLANs. */
+ trunks = NULL;
+ if (!vlan) {
+ size_t n_trunks, n_errors;
+ size_t i;
+
+ trunks = bitmap_allocate(4096);
+ n_trunks = cfg_count("vlan.%s.trunks", port->name);
+ n_errors = 0;
+ for (i = 0; i < n_trunks; i++) {
+ int trunk = cfg_get_vlan(i, "vlan.%s.trunks", port->name);
+ if (trunk >= 0) {
+ bitmap_set1(trunks, trunk);
+ } else {
+ n_errors++;
+ }
+ }
+ if (n_errors) {
+ VLOG_ERR("port %s: invalid values for %zu trunk VLANs",
+ port->name, n_trunks);
+ }
+ if (n_errors == n_trunks) {
+ if (n_errors) {
+ VLOG_ERR("port %s: no valid trunks, trunking all VLANs",
+ port->name);
+ }
+ bitmap_set_multiple(trunks, 0, 4096, 1);
+ }
+ } else {
+ if (cfg_has("vlan.%s.trunks", port->name)) {
+ VLOG_ERR("ignoring vlan.%s.trunks in favor of vlan.%s.vlan",
+ port->name, port->name);
+ }
+ }
+ if (trunks == NULL
+ ? port->trunks != NULL
+ : port->trunks == NULL || !bitmap_equal(trunks, port->trunks, 4096)) {
+ bridge_flush(port->bridge);
+ }
+ bitmap_free(port->trunks);
+ port->trunks = trunks;
+
svec_destroy(&old_ifaces);
svec_destroy(&new_ifaces);
}
iface_destroy(port->ifaces[i]);
}
free(port->ifaces);
+ free(port->trunks);
free(port);
bridge_flush(br);
}
return false;
}
+static bool
+port_trunks_any_mirrored_vlan(const struct mirror *m, const struct port *p)
+{
+ size_t i;
+
+ for (i = 0; i < m->n_vlans; i++) {
+ if (port_trunks_vlan(p, m->vlans[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+
static void
mirror_reconfigure_one(struct mirror *m)
{
/* Determine the VLAN transformation. */
if (cfg_has("%s.transform.set-vlan", pfx)) {
- set_vlan = cfg_get_int(0, "%s.transform.set-vlan", pfx);
+ set_vlan = cfg_get_vlan(0, "%s.transform.set-vlan", pfx);
if (set_vlan == 0) {
set_vlan = OFP_VLAN_NONE;
- } else if (set_vlan < 0 || set_vlan > 4095) {
- VLOG_WARN("%s.transform.set-vlan: %s is not a valid VLAN",
- pfx, cfg_get_string(0, "%s.transform.set-vlan", pfx));
- set_vlan = -1;
}
} else {
set_vlan = -1;
if (svec_contains(&m->src_ports, port->name)
|| (m->n_vlans
- && (!port->vlan || vlan_is_mirrored(m, port->vlan)))) {
+ && (!port->vlan
+ ? port_trunks_any_mirrored_vlan(m, port)
+ : vlan_is_mirrored(m, port->vlan)))) {
port->src_mirrors |= mirror_bit;
} else {
port->src_mirrors &= ~mirror_bit;
return eth_addr_to_uint64(mac);
}
+/* Returns the value numbered 'idx' of 'key', converted to an integer. Returns
+ * -1 if 'idx' is greater than or equal to cfg_count(key) or if the value 'idx'
+ * of 'key' is not a valid integer between 0 and 4095. */
+int
+cfg_get_vlan(int idx, const char *key_, ...)
+{
+ const char *value;
+ int retval;
+ char *key;
+
+ FORMAT_KEY(key_, key);
+ value = get_nth_value(idx, key);
+ if (value && is_int(value)) {
+ retval = atoi(value);
+ if (retval < 0 || retval > 4095) {
+ retval = -1;
+ }
+ } else {
+ retval = -1;
+ }
+ free(key);
+ return retval;
+}
+
/* Fills 'svec' with all of the string values of 'key'. The caller must
* first initialize 'svec'. */
void
CFG_BOOL = 1 << 3, /* Boolean. */
CFG_IP = 1 << 4, /* IPv4 address. */
CFG_MAC = 1 << 5, /* MAC address. */
+ CFG_VLAN = 1 << 6, /* Integer in range 0...4095. */
/* Number allowed. */
CFG_REQUIRED = 1 << 6, /* At least one value allowed. */
bool cfg_get_bool(int idx, const char *key, ...) PRINTF_FORMAT(2, 3);
uint32_t cfg_get_ip(int idx, const char *key, ...) PRINTF_FORMAT(2, 3);
uint64_t cfg_get_mac(int idx, const char *key, ...) PRINTF_FORMAT(2, 3);
+int cfg_get_vlan(int idx, const char *key, ...) PRINTF_FORMAT(2, 3);
void cfg_get_all_strings(struct svec *, const char *key, ...)
PRINTF_FORMAT(2, 3);
port = eth2
.fi
.RE
-.SS "802.1Q VLAN tagging"
-By default, bridge ports are trunk ports. Define key
+.SS "802.1Q VLAN support"
+A bridge port may be configured either as a trunk port or as belonging
+to a single, untagged VLAN. These two options are mutually exclusive
+and a port must be configured in one way or the other.
+.PP
+.I "Trunk Ports"
+.PP
+By default, bridge ports are trunk ports that carry all VLANs. To
+limit the VLANs that a trunk port carries, define
+\fBvlan.\fIport\fB.trunks\fR to one or more integers between 0 and
+4095 designating VLANs. Only frames that have an 802.1Q header with
+one of the listed VLANs are accepted on a trunk port. If 0 is
+included in the list, then frames without an 802.1Q header are also
+accepted. Other frames are discarded.
+.PP
+The following syntax makes network device \fBeth0\fR a trunk port that
+carries VLANs 1, 2, and 3:
+.PP
+.RS
+.nf
+
+[vlan "eth0"]
+ trunks = 1
+ trunks = 2
+ trunks = 3
+
+.fi
+.RE
+.PP
+.I "Untagged VLAN Ports"
+.PP
+A bridge port may be configured with an implicit, untagged VLAN.
+Define key
\fBvlan.\fIport\fB.tag\fR to an integer value \fIvid\fR between 1 and
-4095, inclusive, to instead designate the named \fIport\fR as a member
+4095, inclusive, to designate the named \fIport\fR as a member
of 802.1Q VLAN \fIvid\fR. When \fIport\fR is assigned a VLAN tag this
way, frames arriving on trunk ports will be forwarded to \fIport\fR
only if they are tagged with VLAN \fIvid\fR, and frames arriving on
other VLAN ports will be forwarded to \fIport\fR only if their
-\fIvid\fR values are equal. (Frames received on \fIport\fR will not
-have an 802.1Q header.)
+\fIvid\fR values are equal. Frames forwarded to \fIport\fR will not
+have an 802.1Q header.
+.PP
+When a frame with a 802.1Q header that indicates a nonzero VLAN is
+received on an implicit VLAN port, it is discarded.
.PP
The following syntax makes network device \fBeth0\fR a member of VLAN
101: