console or file for vlog logging instead of the previously used term
'facility'.
- Support for VXLAN Group Policy extension
+ - Initial support for the IETF Auto-Attach SPBM draft standard. This
+ contains rudimentary support for the LLDP protocol as needed for
+ Auto-Attach.
v2.3.0 - 14 Aug 2014
--- /dev/null
+AT_BANNER([auto-attach unit tests])
+
+AT_SETUP([auto-attach - packet tests])
+AT_KEYWORDS([auto-attach])
+AT_CHECK(ovstest test-aa, [], [ignore], [ignore])
+
+AT_CLEANUP
tests/rstp.at \
tests/interface-reconfigure.at \
tests/vlog.at \
- tests/vtep-ctl.at
+ tests/vtep-ctl.at \
+ tests/auto-attach.at
KMOD_TESTSUITE_AT = \
tests/kmod-testsuite.at \
tests/test-util.c \
tests/test-uuid.c \
tests/test-bitmap.c \
- tests/test-vconn.c
+ tests/test-vconn.c \
+ tests/test-aa.c
if !WIN32
tests_ovstest_SOURCES += \
_uuid : <0>
+auto_attach : []
controller : []
datapath_id : []
datapath_type : ""
AT_CHECK([${PERL} $srcdir/uuidfilt.pl stdout], [0],
[[<0>
_uuid : <1>
+auto_attach : []
controller : []
datapath_id : []
datapath_type : ""
--- /dev/null
+/*
+ * Copyright (c) 2015 Avaya, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+#undef NDEBUG
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ovs-lldp.h"
+#include "ovstest.h"
+
+#define ETH_TYPE_LLDP 0x88cc
+
+/* Dummy MAC addresses */
+char chassis_mac[ETHER_ADDR_LEN] = { 0x5e, 0x10, 0x8e, 0xe7, 0x84, 0xad };
+uint8_t eth_src[ETHER_ADDR_LEN] = { 0x5e, 0x10, 0x8e, 0xe7, 0x84, 0xad };
+
+/* LLDP multicast address */
+static const uint8_t eth_addr_lldp[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x0e};
+
+/* Count of tests run */
+int num_tests = 0;
+
+
+/*
+ * Helper function to validate port info
+ */
+static void
+check_received_port(struct lldpd_port *sport,
+ struct lldpd_port *rport)
+{
+ assert(rport->p_id_subtype == sport->p_id_subtype);
+ assert(rport->p_id_len == sport->p_id_len);
+ assert(strncmp(rport->p_id, sport->p_id, sport->p_id_len) == 0);
+ assert(strcmp(rport->p_descr, sport->p_descr) == 0);
+
+ return;
+}
+
+
+/*
+ * Helper function to validate chassis info
+ */
+static void
+check_received_chassis(struct lldpd_chassis *schassis,
+ struct lldpd_chassis *rchassis)
+{
+ assert(rchassis->c_id_subtype == schassis->c_id_subtype);
+ assert(rchassis->c_id_len == schassis->c_id_len);
+ assert(strncmp(rchassis->c_id, schassis->c_id, schassis->c_id_len) == 0);
+ assert(strcmp(rchassis->c_name, schassis->c_name) == 0);
+ assert(strcmp(rchassis->c_descr, schassis->c_descr) == 0);
+ assert(rchassis->c_cap_available == schassis->c_cap_available);
+ assert(rchassis->c_cap_enabled == schassis->c_cap_enabled);
+
+ return;
+}
+
+
+/*
+ * Helper function to validate auto-attach info
+ */
+static void
+check_received_aa(struct lldpd_port *sport,
+ struct lldpd_port *rport,
+ struct lldpd_aa_isid_vlan_maps_tlv *smap)
+{
+ struct lldpd_aa_isid_vlan_maps_tlv *received_map;
+ int i = 0;
+
+ assert(rport->p_element.type == sport->p_element.type);
+ assert(rport->p_element.mgmt_vlan == sport->p_element.mgmt_vlan);
+ assert(rport->p_element.system_id.system_mac[0] ==
+ sport->p_element.system_id.system_mac[0]);
+ assert(rport->p_element.system_id.system_mac[1] ==
+ sport->p_element.system_id.system_mac[1]);
+ assert(rport->p_element.system_id.system_mac[2] ==
+ sport->p_element.system_id.system_mac[2]);
+ assert(rport->p_element.system_id.system_mac[3] ==
+ sport->p_element.system_id.system_mac[3]);
+ assert(rport->p_element.system_id.system_mac[4] ==
+ sport->p_element.system_id.system_mac[4]);
+ assert(rport->p_element.system_id.system_mac[5] ==
+ sport->p_element.system_id.system_mac[5]);
+ assert(rport->p_element.system_id.conn_type ==
+ sport->p_element.system_id.conn_type);
+ assert(rport->p_element.system_id.smlt_id ==
+ sport->p_element.system_id.smlt_id);
+ assert(rport->p_element.system_id.mlt_id[0] ==
+ sport->p_element.system_id.mlt_id[0]);
+ assert(rport->p_element.system_id.mlt_id[1] ==
+ sport->p_element.system_id.mlt_id[1]);
+
+ /* Should receive 2 mappings */
+ assert(!list_is_empty(&rport->p_isid_vlan_maps.m_entries));
+
+ /* For each received isid/vlan mapping */
+ LIST_FOR_EACH (received_map, m_entries,
+ &rport->p_isid_vlan_maps.m_entries) {
+
+ /* Validate against mapping sent */
+ assert(smap[i].isid_vlan_data.status ==
+ received_map->isid_vlan_data.status);
+ assert(smap[i].isid_vlan_data.vlan ==
+ received_map->isid_vlan_data.vlan);
+ assert(smap[i].isid_vlan_data.isid[0] ==
+ received_map->isid_vlan_data.isid[0]);
+ assert(smap[i].isid_vlan_data.isid[1] ==
+ received_map->isid_vlan_data.isid[1]);
+ assert(smap[i].isid_vlan_data.isid[2] ==
+ received_map->isid_vlan_data.isid[2]);
+
+ /* Next mapping sent */
+ i++;
+ }
+ assert(i == 2);
+
+ return;
+}
+
+
+/*
+ * Validate basic send/receive processing
+ */
+static int
+test_aa_send(void)
+{
+ struct lldp *lldp;
+ struct lldpd_hardware hardware;
+ struct lldpd_chassis chassis;
+
+ struct lldpd_chassis *nchassis = NULL;
+ struct lldpd_port *nport = NULL;
+
+ struct lldpd_hardware *hw = NULL;
+ struct lldpd_chassis *ch = NULL;
+
+ struct lldpd_aa_isid_vlan_maps_tlv map_init[2];
+ struct lldpd_aa_isid_vlan_maps_tlv map[2];
+
+ uint32_t stub[512 / 4];
+ struct ofpbuf packet;
+
+ int n;
+
+ /* Prepare data used to construct and validate LLDPPDU */
+ hardware.h_lport.p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
+ hardware.h_lport.p_id = "FastEthernet 1/5";
+ hardware.h_lport.p_id_len = strlen(hardware.h_lport.p_id);
+ hardware.h_lport.p_descr = "Fake port description";
+ hardware.h_lport.p_mfs = 1516;
+
+ /* Auto attach element discovery info */
+ hardware.h_lport.p_element.type = LLDP_TLV_AA_ELEM_TYPE_TAG_CLIENT;
+ hardware.h_lport.p_element.mgmt_vlan = 0xCDC;
+ hardware.h_lport.p_element.system_id.system_mac[0] = 0x1;
+ hardware.h_lport.p_element.system_id.system_mac[1] = 0x2;
+ hardware.h_lport.p_element.system_id.system_mac[2] = 0x3;
+ hardware.h_lport.p_element.system_id.system_mac[3] = 0x4;
+ hardware.h_lport.p_element.system_id.system_mac[4] = 0x5;
+ hardware.h_lport.p_element.system_id.system_mac[5] = 0x6;
+
+ hardware.h_lport.p_element.system_id.conn_type = 0x5;
+ hardware.h_lport.p_element.system_id.smlt_id = 0x3CC;
+ hardware.h_lport.p_element.system_id.mlt_id[0] = 0xB;
+ hardware.h_lport.p_element.system_id.mlt_id[1] = 0xE;
+
+ /* Local chassis info */
+ chassis.c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
+ chassis.c_id = chassis_mac;
+ chassis.c_id_len = ETHER_ADDR_LEN;
+ chassis.c_name = "Dummy chassis";
+ chassis.c_descr = "Long dummy chassis description";
+ chassis.c_cap_available = LLDP_CAP_BRIDGE;
+ chassis.c_cap_enabled = LLDP_CAP_BRIDGE;
+
+ /* ISID/VLAN mappings */
+ map_init[0].isid_vlan_data.status = 0xC;
+ map_init[0].isid_vlan_data.vlan = 0x64;
+ map_init[0].isid_vlan_data.isid[0] = 1;
+ map_init[0].isid_vlan_data.isid[1] = 2;
+ map_init[0].isid_vlan_data.isid[2] = 3;
+
+ map_init[1].isid_vlan_data.status = 0xD;
+ map_init[1].isid_vlan_data.vlan = 0xF;
+ map_init[1].isid_vlan_data.isid[0] = 4;
+ map_init[1].isid_vlan_data.isid[1] = 5;
+ map_init[1].isid_vlan_data.isid[2] = 6;
+
+ /* Prepare an empty packet buffer */
+ ofpbuf_use_stub(&packet, stub, sizeof stub);
+ ofpbuf_clear(&packet);
+
+ /* Create a dummy lldp instance */
+ lldp = lldp_create_dummy();
+ if ((lldp == NULL) ||
+ (lldp->lldpd == NULL) ||
+ (lldp->lldpd->g_hardware.h_entries.next == NULL)) {
+
+ printf("Error: unable to create dummy lldp instance");
+ return 1;
+ }
+
+ /* Populate instance with local chassis info */
+ hw = (struct lldpd_hardware *) lldp->lldpd->g_hardware.h_entries.next;
+ ch = hw->h_lport.p_chassis;
+ ch->c_id_subtype = chassis.c_id_subtype;
+ ch->c_id = chassis.c_id;
+ ch->c_id_len = chassis.c_id_len;
+ ch->c_name = chassis.c_name;
+ ch->c_descr = chassis.c_descr;
+ ch->c_cap_available = chassis.c_cap_available;
+ ch->c_cap_enabled = chassis.c_cap_enabled;
+
+ /* Populate instance with local port info */
+ hw->h_lport.p_id_subtype = hardware.h_lport.p_id_subtype;
+ hw->h_lport.p_id = hardware.h_lport.p_id;
+ hw->h_lport.p_id_len = strlen(hw->h_lport.p_id);
+ hw->h_lport.p_descr = hardware.h_lport.p_descr;
+ hw->h_lport.p_mfs = hardware.h_lport.p_mfs;
+
+ /* Populate instance with auto attach element discovery info */
+
+ hw->h_lport.p_element.type = hardware.h_lport.p_element.type;
+ hw->h_lport.p_element.mgmt_vlan = hardware.h_lport.p_element.mgmt_vlan;
+ hw->h_lport.p_element.system_id.system_mac[0] =
+ hardware.h_lport.p_element.system_id.system_mac[0];
+ hw->h_lport.p_element.system_id.system_mac[1] =
+ hardware.h_lport.p_element.system_id.system_mac[1];
+ hw->h_lport.p_element.system_id.system_mac[2] =
+ hardware.h_lport.p_element.system_id.system_mac[2];
+ hw->h_lport.p_element.system_id.system_mac[3] =
+ hardware.h_lport.p_element.system_id.system_mac[3];
+ hw->h_lport.p_element.system_id.system_mac[4] =
+ hardware.h_lport.p_element.system_id.system_mac[4];
+ hw->h_lport.p_element.system_id.system_mac[5] =
+ hardware.h_lport.p_element.system_id.system_mac[5];
+
+ hw->h_lport.p_element.system_id.conn_type =
+ hardware.h_lport.p_element.system_id.conn_type;
+ hw->h_lport.p_element.system_id.smlt_id =
+ hardware.h_lport.p_element.system_id.smlt_id;
+ hw->h_lport.p_element.system_id.mlt_id[0] =
+ hardware.h_lport.p_element.system_id.mlt_id[0];
+ hw->h_lport.p_element.system_id.mlt_id[1] =
+ hardware.h_lport.p_element.system_id.mlt_id[1];
+
+ /* Populate instance with two auto attach isid/vlan mappings */
+ map[0].isid_vlan_data.status = map_init[0].isid_vlan_data.status;
+ map[0].isid_vlan_data.vlan = map_init[0].isid_vlan_data.vlan;
+ map[0].isid_vlan_data.isid[0] = map_init[0].isid_vlan_data.isid[0];
+ map[0].isid_vlan_data.isid[1] = map_init[0].isid_vlan_data.isid[1];
+ map[0].isid_vlan_data.isid[2] = map_init[0].isid_vlan_data.isid[2];
+
+ map[1].isid_vlan_data.status = map_init[1].isid_vlan_data.status;
+ map[1].isid_vlan_data.vlan = map_init[1].isid_vlan_data.vlan;
+ map[1].isid_vlan_data.isid[0] = map_init[1].isid_vlan_data.isid[0];
+ map[1].isid_vlan_data.isid[1] = map_init[1].isid_vlan_data.isid[1];
+ map[1].isid_vlan_data.isid[2] = map_init[1].isid_vlan_data.isid[2];
+
+ list_init(&hw->h_lport.p_isid_vlan_maps.m_entries);
+ list_push_back(&hw->h_lport.p_isid_vlan_maps.m_entries,
+ &map[0].m_entries);
+ list_push_back(&hw->h_lport.p_isid_vlan_maps.m_entries,
+ &map[1].m_entries);
+
+ /* Construct LLDPPDU (including Ethernet header) */
+ eth_compose(&packet, eth_addr_lldp, eth_src, ETH_TYPE_LLDP, 0);
+ n = lldp_send(lldp->lldpd, hw, &packet);
+
+ if (n == 0) {
+ printf("Error: unable to build packet\n");
+ return 1;
+ }
+
+ /* Decode the constructed LLDPPDU */
+ assert(lldp_decode(NULL, packet.data_, packet.size_, hw,
+ &nchassis, &nport) != -1);
+
+ /* Expecting returned pointers to allocated structures */
+ if (!nchassis || !nport) {
+ printf("Error: unable to decode packet");
+ return 1;
+ }
+
+ /* Verify chassis values */
+ check_received_chassis(&chassis, nchassis);
+
+ /* Verify port values */
+ check_received_port(&hardware.h_lport, nport);
+
+ /* Verify auto attach values */
+ check_received_aa(&hardware.h_lport, nport, map_init);
+
+ return 0;
+}
+
+
+static void
+test_aa_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+ int num_errors = 0;
+
+ /* Make sure we emit valid auto-attach LLDPPDUs */
+ num_tests++;
+ num_errors += test_aa_send();
+
+ /* Add more tests here */
+
+ printf("executed %d tests, %d errors\n", num_tests, num_errors);
+
+ exit(num_errors != 0);
+}
+
+OVSTEST_REGISTER("test-aa", test_aa_main);
m4_include([tests/rstp.at])
m4_include([tests/vlog.at])
m4_include([tests/vtep-ctl.at])
+m4_include([tests/auto-attach.at])
Deletes the current SSL configuration.
.
.IP "[\fB\-\-bootstrap\fR] \fBset\-ssl\fR \fIprivate-key\fR \fIcertificate\fR \fIca-cert\fR"
-Sets the SSL configuration. The \fB\-\-bootstrap\fR option is described
+Sets the SSL configuration. The \fB\-\-bootstrap\fR option is described
below.
.
.ST "CA Certificate Bootstrap"
as part of the SSL certificate chain. The SSL protocol does not
require the controller to send the CA certificate.
.
+.SS "Auto-Attach Commands"
+.
+The IETF Auto-Attach SPBM draft standard describes a compact method of using
+IEEE 802.1AB Link Layer Discovery Protocol (LLDP) together with a IEEE 802.1aq
+Shortest Path Bridging (SPB) network to automatically attach network devices to
+individual services in a SPB network. The intent here is to allow network
+applications and devices using OVS to be able to easily take advantage of
+features offered by industry standard SPB networks. A fundamental element of
+the Auto-Attach feature is to map traditional VLANs onto SPB I_SIDs. These
+commands manage the Auto-Attach I-SID/VLAN mappings.
+.
+.IP "\fBadd\-aa\-mapping \fIbridge i-sid vlan\fR"
+Creates a new Auto-Attach mapping on \fIbridge\fR for \fIi-sid\fR
+and \fIvlan\fR.
+.
+.IP "\fBdel\-aa\-mapping \fIbridge i-sid vlan\fR"
+Deletes an Auto-Attach mapping on \fIbridge\fR for \fIi-sid\fR
+and \fIvlan\fR.
+.IP "\fBget\-aa\-mapping \fIbridge\fR"
+Lists all of the Auto-Attach mappings within \fIbridge\fR on standard output.
+.
.SS "Database Commands"
.
These commands query and modify the contents of \fBovsdb\fR tables.
.IP "\fBFlow_Sample_Collector_Set\fR"
An IPFIX exporter configuration attached to a bridge for sampling
packets on a per-flow basis using OpenFlow \fBsample\fR actions.
+.IP "\fBAutoAttach\fR"
+Configuration for Auto Attach within a bridge.
.PP
Record names must be specified in full and with correct
capitalization. Names of tables and columns are not case-sensitive,
.B "ovs\-vsctl add\-port br0 eth0"
.PP
Alternatively, perform both operations in a single atomic transaction:
-.IP
+.IP
.B "ovs\-vsctl add\-br br0 \-\- add\-port br0 eth0"
.PP
Delete bridge \fBbr0\fR, reporting an error if it does not exist:
del-ssl delete the SSL configuration\n\
set-ssl PRIV-KEY CERT CA-CERT set the SSL configuration\n\
\n\
+Auto Attach commands:\n\
+ add-aa-mapping BRIDGE I-SID VLAN add Auto Attach mapping to BRIDGE\n\
+ del-aa-mapping BRIDGE I-SID VLAN delete Auto Attach mapping VLAN from BRIDGE\n\
+ get-aa-mapping BRIDGE get Auto Attach mappings from BRIDGE\n\
+\n\
Switch commands:\n\
emer-reset reset switch to known good state\n\
\n\
ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_controller);
ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_fail_mode);
ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_ports);
+ ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_auto_attach);
ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_name);
ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_fake_bridge);
ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_interfaces);
ovsdb_idl_add_column(ctx->idl, &ovsrec_interface_col_name);
+
+ ovsdb_idl_add_column(ctx->idl, &ovsrec_autoattach_col_mappings);
ovsdb_idl_add_column(ctx->idl, &ovsrec_interface_col_ofport);
}
if (!parent_name) {
struct ovsrec_port *port;
+ struct ovsrec_autoattach *aa;
struct ovsrec_bridge *br;
iface = ovsrec_interface_insert(ctx->txn);
ovsrec_port_set_name(port, br_name);
ovsrec_port_set_interfaces(port, &iface, 1);
+ aa = ovsrec_autoattach_insert(ctx->txn);
+
br = ovsrec_bridge_insert(ctx->txn);
ovsrec_bridge_set_name(br, br_name);
ovsrec_bridge_set_ports(br, &port, 1);
+ ovsrec_bridge_set_auto_attach(br, aa);
ovs_insert_bridge(ctx->ovs, br);
} else {
ovsrec_open_vswitch_set_ssl(ctx->ovs, ssl);
}
+
+static void
+autoattach_insert_mapping(struct ovsrec_autoattach *aa,
+ int64_t isid,
+ int64_t vlan)
+{
+ int64_t *key_mappings, *value_mappings;
+ size_t i;
+
+ key_mappings = xmalloc(sizeof *aa->key_mappings * (aa->n_mappings + 1));
+ value_mappings = xmalloc(sizeof *aa->value_mappings * (aa->n_mappings + 1));
+
+ for (i = 0; i < aa->n_mappings; i++) {
+ key_mappings[i] = aa->key_mappings[i];
+ value_mappings[i] = aa->value_mappings[i];
+ }
+ key_mappings[aa->n_mappings] = isid;
+ value_mappings[aa->n_mappings] = vlan;
+
+ ovsrec_autoattach_set_mappings(aa, key_mappings, value_mappings,
+ aa->n_mappings + 1);
+
+ free(key_mappings);
+ free(value_mappings);
+}
+
+static void
+cmd_add_aa_mapping(struct vsctl_context *ctx)
+{
+ struct vsctl_bridge *br;
+ int64_t isid, vlan;
+ char *nptr = NULL;
+
+ isid = strtoull(ctx->argv[2], &nptr, 10);
+ if (nptr == ctx->argv[2] || nptr == NULL) {
+ vsctl_fatal("Invalid argument %s", ctx->argv[2]);
+ return;
+ }
+
+ vlan = strtoull(ctx->argv[3], &nptr, 10);
+ if (nptr == ctx->argv[3] || nptr == NULL) {
+ vsctl_fatal("Invalid argument %s", ctx->argv[3]);
+ return;
+ }
+
+ vsctl_context_populate_cache(ctx);
+
+ br = find_bridge(ctx, ctx->argv[1], true);
+ if (br->parent) {
+ br = br->parent;
+ }
+
+ if (br && br->br_cfg) {
+ autoattach_insert_mapping(br->br_cfg->auto_attach, isid, vlan);
+ }
+}
+
+static void
+del_aa_mapping(struct ovsrec_autoattach *aa,
+ int64_t isid,
+ int64_t vlan)
+{
+ int64_t *key_mappings, *value_mappings;
+ size_t i, n;
+
+ key_mappings = xmalloc(sizeof *aa->key_mappings * (aa->n_mappings));
+ value_mappings = xmalloc(sizeof *value_mappings * (aa->n_mappings));
+
+ for (i = n = 0; i < aa->n_mappings; i++) {
+ if (aa->key_mappings[i] != isid && aa->value_mappings[i] != vlan) {
+ key_mappings[n] = aa->key_mappings[i];
+ value_mappings[n++] = aa->value_mappings[i];
+ }
+ }
+
+ ovsrec_autoattach_set_mappings(aa, key_mappings, value_mappings, n);
+
+ free(key_mappings);
+ free(value_mappings);
+}
+
+static void
+cmd_del_aa_mapping(struct vsctl_context *ctx)
+{
+ struct vsctl_bridge *br;
+ int64_t isid, vlan;
+ char *nptr = NULL;
+
+ isid = strtoull(ctx->argv[2], &nptr, 10);
+ if (nptr == ctx->argv[2] || nptr == NULL) {
+ vsctl_fatal("Invalid argument %s", ctx->argv[2]);
+ return;
+ }
+
+ vlan = strtoull(ctx->argv[3], &nptr, 10);
+ if (nptr == ctx->argv[3] || nptr == NULL) {
+ vsctl_fatal("Invalid argument %s", ctx->argv[3]);
+ return;
+ }
+
+ vsctl_context_populate_cache(ctx);
+
+ br = find_bridge(ctx, ctx->argv[1], true);
+ if (br->parent) {
+ br = br->parent;
+ }
+
+ if (br && br->br_cfg && br->br_cfg->auto_attach &&
+ br->br_cfg->auto_attach->key_mappings &&
+ br->br_cfg->auto_attach->value_mappings) {
+ size_t i;
+
+ for (i = 0; i < br->br_cfg->auto_attach->n_mappings; i++) {
+ if (br->br_cfg->auto_attach->key_mappings[i] == isid &&
+ br->br_cfg->auto_attach->value_mappings[i] == vlan) {
+ del_aa_mapping(br->br_cfg->auto_attach, isid, vlan);
+ break;
+ }
+ }
+ }
+}
+
+static void
+pre_aa_mapping(struct vsctl_context *ctx)
+{
+ pre_get_info(ctx);
+
+ ovsdb_idl_add_column(ctx->idl, &ovsrec_autoattach_col_mappings);
+}
+
+static void
+verify_auto_attach(struct ovsrec_bridge *bridge)
+{
+ if (bridge) {
+ ovsrec_bridge_verify_auto_attach(bridge);
+
+ if (bridge->auto_attach) {
+ ovsrec_autoattach_verify_mappings(bridge->auto_attach);
+ }
+ }
+}
+
+static void
+cmd_get_aa_mapping(struct vsctl_context *ctx)
+{
+ struct vsctl_bridge *br;
+
+ vsctl_context_populate_cache(ctx);
+
+ br = find_bridge(ctx, ctx->argv[1], true);
+ if (br->parent) {
+ br = br->parent;
+ }
+
+ verify_auto_attach(br->br_cfg);
+
+ if (br && br->br_cfg && br->br_cfg->auto_attach &&
+ br->br_cfg->auto_attach->key_mappings &&
+ br->br_cfg->auto_attach->value_mappings) {
+ size_t i;
+
+ for (i = 0; i < br->br_cfg->auto_attach->n_mappings; i++) {
+ ds_put_format(&ctx->output, "%"PRId64" %"PRId64"\n",
+ (long int) br->br_cfg->auto_attach->key_mappings[i],
+ (long int) br->br_cfg->auto_attach->value_mappings[i]);
+ }
+ }
+}
+
\f
/* Parameter commands. */
{&ovsrec_table_flow_sample_collector_set, NULL,
&ovsrec_flow_sample_collector_set_col_ipfix}}},
+ {&ovsrec_table_autoattach,
+ {{&ovsrec_table_bridge,
+ &ovsrec_bridge_col_name,
+ &ovsrec_bridge_col_auto_attach},
+ {NULL, NULL, NULL}}},
+
{&ovsrec_table_flow_sample_collector_set,
{{NULL, NULL, NULL},
{NULL, NULL, NULL}}},
{"del-ssl", 0, 0, pre_cmd_del_ssl, cmd_del_ssl, NULL, "", RW},
{"set-ssl", 3, 3, pre_cmd_set_ssl, cmd_set_ssl, NULL, "--bootstrap", RW},
+ /* Auto Attach commands. */
+ {"add-aa-mapping", 3, 3, pre_get_info, cmd_add_aa_mapping, NULL, "", RW},
+ {"del-aa-mapping", 3, 3, pre_aa_mapping, cmd_del_aa_mapping, NULL, "", RW},
+ {"get-aa-mapping", 1, 1, pre_aa_mapping, cmd_get_aa_mapping, NULL, "", RO},
+
/* Switch commands. */
{"emer-reset", 0, 0, pre_cmd_emer_reset, cmd_emer_reset, NULL, "", RW},
#include "jsonrpc.h"
#include "lacp.h"
#include "list.h"
+#include "ovs-lldp.h"
#include "mac-learning.h"
#include "mcast-snooping.h"
#include "meta-flow.h"
/* Port mirroring. */
struct hmap mirrors; /* "struct mirror" indexed by UUID. */
+ /* Auto Attach */
+ struct hmap mappings; /* "struct" indexed by UUID */
+
/* Used during reconfiguration. */
struct shash wanted_ports;
struct ovsrec_interface *synth_local_ifacep;
};
+struct aa_mapping {
+ struct hmap_node hmap_node; /* In struct bridge's "mappings" hmap. */
+ struct bridge *bridge;
+ int64_t isid;
+ int64_t vlan;
+ char *br_name;
+};
+
/* All bridges, indexed by name. */
static struct hmap all_bridges = HMAP_INITIALIZER(&all_bridges);
static int stats_timer_interval;
static long long int stats_timer = LLONG_MIN;
+/* Each time this timer expires, the bridge fetches the list of port/VLAN
+ * membership that has been modified by the AA.
+ */
+#define AA_REFRESH_INTERVAL (1000) /* In milliseconds. */
+static long long int aa_refresh_timer = LLONG_MIN;
+
/* In some datapaths, creating and destroying OpenFlow ports can be extremely
* expensive. This can cause bridge_reconfigure() to take a long time during
* which no other work can be done. To deal with this problem, we limit port
static void bridge_configure_rstp(struct bridge *);
static void bridge_configure_tables(struct bridge *);
static void bridge_configure_dp_desc(struct bridge *);
+static void bridge_configure_aa(struct bridge *);
+static void bridge_aa_refresh_queued(struct bridge *);
+static bool bridge_aa_need_refresh(struct bridge *);
static void bridge_configure_remotes(struct bridge *,
const struct sockaddr_in *managers,
size_t n_managers);
ovsdb_idl_omit_alert(idl, &ovsrec_port_col_statistics);
ovsdb_idl_omit_alert(idl, &ovsrec_port_col_bond_active_slave);
ovsdb_idl_omit(idl, &ovsrec_port_col_external_ids);
-
+ ovsdb_idl_omit_alert(idl, &ovsrec_port_col_trunks);
+ ovsdb_idl_omit_alert(idl, &ovsrec_port_col_vlan_mode);
ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_admin_state);
ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_duplex);
ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_link_speed);
cfm_init();
ovs_numa_init();
stp_init();
+ lldp_init();
rstp_init();
}
iface_set_mac(br, port, iface);
ofproto_port_set_bfd(br->ofproto, iface->ofp_port,
&iface->cfg->bfd);
+ ofproto_port_set_lldp(br->ofproto, iface->ofp_port,
+ &iface->cfg->lldp);
}
}
bridge_configure_mirrors(br);
bridge_configure_rstp(br);
bridge_configure_tables(br);
bridge_configure_dp_desc(br);
+ bridge_configure_aa(br);
}
free(managers);
}
}
}
+
+ /* Refresh AA port status if necessary. */
+ if (time_msec() >= aa_refresh_timer) {
+ struct bridge *br;
+
+ HMAP_FOR_EACH (br, node, &all_bridges) {
+ if (bridge_aa_need_refresh(br)) {
+ struct ovsdb_idl_txn *txn;
+
+ txn = ovsdb_idl_txn_create(idl);
+ bridge_aa_refresh_queued(br);
+ ovsdb_idl_txn_commit(txn);
+ ovsdb_idl_txn_destroy(txn);
+ }
+ }
+
+ aa_refresh_timer = time_msec() + AA_REFRESH_INTERVAL;
+ }
}
static void
hmap_init(&br->iface_by_name);
hmap_init(&br->mirrors);
+ hmap_init(&br->mappings);
hmap_insert(&all_bridges, &br->node, hash_string(br->name, 0));
}
hmap_destroy(&br->ports);
hmap_destroy(&br->iface_by_name);
hmap_destroy(&br->mirrors);
+ hmap_destroy(&br->mappings);
free(br->name);
free(br->type);
free(br);
ofproto_set_dp_desc(br->ofproto,
smap_get(&br->cfg->other_config, "dp-desc"));
}
+
+static struct aa_mapping *
+bridge_aa_mapping_find(struct bridge *br, const int64_t isid)
+{
+ struct aa_mapping *m;
+
+ HMAP_FOR_EACH_IN_BUCKET (m,
+ hmap_node,
+ hash_bytes(&isid, sizeof isid, 0),
+ &br->mappings) {
+ if (isid == m->isid) {
+ return m;
+ }
+ }
+ return NULL;
+}
+
+static struct aa_mapping *
+bridge_aa_mapping_create(struct bridge *br,
+ const int64_t isid,
+ const int64_t vlan)
+{
+ struct aa_mapping *m;
+
+ m = xzalloc(sizeof *m);
+ m->bridge = br;
+ m->isid = isid;
+ m->vlan = vlan;
+ m->br_name = xstrdup(br->name);
+ hmap_insert(&br->mappings,
+ &m->hmap_node,
+ hash_bytes(&isid, sizeof isid, 0));
+
+ return m;
+}
+
+static void
+bridge_aa_mapping_destroy(struct aa_mapping *m)
+{
+ if (m) {
+ struct bridge *br = m->bridge;
+
+ if (br->ofproto) {
+ ofproto_aa_mapping_unregister(br->ofproto, m);
+ }
+
+ hmap_remove(&br->mappings, &m->hmap_node);
+ if (m->br_name) {
+ free(m->br_name);
+ }
+ free(m);
+ }
+}
+
+static bool
+bridge_aa_mapping_configure(struct aa_mapping *m)
+{
+ struct aa_mapping_settings s;
+
+ s.isid = m->isid;
+ s.vlan = m->vlan;
+
+ /* Configure. */
+ ofproto_aa_mapping_register(m->bridge->ofproto, m, &s);
+
+ return true;
+}
+
+static void
+bridge_configure_aa(struct bridge *br)
+{
+ const struct ovsdb_datum *mc;
+ struct ovsrec_autoattach *auto_attach = br->cfg->auto_attach;
+ struct aa_settings aa_s;
+ struct aa_mapping *m, *next;
+ size_t i;
+
+ if (!auto_attach) {
+ ofproto_set_aa(br->ofproto, NULL, NULL);
+ return;
+ }
+
+ memset(&aa_s, 0, sizeof aa_s);
+ aa_s.system_description = auto_attach->system_description;
+ aa_s.system_name = auto_attach->system_name;
+ ofproto_set_aa(br->ofproto, NULL, &aa_s);
+
+ mc = ovsrec_autoattach_get_mappings(auto_attach,
+ OVSDB_TYPE_INTEGER,
+ OVSDB_TYPE_INTEGER);
+ HMAP_FOR_EACH_SAFE (m, next, hmap_node, &br->mappings) {
+ union ovsdb_atom atom;
+
+ atom.integer = m->isid;
+ if (ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_UUID) == UINT_MAX) {
+ VLOG_INFO("Deleting isid=%"PRId64", vlan=%"PRId64,
+ m->isid,
+ m->vlan);
+ bridge_aa_mapping_destroy(m);
+ }
+ }
+
+ /* Add new mappings and reconfigure existing ones. */
+ for (i = 0; i < auto_attach->n_mappings; ++i) {
+ struct aa_mapping *m =
+ bridge_aa_mapping_find(br, auto_attach->key_mappings[i]);
+
+ if (!m) {
+ VLOG_INFO("Adding isid=%"PRId64", vlan=%"PRId64,
+ auto_attach->key_mappings[i],
+ auto_attach->value_mappings[i]);
+ m = bridge_aa_mapping_create(br,
+ auto_attach->key_mappings[i],
+ auto_attach->value_mappings[i]);
+
+ if (!bridge_aa_mapping_configure(m)) {
+ bridge_aa_mapping_destroy(m);
+ }
+ }
+ }
+}
+
+static bool
+bridge_aa_need_refresh(struct bridge *br)
+{
+ return ofproto_aa_vlan_get_queue_size(br->ofproto) > 0;
+}
+
+static void
+bridge_aa_update_trunks(struct port *port, struct bridge_aa_vlan *m)
+{
+ int64_t *trunks = NULL;
+ unsigned int i = 0;
+ bool found = false, reconfigure = false;
+
+ for (i = 0; i < port->cfg->n_trunks; i++) {
+ if (port->cfg->trunks[i] == m->vlan) {
+ found = true;
+ break;
+ }
+ }
+
+ switch (m->oper) {
+ case BRIDGE_AA_VLAN_OPER_ADD:
+ if (!found) {
+ trunks = xmalloc(sizeof *trunks * (port->cfg->n_trunks + 1));
+
+ for (i = 0; i < port->cfg->n_trunks; i++) {
+ trunks[i] = port->cfg->trunks[i];
+ }
+ trunks[i++] = m->vlan;
+ reconfigure = true;
+ }
+
+ break;
+
+ case BRIDGE_AA_VLAN_OPER_REMOVE:
+ if (found) {
+ unsigned int j = 0;
+
+ trunks = xmalloc(sizeof *trunks * (port->cfg->n_trunks - 1));
+
+ for (i = 0; i < port->cfg->n_trunks; i++) {
+ if (port->cfg->trunks[i] != m->vlan) {
+ trunks[j++] = port->cfg->trunks[i];
+ }
+ }
+ i = j;
+ reconfigure = true;
+ }
+
+ break;
+
+ case BRIDGE_AA_VLAN_OPER_UNDEF:
+ default:
+ VLOG_WARN("unrecognized operation %u", m->oper);
+ break;
+ }
+
+ if (reconfigure) {
+ /* VLAN switching under trunk mode cause the trunk port to switch all
+ * VLANs, see ovs-vswitchd.conf.db
+ */
+ if (i == 0) {
+ static char *vlan_mode_access = "access";
+ ovsrec_port_set_vlan_mode(port->cfg, vlan_mode_access);
+ }
+
+ if (i == 1) {
+ static char *vlan_mode_trunk = "trunk";
+ ovsrec_port_set_vlan_mode(port->cfg, vlan_mode_trunk);
+ }
+
+ ovsrec_port_set_trunks(port->cfg, trunks, i);
+
+ /* Force reconfigure of the port. */
+ port_configure(port);
+ }
+}
+
+static void
+bridge_aa_refresh_queued(struct bridge *br)
+{
+ struct ovs_list *list = xmalloc(sizeof *list);
+ struct bridge_aa_vlan *node;
+
+ list_init(list);
+ ofproto_aa_vlan_get_queued(br->ofproto, list);
+
+ LIST_FOR_EACH(node, list_node, list) {
+ struct port *port;
+
+ VLOG_INFO("ifname=%s, vlan=%u, oper=%u", node->port_name, node->vlan,
+ node->oper);
+
+ port = port_lookup(br, node->port_name);
+ if (port) {
+ bridge_aa_update_trunks(port, node);
+ }
+
+ list_remove(&node->list_node);
+ free(node->port_name);
+ free(node);
+ }
+
+ free(list);
+}
+
\f
/* Port functions. */
{"name": "Open_vSwitch",
- "version": "7.11.1",
- "cksum": "1038213587 21518",
+ "version": "7.11.2",
+ "cksum": "320332148 22294",
"tables": {
"Open_vSwitch": {
"columns": {
"maxInteger": 254},
"value": {"type": "uuid",
"refTable": "Flow_Table"},
- "min": 0, "max": "unlimited"}}},
+ "min": 0, "max": "unlimited"}},
+ "auto_attach": {
+ "type": {"key": {"type": "uuid",
+ "refTable": "AutoAttach"},
+ "min": 0, "max": 1}}},
"indexes": [["name"]]},
"Port": {
"columns": {
"type": {"key": {"type": "boolean"},
"min": 0, "max": 1},
"ephemeral": true},
+ "lldp": {
+ "type": {"key": "string", "value": "string",
+ "min": 0, "max": "unlimited"}},
"other_config": {
"type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}},
"statistics": {
"external_ids": {
"type": {"key": "string", "value": "string",
"min": 0, "max": "unlimited"}}},
- "maxRows": 1}}}
+ "maxRows": 1},
+ "AutoAttach": {
+ "columns": {
+ "system_name": {
+ "type": "string"},
+ "system_description": {
+ "type": "string"},
+ "mappings": {
+ "type": {"key": {"type": "integer",
+ "minInteger": 0,
+ "maxInteger": 16777215},
+ "value": {"type": "integer",
+ "minInteger": 0,
+ "maxInteger": 4095},
+ "min": 0, "max": "unlimited"}}}}}}
a different type of mirror instead.
</p>
</column>
+
+ <column name="auto_attach">
+ Auto Attach configuration.
+ </column>
</group>
<group title="OpenFlow Configuration">
</column>
</group>
+ <group title="Auto Attach Configuration">
+ <p>
+ Auto Attach configuration for a particular interface.
+ </p>
+
+ <column name="lldp" key="enable" type='{"type": "boolean"}'>
+ True to enable LLDP on this <ref table="Interface"/>. If not
+ specified, LLDP will be disabled by default.
+ </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.
</group>
</table>
+ <table name="AutoAttach">
+ <p>Auto Attach configuration within a bridge. The IETF Auto-Attach SPBM
+ draft standard describes a compact method of using IEEE 802.1AB Link
+ Layer Discovery Protocol (LLDP) together with a IEEE 802.1aq Shortest
+ Path Bridging (SPB) network to automatically attach network devices
+ to individual services in a SPB network. The intent here is to allow
+ network applications and devices using OVS to be able to easily take
+ advantage of features offered by industry standard SPB networks.</p>
+
+ <p>Auto Attach (AA) uses LLDP to communicate between a directly connected
+ Auto Attach Client (AAC) and Auto Attach Server (AAS). The LLDP protocol
+ is extended to add two new Type-Length-Value tuples (TLVs). The first
+ new TLV supports the ongoing discovery of directly connected AA
+ correspondents. Auto Attach operates by regularly transmitting AA
+ discovery TLVs between the AA client and AA server. By exchanging these
+ discovery messages, both the AAC and AAS learn the system name and
+ system description of their peer. In the OVS context, OVS operates as
+ the AA client and the AA server resides on a switch at the edge of the
+ SPB network.</p>
+
+ <p>Once AA discovery has been completed the AAC then uses the
+ second new TLV to deliver identifier mappings from the AAC to the AAS. A primary
+ feature of Auto Attach is to facilitate the mapping of VLANs defined
+ outside the SPB network onto service ids (ISIDs) defined within the SPM
+ network. By doing so individual external VLANs can be mapped onto
+ specific SPB network services. These VLAN id to ISID mappings can be
+ configured and managed locally using new options added to the ovs-vsctl
+ command.</p>
+
+ <p>The Auto Attach OVS feature does not provide a full implementation of
+ the LLDP protocol. Support for the mandatory TLVs as defined by the LLDP
+ standard and support for the AA TLV extensions is provided. LLDP
+ protocol support in OVS can be enabled or disabled on a port by port
+ basis. LLDP support is disabled by default.</p>
+
+ <column name="system_name">
+ The system_name string is exported in LLDP messages. It should uniquely
+ identify the bridge in the network.
+ </column>
+
+ <column name="system_description">
+ The system_description string is exported in LLDP messages. It should
+ describe the type of software and hardware.
+ </column>
+
+ <column name="mappings">
+ A mapping from SPB network Individual Service Identifier (ISID) to VLAN id.
+ </column>
+ </table>
</database>