Previously we could only generate ARP requests from IPv4 packets
and NS requests from IPv6 packets. This was the case because we rely on
information in the packet to generate the ARP/NS requests.

However in case of ARP/NS requests originating from the Logical_Router
pipeline for nexthop lookups we overwrite the affected fields
afterwards. This overwrite is done by the userdata openflow actions.
Because of this we actually do not rely on any information of the IPv4/6
packets in these cases.

Unfortunately we can not easily determine if we are actually later
overwriting the affected fields. The approach now is to use the fields
from the IP header if we have a matching IP version and default to some
values otherwise. In case we overwrite this data afterwards we are
generally good. If we do not overwrite this data because of some bug we
will send out invalid ARP/NS requests. They will hopefully be dropped by
the rest of the network.

The alternative would have been to introduce new arp/nd_ns actions where
we guarantee this overwrite. This would not suffer from the above
limitations, but would require a coordination on upgrades between all
ovn-controllers and northd.

Signed-off-by: Felix Huettner <felix.huettner@mail.schwarz>
---
v4->v5: rebase
v4: newly added

 controller/pinctrl.c |  52 +++++++--
 lib/actions.c        |   4 +-
 northd/northd.c      |   9 +-
 tests/ovn-northd.at  |   8 +-
 tests/ovn.at         | 268 ++++++++++++++++++++++++++++++++++++++++++-
 5 files changed, 320 insertions(+), 21 deletions(-)

diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index f2e382a44..4c520bd5e 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -1574,9 +1574,11 @@ pinctrl_handle_arp(struct rconn *swconn, const struct 
flow *ip_flow,
                    const struct ofputil_packet_in *pin,
                    struct ofpbuf *userdata, const struct ofpbuf *continuation)
 {
-    /* This action only works for IP packets, and the switch should only send
-     * us IP packets this way, but check here just to be sure. */
-    if (ip_flow->dl_type != htons(ETH_TYPE_IP)) {
+    uint16_t dl_type = ntohs(ip_flow->dl_type);
+
+    /* This action only works for IPv4 or IPv6 packets, and the switch should
+     * only send us IP packets this way, but check here just to be sure. */
+    if (dl_type != ETH_TYPE_IP && dl_type != ETH_TYPE_IPV6) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
         VLOG_WARN_RL(&rl, "ARP action on non-IP packet (Ethertype %"PRIx16")",
                      ntohs(ip_flow->dl_type));
@@ -1600,9 +1602,25 @@ pinctrl_handle_arp(struct rconn *swconn, const struct 
flow *ip_flow,
     struct arp_eth_header *arp = dp_packet_l3(&packet);
     arp->ar_op = htons(ARP_OP_REQUEST);
     arp->ar_sha = ip_flow->dl_src;
-    put_16aligned_be32(&arp->ar_spa, ip_flow->nw_src);
     arp->ar_tha = eth_addr_zero;
-    put_16aligned_be32(&arp->ar_tpa, ip_flow->nw_dst);
+
+    /* We might be here without actually currently handling an IPv4 packet.
+     * This can happen in the case where we route IPv6 packets over an IPv4
+     * link.
+     * In these cases we have no destination IPv4 address from the packet that
+     * we can reuse. But we receive the actual destination IPv4 address via
+     * userdata anyway, so what we set for now is irrelevant.
+     * This is just a hope since we do not parse the userdata. If we land here
+     * for whatever reason without being an IPv4 packet and without userdata we
+     * will send out a wrong packet.
+     */
+    if (ip_flow->dl_type == htons(ETH_TYPE_IP)) {
+        put_16aligned_be32(&arp->ar_spa, ip_flow->nw_src);
+        put_16aligned_be32(&arp->ar_tpa, ip_flow->nw_dst);
+    } else {
+        put_16aligned_be32(&arp->ar_spa, 0);
+        put_16aligned_be32(&arp->ar_tpa, 0);
+    }
 
     if (ip_flow->vlans[0].tci & htons(VLAN_CFI)) {
         eth_push_vlan(&packet, htons(ETH_TYPE_VLAN_8021Q),
@@ -6741,8 +6759,11 @@ pinctrl_handle_nd_ns(struct rconn *swconn, const struct 
flow *ip_flow,
                      struct ofpbuf *userdata,
                      const struct ofpbuf *continuation)
 {
-    /* This action only works for IPv6 packets. */
-    if (get_dl_type(ip_flow) != htons(ETH_TYPE_IPV6)) {
+    uint16_t dl_type = ntohs(ip_flow->dl_type);
+
+    /* This action only works for IPv4 or IPv6 packets, and the switch should
+     * only send us IP packets this way, but check here just to be sure. */
+    if (dl_type != ETH_TYPE_IP && dl_type != ETH_TYPE_IPV6) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
         VLOG_WARN_RL(&rl, "NS action on non-IPv6 packet");
         return;
@@ -6758,8 +6779,23 @@ pinctrl_handle_nd_ns(struct rconn *swconn, const struct 
flow *ip_flow,
     dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
 
     in6_generate_lla(ip_flow->dl_src, &ipv6_src);
+
+    /* We might be here without actually currently handling an IPv6 packet.
+     * This can happen in the case where we route IPv4 packets over an IPv6
+     * link.
+     * In these cases we have no destination IPv6 address from the packet that
+     * we can reuse. But we receive the actual destination IPv6 address via
+     * userdata anyway, so what we pass to compose_nd_ns is irrelevant.
+     * This is just a hope since we do not parse the userdata. If we land here
+     * for whatever reason without being an IPv6 packet and without userdata we
+     * will send out a wrong packet.
+     */
+    struct in6_addr ipv6_dst = IN6ADDR_EXACT_INIT;
+    if (get_dl_type(ip_flow) == htons(ETH_TYPE_IPV6)) {
+        ipv6_dst = ip_flow->ipv6_dst;
+    }
     compose_nd_ns(&packet, ip_flow->dl_src, &ipv6_src,
-                  &ip_flow->ipv6_dst);
+                  &ipv6_dst);
 
     /* Reload previous packet metadata and set actions from userdata. */
     set_actions_and_enqueue_msg(swconn, &packet,
diff --git a/lib/actions.c b/lib/actions.c
index e8cc0994d..c7c83a34c 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -1718,7 +1718,7 @@ parse_nested_action(struct action_context *ctx, enum 
ovnact_type type,
 static void
 parse_ARP(struct action_context *ctx)
 {
-    parse_nested_action(ctx, OVNACT_ARP, "ip4", ctx->scope);
+    parse_nested_action(ctx, OVNACT_ARP, "ip", ctx->scope);
 }
 
 static void
@@ -1772,7 +1772,7 @@ parse_ND_NA_ROUTER(struct action_context *ctx)
 static void
 parse_ND_NS(struct action_context *ctx)
 {
-    parse_nested_action(ctx, OVNACT_ND_NS, "ip6", ctx->scope);
+    parse_nested_action(ctx, OVNACT_ND_NS, "ip", ctx->scope);
 }
 
 static void
diff --git a/northd/northd.c b/northd/northd.c
index e697973ce..970cdca6e 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -13549,7 +13549,8 @@ build_arp_request_flows_for_lrouter(
 
         ds_clear(match);
         ds_put_format(match, "eth.dst == 00:00:00:00:00:00 && "
-                      "ip6 && " REG_NEXT_HOP_IPV6 " == %s",
+                      REGBIT_NEXTHOP_IS_IPV4" == 0 && "
+                      REG_NEXT_HOP_IPV6 " == %s",
                       route->nexthop);
         struct in6_addr sn_addr;
         struct eth_addr eth_dst;
@@ -13579,7 +13580,8 @@ build_arp_request_flows_for_lrouter(
     }
 
     ovn_lflow_metered(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
-                      "eth.dst == 00:00:00:00:00:00 && ip4",
+                      "eth.dst == 00:00:00:00:00:00 && "
+                      REGBIT_NEXTHOP_IS_IPV4" == 1",
                       "arp { "
                       "eth.dst = ff:ff:ff:ff:ff:ff; "
                       "arp.spa = " REG_SRC_IPV4 "; "
@@ -13591,7 +13593,8 @@ build_arp_request_flows_for_lrouter(
                                      meter_groups),
                       lflow_ref);
     ovn_lflow_metered(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
-                      "eth.dst == 00:00:00:00:00:00 && ip6",
+                      "eth.dst == 00:00:00:00:00:00 && "
+                      REGBIT_NEXTHOP_IS_IPV4" == 0",
                       "nd_ns { "
                       "nd.target = " REG_NEXT_HOP_IPV6 "; "
                       "output; "
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 41761ba96..a6aa66a0c 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -6796,10 +6796,10 @@ AT_CHECK([grep -e "lr_in_arp_resolve" lr0flows | 
ovn_strip_lflows], [0], [dnl
 
 AT_CHECK([grep -e "lr_in_arp_request" lr0flows | ovn_strip_lflows], [0], [dnl
   table=??(lr_in_arp_request  ), priority=0    , match=(1), action=(output;)
-  table=??(lr_in_arp_request  ), priority=100  , match=(eth.dst == 
00:00:00:00:00:00 && ip4), action=(arp { eth.dst = ff:ff:ff:ff:ff:ff; arp.spa = 
reg1; arp.tpa = reg0; arp.op = 1; output; }; output;)
-  table=??(lr_in_arp_request  ), priority=100  , match=(eth.dst == 
00:00:00:00:00:00 && ip6), action=(nd_ns { nd.target = xxreg0; output; }; 
output;)
-  table=??(lr_in_arp_request  ), priority=200  , match=(eth.dst == 
00:00:00:00:00:00 && ip6 && xxreg0 == 2001:db8::10), action=(nd_ns { eth.dst = 
33:33:ff:00:00:10; ip6.dst = ff02::1:ff00:10; nd.target = 2001:db8::10; output; 
}; output;)
-  table=??(lr_in_arp_request  ), priority=200  , match=(eth.dst == 
00:00:00:00:00:00 && ip6 && xxreg0 == 2001:db8::20), action=(nd_ns { eth.dst = 
33:33:ff:00:00:20; ip6.dst = ff02::1:ff00:20; nd.target = 2001:db8::20; output; 
}; output;)
+  table=??(lr_in_arp_request  ), priority=100  , match=(eth.dst == 
00:00:00:00:00:00 && reg9[[9]] == 0), action=(nd_ns { nd.target = xxreg0; 
output; }; output;)
+  table=??(lr_in_arp_request  ), priority=100  , match=(eth.dst == 
00:00:00:00:00:00 && reg9[[9]] == 1), action=(arp { eth.dst = 
ff:ff:ff:ff:ff:ff; arp.spa = reg1; arp.tpa = reg0; arp.op = 1; output; }; 
output;)
+  table=??(lr_in_arp_request  ), priority=200  , match=(eth.dst == 
00:00:00:00:00:00 && reg9[[9]] == 0 && xxreg0 == 2001:db8::10), action=(nd_ns { 
eth.dst = 33:33:ff:00:00:10; ip6.dst = ff02::1:ff00:10; nd.target = 
2001:db8::10; output; }; output;)
+  table=??(lr_in_arp_request  ), priority=200  , match=(eth.dst == 
00:00:00:00:00:00 && reg9[[9]] == 0 && xxreg0 == 2001:db8::20), action=(nd_ns { 
eth.dst = 33:33:ff:00:00:20; ip6.dst = ff02::1:ff00:20; nd.target = 
2001:db8::20; output; }; output;)
 ])
 
 AT_CLEANUP
diff --git a/tests/ovn.at b/tests/ovn.at
index 1f1a7963d..6894947d1 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -1541,11 +1541,11 @@ clone { ip4.dst = 255.255.255.255; output; }; next;
 # arp
 arp { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output;
     encodes as 
controller(userdata=00.00.00.00.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.OFTABLE_SAVE_INPORT_HEX.00.00.00,pause),resubmit(,OFTABLE_SAVE_INPORT)
-    has prereqs ip4
+    has prereqs ip
 arp { };
     formats as arp { drop; };
     encodes as controller(userdata=00.00.00.00.00.00.00.00,pause)
-    has prereqs ip4
+    has prereqs ip
 
 # get_arp
 get_arp(outport, ip4.dst);
@@ -1709,12 +1709,12 @@ reg9[[8]] = dhcp_relay_resp_chk(192.168.1, 172.16.1.1);
 # nd_ns
 nd_ns { nd.target = xxreg0; output; };
     encodes as 
controller(userdata=00.00.00.09.00.00.00.00.00.1c.00.18.00.80.00.00.00.00.00.00.00.01.de.10.80.00.3e.10.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.OFTABLE_SAVE_INPORT_HEX.00.00.00,pause)
-    has prereqs ip6
+    has prereqs ip
 
 nd_ns { };
     formats as nd_ns { drop; };
     encodes as controller(userdata=00.00.00.09.00.00.00.00,pause)
-    has prereqs ip6
+    has prereqs ip
 
 # nd_na
 nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = 
inport; inport = ""; /* Allow sending out inport. */ output; };
@@ -38706,6 +38706,266 @@ OVN_CLEANUP([hv1],[hv2])
 AT_CLEANUP
 ])
 
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([2 HVs, 2 LS, 1 lport/LS, LRs connected via LS, IPv4 over IPv6, 
dynamic])
+AT_SKIP_IF([test $HAVE_SCAPY = no])
+ovn_start
+
+# Logical network:
+# Two LRs - R1 and R2 that are connected to ls-transfer in 2001:db8::/64
+# network. R1 has a switchs ls1 (192.168.1.0/24) connected to it.
+# R2 has ls2 (172.16.1.0/24) connected to it.
+
+ls1_lp1_mac="f0:00:00:01:02:03"
+rp_ls1_mac="00:00:00:01:02:03"
+rp_ls2_mac="00:00:00:01:02:04"
+ls2_lp1_mac="f0:00:00:01:02:04"
+
+ls1_lp1_ip="192.168.1.2"
+ls2_lp1_ip="172.16.1.2"
+
+check ovn-nbctl lr-add R1
+check ovn-nbctl lr-add R2
+
+check ovn-nbctl ls-add ls1
+check ovn-nbctl ls-add ls2
+check ovn-nbctl ls-add ls-transfer
+
+# Connect ls1 to R1
+check ovn-nbctl lrp-add R1 ls1 $rp_ls1_mac 192.168.1.1/24
+check ovn-nbctl set Logical_Router R1 options:dynamic_neigh_routers=true
+
+check ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 
type=router \
+  options:router-port=ls1 addresses=\"$rp_ls1_mac\"
+
+# Connect ls2 to R2
+check ovn-nbctl lrp-add R2 ls2 $rp_ls2_mac 172.16.1.1/24
+check ovn-nbctl set Logical_Router R2 options:dynamic_neigh_routers=true
+
+check ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 
type=router \
+  options:router-port=ls2 addresses=\"$rp_ls2_mac\"
+
+# Connect R1 to R2
+check ovn-nbctl lrp-add R1 R1_ls-transfer 00:00:00:02:03:04 2001:db8::1/64
+check ovn-nbctl lrp-add R2 R2_ls-transfer 00:00:00:02:03:05 2001:db8::2/64
+
+check ovn-nbctl lsp-add ls-transfer ls-transfer_r1 -- \
+  set Logical_Switch_Port ls-transfer_r1 type=router \
+  options:router-port=R1_ls-transfer addresses=\"router\"
+check ovn-nbctl lsp-add ls-transfer ls-transfer_r2 -- \
+  set Logical_Switch_Port ls-transfer_r2 type=router \
+  options:router-port=R2_ls-transfer addresses=\"router\"
+
+AT_CHECK([ovn-nbctl lr-route-add R1 "0.0.0.0/0" 2001:db8::2])
+AT_CHECK([ovn-nbctl lr-route-add R2 "0.0.0.0/0" 2001:db8::1])
+
+# Create logical port ls1-lp1 in ls1
+check ovn-nbctl lsp-add ls1 ls1-lp1 \
+-- lsp-set-addresses ls1-lp1 "$ls1_lp1_mac $ls1_lp1_ip"
+
+# Create logical port ls2-lp1 in ls2
+check ovn-nbctl lsp-add ls2 ls2-lp1 \
+-- lsp-set-addresses ls2-lp1 "$ls2_lp1_mac $ls2_lp1_ip"
+
+# Create two hypervisor and create OVS ports corresponding to logical ports.
+net_add n1
+
+sim_add hv1
+as hv1
+check ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+check ovs-vsctl -- add-port br-int hv1-vif1 -- \
+    set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \
+    options:tx_pcap=hv1/vif1-tx.pcap \
+    options:rxq_pcap=hv1/vif1-rx.pcap \
+    ofport-request=1
+
+sim_add hv2
+as hv2
+check ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.2
+check ovs-vsctl -- add-port br-int hv2-vif1 -- \
+    set interface hv2-vif1 external-ids:iface-id=ls2-lp1 \
+    options:tx_pcap=hv2/vif1-tx.pcap \
+    options:rxq_pcap=hv2/vif1-rx.pcap \
+    ofport-request=1
+
+
+# Pre-populate the hypervisors' ARP tables so that we don't lose any
+# packets for ARP resolution (native tunneling doesn't queue packets
+# for ARP resolution).
+OVN_POPULATE_ARP
+
+# Allow some time for ovn-northd and ovn-controller to catch up.
+wait_for_ports_up
+check ovn-nbctl --wait=hv sync
+
+# Packet to send.
+packet=$(fmt_pkt "Ether(dst='${rp_ls1_mac}', src='${ls1_lp1_mac}')/ \
+                        IP(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', ttl=64)/ \
+                        UDP(sport=53, dport=4369)")
+check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet"
+
+# Packet to Expect
+# The TTL should be decremented by 2.
+expected=$(fmt_pkt "Ether(dst='${ls2_lp1_mac}', src='${rp_ls2_mac}')/ \
+                        IP(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', ttl=62)/ \
+                        UDP(sport=53, dport=4369)")
+echo ${expected} > expected
+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+
+AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \
+grep "reg0 == 172.16.1.2" | wc -l], [0], [1
+])
+
+# Disable the ls2-lp1 port.
+check ovn-nbctl --wait=hv set logical_switch_port ls2-lp1 enabled=false
+
+AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \
+grep "reg0 == 172.16.1.2" | wc -l], [0], [0
+])
+
+# Send the same packet again and it should not be delivered
+check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet"
+
+# The 2nd packet sent shound not be received.
+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+
+OVN_CLEANUP([hv1],[hv2])
+
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([2 HVs, 2 LS, 1 lport/LS, LRs connected via LS, IPv6 over IPv4, 
dynamic])
+AT_SKIP_IF([test $HAVE_SCAPY = no])
+ovn_start
+
+# Logical network:
+# Two LRs - R1 and R2 that are connected to ls-transfer in 10.0.0.0/24
+# network. R1 has a switchs ls1 (2001:db8:1::/64) connected to it.
+# R2 has ls2 (2001:db8:2::/64) connected to it.
+
+ls1_lp1_mac="f0:00:00:01:02:03"
+rp_ls1_mac="00:00:00:01:02:03"
+rp_ls2_mac="00:00:00:01:02:04"
+ls2_lp1_mac="f0:00:00:01:02:04"
+
+ls1_lp1_ip="2001:db8:1::2"
+ls2_lp1_ip="2001:db8:2::2"
+
+check ovn-nbctl lr-add R1
+check ovn-nbctl lr-add R2
+
+check ovn-nbctl ls-add ls1
+check ovn-nbctl ls-add ls2
+check ovn-nbctl ls-add ls-transfer
+
+# Connect ls1 to R1
+check ovn-nbctl lrp-add R1 ls1 $rp_ls1_mac 2001:db8:1::1/64
+check ovn-nbctl set Logical_Router R1 options:dynamic_neigh_routers=true
+
+check ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 
type=router \
+  options:router-port=ls1 addresses=\"$rp_ls1_mac\"
+
+# Connect ls2 to R2
+check ovn-nbctl lrp-add R2 ls2 $rp_ls2_mac 2001:db8:2::1/64
+check ovn-nbctl set Logical_Router R2 options:dynamic_neigh_routers=true
+
+check ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 
type=router \
+  options:router-port=ls2 addresses=\"$rp_ls2_mac\"
+
+# Connect R1 to R2
+check ovn-nbctl lrp-add R1 R1_ls-transfer 00:00:00:02:03:04 10.0.0.1/24
+check ovn-nbctl lrp-add R2 R2_ls-transfer 00:00:00:02:03:05 10.0.0.2/24
+
+check ovn-nbctl lsp-add ls-transfer ls-transfer_r1 -- \
+  set Logical_Switch_Port ls-transfer_r1 type=router \
+  options:router-port=R1_ls-transfer addresses=\"router\"
+check ovn-nbctl lsp-add ls-transfer ls-transfer_r2 -- \
+  set Logical_Switch_Port ls-transfer_r2 type=router \
+  options:router-port=R2_ls-transfer addresses=\"router\"
+
+AT_CHECK([ovn-nbctl lr-route-add R1 "::/0" 10.0.0.2])
+AT_CHECK([ovn-nbctl lr-route-add R2 "::/0" 10.0.0.1])
+
+# Create logical port ls1-lp1 in ls1
+check ovn-nbctl lsp-add ls1 ls1-lp1 \
+-- lsp-set-addresses ls1-lp1 "$ls1_lp1_mac $ls1_lp1_ip"
+
+# Create logical port ls2-lp1 in ls2
+check ovn-nbctl lsp-add ls2 ls2-lp1 \
+-- lsp-set-addresses ls2-lp1 "$ls2_lp1_mac $ls2_lp1_ip"
+
+# Create two hypervisor and create OVS ports corresponding to logical ports.
+net_add n1
+
+sim_add hv1
+as hv1
+check ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+check ovs-vsctl -- add-port br-int hv1-vif1 -- \
+    set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \
+    options:tx_pcap=hv1/vif1-tx.pcap \
+    options:rxq_pcap=hv1/vif1-rx.pcap \
+    ofport-request=1
+
+sim_add hv2
+as hv2
+check ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.2
+check ovs-vsctl -- add-port br-int hv2-vif1 -- \
+    set interface hv2-vif1 external-ids:iface-id=ls2-lp1 \
+    options:tx_pcap=hv2/vif1-tx.pcap \
+    options:rxq_pcap=hv2/vif1-rx.pcap \
+    ofport-request=1
+
+
+# Pre-populate the hypervisors' ARP tables so that we don't lose any
+# packets for ARP resolution (native tunneling doesn't queue packets
+# for ARP resolution).
+OVN_POPULATE_ARP
+
+# Allow some time for ovn-northd and ovn-controller to catch up.
+wait_for_ports_up
+check ovn-nbctl --wait=hv sync
+
+# Packet to send.
+packet=$(fmt_pkt "Ether(dst='${rp_ls1_mac}', src='${ls1_lp1_mac}')/ \
+                        IPv6(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', 
hlim=64)/ \
+                        UDP(sport=53, dport=4369)")
+check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet"
+
+# Packet to Expect
+# The TTL should be decremented by 2.
+expected=$(fmt_pkt "Ether(dst='${ls2_lp1_mac}', src='${rp_ls2_mac}')/ \
+                        IPv6(src='${ls1_lp1_ip}', dst='${ls2_lp1_ip}', 
hlim=62)/ \
+                        UDP(sport=53, dport=4369)")
+echo ${expected} > expected
+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+
+AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \
+grep "reg0 == 2001:db8:2::2" | wc -l], [0], [1
+])
+
+# Disable the ls2-lp1 port.
+check ovn-nbctl --wait=hv set logical_switch_port ls2-lp1 enabled=false
+
+AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \
+grep "reg0 == 2001:db8:2::2" | wc -l], [0], [0
+])
+
+# Send the same packet again and it should not be delivered
+check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 "$packet"
+
+# The 2nd packet sent shound not be received.
+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+
+OVN_CLEANUP([hv1],[hv2])
+
+AT_CLEANUP
+])
+
 OVN_FOR_EACH_NORTHD([
 AT_SETUP([2 HVs, 2 LS, 1 lport/LS, LRs connected via LS, IPv4 over IPv6, ECMP])
 AT_SKIP_IF([test $HAVE_SCAPY = no])
-- 
2.45.2

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to