/*
- * Copyright (c) 2008, 2009 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#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"
+VLOG_DEFINE_THIS_MODULE(mac_learning)
+
+/* 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)
{
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);
}
}