+/* 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)
+{
+ return !ml->flood_vlans || !bitmap_is_set(ml->flood_vlans, vlan);
+}
+
+/* Returns true if 'src_mac' may be learned on 'vlan' for 'ml'.
+ * Returns false if 'ml' is NULL, if src_mac is not valid for learning, or if
+ * 'vlan' is configured on 'ml' to flood all packets. */
+bool
+mac_learning_may_learn(const struct mac_learning *ml,
+ const uint8_t src_mac[ETH_ADDR_LEN], uint16_t vlan)
+{
+ return ml && is_learning_vlan(ml, vlan) && !eth_addr_is_multicast(src_mac);
+}
+
+/* Searches 'ml' for and returns a MAC learning entry for 'src_mac' in 'vlan',
+ * inserting a new entry if necessary. The caller must have already verified,
+ * by calling mac_learning_may_learn(), that 'src_mac' and 'vlan' are
+ * learnable.
+ *
+ * If the returned MAC entry is new (as may be determined by calling
+ * mac_entry_is_new()), then the caller must pass the new entry to
+ * mac_learning_changed(). The caller must also initialize the new entry's
+ * 'port' member. Otherwise calling those functions is at the caller's
+ * discretion. */
+struct mac_entry *
+mac_learning_insert(struct mac_learning *ml,
+ const uint8_t src_mac[ETH_ADDR_LEN], uint16_t vlan)
+{
+ struct mac_entry *e;
+
+ e = mac_entry_lookup(ml, src_mac, vlan);