[ovs-dev] [PATCH v2 6/7] dpif-xlate: Snoop multicast packets and send them properly

Flavio Leitner fbl at redhat.com
Thu May 29 01:10:33 UTC 2014


If the packet is multicast and the snooping feature is enabled,
update the multicast snooping database accordingly and send it
to the right ports.

If the packet is not multicast or the snooping feature is disabled,
let the MAC learning handle the packet as before.

Signed-off-by: Flavio Leitner <fbl at redhat.com>
---
 ofproto/ofproto-dpif-xlate.c | 234 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 219 insertions(+), 15 deletions(-)

diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index f9b54f2..da86739 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -1493,6 +1493,151 @@ update_learning_table(const struct xbridge *xbridge,
     }
 }
 
+/* Updates multicast snooping table 'ms' given that a packet matching 'flow'
+ * was received on 'in_xbundle' in 'vlan' and is Report or Query. */
+static void
+update_mcast_snooping_table__(const struct xbridge *xbridge,
+                              const struct flow *flow,
+                              const struct mcast_ip *grp, int vlan,
+                              struct xbundle *in_xbundle)
+    OVS_REQ_WRLOCK(xbridge->ms->rwlock)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 30);
+
+    switch (ntohs(flow->tp_src)) {
+    case IGMP_HOST_MEMBERSHIP_REPORT:
+    case IGMPV2_HOST_MEMBERSHIP_REPORT:
+        if (mcast_snooping_add_group(xbridge->ms, grp, vlan,
+            in_xbundle->ofbundle)) {
+            VLOG_DBG_RL(&rl, "bridge %s: multicast snooping learned that "
+                        IP_FMT" is on port %s in VLAN %d",
+                        xbridge->name, IP_ARGS(grp->ip4),
+                        in_xbundle->name, vlan);
+        }
+        break;
+    case IGMP_HOST_LEAVE_MESSAGE:
+        if (mcast_snooping_leave_group(xbridge->ms, grp, vlan,
+            in_xbundle->ofbundle)) {
+            VLOG_DBG_RL(&rl, "bridge %s: multicast snooping leaving "
+                        IP_FMT" is on port %s in VLAN %d",
+                        xbridge->name, IP_ARGS(grp->ip4),
+                        in_xbundle->name, vlan);
+        }
+        break;
+    case IGMP_HOST_MEMBERSHIP_QUERY:
+        if (flow->nw_src && mcast_snooping_add_mrouter(xbridge->ms, vlan,
+            in_xbundle->ofbundle)) {
+            VLOG_DBG_RL(&rl, "bridge %s: multicast snooping query from "
+                        IP_FMT" is on port %s in VLAN %d",
+                        xbridge->name, IP_ARGS(flow->nw_src),
+                        in_xbundle->name, vlan);
+        }
+        break;
+    }
+}
+
+/* Updates multicast snooping table 'ms' given that a packet matching 'flow'
+ * was received on 'in_xbundle' in 'vlan'. */
+static void
+update_mcast_snooping_table(const struct xbridge *xbridge,
+                            const struct flow *flow, int vlan,
+                            struct xbundle *in_xbundle)
+{
+    struct mcast_ip maddr;
+    struct xbundle *mcast_xbundle;
+    struct mcast_fport_bundle *fport;
+
+    /* Don't learn the OFPP_NONE port. */
+    if (in_xbundle == &ofpp_none_bundle) {
+        return;
+    }
+
+    /* Don't learn from flood ports */
+    mcast_xbundle = NULL;
+    ovs_rwlock_wrlock(&xbridge->ms->rwlock);
+    LIST_FOR_EACH(fport, list_node, &xbridge->ms->fport_list) {
+        mcast_xbundle = xbundle_lookup(fport->port.p);
+        if (mcast_xbundle == in_xbundle) {
+            break;
+        }
+    }
+
+    if (!mcast_xbundle || mcast_xbundle != in_xbundle) {
+        maddr.proto = flow->dl_type;
+        maddr.ip4 = flow->igmp_group_ip4;
+        update_mcast_snooping_table__(xbridge, flow, &maddr, vlan, in_xbundle);
+    }
+    ovs_rwlock_unlock(&xbridge->ms->rwlock);
+}
+
+/* send the packet to ports having the multicast group learned */
+static void
+xlate_normal_mcast_send_group(struct xlate_ctx *ctx, struct mcast_entry *mcast,
+                              struct xbundle *in_xbundle, uint16_t vlan)
+    OVS_REQ_RDLOCK(ctx->xbridge->ms->rwlock)
+{
+    struct mcast_entry_bundle *b;
+    struct xbundle *mcast_xbundle;
+
+    LIST_FOR_EACH(b, bundle_node, &mcast->lru_bundles) {
+        mcast_xbundle = xbundle_lookup(b->port.p);
+        if (mcast_xbundle && mcast_xbundle != in_xbundle) {
+            xlate_report(ctx, "forwarding to mcast group port");
+            output_normal(ctx, mcast_xbundle, vlan);
+        } else if (!mcast_xbundle) {
+            xlate_report(ctx, "mcast group port is unknown, dropping");
+        } else {
+            xlate_report(ctx, "mcast group port is input port, dropping");
+        }
+    }
+}
+
+/* send the packet to ports connected to multicast routers */
+static void
+xlate_normal_mcast_send_mrouters(struct xlate_ctx *ctx,
+                                 struct mcast_snooping *ms,
+                                 struct xbundle *in_xbundle, uint16_t vlan)
+    OVS_REQ_RDLOCK(ms->rwlock)
+{
+    struct mcast_mrouter_bundle *mrouter;
+    struct xbundle *mcast_xbundle;
+
+    LIST_FOR_EACH(mrouter, lru_node, &ms->mrouter_lru) {
+        mcast_xbundle = xbundle_lookup(mrouter->port.p);
+        if (mcast_xbundle && mcast_xbundle != in_xbundle) {
+            xlate_report(ctx, "forwarding to mcast router port");
+            output_normal(ctx, mcast_xbundle, vlan);
+        } else if (!mcast_xbundle) {
+            xlate_report(ctx, "mcast router port is unknown, dropping");
+        } else {
+            xlate_report(ctx, "mcast router port is input port, dropping");
+        }
+    }
+}
+
+/* send the packet to ports flagged to be flooded */
+static void
+xlate_normal_mcast_send_fports(struct xlate_ctx *ctx,
+                               struct mcast_snooping *ms,
+                               struct xbundle *in_xbundle, uint16_t vlan)
+    OVS_REQ_RDLOCK(ms->rwlock)
+{
+    struct mcast_fport_bundle *fport;
+    struct xbundle *mcast_xbundle;
+
+    LIST_FOR_EACH(fport, list_node, &ms->fport_list) {
+        mcast_xbundle = xbundle_lookup(fport->port.p);
+        if (mcast_xbundle && mcast_xbundle != in_xbundle) {
+            xlate_report(ctx, "forwarding to mcast flood port");
+            output_normal(ctx, mcast_xbundle, vlan);
+        } else if (!mcast_xbundle) {
+            xlate_report(ctx, "mcast flood port is unknown, dropping");
+        } else {
+            xlate_report(ctx, "mcast flood port is input port, dropping");
+        }
+    }
+}
+
 static void
 xlate_normal_flood(struct xlate_ctx *ctx, struct xbundle *in_xbundle,
                    uint16_t vlan)
@@ -1588,24 +1733,83 @@ xlate_normal(struct xlate_ctx *ctx)
     }
 
     /* Determine output bundle. */
-    ovs_rwlock_rdlock(&ctx->xbridge->ml->rwlock);
-    mac = mac_learning_lookup(ctx->xbridge->ml, flow->dl_dst, vlan);
-    mac_port = mac ? mac->port.p : NULL;
-    ovs_rwlock_unlock(&ctx->xbridge->ml->rwlock);
-
-    if (mac_port) {
-        struct xbundle *mac_xbundle = xbundle_lookup(mac_port);
-        if (mac_xbundle && mac_xbundle != in_xbundle) {
-            xlate_report(ctx, "forwarding to learned port");
-            output_normal(ctx, mac_xbundle, vlan);
-        } else if (!mac_xbundle) {
-            xlate_report(ctx, "learned port is unknown, dropping");
+    if (mcast_snooping_enabled(ctx->xbridge->ms)
+        &&!eth_addr_is_broadcast(flow->dl_dst)
+        && eth_addr_is_multicast(flow->dl_dst)
+        && flow->dl_type == htons(ETH_TYPE_IP)) {
+        struct mcast_entry *mcast;
+
+        if (flow->nw_proto == IPPROTO_IGMP) {
+            if (ctx->xin->may_learn) {
+                if (mcast_snooping_is_membership(flow->tp_src) ||
+                    mcast_snooping_is_query(flow->tp_src)) {
+                    update_mcast_snooping_table(ctx->xbridge, flow, vlan,
+                                                in_xbundle);
+                    }
+            }
+
+            if (mcast_snooping_is_membership(flow->tp_src)) {
+                ovs_rwlock_rdlock(&ctx->xbridge->ms->rwlock);
+                xlate_normal_mcast_send_mrouters(ctx, ctx->xbridge->ms,
+                                                 in_xbundle, vlan);
+                ovs_rwlock_unlock(&ctx->xbridge->ms->rwlock);
+            } else {
+                xlate_report(ctx, "multicast traffic, flooding");
+                xlate_normal_flood(ctx, in_xbundle, vlan);
+            }
+            return;
         } else {
-            xlate_report(ctx, "learned port is input port, dropping");
+            if (ip_is_local_multicast(flow->nw_dst)) {
+                /* RFC4541: section 2.1.2, item 2: Packets with a dst IP
+                 * address in the 224.0.0.x range which are not IGMP must
+                 * be forwarded on all ports */
+                xlate_report(ctx, "RFC4541: section 2.1.2, item 2, flooding");
+                xlate_normal_flood(ctx, in_xbundle, vlan);
+                return;
+            }
         }
+
+        /* forwarding to group base ports */
+        ovs_rwlock_rdlock(&ctx->xbridge->ms->rwlock);
+        mcast = mcast_snooping_lookup4(ctx->xbridge->ms, flow->nw_dst, vlan);
+        if (mcast) {
+            xlate_normal_mcast_send_group(ctx, mcast, in_xbundle, vlan);
+            xlate_normal_mcast_send_fports(ctx, ctx->xbridge->ms, in_xbundle,
+                                           vlan);
+            xlate_normal_mcast_send_mrouters(ctx, ctx->xbridge->ms, in_xbundle,
+                                             vlan);
+        } else {
+            if (mcast_snooping_flood_unreg(ctx->xbridge->ms)) {
+                xlate_report(ctx, "unregistered multicast, flooding");
+                xlate_normal_flood(ctx, in_xbundle, vlan);
+            } else {
+                xlate_normal_mcast_send_mrouters(ctx, ctx->xbridge->ms,
+                                                 in_xbundle, vlan);
+                xlate_normal_mcast_send_fports(ctx, ctx->xbridge->ms,
+                                               in_xbundle, vlan);
+            }
+        }
+        ovs_rwlock_unlock(&ctx->xbridge->ms->rwlock);
     } else {
-        xlate_report(ctx, "no learned MAC for destination, flooding");
-        xlate_normal_flood(ctx, in_xbundle, vlan);
+        ovs_rwlock_rdlock(&ctx->xbridge->ml->rwlock);
+        mac = mac_learning_lookup(ctx->xbridge->ml, flow->dl_dst, vlan);
+        mac_port = mac ? mac->port.p : NULL;
+        ovs_rwlock_unlock(&ctx->xbridge->ml->rwlock);
+
+        if (mac_port) {
+            struct xbundle *mac_xbundle = xbundle_lookup(mac_port);
+            if (mac_xbundle && mac_xbundle != in_xbundle) {
+                xlate_report(ctx, "forwarding to learned port");
+                output_normal(ctx, mac_xbundle, vlan);
+            } else if (!mac_xbundle) {
+                xlate_report(ctx, "learned port is unknown, dropping");
+            } else {
+                xlate_report(ctx, "learned port is input port, dropping");
+            }
+        } else {
+            xlate_normal_flood(ctx, in_xbundle, vlan);
+            xlate_report(ctx, "no learned MAC for destination, flooding");
+        }
     }
 }
 
-- 
1.9.3




More information about the dev mailing list