Avoid learning Link-Local reserved multicast addresses if advertised in
a MLD reports since this interferes with Slaac IPv6 address resolution
implemented in OVN.

Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2154930
Tested-by: Eduardo Olivares <eoliv...@redhat.com>
Signed-off-by: Lorenzo Bianconi <lorenzo.bianc...@redhat.com>
---
 lib/mcast-snooping.c | 69 ++++++++++++++++++++++++++++++++------------
 lib/packets.c        | 11 +++++++
 lib/packets.h        | 13 +++++++++
 3 files changed, 74 insertions(+), 19 deletions(-)

diff --git a/lib/mcast-snooping.c b/lib/mcast-snooping.c
index 029ca2855..a9d2e7900 100644
--- a/lib/mcast-snooping.c
+++ b/lib/mcast-snooping.c
@@ -38,6 +38,8 @@
 COVERAGE_DEFINE(mcast_snooping_learned);
 COVERAGE_DEFINE(mcast_snooping_expired);
 
+VLOG_DEFINE_THIS_MODULE(mcast_snooping);
+
 static struct mcast_port_bundle *
 mcast_snooping_port_lookup(struct ovs_list *list, void *port);
 static struct mcast_mrouter_bundle *
@@ -489,6 +491,28 @@ mcast_snooping_add_report(struct mcast_snooping *ms,
     return count;
 }
 
+static bool
+mcast_snooping_should_learn_mld_group(struct in6_addr *addr)
+{
+    /* we should learn multicast group from the following IPv6 multicast
+     * groups:
+     * - All Nodes Address       (ff02::1)
+     * - All Router Address      (ff02::2)
+     * - All Site Router Address (ff05::2)
+     * - Solicited-Node Address  (ff02::1:ff00:0000/104)
+     */
+    if (in6_addr_is_solicited_node(addr) || ipv6_is_all_hosts(addr) ||
+        ipv6_is_all_router(addr) || ipv6_is_all_site_router(addr)) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        char ip_str[INET6_ADDRSTRLEN + 1];
+
+        ipv6_string_mapped(ip_str, addr);
+        VLOG_DBG_RL(&rl, "Invalid Multicast group %s", ip_str);
+        return false;
+    }
+    return true;
+}
+
 int
 mcast_snooping_add_mld(struct mcast_snooping *ms,
                           const struct dp_packet *p,
@@ -531,26 +555,33 @@ mcast_snooping_add_mld(struct mcast_snooping *ms,
                 break;
             }
             /* Only consider known record types. */
-            if (record->type >= IGMPV3_MODE_IS_INCLUDE
-                && record->type <= IGMPV3_BLOCK_OLD_SOURCES) {
-                struct in6_addr maddr;
-                memcpy(maddr.s6_addr, record->maddr.be16, 16);
-                addr = &maddr;
-                /*
-                 * If record is INCLUDE MODE and there are no sources, it's
-                 * equivalent to a LEAVE.
-                 */
-                if (record->nsrcs == htons(0)
-                    && (record->type == IGMPV3_MODE_IS_INCLUDE
-                        || record->type == IGMPV3_CHANGE_TO_INCLUDE_MODE)) {
-                    ret = mcast_snooping_leave_group(ms, addr, vlan, port);
-                } else {
-                    ret = mcast_snooping_add_group(ms, addr, vlan, port);
-                }
-                if (ret) {
-                    count++;
-                }
+            if (record->type < IGMPV3_MODE_IS_INCLUDE ||
+                record->type > IGMPV3_BLOCK_OLD_SOURCES) {
+                goto next;
+            }
+
+            struct in6_addr maddr;
+            memcpy(maddr.s6_addr, record->maddr.be16, 16);
+            if (!mcast_snooping_should_learn_mld_group(&maddr)) {
+                goto next;
+            }
+
+            /*
+             * If record is INCLUDE MODE and there are no sources, it's
+             * equivalent to a LEAVE.
+             */
+            addr = &maddr;
+            if (record->nsrcs == htons(0)
+                && (record->type == IGMPV3_MODE_IS_INCLUDE
+                    || record->type == IGMPV3_CHANGE_TO_INCLUDE_MODE)) {
+                ret = mcast_snooping_leave_group(ms, addr, vlan, port);
+            } else {
+                ret = mcast_snooping_add_group(ms, addr, vlan, port);
+            }
+            if (ret) {
+                count++;
             }
+next:
             offset += sizeof(*record)
                       + ntohs(record->nsrcs) * sizeof(struct in6_addr)
                       + record->aux_len;
diff --git a/lib/packets.c b/lib/packets.c
index 06f516cb1..b43f78bc1 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -38,6 +38,7 @@
 const struct in6_addr in6addr_exact = IN6ADDR_EXACT_INIT;
 const struct in6_addr in6addr_all_hosts = IN6ADDR_ALL_HOSTS_INIT;
 const struct in6_addr in6addr_all_routers = IN6ADDR_ALL_ROUTERS_INIT;
+const struct in6_addr in6addr_all_site_routers = IN6ADDR_ALL_SITE_ROUTERS_INIT;
 
 struct in6_addr
 flow_tnl_dst(const struct flow_tnl *tnl)
@@ -605,6 +606,16 @@ in6_addr_solicited_node(struct in6_addr *addr, const 
struct in6_addr *ip6)
     memcpy(&addr->s6_addr[13], &ip6->s6_addr[13], 3);
 }
 
+bool
+in6_addr_is_solicited_node(struct in6_addr *addr)
+{
+    union ovs_16aligned_in6_addr *taddr =
+        (union ovs_16aligned_in6_addr *) addr;
+    return taddr->be16[0] ==  htons(0xff02) &&
+           taddr->be16[5] ==  htons(0x1) &&
+           taddr->be16[6] ==  htons(0xff00);
+}
+
 /*
  * Generates ipv6 EUI64 address from the given eth addr
  * and prefix and stores it in 'lla'
diff --git a/lib/packets.h b/lib/packets.h
index 8626aac8d..e88753456 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -1146,6 +1146,10 @@ extern const struct in6_addr in6addr_all_routers;
 #define IN6ADDR_ALL_ROUTERS_INIT { { { 
0xff,0x02,0x00,0x00,0x00,0x00,0x00,0x00, \
                                        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02 
} } }
 
+extern const struct in6_addr in6addr_all_site_routers;
+#define IN6ADDR_ALL_SITE_ROUTERS_INIT { { { 
0xff,0x05,0x00,0x00,0x00,0x00,0x00,0x00, \
+                                            
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02 } } }
+
 static inline bool ipv6_addr_equals(const struct in6_addr *a,
                                     const struct in6_addr *b)
 {
@@ -1169,6 +1173,14 @@ static inline bool ipv6_is_all_hosts(const struct 
in6_addr *addr) {
     return ipv6_addr_equals(addr, &in6addr_all_hosts);
 }
 
+static inline bool ipv6_is_all_router(const struct in6_addr *addr) {
+    return ipv6_addr_equals(addr, &in6addr_all_routers);
+}
+
+static inline bool ipv6_is_all_site_router(const struct in6_addr *addr) {
+    return ipv6_addr_equals(addr, &in6addr_all_site_routers);
+}
+
 static inline bool ipv6_addr_is_set(const struct in6_addr *addr) {
     return !ipv6_addr_equals(addr, &in6addr_any);
 }
@@ -1207,6 +1219,7 @@ in6_addr_get_mapped_ipv4(const struct in6_addr *addr)
 
 void in6_addr_solicited_node(struct in6_addr *addr,
                              const struct in6_addr *ip6);
+bool in6_addr_is_solicited_node(struct in6_addr *addr);
 
 void in6_generate_eui64(struct eth_addr ea, const struct in6_addr *prefix,
                         struct in6_addr *lla);
-- 
2.39.1

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to