get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/1525571/
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 1525571,
    "url": "http://patchwork.ozlabs.org/api/patches/1525571/",
    "web_url": "http://patchwork.ozlabs.org/project/ovn/patch/20210907224516.489604-5-blp@ovn.org/",
    "project": {
        "id": 68,
        "url": "http://patchwork.ozlabs.org/api/projects/68/",
        "name": "Open Virtual Network development",
        "link_name": "ovn",
        "list_id": "ovs-dev.openvswitch.org",
        "list_email": "ovs-dev@openvswitch.org",
        "web_url": "http://openvswitch.org/",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20210907224516.489604-5-blp@ovn.org>",
    "list_archive_url": null,
    "date": "2021-09-07T22:45:10",
    "name": "[ovs-dev,v2,04/10] ovn-northd-ddlog: Reverse order of joins for connection tracking flows.",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "53cf82a0549ef7b133caa05715e59bbce4ed9b6d",
    "submitter": {
        "id": 67603,
        "url": "http://patchwork.ozlabs.org/api/people/67603/",
        "name": "Ben Pfaff",
        "email": "blp@ovn.org"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/ovn/patch/20210907224516.489604-5-blp@ovn.org/mbox/",
    "series": [
        {
            "id": 261352,
            "url": "http://patchwork.ozlabs.org/api/series/261352/",
            "web_url": "http://patchwork.ozlabs.org/project/ovn/list/?series=261352",
            "date": "2021-09-07T22:45:06",
            "name": "3x performance improvement for ddlog with load balancer benchmark",
            "version": 2,
            "mbox": "http://patchwork.ozlabs.org/series/261352/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/1525571/comments/",
    "check": "success",
    "checks": "http://patchwork.ozlabs.org/api/patches/1525571/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<ovs-dev-bounces@openvswitch.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "dev@openvswitch.org"
        ],
        "Delivered-To": [
            "patchwork-incoming@bilbo.ozlabs.org",
            "ovs-dev@lists.linuxfoundation.org"
        ],
        "Authentication-Results": "ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org\n (client-ip=2605:bc80:3010::138; helo=smtp1.osuosl.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=<UNKNOWN>)",
        "Received": [
            "from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest\n SHA256)\n\t(No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 4H40jX1g8dz9t1s\n\tfor <incoming@patchwork.ozlabs.org>; Wed,  8 Sep 2021 08:45:51 +1000 (AEST)",
            "from localhost (localhost [127.0.0.1])\n\tby smtp1.osuosl.org (Postfix) with ESMTP id 48C6880C3F;\n\tTue,  7 Sep 2021 22:45:48 +0000 (UTC)",
            "from smtp1.osuosl.org ([127.0.0.1])\n\tby localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)\n\twith ESMTP id W08ZY_qs4lsb; Tue,  7 Sep 2021 22:45:43 +0000 (UTC)",
            "from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56])\n\tby smtp1.osuosl.org (Postfix) with ESMTPS id 76D9080C04;\n\tTue,  7 Sep 2021 22:45:41 +0000 (UTC)",
            "from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id 23D15C0028;\n\tTue,  7 Sep 2021 22:45:39 +0000 (UTC)",
            "from smtp2.osuosl.org (smtp2.osuosl.org [140.211.166.133])\n by lists.linuxfoundation.org (Postfix) with ESMTP id 33581C002B\n for <dev@openvswitch.org>; Tue,  7 Sep 2021 22:45:36 +0000 (UTC)",
            "from localhost (localhost [127.0.0.1])\n by smtp2.osuosl.org (Postfix) with ESMTP id 5EB37402B9\n for <dev@openvswitch.org>; Tue,  7 Sep 2021 22:45:35 +0000 (UTC)",
            "from smtp2.osuosl.org ([127.0.0.1])\n by localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)\n with ESMTP id e1jgSt9Zkv1T for <dev@openvswitch.org>;\n Tue,  7 Sep 2021 22:45:33 +0000 (UTC)",
            "from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net\n [217.70.183.201])\n by smtp2.osuosl.org (Postfix) with ESMTPS id 7C734402A1\n for <dev@openvswitch.org>; Tue,  7 Sep 2021 22:45:33 +0000 (UTC)",
            "(Authenticated sender: blp@ovn.org)\n by relay8-d.mail.gandi.net (Postfix) with ESMTPSA id 307AC1BF204;\n Tue,  7 Sep 2021 22:45:30 +0000 (UTC)"
        ],
        "X-Virus-Scanned": [
            "amavisd-new at osuosl.org",
            "amavisd-new at osuosl.org"
        ],
        "X-Greylist": "domain auto-whitelisted by SQLgrey-1.8.0",
        "From": "Ben Pfaff <blp@ovn.org>",
        "To": "dev@openvswitch.org",
        "Date": "Tue,  7 Sep 2021 15:45:10 -0700",
        "Message-Id": "<20210907224516.489604-5-blp@ovn.org>",
        "X-Mailer": "git-send-email 2.31.1",
        "In-Reply-To": "<20210907224516.489604-1-blp@ovn.org>",
        "References": "<20210907224516.489604-1-blp@ovn.org>",
        "MIME-Version": "1.0",
        "Cc": "Ben Pfaff <blp@ovn.org>",
        "Subject": "[ovs-dev] [PATCH ovn v2 04/10] ovn-northd-ddlog: Reverse order of\n\tjoins for connection tracking flows.",
        "X-BeenThere": "ovs-dev@openvswitch.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "<ovs-dev.openvswitch.org>",
        "List-Unsubscribe": "<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>",
        "List-Archive": "<http://mail.openvswitch.org/pipermail/ovs-dev/>",
        "List-Post": "<mailto:ovs-dev@openvswitch.org>",
        "List-Help": "<mailto:ovs-dev-request@openvswitch.org?subject=help>",
        "List-Subscribe": "<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=subscribe>",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Errors-To": "ovs-dev-bounces@openvswitch.org",
        "Sender": "\"dev\" <ovs-dev-bounces@openvswitch.org>"
    },
    "content": "DDlog evaluates rules in the order given by syntax.  The rules for\nload balancers all first evaluated a Router or Switch, then joined\nthat against the load balancers.  However, the expensive logic was\nall on the load balancers.  This meant that the load balancer logic\nwas happening many times, once per switch or router that contained\nit.  When a single load balancer was part of many switches or\nrouters, this did a lot of redundant processing.  This commit\nreverses the join order, which speeds up processing a lot.\n\nThis commit looks big because it also converts a lot of rules from\nthe FLWOR syntax to traditional Datalog-style syntax.  This is not\ncompletely needed, but the Datalog-style syntax is more versatile\n(it supports FlatMap and aggregation), so I tend to make that sort\nof change as I refactor things.\n\nSigned-off-by: Ben Pfaff <blp@ovn.org>\n---\n northd/lrouter.dl    |  11 --\n northd/ovn_northd.dl | 419 +++++++++++++++++++++----------------------\n 2 files changed, 209 insertions(+), 221 deletions(-)",
    "diff": "diff --git a/northd/lrouter.dl b/northd/lrouter.dl\nindex 17d803292..c0ec6be47 100644\n--- a/northd/lrouter.dl\n+++ b/northd/lrouter.dl\n@@ -560,17 +560,6 @@ RouterLB(router, lb) :-\n     router in &Router(.lbs = lbs),\n     var lb = FlatMap(lbs).\n \n-/* Load balancer VIPs associated with routers */\n-relation RouterLBVIP(\n-    router: Intern<Router>,\n-    lb: Intern<LoadBalancer>,\n-    vip: istring,\n-    backends: istring)\n-\n-RouterLBVIP(router, lb, vip, backends) :-\n-    RouterLB(router, lb@(&LoadBalancer{.lb = &nb::Load_Balancer{.vips = vips}})),\n-    (var vip, var backends) = FlatMap(vips).\n-\n /* Router-to-router logical port connections */\n relation RouterRouterPeer(rport1: uuid, rport2: uuid, rport2_name: istring)\n \ndiff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl\nindex 4348171ba..f678a4f50 100644\n--- a/northd/ovn_northd.dl\n+++ b/northd/ovn_northd.dl\n@@ -3232,9 +3232,7 @@ Flow(.logical_datapath = sw._uuid,\n      .io_port          = None,\n      .controller_meter = meter,\n      .stage_hint       = 0) :-\n-    sw in &Switch(),\n     LBVIPWithStatus[lbvip@&LBVIPWithStatus{.lb = lb}],\n-    sw.load_balancer.contains(lb._uuid),\n     var priority = if (lbvip.vip_port != 0) { 120 } else { 110 },\n     (var actions, var reject) = {\n         /* Store the original destination IP to be used when generating\n@@ -3256,12 +3254,14 @@ Flow(.logical_datapath = sw._uuid,\n \n         build_lb_vip_actions(lbvip, s_SWITCH_OUT_QOS_MARK(), actions0 ++ actions1)\n     },\n+    var __match = \"ct.new && \" ++ get_match_for_lb_key(lbvip.vip_addr, lbvip.vip_port, lb.protocol, false, false, false),\n+    sw in &Switch(),\n+    sw.load_balancer.contains(lb._uuid),\n     var meter = if (reject) {\n         sw.copp.get(cOPP_REJECT())\n     } else {\n         None\n-    },\n-    var __match = \"ct.new && \" ++ get_match_for_lb_key(lbvip.vip_addr, lbvip.vip_port, lb.protocol, false, false, false).\n+    }.\n \n /* Ingress Pre-Hairpin/Nat-Hairpin/Hairpin tabled (Priority 0).\n  * Packets that don't need hairpinning should continue processing.\n@@ -5657,6 +5657,26 @@ for (RouterPortNetworksIPv4Addr(.port = &RouterPort{.lrp = lrp,\n     }\n }\n \n+LogicalRouterNdFlow(.lr = r,\n+                    .lrp = Some{lrp},\n+                    .action = i\"nd_na\",\n+                    .ip = ip,\n+                    .sn_ip = false,\n+                    .mac = rEG_INPORT_ETH_ADDR(),\n+                    .extra_match = residence_check,\n+                    .drop = false,\n+                    .priority = 90,\n+                    .stage_hint = 0) :-\n+    LBVIP[lbvip@&LBVIP{.vip_key = lb_key, .lb = lb}],\n+    Some{(IPv6{var ip}, _)} = ip_address_and_port_from_lb_key(lb_key.ival()),\n+    r in &Router(),\n+    r.load_balancer.contains(lb._uuid),\n+    &RouterPort(.router = r, .lrp = lrp, .is_redirect = is_redirect),\n+    var residence_check = match (is_redirect) {\n+        true -> Some{i\"is_chassis_resident(${json_escape(chassis_redirect_name(lrp.name))})\"},\n+        false -> None\n+    }.\n+\n for (&RouterPort(.lrp = lrp,\n                  .router = router@&Router{._uuid = lr_uuid},\n                  .json_name = json_name,\n@@ -5675,22 +5695,7 @@ var residence_check = match (is_redirect) {\n                          .extra_match = residence_check,\n                          .drop = false,\n                          .priority = 90,\n-                         .stage_hint = 0);\n-    for (RouterLBVIP(.router = &Router{._uuid= lr_uuid}, .vip = vip)) {\n-        Some{(var ip_address, _)} = ip_address_and_port_from_lb_key(vip.ival()) in {\n-            IPv6{var ipv6} = ip_address in\n-            LogicalRouterNdFlow(.lr = router,\n-                                .lrp = Some{lrp},\n-                                .action = i\"nd_na\",\n-                                .ip = ipv6,\n-                                .sn_ip = false,\n-                                .mac = rEG_INPORT_ETH_ADDR(),\n-                                .extra_match = residence_check,\n-                                .drop = false,\n-                                .priority = 90,\n-                                .stage_hint = 0)\n-        }\n-    }\n+                         .stage_hint = 0)\n }\n \n /* Drop IP traffic destined to router owned IPs except if the IP is\n@@ -6659,189 +6664,184 @@ function nats_contain_vip(nats: Vec<NAT>, vip: v46_ip): bool {\n     return false\n }\n \n-/* Load balancing and packet defrag are only valid on\n- * Gateway routers or router with gateway port. */\n-for (RouterLBVIP(\n-        .router = r@&Router{._uuid = lr_uuid,\n-                            .l3dgw_ports = l3dgw_ports,\n-                            .is_gateway = is_gateway,\n-                            .nats = nats},\n-        .lb = lb,\n-        .vip = vip,\n-        .backends = backends)\n-     if not l3dgw_ports.is_empty() or is_gateway)\n-{\n-    if (backends == i\"\" and not lb.lb.options.get_bool_def(i\"reject\", false)) {\n-        for (LoadBalancerEmptyEvents(lb.lb._uuid)) {\n-            Some {(var __match, var __action)} =\n-                build_empty_lb_event_flow(vip, lb.lb) in\n-            Flow(.logical_datapath = lr_uuid,\n-                 .stage            = s_ROUTER_IN_DNAT(),\n-                 .priority         = 130,\n-                 .__match          = __match,\n-                 .actions          = __action,\n-                 .io_port          = None,\n-                 .controller_meter = r.copp.get(cOPP_EVENT_ELB()),\n-                 .stage_hint       = stage_hint(lb.lb._uuid))\n-        }\n-    };\n-\n-    /* A set to hold all ips that need defragmentation and tracking. */\n+/* If there are any load balancing rules, we should send\n+ * the packet to conntrack for defragmentation and\n+ * tracking.  This helps with two things.\n+ *\n+ * 1. With tracking, we can send only new connections to\n+ *    pick a DNAT ip address from a group.\n+ * 2. If there are L4 ports in load balancing rules, we\n+ *    need the defragmentation to match on L4 ports.\n+ *\n+ * One of these flows must be created for each unique LB VIP address.\n+ * We create one for each VIP:port pair; flows with the same IP and\n+ * different port numbers will produce identical flows that will\n+ * get merged by DDlog. */\n+Flow(.logical_datapath = r._uuid,\n+     .stage            = s_ROUTER_IN_DEFRAG(),\n+     .priority         = prio,\n+     .__match          = __match,\n+     .actions          = __actions,\n+     .stage_hint       = 0,\n+     .io_port          = None,\n+     .controller_meter = None) :-\n+    LBVIP[lbvip@&LBVIP{.vip_key = lb_key, .lb = lb}],\n+    Some{(var ip, var port)} = ip_address_and_port_from_lb_key(lb_key.ival()),\n+    var prio = if (port != 0) { 110 } else { 100 },\n+    var proto = match (lb.protocol) {\n+        Some{proto} -> proto,\n+        _ -> i\"tcp\"\n+    },\n+    var proto_match = if (port != 0) { \" && ${proto}\" } else { \"\" },\n+    var __match = (\"ip && ${ip.ipX()}.dst == ${ip}\" ++ proto_match).intern(),\n+    var actions1 = \"${ip.xxreg()}${rEG_NEXT_HOP()} = ${ip}; \",\n+    var actions2 =\n+        if (port != 0) {\n+            \"${rEG_ORIG_TP_DPORT_ROUTER()} = ${proto}.dst; ct_dnat;\"\n+        } else {\n+            \"ct_dnat;\"\n+        },\n+    var __actions = (actions1 ++ actions2).intern(),\n+    r in &Router(),\n+    not r.l3dgw_ports.is_empty() or r.is_gateway,\n+    r.load_balancer.contains(lb._uuid).\n \n-    /* vip contains IP:port or just IP. */\n-    Some{(var ip_address, var port)} = ip_address_and_port_from_lb_key(vip.ival()) in\n-    var ipX = ip_address.ipX() in\n-    var proto = match (lb.lb.protocol) {\n+/* Higher priority rules are added for load-balancing in DNAT\n+ * table.  For every match (on a VIP[:port]), we add two flows\n+ * via add_router_lb_flow().  One flow is for specific matching\n+ * on ct.new with an action of \"ct_lb($targets);\".  The other\n+ * flow is for ct.est with an action of \"ct_dnat;\". */\n+Flow(.logical_datapath = r._uuid,\n+     .stage            = s_ROUTER_IN_DNAT(),\n+     .priority         = prio,\n+     .__match          = __match.intern(),\n+     .actions          = actions,\n+     .stage_hint       = 0,\n+     .io_port          = None,\n+     .controller_meter = None) :-\n+    LBVIP[lbvip@&LBVIP{.vip_key = lb_key, .lb = lb}],\n+    Some{(var ip, var port)} = ip_address_and_port_from_lb_key(lb_key.ival()),\n+    var proto = match (lb.protocol) {\n         Some{proto} -> proto,\n         _ -> i\"tcp\"\n-    } in {\n-        /* If there are any load balancing rules, we should send\n-         * the packet to conntrack for defragmentation and\n-         * tracking.  This helps with two things.\n-         *\n-         * 1. With tracking, we can send only new connections to\n-         *    pick a DNAT ip address from a group.\n-         * 2. If there are L4 ports in load balancing rules, we\n-         *    need the defragmentation to match on L4 ports. */\n-        var match1 = \"ip && ${ipX}.dst == ${ip_address}\" in\n-        (var prio, var match2) =\n-            if (port != 0) {\n-                (110, \" && ${proto}\")\n-            } else {\n-                (100, \"\")\n-            } in\n-        var __match = match1 ++ match2 in\n-        var xx = ip_address.xxreg() in\n-        var actions1 = \"${xx}${rEG_NEXT_HOP()} = ${ip_address}; \" in\n-        var actions2 =\n-            if (port != 0) {\n-                \"${rEG_ORIG_TP_DPORT_ROUTER()} = ${proto}.dst; ct_dnat;\"\n-            } else {\n-                \"ct_dnat;\"\n-            } in\n-        var __actions = actions1 ++ actions2 in\n-        /* One of these flows must be created for each unique LB VIP address.\n-         * We create one for each VIP:port pair; flows with the same IP and\n-         * different port numbers will produce identical flows that will\n-         * get merged by DDlog. */\n-        Flow(.logical_datapath = lr_uuid,\n-             .stage            = s_ROUTER_IN_DEFRAG(),\n-             .priority         = prio,\n-             .__match          = __match.intern(),\n-             .actions          = __actions.intern(),\n-             .stage_hint       = 0,\n-             .io_port          = None,\n-             .controller_meter = None);\n+    },\n+    var match1 = \"${ip.ipX()} && ${ip.xxreg()}${rEG_NEXT_HOP()} == ${ip}\",\n+    var match2 = if (port != 0) {\n+        \" && ${proto} && ${rEG_ORIG_TP_DPORT_ROUTER()} == ${port}\"\n+    } else {\n+        \"\"\n+    },\n+    var prio = if (port != 0) { 120 } else { 110 },\n+    var match0 = \"ct.est && \" ++ match1 ++ match2 ++ \" && ct_label.natted == 1\",\n+    r in &Router(),\n+    not r.l3dgw_ports.is_empty() or r.is_gateway,\n+    r.load_balancer.contains(lb._uuid),\n+    var __match = match0 ++ match ((r.l3dgw_ports.nth(0), lbvip.backend_ips != i\"\" or lb.options.get_bool_def(i\"reject\", false))) {\n+            (Some {var gw_port}, true) -> \" && is_chassis_resident(${json_escape(chassis_redirect_name(gw_port.name))})\",\n+            _ -> \"\"\n+        },\n+    var actions = match (snat_for_lb(r.options, lb)) {\n+        SkipSNAT -> i\"flags.skip_snat_for_lb = 1; next;\",\n+        ForceSNAT -> i\"flags.force_snat_for_lb = 1; next;\",\n+        _ -> i\"next;\"\n+    }.\n \n-        /* Higher priority rules are added for load-balancing in DNAT\n-         * table.  For every match (on a VIP[:port]), we add two flows\n-         * via add_router_lb_flow().  One flow is for specific matching\n-         * on ct.new with an action of \"ct_lb($targets);\".  The other\n-         * flow is for ct.est with an action of \"ct_dnat;\". */\n-        var xx = ip_address.xxreg() in\n-        var match1 = \"${ipX} && ${xx}${rEG_NEXT_HOP()} == ${ip_address}\" in\n-        (var prio, var match2) =\n-            if (port != 0) {\n-                (120, \" && ${proto} && ${rEG_ORIG_TP_DPORT_ROUTER()} == ${port}\")\n-            } else {\n-                (110, \"\")\n-            } in\n-        var __match = match1 ++ match2 ++\n-            match ((l3dgw_ports.nth(0), backends != i\"\" or lb.lb.options.get_bool_def(i\"reject\", false))) {\n-                (Some{gw_port}, true) -> \" && is_chassis_resident(${json_escape(chassis_redirect_name(gw_port.name))})\",\n-                _ -> \"\"\n-            } in\n-        var snat_for_lb = snat_for_lb(r.options, lb.lb) in\n-        {\n-            /* A match and actions for established connections. */\n-            var est_match = \"ct.est && \" ++ match1 ++ match2 ++ \" && ct_label.natted == 1\" ++\n-                match ((l3dgw_ports.nth(0), backends != i\"\" or lb.lb.options.get_bool_def(i\"reject\", false))) {\n-                    (Some {var gw_port}, true) -> \" && is_chassis_resident(${json_string_escape(chassis_redirect_name(gw_port.name))})\",\n-                    _ -> \"\"\n-                } in\n-            var actions =\n-                match (snat_for_lb) {\n-                    SkipSNAT -> i\"flags.skip_snat_for_lb = 1; next;\",\n-                    ForceSNAT -> i\"flags.force_snat_for_lb = 1; next;\",\n-                    _ -> i\"next;\"\n-                } in\n-            Flow(.logical_datapath = lr_uuid,\n-                 .stage            = s_ROUTER_IN_DNAT(),\n-                 .priority         = prio,\n-                 .__match          = est_match.intern(),\n-                 .actions          = actions,\n-                 .stage_hint       = 0,\n-                 .io_port          = None,\n-                 .controller_meter = None);\n+/* The load balancer vip is also present in the NAT entries.\n+ * So add a high priority lflow to advance the the packet\n+ * destined to the vip (and the vip port if defined)\n+ * in the S_ROUTER_IN_UNSNAT stage.\n+ * There seems to be an issue with ovs-vswitchd. When the new\n+ * connection packet destined for the lb vip is received,\n+ * it is dnat'ed in the S_ROUTER_IN_DNAT stage in the dnat\n+ * conntrack zone. For the next packet, if it goes through\n+ * unsnat stage, the conntrack flags are not set properly, and\n+ * it doesn't hit the established state flows in\n+ * S_ROUTER_IN_DNAT stage. */\n+Flow(.logical_datapath = r._uuid,\n+     .stage            = s_ROUTER_IN_UNSNAT(),\n+     .priority         = 120,\n+     .__match          = __match,\n+     .actions          = i\"next;\",\n+     .stage_hint       = stage_hint(lb._uuid),\n+     .io_port          = None,\n+     .controller_meter = None) :-\n+    LBVIP[lbvip@&LBVIP{.vip_key = lb_key, .lb = lb}],\n+    Some{(var ip, var port)} = ip_address_and_port_from_lb_key(lb_key.ival()),\n+    var proto = match (lb.protocol) {\n+        Some{proto} -> proto,\n+        _ -> i\"tcp\"\n+    },\n+    var port_match = if (port != 0) { \" && ${proto}.dst == ${port}\" } else { \"\" },\n+    var __match = (\"${ip.ipX()} && ${ip.ipX()}.dst == ${ip} && ${proto}\" ++\n+                   port_match).intern(),\n+    r in &Router(),\n+    not r.l3dgw_ports.is_empty() or r.is_gateway,\n+    r.load_balancer.contains(lb._uuid),\n+    nats_contain_vip(r.nats, ip).\n \n-            if (nats_contain_vip(nats, ip_address)) {\n-                /* The load balancer vip is also present in the NAT entries.\n-                 * So add a high priority lflow to advance the the packet\n-                 * destined to the vip (and the vip port if defined)\n-                 * in the S_ROUTER_IN_UNSNAT stage.\n-                 * There seems to be an issue with ovs-vswitchd. When the new\n-                 * connection packet destined for the lb vip is received,\n-                 * it is dnat'ed in the S_ROUTER_IN_DNAT stage in the dnat\n-                 * conntrack zone. For the next packet, if it goes through\n-                 * unsnat stage, the conntrack flags are not set properly, and\n-                 * it doesn't hit the established state flows in\n-                 * S_ROUTER_IN_DNAT stage. */\n-                var match3 = \"${ipX} && ${ipX}.dst == ${ip_address} && ${proto}\" ++\n-                             if (port != 0) { \" && ${proto}.dst == ${port}\" }\n-                             else { \"\" } in\n-                Flow(.logical_datapath = lr_uuid,\n-                     .stage            = s_ROUTER_IN_UNSNAT(),\n-                     .priority         = 120,\n-                     .__match          = match3.intern(),\n-                     .actions          = i\"next;\",\n-                     .stage_hint       = stage_hint(lb.lb._uuid),\n-                     .io_port          = None,\n-                     .controller_meter = None)\n-            };\n+/* Add logical flows to UNDNAT the load balanced reverse traffic in\n+ * the router egress pipleine stage - S_ROUTER_OUT_UNDNAT if the logical\n+ * router has a gateway router port associated.\n+ */\n+Flow(.logical_datapath = r._uuid,\n+     .stage            = s_ROUTER_OUT_UNDNAT(),\n+     .priority         = 120,\n+     .__match          = __match,\n+     .actions          = action,\n+     .stage_hint       = stage_hint(lb._uuid),\n+     .io_port          = None,\n+     .controller_meter = None) :-\n+    LBVIP[lbvip@&LBVIP{.vip_key = lb_key, .lb = lb, .backend_ips = backends}],\n+    Some{(var ip, var port)} = ip_address_and_port_from_lb_key(lb_key.ival()),\n+    var proto = match (lb.protocol) {\n+        Some{proto} -> proto,\n+        _ -> i\"tcp\"\n+    },\n+    var conds = {\n+        var conds = vec_empty();\n+        for (ip_str in backends.split(\",\")) {\n+            match (ip_address_and_port_from_lb_key(ip_str)) {\n+                None -> () /* FIXME: put a break here */,\n+                Some{(ip_, port_)} -> conds.push(\n+                    \"(${ip_.ipX()}.src == ${ip_}\" ++\n+                    if (port_ != 0) {\n+                        \" && ${proto}.src == ${port_})\"\n+                    } else {\n+                        \")\"\n+                    })\n+            }\n+        };\n+        conds.join(\" || \")\n+    },\n+    conds != \"\",\n+    r in &Router(),\n+    Some{var gwport} = r.l3dgw_ports.nth(0),\n+    r.load_balancer.contains(lb._uuid),\n+    var __match =\n+        i\"${ip.ipX()} && (${conds}) && \"\n+        \"outport == ${json_escape(gwport.name)} && \"\n+        \"is_chassis_resident(${json_escape(chassis_redirect_name(gwport.name))})\",\n+    var action = match (snat_for_lb(r.options, lb)) {\n+            SkipSNAT -> i\"flags.skip_snat_for_lb = 1; ct_dnat;\",\n+            ForceSNAT -> i\"flags.force_snat_for_lb = 1; ct_dnat;\",\n+            _ -> i\"ct_dnat;\"\n+        }.\n \n-            Some{var gwport} = l3dgw_ports.nth(0) in\n-            /* Add logical flows to UNDNAT the load balanced reverse traffic in\n-             * the router egress pipleine stage - S_ROUTER_OUT_UNDNAT if the logical\n-             * router has a gateway router port associated.\n-             */\n-            var conds = {\n-                var conds = vec_empty();\n-                for (ip_str in backends.split(\",\")) {\n-                    match (ip_address_and_port_from_lb_key(ip_str)) {\n-                        None -> () /* FIXME: put a break here */,\n-                        Some{(ip_address_, port_)} -> conds.push(\n-                            \"(${ipX}.src == ${ip_address_}\" ++\n-                            if (port_ != 0) {\n-                                \" && ${proto}.src == ${port_})\"\n-                            } else {\n-                                \")\"\n-                            })\n-                    }\n-                };\n-                conds\n-            } in\n-            not conds.is_empty() in\n-            var undnat_match =\n-                \"${ip_address.ipX()} && (\" ++ conds.join(\" || \") ++\n-                \") && outport == ${json_escape(gwport.name)} && \"\n-                \"is_chassis_resident(${json_escape(chassis_redirect_name(gwport.name))})\" in\n-            var action =\n-                match (snat_for_lb) {\n-                    SkipSNAT -> i\"flags.skip_snat_for_lb = 1; ct_dnat;\",\n-                    ForceSNAT -> i\"flags.force_snat_for_lb = 1; ct_dnat;\",\n-                    _ -> i\"ct_dnat;\"\n-                } in\n-            Flow(.logical_datapath = lr_uuid,\n-                 .stage            = s_ROUTER_OUT_UNDNAT(),\n-                 .priority         = 120,\n-                 .__match          = undnat_match.intern(),\n-                 .actions          = action,\n-                 .stage_hint       = stage_hint(lb.lb._uuid),\n-                 .io_port          = None,\n-                 .controller_meter = None)\n-        }\n-    }\n-}\n+Flow(.logical_datapath = r._uuid,\n+     .stage            = s_ROUTER_IN_DNAT(),\n+     .priority         = 130,\n+     .__match          = __match,\n+     .actions          = __action,\n+     .io_port          = None,\n+     .controller_meter = r.copp.get(cOPP_EVENT_ELB()),\n+     .stage_hint       = stage_hint(lb._uuid)) :-\n+    LBVIP[lbvip@&LBVIP{.vip_key = lb_key, .lb = lb, .backend_ips = i\"\"}],\n+    not lb.options.get_bool_def(i\"reject\", false),\n+    LoadBalancerEmptyEvents(lb._uuid),\n+    Some {(var __match, var __action)} = build_empty_lb_event_flow(lb_key, lb),\n+    r in &Router(),\n+    not r.l3dgw_ports.is_empty() or r.is_gateway,\n+    r.load_balancer.contains(lb._uuid).\n \n /* Higher priority rules are added for load-balancing in DNAT\n  * table.  For every match (on a VIP[:port]), we add two flows\n@@ -6856,24 +6856,23 @@ Flow(.logical_datapath = r._uuid,\n      .io_port          = None,\n      .controller_meter = meter,\n      .stage_hint       = 0) :-\n+    LBVIPWithStatus[lbvip@&LBVIPWithStatus{.lb = lb}],\n+    var priority = if (lbvip.vip_port != 0) 120 else 110,\n+    (var actions0, var reject) = build_lb_vip_actions(lbvip, s_ROUTER_OUT_SNAT(), \"\"),\n+    var match0 = \"ct.new && \" ++\n+        get_match_for_lb_key(lbvip.vip_addr, lbvip.vip_port, lb.protocol, true, true, true),\n     r in &Router(),\n     r.l3dgw_ports.len() > 0 or r.is_gateway,\n-    LBVIPWithStatus[lbvip@&LBVIPWithStatus{.lb = lb}],\n     r.load_balancer.contains(lb._uuid),\n-    var __match\n-        = \"ct.new && \" ++\n-          get_match_for_lb_key(lbvip.vip_addr, lbvip.vip_port, lb.protocol, true, true, true) ++\n-          match (r.l3dgw_ports.nth(0)) {\n-              Some{gw_port} -> \" && is_chassis_resident(${json_escape(chassis_redirect_name(gw_port.name))})\",\n-              _ -> \"\"\n-          },\n-    var priority = if (lbvip.vip_port != 0) 120 else 110,\n-    var force_snat = match (snat_for_lb(r.options, lb)) {\n-        SkipSNAT -> \"flags.skip_snat_for_lb = 1; \",\n-        ForceSNAT -> \"flags.force_snat_for_lb = 1; \",\n+    var actions = match ((reject, snat_for_lb(r.options, lb))) {\n+        (false, SkipSNAT) -> \"flags.skip_snat_for_lb = 1; \",\n+        (false, ForceSNAT) -> \"flags.force_snat_for_lb = 1; \",\n         _ -> \"\"\n+    } ++ actions0,\n+    var __match = match (r.l3dgw_ports.nth(0)) {\n+        Some{gw_port} -> \"${match0} && is_chassis_resident(${json_escape(chassis_redirect_name(gw_port.name))})\",\n+        _ -> match0\n     },\n-    (var actions, var reject) = build_lb_vip_actions(lbvip, s_ROUTER_OUT_SNAT(), force_snat),\n     var meter = if (reject) {\n         r.copp.get(cOPP_REJECT())\n     } else {\n",
    "prefixes": [
        "ovs-dev",
        "v2",
        "04/10"
    ]
}