We've had a couple of requests for this over the years. It's easy to do, so let's implement it.
Signed-off-by: Ben Pfaff <b...@nicira.com> --- NEWS | 1 + lib/mac-learning.c | 24 ++++++++++++++--- lib/mac-learning.h | 5 +++- ofproto/ofproto-dpif.c | 6 +++- ofproto/ofproto-provider.h | 11 +++++-- ofproto/ofproto.c | 11 +++++--- ofproto/ofproto.h | 3 +- tests/ofproto-dpif.at | 62 ++++++++++++++++++++++++++++++++++++++++++++ vswitchd/bridge.c | 19 ++++++++++--- vswitchd/vswitch.xml | 9 ++++++ 10 files changed, 131 insertions(+), 20 deletions(-) diff --git a/NEWS b/NEWS index 3cb1bd3..72c5b87 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,6 @@ post-v1.9.0 -------------------- + - The maximum size of the MAC learning table is now configurable. v1.9.0 - xx xxx xxxx diff --git a/lib/mac-learning.c b/lib/mac-learning.c index 3c541af..f609d48 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 8f8fd45..284e7f6 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 c800386..ffb500a 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -2422,10 +2422,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. */ @@ -7252,6 +7254,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 a62473b..8bfb2e8 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -1201,9 +1201,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 2fb2fc8..54722c8 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -546,12 +546,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 5599cd6..bd35e89 100644 --- a/ofproto/ofproto.h +++ b/ofproto/ofproto.h @@ -212,7 +212,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 bc2362d..24faed4 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -929,6 +929,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], + [0,1,2]) + 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], + [0,1,2]) + +# 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 a481f06..984c959 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -180,7 +180,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 *); @@ -543,7 +543,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); @@ -1432,18 +1432,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 e9ea0c4..0dd8d21 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -617,6 +617,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"> -- 1.7.2.5 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev