[ovs-dev] [PATCH ovn v2 1/2] northd: Optimize ct nat for load balancer traffic.

Mark Gray mark.d.gray at redhat.com
Tue Apr 20 07:29:17 UTC 2021


On 19/04/2021 21:51, Numan Siddique wrote:
> On Fri, Apr 16, 2021 at 4:52 AM Mark Gray <mark.d.gray at redhat.com> wrote:
>>
>> I noticed some tests failing but I think the ddlog tests look flakey as
>> I saw other failures without these patches.
>>
>> I did my best to review the ddlog part but I am not familiar with ddlog
> 
> Thanks for the reviews.  Please see below for few comments.
> 
> 
>>
>> On 12/04/2021 15:41, numans at ovn.org wrote:
>>> From: Numan Siddique <numans at ovn.org>
>>>
>>> For a load balancer traffic destined to VIP, we do the below actions
>>> related to conntrack in the ingress logical switch pipeline.
>>>   1.  Send the packet to conntrack - ct()
>>>   2a. if ct.new then ct_lb(backends)
>>>   2b. if ct.est then ct(nat)
>>>
>>> The step 2b is unnecessary and can be removed to avoid another
>>> recirculation.
>>>
>>> This patch improves this by doing the below.
>>>
>>> For a load balancer traffic destined to VIP, we will now do
>>>   1.   ct(nat)
>>>   2a.  if ct.new - ct_lb(backends)
>>>   2b.  if ct.est, no further action related to conntrack.
>>>
>>> For non load balancer connection traffic, we will now do
>>>   1.  ct(nat)
>>>   2a. if ct.new then ct(commit)
>>>   2b  if ct.est, no further action related to conntrack.
>>>
>>> The same improvement is done for the egress logical switch
>>> pipeline.  The stages - ls_in_lb and ls_out_lb are removed
>>> since these stages are no longer needed.
>>>
>>> Signed-off-by: Numan Siddique <numans at ovn.org>
>>> ---
>>>  northd/ovn-northd.8.xml | 150 ++++++++++++-----------
>>>  northd/ovn-northd.c     | 154 +++++++++--------------
>>>  northd/ovn_northd.dl    | 184 ++++++++++------------------
>>>  tests/ovn-northd.at     | 265 ++++++++++++++++++++++++++++++----------
>>>  tests/ovn.at            | 177 ++++++++++++++-------------
>>>  5 files changed, 495 insertions(+), 435 deletions(-)
>>>
>>> diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
>>> index a62f5c057a..6b295c8e63 100644
>>> --- a/northd/ovn-northd.8.xml
>>> +++ b/northd/ovn-northd.8.xml
>>> @@ -443,10 +443,11 @@
>>>        addresses (and ports) are configured in <code>OVN_Northbound</code>
>>>        database for alogical switch datapath, a priority-100 flow is added
>>
>> You did not introduce it^ but perhaps you could fix.
>>
>> s/alogical/a logical
> 
> Ack
> 
>>
>>>        with the match <code>ip</code> to match on IP packets and sets the action
>>> -      <code>reg0[0] = 1; next;</code> to act as a hint for table
>>> +      <code>reg0[2] = 1; next;</code> to act as a hint for table
>>>        <code>Pre-stateful</code> to send IP packets to the connection tracker
>>> -      for packet de-fragmentation before eventually advancing to ingress
>>> -      table <code>LB</code>.
>>> +      for packet de-fragmentation (and to possibly do dnat for already
>>
>> Maybe capitalize to "DNAT" to keep consistent with the rest of the man page
> Ack
> 
>>> +      established load balanced traffic) before eventually advancing to ingress
>>> +      table <code>Stateful</code>.
>>>        If controller_event has been enabled and load balancing rules with
>>>        empty backends have been added in <code>OVN_Northbound</code>, a 130 flow
>>>        is added to trigger ovn-controller events whenever the chassis receives a
>>> @@ -504,11 +505,38 @@
>>>      <p>
>>>        This table prepares flows for all possible stateful processing
>>>        in next tables.  It contains a priority-0 flow that simply moves
>>> -      traffic to the next table.  A priority-100 flow sends the packets to
>>> -      connection tracker based on a hint provided by the previous tables
>>> -      (with a match for <code>reg0[0] == 1</code>) by using the
>>> -      <code>ct_next;</code> action.
>>> +      traffic to the next table.
>>>      </p>
>>> +    <ul>
>>> +      <li>
>>> +        Priority-120 flows that send the packets to connection tracker using
>>> +        <code>ct_lb;</code> as the action so that the already established
>>> +        traffic destined to the load balancer VIP gets dnatted based on a hint
>>
>> DNATted
> 
> Ack.
> 
>>> +        provided by the previous tables (with a match
>>> +        for <code>reg0[2] == 1</code> and on supported load balancer protocols
>>> +        and address families).  For IPv4 traffic the flows also load the
>>> +        original destination IP and transport port in registers
>>> +        <code>reg1</code> and <code>reg2</code>.  For IPv6 traffic the flows
>>> +        also load the original destination IP and transport port in
>>> +        registers <code>xxreg1</code> and <code>reg2</code>.
>>> +      </li>
>>> +
>>> +      <li>
>>> +         A priority-110 flow sends the packets to connection tracker based
>>> +         on a hint provided by the previous tables
>>> +         (with a match for <code>reg0[2] == 1</code>) by using the
>>> +         <code>ct_lb;</code> action.  This flow is added to handle
>>> +         the traffic for load balancer VIPs whose protocol is not defined
>>> +         (mainly for ICMP traffic).
>>> +      </li>
>>> +
>>> +      <li>
>>> +         A priority-100 flow sends the packets to connection tracker based
>>> +         on a hint provided by the previous tables
>>> +         (with a match for <code>reg0[0] == 1</code>) by using the
>>> +         <code>ct_next;</code> action.
>>> +      </li>
>>> +    </ul>
>>>
>>>      <h3>Ingress Table 8: <code>from-lport</code> ACL hints</h3>
>>>
>>> @@ -743,33 +771,7 @@
>>>        </li>
>>>      </ul>
>>>
>>> -    <h3>Ingress Table 12: LB</h3>
>>> -
>>> -    <p>
>>> -      It contains a priority-0 flow that simply moves traffic to the next
>>> -      table.
>>> -    </p>
>>> -
>>> -    <p>
>>> -      A priority-65535 flow with the match
>>> -      <code>inport == <var>I</var></code> for all logical switch
>>> -      datapaths to move traffic to the next table. Where <var>I</var>
>>> -      is the peer of a logical router port. This flow is added to
>>> -      skip the connection tracking of packets which enter from
>>> -      logical router datapath to logical switch datapath.
>>> -    </p>
>>> -
>>> -    <p>
>>> -      For established connections a priority 65534 flow matches on
>>> -      <code>ct.est && !ct.rel && !ct.new &&
>>> -      !ct.inv</code> and sets an action <code>reg0[2] = 1; next;</code> to act
>>> -      as a hint for table <code>Stateful</code> to send packets through
>>> -      connection tracker to NAT the packets.  (The packet will automatically
>>> -      get DNATed to the same IP address as the first packet in that
>>> -      connection.)
>>> -    </p>
>>> -
>>> -    <h3>Ingress Table 13: Stateful</h3>
>>> +    <h3>Ingress Table 12: Stateful</h3>
>>>
>>>      <ul>
>>>        <li>
>>> @@ -826,23 +828,12 @@
>>>          <code>ct_commit; next;</code> action based on a hint provided by
>>>          the previous tables (with a match for <code>reg0[1] == 1</code>).
>>>        </li>
>>> -      <li>
>>> -        Priority-100 flows that send the packets to connection tracker using
>>> -        <code>ct_lb;</code> as the action based on a hint provided by the
>>> -        previous tables (with a match for <code>reg0[2] == 1</code> and
>>> -        on supported load balancer protocols and address families).
>>> -        For IPv4 traffic the flows also load the original destination
>>> -        IP and transport port in registers <code>reg1</code> and
>>> -        <code>reg2</code>.  For IPv6 traffic the flows also load the original
>>> -        destination IP and transport port in registers <code>xxreg1</code> and
>>> -        <code>reg2</code>.
>>> -      </li>
>>>        <li>
>>>          A priority-0 flow that simply moves traffic to the next table.
>>>        </li>
>>>      </ul>
>>>
>>> -    <h3>Ingress Table 14: Pre-Hairpin</h3>
>>> +    <h3>Ingress Table 13: Pre-Hairpin</h3>
>>>      <ul>
>>>        <li>
>>>          If the logical switch has load balancer(s) configured, then a
>>> @@ -860,7 +851,7 @@
>>>        </li>
>>>      </ul>
>>>
>>> -    <h3>Ingress Table 15: Nat-Hairpin</h3>
>>> +    <h3>Ingress Table 14: Nat-Hairpin</h3>
>>>      <ul>
>>>        <li>
>>>           If the logical switch has load balancer(s) configured, then a
>>> @@ -895,7 +886,7 @@
>>>        </li>
>>>      </ul>
>>>
>>> -    <h3>Ingress Table 16: Hairpin</h3>
>>> +    <h3>Ingress Table 15: Hairpin</h3>
>>>      <ul>
>>>        <li>
>>>          A priority-1 flow that hairpins traffic matched by non-default
>>> @@ -908,7 +899,7 @@
>>>        </li>
>>>      </ul>
>>>
>>> -    <h3>Ingress Table 17: ARP/ND responder</h3>
>>> +    <h3>Ingress Table 16: ARP/ND responder</h3>
>>>
>>>      <p>
>>>        This table implements ARP/ND responder in a logical switch for known
>>> @@ -1198,7 +1189,7 @@ output;
>>>        </li>
>>>      </ul>
>>>
>>> -    <h3>Ingress Table 18: DHCP option processing</h3>
>>> +    <h3>Ingress Table 17: DHCP option processing</h3>
>>>
>>>      <p>
>>>        This table adds the DHCPv4 options to a DHCPv4 packet from the
>>> @@ -1259,7 +1250,7 @@ next;
>>>        </li>
>>>      </ul>
>>>
>>> -    <h3>Ingress Table 19: DHCP responses</h3>
>>> +    <h3>Ingress Table 18: DHCP responses</h3>
>>>
>>>      <p>
>>>        This table implements DHCP responder for the DHCP replies generated by
>>> @@ -1340,7 +1331,7 @@ output;
>>>        </li>
>>>      </ul>
>>>
>>> -    <h3>Ingress Table 20 DNS Lookup</h3>
>>> +    <h3>Ingress Table 19 DNS Lookup</h3>
>>>
>>>      <p>
>>>        This table looks up and resolves the DNS names to the corresponding
>>> @@ -1369,7 +1360,7 @@ reg0[4] = dns_lookup(); next;
>>>        </li>
>>>      </ul>
>>>
>>> -    <h3>Ingress Table 21 DNS Responses</h3>
>>> +    <h3>Ingress Table 20 DNS Responses</h3>
>>>
>>>      <p>
>>>        This table implements DNS responder for the DNS replies generated by
>>> @@ -1404,7 +1395,7 @@ output;
>>>        </li>
>>>      </ul>
>>>
>>> -    <h3>Ingress table 22 External ports</h3>
>>> +    <h3>Ingress table 21 External ports</h3>
>>>
>>>      <p>
>>>        Traffic from the <code>external</code> logical ports enter the ingress
>>> @@ -1447,7 +1438,7 @@ output;
>>>        </li>
>>>      </ul>
>>>
>>> -    <h3>Ingress Table 23 Destination Lookup</h3>
>>> +    <h3>Ingress Table 22 Destination Lookup</h3>
>>>
>>>      <p>
>>>        This table implements switching behavior.  It contains these logical
>>> @@ -1673,9 +1664,11 @@ output;
>>>        Moreover it contains a priority-110 flow to move IPv6 Neighbor Discovery
>>>        traffic to the next table. If any load balancing rules exist for the
>>>        datapath, a priority-100 flow is added with a match of <code>ip</code>
>>> -      and action of <code>reg0[0] = 1; next;</code> to act as a hint for
>>> +      and action of <code>reg0[2] = 1; next;</code> to act as a hint for
>>>        table <code>Pre-stateful</code> to send IP packets to the connection
>>> -      tracker for packet de-fragmentation.
>>> +      tracker for packet de-fragmentation and possibly dnat the destination
>>
>> Maybe DNAT
> 
> Ack
> 
>>> +      VIP to one of the selected backend for already commited load balanced
>>> +      traffic.
>>>      </p>
>>>
>>>      <p>
>>> @@ -1717,20 +1710,39 @@ output;
>>>      <h3>Egress Table 2: Pre-stateful</h3>
>>>
>>>      <p>
>>> -      This is similar to ingress table <code>Pre-stateful</code>.
>>> +      This is similar to ingress table <code>Pre-stateful</code>.  This table
>>> +      adds the below 3 logical flows.
>>>      </p>
>>>
>>> -    <h3>Egress Table 3: LB</h3>
>>> -    <p>
>>> -      This is similar to ingress table <code>LB</code>.
>>> -    </p>
>>> +    <ul>
>>> +      <li>
>>> +        A Priority-120 flow that send the packets to connection tracker using
>>> +        <code>ct_lb;</code> as the action so that the already established
>>> +        traffic gets undnatted from the backend IP to the load balancer VIP
>>
>> I see "unDNATted" as the spelling elsewhere. Maybe you could change to
>> keep consistent.
> 
> Ack
> 
>>
>>> +        based on a hint provided by the previous tables with a match
>>> +        for <code>reg0[2] == 1</code>.  If the packet was not dnatted earlier,
>>
>> Maybe DNATted
> 
> Ack
> 
>>> +        then <code>ct_lb</code> functions like <code>ct_next</code>.
>>> +      </li>
>>> +
>>> +      <li>
>>> +        A priority-100 flow sends the packets to connection tracker based
>>> +        on a hint provided by the previous tables
>>> +        (with a match for <code>reg0[0] == 1</code>) by using the
>>> +        <code>ct_next;</code> action.
>>> +      </li>
>>> +
>>> +      <li>
>>> +        A priority-0 flow that matches all packets to advance to the next
>>> +        table.
>>> +      </li>
>>> +    </ul>
>>>
>>> -    <h3>Egress Table 4: <code>from-lport</code> ACL hints</h3>
>>> +    <h3>Egress Table 3: <code>from-lport</code> ACL hints</h3>
>>>      <p>
>>>        This is similar to ingress table <code>ACL hints</code>.
>>>      </p>
>>>
>>> -    <h3>Egress Table 5: <code>to-lport</code> ACLs</h3>
>>> +    <h3>Egress Table 4: <code>to-lport</code> ACLs</h3>
>>>
>>>      <p>
>>>        This is similar to ingress table <code>ACLs</code> except for
>>> @@ -1767,28 +1779,28 @@ output;
>>>        </li>
>>>      </ul>
>>>
>>> -    <h3>Egress Table 6: <code>to-lport</code> QoS Marking</h3>
>>> +    <h3>Egress Table 5: <code>to-lport</code> QoS Marking</h3>
>>>
>>>      <p>
>>>        This is similar to ingress table <code>QoS marking</code> except
>>>        they apply to <code>to-lport</code> QoS rules.
>>>      </p>
>>>
>>> -    <h3>Egress Table 7: <code>to-lport</code> QoS Meter</h3>
>>> +    <h3>Egress Table 6: <code>to-lport</code> QoS Meter</h3>
>>>
>>>      <p>
>>>        This is similar to ingress table <code>QoS meter</code> except
>>>        they apply to <code>to-lport</code> QoS rules.
>>>      </p>
>>>
>>> -    <h3>Egress Table 8: Stateful</h3>
>>> +    <h3>Egress Table 7: Stateful</h3>
>>>
>>>      <p>
>>>        This is similar to ingress table <code>Stateful</code> except that
>>>        there are no rules added for load balancing new connections.
>>>      </p>
>>>
>>> -    <h3>Egress Table 9: Egress Port Security - IP</h3>
>>> +    <h3>Egress Table 8: Egress Port Security - IP</h3>
>>>
>>>      <p>
>>>        This is similar to the port security logic in table
>>> @@ -1798,7 +1810,7 @@ output;
>>>        <code>ip4.src</code> and <code>ip6.src</code>
>>>      </p>
>>>
>>> -    <h3>Egress Table 10: Egress Port Security - L2</h3>
>>> +    <h3>Egress Table 9: Egress Port Security - L2</h3>
>>>
>>>      <p>
>>>        This is similar to the ingress port security logic in ingress table
>>> diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
>>> index 9839b8c4f5..637d3a10a9 100644
>>> --- a/northd/ovn-northd.c
>>> +++ b/northd/ovn-northd.c
>>> @@ -147,32 +147,30 @@ enum ovn_stage {
>>>      PIPELINE_STAGE(SWITCH, IN,  ACL,            9, "ls_in_acl")           \
>>>      PIPELINE_STAGE(SWITCH, IN,  QOS_MARK,      10, "ls_in_qos_mark")      \
>>>      PIPELINE_STAGE(SWITCH, IN,  QOS_METER,     11, "ls_in_qos_meter")     \
>>> -    PIPELINE_STAGE(SWITCH, IN,  LB,            12, "ls_in_lb")            \
>>> -    PIPELINE_STAGE(SWITCH, IN,  STATEFUL,      13, "ls_in_stateful")      \
>>> -    PIPELINE_STAGE(SWITCH, IN,  PRE_HAIRPIN,   14, "ls_in_pre_hairpin")   \
>>> -    PIPELINE_STAGE(SWITCH, IN,  NAT_HAIRPIN,   15, "ls_in_nat_hairpin")   \
>>> -    PIPELINE_STAGE(SWITCH, IN,  HAIRPIN,       16, "ls_in_hairpin")       \
>>> -    PIPELINE_STAGE(SWITCH, IN,  ARP_ND_RSP,    17, "ls_in_arp_rsp")       \
>>> -    PIPELINE_STAGE(SWITCH, IN,  DHCP_OPTIONS,  18, "ls_in_dhcp_options")  \
>>> -    PIPELINE_STAGE(SWITCH, IN,  DHCP_RESPONSE, 19, "ls_in_dhcp_response") \
>>> -    PIPELINE_STAGE(SWITCH, IN,  DNS_LOOKUP,    20, "ls_in_dns_lookup")    \
>>> -    PIPELINE_STAGE(SWITCH, IN,  DNS_RESPONSE,  21, "ls_in_dns_response")  \
>>> -    PIPELINE_STAGE(SWITCH, IN,  EXTERNAL_PORT, 22, "ls_in_external_port") \
>>> -    PIPELINE_STAGE(SWITCH, IN,  L2_LKUP,       23, "ls_in_l2_lkup")       \
>>> -    PIPELINE_STAGE(SWITCH, IN,  L2_UNKNOWN,    24, "ls_in_l2_unknown")    \
>>> +    PIPELINE_STAGE(SWITCH, IN,  STATEFUL,      12, "ls_in_stateful")      \
>>> +    PIPELINE_STAGE(SWITCH, IN,  PRE_HAIRPIN,   13, "ls_in_pre_hairpin")   \
>>> +    PIPELINE_STAGE(SWITCH, IN,  NAT_HAIRPIN,   14, "ls_in_nat_hairpin")   \
>>> +    PIPELINE_STAGE(SWITCH, IN,  HAIRPIN,       15, "ls_in_hairpin")       \
>>> +    PIPELINE_STAGE(SWITCH, IN,  ARP_ND_RSP,    16, "ls_in_arp_rsp")       \
>>> +    PIPELINE_STAGE(SWITCH, IN,  DHCP_OPTIONS,  17, "ls_in_dhcp_options")  \
>>> +    PIPELINE_STAGE(SWITCH, IN,  DHCP_RESPONSE, 18, "ls_in_dhcp_response") \
>>> +    PIPELINE_STAGE(SWITCH, IN,  DNS_LOOKUP,    19, "ls_in_dns_lookup")    \
>>> +    PIPELINE_STAGE(SWITCH, IN,  DNS_RESPONSE,  20, "ls_in_dns_response")  \
>>> +    PIPELINE_STAGE(SWITCH, IN,  EXTERNAL_PORT, 21, "ls_in_external_port") \
>>> +    PIPELINE_STAGE(SWITCH, IN,  L2_LKUP,       22, "ls_in_l2_lkup")       \
>>> +    PIPELINE_STAGE(SWITCH, IN,  L2_UNKNOWN,    23, "ls_in_l2_unknown")    \
>>>                                                                            \
>>>      /* Logical switch egress stages. */                                   \
>>>      PIPELINE_STAGE(SWITCH, OUT, PRE_LB,       0, "ls_out_pre_lb")         \
>>>      PIPELINE_STAGE(SWITCH, OUT, PRE_ACL,      1, "ls_out_pre_acl")        \
>>>      PIPELINE_STAGE(SWITCH, OUT, PRE_STATEFUL, 2, "ls_out_pre_stateful")   \
>>> -    PIPELINE_STAGE(SWITCH, OUT, LB,           3, "ls_out_lb")             \
>>> -    PIPELINE_STAGE(SWITCH, OUT, ACL_HINT,     4, "ls_out_acl_hint")       \
>>> -    PIPELINE_STAGE(SWITCH, OUT, ACL,          5, "ls_out_acl")            \
>>> -    PIPELINE_STAGE(SWITCH, OUT, QOS_MARK,     6, "ls_out_qos_mark")       \
>>> -    PIPELINE_STAGE(SWITCH, OUT, QOS_METER,    7, "ls_out_qos_meter")      \
>>> -    PIPELINE_STAGE(SWITCH, OUT, STATEFUL,     8, "ls_out_stateful")       \
>>> -    PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_IP,  9, "ls_out_port_sec_ip")    \
>>> -    PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_L2, 10, "ls_out_port_sec_l2")    \
>>> +    PIPELINE_STAGE(SWITCH, OUT, ACL_HINT,     3, "ls_out_acl_hint")       \
>>> +    PIPELINE_STAGE(SWITCH, OUT, ACL,          4, "ls_out_acl")            \
>>> +    PIPELINE_STAGE(SWITCH, OUT, QOS_MARK,     5, "ls_out_qos_mark")       \
>>> +    PIPELINE_STAGE(SWITCH, OUT, QOS_METER,    6, "ls_out_qos_meter")      \
>>> +    PIPELINE_STAGE(SWITCH, OUT, STATEFUL,     7, "ls_out_stateful")       \
>>> +    PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_IP,  8, "ls_out_port_sec_ip")    \
>>> +    PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_L2,  9, "ls_out_port_sec_l2")    \
>>>                                                                        \
>>>      /* Logical router ingress stages. */                              \
>>>      PIPELINE_STAGE(ROUTER, IN,  ADMISSION,       0, "lr_in_admission")    \
>>> @@ -5159,9 +5157,9 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
>>>       */>      if (vip_configured) {
>>>          ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB,
>>> -                      100, "ip", REGBIT_CONNTRACK_DEFRAG" = 1; next;");
>>> +                      100, "ip", REGBIT_CONNTRACK_NAT" = 1; next;");
>>>          ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB,
>>> -                      100, "ip", REGBIT_CONNTRACK_DEFRAG" = 1; next;");
>>> +                      100, "ip", REGBIT_CONNTRACK_NAT" = 1; next;");
>>>      }
>>
>> The comment above this that describes this code^ needs to be changed.
> 
> Ack.
> 
>>>  }
>>>
>>> @@ -5173,10 +5171,46 @@ build_pre_stateful(struct ovn_datapath *od, struct hmap *lflows)
>>>      ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 0, "1", "next;");
>>>      ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_STATEFUL, 0, "1", "next;");
>>>
>>> +    const char *lb_protocols[] = {"tcp", "udp", "sctp"};
>>> +    struct ds actions = DS_EMPTY_INITIALIZER;
>>> +    struct ds match = DS_EMPTY_INITIALIZER;
>>> +
>>> +    for (size_t i = 0; i < ARRAY_SIZE(lb_protocols); i++) {
>>> +        ds_clear(&match);
>>> +        ds_clear(&actions);
>>> +        ds_put_format(&match, REGBIT_CONNTRACK_NAT" == 1 && ip4 && %s",
>>> +                      lb_protocols[i]);
>>> +        ds_put_format(&actions, REG_ORIG_DIP_IPV4 " = ip4.dst; "
>>> +                                REG_ORIG_TP_DPORT " = %s.dst; ct_lb;",
>>> +                      lb_protocols[i]);
>>> +        ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 120,
>>> +                      ds_cstr(&match), ds_cstr(&actions));
>>> +
>>> +        ds_clear(&match);
>>> +        ds_clear(&actions);
>>> +        ds_put_format(&match, REGBIT_CONNTRACK_NAT" == 1 && ip6 && %s",
>>> +                      lb_protocols[i]);
>>> +        ds_put_format(&actions, REG_ORIG_DIP_IPV6 " = ip6.dst; "
>>> +                                REG_ORIG_TP_DPORT " = %s.dst; ct_lb;",
>>> +                      lb_protocols[i]);
>>> +        ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 120,
>>> +                      ds_cstr(&match), ds_cstr(&actions));
>>> +    }
>>> +
>>> +    ds_destroy(&actions);
>>> +    ds_destroy(&match);
>>> +
>>> +    ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 110,
>>> +                  REGBIT_CONNTRACK_NAT" == 1", "ct_lb;");
>>> +
>>> +    ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_STATEFUL, 110,
>>> +                  REGBIT_CONNTRACK_NAT" == 1", "ct_lb;");
>>> +
>>>      /* If REGBIT_CONNTRACK_DEFRAG is set as 1, then the packets should be
>>>       * sent to conntrack for tracking and defragmentation. */
>>>      ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 100,
>>>                    REGBIT_CONNTRACK_DEFRAG" == 1", "ct_next;");
>>> +>      ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_STATEFUL, 100,
>>>                    REGBIT_CONNTRACK_DEFRAG" == 1", "ct_next;");
>>>  }
>>> @@ -5856,37 +5890,6 @@ build_qos(struct ovn_datapath *od, struct hmap *lflows) {
>>>      }
>>>  }
>>>
>>> -static void
>>> -build_lb(struct ovn_datapath *od, struct hmap *lflows)
>>> -{
>>> -    /* Ingress and Egress LB Table (Priority 0): Packets are allowed by
>>> -     * default.  */
>>> -    ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, 0, "1", "next;");
>>> -    ovn_lflow_add(lflows, od, S_SWITCH_OUT_LB, 0, "1", "next;");
>>> -
>>> -    if (od->nbs->n_load_balancer) {
>>> -        for (size_t i = 0; i < od->n_router_ports; i++) {
>>> -            skip_port_from_conntrack(od, od->router_ports[i],
>>> -                                     S_SWITCH_IN_LB, S_SWITCH_OUT_LB,
>>> -                                     UINT16_MAX, lflows);
>>> -        }
>>> -    }
>>> -
>>> -    if (od->has_lb_vip) {
>>> -        /* Ingress and Egress LB Table (Priority 65534).
>>> -         *
>>> -         * Send established traffic through conntrack for just NAT. */
>>> -        ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, UINT16_MAX - 1,
>>> -                      "ct.est && !ct.rel && !ct.new && !ct.inv && "
>>> -                      "ct_label.natted == 1",
>>> -                      REGBIT_CONNTRACK_NAT" = 1; next;");
>>> -        ovn_lflow_add(lflows, od, S_SWITCH_OUT_LB, UINT16_MAX - 1,
>>> -                      "ct.est && !ct.rel && !ct.new && !ct.inv && "
>>> -                      "ct_label.natted == 1",
>>> -                      REGBIT_CONNTRACK_NAT" = 1; next;");
>>> -    }
>>> -}
>>> -
>>>  static void
>>>  build_lb_rules(struct ovn_datapath *od, struct hmap *lflows,
>>>                 struct ovn_northd_lb *lb)
>>> @@ -5971,48 +5974,6 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows, struct hmap *lbs)
>>>                    REGBIT_CONNTRACK_COMMIT" == 1",
>>>                    "ct_commit { ct_label.blocked = 0; }; next;");
>>>
>>> -    /* If REGBIT_CONNTRACK_NAT is set as 1, then packets should just be sent
>>> -     * through nat (without committing).
>>> -     *
>>> -     * REGBIT_CONNTRACK_COMMIT is set for new connections and
>>> -     * REGBIT_CONNTRACK_NAT is set for established connections. So they
>>> -     * don't overlap.
>>> -     *
>>> -     * In the ingress pipeline, also store the original destination IP and
>>> -     * transport port to be used when detecting hairpin packets.
>>> -     */
>>> -    const char *lb_protocols[] = {"tcp", "udp", "sctp"};
>>> -    struct ds actions = DS_EMPTY_INITIALIZER;
>>> -    struct ds match = DS_EMPTY_INITIALIZER;
>>> -
>>> -    for (size_t i = 0; i < ARRAY_SIZE(lb_protocols); i++) {
>>> -        ds_clear(&match);
>>> -        ds_clear(&actions);
>>> -        ds_put_format(&match, REGBIT_CONNTRACK_NAT" == 1 && ip4 && %s",
>>> -                      lb_protocols[i]);
>>> -        ds_put_format(&actions, REG_ORIG_DIP_IPV4 " = ip4.dst; "
>>> -                                REG_ORIG_TP_DPORT " = %s.dst; ct_lb;",
>>> -                      lb_protocols[i]);
>>> -        ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 100,
>>> -                      ds_cstr(&match), ds_cstr(&actions));
>>> -
>>> -        ds_clear(&match);
>>> -        ds_clear(&actions);
>>> -        ds_put_format(&match, REGBIT_CONNTRACK_NAT" == 1 && ip6 && %s",
>>> -                      lb_protocols[i]);
>>> -        ds_put_format(&actions, REG_ORIG_DIP_IPV6 " = ip6.dst; "
>>> -                                REG_ORIG_TP_DPORT " = %s.dst; ct_lb;",
>>> -                      lb_protocols[i]);
>>> -        ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 100,
>>> -                      ds_cstr(&match), ds_cstr(&actions));
>>> -    }
>>> -
>>> -    ds_destroy(&actions);
>>> -    ds_destroy(&match);
>>> -
>>> -    ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 100,
>>> -                  REGBIT_CONNTRACK_NAT" == 1", "ct_lb;");
>>> -
>>>      /* Load balancing rules for new connections get committed to conntrack
>>>       * table.  So even if REGBIT_CONNTRACK_COMMIT is set in a previous table
>>>       * a higher priority rule for load balancing below also commits the
>>> @@ -6803,7 +6764,6 @@ build_lswitch_lflows_pre_acl_and_acl(struct ovn_datapath *od,
>>
>> I think the comment above this function is wrong now as it references
>> table numbers.
> 
> Ack.
> 
>>
>>>          build_acl_hints(od, lflows);
>>>          build_acls(od, lflows, port_groups, meter_groups);
>>>          build_qos(od, lflows);
>>> -        build_lb(od, lflows);
>>>          build_stateful(od, lflows, lbs);
>>>          build_lb_hairpin(od, lflows);
>>>      }
>>> diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl
>>> index 0063021e13..2c0796938c 100644
>>> --- a/northd/ovn_northd.dl
>>> +++ b/northd/ovn_northd.dl
>>> @@ -1398,32 +1398,30 @@ function s_SWITCH_IN_ACL_HINT():        Stage { Stage{Ingress,  8, "ls_in_acl_hi
>>>  function s_SWITCH_IN_ACL():             Stage { Stage{Ingress,  9, "ls_in_acl"} }
>>>  function s_SWITCH_IN_QOS_MARK():        Stage { Stage{Ingress, 10, "ls_in_qos_mark"} }
>>>  function s_SWITCH_IN_QOS_METER():       Stage { Stage{Ingress, 11, "ls_in_qos_meter"} }
>>> -function s_SWITCH_IN_LB():              Stage { Stage{Ingress, 12, "ls_in_lb"} }
>>> -function s_SWITCH_IN_STATEFUL():        Stage { Stage{Ingress, 13, "ls_in_stateful"} }
>>> -function s_SWITCH_IN_PRE_HAIRPIN():     Stage { Stage{Ingress, 14, "ls_in_pre_hairpin"} }
>>> -function s_SWITCH_IN_NAT_HAIRPIN():     Stage { Stage{Ingress, 15, "ls_in_nat_hairpin"} }
>>> -function s_SWITCH_IN_HAIRPIN():         Stage { Stage{Ingress, 16, "ls_in_hairpin"} }
>>> -function s_SWITCH_IN_ARP_ND_RSP():      Stage { Stage{Ingress, 17, "ls_in_arp_rsp"} }
>>> -function s_SWITCH_IN_DHCP_OPTIONS():    Stage { Stage{Ingress, 18, "ls_in_dhcp_options"} }
>>> -function s_SWITCH_IN_DHCP_RESPONSE():   Stage { Stage{Ingress, 19, "ls_in_dhcp_response"} }
>>> -function s_SWITCH_IN_DNS_LOOKUP():      Stage { Stage{Ingress, 20, "ls_in_dns_lookup"} }
>>> -function s_SWITCH_IN_DNS_RESPONSE():    Stage { Stage{Ingress, 21, "ls_in_dns_response"} }
>>> -function s_SWITCH_IN_EXTERNAL_PORT():   Stage { Stage{Ingress, 22, "ls_in_external_port"} }
>>> -function s_SWITCH_IN_L2_LKUP():         Stage { Stage{Ingress, 23, "ls_in_l2_lkup"} }
>>> -function s_SWITCH_IN_L2_UNKNOWN():      Stage { Stage{Ingress, 24, "ls_in_l2_unknown"} }
>>> +function s_SWITCH_IN_STATEFUL():        Stage { Stage{Ingress, 12, "ls_in_stateful"} }
>>> +function s_SWITCH_IN_PRE_HAIRPIN():     Stage { Stage{Ingress, 13, "ls_in_pre_hairpin"} }
>>> +function s_SWITCH_IN_NAT_HAIRPIN():     Stage { Stage{Ingress, 14, "ls_in_nat_hairpin"} }
>>> +function s_SWITCH_IN_HAIRPIN():         Stage { Stage{Ingress, 15, "ls_in_hairpin"} }
>>> +function s_SWITCH_IN_ARP_ND_RSP():      Stage { Stage{Ingress, 16, "ls_in_arp_rsp"} }
>>> +function s_SWITCH_IN_DHCP_OPTIONS():    Stage { Stage{Ingress, 17, "ls_in_dhcp_options"} }
>>> +function s_SWITCH_IN_DHCP_RESPONSE():   Stage { Stage{Ingress, 18, "ls_in_dhcp_response"} }
>>> +function s_SWITCH_IN_DNS_LOOKUP():      Stage { Stage{Ingress, 19, "ls_in_dns_lookup"} }
>>> +function s_SWITCH_IN_DNS_RESPONSE():    Stage { Stage{Ingress, 20, "ls_in_dns_response"} }
>>> +function s_SWITCH_IN_EXTERNAL_PORT():   Stage { Stage{Ingress, 21, "ls_in_external_port"} }
>>> +function s_SWITCH_IN_L2_LKUP():         Stage { Stage{Ingress, 22, "ls_in_l2_lkup"} }
>>> +function s_SWITCH_IN_L2_UNKNOWN():      Stage { Stage{Ingress, 23, "ls_in_l2_unknown"} }
>>>
>>>  /* Logical switch egress stages. */
>>>  function s_SWITCH_OUT_PRE_LB():         Stage { Stage{ Egress,  0, "ls_out_pre_lb"} }
>>>  function s_SWITCH_OUT_PRE_ACL():        Stage { Stage{ Egress,  1, "ls_out_pre_acl"} }
>>>  function s_SWITCH_OUT_PRE_STATEFUL():   Stage { Stage{ Egress,  2, "ls_out_pre_stateful"} }
>>> -function s_SWITCH_OUT_LB():             Stage { Stage{ Egress,  3, "ls_out_lb"} }
>>> -function s_SWITCH_OUT_ACL_HINT():       Stage { Stage{ Egress,  4, "ls_out_acl_hint"} }
>>> -function s_SWITCH_OUT_ACL():            Stage { Stage{ Egress,  5, "ls_out_acl"} }
>>> -function s_SWITCH_OUT_QOS_MARK():       Stage { Stage{ Egress,  6, "ls_out_qos_mark"} }
>>> -function s_SWITCH_OUT_QOS_METER():      Stage { Stage{ Egress,  7, "ls_out_qos_meter"} }
>>> -function s_SWITCH_OUT_STATEFUL():       Stage { Stage{ Egress,  8, "ls_out_stateful"} }
>>> -function s_SWITCH_OUT_PORT_SEC_IP():    Stage { Stage{ Egress,  9, "ls_out_port_sec_ip"} }
>>> -function s_SWITCH_OUT_PORT_SEC_L2():    Stage { Stage{ Egress, 10, "ls_out_port_sec_l2"} }
>>> +function s_SWITCH_OUT_ACL_HINT():       Stage { Stage{ Egress,  3, "ls_out_acl_hint"} }
>>> +function s_SWITCH_OUT_ACL():            Stage { Stage{ Egress,  4, "ls_out_acl"} }
>>> +function s_SWITCH_OUT_QOS_MARK():       Stage { Stage{ Egress,  5, "ls_out_qos_mark"} }
>>> +function s_SWITCH_OUT_QOS_METER():      Stage { Stage{ Egress,  6, "ls_out_qos_meter"} }
>>> +function s_SWITCH_OUT_STATEFUL():       Stage { Stage{ Egress,  7, "ls_out_stateful"} }
>>> +function s_SWITCH_OUT_PORT_SEC_IP():    Stage { Stage{ Egress,  8, "ls_out_port_sec_ip"} }
>>> +function s_SWITCH_OUT_PORT_SEC_L2():    Stage { Stage{ Egress,  9, "ls_out_port_sec_l2"} }
>>>
>>>  /* Logical router ingress stages. */
>>>  function s_ROUTER_IN_ADMISSION():       Stage { Stage{Ingress,  0, "lr_in_admission"} }
>>> @@ -2049,7 +2047,7 @@ Flow(.logical_datapath = sw.ls._uuid,
>>>      Some {(var __match, var __action)} = build_empty_lb_event_flow(
>>>          vip, lb, has_elb_meter).
>>>
>>> -/* 'REGBIT_CONNTRACK_DEFRAG' is set to let the pre-stateful table send
>>> +/* 'rEGBIT_CONNTRACK_NAT' is set to let the pre-stateful table send>   * packet to conntrack for defragmentation.
>>>   *
>>>   * Send all the packets to conntrack in the ingress pipeline if the
>>> @@ -2083,17 +2081,21 @@ for (sw in &Switch(.has_lb_vip = true)) {
>>>           .stage            = s_SWITCH_IN_PRE_LB(),
>>>           .priority         = 100,
>>>           .__match          = "ip",
>>> -         .actions          = "${rEGBIT_CONNTRACK_DEFRAG()} = 1; next;",
>>> +         .actions          = "${rEGBIT_CONNTRACK_NAT()} = 1; next;",
>>>           .external_ids     = map_empty());
>>>      Flow(.logical_datapath = sw.ls._uuid,
>>>           .stage            = s_SWITCH_OUT_PRE_LB(),
>>>           .priority         = 100,
>>>           .__match          = "ip",
>>> -         .actions          = "${rEGBIT_CONNTRACK_DEFRAG()} = 1; next;",
>>> +         .actions          = "${rEGBIT_CONNTRACK_NAT()} = 1; next;",
>>>           .external_ids     = map_empty())
>>>  }
>>>
>>>  /* Pre-stateful */
>>> +relation LbProtocol[string]
>>> +LbProtocol["tcp"].
>>> +LbProtocol["udp"].
>>> +LbProtocol["sctp"].
>>>  for (&Switch(.ls = ls)) {
>>>      /* Ingress and Egress pre-stateful Table (Priority 0): Packets are
>>>       * allowed by default. */
>>> @@ -2110,6 +2112,47 @@ for (&Switch(.ls = ls)) {
>>>           .actions          = "next;",
>>>           .external_ids     = map_empty());
>>>
>>> +    /* If REGBIT_CONNTRACK_NAT is set as 1, then packets should just be sent
>>
>> should be rEGBIT? and below?
>>> +     * through nat (without committing).
>>> +     *
>>> +     * REGBIT_CONNTRACK_COMMIT is set for new connections and
>>> +     * REGBIT_CONNTRACK_NAT is set for established connections. So they
>>> +     * don't overlap.
>>> +     *
>>> +     * In the ingress pipeline, also store the original destination IP and
>>> +     * transport port to be used when detecting hairpin packets.
>>> +     */
>>> +    for (LbProtocol[protocol]) {
>>> +        Flow(.logical_datapath = ls._uuid,
>>> +             .stage            = s_SWITCH_IN_PRE_STATEFUL(),
>>> +             .priority         = 120,
>>> +             .__match          = "${rEGBIT_CONNTRACK_NAT()} == 1 && ip4 && ${protocol}",
>>> +             .actions          = "${rEG_ORIG_DIP_IPV4()} = ip4.dst; "
>>> +                                 "${rEG_ORIG_TP_DPORT()} = ${protocol}.dst; ct_lb;",
>>> +             .external_ids     = map_empty());
>>> +        Flow(.logical_datapath = ls._uuid,
>>> +             .stage            = s_SWITCH_IN_PRE_STATEFUL(),
>>> +             .priority         = 120,
>>> +             .__match          = "${rEGBIT_CONNTRACK_NAT()} == 1 && ip6 && ${protocol}",
>>> +             .actions          = "${rEG_ORIG_DIP_IPV6()} = ip6.dst; "
>>> +                                 "${rEG_ORIG_TP_DPORT()} = ${protocol}.dst; ct_lb;",
>>> +             .external_ids     = map_empty())
>>> +    };
>>> +
>>> +    Flow(.logical_datapath = ls._uuid,
>>> +         .stage            = s_SWITCH_IN_PRE_STATEFUL(),
>>> +         .priority         = 110,
>>> +         .__match          = "${rEGBIT_CONNTRACK_NAT()} == 1",
>>> +         .actions          = "ct_lb;",
>>> +         .external_ids     = map_empty());
>>> +
>>> +    Flow(.logical_datapath = ls._uuid,
>>> +         .stage            = s_SWITCH_OUT_PRE_STATEFUL(),
>>> +         .priority         = 110,
>>> +         .__match          = "${rEGBIT_CONNTRACK_NAT()} == 1",
>>> +         .actions          = "ct_lb;",
>>> +         .external_ids     = map_empty());
>>> +
>>>      /* If REGBIT_CONNTRACK_DEFRAG is set as 1, then the packets should be
>>>       * sent to conntrack for tracking and defragmentation. */
>>>      Flow(.logical_datapath = ls._uuid,
>>> @@ -2723,66 +2766,7 @@ for (SwitchQoS(.sw = &sw, .qos = &qos)) {
>>>      }
>>>  }
>>>
>>> -/* LB rules */
>>> -for (&Switch(.ls = ls, .has_lb_vip = has_lb_vip)) {
>>> -    /* Ingress and Egress LB Table (Priority 0): Packets are allowed by
>>> -     * default.  */
>>> -    Flow(.logical_datapath = ls._uuid,
>>> -         .stage            = s_SWITCH_IN_LB(),
>>> -         .priority         = 0,
>>> -         .__match          = "1",
>>> -         .actions          = "next;",
>>> -         .external_ids     = map_empty());
>>> -    Flow(.logical_datapath = ls._uuid,
>>> -         .stage            = s_SWITCH_OUT_LB(),
>>> -         .priority         = 0,
>>> -         .__match          = "1",
>>> -         .actions          = "next;",
>>> -         .external_ids     = map_empty());
>>> -
>>> -    if (not ls.load_balancer.is_empty()) {
>>> -        for (&SwitchPort(.lsp = lsp at nb::Logical_Switch_Port{.__type = "router"},
>>> -                         .json_name = lsp_name,
>>> -                         .sw = &Switch{.ls = ls})) {
>>> -            Flow(.logical_datapath = ls._uuid,
>>> -                 .stage            = s_SWITCH_IN_LB(),
>>> -                 .priority         = 65535,
>>> -                 .__match          = "ip && inport == ${lsp_name}",
>>> -                 .actions          = "next;",
>>> -                 .external_ids     = stage_hint(lsp._uuid));
>>> -            Flow(.logical_datapath = ls._uuid,
>>> -                 .stage            = s_SWITCH_OUT_LB(),
>>> -                 .priority         = 65535,
>>> -                 .__match          = "ip && outport == ${lsp_name}",
>>> -                 .actions          = "next;",
>>> -                 .external_ids     = stage_hint(lsp._uuid))
>>> -        }
>>> -    };
>>> -
>>> -    if (has_lb_vip) {
>>> -        /* Ingress and Egress LB Table (Priority 65534).
>>> -         *
>>> -         * Send established traffic through conntrack for just NAT. */
>>> -        Flow(.logical_datapath = ls._uuid,
>>> -             .stage            = s_SWITCH_IN_LB(),
>>> -             .priority         = 65534,
>>> -             .__match          = "ct.est && !ct.rel && !ct.new && !ct.inv && ct_label.natted == 1",
>>> -             .actions          = "${rEGBIT_CONNTRACK_NAT()} = 1; next;",
>>> -             .external_ids     = map_empty());
>>> -        Flow(.logical_datapath = ls._uuid,
>>> -             .stage            = s_SWITCH_OUT_LB(),
>>> -             .priority         = 65534,
>>> -             .__match          = "ct.est && !ct.rel && !ct.new && !ct.inv && ct_label.natted == 1",
>>> -             .actions          = "${rEGBIT_CONNTRACK_NAT()} = 1; next;",
>>> -             .external_ids     = map_empty())
>>> -    }
>>> -}
>>> -
>>>  /* stateful rules */
>>> -relation LbProtocol[string]
>>> -LbProtocol["tcp"].
>>> -LbProtocol["udp"].
>>> -LbProtocol["sctp"].
>>>  for (&Switch(.ls = ls)) {
>>>      /* Ingress and Egress stateful Table (Priority 0): Packets are
>>>       * allowed by default. */
>>> @@ -2814,40 +2798,6 @@ for (&Switch(.ls = ls)) {
>>>           .priority         = 100,
>>>           .__match          = "${rEGBIT_CONNTRACK_COMMIT()} == 1",
>>>           .actions          = "ct_commit { ct_label.blocked = 0; }; next;",
>>> -         .external_ids     = map_empty());
>>> -
>>> -    /* If REGBIT_CONNTRACK_NAT is set as 1, then packets should just be sent
>>> -     * through nat (without committing).
>>> -     *
>>> -     * REGBIT_CONNTRACK_COMMIT is set for new connections and
>>> -     * REGBIT_CONNTRACK_NAT is set for established connections. So they
>>> -     * don't overlap.
>>> -     *
>>> -     * In the ingress pipeline, also store the original destination IP and
>>> -     * transport port to be used when detecting hairpin packets.
>>> -     */
>>> -    for (LbProtocol[protocol]) {
>>> -        Flow(.logical_datapath = ls._uuid,
>>> -             .stage            = s_SWITCH_IN_STATEFUL(),
>>> -             .priority         = 100,
>>> -             .__match          = "${rEGBIT_CONNTRACK_NAT()} == 1 && ip4 && ${protocol}",
>>> -             .actions          = "${rEG_ORIG_DIP_IPV4()} = ip4.dst; "
>>> -                                 "${rEG_ORIG_TP_DPORT()} = ${protocol}.dst; ct_lb;",
>>> -             .external_ids     = map_empty());
>>> -        Flow(.logical_datapath = ls._uuid,
>>> -             .stage            = s_SWITCH_IN_STATEFUL(),
>>> -             .priority         = 100,
>>> -             .__match          = "${rEGBIT_CONNTRACK_NAT()} == 1 && ip6 && ${protocol}",
>>> -             .actions          = "${rEG_ORIG_DIP_IPV6()} = ip6.dst; "
>>> -                                 "${rEG_ORIG_TP_DPORT()} = ${protocol}.dst; ct_lb;",
>>> -             .external_ids     = map_empty())
>>> -    };
>>> -
>>> -    Flow(.logical_datapath = ls._uuid,
>>> -         .stage            = s_SWITCH_OUT_STATEFUL(),
>>> -         .priority         = 100,
>>> -         .__match          = "${rEGBIT_CONNTRACK_NAT()} == 1",
>>> -         .actions          = "ct_lb;",
>>>           .external_ids     = map_empty())
>>>  }
>>>
>>> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
>>> index 96476497df..32b092bcd9 100644
>>> --- a/tests/ovn-northd.at
>>> +++ b/tests/ovn-northd.at
>>> @@ -1201,7 +1201,7 @@ check ovn-nbctl --wait=sb ls-lb-add sw0 lb1
>>>
>>>  AT_CAPTURE_FILE([sbflows])
>>>  OVS_WAIT_FOR_OUTPUT(
>>> -  [ovn-sbctl dump-flows sw0 | tee sbflows | grep 'priority=120.*ct_lb' | sed 's/table=..//'], 0, [dnl
>>> +  [ovn-sbctl dump-flows sw0 | tee sbflows | grep 'priority=120.*backends' | sed 's/table=..//'], 0, [dnl
>> I presume the change s/ct_lb/backends is because you now have multiple
>> "ct_lb" actions in the "ls_in_stateful" table?
>>>    (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
>>>  ])
>>>
>>> @@ -1211,7 +1211,7 @@ wait_row_count Service_Monitor 0
>>>
>>>  AT_CAPTURE_FILE([sbflows2])
>>>  OVS_WAIT_FOR_OUTPUT(
>>> -  [ovn-sbctl dump-flows sw0 | tee sbflows2 | grep 'priority=120.*ct_lb' | sed 's/table=..//'], [0],
>>> +  [ovn-sbctl dump-flows sw0 | tee sbflows2 | grep 'priority=120.*backends' | sed 's/table=..//'], [0],
>>>  [  (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
>>>  ])
>>>
>>> @@ -1222,7 +1222,7 @@ health_check @hc
>>>  wait_row_count Service_Monitor 2
>>>  check ovn-nbctl --wait=sb sync
>>>
>>> -ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
>>> +ovn-sbctl dump-flows sw0 | grep backends | grep priority=120 > lflows.txt
>>>  AT_CHECK([cat lflows.txt | sed 's/table=..//'], [0], [dnl
>>>    (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
>>>  ])
>>> @@ -1233,7 +1233,7 @@ sm_sw1_p1=$(fetch_column Service_Monitor _uuid logical_port=sw1-p1)
>>>
>>>  AT_CAPTURE_FILE([sbflows3])
>>>  OVS_WAIT_FOR_OUTPUT(
>>> -  [ovn-sbctl dump-flows sw0 | tee sbflows 3 | grep 'priority=120.*ct_lb' | sed 's/table=..//'], [0],
>>> +  [ovn-sbctl dump-flows sw0 | tee sbflows 3 | grep 'priority=120.*backends' | sed 's/table=..//'], [0],
>>>  [  (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
>>>  ])
>>>
>>> @@ -1244,7 +1244,7 @@ check ovn-nbctl --wait=sb sync
>>>
>>>  AT_CAPTURE_FILE([sbflows4])
>>>  OVS_WAIT_FOR_OUTPUT(
>>> -  [ovn-sbctl dump-flows sw0 | tee sbflows4 | grep 'priority=120.*ct_lb' | sed 's/table=..//'], [0],
>>> +  [ovn-sbctl dump-flows sw0 | tee sbflows4 | grep 'priority=120.*backends' | sed 's/table=..//'], [0],
>>>  [  (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80);)
>>>  ])
>>>
>>> @@ -1256,7 +1256,7 @@ check ovn-nbctl --wait=sb sync
>>>
>>>  AT_CAPTURE_FILE([sbflows5])
>>>  OVS_WAIT_FOR_OUTPUT(
>>> -  [ovn-sbctl dump-flows sw0 | tee sbflows5 | grep 'priority=120.*ct_lb'], 1)
>>> +  [ovn-sbctl dump-flows sw0 | tee sbflows5 | grep 'priority=120.*backends'], 1)
>>>
>>>  AT_CAPTURE_FILE([sbflows6])
>>>  OVS_WAIT_FOR_OUTPUT(
>>> @@ -1273,7 +1273,7 @@ check ovn-nbctl --wait=sb sync
>>>
>>>  AT_CAPTURE_FILE([sbflows7])
>>>  OVS_WAIT_FOR_OUTPUT(
>>> -  [ovn-sbctl dump-flows sw0 | tee sbflows7 | grep ct_lb | grep priority=120 | sed 's/table=..//'], 0,
>>> +  [ovn-sbctl dump-flows sw0 | tee sbflows7 | grep backends | grep priority=120 | sed 's/table=..//'], 0,
>>>  [  (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
>>>  ])
>>>
>>> @@ -1309,7 +1309,7 @@ wait_row_count Service_Monitor 1 port=1000
>>>
>>>  AT_CAPTURE_FILE([sbflows9])
>>>  OVS_WAIT_FOR_OUTPUT(
>>> -  [ovn-sbctl dump-flows sw0 | tee sbflows9 | grep ct_lb | grep priority=120 | sed 's/table=..//' | sort],
>>> +  [ovn-sbctl dump-flows sw0 | tee sbflows9 | grep backends | grep priority=120 | sed 's/table=..//' | sort],
>>>    0,
>>>  [  (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80);)
>>>    (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(reg1 = 10.0.0.40; reg2[[0..15]] = 1000; ct_lb(backends=10.0.0.3:1000);)
>>> @@ -1323,7 +1323,7 @@ check ovn-nbctl --wait=sb sync
>>>
>>>  AT_CAPTURE_FILE([sbflows10])
>>>  OVS_WAIT_FOR_OUTPUT(
>>> -  [ovn-sbctl dump-flows sw0 | tee sbflows10 | grep ct_lb | grep priority=120 | sed 's/table=..//' | sort],
>>> +  [ovn-sbctl dump-flows sw0 | tee sbflows10 | grep backends | grep priority=120 | sed 's/table=..//' | sort],
>>>    0,
>>>  [  (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
>>>    (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(reg1 = 10.0.0.40; reg2[[0..15]] = 1000; ct_lb(backends=10.0.0.3:1000,20.0.0.3:80);)
>>> @@ -1333,7 +1333,7 @@ AS_BOX([Associate lb1 to sw1])
>>>  check ovn-nbctl --wait=sb ls-lb-add sw1 lb1
>>>  AT_CAPTURE_FILE([sbflows11])
>>>  OVS_WAIT_FOR_OUTPUT(
>>> -  [ovn-sbctl dump-flows sw1 | tee sbflows11 | grep ct_lb | grep priority=120 | sed 's/table=..//' | sort],
>>> +  [ovn-sbctl dump-flows sw1 | tee sbflows11 | grep backends | grep priority=120 | sed 's/table=..//' | sort],
>>>    0, [dnl
>>>    (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
>>>    (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(reg1 = 10.0.0.40; reg2[[0..15]] = 1000; ct_lb(backends=10.0.0.3:1000,20.0.0.3:80);)
>>> @@ -1393,7 +1393,7 @@ ovn-sbctl set service_monitor $sm_sw1_p1 status=offline
>>>  AT_CAPTURE_FILE([sbflows12])
>>>  OVS_WAIT_FOR_OUTPUT(
>>>    [ovn-sbctl dump-flows sw0 | tee sbflows12 | grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" | grep priority=120 | sed 's/table=..//'], [0], [dnl
>>> -  (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0 = 0; reject { outport <-> inport; next(pipeline=egress,table=6);};)
>>> +  (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg0 = 0; reject { outport <-> inport; next(pipeline=egress,table=5);};)
>>>  ])
>>>
>>>  AT_CLEANUP
>>> @@ -1813,13 +1813,13 @@ AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_pre_lb.*priority=100" | grep reg0
>>>  ovn-nbctl ls-lb-add sw0 lb1
>>>  ovn-nbctl --wait=sb sync
>>>  AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_pre_lb.*priority=100" | grep reg0 | sort], [0], [dnl
>>> -  table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
>>> +  table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[2]] = 1; next;)
>>>  ])
>>>
>>>  ovn-nbctl ls-lb-add sw0 lb2
>>>  ovn-nbctl --wait=sb sync
>>>  AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_pre_lb.*priority=100" | grep reg0 | sort], [0], [dnl
>>> -  table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
>>> +  table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[2]] = 1; next;)
>>>  ])
>>>
>>>  lb1_uuid=$(ovn-nbctl --bare --columns _uuid find load_balancer name=lb1)
>>> @@ -1828,7 +1828,7 @@ lb2_uuid=$(ovn-nbctl --bare --columns _uuid find load_balancer name=lb2)
>>>  ovn-nbctl clear load_balancer $lb1_uuid vips
>>>  ovn-nbctl --wait=sb sync
>>>  AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_pre_lb.*priority=100" | grep reg0 | sort], [0], [dnl
>>> -  table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
>>> +  table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[2]] = 1; next;)
>>>  ])
>>>
>>>  ovn-nbctl clear load_balancer $lb2_uuid vips
>>> @@ -1841,14 +1841,14 @@ ovn-nbctl set load_balancer $lb2_uuid vips:"10.0.0.11"="10.0.0.4"
>>>
>>>  ovn-nbctl --wait=sb sync
>>>  AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_pre_lb.*priority=100" | grep reg0 | sort], [0], [dnl
>>> -  table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
>>> +  table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[2]] = 1; next;)
>>>  ])
>>>
>>>  # Now reverse the order of clearing the vip.
>>>  ovn-nbctl clear load_balancer $lb2_uuid vips
>>>  ovn-nbctl --wait=sb sync
>>>  AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_pre_lb.*priority=100" | grep reg0 | sort], [0], [dnl
>>> -  table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
>>> +  table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[2]] = 1; next;)
>>>  ])
>>>
>>>  ovn-nbctl clear load_balancer $lb1_uuid vips
>>> @@ -1900,10 +1900,10 @@ AT_CAPTURE_FILE([sw1flows])
>>>
>>>  AT_CHECK(
>>>    [grep -E 'ls_(in|out)_acl' sw0flows sw1flows | grep pg0 | sort], [0], [dnl
>>> -sw0flows:  table=5 (ls_out_acl         ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
>>> -sw0flows:  table=9 (ls_in_acl          ), priority=2002 , match=(inport == @pg0 && ip4 && tcp && tcp.dst == 80), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=egress,table=6); };)
>>> -sw1flows:  table=5 (ls_out_acl         ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
>>> -sw1flows:  table=9 (ls_in_acl          ), priority=2002 , match=(inport == @pg0 && ip4 && tcp && tcp.dst == 80), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=egress,table=6); };)
>>> +sw0flows:  table=4 (ls_out_acl         ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };)
>>> +sw0flows:  table=9 (ls_in_acl          ), priority=2002 , match=(inport == @pg0 && ip4 && tcp && tcp.dst == 80), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=egress,table=5); };)
>>> +sw1flows:  table=4 (ls_out_acl         ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };)
>>> +sw1flows:  table=9 (ls_in_acl          ), priority=2002 , match=(inport == @pg0 && ip4 && tcp && tcp.dst == 80), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=egress,table=5); };)
>>>  ])
>>>
>>>  AS_BOX([2])
>>> @@ -1916,10 +1916,10 @@ ovn-sbctl dump-flows sw1 > sw1flows2
>>>  AT_CAPTURE_FILE([sw1flows2])
>>>
>>>  AT_CHECK([grep "ls_out_acl" sw0flows2 sw1flows2 | grep pg0 | sort], [0], [dnl
>>> -sw0flows2:  table=5 (ls_out_acl         ), priority=2002 , match=(outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
>>> -sw0flows2:  table=5 (ls_out_acl         ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
>>> -sw1flows2:  table=5 (ls_out_acl         ), priority=2002 , match=(outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
>>> -sw1flows2:  table=5 (ls_out_acl         ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
>>> +sw0flows2:  table=4 (ls_out_acl         ), priority=2002 , match=(outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };)
>>> +sw0flows2:  table=4 (ls_out_acl         ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };)
>>> +sw1flows2:  table=4 (ls_out_acl         ), priority=2002 , match=(outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };)
>>> +sw1flows2:  table=4 (ls_out_acl         ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };)
>>>  ])
>>>
>>>  AS_BOX([3])
>>> @@ -1932,18 +1932,18 @@ ovn-sbctl dump-flows sw1 > sw1flows3
>>>  AT_CAPTURE_FILE([sw1flows3])
>>>
>>>  AT_CHECK([grep "ls_out_acl" sw0flows3 sw1flows3 | grep pg0 | sort], [0], [dnl
>>> -sw0flows3:  table=5 (ls_out_acl         ), priority=2001 , match=(reg0[[7]] == 1 && (outport == @pg0 && ip)), action=(reg0[[1]] = 1; next;)
>>> -sw0flows3:  table=5 (ls_out_acl         ), priority=2001 , match=(reg0[[8]] == 1 && (outport == @pg0 && ip)), action=(next;)
>>> -sw0flows3:  table=5 (ls_out_acl         ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
>>> -sw0flows3:  table=5 (ls_out_acl         ), priority=2002 , match=((reg0[[9]] == 1) && outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
>>> -sw0flows3:  table=5 (ls_out_acl         ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
>>> -sw0flows3:  table=5 (ls_out_acl         ), priority=2003 , match=((reg0[[9]] == 1) && outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
>>> -sw1flows3:  table=5 (ls_out_acl         ), priority=2001 , match=(reg0[[7]] == 1 && (outport == @pg0 && ip)), action=(reg0[[1]] = 1; next;)
>>> -sw1flows3:  table=5 (ls_out_acl         ), priority=2001 , match=(reg0[[8]] == 1 && (outport == @pg0 && ip)), action=(next;)
>>> -sw1flows3:  table=5 (ls_out_acl         ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
>>> -sw1flows3:  table=5 (ls_out_acl         ), priority=2002 , match=((reg0[[9]] == 1) && outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
>>> -sw1flows3:  table=5 (ls_out_acl         ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
>>> -sw1flows3:  table=5 (ls_out_acl         ), priority=2003 , match=((reg0[[9]] == 1) && outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=23); };)
>>> +sw0flows3:  table=4 (ls_out_acl         ), priority=2001 , match=(reg0[[7]] == 1 && (outport == @pg0 && ip)), action=(reg0[[1]] = 1; next;)
>>> +sw0flows3:  table=4 (ls_out_acl         ), priority=2001 , match=(reg0[[8]] == 1 && (outport == @pg0 && ip)), action=(next;)
>>> +sw0flows3:  table=4 (ls_out_acl         ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };)
>>> +sw0flows3:  table=4 (ls_out_acl         ), priority=2002 , match=((reg0[[9]] == 1) && outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };)
>>> +sw0flows3:  table=4 (ls_out_acl         ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };)
>>> +sw0flows3:  table=4 (ls_out_acl         ), priority=2003 , match=((reg0[[9]] == 1) && outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };)
>>> +sw1flows3:  table=4 (ls_out_acl         ), priority=2001 , match=(reg0[[7]] == 1 && (outport == @pg0 && ip)), action=(reg0[[1]] = 1; next;)
>>> +sw1flows3:  table=4 (ls_out_acl         ), priority=2001 , match=(reg0[[8]] == 1 && (outport == @pg0 && ip)), action=(next;)
>>> +sw1flows3:  table=4 (ls_out_acl         ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };)
>>> +sw1flows3:  table=4 (ls_out_acl         ), priority=2002 , match=((reg0[[9]] == 1) && outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };)
>>> +sw1flows3:  table=4 (ls_out_acl         ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };)
>>> +sw1flows3:  table=4 (ls_out_acl         ), priority=2003 , match=((reg0[[9]] == 1) && outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=22); };)
>>>  ])
>>>  AT_CLEANUP
>>>  ])
>>> @@ -2083,17 +2083,17 @@ check ovn-nbctl --wait=sb \
>>>      -- acl-add ls from-lport 2 "udp" allow-related \
>>>      -- acl-add ls to-lport 2 "udp" allow-related
>>>  AT_CHECK([ovn-sbctl lflow-list ls | grep -e ls_in_acl_hint -e ls_out_acl_hint -e ls_in_acl -e ls_out_acl | grep 'ct\.' | sort], [0], [dnl
>>> -  table=4 (ls_out_acl_hint    ), priority=1    , match=(ct.est && ct_label.blocked == 0), action=(reg0[[10]] = 1; next;)
>>> -  table=4 (ls_out_acl_hint    ), priority=2    , match=(ct.est && ct_label.blocked == 1), action=(reg0[[9]] = 1; next;)
>>> -  table=4 (ls_out_acl_hint    ), priority=3    , match=(!ct.est), action=(reg0[[9]] = 1; next;)
>>> -  table=4 (ls_out_acl_hint    ), priority=4    , match=(!ct.new && ct.est && !ct.rpl && ct_label.blocked == 0), action=(reg0[[8]] = 1; reg0[[10]] = 1; next;)
>>> -  table=4 (ls_out_acl_hint    ), priority=5    , match=(!ct.trk), action=(reg0[[8]] = 1; reg0[[9]] = 1; next;)
>>> -  table=4 (ls_out_acl_hint    ), priority=6    , match=(!ct.new && ct.est && !ct.rpl && ct_label.blocked == 1), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;)
>>> -  table=4 (ls_out_acl_hint    ), priority=7    , match=(ct.new && !ct.est), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;)
>>> -  table=5 (ls_out_acl         ), priority=1    , match=(ip && (!ct.est || (ct.est && ct_label.blocked == 1))), action=(reg0[[1]] = 1; next;)
>>> -  table=5 (ls_out_acl         ), priority=65535, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_label.blocked == 0), action=(next;)
>>> -  table=5 (ls_out_acl         ), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0), action=(next;)
>>> -  table=5 (ls_out_acl         ), priority=65535, match=(ct.inv || (ct.est && ct.rpl && ct_label.blocked == 1)), action=(drop;)
>>> +  table=3 (ls_out_acl_hint    ), priority=1    , match=(ct.est && ct_label.blocked == 0), action=(reg0[[10]] = 1; next;)
>>> +  table=3 (ls_out_acl_hint    ), priority=2    , match=(ct.est && ct_label.blocked == 1), action=(reg0[[9]] = 1; next;)
>>> +  table=3 (ls_out_acl_hint    ), priority=3    , match=(!ct.est), action=(reg0[[9]] = 1; next;)
>>> +  table=3 (ls_out_acl_hint    ), priority=4    , match=(!ct.new && ct.est && !ct.rpl && ct_label.blocked == 0), action=(reg0[[8]] = 1; reg0[[10]] = 1; next;)
>>> +  table=3 (ls_out_acl_hint    ), priority=5    , match=(!ct.trk), action=(reg0[[8]] = 1; reg0[[9]] = 1; next;)
>>> +  table=3 (ls_out_acl_hint    ), priority=6    , match=(!ct.new && ct.est && !ct.rpl && ct_label.blocked == 1), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;)
>>> +  table=3 (ls_out_acl_hint    ), priority=7    , match=(ct.new && !ct.est), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;)
>>> +  table=4 (ls_out_acl         ), priority=1    , match=(ip && (!ct.est || (ct.est && ct_label.blocked == 1))), action=(reg0[[1]] = 1; next;)
>>> +  table=4 (ls_out_acl         ), priority=65535, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_label.blocked == 0), action=(next;)
>>> +  table=4 (ls_out_acl         ), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0), action=(next;)
>>> +  table=4 (ls_out_acl         ), priority=65535, match=(ct.inv || (ct.est && ct.rpl && ct_label.blocked == 1)), action=(drop;)
>>>    table=8 (ls_in_acl_hint     ), priority=1    , match=(ct.est && ct_label.blocked == 0), action=(reg0[[10]] = 1; next;)
>>>    table=8 (ls_in_acl_hint     ), priority=2    , match=(ct.est && ct_label.blocked == 1), action=(reg0[[9]] = 1; next;)
>>>    table=8 (ls_in_acl_hint     ), priority=3    , match=(!ct.est), action=(reg0[[9]] = 1; next;)
>>> @@ -2115,17 +2115,17 @@ check ovn-nbctl --wait=sb \
>>>      -- ls-lb-add ls lb
>>>
>>>  AT_CHECK([ovn-sbctl lflow-list ls | grep -e ls_in_acl_hint -e ls_out_acl_hint -e ls_in_acl -e ls_out_acl | grep 'ct\.' | sort], [0], [dnl
>>> -  table=4 (ls_out_acl_hint    ), priority=1    , match=(ct.est && ct_label.blocked == 0), action=(reg0[[10]] = 1; next;)
>>> -  table=4 (ls_out_acl_hint    ), priority=2    , match=(ct.est && ct_label.blocked == 1), action=(reg0[[9]] = 1; next;)
>>> -  table=4 (ls_out_acl_hint    ), priority=3    , match=(!ct.est), action=(reg0[[9]] = 1; next;)
>>> -  table=4 (ls_out_acl_hint    ), priority=4    , match=(!ct.new && ct.est && !ct.rpl && ct_label.blocked == 0), action=(reg0[[8]] = 1; reg0[[10]] = 1; next;)
>>> -  table=4 (ls_out_acl_hint    ), priority=5    , match=(!ct.trk), action=(reg0[[8]] = 1; reg0[[9]] = 1; next;)
>>> -  table=4 (ls_out_acl_hint    ), priority=6    , match=(!ct.new && ct.est && !ct.rpl && ct_label.blocked == 1), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;)
>>> -  table=4 (ls_out_acl_hint    ), priority=7    , match=(ct.new && !ct.est), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;)
>>> -  table=5 (ls_out_acl         ), priority=1    , match=(ip && (!ct.est || (ct.est && ct_label.blocked == 1))), action=(reg0[[1]] = 1; next;)
>>> -  table=5 (ls_out_acl         ), priority=65535, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_label.blocked == 0), action=(next;)
>>> -  table=5 (ls_out_acl         ), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0), action=(next;)
>>> -  table=5 (ls_out_acl         ), priority=65535, match=(ct.inv || (ct.est && ct.rpl && ct_label.blocked == 1)), action=(drop;)
>>> +  table=3 (ls_out_acl_hint    ), priority=1    , match=(ct.est && ct_label.blocked == 0), action=(reg0[[10]] = 1; next;)
>>> +  table=3 (ls_out_acl_hint    ), priority=2    , match=(ct.est && ct_label.blocked == 1), action=(reg0[[9]] = 1; next;)
>>> +  table=3 (ls_out_acl_hint    ), priority=3    , match=(!ct.est), action=(reg0[[9]] = 1; next;)
>>> +  table=3 (ls_out_acl_hint    ), priority=4    , match=(!ct.new && ct.est && !ct.rpl && ct_label.blocked == 0), action=(reg0[[8]] = 1; reg0[[10]] = 1; next;)
>>> +  table=3 (ls_out_acl_hint    ), priority=5    , match=(!ct.trk), action=(reg0[[8]] = 1; reg0[[9]] = 1; next;)
>>> +  table=3 (ls_out_acl_hint    ), priority=6    , match=(!ct.new && ct.est && !ct.rpl && ct_label.blocked == 1), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;)
>>> +  table=3 (ls_out_acl_hint    ), priority=7    , match=(ct.new && !ct.est), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;)
>>> +  table=4 (ls_out_acl         ), priority=1    , match=(ip && (!ct.est || (ct.est && ct_label.blocked == 1))), action=(reg0[[1]] = 1; next;)
>>> +  table=4 (ls_out_acl         ), priority=65535, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_label.blocked == 0), action=(next;)
>>> +  table=4 (ls_out_acl         ), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_label.blocked == 0), action=(next;)
>>> +  table=4 (ls_out_acl         ), priority=65535, match=(ct.inv || (ct.est && ct.rpl && ct_label.blocked == 1)), action=(drop;)
>>>    table=8 (ls_in_acl_hint     ), priority=1    , match=(ct.est && ct_label.blocked == 0), action=(reg0[[10]] = 1; next;)
>>>    table=8 (ls_in_acl_hint     ), priority=2    , match=(ct.est && ct_label.blocked == 1), action=(reg0[[9]] = 1; next;)
>>>    table=8 (ls_in_acl_hint     ), priority=3    , match=(!ct.est), action=(reg0[[9]] = 1; next;)
>>> @@ -2354,20 +2354,20 @@ check ovn-nbctl \
>>>  check ovn-nbctl --wait=sb sync
>>>
>>>  AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_pre_hairpin | sort], [0], [dnl
>>> -  table=14(ls_in_pre_hairpin  ), priority=0    , match=(1), action=(next;)
>>> -  table=14(ls_in_pre_hairpin  ), priority=100  , match=(ip && ct.trk), action=(reg0[[6]] = chk_lb_hairpin(); reg0[[12]] = chk_lb_hairpin_reply(); next;)
>>> +  table=13(ls_in_pre_hairpin  ), priority=0    , match=(1), action=(next;)
>>> +  table=13(ls_in_pre_hairpin  ), priority=100  , match=(ip && ct.trk), action=(reg0[[6]] = chk_lb_hairpin(); reg0[[12]] = chk_lb_hairpin_reply(); next;)
>>>  ])
>>>
>>>  AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_nat_hairpin | sort], [0], [dnl
>>> -  table=15(ls_in_nat_hairpin  ), priority=0    , match=(1), action=(next;)
>>> -  table=15(ls_in_nat_hairpin  ), priority=100  , match=(ip && ct.est && ct.trk && reg0[[6]] == 1), action=(ct_snat;)
>>> -  table=15(ls_in_nat_hairpin  ), priority=100  , match=(ip && ct.new && ct.trk && reg0[[6]] == 1), action=(ct_snat_to_vip; next;)
>>> -  table=15(ls_in_nat_hairpin  ), priority=90   , match=(ip && reg0[[12]] == 1), action=(ct_snat;)
>>> +  table=14(ls_in_nat_hairpin  ), priority=0    , match=(1), action=(next;)
>>> +  table=14(ls_in_nat_hairpin  ), priority=100  , match=(ip && ct.est && ct.trk && reg0[[6]] == 1), action=(ct_snat;)
>>> +  table=14(ls_in_nat_hairpin  ), priority=100  , match=(ip && ct.new && ct.trk && reg0[[6]] == 1), action=(ct_snat_to_vip; next;)
>>> +  table=14(ls_in_nat_hairpin  ), priority=90   , match=(ip && reg0[[12]] == 1), action=(ct_snat;)
>>>  ])
>>>
>>>  AT_CHECK([ovn-sbctl lflow-list sw0 | grep ls_in_hairpin | sort], [0], [dnl
>>> -  table=16(ls_in_hairpin      ), priority=0    , match=(1), action=(next;)
>>> -  table=16(ls_in_hairpin      ), priority=1    , match=((reg0[[6]] == 1 || reg0[[12]] == 1)), action=(eth.dst <-> eth.src; outport = inport; flags.loopback = 1; output;)
>>> +  table=15(ls_in_hairpin      ), priority=0    , match=(1), action=(next;)
>>> +  table=15(ls_in_hairpin      ), priority=1    , match=((reg0[[6]] == 1 || reg0[[12]] == 1)), action=(eth.dst <-> eth.src; outport = inport; flags.loopback = 1; output;)
>>>  ])
>>>
>>>  AT_CLEANUP
>>> @@ -2950,4 +2950,135 @@ wait_row_count FDB 0
>>>  ovn-sbctl list FDB
>>>
>>>  AT_CLEANUP
>>> -])
>>> \ No newline at end of file
>>> +])
>>> +
>>> +OVN_FOR_EACH_NORTHD([
>>> +AT_SETUP([ovn -- LS load balancer logical flows])
>>> +ovn_start
>>> +
>>> +check ovn-nbctl \
>>> +    -- ls-add sw0 \
>>> +    -- lb-add lb0 10.0.0.10:80 10.0.0.4:8080 \
>>> +    -- ls-lb-add sw0 lb0
>>> +
>>> +check ovn-nbctl lr-add lr0
>>> +check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
>>> +check ovn-nbctl lsp-add sw0 sw0-lr0
>>> +check ovn-nbctl lsp-set-type sw0-lr0 router
>>> +check ovn-nbctl lsp-set-addresses sw0-lr0 00:00:00:00:ff:01
>>> +check ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
>>> +
>>> +check ovn-nbctl --wait=sb sync
>>> +
>>> +check_stateful_flows() {
>>> +    ovn-sbctl dump-flows sw0 > sw0flows
>>> +    AT_CAPTURE_FILE([sw0flows])
>>> +
>>> +    AT_CHECK([grep "ls_in_pre_lb" sw0flows | sort], [0], [dnl
>>> +  table=6 (ls_in_pre_lb       ), priority=0    , match=(1), action=(next;)
>>> +  table=6 (ls_in_pre_lb       ), priority=100  , match=(ip), action=(reg0[[2]] = 1; next;)
>>> +  table=6 (ls_in_pre_lb       ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(next;)
>>> +  table=6 (ls_in_pre_lb       ), priority=110  , match=(ip && inport == "sw0-lr0"), action=(next;)
>>> +  table=6 (ls_in_pre_lb       ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2), action=(next;)
>>> +])
>>> +
>>> +    AT_CHECK([grep "ls_in_pre_stateful" sw0flows | sort], [0], [dnl
>>> +  table=7 (ls_in_pre_stateful ), priority=0    , match=(1), action=(next;)
>>> +  table=7 (ls_in_pre_stateful ), priority=100  , match=(reg0[[0]] == 1), action=(ct_next;)
>>> +  table=7 (ls_in_pre_stateful ), priority=110  , match=(reg0[[2]] == 1), action=(ct_lb;)
>>> +  table=7 (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip4 && sctp), action=(reg1 = ip4.dst; reg2[[0..15]] = sctp.dst; ct_lb;)
>>> +  table=7 (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip4 && tcp), action=(reg1 = ip4.dst; reg2[[0..15]] = tcp.dst; ct_lb;)
>>> +  table=7 (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip4 && udp), action=(reg1 = ip4.dst; reg2[[0..15]] = udp.dst; ct_lb;)
>>> +  table=7 (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip6 && sctp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = sctp.dst; ct_lb;)
>>> +  table=7 (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip6 && tcp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = tcp.dst; ct_lb;)
>>> +  table=7 (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip6 && udp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = udp.dst; ct_lb;)
>>> +])
>>> +
>>> +    AT_CHECK([grep "ls_in_lb" sw0flows | sort], [0], [])
>>> +
>>> +    AT_CHECK([grep "ls_in_stateful" sw0flows | sort], [0], [dnl
>>> +  table=12(ls_in_stateful     ), priority=0    , match=(1), action=(next;)
>>> +  table=12(ls_in_stateful     ), priority=100  , match=(reg0[[1]] == 1), action=(ct_commit { ct_label.blocked = 0; }; next;)
>>> +  table=12(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.4:8080);)
>>> +])
>>> +
>>> +    AT_CHECK([grep "ls_out_pre_lb" sw0flows | sort], [0], [dnl
>>> +  table=0 (ls_out_pre_lb      ), priority=0    , match=(1), action=(next;)
>>> +  table=0 (ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[2]] = 1; next;)
>>> +  table=0 (ls_out_pre_lb      ), priority=110  , match=(eth.src == $svc_monitor_mac), action=(next;)
>>> +  table=0 (ls_out_pre_lb      ), priority=110  , match=(ip && outport == "sw0-lr0"), action=(next;)
>>> +  table=0 (ls_out_pre_lb      ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2), action=(next;)
>>> +])
>>> +
>>> +    AT_CHECK([grep "ls_out_pre_stateful" sw0flows | sort], [0], [dnl
>>> +  table=2 (ls_out_pre_stateful), priority=0    , match=(1), action=(next;)
>>> +  table=2 (ls_out_pre_stateful), priority=100  , match=(reg0[[0]] == 1), action=(ct_next;)
>>> +  table=2 (ls_out_pre_stateful), priority=110  , match=(reg0[[2]] == 1), action=(ct_lb;)
>>> +])
>>> +
>>> +    AT_CHECK([grep "ls_out_lb" sw0flows | sort], [0], [])
>>
>> This might be a bit cryptic in the future ("what is the ls_out_lb
>> table?!?") as it will not exist. Do we really need this check here? If
>> so, maybe add  a comment to explain why it is here.
> 
> I will remove it in v4.
> 
>>> +
>>> +    AT_CHECK([grep "ls_out_stateful" sw0flows | sort], [0], [dnl
>>> +  table=7 (ls_out_stateful    ), priority=0    , match=(1), action=(next;)
>>> +  table=7 (ls_out_stateful    ), priority=100  , match=(reg0[[1]] == 1), action=(ct_commit { ct_label.blocked = 0; }; next;)
>>> +])
>>> +}
>>> +
>>> +check_stateful_flows
>>> +
>>> +# Add few ACLs
>>> +check ovn-nbctl --wait=sb acl-add sw0 from-lport 1002 "ip4 && tcp && tcp.dst == 80" allow-related
>>> +check ovn-nbctl --wait=sb acl-add sw0 to-lport 1002 "ip4 && tcp && tcp.src == 80" drop
>>> +
>>> +check_stateful_flows
>>> +
>>> +# Remove load balancer from sw0
>>> +check ovn-nbctl --wait=sb ls-lb-del sw0 lb0
>>> +
>>> +ovn-sbctl dump-flows sw0 > sw0flows
>>> +AT_CAPTURE_FILE([sw0flows])
>>> +
>>> +AT_CHECK([grep "ls_in_pre_lb" sw0flows | sort], [0], [dnl
>>> +  table=6 (ls_in_pre_lb       ), priority=0    , match=(1), action=(next;)
>>> +  table=6 (ls_in_pre_lb       ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(next;)
>>> +  table=6 (ls_in_pre_lb       ), priority=110  , match=(ip && inport == "sw0-lr0"), action=(next;)
>>> +  table=6 (ls_in_pre_lb       ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2), action=(next;)
>>> +])
>>> +
>>> +AT_CHECK([grep "ls_in_pre_stateful" sw0flows | sort], [0], [dnl
>>> +  table=7 (ls_in_pre_stateful ), priority=0    , match=(1), action=(next;)
>>> +  table=7 (ls_in_pre_stateful ), priority=100  , match=(reg0[[0]] == 1), action=(ct_next;)
>>> +  table=7 (ls_in_pre_stateful ), priority=110  , match=(reg0[[2]] == 1), action=(ct_lb;)
>>> +  table=7 (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip4 && sctp), action=(reg1 = ip4.dst; reg2[[0..15]] = sctp.dst; ct_lb;)
>>> +  table=7 (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip4 && tcp), action=(reg1 = ip4.dst; reg2[[0..15]] = tcp.dst; ct_lb;)
>>> +  table=7 (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip4 && udp), action=(reg1 = ip4.dst; reg2[[0..15]] = udp.dst; ct_lb;)
>>> +  table=7 (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip6 && sctp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = sctp.dst; ct_lb;)
>>> +  table=7 (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip6 && tcp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = tcp.dst; ct_lb;)
>>> +  table=7 (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip6 && udp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = udp.dst; ct_lb;)
>>> +])
>>> +
>>> +AT_CHECK([grep "ls_in_stateful" sw0flows | sort], [0], [dnl
>>> +  table=12(ls_in_stateful     ), priority=0    , match=(1), action=(next;)
>>> +  table=12(ls_in_stateful     ), priority=100  , match=(reg0[[1]] == 1), action=(ct_commit { ct_label.blocked = 0; }; next;)
>>> +])
>>> +
>>> +AT_CHECK([grep "ls_out_pre_lb" sw0flows | sort], [0], [dnl
>>> +  table=0 (ls_out_pre_lb      ), priority=0    , match=(1), action=(next;)
>>> +  table=0 (ls_out_pre_lb      ), priority=110  , match=(eth.src == $svc_monitor_mac), action=(next;)
>>> +  table=0 (ls_out_pre_lb      ), priority=110  , match=(ip && outport == "sw0-lr0"), action=(next;)
>> It seems like this^ flow is not described in the man page for
>> ovn-northd. You did not introduce this but maybe you could update it by
>> coping the description from the Ingress table.
> 
> If you see the documentation, you would notice that such flows which
> have corresponding
> flows in the ingress pipeline are not explictly documented.  The
> documentation generally says
> "This table is similar to the ingress table".
> 
> So I will AS IS now.

Ack.

I had a look at it again. I think what threw me was that the ingress
table says:

"       This  table also has a priority-110 flow with the match -->
inport <----- == I for all logical switch datapaths to move traffic
       to the next table. Where I is the peer of a logical router port.
This flow is added to skip the connection  tracking
       of packets which enter from logical router datapath to logical
switch datapath.
"

For this table inport->output but that is "similar" as you said.

> 
> Thanks
> Numan
> 
>>> +  table=0 (ls_out_pre_lb      ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2), action=(next;)
>>> +])
>>> +
>>> +AT_CHECK([grep "ls_out_pre_stateful" sw0flows | sort], [0], [dnl
>>> +  table=2 (ls_out_pre_stateful), priority=0    , match=(1), action=(next;)
>>> +  table=2 (ls_out_pre_stateful), priority=100  , match=(reg0[[0]] == 1), action=(ct_next;)
>>> +  table=2 (ls_out_pre_stateful), priority=110  , match=(reg0[[2]] == 1), action=(ct_lb;)
>>> +])
>>> +
>>> +AT_CHECK([grep "ls_out_stateful" sw0flows | sort], [0], [dnl
>>> +  table=7 (ls_out_stateful    ), priority=0    , match=(1), action=(next;)
>>> +  table=7 (ls_out_stateful    ), priority=100  , match=(reg0[[1]] == 1), action=(ct_commit { ct_label.blocked = 0; }; next;)
>>> +])
>>> +
>>> +AT_CLEANUP
>>> +])
>>> diff --git a/tests/ovn.at b/tests/ovn.at
>>> index 73cc8bf025..d7da84459c 100644
>>> --- a/tests/ovn.at
>>> +++ b/tests/ovn.at
>>> @@ -14088,16 +14088,16 @@ check ovn-nbctl acl-add ls1 to-lport 3 'ip4.src==10.0.0.1' allow
>>>  check ovn-nbctl --wait=hv sync
>>>
>>>  # Check OVS flows, the less restrictive flows should have been installed.
>>> -AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | ofctl_strip_all | \
>>> +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=44 | ofctl_strip_all | \
>>>      grep "priority=1003" | \
>>>      sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl
>>> - table=45, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46)
>>> - table=45, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46)
>>> - table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction()
>>> - table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction()
>>> - table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46)
>>> - table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction()
>>> - table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction()
>>> + table=44, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,45)
>>> + table=44, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,45)
>>> + table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction()
>>> + table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction()
>>> + table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,45)
>>> + table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction()
>>> + table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction()
>>>  ])
>>>
>>>  # Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.3, 10.0.0.4 should be allowed.
>>> @@ -14132,16 +14132,16 @@ check ovn-nbctl acl-del ls1 to-lport 3 'ip4.src==10.0.0.1 || ip4.src==10.0.0.1'
>>>  check ovn-nbctl --wait=hv sync
>>>
>>>  # Check OVS flows, the second less restrictive allow ACL should have been installed.
>>> -AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | ofctl_strip_all | \
>>> +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=44 | ofctl_strip_all | \
>>>      grep "priority=1003" | \
>>>      sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl
>>> - table=45, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46)
>>> - table=45, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46)
>>> - table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction()
>>> - table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction()
>>> - table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46)
>>> - table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction()
>>> - table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction()
>>> + table=44, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,45)
>>> + table=44, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,45)
>>> + table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction()
>>> + table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction()
>>> + table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,45)
>>> + table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction()
>>> + table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction()
>>>  ])
>>>
>>>  # Remove the less restrictive allow ACL.
>>> @@ -14149,16 +14149,16 @@ check ovn-nbctl acl-del ls1 to-lport 3 'ip4.src==10.0.0.1'
>>>  check ovn-nbctl --wait=hv sync
>>>
>>>  # Check OVS flows, the 10.0.0.1 conjunction should have been reinstalled.
>>> -AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | ofctl_strip_all | \
>>> +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=44 | ofctl_strip_all | \
>>>      grep "priority=1003" | \
>>>      sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl
>>> - table=45, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46)
>>> - table=45, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46)
>>> - table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction()
>>> - table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction()
>>> - table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=conjunction(),conjunction()
>>> - table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction()
>>> - table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction()
>>> + table=44, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,45)
>>> + table=44, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,45)
>>> + table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction()
>>> + table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction()
>>> + table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=conjunction(),conjunction()
>>> + table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction()
>>> + table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction()
>>>  ])
>>>
>>>  # Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.3, 10.0.0.4 should be allowed.
>>> @@ -14188,16 +14188,16 @@ check ovn-nbctl acl-add ls1 to-lport 3 'ip4.src==10.0.0.1' allow
>>>  check ovn-nbctl --wait=hv sync
>>>
>>>  # Check OVS flows, the less restrictive flows should have been installed.
>>> -AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | ofctl_strip_all | \
>>> +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=44 | ofctl_strip_all | \
>>>     grep "priority=1003" | \
>>>     sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl
>>> - table=45, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46)
>>> - table=45, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46)
>>> - table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction()
>>> - table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction()
>>> - table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46)
>>> - table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction()
>>> - table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction()
>>> + table=44, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,45)
>>> + table=44, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,45)
>>> + table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction()
>>> + table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction()
>>> + table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,45)
>>> + table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction()
>>> + table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction()
>>>  ])
>>>
>>>  # Add another ACL that overlaps with the existing less restrictive ones.
>>> @@ -14208,19 +14208,19 @@ check ovn-nbctl --wait=hv sync
>>>  # with an additional conjunction action.
>>>  #
>>>  # New non-conjunctive flows should be added to match on 'udp'.
>>> -AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | ofctl_strip_all | \
>>> +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=44 | ofctl_strip_all | \
>>>     grep "priority=1003" | \
>>>     sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl
>>> - table=45, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46)
>>> - table=45, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46)
>>> - table=45, priority=1003,conj_id=4,ip,metadata=0x1 actions=resubmit(,46)
>>> - table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction(),conjunction()
>>> - table=45, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction(),conjunction()
>>> - table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46)
>>> - table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction(),conjunction()
>>> - table=45, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction()
>>> - table=45, priority=1003,udp,metadata=0x1 actions=resubmit(,46)
>>> - table=45, priority=1003,udp6,metadata=0x1 actions=resubmit(,46)
>>> + table=44, priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,45)
>>> + table=44, priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,45)
>>> + table=44, priority=1003,conj_id=4,ip,metadata=0x1 actions=resubmit(,45)
>>> + table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(),conjunction(),conjunction()
>>> + table=44, priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(),conjunction(),conjunction()
>>> + table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,45)
>>> + table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction(),conjunction()
>>> + table=44, priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction()
>>> + table=44, priority=1003,udp,metadata=0x1 actions=resubmit(,45)
>>> + table=44, priority=1003,udp6,metadata=0x1 actions=resubmit(,45)
>>>  ])
>>>
>>>  OVN_CLEANUP([hv1])
>>> @@ -15549,7 +15549,7 @@ wait_for_ports_up ls1-lp_ext1
>>>  # There should be a flow in hv2 to drop traffic from ls1-lp_ext1 destined
>>>  # to router mac.
>>>  AT_CHECK([as hv2 ovs-ofctl dump-flows br-int \
>>> -table=30,dl_src=f0:00:00:00:00:03,dl_dst=a0:10:00:00:00:01 | \
>>> +table=29,dl_src=f0:00:00:00:00:03,dl_dst=a0:10:00:00:00:01 | \
>>>  grep -c "actions=drop"], [0], [1
>>>  ])
>>>
>>> @@ -19994,7 +19994,14 @@ AT_CAPTURE_FILE([sbflows])
>>>  OVS_WAIT_FOR_OUTPUT(
>>>    [ovn-sbctl dump-flows > sbflows
>>>     ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 | sed 's/table=..//'], 0,
>>> -  [  (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80; hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
>>> +  [dnl
>>> +  (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip4 && sctp), action=(reg1 = ip4.dst; reg2[[0..15]] = sctp.dst; ct_lb;)
>>> +  (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip4 && tcp), action=(reg1 = ip4.dst; reg2[[0..15]] = tcp.dst; ct_lb;)
>>> +  (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip4 && udp), action=(reg1 = ip4.dst; reg2[[0..15]] = udp.dst; ct_lb;)
>>> +  (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip6 && sctp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = sctp.dst; ct_lb;)
>>> +  (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip6 && tcp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = tcp.dst; ct_lb;)
>>> +  (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip6 && udp), action=(xxreg1 = ip6.dst; reg2[[0..15]] = udp.dst; ct_lb;)
>>> +  (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; ct_lb(backends=10.0.0.3:80,20.0.0.3:80; hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
>>>  ])
>>>
>>>  AT_CAPTURE_FILE([sbflows2])
>>> @@ -22731,7 +22738,7 @@ check ovn-nbctl --wait=hv sync
>>>  # wait_conj_id_count COUNT ["ID COUNT [MATCH]"]...
>>>  #
>>>  # Waits until COUNT flows matching against conj_id appear in the
>>> -# table 45 on hv1's br-int bridge.  Makes the flows available in
>>> +# table 44 on hv1's br-int bridge.  Makes the flows available in
>>>  # "hv1flows", which will be logged on error.
>>>  #
>>>  # In addition, for each quoted "ID COUNT" or "ID COUNT MATCH",
>>> @@ -22748,7 +22755,7 @@ wait_conj_id_count() {
>>>    echo "waiting for $1 conj_id flows..."
>>>    OVS_WAIT_FOR_OUTPUT_UNQUOTED(
>>>      [ovs-ofctl dump-flows br-int > hv1flows
>>> -     grep table=45 hv1flows | grep -c conj_id],
>>> +     grep table=44 hv1flows | grep -c conj_id],
>>>      [$retval], [$1
>>>  ])
>>>
>>> @@ -22757,7 +22764,7 @@ wait_conj_id_count() {
>>>      set -- $arg; id=$1 count=$2 match=$3
>>>      echo "checking that there are $count ${match:+$match }flows with conj_id=$id..."
>>>      AT_CHECK_UNQUOTED(
>>> -      [grep table=45 hv1flows | grep "$match" | grep -c conj_id=$id],
>>> +      [grep table=44 hv1flows | grep "$match" | grep -c conj_id=$id],
>>>        [0], [$count
>>>  ])
>>>    done
>>> @@ -22782,8 +22789,8 @@ wait_conj_id_count 1 "3 1 udp"
>>>  AS_BOX([Add back the tcp ACL.])
>>>  check ovn-nbctl --wait=hv acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && tcp.dst >= 80 && tcp.dst <= 82" allow
>>>  wait_conj_id_count 2 "3 1 udp" "4 1 tcp"
>>> -AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep udp | grep -c "conj_id=3")])
>>> -AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep tcp | grep -c "conj_id=4")])
>>> +AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep udp | grep -c "conj_id=3")])
>>> +AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=44 | grep tcp | grep -c "conj_id=4")])
>>>
>>>  AS_BOX([Add another tcp ACL.])
>>>  check ovn-nbctl --wait=hv acl-add pg0 to-lport 1002 "outport == @pg0 && inport == @pg0 && ip4 && tcp.dst >= 84 && tcp.dst <= 86" allow
>>> @@ -24750,43 +24757,43 @@ AT_CHECK([kill -0 $(cat hv1/ovn-controller.pid)])
>>>  check ovn-nbctl --wait=hv sync
>>>
>>>  # Check OVS flows are installed properly.
>>> -AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | ofctl_strip_all | \
>>> +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=44 | ofctl_strip_all | \
>>>      grep "priority=2002" | grep conjunction | \
>>>      sed 's/conjunction([[^)]]*)/conjunction()/g' | sort], [0], [dnl
>>> - table=45, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x10/0xfff0 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x100/0xff00 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x1000/0xf000 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x2/0xfffe actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x20/0xffe0 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x200/0xfe00 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x2000/0xe000 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x4/0xfffc actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x40/0xffc0 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x400/0xfc00 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x4000/0xc000 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x8/0xfff8 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x80/0xff80 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x800/0xf800 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x8000/0x8000 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=1 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x100/0x100,reg15=0x3,metadata=0x1,nw_src=192.168.47.3 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x10/0xfff0 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x100/0xff00 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x1000/0xf000 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x2/0xfffe actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x20/0xffe0 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x200/0xfe00 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x2000/0xe000 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x4/0xfffc actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x40/0xffc0 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x400/0xfc00 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x4000/0xc000 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x8/0xfff8 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x80/0xff80 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x800/0xf800 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x8000/0x8000 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=1 actions=conjunction()
>>> - table=45, priority=2002,udp,reg0=0x80/0x80,reg15=0x3,metadata=0x1,nw_src=192.168.47.3 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x10/0xfff0 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x100/0xff00 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x1000/0xf000 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x2/0xfffe actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x20/0xffe0 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x200/0xfe00 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x2000/0xe000 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x4/0xfffc actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x40/0xffc0 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x400/0xfc00 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x4000/0xc000 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x8/0xfff8 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x80/0xff80 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x800/0xf800 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x8000/0x8000 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x100/0x100,metadata=0x1,nw_src=192.168.47.3,tp_dst=1 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x100/0x100,reg15=0x3,metadata=0x1,nw_src=192.168.47.3 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x10/0xfff0 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x100/0xff00 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x1000/0xf000 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x2/0xfffe actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x20/0xffe0 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x200/0xfe00 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x2000/0xe000 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x4/0xfffc actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x40/0xffc0 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x400/0xfc00 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x4000/0xc000 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x8/0xfff8 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x80/0xff80 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x800/0xf800 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=0x8000/0x8000 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x80/0x80,metadata=0x1,nw_src=192.168.47.3,tp_dst=1 actions=conjunction()
>>> + table=44, priority=2002,udp,reg0=0x80/0x80,reg15=0x3,metadata=0x1,nw_src=192.168.47.3 actions=conjunction()
>>>  ])
>>>
>>>  OVN_CLEANUP([hv1])
>>>
>>
>>
>> _______________________________________________
>> dev mailing list
>> dev at openvswitch.org
>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>
> 



More information about the dev mailing list