On Mon, Oct 7, 2024 at 11:01 AM Roberto Bartzen Acosta
<[email protected]> wrote:
>
> Thanks Numan!
>
> Em seg., 7 de out. de 2024 às 11:34, Numan Siddique <[email protected]> 
> escreveu:
>>
>> On Mon, Sep 30, 2024 at 1:57 PM Roberto Bartzen Acosta via dev
>> <[email protected]> wrote:
>> >
>> > Hi Numan,
>> >
>> > Em sex., 27 de set. de 2024 às 12:47, Numan Siddique <[email protected]>
>> > escreveu:
>> >
>> > > On Thu, Sep 26, 2024 at 10:55 AM Roberto Bartzen Acosta via dev
>> > > <[email protected]> wrote:
>> > > >
>> > > > Hi Numan,
>> > > >
>> > > > Thanks for your feedback and review.
>> > > >
>> > > > Em qua., 25 de set. de 2024 às 19:50, Numan Siddique <[email protected]>
>> > > > escreveu:
>> > > >
>> > > > > On Thu, Sep 19, 2024 at 6:12 PM Roberto Bartzen Acosta via dev
>> > > > > <[email protected]> wrote:
>> > > > > >
>> > > > > > This commit fixes the build_distr_lrouter_nat_flows_for_lb function
>> > > to
>> > > > > > include a DNAT flow entry for each DGP in use. Since we have added
>> > > > > support
>> > > > > > to create multiple gateway ports per logical router, it's necessary
>> > > to
>> > > > > > include in the LR NAT rules pipeline a specific entry for each
>> > > attached
>> > > > > DGP.
>> > > > > > Otherwise, the inbound traffic will only be redirected when the
>> > > incoming
>> > > > > LRP
>> > > > > > matches the chassis_resident field.
>> > > > > >
>> > > > > > Additionally, this patch includes the ability to use load-balancer
>> > > with
>> > > > > DGPs
>> > > > > > attached to multiple chassis. We can have each of the DGPs 
>> > > > > > associated
>> > > > > with a
>> > > > > > different chassis, and in this case the DNAT rules added by default
>> > > will
>> > > > > not
>> > > > > > be enough to guarantee outgoing traffic.
>> > > > > >
>> > > > > > To solve the multiple chassis for DGPs problem, this patch include 
>> > > > > > a
>> > > new
>> > > > > > config options to be configured in the load-balancer. If the
>> > > > > use_stateless_nat
>> > > > > > is set to true, the logical router that references this 
>> > > > > > load-balancer
>> > > > > will use
>> > > > > > Stateless NAT rules when the logical router has multiple DGPs. 
>> > > > > > After
>> > > > > applying
>> > > > > > this patch and setting the use_stateless_nat option, the inbound
>> > > and/or
>> > > > > > outbound traffic can pass through any chassis where the DGP resides
>> > > > > without
>> > > > > > having problems with CT state.
>> > > > > >
>> > > > > > Reported-at:
>> > > https://bugs.launchpad.net/ubuntu/+source/ovn/+bug/2054322
>> > > > > > Fixes: 15348b7b806f ("ovn-northd: Multiple distributed gateway port
>> > > > > support.")
>> > > > > >
>> > > > > > Signed-off-by: Roberto Bartzen Acosta 
>> > > > > > <[email protected]>
>> > > > >
>> > > > > Hi Roberto,
>> > > > >
>> > > > > Thanks for the patch.  I tested this patch using the test example in
>> > > > > multinode.at.
>> > > > >
>> > > > > The test case adds the below load balancer
>> > > > >
>> > > > > [root@ovn-central ~]# ovn-nbctl lb-list
>> > > > > UUID                                    LB                  PROTO
>> > > > > VIP                  IPs
>> > > > > f3e29869-3bb5-4df0-960a-171106f5913a    lb0                 tcp
>> > > > > 172.16.0.100:9000    10.0.0.3:80,10.0.0.4:80
>> > > > >
>> > > > > And the below logical flows are generated by this patch
>> > > > >
>> > > > > --------
>> > > > > [root@ovn-central ~]# ovn-sbctl dump-flows lr0 | grep 172.16.0.100
>> > > > >   table=6 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> > > > > == 172.16.0.100), action=(ct_dnat;)
>> > > > >   table=8 (lr_in_dnat         ), priority=120  , match=(ct.new &&
>> > > > > !ct.rel && ip4 && ip4.dst == 172.16.0.100 && tcp && tcp.dst == 9000 
>> > > > > &&
>> > > > > is_chassis_resident("cr-lr0-public-p1")),
>> > > > > action=(ip4.dst=10.0.0.3;ip4.dst=10.0.0.4;ct_lb_mark(backends=
>> > > 10.0.0.3:80,
>> > > > > 10.0.0.4:80);)
>> > > > >   table=8 (lr_in_dnat         ), priority=120  , match=(ct.new &&
>> > > > > !ct.rel && ip4 && ip4.dst == 172.16.0.100 && tcp && tcp.dst == 9000 
>> > > > > &&
>> > > > > is_chassis_resident("cr-lr0-public-p2")),
>> > > > > action=(ip4.dst=10.0.0.3;ip4.dst=10.0.0.4;ct_lb_mark(backends=
>> > > 10.0.0.3:80,
>> > > > > 10.0.0.4:80);)
>> > > > >   table=3 (lr_out_snat        ), priority=160  , match=(ip4 &&
>> > > > > ((ip4.src == 10.0.0.3 && tcp.src == 80) || (ip4.src == 10.0.0.4 &&
>> > > > > tcp.src == 80)) && (inport == "lr0-public-p1" || outport ==
>> > > > > "lr0-public-p1") && is_chassis_resident("cr-lr0-public-p1") && tcp),
>> > > > > action=(ip4.src=172.16.0.100; tcp.src=9000; next;)
>> > > > >   table=3 (lr_out_snat        ), priority=160  , match=(ip4 &&
>> > > > > ((ip4.src == 10.0.0.3 && tcp.src == 80) || (ip4.src == 10.0.0.4 &&
>> > > > > tcp.src == 80)) && (inport == "lr0-public-p2" || outport ==
>> > > > > "lr0-public-p2") && is_chassis_resident("cr-lr0-public-p2") && tcp),
>> > > > > action=(ip4.src=172.16.0.100; tcp.src=9000; next;)
>> > > > > --------------
>> > > > >
>> > > > >
>> > > > > I fail to understand the reason for modifying the ip4.dst before
>> > > > > calling ct_lb_mark.  Can you please explain why ?  Because the
>> > > > > action=(ip4.dst=10.0.0.3;ip4.dst=10.0.0.4;) will first modify the
>> > > > > ip4.dst to 10.0.0.3 and
>> > > > > then to 10.0.0.4 and then the ct_lb_mark will actually do the
>> > > > > conntrack with NAT to either 10.0.0.3 or 10.0.0.4.
>> > > > >
>> > > > > Is it because you want the conntrack entry to not have 172.16.0.100 ?
>> > > > >
>> > > >
>> > >
>> > > > The only reason I included this ip4.dst action in the DNAT rule is
>> > > because
>> > > > it's required to accept packets coming from a chassis that doesn't have
>> > > > previously created conntrack entries. The main feature introduced in 
>> > > > this
>> > > > patch is to allow the administrator to have multiple DGPs attached to
>> > > > different chassis (is_chassis_resident...). So, my implementation was
>> > > based
>> > > > on the normal behavior when using stateless NAT for external addresses,
>> > > > where we need to add the ipx.dst in lr_in_dnat for traffic to be 
>> > > > received
>> > > > on the chassis (put the DGP port as chassis_resident match, as is the
>> > > case
>> > > > with stateless NAT [1] with DGP[2]).
>> > > >
>> > > > The question is, if we only have the ct_lb_mark, packets that pass
>> > > through
>> > > > the chassis and are already part of an active flow in another chassis
>> > > (same
>> > > > IPs and Ports) will be dropped because there is no correspondence in 
>> > > > the
>> > > > backend. So only packets with the NEW flag will be accepted and sent to
>> > > the
>> > > > backend (at least for TCP traffic). If we only have the ip4.dst action,
>> > > > this will always perform the dnat for the same backend, without
>> > > balancing.
>> > > > Therefore, the combination of the two actions allows the packet to 
>> > > > always
>> > > > be received (regardless of whether conntrack is active for it), and
>> > > > ct_lb_mark will take care of balancing for different backends.
>> > > >
>> > > > If we had conntrack sync between different chassis this would not be
>> > > > necessary, as the ct_lb_mark action could always be executed without
>> > > > dropping packets due to lack of correspondence in the conntrack table.
>> > > >
>> > > > [1]
>> > > >
>> > > https://github.com/ovn-org/ovn/blob/b93e9a5e6f3aa3cb3e2065bd8e0aa0b6fc1fd19a/northd/northd.c#L15737
>> > > > [2]
>> > > >
>> > > https://github.com/ovn-org/ovn/blob/b93e9a5e6f3aa3cb3e2065bd8e0aa0b6fc1fd19a/northd/northd.c#L15726
>> > > >
>> > > >
>> > >
>> > > I'm sorry, but it's not 100% clear to me.  I know that you've already
>> > > explained to Mark in the older version of this patch.
>> > > Can you please explain with an example ?
>> > >
>> > > Let's take the below topology you've added in the mutlinode test as 
>> > > example
>> > >
>> > > # Network topology
>> > > #
>> > > #             publicp1 (ovn-chassis-3) (20.0.0.3/24)
>> > > #                |
>> > > #              overlay
>> > > #                |
>> > > #      DGP public1 (ovn-gw-1) (20.0.0.1/24)
>> > > #                |
>> > > #                |
>> > > #                |
>> > > #               lr0 ------- sw0 --- sw0p1 (ovn-chassis-1) 10.0.0.3/24
>> > > #                |           |
>> > > #                |           + ---  sw0p2 (ovn-chassis-2) 10.0.0.4/24
>> > > #                |
>> > > #      DGP public2 (ovn-gw-2) (30.0.0.1/24)
>> > > #                |
>> > > #              overlay
>> > > #                |
>> > > #             publicp2 (ovn-chassis-4) (30.0.0.3/24)
>> > >
>> > >
>> > > load balancer is configured on lr0 ->   ovn-nbctl lb-add lb0
>> > > "172.16.0.100:80" "10.0.0.3:80,10.0.0.4:80"
>> > > and it is attached to both lr0 and sw0.
>> > >
>> > > Scenario 1:
>> > >
>> > > publicp1 with IP 20.0.0.3 sends TCP traffic to VIP 172.16.0.100.  I
>> > > think this is what will happen
>> > >
>> > >   - The packet (ip4.src = 20.0.0.3 , ip4.dst = 172.16.0.100,  tcp.dst
>> > > = 80) from ovn-chassis-3 will be sent out via the localnet bridge
>> > > (br-ex)
>> > >     and the packet is received on ovn-gw-1 (as DGP public1 is resident
>> > > on it) via the localnet bridge and the
>> > >     packet first enters public logical switch pipeline and then the
>> > > lr0 router pipeline.
>> > >
>> > >  -  In the router's lr_in_dnat state,  the packet will be load
>> > > balanced to one of the backends using ct_lb_mark.  Lets say 10.0.0.3
>> > > is chosen
>> > >  - The packet from router pipeline lr0 enters sw0 switch pipeline and
>> > > then the packet is tunneled to ovn-chassis-1 and delivered to sw0p1.
>> > > - The reply packet (ip4.src = 10.0.0.3, ip4.dst = 20.0.0.3, tcp.src =
>> > > 80) will enter the router pipeline and since 20.0.0.0/24 is handled by
>> > > DGP public1,
>> > >    the packet is tunnelled to ovn-gw-1.
>> > > - In the router pipeline of ovn-gw-1, the packet is undnatted from
>> > > ip4.src 10.0.0.3  to 172.16.0.100 and the packet is sent out via the
>> > > localnet bridge.
>> > >  - ovn-chassis-3 receives the packet via the localnet bridge and into
>> > > br-int and finally to publicp1.
>> > >
>> > >
>> > > In this scenario the load balancing is handled and conntrack entries
>> > > are created in ovn-gw-1.  And there is no need to add flows in
>> > > "lr_out_snat" for stateless NAT
>> > > or set ip4.dst to one or all of backend IPs before "ct_lb_mark" in
>> > > lr_in_dnat stage.
>> > >
>> > > Scenario 2:
>> > > publicp2 with IP 30.0.0.3 sends TCP traffic to VIP 172.16.0.100.
>> > >
>> > > This is similar to scenario 1.  Except that load balancing happens in
>> > > ovn-gw-2.
>> > > since DGP public2 is on this chassis.
>> > >
>> > >
>> > > Scenario 3:
>> > >
>> > > An external entity with IP 20.0.0.50 sends  TCP traffic to VIP
>> > > 172.16.0.100.
>> > >
>> > >  - This scenario is similar to the first one. The packet from this
>> > > external entity is received on ovn-gw-1 via the localnet bridge.
>> > >     Rest all is the same.
>> > >
>> > > Scenario 4:
>> > >
>> > >  sw0p2 on ovn-chassis-2 sends TCP traffic to VIP 172.16.0.100.
>> > >
>> > >   - Since sw0 is attached with load balancer lb0,  load balancing
>> > > happens in the source chassis - ovn-chassis-2 itself and depending on
>> > > the backend chosen,
>> > >      the packet is tunnelled to ovn-chassis-1 (if 10.0.0.3 is chosen)
>> > > or delivered directly to sw0p2 (if i0.0.0.4 is chosen).
>> > >
>> > >
>> > > Scenario 5:
>> > >
>> > >  An external entity with IP 40.0.0.40 sends TCP traffic to VIP
>> > > 172.16.0.100 and there are 2 ECMP routes configured
>> > >   172.16.0.100 via 20.0.0.1
>> > >   172.16.0.100 via 30.0.0.1
>> > >
>> > > In this case if the packet uses the route via 20.0.0.1 it will be
>> > > received on ovn-gw-1 via the localnet bridge br-ex.
>> > >
>> > >  - With your patch, ip4.dst  is modified to the last backend in the
>> > > list and then ct_lb_mark chosen as one of the backends
>> > >   - And then it is tunnelled to the destination chassis.
>> > >
>> > >
>> > > Is this the scenario 5 you're trying to address  with the stateless
>> > > NAT ?  How would the reply packet work ?
>> > > The reply packet from backend 10.0.0.3 can use either of the paths ?
>> > > i.e ovn-gw-1 or ovn-gw-2 ?
>> > >
>> >
>> >
>> > Yes, this scenario would be the main goal of this patch.
>> > Let me explain with an example related to the OVN interconnect (one of the
>> > use cases of this patch). In the context of ovn-ic the backend 10.0.0.3
>> > could reply packets using either of the paths (ovn-gw-1 or ovn-gw-2).
>> >
>> >
>> >
>> >                                                  LB VIP 172.16.0.100
>> >
>> >                                                                     |
>> > vm (40.0.0.40) - LR-EXT - lrp1 - chassis1 - Transit switch TS1 -  ovn-gw-1
>> > - lr0-public-p1 - lr0 - sw0 - VM 10.0.0.3 (ovn-chassis-1)
>> >                                         - lrp2 - chassis2 - Transit switch
>> > TS2 -  ovn-gw-2 - lr0-public-p2 -                - VM 10.0.0.4
>> > (ovn-chassis-2)
>> >
>> >
>> > Let's take an example of the ovn-ic in the above topology:
>> >
>> > Assumptions:
>> > A) logical router LR-EXT: route to 172.16.0.100/32 via ECMP (lrp1/chassis1
>> > and lrp2/chassis2)
>> > B) logical router lr0: route to 40.0.0.0/24 via ECMP
>> > (lr0-public-p1/ovn-gw-1 and lr0-public-p2/ovn-gw-2)
>> >
>> >
>> > 1 - VM 40.0.0.40 send a request to the LB 172.16.0.100:80
>> > 2 - LR-EXT chooses one of the possible outgoing routes via ECMP - e.g. lrp1
>> > / chassis1 / TS1
>> > 3 - The lr0 receives the ingoing traffic through the port lr0-public-p1 /
>> > ovn-gw-1 because the TS1 is used to start the traffic
>> > 4 - with this patch we added lr_in_dnat rules for all DGPs (with or without
>> > the stateless NAT config flag)
>> >
>> > with the stateless NAT config flag True:
>> >    ...is_chassis_resident("cr-lr0-public-p1")),
>> > action=(ip4.dst=10.0.0.3;ip4.dst=10.0.0.4;ct_lb_mark(backends=10.0.0.3:80
>> > ,10.0.0.4:80);)
>> >    ...is_chassis_resident("cr-lr0-public-p2")),
>> > action=(ip4.dst=10.0.0.3;ip4.dst=10.0.0.4;ct_lb_mark(backends=10.0.0.3:80
>> > ,10.0.0.4:80);)
>> > 5 - The ip4.dst will perform the action cumulatively, basically the last
>> > ip4.dst action applied will be to change dst to 10.0.0.4.
>> > 5.1 - The ct_lb_mark will check the conntrack and validate if the traffic
>> > refers to a new one (TCP SYNC), and then, it executes the final action
>> > provided by ct_lb_mark and select one of the available backends. e.g
>> > 10.0.0.3
>> >
>> > without the stateless NAT config flag:
>> >    ...is_chassis_resident("cr-lr0-public-p1")), 
>> > action=(ct_lb_mark(backends=
>> > 10.0.0.3:80,10.0.0.4:80);)
>> >    ...is_chassis_resident("cr-lr0-public-p2")), 
>> > action=(ct_lb_mark(backends=
>> > 10.0.0.3:80,10.0.0.4:80);)
>> >
>> > 5 - same as the step 5.1 (ct_lb_mark check only).
>> >
>> > 6 - VM 10.0.0.3 receives the new traffic and sends a reply (TCP SYN+ACK)
>> >
>> > 7 - lr0 receives the response packet (from VM 10.0.0.3) in the routing
>> > pipeline and route via one of the available paths (ECMP).
>> >   For example: lr0-public-p2 / ovn-gw-2 / TS2
>> >   So, we have a different outgoing path of the original one.
>> >
>> > 8 - ovn-gw-2 receives (SYN+ACK). We have no conntrack entries on ovn-gw-2
>> > to match and perform the LB SNAT action! The ct_lb_mark previously creates
>> > the conntrack entry on chassis ovn-gw-1 (when receives TCP SYN).
>> >
>> > with the stateless NAT config flag True:
>> >   The outgoing packet fail to match the lb conntrack but the packet is
>> > SNATed by the lr_out_snat rule with a priority lower then cl_lb_mark,
>> > executing after cl_lb_mark in the pipeline.
>> >
>> > 9 - The SYN+ACK packet crosses the TS2 and is delivered to VM 40.0.0.40 via
>> > lrp2 (SRC = 172.16.0.100:80)
>> >
>> >   lr_out_snat will match and SNATed:
>> >   match=(ip4 && ((ip4.src == 10.0.0.3 && tcp.src == 80) || (ip4.src ==
>> > 10.0.0.4 && tcp.src == 80)) && (inport == "lr0-public-p1" || outport ==
>> > "lr0-public-p1") && is_chassis_resident("cr-lr0-public-p1") && tcp),
>> > action=(ip4.src=172.16.0.100; tcp.src=80; next;)
>> >
>> > without the stateless NAT config flag:
>> >   The outgoing will be returned to the VM 40.0.0.4 with the LR SNAT for
>> > public-p2 port as ip4.src! So, basically this will break the TCP
>> > handshake!!!
>> >
>> > 9 - The SYN+ACK packet cross the TS2 and is delivered to the VM 40.0.0.40
>> > via lrp2 (SRC = 30.0.0.1:80)
>> >
>> > Moving forward on the happy path (stateless NAT is true):
>> >
>> > 10 - VM 40.0.0.40 sends an ACK to complete the TCP handshake.
>> >
>> > 11 - 2 - LR-EXT chooses one of the possible outgoing routes via ECMP
>> >   Let's assume that LR-EXT correctly execute the ecmp algorithm (per flow
>> > basis) and forwards to the same initial path because we're using the same
>> > flow (src/dst IPs and src/dts TCP ports still the same).
>> >   e.g. still using the lrp1 / chassis1 / TS1
>> >
>> > 12 - The lr0 receive the ingoing traffic through the port lr0-public-p1 /
>> > ovn-gw-1 / TS1
>> >
>> > With this patch we have 2 possible behaviours:
>> >
>> > 12.1 - Without Stateless NAT config flag
>> >
>> >   table=1 (lr_out_undnat      ), priority=120  , match=(ip4 && ((ip4.src ==
>> > 10.0.0.3 && tcp.src == 80) || (ip4.src == 10.0.0.4 && tcp.src == 80)) &&
>> > (inport == "lr0-public-p1" || outport ==
>> > "lr0-public-p1") && is_chassis_resident("cr-lr0-public-p1")),
>> > action=(ct_dnat_in_czone;)
>> >
>> >   table=8 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
>> > && ip4 && ip4.dst == 172.16.0.100 && tcp && tcp.dst == 9000 &&
>> > is_chassis_resident("cr-lr0-public-p1")), action=(ct_lb_mark(backends=
>> > 10.0.0.3:80,10.0.0.4:80);)
>> >
>> > openflow: br-int (ovn-gw-1)
>> >  cookie=0x9381c20e, duration=2361.675s, table=15, n_packets=2, n_bytes=148,
>> > idle_age=1564,
>> > priority=120,ct_state=+new-rel+trk,tcp,metadata=0x4,nw_dst=172.16.0.100,tp_dst=80
>> > actions=group:3
>> >
>> > ovs-appctl dpctl/dump-conntrack (ovn-gw-1)
>> > tcp,orig=(src=40.0.0.40,dst=172.16.0.100,sport=35274,dport=80),reply=(src=10.0.0.3,dst=40.0.0.40,sport=80,dport=35274),zone=8,mark=2,protoinfo=(state=TIME_WAIT)
>> >
>> > At this point, the ACK packet will be discarded because we didn't perform
>> > the SYN+ACK return on this chassis (ovn-gw -1). So, without the previously
>> > established conntrack to match the ACK packet we broke the TCP handshake.
>> >
>> > Remember the flow:
>> >  -> SYN: ovn-gw-1
>> >  <- SYN+ACK: ovn-gw-2
>> >  -> ACK: ovn-gw-1
>> >
>> > 12.2 - With stateless NAT config flag is True
>> >
>> >   table=1 (lr_out_undnat      ), priority=120  , match=(ip4 && ((ip4.src ==
>> > 10.0.0.3 && tcp.src == 80) || (ip4.src == 10.0.0.4 && tcp.src == 80)) &&
>> > (inport == "lr0-public-p1" || outport ==
>> > "lr0-public-p1") && is_chassis_resident("cr-lr0-public-p1")), 
>> > action=(next;)
>> >
>> >   table=8 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
>> > && ip4 && ip4.dst == 172.16.0.100 && tcp && tcp.dst == 9000 &&
>> > is_chassis_resident("cr-lr0-public-p1")),
>> > action=(ip4.dst=10.0.0.3;ip4.dst=10.0.0.4;ct_lb_mark(backends=10.0.0.3:80
>> > ,10.0.0.4:80);)
>> >
>> > openflow: br-int (ovn-gw-1)
>> >  cookie=0xab99f240, duration=1086.022s, table=15, n_packets=610,
>> > n_bytes=48979, idle_age=825,
>> > priority=120,ct_state=+new-rel+trk,tcp,metadata=0x2,nw_dst=172.16.0.100,tp_dst=80
>> > actions=mod_nw_dst:10.0.0.3,mod_nw_dst:10.0.0.4,group:1
>> >
>> > ovs-appctl dpctl/dump-conntrack (ovn-gw-1)
>> > tcp,orig=(src=40.0.0.40,dst=10.0.0.3,sport=54632,dport=80),reply=(src=10.0.0.3,dst=40.0.0.40,sport=80,dport=54632),zone=7,mark=2,protoinfo=(state=SYN_SENT)
>> >
>> > This is the reason to use stateless NAT rules. Unlike the case where we
>> > don't have the SYN_SENT, now the ACK packet will be accepted and forwarded
>> > after performing the mod_nw_dst action.
>> >
>> > How does this work? We already have the previous flow created by the first
>> > packet (SYN), so regardless of the IP.dst modified in the action, the
>> > packet will be forwarded to the same backend that corresponds to the
>> > conntrack match that is in SYN_SENT state. That's why I created the action
>> > with the two modifiers (ip4.dst + ct_lb_mark). Using only the ct_lb_mark
>> > action creates a strong dependency on the connection state match in the
>> > conntrack entry (which we don't have in these cases). However, only using
>> > ip4.dst doesn't create traffic balancing as it will always send to the same
>> > backend (last in the ip4.dst).
>> >
>> >
>> > I hope this has helped clarify the design decisions when creating/modifying
>> > flows (lr_in_dnat/lr_out_snat/lr_out_undnat).
>> >
>> >
>>
>> Thanks for the detailed explanation.  Its clear to me now.
>>
>> I'd suggest the following
>>
>> 1.  In lr_in_dnat stage,  choose either the first backend or the last
>> backend to modify the ip4.dst before ct_lb_mark.
>>      I've no strong preference.  Either would do.
>>
>> 2.  Please document or add comments in northd.c (and in ovn-nb.xml)so
>> that we don't lose the context later.
>>
>> 3.  Please run the ovn-fake-multinode tests in your github CI repo and
>> make sure it passes.  I think you can trigger the test in your repo
>>     in the Actions tab.
>>
>> 4.  In the setup you described above,   is it possible to add the
>> (below) route  in the vm (40.0.0.40) deployment so that 40.0.0.0.40
>> can
>>      talk directly to 10.0.0.3 and 10.0.0.4 instead of the LB VIP and
>> send the tcp traffic to 10.0.0.3 and see if it works ?
>>      If not, then please document the same  i.e  with the option
>> stateless_nat in the LB,  the backends cannot be directly
>> communicated.
>>
>>      logical router LR-EXT: route to 10.0.0.0/24 via ECMP (lrp1/chassis1
>>      and lrp2/chassis2)
>>
>
> I'll do your suggestions. Just a quick question: how can I 
> customize/parametrize the ovn-fake-multinode elements to run on CI? I mean to 
> enable 2 more ovn-chassis needed to run the related tests. I've enabled it 
> locally in the ./ovn_cluster.sh file but how do I change this for the 
> fake-multinode CI actions? Is it per job or is this global?

Its global.  Take a look here -
https://github.com/ovn-org/ovn/blob/main/.github/workflows/ovn-fake-multinode-tests.yml#L152
You can customize here to enbable 2 additional chassis.
>
> Regarding your suggestion 4, I imagine it makes more sense to 
> integrate/create a new test with the ovn-interconnect setup + LB + multiple 
> DGPs, which allows us to have multiple paths on routers on both sides 
> (addressing the case discussed above)

Sounds good to me.

Numan

>
> Thanks,
> Roberto
>
>>
>> Thanks
>> Numan
>>
>>
>> >
>> >
>> > >
>> > > Thanks
>> > > Numan
>> > >
>> > >
>> > > > >
>> > > > > Also I don't understand why this patch adds the logical flows in
>> > > > > "lr_out_snat" stage ?
>> > > > >
>> > > >
>> > > > The flow for lr_out_snat is necessary for the correct functioning of
>> > > > stateless NAT for the same reason explained previously. I mean, if the
>> > > > outgoing packet is redirected to a chassis that doesn't have an active
>> > > > conntrack entry, it will not be NATed by ct_lb action because it 
>> > > > doesn't
>> > > > refer to a valid flow (use case with ecmp).
>> > > >
>> > > > So it is necessary to create a stateless SNAT rule (similar to this 
>> > > > [3])
>> > > > with a lower priority than the other router pipeline entries, in this
>> > > case,
>> > > > if the packet is not SNATed by ct_lb (conntrack missed) it will be 
>> > > > SNATed
>> > > > by stateless NAT rule.
>> > > >
>> > > > [3]
>> > > >
>> > > https://github.com/ovn-org/ovn/blob/b93e9a5e6f3aa3cb3e2065bd8e0aa0b6fc1fd19a/northd/northd.c#L15884
>> > > >
>> > > >
>> > > >
>> > > > >
>> > > > > Using the system multinode test as an example,  the below fails
>> > > > > (which is a regression)
>> > > > >
>> > > > > ---
>> > > > > root@ovn-chassis-3 ~]# ip netns exec publicp1 nc -vz 10.0.0.3 80
>> > > > > ----
>> > > > >
>> > > > > In the above test,  publicp1 with IP 20.0.0.3 when it tries to 
>> > > > > connect
>> > > > > to one if the backends directly (without the LB VIP), it fails.
>> > > > > It fails because of the logical flows in "lr_out_snat".
>> > > > >
>> > > > >
>> > > > > Looks to me the solution proposed here is incomplete.
>> > > > >
>> > > > > Also please note that in our CI we run the multinode tests
>> > > > > periodically once a day using the v0.1 of the ovn-fake-multinode
>> > > > > and the tests you added will fail.  This needs to be fixed and until
>> > > > > we move to the latest version of ovn-fake-multinode.
>> > > > >
>> > > >
>> > > > I imagine that the test you are doing is using the same port as the LB
>> > > > backend (TCP 80 in this case). So, the stateless lr_out_snat flow will
>> > > > force the output to be SNATed because this port is in use by the 
>> > > > backend.
>> > > > Traffic to/from other ports will work without problems and will follow
>> > > the
>> > > > normal programmed flows (e.g. ICMP).
>> > > >
>> > > > This is necessary to ensure the egress traffic because the DGPs are
>> > > > distributed across multiple chassis. Also, this setup is being 
>> > > > validated
>> > > in
>> > > > the test ovn-fake-multinode testcase (ICMP from the backends chassis 
>> > > > use
>> > > > the router's default SNAT and not the LB's). I didn't understand the
>> > > > regression you mentioned because this was programmed to be stateless 
>> > > > and
>> > > > it's traffic that uses the same ports as the LB backend, could you
>> > > explain
>> > > > better?
>> > > >
>> > > > Thanks,
>> > > > Roberto
>> > > >
>> > > >
>> > > > > Thanks
>> > > > > Numan
>> > > > >
>> > > > >
>> > > > > > ---
>> > > > > >  northd/en-lr-stateful.c   |  12 -
>> > > > > >  northd/northd.c           | 116 ++++++--
>> > > > > >  ovn-nb.xml                |  10 +
>> > > > > >  tests/multinode-macros.at |  40 +++
>> > > > > >  tests/multinode.at        | 556
>> > > ++++++++++++++++++++++++++++++++++++++
>> > > > > >  tests/ovn-northd.at       | 320 ++++++++++++++++++++++
>> > > > > >  6 files changed, 1017 insertions(+), 37 deletions(-)
>> > > > > >
>> > > > > > diff --git a/northd/en-lr-stateful.c b/northd/en-lr-stateful.c
>> > > > > > index baf1bd2f8..f09691af6 100644
>> > > > > > --- a/northd/en-lr-stateful.c
>> > > > > > +++ b/northd/en-lr-stateful.c
>> > > > > > @@ -516,18 +516,6 @@ lr_stateful_record_create(struct
>> > > lr_stateful_table
>> > > > > *table,
>> > > > > >
>> > > > > >      table->array[od->index] = lr_stateful_rec;
>> > > > > >
>> > > > > > -    /* Load balancers are not supported (yet) if a logical router
>> > > has
>> > > > > multiple
>> > > > > > -     * distributed gateway port.  Log a warning. */
>> > > > > > -    if (lr_stateful_rec->has_lb_vip &&
>> > > lr_has_multiple_gw_ports(od)) {
>> > > > > > -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1,
>> > > 1);
>> > > > > > -        VLOG_WARN_RL(&rl, "Load-balancers are configured on 
>> > > > > > logical
>> > > "
>> > > > > > -                     "router %s, which has %"PRIuSIZE" 
>> > > > > > distributed "
>> > > > > > -                     "gateway ports. Load-balancer is not 
>> > > > > > supported
>> > > "
>> > > > > > -                     "yet when there is more than one distributed 
>> > > > > > "
>> > > > > > -                     "gateway port on the router.",
>> > > > > > -                     od->nbr->name, od->n_l3dgw_ports);
>> > > > > > -    }
>> > > > > > -
>> > > > > >      return lr_stateful_rec;
>> > > > > >  }
>> > > > > >
>> > > > > > diff --git a/northd/northd.c b/northd/northd.c
>> > > > > > index a267cd5f8..bbe97acf8 100644
>> > > > > > --- a/northd/northd.c
>> > > > > > +++ b/northd/northd.c
>> > > > > > @@ -11807,31 +11807,30 @@ static void
>> > > > > >  build_distr_lrouter_nat_flows_for_lb(struct 
>> > > > > > lrouter_nat_lb_flows_ctx
>> > > > > *ctx,
>> > > > > >                                       enum lrouter_nat_lb_flow_type
>> > > type,
>> > > > > >                                       struct ovn_datapath *od,
>> > > > > > -                                     struct lflow_ref *lflow_ref)
>> > > > > > +                                     struct lflow_ref *lflow_ref,
>> > > > > > +                                     struct ovn_port *dgp,
>> > > > > > +                                     bool stateless_nat)
>> > > > > >  {
>> > > > > > -    struct ovn_port *dgp = od->l3dgw_ports[0];
>> > > > > > -
>> > > > > > -    const char *undnat_action;
>> > > > > > -
>> > > > > > -    switch (type) {
>> > > > > > -    case LROUTER_NAT_LB_FLOW_FORCE_SNAT:
>> > > > > > -        undnat_action = "flags.force_snat_for_lb = 1; next;";
>> > > > > > -        break;
>> > > > > > -    case LROUTER_NAT_LB_FLOW_SKIP_SNAT:
>> > > > > > -        undnat_action = "flags.skip_snat_for_lb = 1; next;";
>> > > > > > -        break;
>> > > > > > -    case LROUTER_NAT_LB_FLOW_NORMAL:
>> > > > > > -    case LROUTER_NAT_LB_FLOW_MAX:
>> > > > > > -        undnat_action = lrouter_use_common_zone(od)
>> > > > > > -                        ? "ct_dnat_in_czone;"
>> > > > > > -                        : "ct_dnat;";
>> > > > > > -        break;
>> > > > > > -    }
>> > > > > > +    struct ds dnat_action = DS_EMPTY_INITIALIZER;
>> > > > > >
>> > > > > >      /* Store the match lengths, so we can reuse the ds buffer. */
>> > > > > >      size_t new_match_len = ctx->new_match->length;
>> > > > > >      size_t undnat_match_len = ctx->undnat_match->length;
>> > > > > >
>> > > > > > +    /* dnat_action: Add the LB backend IPs as a destination action
>> > > of
>> > > > > the
>> > > > > > +     *              lr_in_dnat NAT rule with cumulative effect
>> > > because
>> > > > > any
>> > > > > > +     *              backend dst IP used in the action list will
>> > > > > redirect the
>> > > > > > +     *              packet to the ct_lb pipeline.
>> > > > > > +     */
>> > > > > > +    if (stateless_nat) {
>> > > > > > +        for (size_t i = 0; i < ctx->lb_vip->n_backends; i++) {
>> > > > > > +            struct ovn_lb_backend *backend =
>> > > &ctx->lb_vip->backends[i];
>> > > > > > +            bool ipv6 = !IN6_IS_ADDR_V4MAPPED(&backend->ip);
>> > > > > > +            ds_put_format(&dnat_action, "%s.dst=%s;", ipv6 ? 
>> > > > > > "ip6" :
>> > > > > "ip4",
>> > > > > > +                          backend->ip_str);
>> > > > > > +        }
>> > > > > > +    }
>> > > > > > +    ds_put_format(&dnat_action, "%s", ctx->new_action[type]);
>> > > > > >
>> > > > > >      const char *meter = NULL;
>> > > > > >
>> > > > > > @@ -11841,20 +11840,46 @@ 
>> > > > > > build_distr_lrouter_nat_flows_for_lb(struct
>> > > > > lrouter_nat_lb_flows_ctx *ctx,
>> > > > > >
>> > > > > >      if (ctx->lb_vip->n_backends || 
>> > > > > > !ctx->lb_vip->empty_backend_rej)
>> > > {
>> > > > > >          ds_put_format(ctx->new_match, " && 
>> > > > > > is_chassis_resident(%s)",
>> > > > > > -                      od->l3dgw_ports[0]->cr_port->json_key);
>> > > > > > +                      dgp->cr_port->json_key);
>> > > > > >      }
>> > > > > >
>> > > > > >      ovn_lflow_add_with_hint__(ctx->lflows, od, S_ROUTER_IN_DNAT,
>> > > > > ctx->prio,
>> > > > > > -                              ds_cstr(ctx->new_match),
>> > > > > ctx->new_action[type],
>> > > > > > +                              ds_cstr(ctx->new_match),
>> > > > > ds_cstr(&dnat_action),
>> > > > > >                                NULL, meter, &ctx->lb->nlb->header_,
>> > > > > >                                lflow_ref);
>> > > > > >
>> > > > > >      ds_truncate(ctx->new_match, new_match_len);
>> > > > > >
>> > > > > > +    ds_destroy(&dnat_action);
>> > > > > >      if (!ctx->lb_vip->n_backends) {
>> > > > > >          return;
>> > > > > >      }
>> > > > > >
>> > > > > > +    struct ds undnat_action = DS_EMPTY_INITIALIZER;
>> > > > > > +    struct ds snat_action = DS_EMPTY_INITIALIZER;
>> > > > > > +
>> > > > > > +    switch (type) {
>> > > > > > +    case LROUTER_NAT_LB_FLOW_FORCE_SNAT:
>> > > > > > +        ds_put_format(&undnat_action, "flags.force_snat_for_lb = 
>> > > > > > 1;
>> > > > > next;");
>> > > > > > +        break;
>> > > > > > +    case LROUTER_NAT_LB_FLOW_SKIP_SNAT:
>> > > > > > +        ds_put_format(&undnat_action, "flags.skip_snat_for_lb = 1;
>> > > > > next;");
>> > > > > > +        break;
>> > > > > > +    case LROUTER_NAT_LB_FLOW_NORMAL:
>> > > > > > +    case LROUTER_NAT_LB_FLOW_MAX:
>> > > > > > +        ds_put_format(&undnat_action, "%s",
>> > > > > > +                      lrouter_use_common_zone(od) ?
>> > > "ct_dnat_in_czone;"
>> > > > > > +                      : "ct_dnat;");
>> > > > > > +        break;
>> > > > > > +    }
>> > > > > > +
>> > > > > > +    /* undnat_action: Remove the ct action from the lr_out_undenat
>> > > NAT
>> > > > > rule.
>> > > > > > +     */
>> > > > > > +    if (stateless_nat) {
>> > > > > > +        ds_clear(&undnat_action);
>> > > > > > +        ds_put_format(&undnat_action, "next;");
>> > > > > > +    }
>> > > > > > +
>> > > > > >      /* We need to centralize the LB traffic to properly perform
>> > > > > >       * the undnat stage.
>> > > > > >       */
>> > > > > > @@ -11873,11 +11898,41 @@ 
>> > > > > > build_distr_lrouter_nat_flows_for_lb(struct
>> > > > > lrouter_nat_lb_flows_ctx *ctx,
>> > > > > >      ds_put_format(ctx->undnat_match, ") && (inport == %s || 
>> > > > > > outport
>> > > ==
>> > > > > %s)"
>> > > > > >                    " && is_chassis_resident(%s)", dgp->json_key,
>> > > > > dgp->json_key,
>> > > > > >                    dgp->cr_port->json_key);
>> > > > > > +    /* Use the LB protocol as matching criteria for out undnat and
>> > > snat
>> > > > > when
>> > > > > > +     * creating LBs with stateless NAT. */
>> > > > > > +    if (stateless_nat) {
>> > > > > > +        ds_put_format(ctx->undnat_match, " && %s", 
>> > > > > > ctx->lb->proto);
>> > > > > > +    }
>> > > > > >      ovn_lflow_add_with_hint(ctx->lflows, od, S_ROUTER_OUT_UNDNAT,
>> > > 120,
>> > > > > > -                            ds_cstr(ctx->undnat_match),
>> > > undnat_action,
>> > > > > > -                            &ctx->lb->nlb->header_,
>> > > > > > +                            ds_cstr(ctx->undnat_match),
>> > > > > > +                            ds_cstr(&undnat_action),
>> > > > > &ctx->lb->nlb->header_,
>> > > > > >                              lflow_ref);
>> > > > > > +
>> > > > > > +    /* snat_action: Add a new lr_out_snat rule with the LB VIP as
>> > > > > source IP
>> > > > > > +     *              action to perform the NAT stateless pipeline
>> > > > > completely.
>> > > > > > +     */
>> > > > > > +    if (stateless_nat) {
>> > > > > > +        if (ctx->lb_vip->port_str) {
>> > > > > > +            ds_put_format(&snat_action, "%s.src=%s; %s.src=%s;
>> > > next;",
>> > > > > > +                          ctx->lb_vip->address_family == AF_INET6 
>> > > > > > ?
>> > > > > > +                          "ip6" : "ip4",
>> > > > > > +                          ctx->lb_vip->vip_str, ctx->lb->proto,
>> > > > > > +                          ctx->lb_vip->port_str);
>> > > > > > +        } else {
>> > > > > > +            ds_put_format(&snat_action, "%s.src=%s; next;",
>> > > > > > +                          ctx->lb_vip->address_family == AF_INET6 
>> > > > > > ?
>> > > > > > +                          "ip6" : "ip4",
>> > > > > > +                          ctx->lb_vip->vip_str);
>> > > > > > +        }
>> > > > > > +        ovn_lflow_add_with_hint(ctx->lflows, od, 
>> > > > > > S_ROUTER_OUT_SNAT,
>> > > 160,
>> > > > > > +                                ds_cstr(ctx->undnat_match),
>> > > > > > +                                ds_cstr(&snat_action),
>> > > > > &ctx->lb->nlb->header_,
>> > > > > > +                                lflow_ref);
>> > > > > > +    }
>> > > > > > +
>> > > > > >      ds_truncate(ctx->undnat_match, undnat_match_len);
>> > > > > > +    ds_destroy(&undnat_action);
>> > > > > > +    ds_destroy(&snat_action);
>> > > > > >  }
>> > > > > >
>> > > > > >  static void
>> > > > > > @@ -12022,6 +12077,8 @@ build_lrouter_nat_flows_for_lb(
>> > > > > >       * lflow generation for them.
>> > > > > >       */
>> > > > > >      size_t index;
>> > > > > > +    bool use_stateless_nat = smap_get_bool(&lb->nlb->options,
>> > > > > > +                                           "use_stateless_nat",
>> > > false);
>> > > > > >      BITMAP_FOR_EACH_1 (index, bitmap_len, lb_dps->nb_lr_map) {
>> > > > > >          struct ovn_datapath *od = lr_datapaths->array[index];
>> > > > > >          enum lrouter_nat_lb_flow_type type;
>> > > > > > @@ -12043,8 +12100,17 @@ build_lrouter_nat_flows_for_lb(
>> > > > > >          if (!od->n_l3dgw_ports) {
>> > > > > >              bitmap_set1(gw_dp_bitmap[type], index);
>> > > > > >          } else {
>> > > > > > -            build_distr_lrouter_nat_flows_for_lb(&ctx, type, od,
>> > > > > > -                                                 
>> > > > > > lb_dps->lflow_ref);
>> > > > > > +            /* Create stateless LB NAT rules when using multiple
>> > > DGPs
>> > > > > and
>> > > > > > +             * use_stateless_nat is true.
>> > > > > > +             */
>> > > > > > +            bool stateless_nat = (od->n_l3dgw_ports > 1)
>> > > > > > +                ? use_stateless_nat : false;
>> > > > > > +            for (size_t i = 0; i < od->n_l3dgw_ports; i++) {
>> > > > > > +                struct ovn_port *dgp = od->l3dgw_ports[i];
>> > > > > > +                build_distr_lrouter_nat_flows_for_lb(&ctx, type, 
>> > > > > > od,
>> > > > > > +
>> > >  lb_dps->lflow_ref,
>> > > > > dgp,
>> > > > > > +                                                     
>> > > > > > stateless_nat);
>> > > > > > +            }
>> > > > > >          }
>> > > > > >
>> > > > > >          if (lb->affinity_timeout) {
>> > > > > > diff --git a/ovn-nb.xml b/ovn-nb.xml
>> > > > > > index 2836f58f5..ad03c6214 100644
>> > > > > > --- a/ovn-nb.xml
>> > > > > > +++ b/ovn-nb.xml
>> > > > > > @@ -2302,6 +2302,16 @@ or
>> > > > > >          local anymore by the ovn-controller. This option is set to
>> > > > > >          <code>false</code> by default.
>> > > > > >        </column>
>> > > > > > +
>> > > > > > +      <column name="options" key="use_stateless_nat"
>> > > > > > +              type='{"type": "boolean"}'>
>> > > > > > +        If the load balancer is configured with
>> > > > > <code>use_stateless_nat</code>
>> > > > > > +        option to <code>true</code>, the logical router that
>> > > references
>> > > > > this
>> > > > > > +        load balancer will use Stateless NAT rules when the 
>> > > > > > logical
>> > > > > router
>> > > > > > +        has multiple distributed gateway ports(DGP). Otherwise, 
>> > > > > > the
>> > > > > outbound
>> > > > > > +        traffic may be dropped in scenarios where we have 
>> > > > > > different
>> > > > > chassis
>> > > > > > +        for each DGP. This option is set to <code>false</code> by
>> > > > > default.
>> > > > > > +      </column>
>> > > > > >      </group>
>> > > > > >    </table>
>> > > > > >
>> > > > > > diff --git a/tests/multinode-macros.at b/tests/multinode-macros.at
>> > > > > > index 757917626..2f69433fc 100644
>> > > > > > --- a/tests/multinode-macros.at
>> > > > > > +++ b/tests/multinode-macros.at
>> > > > > > @@ -40,6 +40,27 @@ m4_define([M_START_TCPDUMP],
>> > > > > >      ]
>> > > > > >  )
>> > > > > >
>> > > > > > +# M_EXEC([fake_node], [command])
>> > > > > > +#
>> > > > > > +# Execute 'command' in 'fakenode'
>> > > > > > +m4_define([M_EXEC],
>> > > > > > +    [podman exec $1 $2])
>> > > > > > +
>> > > > > > +# M_CHECK_EXEC([fake_node], [command], other_params...)
>> > > > > > +#
>> > > > > > +# Wrapper for AT_CHECK that executes 'command' inside
>> > > 'fake_node''s'.
>> > > > > > +# 'other_params' as passed as they are to AT_CHECK.
>> > > > > > +m4_define([M_CHECK_EXEC],
>> > > > > > +    [ AT_CHECK([M_EXEC([$1], [$2])],
>> > > m4_shift(m4_shift(m4_shift($@)))) ]
>> > > > > > +)
>> > > > > > +
>> > > > > > +# M_FORMAT_CT([ip-addr])
>> > > > > > +#
>> > > > > > +# Strip content from the piped input which would differ from test 
>> > > > > > to
>> > > > > test
>> > > > > > +# and limit the output to the rows containing 'ip-addr'.
>> > > > > > +#
>> > > > > > +m4_define([M_FORMAT_CT],
>> > > > > > +    [[grep -F "dst=$1," | sed -e 's/id=[0-9]*/id=<cleared>/g' -e
>> > > > > 's/state=[0-9_A-Z]*/state=<cleared>/g' | sort | uniq | sed -e
>> > > > > 's/zone=[[0-9]]*/zone=<cleared>/' -e 
>> > > > > 's/mark=[[0-9]]*/mark=<cleared>/'
>> > > ]])
>> > > > > >
>> > > > > >  OVS_START_SHELL_HELPERS
>> > > > > >
>> > > > > > @@ -76,6 +97,25 @@ multinode_nbctl () {
>> > > > > >      m_as ovn-central ovn-nbctl "$@"
>> > > > > >  }
>> > > > > >
>> > > > > > +check_fake_multinode_setup_by_nodes() {
>> > > > > > +    check m_as ovn-central ovn-nbctl --wait=sb sync
>> > > > > > +    for c in $1
>> > > > > > +    do
>> > > > > > +        AT_CHECK([m_as $c ovn-appctl -t ovn-controller version],
>> > > [0],
>> > > > > [ignore])
>> > > > > > +    done
>> > > > > > +}
>> > > > > > +
>> > > > > > +cleanup_multinode_resources_by_nodes() {
>> > > > > > +    m_as ovn-central rm -f /etc/ovn/ovnnb_db.db
>> > > > > > +    m_as ovn-central /usr/share/ovn/scripts/ovn-ctl restart_northd
>> > > > > > +    check m_as ovn-central ovn-nbctl --wait=sb sync
>> > > > > > +    for c in $1
>> > > > > > +    do
>> > > > > > +        m_as $c ovs-vsctl del-br br-int
>> > > > > > +        m_as $c ip --all netns delete
>> > > > > > +    done
>> > > > > > +}
>> > > > > > +
>> > > > > >  # m_count_rows TABLE [CONDITION...]
>> > > > > >  #
>> > > > > >  # Prints the number of rows in TABLE (that satisfy CONDITION).
>> > > > > > diff --git a/tests/multinode.at b/tests/multinode.at
>> > > > > > index a0eb8fc67..b1beb4d97 100644
>> > > > > > --- a/tests/multinode.at
>> > > > > > +++ b/tests/multinode.at
>> > > > > > @@ -1591,3 +1591,559 @@ AT_CHECK([cat ch1_eth2.tcpdump], [0], [dnl
>> > > > > >  ])
>> > > > > >
>> > > > > >  AT_CLEANUP
>> > > > > > +
>> > > > > > +AT_SETUP([ovn multinode load-balancer with multiple DGPs and
>> > > multiple
>> > > > > chassis])
>> > > > > > +
>> > > > > > +# Check that ovn-fake-multinode setup is up and running - requires
>> > > > > additional nodes
>> > > > > > +check_fake_multinode_setup_by_nodes 'ovn-chassis-1 ovn-chassis-2
>> > > > > ovn-chassis-3 ovn-chassis-4 ovn-gw-1 ovn-gw-2'
>> > > > > > +
>> > > > > > +# Delete the multinode NB and OVS resources before starting the
>> > > test.
>> > > > > > +cleanup_multinode_resources_by_nodes 'ovn-chassis-1 ovn-chassis-2
>> > > > > ovn-chassis-3 ovn-chassis-4 ovn-gw-1 ovn-gw-2'
>> > > > > > +
>> > > > > > +# Network topology
>> > > > > > +#
>> > > > > > +#             publicp1 (ovn-chassis-3) (20.0.0.3/24)
>> > > > > > +#                |
>> > > > > > +#              overlay
>> > > > > > +#                |
>> > > > > > +#      DGP public1 (ovn-gw-1) (20.0.0.1/24)
>> > > > > > +#                |
>> > > > > > +#                |
>> > > > > > +#                |
>> > > > > > +#               lr0 ------- sw0 --- sw0p1 (ovn-chassis-1)
>> > > 10.0.0.3/24
>> > > > > > +#                |           |
>> > > > > > +#                |           + ---  sw0p2 (ovn-chassis-2)
>> > > 10.0.0.4/24
>> > > > > > +#                |
>> > > > > > +#      DGP public2 (ovn-gw-2) (30.0.0.1/24)
>> > > > > > +#                |
>> > > > > > +#              overlay
>> > > > > > +#                |
>> > > > > > +#             publicp2 (ovn-chassis-4) (30.0.0.3/24)
>> > > > > > +
>> > > > > > +# Delete already used ovs-ports
>> > > > > > +m_as ovn-chassis-1 ovs-vsctl del-port br-int sw0p1-p
>> > > > > > +m_as ovn-chassis-2 ovs-vsctl del-port br-int sw0p2-p
>> > > > > > +m_as ovn-chassis-1 ip link del sw0p1-p
>> > > > > > +m_as ovn-chassis-2 ip link del sw0p2-p
>> > > > > > +m_as ovn-chassis-3 ovs-vsctl del-port br-int publicp1-p
>> > > > > > +m_as ovn-chassis-4 ovs-vsctl del-port br-int publicp2-p
>> > > > > > +m_as ovn-chassis-3 ip link del publicp1-p
>> > > > > > +m_as ovn-chassis-4 ip link del publicp2-p
>> > > > > > +
>> > > > > > +# Create East-West switch for LB backends
>> > > > > > +check multinode_nbctl ls-add sw0
>> > > > > > +check multinode_nbctl lsp-add sw0 sw0-port1
>> > > > > > +check multinode_nbctl lsp-set-addresses sw0-port1 
>> > > > > > "50:54:00:00:00:03
>> > > > > 10.0.0.3 1000::3"
>> > > > > > +check multinode_nbctl lsp-add sw0 sw0-port2
>> > > > > > +check multinode_nbctl lsp-set-addresses sw0-port2 
>> > > > > > "50:54:00:00:00:04
>> > > > > 10.0.0.4 1000::4"
>> > > > > > +
>> > > > > > +m_as ovn-chassis-1 /data/create_fake_vm.sh sw0-port1 sw0p1
>> > > > > 50:54:00:00:00:03 1400 10.0.0.3 24 10.0.0.1 1000::3/64 1000::a
>> > > > > > +m_as ovn-chassis-2 /data/create_fake_vm.sh sw0-port2 sw0p2
>> > > > > 50:54:00:00:00:04 1400 10.0.0.4 24 10.0.0.1 1000::4/64 1000::a
>> > > > > > +
>> > > > > > +m_wait_for_ports_up
>> > > > > > +
>> > > > > > +M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 
>> > > > > > 2
>> > > > > 10.0.0.4 | FORMAT_PING], \
>> > > > > > +[0], [dnl
>> > > > > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
>> > > > > > +])
>> > > > > > +
>> > > > > > +M_NS_CHECK_EXEC([ovn-chassis-2], [sw0p2], [ping -q -c 3 -i 0.3 -w 
>> > > > > > 2
>> > > > > 10.0.0.3 | FORMAT_PING], \
>> > > > > > +[0], [dnl
>> > > > > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Create a logical router and attach to sw0
>> > > > > > +check multinode_nbctl lr-add lr0
>> > > > > > +check multinode_nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01
>> > > 10.0.0.1/24
>> > > > > 1000::a/64
>> > > > > > +check multinode_nbctl lsp-add sw0 sw0-lr0
>> > > > > > +check multinode_nbctl lsp-set-type sw0-lr0 router
>> > > > > > +check multinode_nbctl lsp-set-addresses sw0-lr0 router
>> > > > > > +check multinode_nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
>> > > > > > +
>> > > > > > +# create external connection for N/S traffic using multiple DGPs
>> > > > > > +check multinode_nbctl ls-add public
>> > > > > > +
>> > > > > > +# DGP public1
>> > > > > > +check multinode_nbctl lsp-add public ln-public-1
>> > > > > > +check multinode_nbctl lsp-set-type ln-public-1 localnet
>> > > > > > +check multinode_nbctl lsp-set-addresses ln-public-1 unknown
>> > > > > > +check multinode_nbctl lsp-set-options ln-public-1
>> > > network_name=public1
>> > > > > > +
>> > > > > > +# DGP public2
>> > > > > > +# create exteranl connection for N/S traffic
>> > > > > > +check multinode_nbctl lsp-add public ln-public-2
>> > > > > > +check multinode_nbctl lsp-set-type ln-public-2 localnet
>> > > > > > +check multinode_nbctl lsp-set-addresses ln-public-2 unknown
>> > > > > > +check multinode_nbctl lsp-set-options ln-public-2
>> > > network_name=public2
>> > > > > > +
>> > > > > > +# Attach DGP public1 to GW-1 and chassis-3 (overlay connectivity)
>> > > > > > +m_as ovn-gw-1 ovs-vsctl set open .
>> > > > > external-ids:ovn-bridge-mappings=public1:br-ex
>> > > > > > +m_as ovn-chassis-3 ovs-vsctl set open .
>> > > > > external-ids:ovn-bridge-mappings=public1:br-ex
>> > > > > > +
>> > > > > > +# Attach DGP public2 to GW-2 and chassis-4 (overlay connectivity)
>> > > > > > +m_as ovn-gw-2 ovs-vsctl set open .
>> > > > > external-ids:ovn-bridge-mappings=public2:br-ex
>> > > > > > +m_as ovn-chassis-4 ovs-vsctl set open .
>> > > > > external-ids:ovn-bridge-mappings=public2:br-ex
>> > > > > > +
>> > > > > > +# Create the external LR0 port to the DGP public1
>> > > > > > +check multinode_nbctl lsp-add public public-port1
>> > > > > > +check multinode_nbctl lsp-set-addresses public-port1
>> > > "40:54:00:00:00:03
>> > > > > 20.0.0.3 2000::3"
>> > > > > > +
>> > > > > > +check multinode_nbctl lrp-add lr0 lr0-public-p1 00:00:00:00:ff:02
>> > > > > 20.0.0.1/24 2000::a/64
>> > > > > > +check multinode_nbctl lsp-add public public-lr0-p1
>> > > > > > +check multinode_nbctl lsp-set-type public-lr0-p1 router
>> > > > > > +check multinode_nbctl lsp-set-addresses public-lr0-p1 router
>> > > > > > +check multinode_nbctl lsp-set-options public-lr0-p1
>> > > > > router-port=lr0-public-p1
>> > > > > > +check multinode_nbctl lrp-set-gateway-chassis lr0-public-p1
>> > > ovn-gw-1 10
>> > > > > > +
>> > > > > > +# Create a VM on ovn-chassis-3 in the same public1 overlay
>> > > > > > +m_as ovn-chassis-3 /data/create_fake_vm.sh public-port1 publicp1
>> > > > > 40:54:00:00:00:03 1400 20.0.0.3 24 20.0.0.1 2000::4/64 2000::a
>> > > > > > +
>> > > > > > +m_wait_for_ports_up public-port1
>> > > > > > +
>> > > > > > +M_NS_CHECK_EXEC([ovn-chassis-3], [publicp1], [ping -q -c 3 -i 0.3
>> > > -w 2
>> > > > > 20.0.0.1 | FORMAT_PING], \
>> > > > > > +[0], [dnl
>> > > > > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Create the external LR0 port to the DGP public2
>> > > > > > +check multinode_nbctl lsp-add public public-port2
>> > > > > > +check multinode_nbctl lsp-set-addresses public-port2
>> > > "60:54:00:00:00:03
>> > > > > 30.0.0.3 3000::3"
>> > > > > > +
>> > > > > > +check multinode_nbctl lrp-add lr0 lr0-public-p2 00:00:00:00:ff:03
>> > > > > 30.0.0.1/24 3000::a/64
>> > > > > > +check multinode_nbctl lsp-add public public-lr0-p2
>> > > > > > +check multinode_nbctl lsp-set-type public-lr0-p2 router
>> > > > > > +check multinode_nbctl lsp-set-addresses public-lr0-p2 router
>> > > > > > +check multinode_nbctl lsp-set-options public-lr0-p2
>> > > > > router-port=lr0-public-p2
>> > > > > > +check multinode_nbctl lrp-set-gateway-chassis lr0-public-p2
>> > > ovn-gw-2 10
>> > > > > > +
>> > > > > > +# Create a VM on ovn-chassis-4 in the same public2 overlay
>> > > > > > +m_as ovn-chassis-4 /data/create_fake_vm.sh public-port2 publicp2
>> > > > > 60:54:00:00:00:03 1400 30.0.0.3 24 30.0.0.1 3000::4/64 3000::a
>> > > > > > +
>> > > > > > +m_wait_for_ports_up public-port2
>> > > > > > +
>> > > > > > +M_NS_CHECK_EXEC([ovn-chassis-4], [publicp2], [ping -q -c 3 -i 0.3
>> > > -w 2
>> > > > > 30.0.0.1 | FORMAT_PING], \
>> > > > > > +[0], [dnl
>> > > > > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Add a default route for multiple DGPs - using ECMP
>> > > > > > +####check multinode_nbctl --ecmp lr-route-add lr0 0.0.0.0/0
>> > > 20.0.0.3
>> > > > > > +####check multinode_nbctl --ecmp lr-route-add lr0 0.0.0.0/0
>> > > 30.0.0.3
>> > > > > > +
>> > > > > > +# Add SNAT rules using gateway-port
>> > > > > > +check multinode_nbctl --gateway-port lr0-public-p1 lr-nat-add lr0
>> > > snat
>> > > > > 20.0.0.1 10.0.0.0/24
>> > > > > > +check multinode_nbctl --gateway-port lr0-public-p2 lr-nat-add lr0
>> > > snat
>> > > > > 30.0.0.1 10.0.0.0/24
>> > > > > > +
>> > > > > > +M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 
>> > > > > > 2
>> > > > > 20.0.0.3 | FORMAT_PING], \
>> > > > > > +[0], [dnl
>> > > > > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
>> > > > > > +])
>> > > > > > +
>> > > > > > +M_NS_CHECK_EXEC([ovn-chassis-2], [sw0p2], [ping -q -c 3 -i 0.3 -w 
>> > > > > > 2
>> > > > > 30.0.0.3 | FORMAT_PING], \
>> > > > > > +[0], [dnl
>> > > > > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
>> > > > > > +])
>> > > > > > +
>> > > > > > +# create LB
>> > > > > > +check multinode_nbctl lb-add lb0 "172.16.0.100:80" "10.0.0.3:80,
>> > > > > 10.0.0.4:80"
>> > > > > > +check multinode_nbctl lr-lb-add lr0 lb0
>> > > > > > +check multinode_nbctl ls-lb-add sw0 lb0
>> > > > > > +
>> > > > > > +# Set use_stateless_nat to true
>> > > > > > +check multinode_nbctl set load_balancer lb0
>> > > > > options:use_stateless_nat=true
>> > > > > > +
>> > > > > > +# Start backend http services
>> > > > > > +M_NS_DAEMONIZE([ovn-chassis-1], [sw0p1], [$PYTHON -m http.server
>> > > --bind
>> > > > > 10.0.0.3 80 >/dev/null 2>&1], [http1.pid])
>> > > > > > +M_NS_DAEMONIZE([ovn-chassis-2], [sw0p2], [$PYTHON -m http.server
>> > > --bind
>> > > > > 10.0.0.4 80 >/dev/null 2>&1], [http2.pid])
>> > > > > > +
>> > > > > > +# wait for http server be ready
>> > > > > > +sleep 2
>> > > > > > +
>> > > > > > +# Flush conntrack entries for easier output parsing of next test.
>> > > > > > +m_as ovn-gw-1 ovs-appctl dpctl/flush-conntrack
>> > > > > > +m_as ovn-gw-2 ovs-appctl dpctl/flush-conntrack
>> > > > > > +
>> > > > > > +M_NS_EXEC([ovn-chassis-3], [publicp1], [sh -c 'curl -v
>> > > 172.16.0.100:80
>> > > > > --retry 3 --max-time 1 --local-port 59002 2> curl.out'])
>> > > > > > +M_NS_CHECK_EXEC([ovn-chassis-3], [publicp1], [sh -c 'cat curl.out 
>> > > > > > |
>> > > > > grep -i -e connect | grep -v 'Server:''], \
>> > > > > > +[0], [dnl
>> > > > > > +* Connected to 172.16.0.100 (172.16.0.100) port 80
>> > > > > > +* Closing connection
>> > > > > > +])
>> > > > > > +
>> > > > > > +M_NS_EXEC([ovn-chassis-4], [publicp2], [sh -c 'curl -v
>> > > 172.16.0.100:80
>> > > > > --retry 3 --max-time 1 --local-port 59003 2> curl.out'])
>> > > > > > +M_NS_CHECK_EXEC([ovn-chassis-4], [publicp2], [sh -c 'cat curl.out 
>> > > > > > |
>> > > > > grep -i -e connect | grep -v 'Server:''], \
>> > > > > > +[0], [dnl
>> > > > > > +* Connected to 172.16.0.100 (172.16.0.100) port 80
>> > > > > > +* Closing connection
>> > > > > > +])
>> > > > > > +
>> > > > > > +m_as ovn-gw-1 ovs-appctl dpctl/flush-conntrack
>> > > > > > +m_as ovn-gw-2 ovs-appctl dpctl/flush-conntrack
>> > > > > > +
>> > > > > > +M_NS_EXEC([ovn-chassis-3], [publicp1], [sh -c 'curl -v
>> > > 172.16.0.100:80
>> > > > > --retry 3 --max-time 1 --local-port 59001'])
>> > > > > > +OVS_WAIT_FOR_OUTPUT([m_as ovn-gw-1 ovs-appctl 
>> > > > > > dpctl/dump-conntrack |
>> > > > > M_FORMAT_CT(20.0.0.3) | \
>> > > > > > +grep tcp | sed -E -e 's/10.0.0.3|10.0.0.4/<cleared>/g' | sort],
>> > > [0],
>> > > > > [dnl
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=20.0.0.3,dst=<cleared>,sport=59001,dport=80),reply=(src=<cleared>,dst=20.0.0.3,sport=80,dport=59001),zone=<cleared>,mark=<cleared>,protoinfo=(state=<cleared>)
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=20.0.0.3,dst=<cleared>,sport=59001,dport=80),reply=(src=<cleared>,dst=20.0.0.3,sport=80,dport=59001),zone=<cleared>,protoinfo=(state=<cleared>)
>> > > > > > +])
>> > > > > > +
>> > > > > > +M_NS_EXEC([ovn-chassis-4], [publicp2], [sh -c 'curl -v
>> > > 172.16.0.100:80
>> > > > > --retry 3 --max-time 1 --local-port 59000'])
>> > > > > > +OVS_WAIT_FOR_OUTPUT([m_as ovn-gw-2 ovs-appctl 
>> > > > > > dpctl/dump-conntrack |
>> > > > > M_FORMAT_CT(30.0.0.3) | \
>> > > > > > +grep tcp | sed -E -e 's/10.0.0.3|10.0.0.4/<cleared>/g' | sort],
>> > > [0],
>> > > > > [dnl
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=30.0.0.3,dst=<cleared>,sport=59000,dport=80),reply=(src=<cleared>,dst=30.0.0.3,sport=80,dport=59000),zone=<cleared>,mark=<cleared>,protoinfo=(state=<cleared>)
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=30.0.0.3,dst=<cleared>,sport=59000,dport=80),reply=(src=<cleared>,dst=30.0.0.3,sport=80,dport=59000),zone=<cleared>,protoinfo=(state=<cleared>)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# create a big file on web servers for download
>> > > > > > +M_NS_EXEC([ovn-chassis-1], [sw0p1], [dd bs=512 count=200000
>> > > > > if=/dev/urandom of=download_file])
>> > > > > > +M_NS_EXEC([ovn-chassis-2], [sw0p2], [dd bs=512 count=200000
>> > > > > if=/dev/urandom of=download_file])
>> > > > > > +
>> > > > > > +# Flush conntrack entries for easier output parsing of next test.
>> > > > > > +m_as ovn-chassis-1 ovs-appctl dpctl/flush-conntrack
>> > > > > > +m_as ovn-chassis-2 ovs-appctl dpctl/flush-conntrack
>> > > > > > +m_as ovn-gw-1 ovs-appctl dpctl/flush-conntrack
>> > > > > > +m_as ovn-gw-2 ovs-appctl dpctl/flush-conntrack
>> > > > > > +
>> > > > > > +M_NS_EXEC([ovn-chassis-3], [publicp1], [sh -c 'curl -v -O
>> > > > > 172.16.0.100:80/download_file --retry 3 --max-time 1 --local-port
>> > > 59004
>> > > > > 2>curl.out'])
>> > > > > > +
>> > > > > > +gw1_ct=$(m_as ovn-gw-1 ovs-appctl dpctl/dump-conntrack | sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +gw2_ct=$(m_as ovn-gw-2 ovs-appctl dpctl/dump-conntrack | sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +chassis1_ct=$(m_as ovn-chassis-1 ovs-appctl dpctl/dump-conntrack |
>> > > sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +chassis2_ct=$(m_as ovn-chassis-2 ovs-appctl dpctl/dump-conntrack |
>> > > sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +chassis1_flow=$(m_as ovn-chassis-1 ovs-dpctl dump-flows | sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +chassis2_flow=$(m_as ovn-chassis-2 ovs-dpctl dump-flows | sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +
>> > > > > > +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-3 ip netns exec publicp1 cat
>> > > > > curl.out | \
>> > > > > > +grep -i -e connect | grep -v 'Server:'], [0], [dnl
>> > > > > > +* Connected to 172.16.0.100 (172.16.0.100) port 80
>> > > > > > +* Closing connection
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Check if we have only one backend for the same connection - 
>> > > > > > orig +
>> > > > > dest ports
>> > > > > > +OVS_WAIT_FOR_OUTPUT([echo -e $gw1_ct | M_FORMAT_CT(20.0.0.3) | \
>> > > > > > +grep tcp | sed -E -e 's/10.0.0.3|10.0.0.4/<cleared>/g' | sort],
>> > > [0],
>> > > > > [dnl
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=20.0.0.3,dst=<cleared>,sport=59004,dport=80),reply=(src=<cleared>,dst=20.0.0.3,sport=80,dport=59004),zone=<cleared>,mark=<cleared>,protoinfo=(state=<cleared>)
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=20.0.0.3,dst=<cleared>,sport=59004,dport=80),reply=(src=<cleared>,dst=20.0.0.3,sport=80,dport=59004),zone=<cleared>,protoinfo=(state=<cleared>)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Check if gw-2 is empty to ensure that the traffic only come
>> > > from/to
>> > > > > the originator chassis via DGP public1
>> > > > > > +AT_CHECK([echo -e $gw2_ct | grep "20.0.0.3" -c], [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Check the backend IP from ct entries on gw-1 (DGP public1)
>> > > > > > +backend_check=$(echo -e $gw1_ct | grep "10.0.0.3" | grep 
>> > > > > > "dport=80"
>> > > -c)
>> > > > > > +
>> > > > > > +if [[ $backend_check -gt 0 ]]; then
>> > > > > > +# Backend resides on ovn-chassis-1
>> > > > > > +AT_CHECK([echo -e $chassis1_ct | M_FORMAT_CT(20.0.0.3) | \
>> > > > > > +grep tcp], [0], [dnl
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=20.0.0.3,dst=10.0.0.3,sport=59004,dport=80),reply=(src=10.0.0.3,dst=20.0.0.3,sport=80,dport=59004),zone=<cleared>,protoinfo=(state=<cleared>)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Ensure that the traffic only come from ovn-chassis-1
>> > > > > > +AT_CHECK([echo -e $chassis2_ct | grep "20.0.0.3" | grep "dport=80"
>> > > -c],
>> > > > > [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +AT_CHECK([echo -e $chassis2_flow | grep "20.0.0.3" | grep 
>> > > > > > "dport=80"
>> > > > > -c], [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +else
>> > > > > > +# Backend resides on ovn-chassis-2
>> > > > > > +AT_CHECK([echo -e $chassis2_ct | M_FORMAT_CT(20.0.0.3) | \
>> > > > > > +grep tcp], [0], [dnl
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=20.0.0.3,dst=10.0.0.4,sport=59004,dport=80),reply=(src=10.0.0.4,dst=20.0.0.3,sport=80,dport=59004),zone=<cleared>,protoinfo=(state=<cleared>)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Ensure that the traffic only come from ovn-chassis-2
>> > > > > > +AT_CHECK([echo -e $chassis1_ct | grep "20.0.0.3" | grep "dport=80"
>> > > -c],
>> > > > > [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +AT_CHECK([echo -e $chassis1_flow | grep "20.0.0.3" | grep 
>> > > > > > "dport=80"
>> > > > > -c], [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +fi
>> > > > > > +
>> > > > > > +# Flush conntrack entries for easier output parsing of next test.
>> > > > > > +m_as ovn-chassis-1 ovs-appctl dpctl/flush-conntrack
>> > > > > > +m_as ovn-chassis-2 ovs-appctl dpctl/flush-conntrack
>> > > > > > +m_as ovn-gw-1 ovs-appctl dpctl/flush-conntrack
>> > > > > > +m_as ovn-gw-2 ovs-appctl dpctl/flush-conntrack
>> > > > > > +
>> > > > > > +# Check the flows again for a new source port
>> > > > > > +M_NS_EXEC([ovn-chassis-3], [publicp1], [sh -c 'curl -v -O
>> > > > > 172.16.0.100:80/download_file --retry 3 --max-time 1 --local-port
>> > > 59005
>> > > > > 2>curl.out'])
>> > > > > > +
>> > > > > > +gw1_ct=$(m_as ovn-gw-1 ovs-appctl dpctl/dump-conntrack | sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +gw2_ct=$(m_as ovn-gw-2 ovs-appctl dpctl/dump-conntrack | sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +chassis1_ct=$(m_as ovn-chassis-1 ovs-appctl dpctl/dump-conntrack |
>> > > sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +chassis2_ct=$(m_as ovn-chassis-2 ovs-appctl dpctl/dump-conntrack |
>> > > sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +chassis1_flow=$(m_as ovn-chassis-1 ovs-dpctl dump-flows | sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +chassis2_flow=$(m_as ovn-chassis-2 ovs-dpctl dump-flows | sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +
>> > > > > > +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-3 ip netns exec publicp1 cat
>> > > > > curl.out | \
>> > > > > > +grep -i -e connect | grep -v 'Server:'], [0], [dnl
>> > > > > > +* Connected to 172.16.0.100 (172.16.0.100) port 80
>> > > > > > +* Closing connection
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Check if we have only one backend for the same connection - 
>> > > > > > orig +
>> > > > > dest ports
>> > > > > > +OVS_WAIT_FOR_OUTPUT([echo -e $gw1_ct | M_FORMAT_CT(20.0.0.3) | \
>> > > > > > +grep tcp | sed -E -e 's/10.0.0.3|10.0.0.4/<cleared>/g' | sort],
>> > > [0],
>> > > > > [dnl
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=20.0.0.3,dst=<cleared>,sport=59005,dport=80),reply=(src=<cleared>,dst=20.0.0.3,sport=80,dport=59005),zone=<cleared>,mark=<cleared>,protoinfo=(state=<cleared>)
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=20.0.0.3,dst=<cleared>,sport=59005,dport=80),reply=(src=<cleared>,dst=20.0.0.3,sport=80,dport=59005),zone=<cleared>,protoinfo=(state=<cleared>)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Check if gw-2 is empty to ensure that the traffic only come
>> > > from/to
>> > > > > the originator chassis via DGP public1
>> > > > > > +AT_CHECK([echo -e $gw2_ct | grep "20.0.0.3" -c], [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Check the backend IP from ct entries on gw-1 (DGP public1)
>> > > > > > +backend_check=$(echo -e $gw1_ct | grep "10.0.0.3" | grep 
>> > > > > > "dport=80"
>> > > -c)
>> > > > > > +
>> > > > > > +if [[ $backend_check -gt 0 ]]; then
>> > > > > > +# Backend resides on ovn-chassis-1
>> > > > > > +AT_CHECK([echo -e $chassis1_ct | M_FORMAT_CT(20.0.0.3) | \
>> > > > > > +grep tcp], [0], [dnl
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=20.0.0.3,dst=10.0.0.3,sport=59005,dport=80),reply=(src=10.0.0.3,dst=20.0.0.3,sport=80,dport=59005),zone=<cleared>,protoinfo=(state=<cleared>)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Ensure that the traffic only come from ovn-chassis-1
>> > > > > > +AT_CHECK([echo -e $chassis2_ct | grep "20.0.0.3" | grep "dport=80"
>> > > -c],
>> > > > > [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +AT_CHECK([echo -e $chassis2_flow | grep "20.0.0.3" | grep 
>> > > > > > "dport=80"
>> > > > > -c], [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +else
>> > > > > > +# Backend resides on ovn-chassis-2
>> > > > > > +AT_CHECK([echo -e $chassis2_ct | M_FORMAT_CT(20.0.0.3) | \
>> > > > > > +grep tcp], [0], [dnl
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=20.0.0.3,dst=10.0.0.4,sport=59005,dport=80),reply=(src=10.0.0.4,dst=20.0.0.3,sport=80,dport=59005),zone=<cleared>,protoinfo=(state=<cleared>)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Ensure that the traffic only come from ovn-chassis-2
>> > > > > > +AT_CHECK([echo -e $chassis1_ct | grep "20.0.0.3" | grep "dport=80"
>> > > -c],
>> > > > > [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +AT_CHECK([echo -e $chassis1_flow | grep "20.0.0.3" | grep 
>> > > > > > "dport=80"
>> > > > > -c], [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +fi
>> > > > > > +
>> > > > > > +# Flush conntrack entries for easier output parsing of next test.
>> > > > > > +m_as ovn-chassis-1 ovs-appctl dpctl/flush-conntrack
>> > > > > > +m_as ovn-chassis-2 ovs-appctl dpctl/flush-conntrack
>> > > > > > +m_as ovn-gw-1 ovs-appctl dpctl/flush-conntrack
>> > > > > > +m_as ovn-gw-2 ovs-appctl dpctl/flush-conntrack
>> > > > > > +
>> > > > > > +# Start a new test using the second DGP as origin (public2)
>> > > > > > +M_NS_EXEC([ovn-chassis-4], [publicp2], [sh -c 'curl -v -O
>> > > > > 172.16.0.100:80/download_file --retry 3 --max-time 1 --local-port
>> > > 59006
>> > > > > 2>curl.out'])
>> > > > > > +
>> > > > > > +gw1_ct=$(m_as ovn-gw-1 ovs-appctl dpctl/dump-conntrack | sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +gw2_ct=$(m_as ovn-gw-2 ovs-appctl dpctl/dump-conntrack | sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +chassis1_ct=$(m_as ovn-chassis-1 ovs-appctl dpctl/dump-conntrack |
>> > > sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +chassis2_ct=$(m_as ovn-chassis-2 ovs-appctl dpctl/dump-conntrack |
>> > > sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +chassis1_flow=$(m_as ovn-chassis-1 ovs-dpctl dump-flows | sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +chassis2_flow=$(m_as ovn-chassis-2 ovs-dpctl dump-flows | sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +
>> > > > > > +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-4 ip netns exec publicp2 cat
>> > > > > curl.out | \
>> > > > > > +grep -i -e connect | grep -v 'Server:'], [0], [dnl
>> > > > > > +* Connected to 172.16.0.100 (172.16.0.100) port 80
>> > > > > > +* Closing connection
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Check if we have only one backend for the same connection - 
>> > > > > > orig +
>> > > > > dest ports
>> > > > > > +OVS_WAIT_FOR_OUTPUT([echo -e $gw2_ct | M_FORMAT_CT(30.0.0.3) | \
>> > > > > > +grep tcp | sed -E -e 's/10.0.0.3|10.0.0.4/<cleared>/g' | sort],
>> > > [0],
>> > > > > [dnl
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=30.0.0.3,dst=<cleared>,sport=59006,dport=80),reply=(src=<cleared>,dst=30.0.0.3,sport=80,dport=59006),zone=<cleared>,mark=<cleared>,protoinfo=(state=<cleared>)
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=30.0.0.3,dst=<cleared>,sport=59006,dport=80),reply=(src=<cleared>,dst=30.0.0.3,sport=80,dport=59006),zone=<cleared>,protoinfo=(state=<cleared>)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Check if gw-1 is empty to ensure that the traffic only come
>> > > from/to
>> > > > > the originator chassis via DGP public2
>> > > > > > +AT_CHECK([echo -e $gw1_ct | grep "30.0.0.3" -c], [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Check the backend IP from ct entries on gw-2 (DGP public2)
>> > > > > > +backend_check=$(echo -e $gw2_ct | grep "10.0.0.3" | grep 
>> > > > > > "dport=80"
>> > > -c)
>> > > > > > +
>> > > > > > +if [[ $backend_check -gt 0 ]]; then
>> > > > > > +# Backend resides on ovn-chassis-1
>> > > > > > +AT_CHECK([echo -e $chassis1_ct | M_FORMAT_CT(30.0.0.3) | \
>> > > > > > +grep tcp], [0], [dnl
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=30.0.0.3,dst=10.0.0.3,sport=59006,dport=80),reply=(src=10.0.0.3,dst=30.0.0.3,sport=80,dport=59006),zone=<cleared>,protoinfo=(state=<cleared>)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Ensure that the traffic only come from ovn-chassis-1
>> > > > > > +AT_CHECK([echo -e $chassis2_ct | grep "30.0.0.3" | grep "dport=80"
>> > > -c],
>> > > > > [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +AT_CHECK([echo -e $chassis2_flow | grep "30.0.0.3" | grep 
>> > > > > > "dport=80"
>> > > > > -c], [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +else
>> > > > > > +# Backend resides on ovn-chassis-2
>> > > > > > +AT_CHECK([echo -e $chassis2_ct | M_FORMAT_CT(30.0.0.3) | \
>> > > > > > +grep tcp], [0], [dnl
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=30.0.0.3,dst=10.0.0.4,sport=59006,dport=80),reply=(src=10.0.0.4,dst=30.0.0.3,sport=80,dport=59006),zone=<cleared>,protoinfo=(state=<cleared>)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Ensure that the traffic only come from ovn-chassis-2
>> > > > > > +AT_CHECK([echo -e $chassis1_ct | grep "30.0.0.3" | grep "dport=80"
>> > > -c],
>> > > > > [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +AT_CHECK([echo -e $chassis1_flow | grep "30.0.0.3" | grep 
>> > > > > > "dport=80"
>> > > > > -c], [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +fi
>> > > > > > +
>> > > > > > +# Flush conntrack entries for easier output parsing of next test.
>> > > > > > +m_as ovn-chassis-1 ovs-appctl dpctl/flush-conntrack
>> > > > > > +m_as ovn-chassis-2 ovs-appctl dpctl/flush-conntrack
>> > > > > > +m_as ovn-gw-1 ovs-appctl dpctl/flush-conntrack
>> > > > > > +m_as ovn-gw-2 ovs-appctl dpctl/flush-conntrack
>> > > > > > +
>> > > > > > +# Check the flows again for a new source port using the second DGP
>> > > as
>> > > > > origin (public2)
>> > > > > > +M_NS_EXEC([ovn-chassis-4], [publicp2], [sh -c 'curl -v -O
>> > > > > 172.16.0.100:80/download_file --retry 3 --max-time 1 --local-port
>> > > 59007
>> > > > > 2>curl.out'])
>> > > > > > +
>> > > > > > +gw1_ct=$(m_as ovn-gw-1 ovs-appctl dpctl/dump-conntrack | sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +gw2_ct=$(m_as ovn-gw-2 ovs-appctl dpctl/dump-conntrack | sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +chassis1_ct=$(m_as ovn-chassis-1 ovs-appctl dpctl/dump-conntrack |
>> > > sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +chassis2_ct=$(m_as ovn-chassis-2 ovs-appctl dpctl/dump-conntrack |
>> > > sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +chassis1_flow=$(m_as ovn-chassis-1 ovs-dpctl dump-flows | sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +chassis2_flow=$(m_as ovn-chassis-2 ovs-dpctl dump-flows | sed
>> > > > > ':a;N;$!ba;s/\n/\\n/g')
>> > > > > > +
>> > > > > > +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-4 ip netns exec publicp2 cat
>> > > > > curl.out | \
>> > > > > > +grep -i -e connect | grep -v 'Server:'], [0], [dnl
>> > > > > > +* Connected to 172.16.0.100 (172.16.0.100) port 80
>> > > > > > +* Closing connection
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Check if we have only one backend for the same connection - 
>> > > > > > orig +
>> > > > > dest ports
>> > > > > > +OVS_WAIT_FOR_OUTPUT([echo -e $gw2_ct | M_FORMAT_CT(30.0.0.3) | \
>> > > > > > +grep tcp | sed -E -e 's/10.0.0.3|10.0.0.4/<cleared>/g' | sort],
>> > > [0],
>> > > > > [dnl
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=30.0.0.3,dst=<cleared>,sport=59007,dport=80),reply=(src=<cleared>,dst=30.0.0.3,sport=80,dport=59007),zone=<cleared>,mark=<cleared>,protoinfo=(state=<cleared>)
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=30.0.0.3,dst=<cleared>,sport=59007,dport=80),reply=(src=<cleared>,dst=30.0.0.3,sport=80,dport=59007),zone=<cleared>,protoinfo=(state=<cleared>)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Check if gw-1 is empty to ensure that the traffic only come
>> > > from/to
>> > > > > the originator chassis via DGP public2
>> > > > > > +AT_CHECK([echo -e $gw1_ct | grep "30.0.0.3" -c], [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Check the backend IP from ct entries on gw-1 (DGP public1)
>> > > > > > +backend_check=$(echo -e $gw2_ct | grep "10.0.0.3" | grep 
>> > > > > > "dport=80"
>> > > -c)
>> > > > > > +
>> > > > > > +if [[ $backend_check -gt 0 ]]; then
>> > > > > > +# Backend resides on ovn-chassis-1
>> > > > > > +AT_CHECK([echo -e $chassis1_ct | M_FORMAT_CT(30.0.0.3) | \
>> > > > > > +grep tcp], [0], [dnl
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=30.0.0.3,dst=10.0.0.3,sport=59007,dport=80),reply=(src=10.0.0.3,dst=30.0.0.3,sport=80,dport=59007),zone=<cleared>,protoinfo=(state=<cleared>)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Ensure that the traffic only come from ovn-chassis-1
>> > > > > > +AT_CHECK([echo -e $chassis2_ct | grep "30.0.0.3" | grep "dport=80"
>> > > -c],
>> > > > > [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +AT_CHECK([echo -e $chassis2_flow | grep "30.0.0.3" | grep 
>> > > > > > "dport=80"
>> > > > > -c], [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +else
>> > > > > > +# Backend resides on ovn-chassis-2
>> > > > > > +AT_CHECK([echo -e $chassis2_ct | M_FORMAT_CT(30.0.0.3) | \
>> > > > > > +grep tcp], [0], [dnl
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=30.0.0.3,dst=10.0.0.4,sport=59007,dport=80),reply=(src=10.0.0.4,dst=30.0.0.3,sport=80,dport=59007),zone=<cleared>,protoinfo=(state=<cleared>)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Ensure that the traffic only come from ovn-chassis-2
>> > > > > > +AT_CHECK([echo -e $chassis1_ct | grep "30.0.0.3" | grep "dport=80"
>> > > -c],
>> > > > > [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +AT_CHECK([echo -e $chassis1_flow | grep "30.0.0.3" | grep 
>> > > > > > "dport=80"
>> > > > > -c], [1], [dnl
>> > > > > > +0
>> > > > > > +])
>> > > > > > +fi
>> > > > > > +
>> > > > > > +# Check multiple requests coming from DGP's public1 and public2
>> > > > > > +
>> > > > > > +M_NS_EXEC([ovn-chassis-4], [publicp2], [sh -c 'curl -v -O
>> > > > > 172.16.0.100:80/download_file --retry 3 --max-time 1 2>curl.out'])
>> > > > > > +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-4 cat curl.out | \
>> > > > > > +sed 's/\(.*\)200 OK/200 OK\n/' | grep -i -e connect -e "200 OK" |
>> > > grep
>> > > > > -v 'Server:'], [0], [dnl
>> > > > > > +* Connected to 172.16.0.100 (172.16.0.100) port 80
>> > > > > > +200 OK
>> > > > > > +* Closing connection
>> > > > > > +])
>> > > > > > +
>> > > > > > +M_NS_EXEC([ovn-chassis-3], [publicp1], [sh -c 'curl -v -O
>> > > > > 172.16.0.100:80/download_file --retry 3 --max-time 1 2>curl.out'])
>> > > > > > +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-3 cat curl.out | \
>> > > > > > +sed 's/\(.*\)200 OK/200 OK\n/' | grep -i -e connect -e "200 OK" |
>> > > grep
>> > > > > -v 'Server:'], [0], [dnl
>> > > > > > +* Connected to 172.16.0.100 (172.16.0.100) port 80
>> > > > > > +200 OK
>> > > > > > +* Closing connection
>> > > > > > +])
>> > > > > > +
>> > > > > > +M_NS_EXEC([ovn-chassis-4], [publicp2], [sh -c 'curl -v -O
>> > > > > 172.16.0.100:80/download_file --retry 3 --max-time 1 2>curl.out'])
>> > > > > > +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-4 cat curl.out | \
>> > > > > > +sed 's/\(.*\)200 OK/200 OK\n/' | grep -i -e connect -e "200 OK" |
>> > > grep
>> > > > > -v 'Server:'], [0], [dnl
>> > > > > > +* Connected to 172.16.0.100 (172.16.0.100) port 80
>> > > > > > +200 OK
>> > > > > > +* Closing connection
>> > > > > > +])
>> > > > > > +
>> > > > > > +M_NS_EXEC([ovn-chassis-3], [publicp1], [sh -c 'curl -v -O
>> > > > > 172.16.0.100:80/download_file --retry 3 --max-time 1 2>curl.out'])
>> > > > > > +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-3 cat curl.out | \
>> > > > > > +sed 's/\(.*\)200 OK/200 OK\n/' | grep -i -e connect -e "200 OK" |
>> > > grep
>> > > > > -v 'Server:'], [0], [dnl
>> > > > > > +* Connected to 172.16.0.100 (172.16.0.100) port 80
>> > > > > > +200 OK
>> > > > > > +* Closing connection
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Remove the LB and change the VIP port - different from the 
>> > > > > > backend
>> > > > > ports
>> > > > > > +check multinode_nbctl lb-del lb0
>> > > > > > +
>> > > > > > +# create LB again
>> > > > > > +check multinode_nbctl lb-add lb0 "172.16.0.100:9000" "10.0.0.3:80,
>> > > > > 10.0.0.4:80"
>> > > > > > +check multinode_nbctl lr-lb-add lr0 lb0
>> > > > > > +check multinode_nbctl ls-lb-add sw0 lb0
>> > > > > > +
>> > > > > > +# Set use_stateless_nat to true
>> > > > > > +check multinode_nbctl set load_balancer lb0
>> > > > > options:use_stateless_nat=true
>> > > > > > +
>> > > > > > +m_as ovn-gw-1 ovs-appctl dpctl/flush-conntrack
>> > > > > > +m_as ovn-gw-2 ovs-appctl dpctl/flush-conntrack
>> > > > > > +
>> > > > > > +# Check end-to-end request using a new port for VIP
>> > > > > > +M_NS_EXEC([ovn-chassis-3], [publicp1], [sh -c 'curl -v -O
>> > > > > 172.16.0.100:9000/download_file --retry 3 --max-time 1 --local-port
>> > > 59008
>> > > > > 2>curl.out'])
>> > > > > > +OVS_WAIT_FOR_OUTPUT([m_as ovn-gw-1 ovs-appctl 
>> > > > > > dpctl/dump-conntrack |
>> > > > > M_FORMAT_CT(20.0.0.3) | \
>> > > > > > +grep tcp | sed -E -e 's/10.0.0.3|10.0.0.4/<cleared>/g' | sort],
>> > > [0],
>> > > > > [dnl
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=20.0.0.3,dst=<cleared>,sport=59008,dport=80),reply=(src=<cleared>,dst=20.0.0.3,sport=80,dport=59008),zone=<cleared>,protoinfo=(state=<cleared>)
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=20.0.0.3,dst=<cleared>,sport=59008,dport=9000),reply=(src=<cleared>,dst=20.0.0.3,sport=80,dport=59008),zone=<cleared>,mark=<cleared>,protoinfo=(state=<cleared>)
>> > > > > > +])
>> > > > > > +
>> > > > > > +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-3 cat curl.out | \
>> > > > > > +sed 's/\(.*\)200 OK/200 OK\n/' | grep -i -e connect -e "200 OK" |
>> > > grep
>> > > > > -v 'Server:'], [0], [dnl
>> > > > > > +* Connected to 172.16.0.100 (172.16.0.100) port 9000
>> > > > > > +200 OK
>> > > > > > +* Closing connection
>> > > > > > +])
>> > > > > > +
>> > > > > > +m_as ovn-gw-1 ovs-appctl dpctl/flush-conntrack
>> > > > > > +m_as ovn-gw-2 ovs-appctl dpctl/flush-conntrack
>> > > > > > +
>> > > > > > +# Check end-to-end request using a new port for VIP
>> > > > > > +M_NS_EXEC([ovn-chassis-4], [publicp2], [sh -c 'curl -v -O
>> > > > > 172.16.0.100:9000/download_file --retry 3 --max-time 1 --local-port
>> > > 59008
>> > > > > 2>curl.out'])
>> > > > > > +OVS_WAIT_FOR_OUTPUT([m_as ovn-gw-2 ovs-appctl 
>> > > > > > dpctl/dump-conntrack |
>> > > > > M_FORMAT_CT(30.0.0.3) | \
>> > > > > > +grep tcp | sed -E -e 's/10.0.0.3|10.0.0.4/<cleared>/g' | sort],
>> > > [0],
>> > > > > [dnl
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=30.0.0.3,dst=<cleared>,sport=59008,dport=80),reply=(src=<cleared>,dst=30.0.0.3,sport=80,dport=59008),zone=<cleared>,protoinfo=(state=<cleared>)
>> > > > > >
>> > > > >
>> > > +tcp,orig=(src=30.0.0.3,dst=<cleared>,sport=59008,dport=9000),reply=(src=<cleared>,dst=30.0.0.3,sport=80,dport=59008),zone=<cleared>,mark=<cleared>,protoinfo=(state=<cleared>)
>> > > > > > +])
>> > > > > > +
>> > > > > > +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-3 cat curl.out | \
>> > > > > > +sed 's/\(.*\)200 OK/200 OK\n/' | grep -i -e connect -e "200 OK" |
>> > > grep
>> > > > > -v 'Server:'], [0], [dnl
>> > > > > > +* Connected to 172.16.0.100 (172.16.0.100) port 9000
>> > > > > > +200 OK
>> > > > > > +* Closing connection
>> > > > > > +])
>> > > > > > +
>> > > > > > +AT_CLEANUP
>> > > > > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
>> > > > > > index dcc3dbbc3..9e7a2f225 100644
>> > > > > > --- a/tests/ovn-northd.at
>> > > > > > +++ b/tests/ovn-northd.at
>> > > > > > @@ -13864,3 +13864,323 @@ check_no_redirect
>> > > > > >
>> > > > > >  AT_CLEANUP
>> > > > > >  ])
>> > > > > > +
>> > > > > > +OVN_FOR_EACH_NORTHD_NO_HV_PARALLELIZATION([
>> > > > > > +AT_SETUP([Load balancer with Distributed Gateway Ports (LB + DGP +
>> > > NAT
>> > > > > Stateless)])
>> > > > > > +ovn_start
>> > > > > > +
>> > > > > > +check ovn-nbctl ls-add public
>> > > > > > +check ovn-nbctl lr-add lr1
>> > > > > > +
>> > > > > > +# lr1 DGP ts1
>> > > > > > +check ovn-nbctl ls-add ts1
>> > > > > > +check ovn-nbctl lrp-add lr1 lr1-ts1 00:00:01:02:03:04
>> > > 172.16.10.1/24
>> > > > > > +check ovn-nbctl lrp-set-gateway-chassis lr1-ts1 chassis-2
>> > > > > > +
>> > > > > > +# lr1 DGP ts2
>> > > > > > +check ovn-nbctl ls-add ts2
>> > > > > > +check ovn-nbctl lrp-add lr1 lr1-ts2 00:00:01:02:03:05
>> > > 172.16.20.1/24
>> > > > > > +check ovn-nbctl lrp-set-gateway-chassis lr1-ts2 chassis-3
>> > > > > > +
>> > > > > > +# lr1 DGP public
>> > > > > > +check ovn-nbctl lrp-add lr1 lr1_public 00:de:ad:ff:00:01
>> > > 173.16.0.1/16
>> > > > > > +check ovn-nbctl lrp-add lr1 lr1_s1 00:de:ad:fe:00:02 172.16.0.1/24
>> > > > > > +check ovn-nbctl lrp-set-gateway-chassis lr1_public chassis-1
>> > > > > > +
>> > > > > > +check ovn-nbctl ls-add s1
>> > > > > > +# s1 - lr1
>> > > > > > +check ovn-nbctl lsp-add s1 s1_lr1
>> > > > > > +check ovn-nbctl lsp-set-type s1_lr1 router
>> > > > > > +check ovn-nbctl lsp-set-addresses s1_lr1 "00:de:ad:fe:00:02
>> > > 172.16.0.1"
>> > > > > > +check ovn-nbctl lsp-set-options s1_lr1 router-port=lr1_s1
>> > > > > > +
>> > > > > > +# s1 - backend vm1
>> > > > > > +check ovn-nbctl lsp-add s1 vm1
>> > > > > > +check ovn-nbctl lsp-set-addresses vm1 "00:de:ad:01:00:01
>> > > 172.16.0.101"
>> > > > > > +
>> > > > > > +# s1 - backend vm2
>> > > > > > +check ovn-nbctl lsp-add s1 vm2
>> > > > > > +check ovn-nbctl lsp-set-addresses vm2 "00:de:ad:01:00:02
>> > > 172.16.0.102"
>> > > > > > +
>> > > > > > +# s1 - backend vm3
>> > > > > > +check ovn-nbctl lsp-add s1 vm3
>> > > > > > +check ovn-nbctl lsp-set-addresses vm3 "00:de:ad:01:00:03
>> > > 172.16.0.103"
>> > > > > > +
>> > > > > > +# Add the lr1 DGP ts1 to the public switch
>> > > > > > +check ovn-nbctl lsp-add public public_lr1_ts1
>> > > > > > +check ovn-nbctl lsp-set-type public_lr1_ts1 router
>> > > > > > +check ovn-nbctl lsp-set-addresses public_lr1_ts1 router
>> > > > > > +check ovn-nbctl lsp-set-options public_lr1_ts1 router-port=lr1-ts1
>> > > > > nat-addresses=router
>> > > > > > +
>> > > > > > +# Add the lr1 DGP ts2 to the public switch
>> > > > > > +check ovn-nbctl lsp-add public public_lr1_ts2
>> > > > > > +check ovn-nbctl lsp-set-type public_lr1_ts2 router
>> > > > > > +check ovn-nbctl lsp-set-addresses public_lr1_ts2 router
>> > > > > > +check ovn-nbctl lsp-set-options public_lr1_ts2 router-port=lr1-ts2
>> > > > > nat-addresses=router
>> > > > > > +
>> > > > > > +# Add the lr1 DGP public to the public switch
>> > > > > > +check ovn-nbctl lsp-add public public_lr1
>> > > > > > +check ovn-nbctl lsp-set-type public_lr1 router
>> > > > > > +check ovn-nbctl lsp-set-addresses public_lr1 router
>> > > > > > +check ovn-nbctl lsp-set-options public_lr1 router-port=lr1_public
>> > > > > nat-addresses=router
>> > > > > > +
>> > > > > > +# Create the Load Balancer lb1
>> > > > > > +check ovn-nbctl --wait=sb lb-add lb1 "30.0.0.1"
>> > > > > "172.16.0.103,172.16.0.102,172.16.0.101"
>> > > > > > +
>> > > > > > +# Set use_stateless_nat to true
>> > > > > > +check ovn-nbctl --wait=sb set load_balancer lb1
>> > > > > options:use_stateless_nat=true
>> > > > > > +
>> > > > > > +# Associate load balancer to s1
>> > > > > > +check ovn-nbctl ls-lb-add s1 lb1
>> > > > > > +check ovn-nbctl --wait=sb sync
>> > > > > > +
>> > > > > > +ovn-sbctl dump-flows s1 > s1flows
>> > > > > > +AT_CAPTURE_FILE([s1flows])
>> > > > > > +
>> > > > > > +AT_CHECK([grep "ls_in_pre_stateful" s1flows | ovn_strip_lflows |
>> > > grep
>> > > > > "30.0.0.1"], [0], [dnl
>> > > > > > +  table=??(ls_in_pre_stateful ), priority=120  , match=(reg0[[2]]
>> > > == 1
>> > > > > && ip4.dst == 30.0.0.1), action=(reg1 = 30.0.0.1; ct_lb_mark;)
>> > > > > > +])
>> > > > > > +AT_CHECK([grep "ls_in_lb" s1flows | ovn_strip_lflows | grep
>> > > > > "30.0.0.1"], [0], [dnl
>> > > > > > +  table=??(ls_in_lb           ), priority=110  , match=(ct.new &&
>> > > > > ip4.dst == 30.0.0.1),
>> > > > > action=(ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Associate load balancer to lr1 with DGP
>> > > > > > +check ovn-nbctl lr-lb-add lr1 lb1
>> > > > > > +check ovn-nbctl --wait=sb sync
>> > > > > > +
>> > > > > > +ovn-sbctl dump-flows lr1 > lr1flows
>> > > > > > +AT_CAPTURE_FILE([lr1flows])
>> > > > > > +
>> > > > > > +# Check stateless NAT rules for load balancer with multiple DGP
>> > > > > > +# 1. Check if the backend IPs are in the ipX.dst action
>> > > > > > +AT_CHECK([grep "lr_in_dnat" lr1flows | ovn_strip_lflows | grep
>> > > > > "30.0.0.1"], [0], [dnl
>> > > > > > +  table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> > > > > !ct.rel && ip4 && ip4.dst == 30.0.0.1 &&
>> > > > > is_chassis_resident("cr-lr1-ts1")),
>> > > > >
>> > > action=(ip4.dst=172.16.0.103;ip4.dst=172.16.0.102;ip4.dst=172.16.0.101;ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);)
>> > > > > > +  table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> > > > > !ct.rel && ip4 && ip4.dst == 30.0.0.1 &&
>> > > > > is_chassis_resident("cr-lr1-ts2")),
>> > > > >
>> > > action=(ip4.dst=172.16.0.103;ip4.dst=172.16.0.102;ip4.dst=172.16.0.101;ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);)
>> > > > > > +  table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> > > > > !ct.rel && ip4 && ip4.dst == 30.0.0.1 &&
>> > > > > is_chassis_resident("cr-lr1_public")),
>> > > > >
>> > > action=(ip4.dst=172.16.0.103;ip4.dst=172.16.0.102;ip4.dst=172.16.0.101;ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# 2. Check if the DGP ports are in the match with action next
>> > > > > > +AT_CHECK([grep "lr_out_undnat" lr1flows | ovn_strip_lflows], [0],
>> > > [dnl
>> > > > > > +  table=??(lr_out_undnat      ), priority=0    , match=(1),
>> > > > > action=(next;)
>> > > > > > +  table=??(lr_out_undnat      ), priority=120  , match=(ip4 &&
>> > > > > ((ip4.src == 172.16.0.103) || (ip4.src == 172.16.0.102) || (ip4.src 
>> > > > > ==
>> > > > > 172.16.0.101)) && (inport == "lr1-ts1" || outport == "lr1-ts1") &&
>> > > > > is_chassis_resident("cr-lr1-ts1") && tcp), action=(next;)
>> > > > > > +  table=??(lr_out_undnat      ), priority=120  , match=(ip4 &&
>> > > > > ((ip4.src == 172.16.0.103) || (ip4.src == 172.16.0.102) || (ip4.src 
>> > > > > ==
>> > > > > 172.16.0.101)) && (inport == "lr1-ts2" || outport == "lr1-ts2") &&
>> > > > > is_chassis_resident("cr-lr1-ts2") && tcp), action=(next;)
>> > > > > > +  table=??(lr_out_undnat      ), priority=120  , match=(ip4 &&
>> > > > > ((ip4.src == 172.16.0.103) || (ip4.src == 172.16.0.102) || (ip4.src 
>> > > > > ==
>> > > > > 172.16.0.101)) && (inport == "lr1_public" || outport == "lr1_public")
>> > > &&
>> > > > > is_chassis_resident("cr-lr1_public") && tcp), action=(next;)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# 3. Check if the VIP IP is in the ipX.src action
>> > > > > > +AT_CHECK([grep "lr_out_snat" lr1flows | ovn_strip_lflows], [0], 
>> > > > > > [dnl
>> > > > > > +  table=??(lr_out_snat        ), priority=0    , match=(1),
>> > > > > action=(next;)
>> > > > > > +  table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> > > > > action=(next;)
>> > > > > > +  table=??(lr_out_snat        ), priority=160  , match=(ip4 &&
>> > > > > ((ip4.src == 172.16.0.103) || (ip4.src == 172.16.0.102) || (ip4.src 
>> > > > > ==
>> > > > > 172.16.0.101)) && (inport == "lr1-ts1" || outport == "lr1-ts1") &&
>> > > > > is_chassis_resident("cr-lr1-ts1") && tcp), action=(ip4.src=30.0.0.1;
>> > > next;)
>> > > > > > +  table=??(lr_out_snat        ), priority=160  , match=(ip4 &&
>> > > > > ((ip4.src == 172.16.0.103) || (ip4.src == 172.16.0.102) || (ip4.src 
>> > > > > ==
>> > > > > 172.16.0.101)) && (inport == "lr1-ts2" || outport == "lr1-ts2") &&
>> > > > > is_chassis_resident("cr-lr1-ts2") && tcp), action=(ip4.src=30.0.0.1;
>> > > next;)
>> > > > > > +  table=??(lr_out_snat        ), priority=160  , match=(ip4 &&
>> > > > > ((ip4.src == 172.16.0.103) || (ip4.src == 172.16.0.102) || (ip4.src 
>> > > > > ==
>> > > > > 172.16.0.101)) && (inport == "lr1_public" || outport == "lr1_public")
>> > > &&
>> > > > > is_chassis_resident("cr-lr1_public") && tcp), 
>> > > > > action=(ip4.src=30.0.0.1;
>> > > > > next;)
>> > > > > > +])
>> > > > > > +
>> > > > > > +AT_CLEANUP
>> > > > > > +])
>> > > > > > +
>> > > > > > +OVN_FOR_EACH_NORTHD_NO_HV_PARALLELIZATION([
>> > > > > > +AT_SETUP([Load balancer with Distributed Gateway Ports (LB + DGP +
>> > > NAT
>> > > > > Stateless) - IPv6])
>> > > > > > +ovn_start
>> > > > > > +
>> > > > > > +check ovn-nbctl ls-add public
>> > > > > > +check ovn-nbctl lr-add lr1
>> > > > > > +
>> > > > > > +# lr1 DGP ts1
>> > > > > > +check ovn-nbctl ls-add ts1
>> > > > > > +check ovn-nbctl lrp-add lr1 lr1-ts1 00:00:01:02:03:04
>> > > > > 2001:db8:aaaa:1::1/64
>> > > > > > +check ovn-nbctl lrp-set-gateway-chassis lr1-ts1 chassis-2
>> > > > > > +
>> > > > > > +# lr1 DGP ts2
>> > > > > > +check ovn-nbctl ls-add ts2
>> > > > > > +check ovn-nbctl lrp-add lr1 lr1-ts2 00:00:01:02:03:05
>> > > > > 2001:db8:aaaa:2::1/64
>> > > > > > +check ovn-nbctl lrp-set-gateway-chassis lr1-ts2 chassis-3
>> > > > > > +
>> > > > > > +# lr1 DGP public
>> > > > > > +check ovn-nbctl lrp-add lr1 lr1_public 00:de:ad:ff:00:01
>> > > > > 2001:db8:bbbb::1/64
>> > > > > > +check ovn-nbctl lrp-add lr1 lr1_s1 00:de:ad:fe:00:02
>> > > > > 2001:db8:aaaa:3::1/64
>> > > > > > +check ovn-nbctl lrp-set-gateway-chassis lr1_public chassis-1
>> > > > > > +
>> > > > > > +check ovn-nbctl ls-add s1
>> > > > > > +# s1 - lr1
>> > > > > > +check ovn-nbctl lsp-add s1 s1_lr1
>> > > > > > +check ovn-nbctl lsp-set-type s1_lr1 router
>> > > > > > +check ovn-nbctl lsp-set-addresses s1_lr1 "00:de:ad:fe:00:02
>> > > > > 2001:db8:aaaa:3::1"
>> > > > > > +check ovn-nbctl lsp-set-options s1_lr1 router-port=lr1_s1
>> > > > > > +
>> > > > > > +# s1 - backend vm1
>> > > > > > +check ovn-nbctl lsp-add s1 vm1
>> > > > > > +check ovn-nbctl lsp-set-addresses vm1 "00:de:ad:01:00:01
>> > > > > 2001:db8:aaaa:3::101"
>> > > > > > +
>> > > > > > +# s1 - backend vm2
>> > > > > > +check ovn-nbctl lsp-add s1 vm2
>> > > > > > +check ovn-nbctl lsp-set-addresses vm2 "00:de:ad:01:00:02
>> > > > > 2001:db8:aaaa:3::102"
>> > > > > > +
>> > > > > > +# s1 - backend vm3
>> > > > > > +check ovn-nbctl lsp-add s1 vm3
>> > > > > > +check ovn-nbctl lsp-set-addresses vm3 "00:de:ad:01:00:03
>> > > > > 2001:db8:aaaa:3::103"
>> > > > > > +
>> > > > > > +# Add the lr1 DGP ts1 to the public switch
>> > > > > > +check ovn-nbctl lsp-add public public_lr1_ts1
>> > > > > > +check ovn-nbctl lsp-set-type public_lr1_ts1 router
>> > > > > > +check ovn-nbctl lsp-set-addresses public_lr1_ts1 router
>> > > > > > +check ovn-nbctl lsp-set-options public_lr1_ts1 router-port=lr1-ts1
>> > > > > nat-addresses=router
>> > > > > > +
>> > > > > > +# Add the lr1 DGP ts2 to the public switch
>> > > > > > +check ovn-nbctl lsp-add public public_lr1_ts2
>> > > > > > +check ovn-nbctl lsp-set-type public_lr1_ts2 router
>> > > > > > +check ovn-nbctl lsp-set-addresses public_lr1_ts2 router
>> > > > > > +check ovn-nbctl lsp-set-options public_lr1_ts2 router-port=lr1-ts2
>> > > > > nat-addresses=router
>> > > > > > +
>> > > > > > +# Add the lr1 DGP public to the public switch
>> > > > > > +check ovn-nbctl lsp-add public public_lr1
>> > > > > > +check ovn-nbctl lsp-set-type public_lr1 router
>> > > > > > +check ovn-nbctl lsp-set-addresses public_lr1 router
>> > > > > > +check ovn-nbctl lsp-set-options public_lr1 router-port=lr1_public
>> > > > > nat-addresses=router
>> > > > > > +
>> > > > > > +# Create the Load Balancer lb1
>> > > > > > +check ovn-nbctl --wait=sb lb-add lb1 "2001:db8:cccc::1"
>> > > > > "2001:db8:aaaa:3::103,2001:db8:aaaa:3::102,2001:db8:aaaa:3::101"
>> > > > > > +
>> > > > > > +# Set use_stateless_nat to true
>> > > > > > +check ovn-nbctl --wait=sb set load_balancer lb1
>> > > > > options:use_stateless_nat=true
>> > > > > > +
>> > > > > > +# Associate load balancer to s1
>> > > > > > +check ovn-nbctl ls-lb-add s1 lb1
>> > > > > > +check ovn-nbctl --wait=sb sync
>> > > > > > +
>> > > > > > +ovn-sbctl dump-flows s1 > s1flows
>> > > > > > +AT_CAPTURE_FILE([s1flows])
>> > > > > > +
>> > > > > > +AT_CHECK([grep "ls_in_pre_stateful" s1flows | ovn_strip_lflows |
>> > > grep
>> > > > > "2001:db8:cccc::1"], [0], [dnl
>> > > > > > +  table=??(ls_in_pre_stateful ), priority=120  , match=(reg0[[2]]
>> > > == 1
>> > > > > && ip6.dst == 2001:db8:cccc::1), action=(xxreg1 = 2001:db8:cccc::1;
>> > > > > ct_lb_mark;)
>> > > > > > +])
>> > > > > > +AT_CHECK([grep "ls_in_lb" s1flows | ovn_strip_lflows | grep
>> > > > > "2001:db8:cccc::1"], [0], [dnl
>> > > > > > +  table=??(ls_in_lb           ), priority=110  , match=(ct.new &&
>> > > > > ip6.dst == 2001:db8:cccc::1),
>> > > > >
>> > > action=(ct_lb_mark(backends=2001:db8:aaaa:3::103,2001:db8:aaaa:3::102,2001:db8:aaaa:3::101);)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Associate load balancer to lr1 with DGP
>> > > > > > +check ovn-nbctl lr-lb-add lr1 lb1
>> > > > > > +check ovn-nbctl --wait=sb sync
>> > > > > > +
>> > > > > > +ovn-sbctl dump-flows lr1 > lr1flows
>> > > > > > +AT_CAPTURE_FILE([lr1flows])
>> > > > > > +
>> > > > > > +# Check stateless NAT rules for load balancer with multiple DGP
>> > > > > > +# 1. Check if the backend IPs are in the ipX.dst action
>> > > > > > +AT_CHECK([grep "lr_in_dnat" lr1flows | ovn_strip_lflows | grep
>> > > > > "2001:db8:cccc::1"], [0], [dnl
>> > > > > > +  table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> > > > > !ct.rel && ip6 && ip6.dst == 2001:db8:cccc::1 &&
>> > > > > is_chassis_resident("cr-lr1-ts1")),
>> > > > >
>> > > action=(ip6.dst=2001:db8:aaaa:3::103;ip6.dst=2001:db8:aaaa:3::102;ip6.dst=2001:db8:aaaa:3::101;ct_lb_mark(backends=2001:db8:aaaa:3::103,2001:db8:aaaa:3::102,2001:db8:aaaa:3::101);)
>> > > > > > +  table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> > > > > !ct.rel && ip6 && ip6.dst == 2001:db8:cccc::1 &&
>> > > > > is_chassis_resident("cr-lr1-ts2")),
>> > > > >
>> > > action=(ip6.dst=2001:db8:aaaa:3::103;ip6.dst=2001:db8:aaaa:3::102;ip6.dst=2001:db8:aaaa:3::101;ct_lb_mark(backends=2001:db8:aaaa:3::103,2001:db8:aaaa:3::102,2001:db8:aaaa:3::101);)
>> > > > > > +  table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> > > > > !ct.rel && ip6 && ip6.dst == 2001:db8:cccc::1 &&
>> > > > > is_chassis_resident("cr-lr1_public")),
>> > > > >
>> > > action=(ip6.dst=2001:db8:aaaa:3::103;ip6.dst=2001:db8:aaaa:3::102;ip6.dst=2001:db8:aaaa:3::101;ct_lb_mark(backends=2001:db8:aaaa:3::103,2001:db8:aaaa:3::102,2001:db8:aaaa:3::101);)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# 2. Check if the DGP ports are in the match with action next
>> > > > > > +AT_CHECK([grep "lr_out_undnat" lr1flows | ovn_strip_lflows], [0],
>> > > [dnl
>> > > > > > +  table=??(lr_out_undnat      ), priority=0    , match=(1),
>> > > > > action=(next;)
>> > > > > > +  table=??(lr_out_undnat      ), priority=120  , match=(ip6 &&
>> > > > > ((ip6.src == 2001:db8:aaaa:3::103) || (ip6.src ==
>> > > 2001:db8:aaaa:3::102) ||
>> > > > > (ip6.src == 2001:db8:aaaa:3::101)) && (inport == "lr1-ts1" || outport
>> > > ==
>> > > > > "lr1-ts1") && is_chassis_resident("cr-lr1-ts1") && tcp), 
>> > > > > action=(next;)
>> > > > > > +  table=??(lr_out_undnat      ), priority=120  , match=(ip6 &&
>> > > > > ((ip6.src == 2001:db8:aaaa:3::103) || (ip6.src ==
>> > > 2001:db8:aaaa:3::102) ||
>> > > > > (ip6.src == 2001:db8:aaaa:3::101)) && (inport == "lr1-ts2" || outport
>> > > ==
>> > > > > "lr1-ts2") && is_chassis_resident("cr-lr1-ts2") && tcp), 
>> > > > > action=(next;)
>> > > > > > +  table=??(lr_out_undnat      ), priority=120  , match=(ip6 &&
>> > > > > ((ip6.src == 2001:db8:aaaa:3::103) || (ip6.src ==
>> > > 2001:db8:aaaa:3::102) ||
>> > > > > (ip6.src == 2001:db8:aaaa:3::101)) && (inport == "lr1_public" ||
>> > > outport ==
>> > > > > "lr1_public") && is_chassis_resident("cr-lr1_public") && tcp),
>> > > > > action=(next;)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# 3. Check if the VIP IP is in the ipX.src action
>> > > > > > +AT_CHECK([grep "lr_out_snat" lr1flows | ovn_strip_lflows], [0], 
>> > > > > > [dnl
>> > > > > > +  table=??(lr_out_snat        ), priority=0    , match=(1),
>> > > > > action=(next;)
>> > > > > > +  table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> > > > > action=(next;)
>> > > > > > +  table=??(lr_out_snat        ), priority=160  , match=(ip6 &&
>> > > > > ((ip6.src == 2001:db8:aaaa:3::103) || (ip6.src ==
>> > > 2001:db8:aaaa:3::102) ||
>> > > > > (ip6.src == 2001:db8:aaaa:3::101)) && (inport == "lr1-ts1" || outport
>> > > ==
>> > > > > "lr1-ts1") && is_chassis_resident("cr-lr1-ts1") && tcp),
>> > > > > action=(ip6.src=2001:db8:cccc::1; next;)
>> > > > > > +  table=??(lr_out_snat        ), priority=160  , match=(ip6 &&
>> > > > > ((ip6.src == 2001:db8:aaaa:3::103) || (ip6.src ==
>> > > 2001:db8:aaaa:3::102) ||
>> > > > > (ip6.src == 2001:db8:aaaa:3::101)) && (inport == "lr1-ts2" || outport
>> > > ==
>> > > > > "lr1-ts2") && is_chassis_resident("cr-lr1-ts2") && tcp),
>> > > > > action=(ip6.src=2001:db8:cccc::1; next;)
>> > > > > > +  table=??(lr_out_snat        ), priority=160  , match=(ip6 &&
>> > > > > ((ip6.src == 2001:db8:aaaa:3::103) || (ip6.src ==
>> > > 2001:db8:aaaa:3::102) ||
>> > > > > (ip6.src == 2001:db8:aaaa:3::101)) && (inport == "lr1_public" ||
>> > > outport ==
>> > > > > "lr1_public") && is_chassis_resident("cr-lr1_public") && tcp),
>> > > > > action=(ip6.src=2001:db8:cccc::1; next;)
>> > > > > > +])
>> > > > > > +
>> > > > > > +AT_CLEANUP
>> > > > > > +])
>> > > > > > +
>> > > > > > +OVN_FOR_EACH_NORTHD_NO_HV_PARALLELIZATION([
>> > > > > > +AT_SETUP([Load balancer with Distributed Gateway Ports (DGP)])
>> > > > > > +ovn_start
>> > > > > > +
>> > > > > > +check ovn-nbctl ls-add public
>> > > > > > +check ovn-nbctl lr-add lr1
>> > > > > > +
>> > > > > > +# lr1 DGP ts1
>> > > > > > +check ovn-nbctl ls-add ts1
>> > > > > > +check ovn-nbctl lrp-add lr1 lr1-ts1 00:00:01:02:03:04
>> > > 172.16.10.1/24
>> > > > > > +check ovn-nbctl lrp-set-gateway-chassis lr1-ts1 chassis-1
>> > > > > > +
>> > > > > > +# lr1 DGP ts2
>> > > > > > +check ovn-nbctl ls-add ts2
>> > > > > > +check ovn-nbctl lrp-add lr1 lr1-ts2 00:00:01:02:03:05
>> > > 172.16.20.1/24
>> > > > > > +check ovn-nbctl lrp-set-gateway-chassis lr1-ts2 chassis-1
>> > > > > > +
>> > > > > > +# lr1 DGP public
>> > > > > > +check ovn-nbctl lrp-add lr1 lr1_public 00:de:ad:ff:00:01
>> > > 173.16.0.1/16
>> > > > > > +check ovn-nbctl lrp-add lr1 lr1_s1 00:de:ad:fe:00:02 172.16.0.1/24
>> > > > > > +check ovn-nbctl lrp-set-gateway-chassis lr1_public chassis-1
>> > > > > > +
>> > > > > > +check ovn-nbctl ls-add s1
>> > > > > > +# s1 - lr1
>> > > > > > +check ovn-nbctl lsp-add s1 s1_lr1
>> > > > > > +check ovn-nbctl lsp-set-type s1_lr1 router
>> > > > > > +check ovn-nbctl lsp-set-addresses s1_lr1 "00:de:ad:fe:00:02
>> > > 172.16.0.1"
>> > > > > > +check ovn-nbctl lsp-set-options s1_lr1 router-port=lr1_s1
>> > > > > > +
>> > > > > > +# s1 - backend vm1
>> > > > > > +check ovn-nbctl lsp-add s1 vm1
>> > > > > > +check ovn-nbctl lsp-set-addresses vm1 "00:de:ad:01:00:01
>> > > 172.16.0.101"
>> > > > > > +
>> > > > > > +# s1 - backend vm2
>> > > > > > +check ovn-nbctl lsp-add s1 vm2
>> > > > > > +check ovn-nbctl lsp-set-addresses vm2 "00:de:ad:01:00:02
>> > > 172.16.0.102"
>> > > > > > +
>> > > > > > +# s1 - backend vm3
>> > > > > > +check ovn-nbctl lsp-add s1 vm3
>> > > > > > +check ovn-nbctl lsp-set-addresses vm3 "00:de:ad:01:00:03
>> > > 172.16.0.103"
>> > > > > > +
>> > > > > > +# Add the lr1 DGP ts1 to the public switch
>> > > > > > +check ovn-nbctl lsp-add public public_lr1_ts1
>> > > > > > +check ovn-nbctl lsp-set-type public_lr1_ts1 router
>> > > > > > +check ovn-nbctl lsp-set-addresses public_lr1_ts1 router
>> > > > > > +check ovn-nbctl lsp-set-options public_lr1_ts1 router-port=lr1-ts1
>> > > > > nat-addresses=router
>> > > > > > +
>> > > > > > +# Add the lr1 DGP ts2 to the public switch
>> > > > > > +check ovn-nbctl lsp-add public public_lr1_ts2
>> > > > > > +check ovn-nbctl lsp-set-type public_lr1_ts2 router
>> > > > > > +check ovn-nbctl lsp-set-addresses public_lr1_ts2 router
>> > > > > > +check ovn-nbctl lsp-set-options public_lr1_ts2 router-port=lr1-ts2
>> > > > > nat-addresses=router
>> > > > > > +
>> > > > > > +# Add the lr1 DGP public to the public switch
>> > > > > > +check ovn-nbctl lsp-add public public_lr1
>> > > > > > +check ovn-nbctl lsp-set-type public_lr1 router
>> > > > > > +check ovn-nbctl lsp-set-addresses public_lr1 router
>> > > > > > +check ovn-nbctl lsp-set-options public_lr1 router-port=lr1_public
>> > > > > nat-addresses=router
>> > > > > > +
>> > > > > > +# Create the Load Balancer lb1
>> > > > > > +check ovn-nbctl --wait=sb lb-add lb1 "30.0.0.1"
>> > > > > "172.16.0.103,172.16.0.102,172.16.0.101"
>> > > > > > +
>> > > > > > +# Associate load balancer to s1
>> > > > > > +check ovn-nbctl ls-lb-add s1 lb1
>> > > > > > +check ovn-nbctl --wait=sb sync
>> > > > > > +
>> > > > > > +ovn-sbctl dump-flows s1 > s1flows
>> > > > > > +AT_CAPTURE_FILE([s1flows])
>> > > > > > +
>> > > > > > +AT_CHECK([grep "ls_in_pre_stateful" s1flows | ovn_strip_lflows |
>> > > grep
>> > > > > "30.0.0.1"], [0], [dnl
>> > > > > > +  table=??(ls_in_pre_stateful ), priority=120  , match=(reg0[[2]]
>> > > == 1
>> > > > > && ip4.dst == 30.0.0.1), action=(reg1 = 30.0.0.1; ct_lb_mark;)
>> > > > > > +])
>> > > > > > +AT_CHECK([grep "ls_in_lb" s1flows | ovn_strip_lflows | grep
>> > > > > "30.0.0.1"], [0], [dnl
>> > > > > > +  table=??(ls_in_lb           ), priority=110  , match=(ct.new &&
>> > > > > ip4.dst == 30.0.0.1),
>> > > > > action=(ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);)
>> > > > > > +])
>> > > > > > +
>> > > > > > +# Associate load balancer to lr1 with DGP
>> > > > > > +check ovn-nbctl lr-lb-add lr1 lb1
>> > > > > > +check ovn-nbctl --wait=sb sync
>> > > > > > +
>> > > > > > +ovn-sbctl dump-flows lr1 > lr1flows
>> > > > > > +AT_CAPTURE_FILE([lr1flows])
>> > > > > > +
>> > > > > > +AT_CHECK([grep "lr_in_dnat" lr1flows | ovn_strip_lflows | grep
>> > > > > "30.0.0.1"], [0], [dnl
>> > > > > > +  table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> > > > > !ct.rel && ip4 && ip4.dst == 30.0.0.1 &&
>> > > > > is_chassis_resident("cr-lr1-ts1")),
>> > > > > action=(ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);)
>> > > > > > +  table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> > > > > !ct.rel && ip4 && ip4.dst == 30.0.0.1 &&
>> > > > > is_chassis_resident("cr-lr1-ts2")),
>> > > > > action=(ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);)
>> > > > > > +  table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> > > > > !ct.rel && ip4 && ip4.dst == 30.0.0.1 &&
>> > > > > is_chassis_resident("cr-lr1_public")),
>> > > > > action=(ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);)
>> > > > > > +])
>> > > > > > +
>> > > > > > +AT_CLEANUP
>> > > > > > +])
>> > > > > > --
>> > > > > > 2.34.1
>> > > > > >
>> > > > > >
>> > > > > > --
>> > > > > >
>> > > > > >
>> > > > > >
>> > > > > >
>> > > > > > _'Esta mensagem é direcionada apenas para os endereços constantes 
>> > > > > > no
>> > > > > > cabeçalho inicial. Se você não está listado nos endereços 
>> > > > > > constantes
>> > > no
>> > > > > > cabeçalho, pedimos-lhe que desconsidere completamente o conteúdo
>> > > dessa
>> > > > > > mensagem e cuja cópia, encaminhamento e/ou execução das ações 
>> > > > > > citadas
>> > > > > estão
>> > > > > > imediatamente anuladas e proibidas'._
>> > > > > >
>> > > > > >
>> > > > > > * **'Apesar do Magazine Luiza tomar
>> > > > > > todas as precauções razoáveis para assegurar que nenhum vírus 
>> > > > > > esteja
>> > > > > > presente nesse e-mail, a empresa não poderá aceitar a
>> > > responsabilidade
>> > > > > por
>> > > > > > quaisquer perdas ou danos causados por esse e-mail ou por seus
>> > > anexos'.*
>> > > > > >
>> > > > > >
>> > > > > >
>> > > > > > _______________________________________________
>> > > > > > dev mailing list
>> > > > > > [email protected]
>> > > > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>> > > > > >
>> > > > >
>> > > >
>> > > > --
>> > > >
>> > > >
>> > > >
>> > > >
>> > > > _‘Esta mensagem é direcionada apenas para os endereços constantes no
>> > > > cabeçalho inicial. Se você não está listado nos endereços constantes no
>> > > > cabeçalho, pedimos-lhe que desconsidere completamente o conteúdo dessa
>> > > > mensagem e cuja cópia, encaminhamento e/ou execução das ações citadas
>> > > estão
>> > > > imediatamente anuladas e proibidas’._
>> > > >
>> > > >
>> > > > * **‘Apesar do Magazine Luiza tomar
>> > > > todas as precauções razoáveis para assegurar que nenhum vírus esteja
>> > > > presente nesse e-mail, a empresa não poderá aceitar a responsabilidade
>> > > por
>> > > > quaisquer perdas ou danos causados por esse e-mail ou por seus 
>> > > > anexos’.*
>> > > >
>> > > >
>> > > >
>> > > > _______________________________________________
>> > > > dev mailing list
>> > > > [email protected]
>> > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>> > >
>> >
>> > --
>> >
>> >
>> >
>> >
>> > _‘Esta mensagem é direcionada apenas para os endereços constantes no
>> > cabeçalho inicial. Se você não está listado nos endereços constantes no
>> > cabeçalho, pedimos-lhe que desconsidere completamente o conteúdo dessa
>> > mensagem e cuja cópia, encaminhamento e/ou execução das ações citadas estão
>> > imediatamente anuladas e proibidas’._
>> >
>> >
>> > * **‘Apesar do Magazine Luiza tomar
>> > todas as precauções razoáveis para assegurar que nenhum vírus esteja
>> > presente nesse e-mail, a empresa não poderá aceitar a responsabilidade por
>> > quaisquer perdas ou danos causados por esse e-mail ou por seus anexos’.*
>> >
>> >
>> >
>> > _______________________________________________
>> > dev mailing list
>> > [email protected]
>> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>
>
>
> ‘Esta mensagem é direcionada apenas para os endereços constantes no cabeçalho 
> inicial. Se você não está listado nos endereços constantes no cabeçalho, 
> pedimos-lhe que desconsidere completamente o conteúdo dessa mensagem e cuja 
> cópia, encaminhamento e/ou execução das ações citadas estão imediatamente 
> anuladas e proibidas’.
>
>  ‘Apesar do Magazine Luiza tomar todas as precauções razoáveis para assegurar 
> que nenhum vírus esteja presente nesse e-mail, a empresa não poderá aceitar a 
> responsabilidade por quaisquer perdas ou danos causados por esse e-mail ou 
> por seus anexos’.
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to