Add support for VLAN tags to the MAC learning library.
authorBen Pfaff <blp@nicira.com>
Thu, 18 Dec 2008 22:00:23 +0000 (14:00 -0800)
committerBen Pfaff <blp@nicira.com>
Thu, 18 Dec 2008 22:00:31 +0000 (14:00 -0800)
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.

lib/learning-switch.c
lib/mac-learning.c
lib/mac-learning.h
secchan/in-band.c

index 76636a7bf08fa6184da15242322ecc61ad00d91d..ff03ab59c5e735d7cf3c7578f75b792209122ed4 100644 (file)
@@ -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;
         }
index b924e496d78cebef20fed0de1a82a43aabb01557..c8c241e0046101348e1affe1aef66c950c4537b4 100644 (file)
@@ -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;
         }
index 3c1b3dc3c801c8685434612055d73cbffeef74b1..7dc12be35078a5ed8b4b1dca74596e2588fa94cb 100644 (file)
@@ -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 */
index f9773a793c53dc63a27f72b445f475162b4588a0..e2b92bb0caccca30e659aadd6c10cae3084dcde8 100644 (file)
@@ -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 {