bbaaef
From 14de43e68f38fab76e50c21d3773f44c6e119d0e Mon Sep 17 00:00:00 2001
bbaaef
From: Numan Siddique <numans@ovn.org>
bbaaef
Date: Mon, 27 Jan 2020 18:36:24 +0530
bbaaef
Subject: [PATCH 2/2] ovn-northd: Consider load balancer active backends in
bbaaef
 router pipeline
bbaaef
MIME-Version: 1.0
bbaaef
Content-Type: text/plain; charset=UTF-8
bbaaef
Content-Transfer-Encoding: 8bit
bbaaef
bbaaef
The commit [1] which added lood balancer health check support
bbaaef
missed out on updating the logical flows to consider only
bbaaef
active backends in the logical router pipeline if a load balancer
bbaaef
is associated. This patch fixes it. It also refactors the code
bbaaef
a bit.
bbaaef
bbaaef
Without this, an offline backend may be chosen for load balancing,
bbaaef
resulting in the packet loss.
bbaaef
bbaaef
This patch also adds logical flows in ingress ACL and egress ACL logical
bbaaef
switch datapath pipeline to skip the ACL policies for service monitor health
bbaaef
check traffic.
bbaaef
bbaaef
[1] - ba0d6eae960d("ovn-northd: Add support for Load Balancer health check")
bbaaef
bbaaef
Reported-by: Maciej Józefczyk <mjozefcz@redhat.com>
bbaaef
Fixes - ba0d6eae960d("ovn-northd: Add support for Load Balancer health check")
bbaaef
Signed-off-by: Numan Siddique <numans@ovn.org>
bbaaef
Acked-by: Dumitru Ceara <dceara@redhat.com>
bbaaef
bbaaef
(cherry-picked from upstream commit bb9f2b9ce56c9319066524c0ad89034adae6b168)
bbaaef
Conflicts:
bbaaef
	tests/ovn.at
bbaaef
bbaaef
Change-Id: Ibacbaace0d710aa4d3e0b1d077e032443dd2a9e6
bbaaef
---
bbaaef
 ovn/northd/ovn-northd.8.xml |  22 +++
bbaaef
 ovn/northd/ovn-northd.c     | 268 +++++++++++++++++-------------------
bbaaef
 tests/ovn.at                |  27 ++++
bbaaef
 3 files changed, 172 insertions(+), 145 deletions(-)
bbaaef
bbaaef
diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
bbaaef
index 11ef65964..50f7d941b 100644
bbaaef
--- a/ovn/northd/ovn-northd.8.xml
bbaaef
+++ b/ovn/northd/ovn-northd.8.xml
bbaaef
@@ -410,6 +410,17 @@
bbaaef
         in the request direction are skipped here to let a newly created
bbaaef
         ACL re-allow this connection.
bbaaef
       
bbaaef
+
bbaaef
+      
  • bbaaef
    +        A priority 34000 logical flow is added for each logical switch datapath
    bbaaef
    +        with the match eth.dst = E to allow the service
    bbaaef
    +        monitor reply packet destined to ovn-controller
    bbaaef
    +        with the action next, where E is the
    bbaaef
    +        service monitor mac defined in the
    bbaaef
    +        
    bbaaef
    +        db="OVN_Northbound"/> colum of 
    bbaaef
    +        db="OVN_Northbound"/> table.
    bbaaef
    +      
    bbaaef
         
    bbaaef
     
    bbaaef
         

    Ingress Table 7: from-lport QoS Marking

    bbaaef
    @@ -1220,6 +1231,17 @@ output;
    bbaaef
             to allow the DNS reply packet from the
    bbaaef
             Ingress Table 15:DNS responses.
    bbaaef
           
    bbaaef
    +
    bbaaef
    +      
  • bbaaef
    +        A priority 34000 logical flow is added for each logical switch datapath
    bbaaef
    +        with the match eth.src = E to allow the service
    bbaaef
    +        monitor request packet generated by ovn-controller
    bbaaef
    +        with the action next, where E is the
    bbaaef
    +        service monitor mac defined in the
    bbaaef
    +        
    bbaaef
    +        db="OVN_Northbound"/> colum of 
    bbaaef
    +        db="OVN_Northbound"/> table.
    bbaaef
    +      
    bbaaef
         
    bbaaef
     
    bbaaef
         

    Egress Table 5: to-lport QoS Marking

    bbaaef
    diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
    bbaaef
    index b71ae7a94..7139143b3 100644
    bbaaef
    --- a/ovn/northd/ovn-northd.c
    bbaaef
    +++ b/ovn/northd/ovn-northd.c
    bbaaef
    @@ -3008,6 +3008,7 @@ struct lb_vip {
    bbaaef
     struct lb_vip_backend {
    bbaaef
         char *ip;
    bbaaef
         uint16_t port;
    bbaaef
    +    int addr_family;
    bbaaef
     
    bbaaef
         struct ovn_port *op; /* Logical port to which the ip belong to. */
    bbaaef
         bool health_check;
    bbaaef
    @@ -3163,6 +3164,7 @@ ovn_lb_create(struct northd_context *ctx, struct hmap *lbs,
    bbaaef
     
    bbaaef
                 lb->vips[n_vips].backends[i].ip = backend_ip;
    bbaaef
                 lb->vips[n_vips].backends[i].port = backend_port;
    bbaaef
    +            lb->vips[n_vips].backends[i].addr_family = addr_family;
    bbaaef
                 lb->vips[n_vips].backends[i].op = op;
    bbaaef
                 lb->vips[n_vips].backends[i].svc_mon_src_ip = svc_mon_src_ip;
    bbaaef
     
    bbaaef
    @@ -3226,6 +3228,38 @@ ovn_lb_destroy(struct ovn_lb *lb)
    bbaaef
         free(lb->vips);
    bbaaef
     }
    bbaaef
     
    bbaaef
    +static void build_lb_vip_ct_lb_actions(struct lb_vip *lb_vip,
    bbaaef
    +                                       struct ds *action)
    bbaaef
    +{
    bbaaef
    +    if (lb_vip->health_check) {
    bbaaef
    +        ds_put_cstr(action, "ct_lb(");
    bbaaef
    +
    bbaaef
    +        size_t n_active_backends = 0;
    bbaaef
    +        for (size_t k = 0; k < lb_vip->n_backends; k++) {
    bbaaef
    +            struct lb_vip_backend *backend = &lb_vip->backends[k];
    bbaaef
    +            if (backend->health_check && backend->sbrec_monitor &&
    bbaaef
    +                backend->sbrec_monitor->status &&
    bbaaef
    +                strcmp(backend->sbrec_monitor->status, "online")) {
    bbaaef
    +                continue;
    bbaaef
    +            }
    bbaaef
    +
    bbaaef
    +            n_active_backends++;
    bbaaef
    +            ds_put_format(action, "%s:%"PRIu16",",
    bbaaef
    +            backend->ip, backend->port);
    bbaaef
    +        }
    bbaaef
    +
    bbaaef
    +        if (!n_active_backends) {
    bbaaef
    +            ds_clear(action);
    bbaaef
    +            ds_put_cstr(action, "drop;");
    bbaaef
    +        } else {
    bbaaef
    +            ds_chomp(action, ',');
    bbaaef
    +            ds_put_cstr(action, ");");
    bbaaef
    +        }
    bbaaef
    +    } else {
    bbaaef
    +        ds_put_format(action, "ct_lb(%s);", lb_vip->backend_ips);
    bbaaef
    +    }
    bbaaef
    +}
    bbaaef
    +
    bbaaef
     static void
    bbaaef
     build_ovn_lbs(struct northd_context *ctx, struct hmap *ports,
    bbaaef
                   struct hmap *lbs)
    bbaaef
    @@ -4579,11 +4613,11 @@ ls_has_dns_records(const struct nbrec_logical_switch *nbs)
    bbaaef
     
    bbaaef
     static void
    bbaaef
     build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows,
    bbaaef
    -                          struct smap_node *node, char *ip_address,
    bbaaef
    -                          struct nbrec_load_balancer *lb, uint16_t port,
    bbaaef
    -                          int addr_family, int pl, struct shash *meter_groups)
    bbaaef
    +                          struct lb_vip *lb_vip,
    bbaaef
    +                          struct nbrec_load_balancer *lb,
    bbaaef
    +                          int pl, struct shash *meter_groups)
    bbaaef
     {
    bbaaef
    -    if (!controller_event_en || node->value[0]) {
    bbaaef
    +    if (!controller_event_en || lb_vip->n_backends) {
    bbaaef
             return;
    bbaaef
         }
    bbaaef
     
    bbaaef
    @@ -4594,32 +4628,35 @@ build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows,
    bbaaef
             meter = "event-elb";
    bbaaef
         }
    bbaaef
     
    bbaaef
    -    if (addr_family == AF_INET) {
    bbaaef
    -        ds_put_format(&match, "ip4.dst == %s && %s",
    bbaaef
    -                      ip_address, lb->protocol);
    bbaaef
    -    } else {
    bbaaef
    -        ds_put_format(&match, "ip6.dst == %s && %s",
    bbaaef
    -                      ip_address, lb->protocol);
    bbaaef
    -    }
    bbaaef
    -    if (port) {
    bbaaef
    +    ds_put_format(&match, "ip%s.dst == %s && %s",
    bbaaef
    +                  lb_vip->addr_family == AF_INET ? "4": "6",
    bbaaef
    +                  lb_vip->vip, lb->protocol);
    bbaaef
    +
    bbaaef
    +    char *vip = lb_vip->vip;
    bbaaef
    +    if (lb_vip->vip_port) {
    bbaaef
             ds_put_format(&match, " && %s.dst == %u", lb->protocol,
    bbaaef
    -                      port);
    bbaaef
    +                      lb_vip->vip_port);
    bbaaef
    +        vip = xasprintf("%s:%u", lb_vip->vip, lb_vip->vip_port);
    bbaaef
         }
    bbaaef
    +
    bbaaef
         action = xasprintf("trigger_event(event = \"%s\", "
    bbaaef
                            "meter = \"%s\", vip = \"%s\", "
    bbaaef
                            "protocol = \"%s\", "
    bbaaef
                            "load_balancer = \"" UUID_FMT "\");",
    bbaaef
                            event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS),
    bbaaef
    -                       meter, node->key, lb->protocol,
    bbaaef
    +                       meter, vip, lb->protocol,
    bbaaef
                            UUID_ARGS(&lb->header_.uuid));
    bbaaef
         ovn_lflow_add(lflows, od, pl, 130, ds_cstr(&match), action);
    bbaaef
         ds_destroy(&match);
    bbaaef
    +    if (lb_vip->vip_port) {
    bbaaef
    +        free(vip);
    bbaaef
    +    }
    bbaaef
         free(action);
    bbaaef
     }
    bbaaef
     
    bbaaef
     static void
    bbaaef
     build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
    bbaaef
    -             struct shash *meter_groups)
    bbaaef
    +             struct shash *meter_groups, struct hmap *lbs)
    bbaaef
     {
    bbaaef
         /* Do not send ND packets to conntrack */
    bbaaef
         ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 110,
    bbaaef
    @@ -4643,40 +4680,29 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
    bbaaef
         struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6);
    bbaaef
         bool vip_configured = false;
    bbaaef
         for (int i = 0; i < od->nbs->n_load_balancer; i++) {
    bbaaef
    -        struct nbrec_load_balancer *lb = od->nbs->load_balancer[i];
    bbaaef
    -        struct smap *vips = &lb->vips;
    bbaaef
    -        struct smap_node *node;
    bbaaef
    -        int addr_family = AF_INET;
    bbaaef
    -
    bbaaef
    -        SMAP_FOR_EACH (node, vips) {
    bbaaef
    -            vip_configured = true;
    bbaaef
    -
    bbaaef
    -            /* node->key contains IP:port or just IP. */
    bbaaef
    -            char *ip_address = NULL;
    bbaaef
    -            uint16_t port;
    bbaaef
    -            ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
    bbaaef
    -                                            &addr_family);
    bbaaef
    -            if (!ip_address) {
    bbaaef
    -                continue;
    bbaaef
    -            }
    bbaaef
    +        struct nbrec_load_balancer *nb_lb = od->nbs->load_balancer[i];
    bbaaef
    +        struct ovn_lb *lb =
    bbaaef
    +            ovn_lb_find(lbs, &nb_lb->header_.uuid);
    bbaaef
    +        ovs_assert(lb);
    bbaaef
     
    bbaaef
    -            if (addr_family == AF_INET) {
    bbaaef
    -                sset_add(&all_ips_v4, ip_address);
    bbaaef
    +        for (size_t j = 0; j < lb->n_vips; j++) {
    bbaaef
    +            struct lb_vip *lb_vip = &lb->vips[j];
    bbaaef
    +            if (lb_vip->addr_family == AF_INET) {
    bbaaef
    +                sset_add(&all_ips_v4, lb_vip->vip);
    bbaaef
                 } else {
    bbaaef
    -                sset_add(&all_ips_v6, ip_address);
    bbaaef
    +                sset_add(&all_ips_v6, lb_vip->vip);
    bbaaef
                 }
    bbaaef
     
    bbaaef
    -            build_empty_lb_event_flow(od, lflows, node, ip_address, lb,
    bbaaef
    -                                      port, addr_family, S_SWITCH_IN_PRE_LB,
    bbaaef
    -                                      meter_groups);
    bbaaef
    -
    bbaaef
    -            free(ip_address);
    bbaaef
    +            build_empty_lb_event_flow(od, lflows, lb_vip, nb_lb,
    bbaaef
    +                                      S_SWITCH_IN_PRE_LB, meter_groups);
    bbaaef
     
    bbaaef
                 /* Ignore L4 port information in the key because fragmented packets
    bbaaef
                  * may not have L4 information.  The pre-stateful table will send
    bbaaef
                  * the packet through ct() action to de-fragment. In stateful
    bbaaef
                  * table, we will eventually look at L4 information. */
    bbaaef
             }
    bbaaef
    +
    bbaaef
    +        vip_configured = !!lb->n_vips;
    bbaaef
         }
    bbaaef
     
    bbaaef
         /* 'REGBIT_CONNTRACK_DEFRAG' is set to let the pre-stateful table send
    bbaaef
    @@ -5224,6 +5250,20 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows,
    bbaaef
                 lflows, od, S_SWITCH_OUT_ACL, 34000, "udp.src == 53",
    bbaaef
                 actions);
    bbaaef
         }
    bbaaef
    +
    bbaaef
    +    /* Add a 34000 priority flow to advance the service monitor reply
    bbaaef
    +     * packets to skip applying ingress ACLs. */
    bbaaef
    +    char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
    bbaaef
    +    ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, 34000, svc_check_match,
    bbaaef
    +                  "next;");
    bbaaef
    +    free(svc_check_match);
    bbaaef
    +
    bbaaef
    +    /* Add a 34000 priority flow to advance the service monitor packets
    bbaaef
    +     * generated by ovn-controller to skip applying egress ACLs. */
    bbaaef
    +    svc_check_match = xasprintf("eth.src == %s", svc_monitor_mac);
    bbaaef
    +    ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, 34000, svc_check_match,
    bbaaef
    +                  "next;");
    bbaaef
    +    free(svc_check_match);
    bbaaef
     }
    bbaaef
     
    bbaaef
     static void
    bbaaef
    @@ -5349,36 +5389,7 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows, struct hmap *lbs)
    bbaaef
                 struct lb_vip *lb_vip = &lb->vips[j];
    bbaaef
                 /* New connections in Ingress table. */
    bbaaef
                 struct ds action = DS_EMPTY_INITIALIZER;
    bbaaef
    -            if (lb_vip->health_check) {
    bbaaef
    -                ds_put_cstr(&action, "ct_lb(");
    bbaaef
    -
    bbaaef
    -                size_t n_active_backends = 0;
    bbaaef
    -                for (size_t k = 0; k < lb_vip->n_backends; k++) {
    bbaaef
    -                    struct lb_vip_backend *backend = &lb_vip->backends[k];
    bbaaef
    -                    bool is_up = true;
    bbaaef
    -                    if (backend->health_check && backend->sbrec_monitor &&
    bbaaef
    -                        backend->sbrec_monitor->status &&
    bbaaef
    -                        strcmp(backend->sbrec_monitor->status, "online")) {
    bbaaef
    -                        is_up = false;
    bbaaef
    -                    }
    bbaaef
    -
    bbaaef
    -                    if (is_up) {
    bbaaef
    -                        n_active_backends++;
    bbaaef
    -                        ds_put_format(&action, "%s:%"PRIu16",",
    bbaaef
    -                        backend->ip, backend->port);
    bbaaef
    -                    }
    bbaaef
    -                }
    bbaaef
    -
    bbaaef
    -                if (!n_active_backends) {
    bbaaef
    -                    ds_clear(&action);
    bbaaef
    -                    ds_put_cstr(&action, "drop;");
    bbaaef
    -                } else {
    bbaaef
    -                    ds_chomp(&action, ',');
    bbaaef
    -                    ds_put_cstr(&action, ");");
    bbaaef
    -                }
    bbaaef
    -            } else {
    bbaaef
    -                ds_put_format(&action, "ct_lb(%s);", lb_vip->backend_ips);
    bbaaef
    -            }
    bbaaef
    +            build_lb_vip_ct_lb_actions(lb_vip, &action);
    bbaaef
     
    bbaaef
                 struct ds match = DS_EMPTY_INITIALIZER;
    bbaaef
                 if (lb_vip->addr_family == AF_INET) {
    bbaaef
    @@ -5699,7 +5710,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
             }
    bbaaef
     
    bbaaef
             build_pre_acls(od, lflows);
    bbaaef
    -        build_pre_lb(od, lflows, meter_groups);
    bbaaef
    +        build_pre_lb(od, lflows, meter_groups, lbs);
    bbaaef
             build_pre_stateful(od, lflows);
    bbaaef
             build_acls(od, lflows, port_groups);
    bbaaef
             build_qos(od, lflows);
    bbaaef
    @@ -6963,15 +6974,11 @@ get_force_snat_ip(struct ovn_datapath *od, const char *key_type,
    bbaaef
     static void
    bbaaef
     add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
    bbaaef
                        struct ds *match, struct ds *actions, int priority,
    bbaaef
    -                   const char *lb_force_snat_ip, struct smap_node *lb_info,
    bbaaef
    -                   bool is_udp, int addr_family, char *ip_addr,
    bbaaef
    -                   uint16_t l4_port, struct nbrec_load_balancer *lb,
    bbaaef
    +                   const char *lb_force_snat_ip, struct lb_vip *lb_vip,
    bbaaef
    +                   bool is_udp, struct nbrec_load_balancer *lb,
    bbaaef
                        struct shash *meter_groups)
    bbaaef
     {
    bbaaef
    -    char *backend_ips = lb_info->value;
    bbaaef
    -
    bbaaef
    -    build_empty_lb_event_flow(od, lflows, lb_info, ip_addr, lb,
    bbaaef
    -                              l4_port, addr_family, S_ROUTER_IN_DNAT,
    bbaaef
    +    build_empty_lb_event_flow(od, lflows, lb_vip, lb, S_ROUTER_IN_DNAT,
    bbaaef
                                   meter_groups);
    bbaaef
     
    bbaaef
         /* A match and actions for new connections. */
    bbaaef
    @@ -7000,7 +7007,7 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
    bbaaef
         free(new_match);
    bbaaef
         free(est_match);
    bbaaef
     
    bbaaef
    -    if (!od->l3dgw_port || !od->l3redirect_port || !backend_ips) {
    bbaaef
    +    if (!od->l3dgw_port || !od->l3redirect_port || !lb_vip->n_backends) {
    bbaaef
             return;
    bbaaef
         }
    bbaaef
     
    bbaaef
    @@ -7009,46 +7016,28 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
    bbaaef
          * router has a gateway router port associated.
    bbaaef
          */
    bbaaef
         struct ds undnat_match = DS_EMPTY_INITIALIZER;
    bbaaef
    -    if (addr_family == AF_INET) {
    bbaaef
    +    if (lb_vip->addr_family == AF_INET) {
    bbaaef
             ds_put_cstr(&undnat_match, "ip4 && (");
    bbaaef
         } else {
    bbaaef
             ds_put_cstr(&undnat_match, "ip6 && (");
    bbaaef
         }
    bbaaef
    -    char *start, *next, *ip_str;
    bbaaef
    -    start = next = xstrdup(backend_ips);
    bbaaef
    -    ip_str = strsep(&next, ",");
    bbaaef
    -    bool backend_ips_found = false;
    bbaaef
    -    while (ip_str && ip_str[0]) {
    bbaaef
    -        char *ip_address = NULL;
    bbaaef
    -        uint16_t port = 0;
    bbaaef
    -        int addr_family_;
    bbaaef
    -        ip_address_and_port_from_lb_key(ip_str, &ip_address, &port,
    bbaaef
    -                                        &addr_family_);
    bbaaef
    -        if (!ip_address) {
    bbaaef
    -            break;
    bbaaef
    -        }
    bbaaef
     
    bbaaef
    -        if (addr_family_ == AF_INET) {
    bbaaef
    -            ds_put_format(&undnat_match, "(ip4.src == %s", ip_address);
    bbaaef
    +    for (size_t i = 0; i < lb_vip->n_backends; i++) {
    bbaaef
    +        struct lb_vip_backend *backend = &lb_vip->backends[i];
    bbaaef
    +        if (backend->addr_family == AF_INET) {
    bbaaef
    +            ds_put_format(&undnat_match, "(ip4.src == %s", backend->ip);
    bbaaef
             } else {
    bbaaef
    -            ds_put_format(&undnat_match, "(ip6.src == %s", ip_address);
    bbaaef
    +            ds_put_format(&undnat_match, "(ip6.src == %s", backend->ip);
    bbaaef
             }
    bbaaef
    -        free(ip_address);
    bbaaef
    -        if (port) {
    bbaaef
    +
    bbaaef
    +        if (backend->port) {
    bbaaef
                 ds_put_format(&undnat_match, " && %s.src == %d) || ",
    bbaaef
    -                          is_udp ? "udp" : "tcp", port);
    bbaaef
    +                          is_udp ? "udp" : "tcp", backend->port);
    bbaaef
             } else {
    bbaaef
                 ds_put_cstr(&undnat_match, ") || ");
    bbaaef
             }
    bbaaef
    -        ip_str = strsep(&next, ",");
    bbaaef
    -        backend_ips_found = true;
    bbaaef
         }
    bbaaef
     
    bbaaef
    -    free(start);
    bbaaef
    -    if (!backend_ips_found) {
    bbaaef
    -        ds_destroy(&undnat_match);
    bbaaef
    -        return;
    bbaaef
    -    }
    bbaaef
         ds_chomp(&undnat_match, ' ');
    bbaaef
         ds_chomp(&undnat_match, '|');
    bbaaef
         ds_chomp(&undnat_match, '|');
    bbaaef
    @@ -7166,7 +7155,8 @@ lrouter_nat_is_stateless(const struct nbrec_nat *nat)
    bbaaef
     
    bbaaef
     static void
    bbaaef
     build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
    -                    struct hmap *lflows, struct shash *meter_groups)
    bbaaef
    +                    struct hmap *lflows, struct shash *meter_groups,
    bbaaef
    +                    struct hmap *lbs)
    bbaaef
     {
    bbaaef
         /* This flow table structure is documented in ovn-northd(8), so please
    bbaaef
          * update ovn-northd.8.xml if you change anything. */
    bbaaef
    @@ -8546,24 +8536,18 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
             struct sset all_ips = SSET_INITIALIZER(&all_ips);
    bbaaef
     
    bbaaef
             for (int i = 0; i < od->nbr->n_load_balancer; i++) {
    bbaaef
    -            struct nbrec_load_balancer *lb = od->nbr->load_balancer[i];
    bbaaef
    -            struct smap *vips = &lb->vips;
    bbaaef
    -            struct smap_node *node;
    bbaaef
    -
    bbaaef
    -            SMAP_FOR_EACH (node, vips) {
    bbaaef
    -                uint16_t port = 0;
    bbaaef
    -                int addr_family;
    bbaaef
    -
    bbaaef
    -                /* node->key contains IP:port or just IP. */
    bbaaef
    -                char *ip_address = NULL;
    bbaaef
    -                ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
    bbaaef
    -                        &addr_family);
    bbaaef
    -                if (!ip_address) {
    bbaaef
    -                    continue;
    bbaaef
    -                }
    bbaaef
    +            struct nbrec_load_balancer *nb_lb = od->nbr->load_balancer[i];
    bbaaef
    +            struct ovn_lb *lb =
    bbaaef
    +                ovn_lb_find(lbs, &nb_lb->header_.uuid);
    bbaaef
    +            ovs_assert(lb);
    bbaaef
    +
    bbaaef
    +            for (size_t j = 0; j < lb->n_vips; j++) {
    bbaaef
    +                struct lb_vip *lb_vip = &lb->vips[j];
    bbaaef
    +                ds_clear(&actions);
    bbaaef
    +                build_lb_vip_ct_lb_actions(lb_vip, &actions);
    bbaaef
     
    bbaaef
    -                if (!sset_contains(&all_ips, ip_address)) {
    bbaaef
    -                    sset_add(&all_ips, ip_address);
    bbaaef
    +                if (!sset_contains(&all_ips, lb_vip->vip)) {
    bbaaef
    +                    sset_add(&all_ips, lb_vip->vip);
    bbaaef
                         /* If there are any load balancing rules, we should send
    bbaaef
                          * the packet to conntrack for defragmentation and
    bbaaef
                          * tracking.  This helps with two things.
    bbaaef
    @@ -8573,12 +8557,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
                          * 2. If there are L4 ports in load balancing rules, we
    bbaaef
                          *    need the defragmentation to match on L4 ports. */
    bbaaef
                         ds_clear(&match);
    bbaaef
    -                    if (addr_family == AF_INET) {
    bbaaef
    +                    if (lb_vip->addr_family == AF_INET) {
    bbaaef
                             ds_put_format(&match, "ip && ip4.dst == %s",
    bbaaef
    -                                      ip_address);
    bbaaef
    -                    } else if (addr_family == AF_INET6) {
    bbaaef
    +                                      lb_vip->vip);
    bbaaef
    +                    } else if (lb_vip->addr_family == AF_INET6) {
    bbaaef
                             ds_put_format(&match, "ip && ip6.dst == %s",
    bbaaef
    -                                      ip_address);
    bbaaef
    +                                      lb_vip->vip);
    bbaaef
                         }
    bbaaef
                         ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG,
    bbaaef
                                       100, ds_cstr(&match), "ct_next;");
    bbaaef
    @@ -8589,28 +8573,25 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
                      * via add_router_lb_flow().  One flow is for specific matching
    bbaaef
                      * on ct.new with an action of "ct_lb($targets);".  The other
    bbaaef
                      * flow is for ct.est with an action of "ct_dnat;". */
    bbaaef
    -                ds_clear(&actions);
    bbaaef
    -                ds_put_format(&actions, "ct_lb(%s);", node->value);
    bbaaef
    -
    bbaaef
                     ds_clear(&match);
    bbaaef
    -                if (addr_family == AF_INET) {
    bbaaef
    +                if (lb_vip->addr_family == AF_INET) {
    bbaaef
                         ds_put_format(&match, "ip && ip4.dst == %s",
    bbaaef
    -                                ip_address);
    bbaaef
    -                } else if (addr_family == AF_INET6) {
    bbaaef
    +                                  lb_vip->vip);
    bbaaef
    +                } else if (lb_vip->addr_family == AF_INET6) {
    bbaaef
                         ds_put_format(&match, "ip && ip6.dst == %s",
    bbaaef
    -                                ip_address);
    bbaaef
    +                                  lb_vip->vip);
    bbaaef
                     }
    bbaaef
     
    bbaaef
                     int prio = 110;
    bbaaef
    -                bool is_udp = lb->protocol && !strcmp(lb->protocol, "udp") ?
    bbaaef
    -                    true : false;
    bbaaef
    -                if (port) {
    bbaaef
    +                bool is_udp = nullable_string_is_equal(nb_lb->protocol, "udp");
    bbaaef
    +
    bbaaef
    +                if (lb_vip->vip_port) {
    bbaaef
                         if (is_udp) {
    bbaaef
                             ds_put_format(&match, " && udp && udp.dst == %d",
    bbaaef
    -                                      port);
    bbaaef
    +                                      lb_vip->vip_port);
    bbaaef
                         } else {
    bbaaef
                             ds_put_format(&match, " && tcp && tcp.dst == %d",
    bbaaef
    -                                      port);
    bbaaef
    +                                      lb_vip->vip_port);
    bbaaef
                         }
    bbaaef
                         prio = 120;
    bbaaef
                     }
    bbaaef
    @@ -8620,11 +8601,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
                                       od->l3redirect_port->json_key);
    bbaaef
                     }
    bbaaef
                     add_router_lb_flow(lflows, od, &match, &actions, prio,
    bbaaef
    -                                   lb_force_snat_ip, node, is_udp,
    bbaaef
    -                                   addr_family, ip_address, port, lb,
    bbaaef
    -                                   meter_groups);
    bbaaef
    -
    bbaaef
    -                free(ip_address);
    bbaaef
    +                                   lb_force_snat_ip, lb_vip, is_udp,
    bbaaef
    +                                   nb_lb, meter_groups);
    bbaaef
                 }
    bbaaef
             }
    bbaaef
             sset_destroy(&all_ips);
    bbaaef
    @@ -9431,7 +9409,7 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
    bbaaef
     
    bbaaef
         build_lswitch_flows(datapaths, ports, port_groups, &lflows, mcgroups,
    bbaaef
                             igmp_groups, meter_groups, lbs);
    bbaaef
    -    build_lrouter_flows(datapaths, ports, &lflows, meter_groups);
    bbaaef
    +    build_lrouter_flows(datapaths, ports, &lflows, meter_groups, lbs);
    bbaaef
     
    bbaaef
         /* Push changes to the Logical_Flow table to database. */
    bbaaef
         const struct sbrec_logical_flow *sbflow, *next_sbflow;
    bbaaef
    diff --git a/tests/ovn.at b/tests/ovn.at
    bbaaef
    index e2565f274..3a5ecf211 100644
    bbaaef
    --- a/tests/ovn.at
    bbaaef
    +++ b/tests/ovn.at
    bbaaef
    @@ -17007,6 +17007,22 @@ ovn-nbctl --wait=sb ls-lb-add sw0 lb1
    bbaaef
     ovn-nbctl --wait=sb ls-lb-add sw1 lb1
    bbaaef
     ovn-nbctl --wait=sb lr-lb-add lr0 lb1
    bbaaef
     
    bbaaef
    +ovn-nbctl ls-add public
    bbaaef
    +ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24
    bbaaef
    +ovn-nbctl lsp-add public public-lr0
    bbaaef
    +ovn-nbctl lsp-set-type public-lr0 router
    bbaaef
    +ovn-nbctl lsp-set-addresses public-lr0 router
    bbaaef
    +ovn-nbctl lsp-set-options public-lr0 router-port=lr0-public
    bbaaef
    +
    bbaaef
    +# localnet port
    bbaaef
    +ovn-nbctl lsp-add public ln-public
    bbaaef
    +ovn-nbctl lsp-set-type ln-public localnet
    bbaaef
    +ovn-nbctl lsp-set-addresses ln-public unknown
    bbaaef
    +ovn-nbctl lsp-set-options ln-public network_name=public
    bbaaef
    +
    bbaaef
    +# schedule the gw router port to a chassis. Change the name of the chassis
    bbaaef
    +ovn-nbctl --wait=hv lrp-set-gateway-chassis lr0-public hv1 20
    bbaaef
    +
    bbaaef
     OVN_POPULATE_ARP
    bbaaef
     ovn-nbctl --wait=hv sync
    bbaaef
     
    bbaaef
    @@ -17018,6 +17034,11 @@ AT_CHECK([cat lflows.txt], [0], [dnl
    bbaaef
       table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
    bbaaef
     ])
    bbaaef
     
    bbaaef
    +ovn-sbctl dump-flows lr0 | grep ct_lb | grep priority=120 > lflows.txt
    bbaaef
    +AT_CHECK([cat lflows.txt], [0], [dnl
    bbaaef
    +  table=6 (lr_in_dnat         ), priority=120  , match=(ct.new && ip && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
    bbaaef
    +])
    bbaaef
    +
    bbaaef
     # get the svc monitor mac.
    bbaaef
     svc_mon_src_mac=`ovn-nbctl get NB_Global . options:svc_monitor_mac | \
    bbaaef
     sed s/":"//g | sed s/\"//g`
    bbaaef
    @@ -17051,5 +17072,11 @@ AT_CHECK([cat lflows.txt], [0], [dnl
    bbaaef
       table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(drop;)
    bbaaef
     ])
    bbaaef
     
    bbaaef
    +ovn-sbctl dump-flows lr0 | grep lr_in_dnat | grep priority=120 > lflows.txt
    bbaaef
    +AT_CHECK([cat lflows.txt], [0], [dnl
    bbaaef
    +  table=6 (lr_in_dnat         ), priority=120  , match=(ct.est && ip && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(ct_dnat;)
    bbaaef
    +  table=6 (lr_in_dnat         ), priority=120  , match=(ct.new && ip && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(drop;)
    bbaaef
    +])
    bbaaef
    +
    bbaaef
     OVN_CLEANUP([hv1], [hv2])
    bbaaef
     AT_CLEANUP
    bbaaef
    -- 
    bbaaef
    2.24.1
    bbaaef