From: Ben Pfaff Date: Thu, 18 Dec 2008 22:00:23 +0000 (-0800) Subject: Add support for VLAN tags to the MAC learning library. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=30a71e8f5cd077c69f559a7160e324679035db29;p=openvswitch Add support for VLAN tags to the MAC learning library. vswitchd needs to keep separate per-VLAN MAC learning tables, so this adds a VLAN tag to each MAC learning table entry. The existing users of the MAC learning table don't care about VLANs, so they always pass in a VLAN of 0. There is a very good chance that vswitchd will need additional features in its MAC learning table that don't fit well into the existing library. In that case this commit will probably be reverted and a separate MAC learning implementation added in the vswitch directory. --- diff --git a/lib/learning-switch.c b/lib/learning-switch.c index 76636a7b..ff03ab59 100644 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@ -409,7 +409,7 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_) 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); @@ -422,7 +422,7 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_) } 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; } diff --git a/lib/mac-learning.c b/lib/mac-learning.c index b924e496..c8c241e0 100644 --- a/lib/mac-learning.c +++ b/lib/mac-learning.c @@ -59,6 +59,7 @@ struct mac_entry { 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. */ }; @@ -72,19 +73,21 @@ struct mac_learning { 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; } } @@ -119,11 +122,16 @@ mac_learning_destroy(struct mac_learning *ml) } /* 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; @@ -135,8 +143,8 @@ mac_learning_learn(struct mac_learning *ml, 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); @@ -145,6 +153,7 @@ mac_learning_learn(struct mac_learning *ml, } list_push_front(bucket, &e->hash_node); e->port = -1; + e->vlan = vlan; } /* Make the entry most-recently-used. */ @@ -160,14 +169,16 @@ mac_learning_learn(struct mac_learning *ml, 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; } diff --git a/lib/mac-learning.h b/lib/mac-learning.h index 3c1b3dc3..7dc12be3 100644 --- a/lib/mac-learning.h +++ b/lib/mac-learning.h @@ -39,8 +39,9 @@ struct mac_learning *mac_learning_create(void); void mac_learning_destroy(struct mac_learning *); bool mac_learning_learn(struct mac_learning *, - const uint8_t src[ETH_ADDR_LEN], uint16_t src_port); + const uint8_t src[ETH_ADDR_LEN], uint16_t vlan, + uint16_t src_port); uint16_t mac_learning_lookup(const struct mac_learning *, - const uint8_t dst[ETH_ADDR_LEN]); + const uint8_t dst[ETH_ADDR_LEN], uint16_t vlan); #endif /* mac-learning.h */ diff --git a/secchan/in-band.c b/secchan/in-band.c index f9773a79..e2b92bb0 100644 --- a/secchan/in-band.c +++ b/secchan/in-band.c @@ -130,7 +130,7 @@ static void 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); } @@ -156,7 +156,7 @@ in_band_local_packet_cb(struct relay *r, void *in_band_) /* 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. */ @@ -171,7 +171,7 @@ in_band_local_packet_cb(struct relay *r, void *in_band_) || 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); @@ -182,7 +182,7 @@ in_band_local_packet_cb(struct relay *r, void *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 {