5f9769
From 137b049777cfc301eadba8a2c3b55764bde6f451 Mon Sep 17 00:00:00 2001
5f9769
Message-Id: <137b049777cfc301eadba8a2c3b55764bde6f451.1610458802.git.lorenzo.bianconi@redhat.com>
5f9769
In-Reply-To: <f21c1b7a467a691847b5552d4570af706fcc5bb0.1610458802.git.lorenzo.bianconi@redhat.com>
5f9769
References: <f21c1b7a467a691847b5552d4570af706fcc5bb0.1610458802.git.lorenzo.bianconi@redhat.com>
5f9769
From: Anton Ivanov <anton.ivanov@cambridgegreys.com>
5f9769
Date: Tue, 5 Jan 2021 17:49:34 +0000
5f9769
Subject: [PATCH 07/16] ovn-northd: Move destination handling into functions.
5f9769
5f9769
1. Move igmp/mld destination handling into a function.
5f9769
2. Move unicast destination handling into a function.
5f9769
5f9769
Signed-off-by: Anton Ivanov <anton.ivanov@cambridgegreys.com>
5f9769
Signed-off-by: Numan Siddique <numans@ovn.org>
5f9769
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
5f9769
---
5f9769
 northd/ovn-northd.c | 433 +++++++++++++++++++++++---------------------
5f9769
 1 file changed, 223 insertions(+), 210 deletions(-)
5f9769
5f9769
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
5f9769
index f4e248f55..27a788095 100644
5f9769
--- a/northd/ovn-northd.c
5f9769
+++ b/northd/ovn-northd.c
5f9769
@@ -6769,8 +6769,7 @@ is_vlan_transparent(const struct ovn_datapath *od)
5f9769
 
5f9769
 static void
5f9769
 build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
5f9769
-                    struct hmap *lflows, struct hmap *mcgroups,
5f9769
-                    struct hmap *igmp_groups)
5f9769
+                    struct hmap *lflows)
5f9769
 {
5f9769
     /* This flow table structure is documented in ovn-northd(8), so please
5f9769
      * update ovn-northd.8.xml if you change anything. */
5f9769
@@ -6778,212 +6777,6 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
5f9769
     struct ds match = DS_EMPTY_INITIALIZER;
5f9769
     struct ds actions = DS_EMPTY_INITIALIZER;
5f9769
     struct ovn_datapath *od;
5f9769
-    struct ovn_port *op;
5f9769
-
5f9769
-
5f9769
-    /* Ingress table 19: Add IP multicast flows learnt from IGMP/MLD
5f9769
-     * (priority 90). */
5f9769
-    struct ovn_igmp_group *igmp_group;
5f9769
-
5f9769
-    HMAP_FOR_EACH (igmp_group, hmap_node, igmp_groups) {
5f9769
-        if (!igmp_group->datapath) {
5f9769
-            continue;
5f9769
-        }
5f9769
-
5f9769
-        ds_clear(&match);
5f9769
-        ds_clear(&actions);
5f9769
-
5f9769
-        struct mcast_switch_info *mcast_sw_info =
5f9769
-            &igmp_group->datapath->mcast_info.sw;
5f9769
-
5f9769
-        if (IN6_IS_ADDR_V4MAPPED(&igmp_group->address)) {
5f9769
-            /* RFC 4541, section 2.1.2, item 2: Skip groups in the 224.0.0.X
5f9769
-             * range.
5f9769
-             */
5f9769
-            ovs_be32 group_address =
5f9769
-                in6_addr_get_mapped_ipv4(&igmp_group->address);
5f9769
-            if (ip_is_local_multicast(group_address)) {
5f9769
-                continue;
5f9769
-            }
5f9769
-
5f9769
-            if (mcast_sw_info->active_v4_flows >= mcast_sw_info->table_size) {
5f9769
-                continue;
5f9769
-            }
5f9769
-            mcast_sw_info->active_v4_flows++;
5f9769
-            ds_put_format(&match, "eth.mcast && ip4 && ip4.dst == %s ",
5f9769
-                          igmp_group->mcgroup.name);
5f9769
-        } else {
5f9769
-            /* RFC 4291, section 2.7.1: Skip groups that correspond to all
5f9769
-             * hosts.
5f9769
-             */
5f9769
-            if (ipv6_is_all_hosts(&igmp_group->address)) {
5f9769
-                continue;
5f9769
-            }
5f9769
-            if (mcast_sw_info->active_v6_flows >= mcast_sw_info->table_size) {
5f9769
-                continue;
5f9769
-            }
5f9769
-            mcast_sw_info->active_v6_flows++;
5f9769
-            ds_put_format(&match, "eth.mcast && ip6 && ip6.dst == %s ",
5f9769
-                          igmp_group->mcgroup.name);
5f9769
-        }
5f9769
-
5f9769
-        /* Also flood traffic to all multicast routers with relay enabled. */
5f9769
-        if (mcast_sw_info->flood_relay) {
5f9769
-            ds_put_cstr(&actions,
5f9769
-                        "clone { "
5f9769
-                            "outport = \""MC_MROUTER_FLOOD "\"; "
5f9769
-                            "output; "
5f9769
-                        "};");
5f9769
-        }
5f9769
-        if (mcast_sw_info->flood_static) {
5f9769
-            ds_put_cstr(&actions,
5f9769
-                        "clone { "
5f9769
-                            "outport =\""MC_STATIC"\"; "
5f9769
-                            "output; "
5f9769
-                        "};");
5f9769
-        }
5f9769
-        ds_put_format(&actions, "outport = \"%s\"; output; ",
5f9769
-                      igmp_group->mcgroup.name);
5f9769
-
5f9769
-        ovn_lflow_add_unique(lflows, igmp_group->datapath, S_SWITCH_IN_L2_LKUP,
5f9769
-                             90, ds_cstr(&match), ds_cstr(&actions));
5f9769
-    }
5f9769
-
5f9769
-    /* Ingress table 19: Destination lookup, unicast handling (priority 50), */
5f9769
-    HMAP_FOR_EACH (op, key_node, ports) {
5f9769
-        if (!op->nbsp || lsp_is_external(op->nbsp)) {
5f9769
-            continue;
5f9769
-        }
5f9769
-
5f9769
-        /* For ports connected to logical routers add flows to bypass the
5f9769
-         * broadcast flooding of ARP/ND requests in table 19. We direct the
5f9769
-         * requests only to the router port that owns the IP address.
5f9769
-         */
5f9769
-        if (lsp_is_router(op->nbsp)) {
5f9769
-            build_lswitch_rport_arp_req_flows(op->peer, op->od, op, lflows,
5f9769
-                                              &op->nbsp->header_);
5f9769
-        }
5f9769
-
5f9769
-        for (size_t i = 0; i < op->nbsp->n_addresses; i++) {
5f9769
-            /* Addresses are owned by the logical port.
5f9769
-             * Ethernet address followed by zero or more IPv4
5f9769
-             * or IPv6 addresses (or both). */
5f9769
-            struct eth_addr mac;
5f9769
-            if (ovs_scan(op->nbsp->addresses[i],
5f9769
-                        ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) {
5f9769
-                ds_clear(&match);
5f9769
-                ds_put_format(&match, "eth.dst == "ETH_ADDR_FMT,
5f9769
-                              ETH_ADDR_ARGS(mac));
5f9769
-
5f9769
-                ds_clear(&actions);
5f9769
-                ds_put_format(&actions, "outport = %s; output;", op->json_key);
5f9769
-                ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_L2_LKUP,
5f9769
-                                        50, ds_cstr(&match),
5f9769
-                                        ds_cstr(&actions),
5f9769
-                                        &op->nbsp->header_);
5f9769
-            } else if (!strcmp(op->nbsp->addresses[i], "unknown")) {
5f9769
-                if (lsp_is_enabled(op->nbsp)) {
5f9769
-                    ovn_multicast_add(mcgroups, &mc_unknown, op);
5f9769
-                    op->od->has_unknown = true;
5f9769
-                }
5f9769
-            } else if (is_dynamic_lsp_address(op->nbsp->addresses[i])) {
5f9769
-                if (!op->nbsp->dynamic_addresses
5f9769
-                    || !ovs_scan(op->nbsp->dynamic_addresses,
5f9769
-                            ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) {
5f9769
-                    continue;
5f9769
-                }
5f9769
-                ds_clear(&match);
5f9769
-                ds_put_format(&match, "eth.dst == "ETH_ADDR_FMT,
5f9769
-                              ETH_ADDR_ARGS(mac));
5f9769
-
5f9769
-                ds_clear(&actions);
5f9769
-                ds_put_format(&actions, "outport = %s; output;", op->json_key);
5f9769
-                ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_L2_LKUP,
5f9769
-                                        50, ds_cstr(&match),
5f9769
-                                        ds_cstr(&actions),
5f9769
-                                        &op->nbsp->header_);
5f9769
-            } else if (!strcmp(op->nbsp->addresses[i], "router")) {
5f9769
-                if (!op->peer || !op->peer->nbrp
5f9769
-                    || !ovs_scan(op->peer->nbrp->mac,
5f9769
-                            ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) {
5f9769
-                    continue;
5f9769
-                }
5f9769
-                ds_clear(&match);
5f9769
-                ds_put_format(&match, "eth.dst == "ETH_ADDR_FMT,
5f9769
-                              ETH_ADDR_ARGS(mac));
5f9769
-                if (op->peer->od->l3dgw_port
5f9769
-                    && op->peer->od->l3redirect_port
5f9769
-                    && op->od->n_localnet_ports) {
5f9769
-                    bool add_chassis_resident_check = false;
5f9769
-                    if (op->peer == op->peer->od->l3dgw_port) {
5f9769
-                        /* The peer of this port represents a distributed
5f9769
-                         * gateway port. The destination lookup flow for the
5f9769
-                         * router's distributed gateway port MAC address should
5f9769
-                         * only be programmed on the gateway chassis. */
5f9769
-                        add_chassis_resident_check = true;
5f9769
-                    } else {
5f9769
-                        /* Check if the option 'reside-on-redirect-chassis'
5f9769
-                         * is set to true on the peer port. If set to true
5f9769
-                         * and if the logical switch has a localnet port, it
5f9769
-                         * means the router pipeline for the packets from
5f9769
-                         * this logical switch should be run on the chassis
5f9769
-                         * hosting the gateway port.
5f9769
-                         */
5f9769
-                        add_chassis_resident_check = smap_get_bool(
5f9769
-                            &op->peer->nbrp->options,
5f9769
-                            "reside-on-redirect-chassis", false);
5f9769
-                    }
5f9769
-
5f9769
-                    if (add_chassis_resident_check) {
5f9769
-                        ds_put_format(&match, " && is_chassis_resident(%s)",
5f9769
-                                      op->peer->od->l3redirect_port->json_key);
5f9769
-                    }
5f9769
-                }
5f9769
-
5f9769
-                ds_clear(&actions);
5f9769
-                ds_put_format(&actions, "outport = %s; output;", op->json_key);
5f9769
-                ovn_lflow_add_with_hint(lflows, op->od,
5f9769
-                                        S_SWITCH_IN_L2_LKUP, 50,
5f9769
-                                        ds_cstr(&match), ds_cstr(&actions),
5f9769
-                                        &op->nbsp->header_);
5f9769
-
5f9769
-                /* Add ethernet addresses specified in NAT rules on
5f9769
-                 * distributed logical routers. */
5f9769
-                if (op->peer->od->l3dgw_port
5f9769
-                    && op->peer == op->peer->od->l3dgw_port) {
5f9769
-                    for (int j = 0; j < op->peer->od->nbr->n_nat; j++) {
5f9769
-                        const struct nbrec_nat *nat
5f9769
-                                                  = op->peer->od->nbr->nat[j];
5f9769
-                        if (!strcmp(nat->type, "dnat_and_snat")
5f9769
-                            && nat->logical_port && nat->external_mac
5f9769
-                            && eth_addr_from_string(nat->external_mac, &mac)) {
5f9769
-
5f9769
-                            ds_clear(&match);
5f9769
-                            ds_put_format(&match, "eth.dst == "ETH_ADDR_FMT
5f9769
-                                          " && is_chassis_resident(\"%s\")",
5f9769
-                                          ETH_ADDR_ARGS(mac),
5f9769
-                                          nat->logical_port);
5f9769
-
5f9769
-                            ds_clear(&actions);
5f9769
-                            ds_put_format(&actions, "outport = %s; output;",
5f9769
-                                          op->json_key);
5f9769
-                            ovn_lflow_add_with_hint(lflows, op->od,
5f9769
-                                                    S_SWITCH_IN_L2_LKUP, 50,
5f9769
-                                                    ds_cstr(&match),
5f9769
-                                                    ds_cstr(&actions),
5f9769
-                                                    &op->nbsp->header_);
5f9769
-                        }
5f9769
-                    }
5f9769
-                }
5f9769
-            } else {
5f9769
-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
5f9769
-
5f9769
-                VLOG_INFO_RL(&rl,
5f9769
-                             "%s: invalid syntax '%s' in addresses column",
5f9769
-                             op->nbsp->name, op->nbsp->addresses[i]);
5f9769
-            }
5f9769
-        }
5f9769
-    }
5f9769
 
5f9769
     /* Ingress table 19: Destination lookup for unknown MACs (priority 0). */
5f9769
     HMAP_FOR_EACH (od, key_node, datapaths) {
5f9769
@@ -7492,6 +7285,218 @@ build_lswitch_destination_lookup_bmcast(struct ovn_datapath *od,
5f9769
     }
5f9769
 }
5f9769
 
5f9769
+
5f9769
+/* Ingress table 19: Add IP multicast flows learnt from IGMP/MLD
5f9769
+ * (priority 90). */
5f9769
+static void
5f9769
+build_lswitch_ip_mcast_igmp_mld(struct ovn_igmp_group *igmp_group,
5f9769
+                                struct hmap *lflows,
5f9769
+                                struct ds *actions,
5f9769
+                                struct ds *match)
5f9769
+{
5f9769
+    if (igmp_group->datapath) {
5f9769
+
5f9769
+        ds_clear(match);
5f9769
+        ds_clear(actions);
5f9769
+
5f9769
+        struct mcast_switch_info *mcast_sw_info =
5f9769
+            &igmp_group->datapath->mcast_info.sw;
5f9769
+
5f9769
+        if (IN6_IS_ADDR_V4MAPPED(&igmp_group->address)) {
5f9769
+            /* RFC 4541, section 2.1.2, item 2: Skip groups in the 224.0.0.X
5f9769
+             * range.
5f9769
+             */
5f9769
+            ovs_be32 group_address =
5f9769
+                in6_addr_get_mapped_ipv4(&igmp_group->address);
5f9769
+            if (ip_is_local_multicast(group_address)) {
5f9769
+                return;
5f9769
+            }
5f9769
+
5f9769
+            if (mcast_sw_info->active_v4_flows >= mcast_sw_info->table_size) {
5f9769
+                return;
5f9769
+            }
5f9769
+            mcast_sw_info->active_v4_flows++;
5f9769
+            ds_put_format(match, "eth.mcast && ip4 && ip4.dst == %s ",
5f9769
+                          igmp_group->mcgroup.name);
5f9769
+        } else {
5f9769
+            /* RFC 4291, section 2.7.1: Skip groups that correspond to all
5f9769
+             * hosts.
5f9769
+             */
5f9769
+            if (ipv6_is_all_hosts(&igmp_group->address)) {
5f9769
+                return;
5f9769
+            }
5f9769
+            if (mcast_sw_info->active_v6_flows >= mcast_sw_info->table_size) {
5f9769
+                return;
5f9769
+            }
5f9769
+            mcast_sw_info->active_v6_flows++;
5f9769
+            ds_put_format(match, "eth.mcast && ip6 && ip6.dst == %s ",
5f9769
+                          igmp_group->mcgroup.name);
5f9769
+        }
5f9769
+
5f9769
+        /* Also flood traffic to all multicast routers with relay enabled. */
5f9769
+        if (mcast_sw_info->flood_relay) {
5f9769
+            ds_put_cstr(actions,
5f9769
+                        "clone { "
5f9769
+                            "outport = \""MC_MROUTER_FLOOD "\"; "
5f9769
+                            "output; "
5f9769
+                        "};");
5f9769
+        }
5f9769
+        if (mcast_sw_info->flood_static) {
5f9769
+            ds_put_cstr(actions,
5f9769
+                        "clone { "
5f9769
+                            "outport =\""MC_STATIC"\"; "
5f9769
+                            "output; "
5f9769
+                        "};");
5f9769
+        }
5f9769
+        ds_put_format(actions, "outport = \"%s\"; output; ",
5f9769
+                      igmp_group->mcgroup.name);
5f9769
+
5f9769
+        ovn_lflow_add_unique(lflows, igmp_group->datapath, S_SWITCH_IN_L2_LKUP,
5f9769
+                             90, ds_cstr(match), ds_cstr(actions));
5f9769
+    }
5f9769
+}
5f9769
+
5f9769
+/* Ingress table 19: Destination lookup, unicast handling (priority 50), */
5f9769
+static void
5f9769
+build_lswitch_ip_unicast_lookup(struct ovn_port *op,
5f9769
+                                struct hmap *lflows,
5f9769
+                                struct hmap *mcgroups,
5f9769
+                                struct ds *actions,
5f9769
+                                struct ds *match)
5f9769
+{
5f9769
+    if (op->nbsp && (!lsp_is_external(op->nbsp))) {
5f9769
+
5f9769
+        /* For ports connected to logical routers add flows to bypass the
5f9769
+         * broadcast flooding of ARP/ND requests in table 19. We direct the
5f9769
+         * requests only to the router port that owns the IP address.
5f9769
+         */
5f9769
+        if (lsp_is_router(op->nbsp)) {
5f9769
+            build_lswitch_rport_arp_req_flows(op->peer, op->od, op, lflows,
5f9769
+                                              &op->nbsp->header_);
5f9769
+        }
5f9769
+
5f9769
+        for (size_t i = 0; i < op->nbsp->n_addresses; i++) {
5f9769
+            /* Addresses are owned by the logical port.
5f9769
+             * Ethernet address followed by zero or more IPv4
5f9769
+             * or IPv6 addresses (or both). */
5f9769
+            struct eth_addr mac;
5f9769
+            if (ovs_scan(op->nbsp->addresses[i],
5f9769
+                        ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) {
5f9769
+                ds_clear(match);
5f9769
+                ds_put_format(match, "eth.dst == "ETH_ADDR_FMT,
5f9769
+                              ETH_ADDR_ARGS(mac));
5f9769
+
5f9769
+                ds_clear(actions);
5f9769
+                ds_put_format(actions, "outport = %s; output;", op->json_key);
5f9769
+                ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_L2_LKUP,
5f9769
+                                        50, ds_cstr(match),
5f9769
+                                        ds_cstr(actions),
5f9769
+                                        &op->nbsp->header_);
5f9769
+            } else if (!strcmp(op->nbsp->addresses[i], "unknown")) {
5f9769
+                if (lsp_is_enabled(op->nbsp)) {
5f9769
+                    ovn_multicast_add(mcgroups, &mc_unknown, op);
5f9769
+                    op->od->has_unknown = true;
5f9769
+                }
5f9769
+            } else if (is_dynamic_lsp_address(op->nbsp->addresses[i])) {
5f9769
+                if (!op->nbsp->dynamic_addresses
5f9769
+                    || !ovs_scan(op->nbsp->dynamic_addresses,
5f9769
+                            ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) {
5f9769
+                    continue;
5f9769
+                }
5f9769
+                ds_clear(match);
5f9769
+                ds_put_format(match, "eth.dst == "ETH_ADDR_FMT,
5f9769
+                              ETH_ADDR_ARGS(mac));
5f9769
+
5f9769
+                ds_clear(actions);
5f9769
+                ds_put_format(actions, "outport = %s; output;", op->json_key);
5f9769
+                ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_L2_LKUP,
5f9769
+                                        50, ds_cstr(match),
5f9769
+                                        ds_cstr(actions),
5f9769
+                                        &op->nbsp->header_);
5f9769
+            } else if (!strcmp(op->nbsp->addresses[i], "router")) {
5f9769
+                if (!op->peer || !op->peer->nbrp
5f9769
+                    || !ovs_scan(op->peer->nbrp->mac,
5f9769
+                            ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) {
5f9769
+                    continue;
5f9769
+                }
5f9769
+                ds_clear(match);
5f9769
+                ds_put_format(match, "eth.dst == "ETH_ADDR_FMT,
5f9769
+                              ETH_ADDR_ARGS(mac));
5f9769
+                if (op->peer->od->l3dgw_port
5f9769
+                    && op->peer->od->l3redirect_port
5f9769
+                    && op->od->n_localnet_ports) {
5f9769
+                    bool add_chassis_resident_check = false;
5f9769
+                    if (op->peer == op->peer->od->l3dgw_port) {
5f9769
+                        /* The peer of this port represents a distributed
5f9769
+                         * gateway port. The destination lookup flow for the
5f9769
+                         * router's distributed gateway port MAC address should
5f9769
+                         * only be programmed on the gateway chassis. */
5f9769
+                        add_chassis_resident_check = true;
5f9769
+                    } else {
5f9769
+                        /* Check if the option 'reside-on-redirect-chassis'
5f9769
+                         * is set to true on the peer port. If set to true
5f9769
+                         * and if the logical switch has a localnet port, it
5f9769
+                         * means the router pipeline for the packets from
5f9769
+                         * this logical switch should be run on the chassis
5f9769
+                         * hosting the gateway port.
5f9769
+                         */
5f9769
+                        add_chassis_resident_check = smap_get_bool(
5f9769
+                            &op->peer->nbrp->options,
5f9769
+                            "reside-on-redirect-chassis", false);
5f9769
+                    }
5f9769
+
5f9769
+                    if (add_chassis_resident_check) {
5f9769
+                        ds_put_format(match, " && is_chassis_resident(%s)",
5f9769
+                                      op->peer->od->l3redirect_port->json_key);
5f9769
+                    }
5f9769
+                }
5f9769
+
5f9769
+                ds_clear(actions);
5f9769
+                ds_put_format(actions, "outport = %s; output;", op->json_key);
5f9769
+                ovn_lflow_add_with_hint(lflows, op->od,
5f9769
+                                        S_SWITCH_IN_L2_LKUP, 50,
5f9769
+                                        ds_cstr(match), ds_cstr(actions),
5f9769
+                                        &op->nbsp->header_);
5f9769
+
5f9769
+                /* Add ethernet addresses specified in NAT rules on
5f9769
+                 * distributed logical routers. */
5f9769
+                if (op->peer->od->l3dgw_port
5f9769
+                    && op->peer == op->peer->od->l3dgw_port) {
5f9769
+                    for (int j = 0; j < op->peer->od->nbr->n_nat; j++) {
5f9769
+                        const struct nbrec_nat *nat
5f9769
+                                                  = op->peer->od->nbr->nat[j];
5f9769
+                        if (!strcmp(nat->type, "dnat_and_snat")
5f9769
+                            && nat->logical_port && nat->external_mac
5f9769
+                            && eth_addr_from_string(nat->external_mac, &mac)) {
5f9769
+
5f9769
+                            ds_clear(match);
5f9769
+                            ds_put_format(match, "eth.dst == "ETH_ADDR_FMT
5f9769
+                                          " && is_chassis_resident(\"%s\")",
5f9769
+                                          ETH_ADDR_ARGS(mac),
5f9769
+                                          nat->logical_port);
5f9769
+
5f9769
+                            ds_clear(actions);
5f9769
+                            ds_put_format(actions, "outport = %s; output;",
5f9769
+                                          op->json_key);
5f9769
+                            ovn_lflow_add_with_hint(lflows, op->od,
5f9769
+                                                    S_SWITCH_IN_L2_LKUP, 50,
5f9769
+                                                    ds_cstr(match),
5f9769
+                                                    ds_cstr(actions),
5f9769
+                                                    &op->nbsp->header_);
5f9769
+                        }
5f9769
+                    }
5f9769
+                }
5f9769
+            } else {
5f9769
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
5f9769
+
5f9769
+                VLOG_INFO_RL(&rl,
5f9769
+                             "%s: invalid syntax '%s' in addresses column",
5f9769
+                             op->nbsp->name, op->nbsp->addresses[i]);
5f9769
+            }
5f9769
+        }
5f9769
+    }
5f9769
+}
5f9769
+
5f9769
 /* Returns a string of the IP address of the router port 'op' that
5f9769
  * overlaps with 'ip_s".  If one is not found, returns NULL.
5f9769
  *
5f9769
@@ -11384,6 +11389,8 @@ build_lswitch_and_lrouter_iterate_by_op(struct ovn_port *op,
5f9769
                                              &lsi->match);
5f9769
     build_lswitch_dhcp_options_and_response(op,lsi->lflows);
5f9769
     build_lswitch_external_port(op, lsi->lflows);
5f9769
+    build_lswitch_ip_unicast_lookup(op, lsi->lflows, lsi->mcgroups,
5f9769
+                                    &lsi->actions, &lsi->match);
5f9769
 
5f9769
     /* Build Logical Router Flows. */
5f9769
     build_adm_ctrl_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
5f9769
@@ -11412,6 +11419,7 @@ build_lswitch_and_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
5f9769
     struct ovn_datapath *od;
5f9769
     struct ovn_port *op;
5f9769
     struct ovn_northd_lb *lb;
5f9769
+    struct ovn_igmp_group *igmp_group;
5f9769
 
5f9769
     char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
5f9769
 
5f9769
@@ -11443,14 +11451,19 @@ build_lswitch_and_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
5f9769
                                              &lsi.actions,
5f9769
                                              &lsi.match);
5f9769
     }
5f9769
+    HMAP_FOR_EACH (igmp_group, hmap_node, igmp_groups) {
5f9769
+        build_lswitch_ip_mcast_igmp_mld(igmp_group,
5f9769
+                                        lsi.lflows,
5f9769
+                                        &lsi.actions,
5f9769
+                                        &lsi.match);
5f9769
+    }
5f9769
     free(svc_check_match);
5f9769
 
5f9769
     ds_destroy(&lsi.match);
5f9769
     ds_destroy(&lsi.actions);
5f9769
 
5f9769
     /* Legacy lswitch build - to be migrated. */
5f9769
-    build_lswitch_flows(datapaths, ports, lflows, mcgroups,
5f9769
-                        igmp_groups);
5f9769
+    build_lswitch_flows(datapaths, ports, lflows);
5f9769
 
5f9769
     /* Legacy lrouter build - to be migrated. */
5f9769
     build_lrouter_flows(datapaths, ports, lflows, meter_groups, lbs);
5f9769
-- 
5f9769
2.29.2
5f9769