summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--lib/mac-learning.c24
-rw-r--r--lib/mac-learning.h5
-rw-r--r--ofproto/ofproto-dpif.c6
-rw-r--r--ofproto/ofproto-provider.h11
-rw-r--r--ofproto/ofproto.c11
-rw-r--r--ofproto/ofproto.h3
-rw-r--r--tests/ofproto-dpif.at62
-rw-r--r--vswitchd/bridge.c19
-rw-r--r--vswitchd/vswitch.xml9
10 files changed, 131 insertions, 20 deletions
diff --git a/NEWS b/NEWS
index e1d40704d..225580578 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,6 @@
post-v1.9.0
--------------------
+ - The maximum size of the MAC learning table is now configurable.
- New support for the VXLAN tunnel protocol (see the IETF draft here:
http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-02).
- New "vlog/disable-rate-limit" and "vlog/enable-rate-limit" commands
diff --git a/lib/mac-learning.c b/lib/mac-learning.c
index 3c541af2b..f609d48e2 100644
--- a/lib/mac-learning.c
+++ b/lib/mac-learning.c
@@ -110,7 +110,8 @@ normalize_idle_time(unsigned int idle_time)
}
/* Creates and returns a new MAC learning table with an initial MAC aging
- * timeout of 'idle_time' seconds. */
+ * timeout of 'idle_time' seconds and an initial maximum of MAC_DEFAULT_MAX
+ * entries. */
struct mac_learning *
mac_learning_create(unsigned int idle_time)
{
@@ -122,6 +123,7 @@ mac_learning_create(unsigned int idle_time)
ml->secret = random_uint32();
ml->flood_vlans = NULL;
ml->idle_time = normalize_idle_time(idle_time);
+ ml->max_entries = MAC_DEFAULT_MAX;
return ml;
}
@@ -176,6 +178,16 @@ mac_learning_set_idle_time(struct mac_learning *ml, unsigned int idle_time)
}
}
+/* Sets the maximum number of entries in 'ml' to 'max_entries', adjusting it
+ * to be within a reasonable range. */
+void
+mac_learning_set_max_entries(struct mac_learning *ml, size_t max_entries)
+{
+ ml->max_entries = (max_entries < 10 ? 10
+ : max_entries > 1000 * 1000 ? 1000 * 1000
+ : max_entries);
+}
+
static bool
is_learning_vlan(const struct mac_learning *ml, uint16_t vlan)
{
@@ -212,7 +224,7 @@ mac_learning_insert(struct mac_learning *ml,
if (!e) {
uint32_t hash = mac_table_hash(ml, src_mac, vlan);
- if (hmap_count(&ml->table) >= MAC_MAX) {
+ if (hmap_count(&ml->table) >= ml->max_entries) {
get_lru(ml, &e);
mac_learning_expire(ml, e);
}
@@ -311,7 +323,9 @@ void
mac_learning_run(struct mac_learning *ml, struct tag_set *set)
{
struct mac_entry *e;
- while (get_lru(ml, &e) && time_now() >= e->expires) {
+ while (get_lru(ml, &e)
+ && (hmap_count(&ml->table) > ml->max_entries
+ || time_now() >= e->expires)) {
COVERAGE_INC(mac_learning_expired);
if (set) {
tag_set_add(set, e->tag);
@@ -323,7 +337,9 @@ mac_learning_run(struct mac_learning *ml, struct tag_set *set)
void
mac_learning_wait(struct mac_learning *ml)
{
- if (!list_is_empty(&ml->lrus)) {
+ if (hmap_count(&ml->table) > ml->max_entries) {
+ poll_immediate_wake();
+ } else if (!list_is_empty(&ml->lrus)) {
struct mac_entry *e = mac_entry_from_lru_node(ml->lrus.next);
poll_timer_wait_until(e->expires * 1000LL);
}
diff --git a/lib/mac-learning.h b/lib/mac-learning.h
index 8f8fd4587..284e7f636 100644
--- a/lib/mac-learning.h
+++ b/lib/mac-learning.h
@@ -26,7 +26,8 @@
struct mac_learning;
-#define MAC_MAX 2048
+/* Default maximum size of a MAC learning table, in entries. */
+#define MAC_DEFAULT_MAX 2048
/* Time, in seconds, before expiring a mac_entry due to inactivity. */
#define MAC_ENTRY_DEFAULT_IDLE_TIME 300
@@ -83,6 +84,7 @@ struct mac_learning {
uint32_t secret; /* Secret for randomizing hash table. */
unsigned long *flood_vlans; /* Bitmap of learning disabled VLANs. */
unsigned int idle_time; /* Max age before deleting an entry. */
+ size_t max_entries; /* Max number of learned MACs. */
};
/* Basics. */
@@ -96,6 +98,7 @@ void mac_learning_wait(struct mac_learning *);
bool mac_learning_set_flood_vlans(struct mac_learning *,
const unsigned long *bitmap);
void mac_learning_set_idle_time(struct mac_learning *, unsigned int idle_time);
+void mac_learning_set_max_entries(struct mac_learning *, size_t max_entries);
/* Learning. */
bool mac_learning_may_learn(const struct mac_learning *,
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index d32f6d163..ca0a06511 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -2762,10 +2762,12 @@ forward_bpdu_changed(struct ofproto *ofproto_)
}
static void
-set_mac_idle_time(struct ofproto *ofproto_, unsigned int idle_time)
+set_mac_table_config(struct ofproto *ofproto_, unsigned int idle_time,
+ size_t max_entries)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
mac_learning_set_idle_time(ofproto->ml, idle_time);
+ mac_learning_set_max_entries(ofproto->ml, max_entries);
}
/* Ports. */
@@ -7940,6 +7942,6 @@ const struct ofproto_class ofproto_dpif_class = {
set_flood_vlans,
is_mirror_output_bundle,
forward_bpdu_changed,
- set_mac_idle_time,
+ set_mac_table_config,
set_realdev,
};
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 8f6f6b1a8..bc0105fc1 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -1279,9 +1279,14 @@ struct ofproto_class {
* will be invoked. */
void (*forward_bpdu_changed)(struct ofproto *ofproto);
- /* Sets the MAC aging timeout for the OFPP_NORMAL action to 'idle_time',
- * in seconds. */
- void (*set_mac_idle_time)(struct ofproto *ofproto, unsigned int idle_time);
+ /* Sets the MAC aging timeout for the OFPP_NORMAL action to 'idle_time', in
+ * seconds, and the maximum number of MAC table entries to
+ * 'max_entries'.
+ *
+ * An implementation that doesn't support configuring these features may
+ * set this function to NULL or implement it as a no-op. */
+ void (*set_mac_table_config)(struct ofproto *ofproto,
+ unsigned int idle_time, size_t max_entries);
/* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
*
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 1130eb748..504b422b2 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -585,12 +585,15 @@ ofproto_set_forward_bpdu(struct ofproto *ofproto, bool forward_bpdu)
}
/* Sets the MAC aging timeout for the OFPP_NORMAL action on 'ofproto' to
- * 'idle_time', in seconds. */
+ * 'idle_time', in seconds, and the maximum number of MAC table entries to
+ * 'max_entries'. */
void
-ofproto_set_mac_idle_time(struct ofproto *ofproto, unsigned idle_time)
+ofproto_set_mac_table_config(struct ofproto *ofproto, unsigned idle_time,
+ size_t max_entries)
{
- if (ofproto->ofproto_class->set_mac_idle_time) {
- ofproto->ofproto_class->set_mac_idle_time(ofproto, idle_time);
+ if (ofproto->ofproto_class->set_mac_table_config) {
+ ofproto->ofproto_class->set_mac_table_config(ofproto, idle_time,
+ max_entries);
}
}
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index e4abe523d..dc5d9ce96 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -229,7 +229,8 @@ void ofproto_set_extra_in_band_remotes(struct ofproto *,
void ofproto_set_in_band_queue(struct ofproto *, int queue_id);
void ofproto_set_flow_eviction_threshold(struct ofproto *, unsigned threshold);
void ofproto_set_forward_bpdu(struct ofproto *, bool forward_bpdu);
-void ofproto_set_mac_idle_time(struct ofproto *, unsigned idle_time);
+void ofproto_set_mac_table_config(struct ofproto *, unsigned idle_time,
+ size_t max_entries);
void ofproto_set_desc(struct ofproto *,
const char *mfr_desc, const char *hw_desc,
const char *sw_desc, const char *serial_desc,
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 6693f1baf..6a4dc2331 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -930,6 +930,68 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br1 | sed 's/[[0-9]]\{1,\}$/?/'], [0], [d
OVS_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([ofproto-dpif - MAC table overflow])
+OVS_VSWITCHD_START(
+ [set bridge br0 fail-mode=standalone other-config:mac-table-size=10 -- \
+ add-port br0 p1 -- set Interface p1 type=dummy -- \
+ add-port br0 p2 -- set Interface p2 type=dummy -- \
+ add-port br0 p3 -- set Interface p3 type=dummy])
+
+arp='eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)'
+
+AT_CHECK([ovs-appctl time/stop])
+
+# Trace 10 ARP packets arriving on p3, to create MAC learning entries.
+for i in 0 1 2 3 4 5 6 7 8 9; do
+ OFPROTO_TRACE(
+ [br0],
+ [in_port(3),eth(src=50:54:00:00:00:0$i,dst=ff:ff:ff:ff:ff:ff),$arp],
+ [-generate],
+ [1,2,100])
+ ovs-appctl time/warp 1000
+done
+
+# Check for the MAC learning entries.
+AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ *[[0-9]]\{1,\}$//' | sort],
+ [0], [dnl
+ 3 0 50:54:00:00:00:00
+ 3 0 50:54:00:00:00:01
+ 3 0 50:54:00:00:00:02
+ 3 0 50:54:00:00:00:03
+ 3 0 50:54:00:00:00:04
+ 3 0 50:54:00:00:00:05
+ 3 0 50:54:00:00:00:06
+ 3 0 50:54:00:00:00:07
+ 3 0 50:54:00:00:00:08
+ 3 0 50:54:00:00:00:09
+ port VLAN MAC Age
+])
+
+# Trace another ARP packet on another MAC.
+OFPROTO_TRACE(
+ [br0],
+ [in_port(3),eth(src=50:54:00:00:00:10,dst=ff:ff:ff:ff:ff:ff),$arp],
+ [-generate],
+ [1,2,100])
+
+# Check that the new one chased the oldest one out of the table.
+AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/' | sort],
+ [0], [dnl
+ 3 0 50:54:00:00:00:01 ?
+ 3 0 50:54:00:00:00:02 ?
+ 3 0 50:54:00:00:00:03 ?
+ 3 0 50:54:00:00:00:04 ?
+ 3 0 50:54:00:00:00:05 ?
+ 3 0 50:54:00:00:00:06 ?
+ 3 0 50:54:00:00:00:07 ?
+ 3 0 50:54:00:00:00:08 ?
+ 3 0 50:54:00:00:00:09 ?
+ 3 0 50:54:00:00:00:10 ?
+ port VLAN MAC Age
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
dnl Test that basic NetFlow reports flow statistics correctly:
dnl - The initial packet of a flow are correctly accounted.
dnl - Later packets within a flow are correctly accounted.
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index b1d2feb05..53dd2f659 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -182,7 +182,7 @@ static void bridge_configure_datapath_id(struct bridge *);
static void bridge_configure_flow_eviction_threshold(struct bridge *);
static void bridge_configure_netflow(struct bridge *);
static void bridge_configure_forward_bpdu(struct bridge *);
-static void bridge_configure_mac_idle_time(struct bridge *);
+static void bridge_configure_mac_table(struct bridge *);
static void bridge_configure_sflow(struct bridge *, int *sflow_bridge_number);
static void bridge_configure_stp(struct bridge *);
static void bridge_configure_tables(struct bridge *);
@@ -591,7 +591,7 @@ bridge_reconfigure_continue(const struct ovsrec_open_vswitch *ovs_cfg)
bridge_configure_mirrors(br);
bridge_configure_flow_eviction_threshold(br);
bridge_configure_forward_bpdu(br);
- bridge_configure_mac_idle_time(br);
+ bridge_configure_mac_table(br);
bridge_configure_remotes(br, managers, n_managers);
bridge_configure_netflow(br);
bridge_configure_sflow(br, &sflow_bridge_number);
@@ -1493,18 +1493,27 @@ bridge_configure_forward_bpdu(struct bridge *br)
false));
}
-/* Set MAC aging time for 'br'. */
+/* Set MAC learning table configuration for 'br'. */
static void
-bridge_configure_mac_idle_time(struct bridge *br)
+bridge_configure_mac_table(struct bridge *br)
{
const char *idle_time_str;
int idle_time;
+ const char *mac_table_size_str;
+ int mac_table_size;
+
idle_time_str = smap_get(&br->cfg->other_config, "mac-aging-time");
idle_time = (idle_time_str && atoi(idle_time_str)
? atoi(idle_time_str)
: MAC_ENTRY_DEFAULT_IDLE_TIME);
- ofproto_set_mac_idle_time(br->ofproto, idle_time);
+
+ mac_table_size_str = smap_get(&br->cfg->other_config, "mac-table-size");
+ mac_table_size = (mac_table_size_str && atoi(mac_table_size_str)
+ ? atoi(mac_table_size_str)
+ : MAC_DEFAULT_MAX);
+
+ ofproto_set_mac_table_config(br->ofproto, idle_time, mac_table_size);
}
static void
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index 31c15b41e..c78899f7e 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -623,6 +623,15 @@
transmit packets.
</p>
</column>
+
+ <column name="other_config" key="mac-table-size"
+ type='{"type": "integer", "minInteger": 1}'>
+ <p>
+ The maximum number of MAC addresses to learn. The default is
+ currently 2048. The value, if specified, is forced into a reasonable
+ range, currently 10 to 1,000,000.
+ </p>
+ </column>
</group>
<group title="Bridge Status">