bbaaef
From 22b415470df9cafe1fec9f3095faf14172d1c4a6 Mon Sep 17 00:00:00 2001
bbaaef
From: Ankur Sharma <ankur.sharma@nutanix.com>
bbaaef
Date: Fri, 1 Nov 2019 01:27:39 +0000
bbaaef
Subject: [PATCH 07/12] OVN: Use ip4.src and ip4.dst actions for NAT rules
bbaaef
bbaaef
For dnat_and_snat rules which are meant to be stateless
bbaaef
instead of using ct_snat/dnat OVN actions, we will use
bbaaef
ip4.src/ip4.dst.
bbaaef
bbaaef
This actions will do 1:1 mapping to inner ip to external ip,
bbaaef
while recalculating the checksums.
bbaaef
bbaaef
Signed-off-by: Ankur Sharma <ankur.sharma@nutanix.com>
bbaaef
Signed-off-by: Numan Siddique <numans@ovn.org>
bbaaef
---
bbaaef
 ovn/northd/ovn-northd.8.xml |  33 +++-
bbaaef
 ovn/northd/ovn-northd.c     |  84 ++++++++--
bbaaef
 tests/ovn-northd.at         |  95 +++++++++++
bbaaef
 tests/ovn.at                | 311 ++++++++++++++++++++++++++++++++++++
bbaaef
 4 files changed, 508 insertions(+), 15 deletions(-)
bbaaef
bbaaef
diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
bbaaef
index 83c90d680..d1af97d15 100644
bbaaef
--- a/ovn/northd/ovn-northd.8.xml
bbaaef
+++ b/ovn/northd/ovn-northd.8.xml
bbaaef
@@ -1840,7 +1840,10 @@ icmp6 {
bbaaef
           B, a priority-90 flow matches
bbaaef
           ip && ip4.dst == B or
bbaaef
           ip && ip6.dst == B
bbaaef
-          with an action ct_snat; .
bbaaef
+          with an action ct_snat; . If the NAT rule is of type
bbaaef
+          dnat_and_snat and has stateless=true in the
bbaaef
+          options, then the action would be ip4/6.dst=
bbaaef
+          (B).
bbaaef
         

bbaaef
 
bbaaef
         

bbaaef
@@ -1862,7 +1865,10 @@ icmp6 {
bbaaef
           ip &&
bbaaef
           ip6.dst == B && inport == GW
bbaaef
           where GW is the logical router gateway port, with an
bbaaef
-          action ct_snat;.
bbaaef
+          action ct_snat;. If the NAT rule is of type
bbaaef
+          dnat_and_snat and has stateless=true in the
bbaaef
+          options, then the action would be ip4/6.dst=
bbaaef
+          (B).
bbaaef
         

bbaaef
 
bbaaef
         

bbaaef
@@ -1990,7 +1996,10 @@ icmp6 {
bbaaef
         Gateway router is configured to force SNAT any DNATed packet,
bbaaef
         the above action will be replaced by
bbaaef
         flags.force_snat_for_dnat = 1; flags.loopback = 1;
bbaaef
-        ct_dnat(B);.
bbaaef
+        ct_dnat(B);. If the NAT rule is of type
bbaaef
+        dnat_and_snat and has stateless=true in the
bbaaef
+        options, then the action would be ip4/6.dst=
bbaaef
+        (B).
bbaaef
       
bbaaef
 
bbaaef
       
  • bbaaef
    @@ -2024,6 +2033,9 @@ icmp6 {
    bbaaef
               where GW is the logical router gateway port, with an
    bbaaef
               action ct_dnat(B);.  The match will
    bbaaef
               include ip6.dst == B in the IPv6 case.
    bbaaef
    +          If the NAT rule is of type dnat_and_snat and has
    bbaaef
    +          stateless=true in the options, then the action
    bbaaef
    +          would be ip4/6.dst=(B).
    bbaaef
             

    bbaaef
     
    bbaaef
             

    bbaaef
    @@ -2698,7 +2710,10 @@ nd_ns {
    bbaaef
               matches ip && ip4.src == B
    bbaaef
               && outport == GW, where GW
    bbaaef
               is the logical router gateway port, with an action
    bbaaef
    -          ct_dnat;.
    bbaaef
    +          ct_dnat;. If the NAT rule is of type
    bbaaef
    +          dnat_and_snat and has stateless=true in the
    bbaaef
    +          options, then the action would be ip4/6.src=
    bbaaef
    +          (B).
    bbaaef
             

    bbaaef
     
    bbaaef
             

    bbaaef
    @@ -2765,7 +2780,10 @@ nd_ns {
    bbaaef
               ip && ip4.src == A with an action
    bbaaef
               ct_snat(B);.  The priority of the flow
    bbaaef
               is calculated based on the mask of A, with matches
    bbaaef
    -          having larger masks getting higher priorities.
    bbaaef
    +          having larger masks getting higher priorities. If the NAT rule is
    bbaaef
    +          of type dnat_and_snat and has stateless=true in the
    bbaaef
    +          options, then the action would be ip4/6.src=
    bbaaef
    +          (B).
    bbaaef
             

    bbaaef
             

    bbaaef
               A priority-0 logical flow with match 1 has actions
    bbaaef
    @@ -2788,7 +2806,10 @@ nd_ns {
    bbaaef
               logical router gateway port, with an action
    bbaaef
               ct_snat(B);.  The priority of the flow
    bbaaef
               is calculated based on the mask of A, with matches
    bbaaef
    -          having larger masks getting higher priorities.
    bbaaef
    +          having larger masks getting higher priorities. If the NAT rule
    bbaaef
    +          is of type dnat_and_snat and has stateless=true
    bbaaef
    +          in the options, then the action would be ip4/6.src=
    bbaaef
    +          (B).
    bbaaef
             

    bbaaef
     
    bbaaef
             

    bbaaef
    diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
    bbaaef
    index 27e90fcb2..8938e458f 100644
    bbaaef
    --- a/ovn/northd/ovn-northd.c
    bbaaef
    +++ b/ovn/northd/ovn-northd.c
    bbaaef
    @@ -6779,6 +6779,18 @@ copy_ra_to_sb(struct ovn_port *op, const char *address_mode)
    bbaaef
         smap_destroy(&options);
    bbaaef
     }
    bbaaef
     
    bbaaef
    +static inline bool
    bbaaef
    +lrouter_nat_is_stateless(const struct nbrec_nat *nat)
    bbaaef
    +{
    bbaaef
    +    const char *stateless = smap_get(&nat->options, "stateless");
    bbaaef
    +
    bbaaef
    +    if (stateless && !strcmp(stateless, "true")) {
    bbaaef
    +        return true;
    bbaaef
    +    }
    bbaaef
    +
    bbaaef
    +    return false;
    bbaaef
    +}
    bbaaef
    +
    bbaaef
     static void
    bbaaef
     build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
                         struct hmap *lflows, struct shash *meter_groups)
    bbaaef
    @@ -7636,6 +7648,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
                 ovs_be32 ip, mask;
    bbaaef
                 struct in6_addr ipv6, mask_v6, v6_exact = IN6ADDR_EXACT_INIT;
    bbaaef
                 bool is_v6 = false;
    bbaaef
    +            bool stateless = lrouter_nat_is_stateless(nat);
    bbaaef
     
    bbaaef
                 char *error = ip_parse_masked(nat->external_ip, &ip, &mask);
    bbaaef
                 if (error || mask != OVS_BE32_MAX) {
    bbaaef
    @@ -7719,16 +7732,25 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
                     if (!od->l3dgw_port) {
    bbaaef
                         /* Gateway router. */
    bbaaef
                         ds_clear(&match);
    bbaaef
    +                    ds_clear(&actions);
    bbaaef
                         ds_put_format(&match, "ip && ip%s.dst == %s",
    bbaaef
                                       is_v6 ? "6" : "4",
    bbaaef
                                       nat->external_ip);
    bbaaef
    +                    if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
    bbaaef
    +                       ds_put_format(&actions, "ip%s.dst=%s; next;",
    bbaaef
    +                                     is_v6 ? "6" : "4", nat->logical_ip);
    bbaaef
    +                    } else {
    bbaaef
    +                       ds_put_cstr(&actions, "ct_snat;");
    bbaaef
    +                    }
    bbaaef
    +
    bbaaef
                         ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 90,
    bbaaef
    -                                  ds_cstr(&match), "ct_snat;");
    bbaaef
    +                                  ds_cstr(&match), ds_cstr(&actions));
    bbaaef
                     } else {
    bbaaef
                         /* Distributed router. */
    bbaaef
     
    bbaaef
                         /* Traffic received on l3dgw_port is subject to NAT. */
    bbaaef
                         ds_clear(&match);
    bbaaef
    +                    ds_clear(&actions);
    bbaaef
                         ds_put_format(&match, "ip && ip%s.dst == %s"
    bbaaef
                                               " && inport == %s",
    bbaaef
                                       is_v6 ? "6" : "4",
    bbaaef
    @@ -7740,8 +7762,16 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
                             ds_put_format(&match, " && is_chassis_resident(%s)",
    bbaaef
                                           od->l3redirect_port->json_key);
    bbaaef
                         }
    bbaaef
    +
    bbaaef
    +                    if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
    bbaaef
    +                        ds_put_format(&actions, "ip%s.dst=%s; next;",
    bbaaef
    +                                      is_v6 ? "6" : "4", nat->logical_ip);
    bbaaef
    +                    } else {
    bbaaef
    +                        ds_put_cstr(&actions, "ct_snat;");
    bbaaef
    +                    }
    bbaaef
    +
    bbaaef
                         ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100,
    bbaaef
    -                                  ds_cstr(&match), "ct_snat;");
    bbaaef
    +                                  ds_cstr(&match), ds_cstr(&actions));
    bbaaef
     
    bbaaef
                         /* Traffic received on other router ports must be
    bbaaef
                          * redirected to the central instance of the l3dgw_port
    bbaaef
    @@ -7778,8 +7808,16 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
                             ds_put_format(&actions,
    bbaaef
                                           "flags.force_snat_for_dnat = 1; ");
    bbaaef
                         }
    bbaaef
    -                    ds_put_format(&actions, "flags.loopback = 1; ct_dnat(%s);",
    bbaaef
    -                                  nat->logical_ip);
    bbaaef
    +
    bbaaef
    +                    if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
    bbaaef
    +                        ds_put_format(&actions, "flags.loopback = 1; "
    bbaaef
    +                                      "ip%s.dst=%s; next;",
    bbaaef
    +                                      is_v6 ? "6" : "4", nat->logical_ip);
    bbaaef
    +                    } else {
    bbaaef
    +                        ds_put_format(&actions, "flags.loopback = 1; "
    bbaaef
    +                                      "ct_dnat(%s);", nat->logical_ip);
    bbaaef
    +                    }
    bbaaef
    +
    bbaaef
                         ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100,
    bbaaef
                                       ds_cstr(&match), ds_cstr(&actions));
    bbaaef
                     } else {
    bbaaef
    @@ -7799,8 +7837,15 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
                                           od->l3redirect_port->json_key);
    bbaaef
                         }
    bbaaef
                         ds_clear(&actions);
    bbaaef
    -                    ds_put_format(&actions, "ct_dnat(%s);",
    bbaaef
    -                                  nat->logical_ip);
    bbaaef
    +
    bbaaef
    +                    if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
    bbaaef
    +                        ds_put_format(&actions, "ip%s.dst=%s; next;",
    bbaaef
    +                                      is_v6 ? "6" : "4", nat->logical_ip);
    bbaaef
    +                    } else {
    bbaaef
    +                        ds_put_format(&actions, "ct_dnat(%s);",
    bbaaef
    +                                      nat->logical_ip);
    bbaaef
    +                    }
    bbaaef
    +
    bbaaef
                         ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100,
    bbaaef
                                       ds_cstr(&match), ds_cstr(&actions));
    bbaaef
     
    bbaaef
    @@ -7844,7 +7889,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
                         ds_put_format(&actions, "eth.src = "ETH_ADDR_FMT"; ",
    bbaaef
                                       ETH_ADDR_ARGS(mac));
    bbaaef
                     }
    bbaaef
    -                ds_put_format(&actions, "ct_dnat;");
    bbaaef
    +
    bbaaef
    +                if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
    bbaaef
    +                    ds_put_format(&actions, "ip%s.src=%s; next;",
    bbaaef
    +                                  is_v6 ? "6" : "4", nat->external_ip);
    bbaaef
    +                } else {
    bbaaef
    +                    ds_put_format(&actions, "ct_dnat;");
    bbaaef
    +                }
    bbaaef
    +
    bbaaef
                     ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 100,
    bbaaef
                                   ds_cstr(&match), ds_cstr(&actions));
    bbaaef
                 }
    bbaaef
    @@ -7861,7 +7913,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
                                       is_v6 ? "6" : "4",
    bbaaef
                                       nat->logical_ip);
    bbaaef
                         ds_clear(&actions);
    bbaaef
    -                    ds_put_format(&actions, "ct_snat(%s);", nat->external_ip);
    bbaaef
    +
    bbaaef
    +                    if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
    bbaaef
    +                        ds_put_format(&actions, "ip%s.src=%s; next;",
    bbaaef
    +                                      is_v6 ? "6" : "4", nat->external_ip);
    bbaaef
    +                    } else {
    bbaaef
    +                        ds_put_format(&actions, "ct_snat(%s);",
    bbaaef
    +                                      nat->external_ip);
    bbaaef
    +                    }
    bbaaef
     
    bbaaef
                         /* The priority here is calculated such that the
    bbaaef
                          * nat->logical_ip with the longest mask gets a higher
    bbaaef
    @@ -7891,7 +7950,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
    bbaaef
                             ds_put_format(&actions, "eth.src = "ETH_ADDR_FMT"; ",
    bbaaef
                                           ETH_ADDR_ARGS(mac));
    bbaaef
                         }
    bbaaef
    -                    ds_put_format(&actions, "ct_snat(%s);", nat->external_ip);
    bbaaef
    +
    bbaaef
    +                    if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
    bbaaef
    +                        ds_put_format(&actions, "ip%s.src=%s; next;",
    bbaaef
    +                                      is_v6 ? "6" : "4", nat->external_ip);
    bbaaef
    +                    } else {
    bbaaef
    +                        ds_put_format(&actions, "ct_snat(%s);",
    bbaaef
    +                                      nat->external_ip);
    bbaaef
    +                    }
    bbaaef
     
    bbaaef
                         /* The priority here is calculated such that the
    bbaaef
                          * nat->logical_ip with the longest mask gets a higher
    bbaaef
    diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
    bbaaef
    index 42033d589..989ed4f47 100644
    bbaaef
    --- a/tests/ovn-northd.at
    bbaaef
    +++ b/tests/ovn-northd.at
    bbaaef
    @@ -966,3 +966,98 @@ OVS_WAIT_UNTIL([ovn-sbctl get Port_Binding ${uuid} options:redirect-type], [0],
    bbaaef
     ])
    bbaaef
     
    bbaaef
     AT_CLEANUP
    bbaaef
    +
    bbaaef
    +AT_SETUP([ovn -- check stateless dnat_and_snat rule])
    bbaaef
    +AT_SKIP_IF([test $HAVE_PYTHON = no])
    bbaaef
    +ovn_start
    bbaaef
    +
    bbaaef
    +ovn-sbctl chassis-add gw1 geneve 127.0.0.1
    bbaaef
    +
    bbaaef
    +ovn-nbctl lr-add R1
    bbaaef
    +ovn-nbctl lrp-add R1 R1-S1 02:ac:10:01:00:01 172.16.1.1/24
    bbaaef
    +
    bbaaef
    +ovn-nbctl ls-add S1
    bbaaef
    +ovn-nbctl lsp-add S1 S1-R1
    bbaaef
    +ovn-nbctl lsp-set-type S1-R1 router
    bbaaef
    +ovn-nbctl lsp-set-addresses S1-R1 router
    bbaaef
    +ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1
    bbaaef
    +
    bbaaef
    +ovn-nbctl lrp-set-gateway-chassis R1-S1 gw1
    bbaaef
    +
    bbaaef
    +uuid=`ovn-sbctl --columns=_uuid --bare find Port_Binding logical_port=cr-R1-S1`
    bbaaef
    +echo "CR-LRP UUID is: " $uuid
    bbaaef
    +
    bbaaef
    +# IPV4
    bbaaef
    +ovn-nbctl lr-nat-add R1 dnat_and_snat  172.16.1.1 50.0.0.11
    bbaaef
    +
    bbaaef
    +OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
    bbaaef
    +wc -l`])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [2
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_dnat | wc -l], [0], [2
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows R1 | grep ip4.dst=| wc -l], [0], [0
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows R1 | grep ip4.src=| wc -l], [0], [0
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +ovn-nbctl lr-nat-del R1 dnat_and_snat  172.16.1.1
    bbaaef
    +
    bbaaef
    +ovn-nbctl --stateless lr-nat-add R1 dnat_and_snat  172.16.1.1 50.0.0.11
    bbaaef
    +OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
    bbaaef
    +wc -l`])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [0
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_dnat | wc -l], [0], [0
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows R1 | grep ip4.dst=| wc -l], [0], [2
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows R1 | grep ip4.src=| wc -l], [0], [2
    bbaaef
    +])
    bbaaef
    +ovn-nbctl lr-nat-del R1 dnat_and_snat  172.16.1.1
    bbaaef
    +
    bbaaef
    +# IPV6
    bbaaef
    +ovn-nbctl lr-nat-add R1 dnat_and_snat fd01::1 fd11::2
    bbaaef
    +
    bbaaef
    +OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
    bbaaef
    +wc -l`])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [2
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_dnat | wc -l], [0], [2
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows R1 | grep ip6.dst=| wc -l], [0], [0
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows R1 | grep ip6.src=| wc -l], [0], [0
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +ovn-nbctl lr-nat-del R1 dnat_and_snat  fd01::1
    bbaaef
    +ovn-nbctl --stateless lr-nat-add R1 dnat_and_snat fd01::1 fd11::2
    bbaaef
    +
    bbaaef
    +OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
    bbaaef
    +wc -l`])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [0
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_dnat | wc -l], [0], [0
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows R1 | grep ip6.dst=| wc -l], [0], [2
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows R1 | grep ip6.src=| wc -l], [0], [2
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +AT_CLEANUP
    bbaaef
    diff --git a/tests/ovn.at b/tests/ovn.at
    bbaaef
    index 951a55226..89bd3f174 100644
    bbaaef
    --- a/tests/ovn.at
    bbaaef
    +++ b/tests/ovn.at
    bbaaef
    @@ -16610,3 +16610,314 @@ as hv4 ovs-appctl fdb/show br-phys
    bbaaef
     OVN_CLEANUP([hv1],[hv2],[hv3],[hv4])
    bbaaef
     
    bbaaef
     AT_CLEANUP
    bbaaef
    +
    bbaaef
    +AT_SETUP([ovn -- Stateless Floating IP])
    bbaaef
    +ovn_start
    bbaaef
    +
    bbaaef
    +# In this test cases we create 3 switches, all connected to same
    bbaaef
    +# physical network (through br-phys on each HV). LS1 and LS2 have
    bbaaef
    +# 1 VIF each. Each HV has 1 VIF port. The first digit
    bbaaef
    +# of VIF port name indicates the hypervisor it is bound to, e.g.
    bbaaef
    +# lp23 means VIF 3 on hv2.
    bbaaef
    +#
    bbaaef
    +# All the switches are connected to a logical router "router".
    bbaaef
    +#
    bbaaef
    +# There is an external logical switch, ls-north.
    bbaaef
    +# This test validates the stateless floating ip implementation.
    bbaaef
    +#
    bbaaef
    +# Each switch's VLAN tag and their logical switch ports are:
    bbaaef
    +#   - ls1:
    bbaaef
    +#       - tagged with VLAN 101
    bbaaef
    +#       - ports: lp11
    bbaaef
    +#   - ls2:
    bbaaef
    +#       - tagged with VLAN 201
    bbaaef
    +#       - ports: lp22
    bbaaef
    +#   - ls-north:
    bbaaef
    +#       - tagged with VLAN 1000
    bbaaef
    +# Note: a localnet port is created for each switch to connect to
    bbaaef
    +# physical network.
    bbaaef
    +
    bbaaef
    +for i in 1 2; do
    bbaaef
    +    ls_name=ls$i
    bbaaef
    +    ovn-nbctl ls-add $ls_name
    bbaaef
    +    ln_port_name=ln$i
    bbaaef
    +    if test $i -eq 1; then
    bbaaef
    +        ovn-nbctl lsp-add $ls_name $ln_port_name "" 101
    bbaaef
    +    elif test $i -eq 2; then
    bbaaef
    +        ovn-nbctl lsp-add $ls_name $ln_port_name "" 201
    bbaaef
    +    fi
    bbaaef
    +    ovn-nbctl lsp-set-addresses $ln_port_name unknown
    bbaaef
    +    ovn-nbctl lsp-set-type $ln_port_name localnet
    bbaaef
    +    ovn-nbctl lsp-set-options $ln_port_name network_name=phys
    bbaaef
    +done
    bbaaef
    +
    bbaaef
    +# lsp_to_ls LSP
    bbaaef
    +#
    bbaaef
    +# Prints the name of the logical switch that contains LSP.
    bbaaef
    +lsp_to_ls () {
    bbaaef
    +    case $1 in dnl (
    bbaaef
    +        lp?[[11]]) echo ls1 ;; dnl (
    bbaaef
    +        lp?[[12]]) echo ls2 ;; dnl (
    bbaaef
    +        *) AT_FAIL_IF([:]) ;;
    bbaaef
    +    esac
    bbaaef
    +}
    bbaaef
    +
    bbaaef
    +vif_to_hv () {
    bbaaef
    +    case $1 in dnl (
    bbaaef
    +        vif[[1]]?) echo hv1 ;; dnl (
    bbaaef
    +        vif[[2]]?) echo hv2 ;; dnl (
    bbaaef
    +        vif?[[north]]?) echo hv4 ;; dnl (
    bbaaef
    +        *) AT_FAIL_IF([:]) ;;
    bbaaef
    +    esac
    bbaaef
    +}
    bbaaef
    +
    bbaaef
    +ip_to_hex() {
    bbaaef
    +       printf "%02x%02x%02x%02x" "$@"
    bbaaef
    +}
    bbaaef
    +
    bbaaef
    +net_add n1
    bbaaef
    +for i in 1 2; do
    bbaaef
    +    sim_add hv$i
    bbaaef
    +    as hv$i
    bbaaef
    +    ovs-vsctl add-br br-phys
    bbaaef
    +    ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
    bbaaef
    +    ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:$i$i"
    bbaaef
    +    ovn_attach n1 br-phys 192.168.0.$i
    bbaaef
    +
    bbaaef
    +    ovs-vsctl add-port br-int vif$i$i -- \
    bbaaef
    +        set Interface vif$i$i external-ids:iface-id=lp$i$i \
    bbaaef
    +                              options:tx_pcap=hv$i/vif$i$i-tx.pcap \
    bbaaef
    +                              options:rxq_pcap=hv$i/vif$i$i-rx.pcap \
    bbaaef
    +                              ofport-request=$i$i
    bbaaef
    +
    bbaaef
    +    lsp_name=lp$i$i
    bbaaef
    +    ls_name=$(lsp_to_ls $lsp_name)
    bbaaef
    +
    bbaaef
    +    ovn-nbctl lsp-add $ls_name $lsp_name
    bbaaef
    +    ovn-nbctl lsp-set-addresses $lsp_name "f0:00:00:00:00:$i$i 192.168.$i.$i"
    bbaaef
    +    ovn-nbctl lsp-set-port-security $lsp_name f0:00:00:00:00:$i$i
    bbaaef
    +
    bbaaef
    +    OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up $lsp_name` = xup])
    bbaaef
    +
    bbaaef
    +done
    bbaaef
    +
    bbaaef
    +ovn-nbctl ls-add ls-underlay
    bbaaef
    +ovn-nbctl lsp-add ls-underlay ln3 "" 1000
    bbaaef
    +ovn-nbctl lsp-set-addresses ln3 unknown
    bbaaef
    +ovn-nbctl lsp-set-type ln3 localnet
    bbaaef
    +ovn-nbctl lsp-set-options ln3 network_name=phys
    bbaaef
    +
    bbaaef
    +ovn-nbctl ls-add ls-north
    bbaaef
    +ovn-nbctl lsp-add ls-north ln4 "" 1000
    bbaaef
    +ovn-nbctl lsp-set-addresses ln4 unknown
    bbaaef
    +ovn-nbctl lsp-set-type ln4 localnet
    bbaaef
    +ovn-nbctl lsp-set-options ln4 network_name=phys
    bbaaef
    +
    bbaaef
    +# Add a VM on ls-north
    bbaaef
    +ovn-nbctl lsp-add ls-north lp-north
    bbaaef
    +ovn-nbctl lsp-set-addresses lp-north "f0:f0:00:00:00:11 172.31.0.10"
    bbaaef
    +ovn-nbctl lsp-set-port-security lp-north f0:f0:00:00:00:11
    bbaaef
    +
    bbaaef
    +# Add 3rd hypervisor
    bbaaef
    +sim_add hv3
    bbaaef
    +as hv3 ovs-vsctl add-br br-phys
    bbaaef
    +as hv3 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
    bbaaef
    +as hv3 ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:33"
    bbaaef
    +as hv3 ovn_attach n1 br-phys 192.168.0.3
    bbaaef
    +
    bbaaef
    +# Add 4th hypervisor
    bbaaef
    +sim_add hv4
    bbaaef
    +as hv4 ovs-vsctl add-br br-phys
    bbaaef
    +as hv4 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
    bbaaef
    +as hv4 ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:44"
    bbaaef
    +as hv4 ovn_attach n1 br-phys 192.168.0.4
    bbaaef
    +
    bbaaef
    +as hv4 ovs-vsctl add-port br-int vif-north -- \
    bbaaef
    +        set Interface vif-north external-ids:iface-id=lp-north \
    bbaaef
    +                              options:tx_pcap=hv4/vif-north-tx.pcap \
    bbaaef
    +                              options:rxq_pcap=hv4/vif-north-rx.pcap \
    bbaaef
    +                              ofport-request=44
    bbaaef
    +
    bbaaef
    +ovn-nbctl lr-add router
    bbaaef
    +ovn-nbctl lrp-add router router-to-ls1 00:00:01:01:02:03 192.168.1.3/24
    bbaaef
    +ovn-nbctl lrp-add router router-to-ls2 00:00:01:01:02:05 192.168.2.3/24
    bbaaef
    +ovn-nbctl lrp-add router router-to-underlay 00:00:01:01:02:07 172.31.0.1/24
    bbaaef
    +
    bbaaef
    +ovn-nbctl lsp-add ls1 ls1-to-router -- set Logical_Switch_Port ls1-to-router type=router \
    bbaaef
    +          options:router-port=router-to-ls1 -- lsp-set-addresses ls1-to-router router
    bbaaef
    +ovn-nbctl lsp-add ls2 ls2-to-router -- set Logical_Switch_Port ls2-to-router type=router \
    bbaaef
    +          options:router-port=router-to-ls2 -- lsp-set-addresses ls2-to-router router
    bbaaef
    +ovn-nbctl lsp-add ls-underlay underlay-to-router -- set Logical_Switch_Port \
    bbaaef
    +                              underlay-to-router type=router \
    bbaaef
    +                              options:router-port=router-to-underlay \
    bbaaef
    +                              -- lsp-set-addresses underlay-to-router router
    bbaaef
    +
    bbaaef
    +ovn-nbctl lrp-set-gateway-chassis router-to-underlay hv3
    bbaaef
    +ovn-nbctl --stateless lr-nat-add router dnat_and_snat 172.31.0.100 192.168.1.1
    bbaaef
    +ovn-nbctl lrp-set-redirect-type router-to-underlay bridged
    bbaaef
    +
    bbaaef
    +ovn-nbctl --wait=sb sync
    bbaaef
    +
    bbaaef
    +
    bbaaef
    +OVN_POPULATE_ARP
    bbaaef
    +
    bbaaef
    +# lsp_to_ls LSP
    bbaaef
    +#
    bbaaef
    +# Prints the name of the logical switch that contains LSP.
    bbaaef
    +lsp_to_ls () {
    bbaaef
    +    case $1 in dnl (
    bbaaef
    +        lp?[[11]]) echo ls1 ;; dnl (
    bbaaef
    +        lp?[[12]]) echo ls2 ;; dnl (
    bbaaef
    +        *) AT_FAIL_IF([:]) ;;
    bbaaef
    +    esac
    bbaaef
    +}
    bbaaef
    +
    bbaaef
    +vif_to_ls () {
    bbaaef
    +    case $1 in dnl (
    bbaaef
    +        vif?[[11]]) echo ls1 ;; dnl (
    bbaaef
    +        vif?[[12]]) echo ls2 ;; dnl (
    bbaaef
    +        vif-north) echo ls-north ;; dnl (
    bbaaef
    +        *) AT_FAIL_IF([:]) ;;
    bbaaef
    +    esac
    bbaaef
    +}
    bbaaef
    +
    bbaaef
    +hv_to_num () {
    bbaaef
    +    case $1 in dnl (
    bbaaef
    +        hv1) echo 1 ;; dnl (
    bbaaef
    +        hv2) echo 2 ;; dnl (
    bbaaef
    +        hv3) echo 3 ;; dnl (
    bbaaef
    +        hv4) echo 4 ;; dnl (
    bbaaef
    +        *) AT_FAIL_IF([:]) ;;
    bbaaef
    +    esac
    bbaaef
    +}
    bbaaef
    +
    bbaaef
    +vif_to_num () {
    bbaaef
    +    case $1 in dnl (
    bbaaef
    +        vif22) echo 22 ;; dnl (
    bbaaef
    +        vif21) echo 21 ;; dnl (
    bbaaef
    +        vif11) echo 11 ;; dnl (
    bbaaef
    +        *) AT_FAIL_IF([:]) ;;
    bbaaef
    +    esac
    bbaaef
    +}
    bbaaef
    +
    bbaaef
    +vif_to_hv () {
    bbaaef
    +    case $1 in dnl (
    bbaaef
    +        vif[[1]]?) echo hv1 ;; dnl (
    bbaaef
    +        vif[[2]]?) echo hv2 ;; dnl (
    bbaaef
    +        vif-north) echo hv4 ;; dnl (
    bbaaef
    +        *) AT_FAIL_IF([:]) ;;
    bbaaef
    +    esac
    bbaaef
    +}
    bbaaef
    +
    bbaaef
    +vif_to_lrp () {
    bbaaef
    +    echo router-to-`vif_to_ls $1`
    bbaaef
    +}
    bbaaef
    +
    bbaaef
    +ip_to_hex() {
    bbaaef
    +       printf "%02x%02x%02x%02x" "$@"
    bbaaef
    +}
    bbaaef
    +
    bbaaef
    +
    bbaaef
    +test_ip() {
    bbaaef
    +        # This packet has bad checksums but logical L3 routing doesn't check.
    bbaaef
    +        local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 outport=$6
    bbaaef
    +        local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
    bbaaef
    +        shift; shift; shift; shift; shift
    bbaaef
    +        hv=`vif_to_hv $inport`
    bbaaef
    +        as $hv ovs-appctl netdev-dummy/receive $inport $packet
    bbaaef
    +        in_ls=`vif_to_ls $inport`
    bbaaef
    +        for outport; do
    bbaaef
    +            out_ls=`vif_to_ls $outport`
    bbaaef
    +            if test $in_ls = $out_ls; then
    bbaaef
    +                # Ports on the same logical switch receive exactly the same packet.
    bbaaef
    +                echo $packet
    bbaaef
    +            else
    bbaaef
    +                # Routing decrements TTL and updates source and dest MAC
    bbaaef
    +                # (and checksum).
    bbaaef
    +                out_lrp=`vif_to_lrp $outport`
    bbaaef
    +                # For North-South, packet will come via gateway chassis, i.e hv3
    bbaaef
    +                if test $inport = vif-north; then
    bbaaef
    +                    echo f0000000001100000101020308004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 >> $outport.expected
    bbaaef
    +                fi
    bbaaef
    +                if test $outport = vif-north; then
    bbaaef
    +                    echo f0f00000001100000101020708004500001c000000003e111726ac1f0064${dst_ip}0035111100080000 >> $outport.expected
    bbaaef
    +                fi
    bbaaef
    +            fi >> $outport.expected
    bbaaef
    +        done
    bbaaef
    +}
    bbaaef
    +
    bbaaef
    +# Dump a bunch of info helpful for debugging if there's a failure.
    bbaaef
    +
    bbaaef
    +echo "------ OVN dump ------"
    bbaaef
    +ovn-nbctl show
    bbaaef
    +ovn-nbctl lr-nat-list router
    bbaaef
    +ovn-sbctl show
    bbaaef
    +ovn-sbctl list port_binding
    bbaaef
    +ovn-sbctl list mac_binding
    bbaaef
    +ovn-sbctl dump-flows
    bbaaef
    +
    bbaaef
    +echo "------ hv1 dump ------"
    bbaaef
    +as hv1 ovs-vsctl show
    bbaaef
    +as hv1 ovs-vsctl list Open_Vswitch
    bbaaef
    +
    bbaaef
    +echo "------ hv2 dump ------"
    bbaaef
    +as hv2 ovs-vsctl show
    bbaaef
    +as hv2 ovs-vsctl list Open_Vswitch
    bbaaef
    +
    bbaaef
    +echo "------ hv3 dump ------"
    bbaaef
    +as hv3 ovs-vsctl show
    bbaaef
    +as hv3 ovs-vsctl list Open_Vswitch
    bbaaef
    +
    bbaaef
    +echo "------ hv4 dump ------"
    bbaaef
    +as hv4 ovs-vsctl show
    bbaaef
    +as hv4 ovs-vsctl list Open_Vswitch
    bbaaef
    +
    bbaaef
    +echo "Send traffic South to Nouth"
    bbaaef
    +sip=`ip_to_hex 192 168 1 1`
    bbaaef
    +dip=`ip_to_hex 172 31 0 10`
    bbaaef
    +test_ip vif11 f00000000011 000001010203 $sip $dip vif-north
    bbaaef
    +
    bbaaef
    +# Confirm that South to North traffic works fine.
    bbaaef
    +OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv4/vif-north-tx.pcap], [vif-north.expected])
    bbaaef
    +
    bbaaef
    +# Confirm that NATing happened without connection tracker
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows router | grep ct_snat | wc -l], [0], [0
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows router | grep ct_dnat | wc -l], [0], [0
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows router | grep ip4.dst=| wc -l], [0], [2
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +AT_CHECK([ovn-sbctl dump-flows router | grep ip4.src=| wc -l], [0], [2
    bbaaef
    +])
    bbaaef
    +
    bbaaef
    +echo "----------- Post Traffic hv1 dump -----------"
    bbaaef
    +as hv1 ovs-ofctl dump-flows br-int
    bbaaef
    +as hv1 ovs-ofctl show br-phys
    bbaaef
    +as hv1 ovs-appctl fdb/show br-phys
    bbaaef
    +as hv1 ovs-dpctl dump-flows
    bbaaef
    +
    bbaaef
    +echo "----------- Post Traffic hv2 dump -----------"
    bbaaef
    +as hv2 ovs-ofctl dump-flows br-int
    bbaaef
    +as hv2 ovs-ofctl show br-phys
    bbaaef
    +as hv2 ovs-appctl fdb/show br-phys
    bbaaef
    +
    bbaaef
    +echo "----------- Post Traffic hv3 dump -----------"
    bbaaef
    +as hv3 ovs-ofctl dump-flows br-int
    bbaaef
    +as hv3 ovs-ofctl show br-phys
    bbaaef
    +as hv3 ovs-appctl dpctl/dump-conntrack
    bbaaef
    +as hv3 ovs-appctl fdb/show br-phys
    bbaaef
    +as hv3 ovs-dpctl dump-flows
    bbaaef
    +as hv3 ovs-ofctl dump-flows br-int
    bbaaef
    +
    bbaaef
    +echo "----------- Post Traffic hv4 dump -----------"
    bbaaef
    +as hv4 ovs-ofctl dump-flows br-int
    bbaaef
    +as hv4 ovs-ofctl show br-phys
    bbaaef
    +as hv4 ovs-appctl fdb/show br-phys
    bbaaef
    +
    bbaaef
    +OVN_CLEANUP([hv1],[hv2],[hv3],[hv4])
    bbaaef
    +
    bbaaef
    +AT_CLEANUP
    bbaaef
    -- 
    bbaaef
    2.23.0
    bbaaef