+
+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=%"PRIu32", vlan=%"PRIu16,
+ 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, *next;
+
+ list_init(list);
+ ofproto_aa_vlan_get_queued(br->ofproto, list);
+
+ LIST_FOR_EACH_SAFE (node, next, 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);
+}
+