flow_extract(&pkt, in_port, &flow);
if (may_learn(sw, in_port) && sw->ml) {
- if (mac_learning_learn(sw->ml, flow.dl_src, in_port)) {
+ if (mac_learning_learn(sw->ml, flow.dl_src, 0, in_port)) {
VLOG_DBG_RL(&rl, "%012llx: learned that "ETH_ADDR_FMT" is on "
"port %"PRIu16, sw->datapath_id,
ETH_ADDR_ARGS(flow.dl_src), in_port);
}
if (sw->ml) {
- uint16_t learned_port = mac_learning_lookup(sw->ml, flow.dl_dst);
+ uint16_t learned_port = mac_learning_lookup(sw->ml, flow.dl_dst, 0);
if (may_send(sw, learned_port)) {
out_port = learned_port;
}
struct list lru_node; /* Element in mac_learning 'lrus' list. */
time_t used; /* Last used 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. */
};
static struct list *
mac_table_bucket(const struct mac_learning *ml,
- const uint8_t mac[ETH_ADDR_LEN])
+ const uint8_t mac[ETH_ADDR_LEN],
+ uint16_t vlan)
{
- uint32_t hash = hash_fnv(mac, ETH_ADDR_LEN, HASH_FNV_BASIS);
+ uint32_t hash = hash_fnv(mac, ETH_ADDR_LEN, HASH_FNV_BASIS) ^ vlan;
const struct list *list = &ml->table[hash & MAC_HASH_BITS];
return (struct list *) list;
}
static struct mac_entry *
-search_bucket(struct list *bucket, const uint8_t mac[ETH_ADDR_LEN])
+search_bucket(struct list *bucket, const uint8_t mac[ETH_ADDR_LEN],
+ uint16_t vlan)
{
struct mac_entry *e;
LIST_FOR_EACH (e, struct mac_entry, hash_node, bucket) {
- if (eth_addr_equals(e->mac, mac)) {
+ if (eth_addr_equals(e->mac, mac) && e->vlan == vlan) {
return e;
}
}
}
/* Attempts to make 'ml' learn from the fact that a frame from 'src_mac' was
- * just observed arriving on 'src_port'. Returns true if we actually learned
- * something from this, false if it just confirms what we already knew. */
+ * just observed arriving from 'src_port' on the given 'vlan'. Returns true if
+ * we actually learned something from this, false if it just confirms what we
+ * already knew.
+ *
+ * The 'vlan' parameter is used to maintain separate per-VLAN learning tables.
+ * Specify 0 if this behavior is undesirable. */
bool
mac_learning_learn(struct mac_learning *ml,
- const uint8_t src_mac[ETH_ADDR_LEN], uint16_t src_port)
+ const uint8_t src_mac[ETH_ADDR_LEN], uint16_t vlan,
+ uint16_t src_port)
{
struct mac_entry *e;
struct list *bucket;
return false;
}
- bucket = mac_table_bucket(ml, src_mac);
- e = search_bucket(bucket, src_mac);
+ bucket = mac_table_bucket(ml, src_mac, vlan);
+ e = search_bucket(bucket, src_mac, vlan);
if (!e) {
e = CONTAINER_OF(ml->lrus.next, struct mac_entry, lru_node);
memcpy(e->mac, src_mac, ETH_ADDR_LEN);
}
list_push_front(bucket, &e->hash_node);
e->port = -1;
+ e->vlan = vlan;
}
/* Make the entry most-recently-used. */
return false;
}
-/* Looks up address 'dst' in 'ml'. Returns the port on which a frame destined
- * for 'dst' should be sent, OFPP_FLOOD if unknown. */
+/* Looks up MAC 'dst' for VLAN 'vlan' in 'ml'. Returns the port on which a
+ * frame destined for 'dst' should be sent, OFPP_FLOOD if unknown. */
uint16_t
mac_learning_lookup(const struct mac_learning *ml,
- const uint8_t dst[ETH_ADDR_LEN])
+ const uint8_t dst[ETH_ADDR_LEN],
+ uint16_t vlan)
{
if (!eth_addr_is_multicast(dst)) {
- struct mac_entry *e = search_bucket(mac_table_bucket(ml, dst), dst);
+ struct mac_entry *e = search_bucket(mac_table_bucket(ml, dst, vlan),
+ dst, vlan);
if (e && time_now() - e->used < 60) {
return e->port;
}
in_band_learn_mac(struct in_band_data *in_band,
uint16_t in_port, const uint8_t src_mac[ETH_ADDR_LEN])
{
- if (mac_learning_learn(in_band->ml, src_mac, in_port)) {
+ if (mac_learning_learn(in_band->ml, src_mac, 0, in_port)) {
VLOG_DBG_RL(&rl, "learned that "ETH_ADDR_FMT" is on port %"PRIu16,
ETH_ADDR_ARGS(src_mac), in_port);
}
/* Deal with local stuff. */
if (in_port == OFPP_LOCAL) {
/* Sent by secure channel. */
- out_port = mac_learning_lookup(in_band->ml, eth->eth_dst);
+ out_port = mac_learning_lookup(in_band->ml, eth->eth_dst, 0);
} else if (eth_addr_equals(eth->eth_dst,
netdev_get_etheraddr(in_band->of_device))) {
/* Sent to secure channel. */
|| is_controller_mac(eth->eth_src, in_band)) {
/* Traffic to or from controller. Switch it by hand. */
in_band_learn_mac(in_band, in_port, eth->eth_src);
- out_port = mac_learning_lookup(in_band->ml, eth->eth_dst);
+ out_port = mac_learning_lookup(in_band->ml, eth->eth_dst, 0);
} else {
const uint8_t *controller_mac;
controller_mac = get_controller_mac(in_band);
out_port = OFPP_FLOOD;
} else if (is_controller_mac(eth->eth_dst, in_band)
&& in_port == mac_learning_lookup(in_band->ml,
- controller_mac)) {
+ controller_mac, 0)) {
/* Drop controller traffic that arrives on the controller port. */
out_port = -1;
} else {