+/* Updates multicast snooping table 'ms' given that a packet matching 'flow'
+ * was received on 'in_xbundle' in 'vlan' and is either Report or Query. */
+static void
+update_mcast_snooping_table4__(const struct xbridge *xbridge,
+ const struct flow *flow,
+ struct mcast_snooping *ms, int vlan,
+ struct xbundle *in_xbundle,
+ const struct dp_packet *packet)
+ OVS_REQ_WRLOCK(ms->rwlock)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 30);
+ int count;
+ ovs_be32 ip4 = flow->igmp_group_ip4;
+
+ switch (ntohs(flow->tp_src)) {
+ case IGMP_HOST_MEMBERSHIP_REPORT:
+ case IGMPV2_HOST_MEMBERSHIP_REPORT:
+ if (mcast_snooping_add_group4(ms, ip4, 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(ip4), in_xbundle->name, vlan);
+ }
+ break;
+ case IGMP_HOST_LEAVE_MESSAGE:
+ if (mcast_snooping_leave_group4(ms, ip4, 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(ip4), in_xbundle->name, vlan);
+ }
+ break;
+ case IGMP_HOST_MEMBERSHIP_QUERY:
+ if (flow->nw_src && mcast_snooping_add_mrouter(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;
+ case IGMPV3_HOST_MEMBERSHIP_REPORT:
+ if ((count = mcast_snooping_add_report(ms, packet, vlan,
+ in_xbundle->ofbundle))) {
+ VLOG_DBG_RL(&rl, "bridge %s: multicast snooping processed %d "
+ "addresses on port %s in VLAN %d",
+ xbridge->name, count, in_xbundle->name, vlan);
+ }
+ break;
+ }
+}
+
+static void
+update_mcast_snooping_table6__(const struct xbridge *xbridge,
+ const struct flow *flow,
+ struct mcast_snooping *ms, int vlan,
+ struct xbundle *in_xbundle,
+ const struct dp_packet *packet)
+ OVS_REQ_WRLOCK(ms->rwlock)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 30);
+ int count;
+
+ switch (ntohs(flow->tp_src)) {
+ case MLD_QUERY:
+ if (!ipv6_addr_equals(&flow->ipv6_src, &in6addr_any)
+ && mcast_snooping_add_mrouter(ms, vlan, in_xbundle->ofbundle)) {
+ VLOG_DBG_RL(&rl, "bridge %s: multicast snooping query on port %s"
+ "in VLAN %d",
+ xbridge->name, in_xbundle->name, vlan);
+ }
+ break;
+ case MLD_REPORT:
+ case MLD_DONE:
+ case MLD2_REPORT:
+ count = mcast_snooping_add_mld(ms, packet, vlan, in_xbundle->ofbundle);
+ if (count) {
+ VLOG_DBG_RL(&rl, "bridge %s: multicast snooping processed %d "
+ "addresses on port %s in VLAN %d",
+ xbridge->name, count, 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,
+ const struct dp_packet *packet)
+{
+ struct mcast_snooping *ms = xbridge->ms;
+ struct xlate_cfg *xcfg;
+ struct xbundle *mcast_xbundle;
+ struct mcast_port_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(&ms->rwlock);
+ xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
+ LIST_FOR_EACH(fport, node, &ms->fport_list) {
+ mcast_xbundle = xbundle_lookup(xcfg, fport->port);
+ if (mcast_xbundle == in_xbundle) {
+ break;
+ }
+ }
+
+ if (!mcast_xbundle || mcast_xbundle != in_xbundle) {
+ if (flow->dl_type == htons(ETH_TYPE_IP)) {
+ update_mcast_snooping_table4__(xbridge, flow, ms, vlan,
+ in_xbundle, packet);
+ } else {
+ update_mcast_snooping_table6__(xbridge, flow, ms, vlan,
+ in_xbundle, packet);
+ }
+ }
+ ovs_rwlock_unlock(&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_snooping *ms OVS_UNUSED,
+ struct mcast_group *grp,
+ struct xbundle *in_xbundle, uint16_t vlan)
+ OVS_REQ_RDLOCK(ms->rwlock)
+{
+ struct xlate_cfg *xcfg;
+ struct mcast_group_bundle *b;
+ struct xbundle *mcast_xbundle;
+
+ xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
+ LIST_FOR_EACH(b, bundle_node, &grp->bundle_lru) {
+ mcast_xbundle = xbundle_lookup(xcfg, b->port);
+ 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 xlate_cfg *xcfg;
+ struct mcast_mrouter_bundle *mrouter;
+ struct xbundle *mcast_xbundle;
+
+ xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
+ LIST_FOR_EACH(mrouter, mrouter_node, &ms->mrouter_lru) {
+ mcast_xbundle = xbundle_lookup(xcfg, mrouter->port);
+ 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 xlate_cfg *xcfg;
+ struct mcast_port_bundle *fport;
+ struct xbundle *mcast_xbundle;
+
+ xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
+ LIST_FOR_EACH(fport, node, &ms->fport_list) {
+ mcast_xbundle = xbundle_lookup(xcfg, fport->port);
+ 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");
+ }
+ }
+}
+
+/* forward the Reports to configured ports */
+static void
+xlate_normal_mcast_send_rports(struct xlate_ctx *ctx,
+ struct mcast_snooping *ms,
+ struct xbundle *in_xbundle, uint16_t vlan)
+ OVS_REQ_RDLOCK(ms->rwlock)
+{
+ struct xlate_cfg *xcfg;
+ struct mcast_port_bundle *rport;
+ struct xbundle *mcast_xbundle;
+
+ xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
+ LIST_FOR_EACH(rport, node, &ms->rport_list) {
+ mcast_xbundle = xbundle_lookup(xcfg, rport->port);
+ if (mcast_xbundle && mcast_xbundle != in_xbundle) {
+ xlate_report(ctx, "forwarding Report to mcast flagged port");
+ output_normal(ctx, mcast_xbundle, vlan);
+ } else if (!mcast_xbundle) {
+ xlate_report(ctx, "mcast port is unknown, dropping the Report");
+ } else {
+ xlate_report(ctx, "mcast port is input port, dropping the Report");
+ }
+ }
+}
+