5f9769
From f21c1b7a467a691847b5552d4570af706fcc5bb0 Mon Sep 17 00:00:00 2001
5f9769
Message-Id: <f21c1b7a467a691847b5552d4570af706fcc5bb0.1610458802.git.lorenzo.bianconi@redhat.com>
5f9769
From: Anton Ivanov <anton.ivanov@cambridgegreys.com>
5f9769
Date: Tue, 5 Jan 2021 17:49:28 +0000
5f9769
Subject: [PATCH 01/16] ovn-northd: Move lswitch ARP/ND Responder to functions.
5f9769
5f9769
Move arp/nd responder lflow processing to per-iterable functions.
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 | 496 +++++++++++++++++++++++---------------------
5f9769
 1 file changed, 260 insertions(+), 236 deletions(-)
5f9769
5f9769
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
5f9769
index b377dffa1..d17cc55ac 100644
5f9769
--- a/northd/ovn-northd.c
5f9769
+++ b/northd/ovn-northd.c
5f9769
@@ -6770,7 +6770,7 @@ is_vlan_transparent(const struct ovn_datapath *od)
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, struct hmap *lbs)
5f9769
+                    struct hmap *igmp_groups)
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,240 +6778,7 @@ 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
-
5f9769
-    /* Ingress table 13: ARP/ND responder, skip requests coming from localnet
5f9769
-     * and vtep ports. (priority 100); see ovn-northd.8.xml for the
5f9769
-     * rationale. */
5f9769
     struct ovn_port *op;
5f9769
-    HMAP_FOR_EACH (op, key_node, ports) {
5f9769
-        if (!op->nbsp) {
5f9769
-            continue;
5f9769
-        }
5f9769
-
5f9769
-        if ((!strcmp(op->nbsp->type, "localnet")) ||
5f9769
-            (!strcmp(op->nbsp->type, "vtep"))) {
5f9769
-            ds_clear(&match);
5f9769
-            ds_put_format(&match, "inport == %s", op->json_key);
5f9769
-            ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP,
5f9769
-                                    100, ds_cstr(&match), "next;",
5f9769
-                                    &op->nbsp->header_);
5f9769
-        }
5f9769
-    }
5f9769
-
5f9769
-    /* Ingress table 13: ARP/ND responder, reply for known IPs.
5f9769
-     * (priority 50). */
5f9769
-    HMAP_FOR_EACH (op, key_node, ports) {
5f9769
-        if (!op->nbsp) {
5f9769
-            continue;
5f9769
-        }
5f9769
-
5f9769
-        if (!strcmp(op->nbsp->type, "virtual")) {
5f9769
-            /* Handle
5f9769
-             *  - GARPs for virtual ip which belongs to a logical port
5f9769
-             *    of type 'virtual' and bind that port.
5f9769
-             *
5f9769
-             *  - ARP reply from the virtual ip which belongs to a logical
5f9769
-             *    port of type 'virtual' and bind that port.
5f9769
-             * */
5f9769
-            ovs_be32 ip;
5f9769
-            const char *virtual_ip = smap_get(&op->nbsp->options,
5f9769
-                                              "virtual-ip");
5f9769
-            const char *virtual_parents = smap_get(&op->nbsp->options,
5f9769
-                                                   "virtual-parents");
5f9769
-            if (!virtual_ip || !virtual_parents ||
5f9769
-                !ip_parse(virtual_ip, &ip)) {
5f9769
-                continue;
5f9769
-            }
5f9769
-
5f9769
-            char *tokstr = xstrdup(virtual_parents);
5f9769
-            char *save_ptr = NULL;
5f9769
-            char *vparent;
5f9769
-            for (vparent = strtok_r(tokstr, ",", &save_ptr); vparent != NULL;
5f9769
-                 vparent = strtok_r(NULL, ",", &save_ptr)) {
5f9769
-                struct ovn_port *vp = ovn_port_find(ports, vparent);
5f9769
-                if (!vp || vp->od != op->od) {
5f9769
-                    /* vparent name should be valid and it should belong
5f9769
-                     * to the same logical switch. */
5f9769
-                    continue;
5f9769
-                }
5f9769
-
5f9769
-                ds_clear(&match);
5f9769
-                ds_put_format(&match, "inport == \"%s\" && "
5f9769
-                              "((arp.op == 1 && arp.spa == %s && "
5f9769
-                              "arp.tpa == %s) || (arp.op == 2 && "
5f9769
-                              "arp.spa == %s))",
5f9769
-                              vparent, virtual_ip, virtual_ip,
5f9769
-                              virtual_ip);
5f9769
-                ds_clear(&actions);
5f9769
-                ds_put_format(&actions,
5f9769
-                    "bind_vport(%s, inport); "
5f9769
-                    "next;",
5f9769
-                    op->json_key);
5f9769
-                ovn_lflow_add_with_hint(lflows, op->od,
5f9769
-                                        S_SWITCH_IN_ARP_ND_RSP, 100,
5f9769
-                                        ds_cstr(&match), ds_cstr(&actions),
5f9769
-                                        &vp->nbsp->header_);
5f9769
-            }
5f9769
-
5f9769
-            free(tokstr);
5f9769
-        } else {
5f9769
-            /*
5f9769
-             * Add ARP/ND reply flows if either the
5f9769
-             *  - port is up and it doesn't have 'unknown' address defined or
5f9769
-             *  - port type is router or
5f9769
-             *  - port type is localport
5f9769
-             */
5f9769
-            if (check_lsp_is_up &&
5f9769
-                !lsp_is_up(op->nbsp) && !lsp_is_router(op->nbsp) &&
5f9769
-                strcmp(op->nbsp->type, "localport")) {
5f9769
-                continue;
5f9769
-            }
5f9769
-
5f9769
-            if (lsp_is_external(op->nbsp) || op->has_unknown) {
5f9769
-                continue;
5f9769
-            }
5f9769
-
5f9769
-            for (size_t i = 0; i < op->n_lsp_addrs; i++) {
5f9769
-                for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
5f9769
-                    ds_clear(&match);
5f9769
-                    ds_put_format(&match, "arp.tpa == %s && arp.op == 1",
5f9769
-                                op->lsp_addrs[i].ipv4_addrs[j].addr_s);
5f9769
-                    ds_clear(&actions);
5f9769
-                    ds_put_format(&actions,
5f9769
-                        "eth.dst = eth.src; "
5f9769
-                        "eth.src = %s; "
5f9769
-                        "arp.op = 2; /* ARP reply */ "
5f9769
-                        "arp.tha = arp.sha; "
5f9769
-                        "arp.sha = %s; "
5f9769
-                        "arp.tpa = arp.spa; "
5f9769
-                        "arp.spa = %s; "
5f9769
-                        "outport = inport; "
5f9769
-                        "flags.loopback = 1; "
5f9769
-                        "output;",
5f9769
-                        op->lsp_addrs[i].ea_s, op->lsp_addrs[i].ea_s,
5f9769
-                        op->lsp_addrs[i].ipv4_addrs[j].addr_s);
5f9769
-                    ovn_lflow_add_with_hint(lflows, op->od,
5f9769
-                                            S_SWITCH_IN_ARP_ND_RSP, 50,
5f9769
-                                            ds_cstr(&match),
5f9769
-                                            ds_cstr(&actions),
5f9769
-                                            &op->nbsp->header_);
5f9769
-
5f9769
-                    /* Do not reply to an ARP request from the port that owns
5f9769
-                     * the address (otherwise a DHCP client that ARPs to check
5f9769
-                     * for a duplicate address will fail).  Instead, forward
5f9769
-                     * it the usual way.
5f9769
-                     *
5f9769
-                     * (Another alternative would be to simply drop the packet.
5f9769
-                     * If everything is working as it is configured, then this
5f9769
-                     * would produce equivalent results, since no one should
5f9769
-                     * reply to the request.  But ARPing for one's own IP
5f9769
-                     * address is intended to detect situations where the
5f9769
-                     * network is not working as configured, so dropping the
5f9769
-                     * request would frustrate that intent.) */
5f9769
-                    ds_put_format(&match, " && inport == %s", op->json_key);
5f9769
-                    ovn_lflow_add_with_hint(lflows, op->od,
5f9769
-                                            S_SWITCH_IN_ARP_ND_RSP, 100,
5f9769
-                                            ds_cstr(&match), "next;",
5f9769
-                                            &op->nbsp->header_);
5f9769
-                }
5f9769
-
5f9769
-                /* For ND solicitations, we need to listen for both the
5f9769
-                 * unicast IPv6 address and its all-nodes multicast address,
5f9769
-                 * but always respond with the unicast IPv6 address. */
5f9769
-                for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) {
5f9769
-                    ds_clear(&match);
5f9769
-                    ds_put_format(&match,
5f9769
-                            "nd_ns && ip6.dst == {%s, %s} && nd.target == %s",
5f9769
-                            op->lsp_addrs[i].ipv6_addrs[j].addr_s,
5f9769
-                            op->lsp_addrs[i].ipv6_addrs[j].sn_addr_s,
5f9769
-                            op->lsp_addrs[i].ipv6_addrs[j].addr_s);
5f9769
-
5f9769
-                    ds_clear(&actions);
5f9769
-                    ds_put_format(&actions,
5f9769
-                            "%s { "
5f9769
-                            "eth.src = %s; "
5f9769
-                            "ip6.src = %s; "
5f9769
-                            "nd.target = %s; "
5f9769
-                            "nd.tll = %s; "
5f9769
-                            "outport = inport; "
5f9769
-                            "flags.loopback = 1; "
5f9769
-                            "output; "
5f9769
-                            "};",
5f9769
-                            lsp_is_router(op->nbsp) ? "nd_na_router" : "nd_na",
5f9769
-                            op->lsp_addrs[i].ea_s,
5f9769
-                            op->lsp_addrs[i].ipv6_addrs[j].addr_s,
5f9769
-                            op->lsp_addrs[i].ipv6_addrs[j].addr_s,
5f9769
-                            op->lsp_addrs[i].ea_s);
5f9769
-                    ovn_lflow_add_with_hint(lflows, op->od,
5f9769
-                                            S_SWITCH_IN_ARP_ND_RSP, 50,
5f9769
-                                            ds_cstr(&match),
5f9769
-                                            ds_cstr(&actions),
5f9769
-                                            &op->nbsp->header_);
5f9769
-
5f9769
-                    /* Do not reply to a solicitation from the port that owns
5f9769
-                     * the address (otherwise DAD detection will fail). */
5f9769
-                    ds_put_format(&match, " && inport == %s", op->json_key);
5f9769
-                    ovn_lflow_add_with_hint(lflows, op->od,
5f9769
-                                            S_SWITCH_IN_ARP_ND_RSP, 100,
5f9769
-                                            ds_cstr(&match), "next;",
5f9769
-                                            &op->nbsp->header_);
5f9769
-                }
5f9769
-            }
5f9769
-        }
5f9769
-    }
5f9769
-
5f9769
-    /* Ingress table 13: ARP/ND responder, by default goto next.
5f9769
-     * (priority 0)*/
5f9769
-    HMAP_FOR_EACH (od, key_node, datapaths) {
5f9769
-        if (!od->nbs) {
5f9769
-            continue;
5f9769
-        }
5f9769
-
5f9769
-        ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 0, "1", "next;");
5f9769
-    }
5f9769
-
5f9769
-    /* Ingress table 13: ARP/ND responder for service monitor source ip.
5f9769
-     * (priority 110)*/
5f9769
-    struct ovn_northd_lb *lb;
5f9769
-    HMAP_FOR_EACH (lb, hmap_node, lbs) {
5f9769
-        for (size_t i = 0; i < lb->n_vips; i++) {
5f9769
-            struct ovn_northd_lb_vip *lb_vip_nb = &lb->vips_nb[i];
5f9769
-            if (!lb_vip_nb->lb_health_check) {
5f9769
-                continue;
5f9769
-            }
5f9769
-
5f9769
-            for (size_t j = 0; j < lb_vip_nb->n_backends; j++) {
5f9769
-                struct ovn_northd_lb_backend *backend_nb =
5f9769
-                    &lb_vip_nb->backends_nb[j];
5f9769
-                if (!backend_nb->op || !backend_nb->svc_mon_src_ip) {
5f9769
-                    continue;
5f9769
-                }
5f9769
-
5f9769
-                ds_clear(&match);
5f9769
-                ds_put_format(&match, "arp.tpa == %s && arp.op == 1",
5f9769
-                              backend_nb->svc_mon_src_ip);
5f9769
-                ds_clear(&actions);
5f9769
-                ds_put_format(&actions,
5f9769
-                    "eth.dst = eth.src; "
5f9769
-                    "eth.src = %s; "
5f9769
-                    "arp.op = 2; /* ARP reply */ "
5f9769
-                    "arp.tha = arp.sha; "
5f9769
-                    "arp.sha = %s; "
5f9769
-                    "arp.tpa = arp.spa; "
5f9769
-                    "arp.spa = %s; "
5f9769
-                    "outport = inport; "
5f9769
-                    "flags.loopback = 1; "
5f9769
-                    "output;",
5f9769
-                    svc_monitor_mac, svc_monitor_mac,
5f9769
-                    backend_nb->svc_mon_src_ip);
5f9769
-                ovn_lflow_add_with_hint(lflows,
5f9769
-                                        backend_nb->op->od,
5f9769
-                                        S_SWITCH_IN_ARP_ND_RSP, 110,
5f9769
-                                        ds_cstr(&match), ds_cstr(&actions),
5f9769
-                                        &lb->nlb->header_);
5f9769
-            }
5f9769
-        }
5f9769
-    }
5f9769
 
5f9769
 
5f9769
     /* Logical switch ingress table 14 and 15: DHCP options and response
5f9769
@@ -7471,6 +7238,251 @@ build_lswitch_lflows_admission_control(struct ovn_datapath *od,
5f9769
     }
5f9769
 }
5f9769
 
5f9769
+/* Ingress table 13: ARP/ND responder, skip requests coming from localnet
5f9769
+ * and vtep ports. (priority 100); see ovn-northd.8.xml for the
5f9769
+ * rationale. */
5f9769
+
5f9769
+static void
5f9769
+build_lswitch_arp_nd_responder_skip_local(struct ovn_port *op,
5f9769
+                                          struct hmap *lflows,
5f9769
+                                          struct ds *match)
5f9769
+{
5f9769
+    if (op->nbsp) {
5f9769
+        if ((!strcmp(op->nbsp->type, "localnet")) ||
5f9769
+            (!strcmp(op->nbsp->type, "vtep"))) {
5f9769
+            ds_clear(match);
5f9769
+            ds_put_format(match, "inport == %s", op->json_key);
5f9769
+            ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP,
5f9769
+                                    100, ds_cstr(match), "next;",
5f9769
+                                    &op->nbsp->header_);
5f9769
+        }
5f9769
+    }
5f9769
+}
5f9769
+
5f9769
+/* Ingress table 13: ARP/ND responder, reply for known IPs.
5f9769
+ * (priority 50). */
5f9769
+static void
5f9769
+build_lswitch_arp_nd_responder_known_ips(struct ovn_port *op,
5f9769
+                                         struct hmap *lflows,
5f9769
+                                         struct hmap *ports,
5f9769
+                                         struct ds *actions,
5f9769
+                                         struct ds *match)
5f9769
+{
5f9769
+    if (op->nbsp) {
5f9769
+        if (!strcmp(op->nbsp->type, "virtual")) {
5f9769
+            /* Handle
5f9769
+             *  - GARPs for virtual ip which belongs to a logical port
5f9769
+             *    of type 'virtual' and bind that port.
5f9769
+             *
5f9769
+             *  - ARP reply from the virtual ip which belongs to a logical
5f9769
+             *    port of type 'virtual' and bind that port.
5f9769
+             * */
5f9769
+            ovs_be32 ip;
5f9769
+            const char *virtual_ip = smap_get(&op->nbsp->options,
5f9769
+                                              "virtual-ip");
5f9769
+            const char *virtual_parents = smap_get(&op->nbsp->options,
5f9769
+                                                   "virtual-parents");
5f9769
+            if (!virtual_ip || !virtual_parents ||
5f9769
+                !ip_parse(virtual_ip, &ip)) {
5f9769
+                return;
5f9769
+            }
5f9769
+
5f9769
+            char *tokstr = xstrdup(virtual_parents);
5f9769
+            char *save_ptr = NULL;
5f9769
+            char *vparent;
5f9769
+            for (vparent = strtok_r(tokstr, ",", &save_ptr); vparent != NULL;
5f9769
+                 vparent = strtok_r(NULL, ",", &save_ptr)) {
5f9769
+                struct ovn_port *vp = ovn_port_find(ports, vparent);
5f9769
+                if (!vp || vp->od != op->od) {
5f9769
+                    /* vparent name should be valid and it should belong
5f9769
+                     * to the same logical switch. */
5f9769
+                    continue;
5f9769
+                }
5f9769
+
5f9769
+                ds_clear(match);
5f9769
+                ds_put_format(match, "inport == \"%s\" && "
5f9769
+                              "((arp.op == 1 && arp.spa == %s && "
5f9769
+                              "arp.tpa == %s) || (arp.op == 2 && "
5f9769
+                              "arp.spa == %s))",
5f9769
+                              vparent, virtual_ip, virtual_ip,
5f9769
+                              virtual_ip);
5f9769
+                ds_clear(actions);
5f9769
+                ds_put_format(actions,
5f9769
+                    "bind_vport(%s, inport); "
5f9769
+                    "next;",
5f9769
+                    op->json_key);
5f9769
+                ovn_lflow_add_with_hint(lflows, op->od,
5f9769
+                                        S_SWITCH_IN_ARP_ND_RSP, 100,
5f9769
+                                        ds_cstr(match), ds_cstr(actions),
5f9769
+                                        &vp->nbsp->header_);
5f9769
+            }
5f9769
+
5f9769
+            free(tokstr);
5f9769
+        } else {
5f9769
+            /*
5f9769
+             * Add ARP/ND reply flows if either the
5f9769
+             *  - port is up and it doesn't have 'unknown' address defined or
5f9769
+             *  - port type is router or
5f9769
+             *  - port type is localport
5f9769
+             */
5f9769
+            if (check_lsp_is_up &&
5f9769
+                !lsp_is_up(op->nbsp) && !lsp_is_router(op->nbsp) &&
5f9769
+                strcmp(op->nbsp->type, "localport")) {
5f9769
+                return;
5f9769
+            }
5f9769
+
5f9769
+            if (lsp_is_external(op->nbsp) || op->has_unknown) {
5f9769
+                return;
5f9769
+            }
5f9769
+
5f9769
+            for (size_t i = 0; i < op->n_lsp_addrs; i++) {
5f9769
+                for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
5f9769
+                    ds_clear(match);
5f9769
+                    ds_put_format(match, "arp.tpa == %s && arp.op == 1",
5f9769
+                                op->lsp_addrs[i].ipv4_addrs[j].addr_s);
5f9769
+                    ds_clear(actions);
5f9769
+                    ds_put_format(actions,
5f9769
+                        "eth.dst = eth.src; "
5f9769
+                        "eth.src = %s; "
5f9769
+                        "arp.op = 2; /* ARP reply */ "
5f9769
+                        "arp.tha = arp.sha; "
5f9769
+                        "arp.sha = %s; "
5f9769
+                        "arp.tpa = arp.spa; "
5f9769
+                        "arp.spa = %s; "
5f9769
+                        "outport = inport; "
5f9769
+                        "flags.loopback = 1; "
5f9769
+                        "output;",
5f9769
+                        op->lsp_addrs[i].ea_s, op->lsp_addrs[i].ea_s,
5f9769
+                        op->lsp_addrs[i].ipv4_addrs[j].addr_s);
5f9769
+                    ovn_lflow_add_with_hint(lflows, op->od,
5f9769
+                                            S_SWITCH_IN_ARP_ND_RSP, 50,
5f9769
+                                            ds_cstr(match),
5f9769
+                                            ds_cstr(actions),
5f9769
+                                            &op->nbsp->header_);
5f9769
+
5f9769
+                    /* Do not reply to an ARP request from the port that owns
5f9769
+                     * the address (otherwise a DHCP client that ARPs to check
5f9769
+                     * for a duplicate address will fail).  Instead, forward
5f9769
+                     * it the usual way.
5f9769
+                     *
5f9769
+                     * (Another alternative would be to simply drop the packet.
5f9769
+                     * If everything is working as it is configured, then this
5f9769
+                     * would produce equivalent results, since no one should
5f9769
+                     * reply to the request.  But ARPing for one's own IP
5f9769
+                     * address is intended to detect situations where the
5f9769
+                     * network is not working as configured, so dropping the
5f9769
+                     * request would frustrate that intent.) */
5f9769
+                    ds_put_format(match, " && inport == %s", op->json_key);
5f9769
+                    ovn_lflow_add_with_hint(lflows, op->od,
5f9769
+                                            S_SWITCH_IN_ARP_ND_RSP, 100,
5f9769
+                                            ds_cstr(match), "next;",
5f9769
+                                            &op->nbsp->header_);
5f9769
+                }
5f9769
+
5f9769
+                /* For ND solicitations, we need to listen for both the
5f9769
+                 * unicast IPv6 address and its all-nodes multicast address,
5f9769
+                 * but always respond with the unicast IPv6 address. */
5f9769
+                for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) {
5f9769
+                    ds_clear(match);
5f9769
+                    ds_put_format(match,
5f9769
+                            "nd_ns && ip6.dst == {%s, %s} && nd.target == %s",
5f9769
+                            op->lsp_addrs[i].ipv6_addrs[j].addr_s,
5f9769
+                            op->lsp_addrs[i].ipv6_addrs[j].sn_addr_s,
5f9769
+                            op->lsp_addrs[i].ipv6_addrs[j].addr_s);
5f9769
+
5f9769
+                    ds_clear(actions);
5f9769
+                    ds_put_format(actions,
5f9769
+                            "%s { "
5f9769
+                            "eth.src = %s; "
5f9769
+                            "ip6.src = %s; "
5f9769
+                            "nd.target = %s; "
5f9769
+                            "nd.tll = %s; "
5f9769
+                            "outport = inport; "
5f9769
+                            "flags.loopback = 1; "
5f9769
+                            "output; "
5f9769
+                            "};",
5f9769
+                            lsp_is_router(op->nbsp) ? "nd_na_router" : "nd_na",
5f9769
+                            op->lsp_addrs[i].ea_s,
5f9769
+                            op->lsp_addrs[i].ipv6_addrs[j].addr_s,
5f9769
+                            op->lsp_addrs[i].ipv6_addrs[j].addr_s,
5f9769
+                            op->lsp_addrs[i].ea_s);
5f9769
+                    ovn_lflow_add_with_hint(lflows, op->od,
5f9769
+                                            S_SWITCH_IN_ARP_ND_RSP, 50,
5f9769
+                                            ds_cstr(match),
5f9769
+                                            ds_cstr(actions),
5f9769
+                                            &op->nbsp->header_);
5f9769
+
5f9769
+                    /* Do not reply to a solicitation from the port that owns
5f9769
+                     * the address (otherwise DAD detection will fail). */
5f9769
+                    ds_put_format(match, " && inport == %s", op->json_key);
5f9769
+                    ovn_lflow_add_with_hint(lflows, op->od,
5f9769
+                                            S_SWITCH_IN_ARP_ND_RSP, 100,
5f9769
+                                            ds_cstr(match), "next;",
5f9769
+                                            &op->nbsp->header_);
5f9769
+                }
5f9769
+            }
5f9769
+        }
5f9769
+    }
5f9769
+}
5f9769
+
5f9769
+/* Ingress table 13: ARP/ND responder, by default goto next.
5f9769
+ * (priority 0)*/
5f9769
+static void
5f9769
+build_lswitch_arp_nd_responder_default(struct ovn_datapath *od,
5f9769
+                                       struct hmap *lflows)
5f9769
+{
5f9769
+    if (od->nbs) {
5f9769
+        ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 0, "1", "next;");
5f9769
+    }
5f9769
+}
5f9769
+
5f9769
+/* Ingress table 13: ARP/ND responder for service monitor source ip.
5f9769
+ * (priority 110)*/
5f9769
+static void
5f9769
+build_lswitch_arp_nd_service_monitor(struct ovn_northd_lb *lb,
5f9769
+                                     struct hmap *lflows,
5f9769
+                                     struct ds *actions,
5f9769
+                                     struct ds *match)
5f9769
+{
5f9769
+    for (size_t i = 0; i < lb->n_vips; i++) {
5f9769
+        struct ovn_northd_lb_vip *lb_vip_nb = &lb->vips_nb[i];
5f9769
+        if (!lb_vip_nb->lb_health_check) {
5f9769
+            continue;
5f9769
+        }
5f9769
+
5f9769
+        for (size_t j = 0; j < lb_vip_nb->n_backends; j++) {
5f9769
+            struct ovn_northd_lb_backend *backend_nb =
5f9769
+                &lb_vip_nb->backends_nb[j];
5f9769
+            if (!backend_nb->op || !backend_nb->svc_mon_src_ip) {
5f9769
+                continue;
5f9769
+            }
5f9769
+
5f9769
+            ds_clear(match);
5f9769
+            ds_put_format(match, "arp.tpa == %s && arp.op == 1",
5f9769
+                          backend_nb->svc_mon_src_ip);
5f9769
+            ds_clear(actions);
5f9769
+            ds_put_format(actions,
5f9769
+                "eth.dst = eth.src; "
5f9769
+                "eth.src = %s; "
5f9769
+                "arp.op = 2; /* ARP reply */ "
5f9769
+                "arp.tha = arp.sha; "
5f9769
+                "arp.sha = %s; "
5f9769
+                "arp.tpa = arp.spa; "
5f9769
+                "arp.spa = %s; "
5f9769
+                "outport = inport; "
5f9769
+                "flags.loopback = 1; "
5f9769
+                "output;",
5f9769
+                svc_monitor_mac, svc_monitor_mac,
5f9769
+                backend_nb->svc_mon_src_ip);
5f9769
+            ovn_lflow_add_with_hint(lflows,
5f9769
+                                    backend_nb->op->od,
5f9769
+                                    S_SWITCH_IN_ARP_ND_RSP, 110,
5f9769
+                                    ds_cstr(match), ds_cstr(actions),
5f9769
+                                    &lb->nlb->header_);
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
@@ -11322,6 +11334,7 @@ build_lswitch_and_lrouter_iterate_by_od(struct ovn_datapath *od,
5f9769
     build_fwd_group_lflows(od, lsi->lflows);
5f9769
     build_lswitch_lflows_admission_control(od, lsi->lflows);
5f9769
     build_lswitch_input_port_sec_od(od, lsi->lflows);
5f9769
+    build_lswitch_arp_nd_responder_default(od, lsi->lflows);
5f9769
 
5f9769
     /* Build Logical Router Flows. */
5f9769
     build_adm_ctrl_flows_for_lrouter(od, lsi->lflows);
5f9769
@@ -11352,7 +11365,12 @@ build_lswitch_and_lrouter_iterate_by_op(struct ovn_port *op,
5f9769
     /* Build Logical Switch Flows. */
5f9769
     build_lswitch_input_port_sec_op(op, lsi->lflows, &lsi->actions,
5f9769
                                     &lsi->match);
5f9769
-
5f9769
+    build_lswitch_arp_nd_responder_skip_local(op, lsi->lflows,
5f9769
+                                              &lsi->match);
5f9769
+    build_lswitch_arp_nd_responder_known_ips(op, lsi->lflows,
5f9769
+                                             lsi->ports,
5f9769
+                                             &lsi->actions,
5f9769
+                                             &lsi->match);
5f9769
     /* Build Logical Router Flows. */
5f9769
     build_adm_ctrl_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
5f9769
                                           &lsi->actions);
5f9769
@@ -11379,6 +11397,7 @@ build_lswitch_and_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
5f9769
 {
5f9769
     struct ovn_datapath *od;
5f9769
     struct ovn_port *op;
5f9769
+    struct ovn_northd_lb *lb;
5f9769
 
5f9769
     char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
5f9769
 
5f9769
@@ -11405,6 +11424,11 @@ build_lswitch_and_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
5f9769
     HMAP_FOR_EACH (op, key_node, ports) {
5f9769
         build_lswitch_and_lrouter_iterate_by_op(op, &lsi);
5f9769
     }
5f9769
+    HMAP_FOR_EACH (lb, hmap_node, lbs) {
5f9769
+        build_lswitch_arp_nd_service_monitor(lb, lsi.lflows,
5f9769
+                                             &lsi.actions,
5f9769
+                                             &lsi.match);
5f9769
+    }
5f9769
     free(svc_check_match);
5f9769
 
5f9769
     ds_destroy(&lsi.match);
5f9769
@@ -11412,7 +11436,7 @@ build_lswitch_and_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
5f9769
 
5f9769
     /* Legacy lswitch build - to be migrated. */
5f9769
     build_lswitch_flows(datapaths, ports, lflows, mcgroups,
5f9769
-                        igmp_groups, lbs);
5f9769
+                        igmp_groups);
5f9769
 
5f9769
     /* Legacy lrouter build - to be migrated. */
5f9769
     build_lrouter_flows(datapaths, ports, lflows, meter_groups, lbs);
5f9769
-- 
5f9769
2.29.2
5f9769