/*
- * Copyright (c) 2008, 2009 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira Networks.
*
- * 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.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
*
- * 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.
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
#include <config.h>
#include <inttypes.h>
#include <stdlib.h>
+#include "bitmap.h"
#include "coverage.h"
#include "hash.h"
#include "list.h"
#include "tag.h"
#include "timeval.h"
#include "util.h"
-
-#define THIS_MODULE VLM_mac_learning
#include "vlog.h"
-#define MAC_HASH_BITS 10
-#define MAC_HASH_MASK (MAC_HASH_SIZE - 1)
-#define MAC_HASH_SIZE (1u << MAC_HASH_BITS)
-
-#define MAC_MAX 1024
-
-/* A MAC learning table entry. */
-struct mac_entry {
- struct list hash_node; /* Element in a mac_learning 'table' list. */
- struct list lru_node; /* Element in 'lrus' or 'free' list. */
- time_t expires; /* Expiration time. */
- uint8_t mac[ETH_ADDR_LEN]; /* Known MAC address. */
- uint16_t vlan; /* VLAN tag. */
- int port; /* Port on which MAC was most recently seen. */
- tag_type tag; /* Tag for this learning entry. */
-};
-
-/* MAC learning table. */
-struct mac_learning {
- struct list free; /* Not-in-use entries. */
- struct list lrus; /* In-use entries, least recently used at the
- front, most recently used at the back. */
- struct list table[MAC_HASH_SIZE]; /* Hash table. */
- struct mac_entry entries[MAC_MAX]; /* All entries. */
- uint32_t secret; /* Secret for */
-};
+VLOG_DEFINE_THIS_MODULE(mac_learning);
+
+COVERAGE_DEFINE(mac_learning_learned);
+COVERAGE_DEFINE(mac_learning_expired);
+
+/* Returns the number of seconds since 'e' was last learned. */
+int
+mac_entry_age(const struct mac_entry *e)
+{
+ time_t remaining = e->expires - time_now();
+ return MAC_ENTRY_IDLE_TIME - remaining;
+}
static uint32_t
mac_table_hash(const uint8_t mac[ETH_ADDR_LEN], uint16_t vlan)
uint16_t vlan)
{
struct mac_entry *e;
- LIST_FOR_EACH (e, struct mac_entry, hash_node, bucket) {
+ LIST_FOR_EACH (e, hash_node, bucket) {
if (eth_addr_equals(e->mac, mac) && e->vlan == vlan) {
return e;
}
list_push_front(&ml->free, &s->lru_node);
}
ml->secret = random_uint32();
+ ml->flood_vlans = NULL;
return ml;
}
void
mac_learning_destroy(struct mac_learning *ml)
{
+ if (ml) {
+ bitmap_free(ml->flood_vlans);
+ }
free(ml);
}
+/* Provides a bitmap of VLANs which have learning disabled, that is, VLANs on
+ * which all packets are flooded. It takes ownership of the bitmap. Returns
+ * true if the set has changed from the previous value. */
+bool
+mac_learning_set_flood_vlans(struct mac_learning *ml, unsigned long *bitmap)
+{
+ bool ret = (bitmap == NULL
+ ? ml->flood_vlans != NULL
+ : (ml->flood_vlans == NULL
+ || !bitmap_equal(bitmap, ml->flood_vlans, 4096)));
+
+ bitmap_free(ml->flood_vlans);
+ ml->flood_vlans = bitmap;
+
+ return ret;
+}
+
+static bool
+is_learning_vlan(const struct mac_learning *ml, uint16_t vlan)
+{
+ return !(ml->flood_vlans && bitmap_is_set(ml->flood_vlans, vlan));
+}
+
/* Attempts to make 'ml' learn from the fact that a frame from 'src_mac' was
* just observed arriving from 'src_port' on the given 'vlan'.
*
* that now need revalidation.
*
* The 'vlan' parameter is used to maintain separate per-VLAN learning tables.
- * Specify 0 if this behavior is undesirable. */
+ * Specify 0 if this behavior is undesirable.
+ *
+ * 'lock_type' specifies whether the entry should be locked or existing locks
+ * are check. */
tag_type
mac_learning_learn(struct mac_learning *ml,
const uint8_t src_mac[ETH_ADDR_LEN], uint16_t vlan,
- uint16_t src_port)
+ uint16_t src_port, enum grat_arp_lock_type lock_type)
{
struct mac_entry *e;
struct list *bucket;
+ if (!is_learning_vlan(ml, vlan)) {
+ return 0;
+ }
+
if (eth_addr_is_multicast(src_mac)) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 30);
VLOG_DBG_RL(&rl, "multicast packet source "ETH_ADDR_FMT,
e->port = -1;
e->vlan = vlan;
e->tag = make_unknown_mac_tag(ml, src_mac, vlan);
+ e->grat_arp_lock = TIME_MIN;
}
- /* Make the entry most-recently-used. */
- list_remove(&e->lru_node);
- list_push_back(&ml->lrus, &e->lru_node);
- e->expires = time_now() + 60;
-
- /* Did we learn something? */
- if (e->port != src_port) {
- tag_type old_tag = e->tag;
- e->port = src_port;
- e->tag = tag_create_random();
- COVERAGE_INC(mac_learning_learned);
- return old_tag;
+ if (lock_type != GRAT_ARP_LOCK_CHECK || time_now() >= e->grat_arp_lock) {
+ /* Make the entry most-recently-used. */
+ list_remove(&e->lru_node);
+ list_push_back(&ml->lrus, &e->lru_node);
+ e->expires = time_now() + MAC_ENTRY_IDLE_TIME;
+ if (lock_type == GRAT_ARP_LOCK_SET) {
+ e->grat_arp_lock = time_now() + MAC_GRAT_ARP_LOCK_TIME;
+ }
+
+ /* Did we learn something? */
+ if (e->port != src_port) {
+ tag_type old_tag = e->tag;
+ e->port = src_port;
+ e->tag = tag_create_random();
+ COVERAGE_INC(mac_learning_learned);
+ return old_tag;
+ }
}
+
return 0;
}
/* Looks up MAC 'dst' for VLAN 'vlan' in 'ml'. Returns the port on which a
- * frame destined for 'dst' should be sent, -1 if unknown. */
+ * frame destined for 'dst' should be sent, -1 if unknown. 'is_grat_arp_locked'
+ * is an optional parameter that returns whether the entry is currently
+ * locked. */
int
mac_learning_lookup(const struct mac_learning *ml,
- const uint8_t dst[ETH_ADDR_LEN], uint16_t vlan)
+ const uint8_t dst[ETH_ADDR_LEN], uint16_t vlan,
+ bool *is_grat_arp_locked)
{
tag_type tag = 0;
- return mac_learning_lookup_tag(ml, dst, vlan, &tag);
+ return mac_learning_lookup_tag(ml, dst, vlan, &tag, is_grat_arp_locked);
}
/* Looks up MAC 'dst' for VLAN 'vlan' in 'ml'. Returns the port on which a
*
* Adds to '*tag' (which the caller must have initialized) the tag that should
* be attached to any flow created based on the return value, if any, to allow
- * those flows to be revalidated when the MAC learning entry changes. */
+ * those flows to be revalidated when the MAC learning entry changes.
+ *
+ * 'is_grat_arp_locked' is an optional parameter that returns whether the entry
+ * is currently locked.*/
int
mac_learning_lookup_tag(const struct mac_learning *ml,
const uint8_t dst[ETH_ADDR_LEN], uint16_t vlan,
- tag_type *tag)
+ tag_type *tag, bool *is_grat_arp_locked)
{
- if (eth_addr_is_multicast(dst)) {
+ if (eth_addr_is_multicast(dst) || !is_learning_vlan(ml, vlan)) {
return -1;
} else {
struct mac_entry *e = search_bucket(mac_table_bucket(ml, dst, vlan),
dst, vlan);
if (e) {
*tag |= e->tag;
+
+ if (is_grat_arp_locked) {
+ *is_grat_arp_locked = time_now() < e->grat_arp_lock;
+ }
+
return e->port;
} else {
*tag |= make_unknown_mac_tag(ml, dst, vlan);
{
if (!list_is_empty(&ml->lrus)) {
struct mac_entry *e = mac_entry_from_lru_node(ml->lrus.next);
- poll_timer_wait((e->expires - time_now()) * 1000);
+ poll_timer_wait_until(e->expires * 1000LL);
}
}