]> git.proxmox.com Git - mirror_ovs.git/blobdiff - tests/ovn.at
OVN: add static IP support to IPAM
[mirror_ovs.git] / tests / ovn.at
index b586afb4e299519a8a0365ccdc63da6ccf880236..8bada32410b538ae9fe4932fabce8df964b68b9c 100644 (file)
@@ -17,13 +17,11 @@ m4_divert_text([PREPARE_TESTS],
      rcv_text=`echo "$rcv_pcap.packets" | sed 's/\.pcap//'`
      exp_text=$2
      exp_n=`wc -l < "$exp_text"`
-     ovs_wait_cond () {
-         $PYTHON "$top_srcdir/utilities/ovs-pcap.in" $rcv_pcap > $rcv_text
-         rcv_n=`wc -l < "$rcv_text"`
-         test $rcv_n -ge $exp_n
-     }
-     ovs_wait || echo "expected $exp_n packets, only received $rcv_n"
-
+     OVS_WAIT_UNTIL(
+       [$PYTHON "$top_srcdir/utilities/ovs-pcap.in" $rcv_pcap > $rcv_text
+        rcv_n=`wc -l < "$rcv_text"`
+        echo "rcv_n=$rcv_n exp_n=$exp_n"
+        test $rcv_n -ge $exp_n])
      sort $exp_text > expout
    }
 ])
@@ -287,6 +285,8 @@ ip6.src == ::1 => ip6.src == 0x1
 inport == "eth0"
 !(inport != "eth0") => inport == "eth0"
 
+(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) => 0
+
 ip4.src == "eth0" => Integer field ip4.src is not compatible with string constant.
 inport == 1 => String field inport is not compatible with integer constant.
 ip4.src = 1.2.3.4 => Syntax error at `=' expecting relational operator.
@@ -353,6 +353,10 @@ eth.dst[40] x => Syntax error at `x' expecting end of input.
 
 ip4.src == {1.2.3.4, $set1, $unknownset} => Syntax error at `$unknownset' expecting address set name.
 eth.src == {$set3, badmac, 00:00:00:00:00:01} => Syntax error at `badmac' expecting constant.
+
+((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) => Parentheses nested too deeply.
+
+ct_label > $set4 => Only == and != operators may be used to compare a field against an empty value set.
 ]])
 sed 's/ =>.*//' test-cases.txt > input.txt
 sed 's/.* => //' test-cases.txt > expout
@@ -1100,9 +1104,9 @@ put_arp(inport, arp.spa, arp.sha);
 # put_dhcp_opts
 reg1[0] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1);
     encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.40.01.02.03.04.03.04.0a.00.00.01,pause)
-reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,mtu=1400,domain="ovn.org");
-    formats as reg2[5] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.254.0, mtu = 1400, domain = "ovn.org");
-    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67,pause)
+reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,mtu=1400,domain="ovn.org",wpad="https://example.org");
+    formats as reg2[5] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.254.0, mtu = 1400, domain = "ovn.org", wpad = "https://example.org");
+    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67,pause)
 reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,mtu=1400,ip_forward_enable=1,default_ttl=121,dns_server={8.8.8.8,7.7.7.7},classless_static_route={30.0.0.0/24,10.0.0.4,40.0.0.0/16,10.0.0.6,0.0.0.0/0,10.0.0.1},ethernet_encap=1,router_discovery=0);
     formats as reg0[15] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.255.0, mtu = 1400, ip_forward_enable = 1, default_ttl = 121, dns_server = {8.8.8.8, 7.7.7.7}, classless_static_route = {30.0.0.0/24, 10.0.0.4, 40.0.0.0/16, 10.0.0.6, 0.0.0.0/0, 10.0.0.1}, ethernet_encap = 1, router_discovery = 0);
     encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.6f.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.ff.00.1a.02.05.78.13.01.01.17.01.79.06.08.08.08.08.08.07.07.07.07.79.14.18.1e.00.00.0a.00.00.04.10.28.00.0a.00.00.06.00.0a.00.00.01.24.01.01.1f.01.00,pause)
@@ -1224,6 +1228,25 @@ set_meter(100, 1000, );
 set_meter(4294967295, 4294967295);
     encodes as meter:3
 
+# log
+log(verdict=allow, severity=warning);
+    encodes as controller(userdata=00.00.00.07.00.00.00.00.00.04)
+log(name="test1", verdict=drop, severity=info);
+    encodes as controller(userdata=00.00.00.07.00.00.00.00.01.06.74.65.73.74.31)
+log(verdict=drop, severity=info, meter="meter1");
+    encodes as controller(userdata=00.00.00.07.00.00.00.00.01.06,meter_id=4)
+log(name="test1", verdict=drop, severity=info, meter="meter1");
+    encodes as controller(userdata=00.00.00.07.00.00.00.00.01.06.74.65.73.74.31,meter_id=4)
+log(verdict=drop);
+    formats as log(verdict=drop, severity=info);
+    encodes as controller(userdata=00.00.00.07.00.00.00.00.01.06)
+log(verdict=bad_verdict, severity=info);
+    Syntax error at `bad_verdict' unknown verdict.
+log(verdict=drop, severity=bad_severity);
+    Syntax error at `bad_severity' unknown severity.
+log(severity=notice);
+    Syntax error at `;' expecting verdict.
+
 # put_nd_ra_opts
 reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 1500, prefix = aef0::/64, slla = ae:01:02:03:04:05);
     encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.00.ff.ff.00.00.00.00.00.00.00.00.05.01.00.00.00.00.05.dc.03.04.40.c0.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.05,pause)
@@ -2402,8 +2425,8 @@ for i in 1 2 3; do
 
             ovn-nbctl \
                 -- lsp-add ls$i lp$i$j$k \
-                -- lsp-set-addresses lp$i$j$k "f0:00:00:00:0$i:$j$k \
-                   192.168.$i$j.$k" $unknown
+                -- lsp-set-addresses lp$i$j$k \
+                   "f0:00:00:00:0$i:$j$k 192.168.$i$j.$k" $unknown
         done
     done
 done
@@ -2522,6 +2545,45 @@ test_ip() {
     done
 }
 
+# test_arp INPORT SHA SPA TPA [REPLY_HA]
+#
+# Causes a packet to be received on INPORT.  The packet is an ARP
+# request with SHA, SPA, and TPA as specified.  If REPLY_HA is provided, then
+# it should be the hardware address of the target to expect to receive in an
+# ARP reply; otherwise no reply is expected.
+#
+# INPORT is an logical switch port number, e.g. 11 for vif11.
+# SHA and REPLY_HA are each 12 hex digits.
+# SPA and TPA are each 8 hex digits.
+test_arp() {
+    local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5
+    local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa}
+    hv=hv`vif_to_hv $inport`
+    as $hv ovs-appctl netdev-dummy/receive vif$inport $request
+    as $hv ovs-appctl ofproto/trace br-int in_port=$inport $request
+
+    # Expect to receive the broadcast ARP on the other logical switch ports if
+    # IP address is not configured to the switch patch port.
+    local i=`vif_to_ls $inport`
+    local j k
+    for j in 1 2 3; do
+        for k in 1 2 3; do
+            # 192.168.33.254 is configured to the switch patch port for lrp33,
+            # so no ARP flooding expected for it.
+            if test $i$j$k != $inport && test $tpa != `ip_to_hex 192 168 33 254`; then
+                echo $request >> $i$j$k.expected
+            fi
+        done
+    done
+
+    # Expect to receive the reply, if any.
+    if test X$reply_ha != X; then
+        lrp=`vif_to_lrp $inport`
+        local reply=${sha}00000000ff${lrp}08060001080006040002${reply_ha}${tpa}${sha}${spa}
+        echo $reply >> $inport.expected
+    fi
+}
+
 as hv1 ovs-vsctl --columns=name,ofport list interface
 as hv1 ovn-sbctl list port_binding
 as hv1 ovn-sbctl list datapath_binding
@@ -2564,10 +2626,13 @@ for is in 1 2 3; do
   done
 done
 
+: > mac_bindings.expected
+
 # 3. Send an IP packet from every logical port to every other subnet,
 #    to an IP address that does not have a static IP-MAC binding.
 #    This should generate a broadcast ARP request for the destination
 #    IP address in the destination subnet.
+#    Moreover generate an ARP reply for each of the IP addresses ARPed
 for is in 1 2 3; do
   for js in 1 2 3; do
     for ks in 1 2 3; do
@@ -2600,51 +2665,29 @@ for is in 1 2 3; do
               echo $arp >> $id$jd2$kd.expected
             done
           done
+          if test $(vif_to_hv ${is}${js}${ks}) = $(vif_to_hv ${id}${jd}1); then
+              hmac=8000000000$o4
+              rmac=00000000ff$id$jd
+              echo ${hmac}${rmac}08004500001c00000000"3f1101"00${sip}${dip}0035111100080000 >> ${id}11.expected
+          fi
+
+          host_mac=8000000000$o4
+          lrmac=00000000ff$id$jd
+
+          arp_reply=${lrmac}${host_mac}08060001080006040002${host_mac}${dip}${lrmac}${lrip}
+
+          hv=hv`vif_to_hv ${id}${jd}1`
+          as $hv ovs-appctl netdev-dummy/receive vif${id}${jd}1 $arp_reply
+
+          host_ip_pretty=192.168.$id$jd.$o4
+          host_mac_pretty=80:00:00:00:00:$o4
+          echo lrp$id$jd,$host_ip_pretty,$host_mac_pretty >> mac_bindings.expected
         done
       done
     done
   done
 done
 
-# test_arp INPORT SHA SPA TPA [REPLY_HA]
-#
-# Causes a packet to be received on INPORT.  The packet is an ARP
-# request with SHA, SPA, and TPA as specified.  If REPLY_HA is provided, then
-# it should be the hardware address of the target to expect to receive in an
-# ARP reply; otherwise no reply is expected.
-#
-# INPORT is an logical switch port number, e.g. 11 for vif11.
-# SHA and REPLY_HA are each 12 hex digits.
-# SPA and TPA are each 8 hex digits.
-test_arp() {
-    local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5
-    local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa}
-    hv=hv`vif_to_hv $inport`
-    as $hv ovs-appctl netdev-dummy/receive vif$inport $request
-    as $hv ovs-appctl ofproto/trace br-int in_port=$inport $request
-
-    # Expect to receive the broadcast ARP on the other logical switch ports if
-    # IP address is not configured to the switch patch port.
-    local i=`vif_to_ls $inport`
-    local j k
-    for j in 1 2 3; do
-        for k in 1 2 3; do
-            # 192.168.33.254 is configured to the switch patch port for lrp33,
-            # so no ARP flooding expected for it.
-            if test $i$j$k != $inport && test $tpa != `ip_to_hex 192 168 33 254`; then
-                echo $request >> $i$j$k.expected
-            fi
-        done
-    done
-
-    # Expect to receive the reply, if any.
-    if test X$reply_ha != X; then
-        lrp=`vif_to_lrp $inport`
-        local reply=${sha}00000000ff${lrp}08060001080006040002${reply_ha}${tpa}${sha}${spa}
-        echo $reply >> $inport.expected
-    fi
-}
-
 # Test router replies to ARP requests from all source ports:
 #
 # 4. Router replies to query for its MAC address from port's own IP address.
@@ -2652,9 +2695,9 @@ test_arp() {
 # 5. Router replies to query for its MAC address from any random IP address
 #    in its subnet.
 #
-# 6. Router replies to query for its MAC address from another subnet.
+# 6. No reply to query for IP address other than router IP.
 #
-# 7. No reply to query for IP address other than router IP.
+# 7. No reply to query from another subnet.
 for i in 1 2 3; do
   for j in 1 2 3; do
     for k in 1 2 3; do
@@ -2663,69 +2706,47 @@ for i in 1 2 3; do
       rip=`ip_to_hex 192 168 $i$j 254`   # Router IP
       rmac=00000000ff$i$j                # Router MAC
       otherip=`ip_to_hex 192 168 $i$j 55` # Some other IP in subnet
-      test_arp $i$j$k $smac $sip        $rip        $rmac #4
-      test_arp $i$j$k $smac $otherip    $rip        $rmac #5
-      test_arp $i$j$k $smac 0a123456    $rip        $rmac #6
-      test_arp $i$j$k $smac $sip        $otherip          #7
-    done
-  done
-done
-
-# Allow some time for packet forwarding.
-# XXX This can be improved.
-sleep 1
-
-# 8. Generate an ARP reply for each of the IP addresses ARPed for
-#    earlier as #3.
-#
-#    Here, the $s is the VIF that originated the ARP request and $d is
-#    the VIF that sends the ARP reply, which is somewhat backward but
-#    it means that $s and $d are the same as #3.
-: > mac_bindings.expected
-for is in 1 2 3; do
-  for js in 1 2 3; do
-    for ks in 1 2 3; do
-      s=$is$js$ks
-      for id in 1 2 3; do
-        for jd in 1 2 3; do
-          if test $is$js = $id$jd; then
-            continue
+      externalip=`ip_to_hex 1 2 3 4`      # Some other IP not in subnet
+
+      test_arp $i$j$k $smac $sip        $rip        $rmac      #4
+      test_arp $i$j$k $smac $otherip    $rip        $rmac      #5
+      test_arp $i$j$k $smac $sip        $otherip               #6
+
+      # When rip is 192.168.33.254, ARP request from externalip won't be
+      # filtered, because 192.168.33.254 is configured to switch peer port
+      # for lrp33.
+      lrp33_rsp=
+      if test $i = 3 && test $j = 3; then
+        lrp33_rsp=$rmac
+      fi
+      test_arp $i$j$k $smac $externalip $rip        $lrp33_rsp #7
+
+      # MAC binding should be learned from ARP request.
+      host_mac_pretty=f0:00:00:00:0$i:$j$k
+
+      host_ip_pretty=192.168.$i$j.$k
+      echo lrp$i$j,$host_ip_pretty,$host_mac_pretty >> mac_bindings.expected
+
+      # mac_binding is learned and overwritten so only the last one remains.
+      if test $k = 3; then
+          # lrp33 will not learn from ARP request, because 192.168.33.254 is
+          # configured to switch peer port for lrp33.
+          if test $i != 3 || test $j != 3; then
+              host_ip_pretty=192.168.$i$j.55
+              echo lrp$i$j,$host_ip_pretty,$host_mac_pretty >> mac_bindings.expected
           fi
+      fi
 
-          kd=1
-          d=$id$jd$kd
-
-          o4=`expr $is '*' 9 + $js '*' 3 + $ks + 10`
-          host_ip=`ip_to_hex 192 168 $id$jd $o4`
-          host_mac=8000000000$o4
-
-          lrmac=00000000ff$id$jd
-          lrip=`ip_to_hex 192 168 $id$jd 254`
-
-          arp=${lrmac}${host_mac}08060001080006040002${host_mac}${host_ip}${lrmac}${lrip}
-
-          echo
-          echo
-          echo
-          hv=hv`vif_to_hv $d`
-          as $hv ovs-appctl netdev-dummy/receive vif$d $arp
-          #as $hv ovs-appctl ofproto/trace br-int in_port=$d $arp
-          #as $hv ovs-ofctl dump-flows br-int table=19
-
-          host_ip_pretty=192.168.$id$jd.$o4
-          host_mac_pretty=80:00:00:00:00:$o4
-          echo lrp$id$jd,$host_ip_pretty,$host_mac_pretty >> mac_bindings.expected
-        done
-      done
     done
   done
 done
 
+
 # Allow some time for packet forwarding.
 # XXX This can be improved.
 sleep 1
 
-# 9. Send an IP packet from every logical port to every other subnet.  These
+# 8. Send an IP packet from every logical port to every other subnet.  These
 #    are the same packets already sent as #3, but now the destinations' IP-MAC
 #    bindings have been discovered via ARP, so instead of provoking an ARP
 #    request, these packets now get routed to their destinations (which don't
@@ -2786,39 +2807,80 @@ OVN_CLEANUP([hv1], [hv2], [hv3])
 
 AT_CLEANUP
 
-# 3 hypervisors, one logical switch, 3 logical ports per hypervisor
-AT_SETUP([ovn -- portsecurity : 3 HVs, 1 LS, 3 lports/HV])
+AT_SETUP([ovn -- IP relocation using GARP request])
 AT_SKIP_IF([test $HAVE_PYTHON = no])
 ovn_start
 
-# Create hypervisors hv[123].
-# Add vif1[123] to hv1, vif2[123] to hv2, vif3[123] to hv3.
-# Add all of the vifs to a single logical switch lsw0.
-# Turn off port security on vifs vif[123]1
-# Turn on l2 port security on vifs vif[123]2
-# Turn of l2 and l3 port security on vifs vif[123]3
-# Make vif13, vif2[23], vif3[123] destinations for unknown MACs.
-ovn-nbctl ls-add lsw0
+# Logical network:
+#
+# Two logical switches ls1, ls2.
+# One logical router lr0 connected to ls[12],
+# with 2 subnets, 1 per logical switch:
+#
+#    lrp1 on ls1 for subnet 192.168.1.1/24
+#    lrp2 on ls2 for subnet 192.168.2.1/24
+#
+# 4 VIFs, 2 per LS lp[12][12], first digit being LS.
+# VIFs' fixed IP addresses are 192.168.[12].1[12].
+#
+# There is a secondary IP 192.168.1.100 that is unknown in NB and learned
+# through ARP only, and it can move between lp11 and lp12.
+#
+ovn-nbctl lr-add lr0
+for i in 1 2 ; do
+    ovn-nbctl ls-add ls$i
+    ovn-nbctl lrp-add lr0 lrp$i 00:00:00:00:ff:0$i 192.168.$i.1/24
+    ovn-nbctl \
+        -- lsp-add ls$i lrp$i-attachment \
+        -- set Logical_Switch_Port lrp$i-attachment type=router \
+                         options:router-port=lrp$i \
+                         addresses=router
+    for j in 1 2; do
+        ovn-nbctl \
+            -- lsp-add ls$i lp$i$j \
+            -- lsp-set-addresses lp$i$j \
+               "f0:00:00:00:00:$i$j 192.168.$i.1$j"
+    done
+done
+
+# Physical network:
+# 2 hypervisors hv[12], lp?1 on hv1, lp?2 on hv2.
+
+# Given the name of a logical port, prints the name of the hypervisor
+# on which it is located, e.g. "vif_to_hv 12" yields 2.
+vif_to_hv() {
+    echo ${1#?}
+}
+
+# Given the name of a logical port, prints the name of its logical router
+# port, e.g. "vif_to_lrp 12" yields 1.
+vif_to_lrp() {
+    echo ${1%?}
+}
+
+# Given the name of a logical port, prints the name of its logical
+# switch, e.g. "vif_to_ls 12" yields 1.
+vif_to_ls() {
+    echo ${1%?}
+}
+
 net_add n1
-for i in 1 2 3; do
+for i in 1 2; do
     sim_add hv$i
     as hv$i
     ovs-vsctl add-br br-phys
     ovn_attach n1 br-phys 192.168.0.$i
-
-    for j in 1 2 3; do
-        ovs-vsctl add-port br-int vif$i$j -- set Interface vif$i$j external-ids:iface-id=lp$i$j options:tx_pcap=hv$i/vif$i$j-tx.pcap options:rxq_pcap=hv$i/vif$i$j-rx.pcap ofport-request=$i$j
-        ovn-nbctl lsp-add lsw0 lp$i$j
-        if test $j = 1; then
-            ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" unknown
-        elif test $j = 2; then
-            ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j"
-            ovn-nbctl lsp-set-port-security lp$i$j f0:00:00:00:00:$i$j
-        else
-            extra_addr="f0:00:00:00:0$i:$i$j fe80::ea2a:eaff:fe28:$i$j"
-            ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" "$extra_addr"
-            ovn-nbctl lsp-set-port-security lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" "$extra_addr"
-        fi
+done
+for i in 1 2; do
+    for j in 1 2; do
+        hv=`vif_to_hv $i$j`
+            as hv$hv ovs-vsctl \
+                -- add-port br-int vif$i$j \
+                -- set Interface vif$i$j \
+                    external-ids:iface-id=lp$i$j \
+                    options:tx_pcap=hv$hv/vif$i$j-tx.pcap \
+                    options:rxq_pcap=hv$hv/vif$i$j-rx.pcap \
+                    ofport-request=$i$j
     done
 done
 
@@ -2831,40 +2893,42 @@ OVN_POPULATE_ARP
 # XXX This should be more systematic.
 sleep 1
 
-# Given the name of a logical port, prints the name of the hypervisor
-# on which it is located.
-vif_to_hv() {
-    echo hv${1%?}
-}
-
-for i in 1 2 3; do
-    for j in 1 2 3; do
+# test_ip INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT...
+#
+# This shell function causes a packet to be received on INPORT.  The packet's
+# content has Ethernet destination DST and source SRC (each exactly 12 hex
+# digits) and Ethernet type ETHTYPE (4 hex digits).  The OUTPORTs (zero or
+# more) list the VIFs on which the packet should be received.  INPORT and the
+# OUTPORTs are specified as logical switch port numbers, e.g. 12 for vif12.
+for i in 1 2; do
+    for j in 1 2; do
         : > $i$j.expected
     done
 done
-
-# test_ip INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT...
-#
-# This shell function causes an ip packet to be received on INPORT.
-# The packet's content has Ethernet destination DST and source SRC
-# (each exactly 12 hex digits) and Ethernet type ETHTYPE (4 hex digits).
-# The OUTPORTs (zero or more) list the VIFs on which the packet should
-# be received.  INPORT and the OUTPORTs are specified as logical switch
-# port numbers, e.g. 11 for vif11.
 test_ip() {
     # This packet has bad checksums but logical L3 routing doesn't check.
     local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5
     local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
     shift; shift; shift; shift; shift
-    hv=`vif_to_hv $inport`
+    hv=hv`vif_to_hv $inport`
     as $hv ovs-appctl netdev-dummy/receive vif$inport $packet
-    #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $packet
+    in_ls=`vif_to_ls $inport`
+    in_lrp=`vif_to_lrp $inport`
     for outport; do
-        echo $packet >> $outport.expected
+        out_ls=`vif_to_ls $outport`
+        if test $in_ls = $out_ls; then
+            # Ports on the same logical switch receive exactly the same packet.
+            echo $packet
+        else
+            # Routing decrements TTL and updates source and dest MAC
+            # (and checksum).
+            out_lrp=`vif_to_lrp $outport`
+            echo f000000000${outport}00000000ff0${out_lrp}08004500001c00000000"3f1101"00${src_ip}${dst_ip}0035111100080000
+        fi >> $outport.expected
     done
 }
 
-# test_arp INPORT SHA SPA TPA DROP [REPLY_HA]
+# test_arp INPORT SHA SPA TPA [REPLY_HA]
 #
 # Causes a packet to be received on INPORT.  The packet is an ARP
 # request with SHA, SPA, and TPA as specified.  If REPLY_HA is provided, then
@@ -2875,30 +2939,187 @@ test_ip() {
 # SHA and REPLY_HA are each 12 hex digits.
 # SPA and TPA are each 8 hex digits.
 test_arp() {
-    local inport=$1 smac=$2 sha=$3 spa=$4 tpa=$5 drop=$6 reply_ha=$7
-    local request=ffffffffffff${smac}08060001080006040001${sha}${spa}ffffffffffff${tpa}
-    hv=`vif_to_hv $inport`
+    local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5
+    local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa}
+    hv=hv`vif_to_hv $inport`
     as $hv ovs-appctl netdev-dummy/receive vif$inport $request
-    #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $request
-    if test $drop != 1; then
-        if test X$reply_ha = X; then
-            # Expect to receive the broadcast ARP on the other logical switch ports
-            # if no reply is expected.
-            local i j
-            for i in 1 2 3; do
-                for j in 1 2 3; do
-                    if test $i$j != $inport; then
-                        echo $request >> $i$j.expected
-                    fi
-                done
-            done
-        else
-            # Expect to receive the reply, if any.
-            local reply=${smac}${reply_ha}08060001080006040002${reply_ha}${tpa}${sha}${spa}
-            echo $reply >> $inport.expected
+
+    # Expect to receive the broadcast ARP on the other logical switch ports if
+    # IP address is not configured to the switch patch port.
+    local i=`vif_to_ls $inport`
+    local j
+    for j in 1 2; do
+        if test $i$j != $inport; then
+            echo $request >> $i$j$k.expected
         fi
-    fi
-}
+    done
+
+    # Expect to receive the reply, if any.
+    if test X$reply_ha != X; then
+        lrp=`vif_to_lrp $inport`
+        local reply=${sha}00000000ff0${lrp}08060001080006040002${reply_ha}${tpa}${sha}${spa}
+        echo $reply >> $inport.expected
+    fi
+}
+
+ip_to_hex() {
+    printf "%02x%02x%02x%02x" "$@"
+}
+
+# lp11 send GARP request to announce ownership of 192.168.1.100.
+
+sha=f00000000011
+spa=`ip_to_hex 192 168 1 100`
+tpa=$spa
+test_arp 11 $sha $spa $tpa
+OVS_WAIT_UNTIL([test `ovn-sbctl find mac_binding ip="192.168.1.100" | wc -l` -gt 0])
+ovn-nbctl --wait=hv sync
+
+# Send an IP packet from lp21 to 192.168.1.100, which should go to lp11.
+
+smac=f00000000021
+dmac=00000000ff02
+sip=`ip_to_hex 192 168 2 11`
+dip=`ip_to_hex 192 168 1 100`
+test_ip 21 $smac $dmac $sip $dip 11
+
+# lp12 send GARP request to announce ownership of 192.168.1.100.
+
+sha=f00000000012
+test_arp 12 $sha $spa $tpa
+OVS_WAIT_UNTIL([ovn-sbctl find mac_binding ip="192.168.1.100" | grep f0:00:00:00:00:12])
+ovn-nbctl --wait=hv sync
+
+# Send an IP packet from lp21 to 192.168.1.100, which should go to lp12.
+
+test_ip 21 $smac $dmac $sip $dip 12
+
+# Now check the packets actually received against the ones expected.
+for i in 1 2; do
+    for j in 1 2; do
+        OVN_CHECK_PACKETS([hv`vif_to_hv $i$j`/vif$i$j-tx.pcap],
+                          [$i$j.expected])
+    done
+done
+
+# Gracefully terminate daemons
+OVN_CLEANUP([hv1], [hv2])
+
+AT_CLEANUP
+
+# 3 hypervisors, one logical switch, 3 logical ports per hypervisor
+AT_SETUP([ovn -- portsecurity : 3 HVs, 1 LS, 3 lports/HV])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+ovn_start
+
+# Create hypervisors hv[123].
+# Add vif1[123] to hv1, vif2[123] to hv2, vif3[123] to hv3.
+# Add all of the vifs to a single logical switch lsw0.
+# Turn off port security on vifs vif[123]1
+# Turn on l2 port security on vifs vif[123]2
+# Turn of l2 and l3 port security on vifs vif[123]3
+# Make vif13, vif2[23], vif3[123] destinations for unknown MACs.
+ovn-nbctl ls-add lsw0
+net_add n1
+for i in 1 2 3; do
+    sim_add hv$i
+    as hv$i
+    ovs-vsctl add-br br-phys
+    ovn_attach n1 br-phys 192.168.0.$i
+
+    for j in 1 2 3; do
+        ovs-vsctl add-port br-int vif$i$j -- set Interface vif$i$j external-ids:iface-id=lp$i$j options:tx_pcap=hv$i/vif$i$j-tx.pcap options:rxq_pcap=hv$i/vif$i$j-rx.pcap ofport-request=$i$j
+        ovn-nbctl lsp-add lsw0 lp$i$j
+        if test $j = 1; then
+            ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" unknown
+        elif test $j = 2; then
+            ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j"
+            ovn-nbctl lsp-set-port-security lp$i$j f0:00:00:00:00:$i$j
+        else
+            extra_addr="f0:00:00:00:0$i:$i$j fe80::ea2a:eaff:fe28:$i$j"
+            ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" "$extra_addr"
+            ovn-nbctl lsp-set-port-security lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" "$extra_addr"
+        fi
+    done
+done
+
+# 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.
+# XXX This should be more systematic.
+sleep 1
+
+# Given the name of a logical port, prints the name of the hypervisor
+# on which it is located.
+vif_to_hv() {
+    echo hv${1%?}
+}
+
+for i in 1 2 3; do
+    for j in 1 2 3; do
+        : > $i$j.expected
+    done
+done
+
+# test_ip INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT...
+#
+# This shell function causes an ip packet to be received on INPORT.
+# The packet's content has Ethernet destination DST and source SRC
+# (each exactly 12 hex digits) and Ethernet type ETHTYPE (4 hex digits).
+# The OUTPORTs (zero or more) list the VIFs on which the packet should
+# be received.  INPORT and the OUTPORTs are specified as logical switch
+# port numbers, e.g. 11 for vif11.
+test_ip() {
+    # This packet has bad checksums but logical L3 routing doesn't check.
+    local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5
+    local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
+    shift; shift; shift; shift; shift
+    hv=`vif_to_hv $inport`
+    as $hv ovs-appctl netdev-dummy/receive vif$inport $packet
+    #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $packet
+    for outport; do
+        echo $packet >> $outport.expected
+    done
+}
+
+# test_arp INPORT SHA SPA TPA DROP [REPLY_HA]
+#
+# Causes a packet to be received on INPORT.  The packet is an ARP
+# request with SHA, SPA, and TPA as specified.  If REPLY_HA is provided, then
+# it should be the hardware address of the target to expect to receive in an
+# ARP reply; otherwise no reply is expected.
+#
+# INPORT is an logical switch port number, e.g. 11 for vif11.
+# SHA and REPLY_HA are each 12 hex digits.
+# SPA and TPA are each 8 hex digits.
+test_arp() {
+    local inport=$1 smac=$2 sha=$3 spa=$4 tpa=$5 drop=$6 reply_ha=$7
+    local request=ffffffffffff${smac}08060001080006040001${sha}${spa}ffffffffffff${tpa}
+    hv=`vif_to_hv $inport`
+    as $hv ovs-appctl netdev-dummy/receive vif$inport $request
+    #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $request
+    if test $drop != 1; then
+        if test X$reply_ha = X; then
+            # Expect to receive the broadcast ARP on the other logical switch ports
+            # if no reply is expected.
+            local i j
+            for i in 1 2 3; do
+                for j in 1 2 3; do
+                    if test $i$j != $inport; then
+                        echo $request >> $i$j.expected
+                    fi
+                done
+            done
+        else
+            # Expect to receive the reply, if any.
+            local reply=${smac}${reply_ha}08060001080006040002${reply_ha}${tpa}${sha}${spa}
+            echo $reply >> $inport.expected
+        fi
+    fi
+}
 
 # test_ipv6 INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT...
 # This function is similar to test_ip() except that it sends
@@ -4013,10 +4234,10 @@ sleep 2
 as hv1 ovs-vsctl show
 
 # This shell function sends a DHCP request packet
-# test_dhcp INPORT SRC_MAC DHCP_TYPE OFFER_IP ...
+# test_dhcp INPORT SRC_MAC DHCP_TYPE OFFER_IP REQUEST_IP ...
 test_dhcp() {
-    local inport=$1 src_mac=$2 dhcp_type=$3 offer_ip=$4 use_ip=$5
-    shift; shift; shift; shift; shift;
+    local inport=$1 src_mac=$2 dhcp_type=$3 ciaddr=$4 offer_ip=$5 request_ip=$6 use_ip=$7
+    shift; shift; shift; shift; shift; shift; shift;
     if test $use_ip != 0; then
         src_ip=$1
         dst_ip=$2
@@ -4025,10 +4246,17 @@ test_dhcp() {
         src_ip=`ip_to_hex 0 0 0 0`
         dst_ip=`ip_to_hex 255 255 255 255`
     fi
-    local request=ffffffffffff${src_mac}0800451001100000000080110000${src_ip}${dst_ip}
+    if test $request_ip != 0; then
+        ip_len=0120
+        udp_len=010b
+    else
+        ip_len=011a
+        udp_len=0106
+    fi
+    local request=ffffffffffff${src_mac}08004510${ip_len}0000000080110000${src_ip}${dst_ip}
     # udp header and dhcp header
-    request=${request}0044004300fc0000
-    request=${request}010106006359aa760000000000000000000000000000000000000000${src_mac}
+    request=${request}00440043${udp_len}0000
+    request=${request}010106006359aa7600000000${ciaddr}000000000000000000000000${src_mac}
     # client hardware padding
     request=${request}00000000000000000000
     # server hostname
@@ -4042,10 +4270,23 @@ test_dhcp() {
     # dhcp magic cookie
     request=${request}63825363
     # dhcp message type
-    request=${request}3501${dhcp_type}ff
+    request=${request}3501${dhcp_type}
+    # dhcp unknown option
+    request=${request}d70701020304050607
+    # dhcp pad option
+    request=${request}00
+    if test $request_ip != 0; then
+        # dhcp requested ip
+        request=${request}3204${request_ip}
+    fi
+    # dhcp end option
+    request=${request}ff
 
+    for port in $inport "$@"; do
+        : >> $port.expected
+    done
     if test $offer_ip != 0; then
-        local srv_mac=$1 srv_ip=$2 expected_dhcp_opts=$3
+        local srv_mac=$1 srv_ip=$2 dhcp_reply_type=$3 expected_dhcp_opts=$4
         # total IP length will be the IP length of the request packet
         # (which is 272 in our case) + 8 (padding bytes) + (expected_dhcp_opts / 2)
         ip_len=`expr 280 + ${#expected_dhcp_opts} / 2`
@@ -4056,9 +4297,13 @@ test_dhcp() {
         local reply=${src_mac}${srv_mac}080045100${ip_len}000000008011XXXX${srv_ip}${offer_ip}
         # udp header and dhcp header.
         # $udp_len var will be in 3 digits. So adding a '0' before $udp_len
-        reply=${reply}004300440${udp_len}0000020106006359aa760000000000000000
-        # your ip address
-        reply=${reply}${offer_ip}
+        reply=${reply}004300440${udp_len}0000020106006359aa7600000000${ciaddr}
+        # your ip address; 0 for NAK
+        if test $dhcp_reply_type = 06; then
+            reply=${reply}00000000
+        else
+            reply=${reply}${offer_ip}
+        fi
         # next server ip address, relay agent ip address, client mac address
         reply=${reply}0000000000000000${src_mac}
         # client hardware padding
@@ -4073,11 +4318,6 @@ test_dhcp() {
         reply=${reply}0000000000000000000000000000000000000000000000000000000000000000
         # dhcp magic cookie
         reply=${reply}63825363
-        # dhcp message type
-        local dhcp_reply_type=02
-        if test $dhcp_type = 03; then
-            dhcp_reply_type=05
-        fi
         reply=${reply}3501${dhcp_reply_type}${expected_dhcp_opts}00000000ff00000000
         echo $reply >> $inport.expected
     else
@@ -4125,8 +4365,10 @@ as hv1 ovs-ofctl dump-flows br-int
 # Send DHCPDISCOVER.
 offer_ip=`ip_to_hex 10 0 0 4`
 server_ip=`ip_to_hex 10 0 0 1`
+ciaddr=`ip_to_hex 0 0 0 0`
+request_ip=0
 expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
-test_dhcp 1 f00000000001 01 $offer_ip 0 ff1000000001 $server_ip $expected_dhcp_opts
+test_dhcp 1 f00000000001 01 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
 
 # NXT_RESUMEs should be 1.
 OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -4145,11 +4387,14 @@ reset_pcap_file hv1-vif2 hv1/vif2
 rm -f 1.expected
 rm -f 2.expected
 
-# Send DHCPREQUEST.
+# Send DHCPREQUEST in the SELECTING/INIT-REBOOT state with the offered IP
+# address in the Requested IP Address option.
 offer_ip=`ip_to_hex 10 0 0 6`
 server_ip=`ip_to_hex 10 0 0 1`
+ciaddr=`ip_to_hex 0 0 0 0`
+request_ip=$offer_ip
 expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
-test_dhcp 2 f00000000002 03 $offer_ip 0 ff1000000001 $server_ip $expected_dhcp_opts
+test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
 
 # NXT_RESUMEs should be 2.
 OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -4166,15 +4411,41 @@ reset_pcap_file hv1-vif2 hv1/vif2
 rm -f 1.expected
 rm -f 2.expected
 
+# Send DHCPREQUEST in the SELECTING/INIT-REBOOT state with a mismatched IP in
+# the Requested IP Address option, expect a DHCPNAK.
+offer_ip=`ip_to_hex 10 0 0 6`
+server_ip=`ip_to_hex 10 0 0 1`
+ciaddr=`ip_to_hex 0 0 0 0`
+request_ip=`ip_to_hex 10 0 0 7`
+expected_dhcp_opts=""
+test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 06 $expected_dhcp_opts
+
+# NXT_RESUMEs should be 3.
+OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
+cat 2.expected | cut -c -48 > expout
+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
+# Skipping the IPv4 checksum.
+cat 2.expected | cut -c 53- > expout
+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
+
+reset_pcap_file hv1-vif1 hv1/vif1
+reset_pcap_file hv1-vif2 hv1/vif2
+rm -f 1.expected
+rm -f 2.expected
+
 # Send Invalid DHCPv4 packet on ls1-lp2. It should be received by ovn-controller
 # but should be resumed without the reply.
 # ls1-lp1 (vif1-tx.pcap) should receive the DHCPv4 request packet twice,
 # one from ovn-controller and the other from "ovs-ofctl resume."
+ciaddr=`ip_to_hex 0 0 0 0`
 offer_ip=0
-test_dhcp 2 f00000000002 08 $offer_ip 0 1 1
+request_ip=0
+test_dhcp 2 f00000000002 08 $ciaddr $offer_ip $request_ip 0 1 1
 
-# NXT_RESUMEs should be 3.
-OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+# NXT_RESUMEs should be 4.
+OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
 
 # vif1-tx.pcap should have received the DHCPv4 (invalid) request packet
 OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [1.expected])
@@ -4187,28 +4458,33 @@ rm -f 2.expected
 # Send DHCPv4 packet on ls2-lp1. It doesn't have any DHCPv4 options defined.
 # ls2-lp2 (vif4-tx.pcap) should receive the DHCPv4 request packet once.
 
-test_dhcp 3 f00000000003 01 0 4 0
+ciaddr=`ip_to_hex 0 0 0 0`
+test_dhcp 3 f00000000003 01 $ciaddr 0 0 4 0
 
 # Send DHCPv4 packet on ls2-lp2. "router" DHCPv4 option is not defined for
 # this lport.
-test_dhcp 4 f00000000004 01 0 3 0
+ciaddr=`ip_to_hex 0 0 0 0`
+test_dhcp 4 f00000000004 01 $ciaddr 0 0 3 0
 
-# NXT_RESUMEs should be 3.
-OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+# NXT_RESUMEs should be 4.
+OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
 
-OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [3.expected])
-OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [4.expected])
+#OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [3.expected])
+#OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [4.expected])
 
-# Send DHCPREQUEST with ip4.src set to 10.0.0.6 and ip4.dst set to 10.0.0.1.
+# Send DHCPREQUEST in the RENEWING/REBINDING state with ip4.src set to 10.0.0.6
+# and ip4.dst set to 10.0.0.1.
 offer_ip=`ip_to_hex 10 0 0 6`
 server_ip=`ip_to_hex 10 0 0 1`
-expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
+ciaddr=$offer_ip
+request_ip=0
 src_ip=$offer_ip
 dst_ip=$server_ip
-test_dhcp 2 f00000000002 03 $offer_ip 1 $src_ip $dst_ip ff1000000001 $server_ip $expected_dhcp_opts
+expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
+test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
 
-# NXT_RESUMEs should be 4.
-OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+# NXT_RESUMEs should be 5.
+OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
 
 $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
 cat 2.expected | cut -c -48 > expout
@@ -4222,16 +4498,71 @@ reset_pcap_file hv1-vif2 hv1/vif2
 rm -f 1.expected
 rm -f 2.expected
 
-# Send DHCPREQUEST with ip4.src set to 10.0.0.6 and ip4.dst set to 255.255.255.255.
+# Send DHCPREQUEST in the RENEWING/REBINDING state with ip4.src set to 10.0.0.6
+# and ip4.dst set to 255.255.255.255.
 offer_ip=`ip_to_hex 10 0 0 6`
 server_ip=`ip_to_hex 10 0 0 1`
+ciaddr=$offer_ip
+request_ip=0
+src_ip=$offer_ip
+dst_ip=`ip_to_hex 255 255 255 255`
 expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
+test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
+
+# NXT_RESUMEs should be 6.
+OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
+cat 2.expected | cut -c -48 > expout
+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
+# Skipping the IPv4 checksum.
+cat 2.expected | cut -c 53- > expout
+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
+
+reset_pcap_file hv1-vif1 hv1/vif1
+reset_pcap_file hv1-vif2 hv1/vif2
+rm -f 1.expected
+rm -f 2.expected
+
+# Send DHCPREQUEST in the RENEWING/REBINDING state with a mismatched IP in the
+# ciaddr, expect a DHCPNAK.
+offer_ip=`ip_to_hex 10 0 0 6`
+server_ip=`ip_to_hex 10 0 0 1`
+ciaddr=`ip_to_hex 10 0 0 7`
+request_ip=0
 src_ip=$offer_ip
 dst_ip=`ip_to_hex 255 255 255 255`
-test_dhcp 2 f00000000002 03 $offer_ip 1 $src_ip $dst_ip ff1000000001 $server_ip $expected_dhcp_opts
+expected_dhcp_opts=""
+test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
 
-# NXT_RESUMEs should be 5.
-OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+# NXT_RESUMEs should be 7.
+OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
+cat 2.expected | cut -c -48 > expout
+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
+# Skipping the IPv4 checksum.
+cat 2.expected | cut -c 53- > expout
+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
+
+reset_pcap_file hv1-vif1 hv1/vif1
+reset_pcap_file hv1-vif2 hv1/vif2
+rm -f 1.expected
+rm -f 2.expected
+
+# Send DHCPREQUEST in the RENEWING/REBINDING state without a specifyied ciaddr,
+# expect a DHCPNAK.
+offer_ip=`ip_to_hex 10 0 0 6`
+server_ip=`ip_to_hex 10 0 0 1`
+ciaddr=`ip_to_hex 0 0 0 0`
+request_ip=0
+src_ip=$offer_ip
+dst_ip=`ip_to_hex 255 255 255 255`
+expected_dhcp_opts=""
+test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
+
+# NXT_RESUMEs should be 8.
+OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
 
 $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
 cat 2.expected | cut -c -48 > expout
@@ -4247,12 +4578,13 @@ rm -f 2.expected
 
 # Send DHCPREQUEST with ip4.src set to 10.0.0.6 and ip4.dst set to 10.0.0.4.
 # The packet should not be received by ovn-controller.
+ciaddr=`ip_to_hex 0 0 0 0`
 src_ip=`ip_to_hex 10 0 0 6`
 dst_ip=`ip_to_hex 10 0 0 4`
-test_dhcp 2 f00000000002 03 0 1 $src_ip $dst_ip 1
+test_dhcp 2 f00000000002 03 $ciaddr 0 0 1 $src_ip $dst_ip 1
 
-# NXT_RESUMEs should be 5.
-OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+# NXT_RESUMEs should be 8.
+OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
 
 # vif1-tx.pcap should have received the DHCPv4 request packet
 OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [1.expected])
@@ -4367,15 +4699,23 @@ trim_zeros() {
 # from the "ovs-ofctl monitor br-int resume"
 test_dhcpv6() {
     local inport=$1 src_mac=$2 src_lla=$3 msg_code=$4 offer_ip=$5
-    local request=ffffffffffff${src_mac}86dd00000000002a1101${src_lla}
+    if test $msg_code != 0b; then
+        req_len=2a
+    else
+        req_len=1a
+    fi
+    local request=ffffffffffff${src_mac}86dd0000000000${req_len}1101${src_lla}
     # dst ip ff02::1:2
     request=${request}ff020000000000000000000000010002
     # udp header and dhcpv6 header
-    request=${request}02220223002affff${msg_code}010203
+    request=${request}0222022300${req_len}ffff${msg_code}010203
     # Client identifier
     request=${request}0001000a00030001${src_mac}
-    # IA-NA (Identity Association for Non Temporary Address)
-    request=${request}0003000c0102030400000e1000001518
+    # Add IA-NA (Identity Association for Non Temporary Address) if msg_code
+    # is not 11 (information request packet)
+    if test $msg_code != 0b; then
+        request=${request}0003000c0102030400000e1000001518
+    fi
     shift; shift; shift; shift; shift;
     if test $offer_ip != 0; then
         local server_mac=000000100001
@@ -4506,7 +4846,7 @@ cat 4.expected > expout
 AT_CHECK([cat 4.packets], [0], [expout])
 
 # Send DHCPv6 packet on ls1-lp3. native DHCPv6 works as stateless mode for this port.
-# The DHCPv6 reply should doesn't contian offer_ip.
+# The DHCPv6 reply shouldn't contain offer_ip.
 src_mac=f00000000022
 src_lla=fe80000000000000f20000fffe000022
 reset_pcap_file hv1-vif5 hv1/vif5
@@ -4520,6 +4860,23 @@ $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif5-tx.pcap | trim_zeros > 5.pa
 cat 5.expected | cut -c 1-120,125- > expout
 AT_CHECK([cat 5.packets | cut -c 1-120,125- ], [0], [expout])
 
+# Send DHCPv6 information request (code 11) on ls1-lp3. The DHCPv6 reply
+# shouldn't contain offer_ip
+src_mac=f00000000022
+src_lla=fe80000000000000f20000fffe000022
+reset_pcap_file hv1-vif5 hv1/vif5
+rm -f 5.expected
+test_dhcpv6 5 $src_mac $src_lla 0b 1 5
+
+# NXT_RESUMEs should be 4.
+OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif5-tx.pcap |
+trim_zeros > 5.packets
+# Skipping the UDP checksum
+cat 5.expected | cut -c 1-120,125- > expout
+AT_CHECK([cat 5.packets | cut -c 1-120,125- ], [0], [expout])
+
 as hv1
 OVS_APP_EXIT_AND_WAIT([ovn-controller])
 OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
@@ -5063,7 +5420,7 @@ ovn-nbctl ls-add sw0
 ovn-nbctl lsp-add sw0 p0 -- lsp-set-addresses p0 dynamic
 ovn-nbctl --wait=sb add Logical-Switch sw0 other_config subnet=192.168.1.0/24
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p0 dynamic_addresses], [0],
-    ["0a:00:00:00:00:01 192.168.1.2"
+    ["0a:00:00:a8:01:03 192.168.1.2"
 ])
 
 # Add 9 more ports to sw0, addresses should all be unique.
@@ -5071,31 +5428,31 @@ for n in `seq 1 9`; do
     ovn-nbctl --wait=sb lsp-add sw0 "p$n" -- lsp-set-addresses "p$n" dynamic
 done
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p1 dynamic_addresses], [0],
-    ["0a:00:00:00:00:02 192.168.1.3"
+    ["0a:00:00:a8:01:04 192.168.1.3"
 ])
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p2 dynamic_addresses], [0],
-    ["0a:00:00:00:00:03 192.168.1.4"
+    ["0a:00:00:a8:01:05 192.168.1.4"
 ])
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p3 dynamic_addresses], [0],
-    ["0a:00:00:00:00:04 192.168.1.5"
+    ["0a:00:00:a8:01:06 192.168.1.5"
 ])
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p4 dynamic_addresses], [0],
-    ["0a:00:00:00:00:05 192.168.1.6"
+    ["0a:00:00:a8:01:07 192.168.1.6"
 ])
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p5 dynamic_addresses], [0],
-    ["0a:00:00:00:00:06 192.168.1.7"
+    ["0a:00:00:a8:01:08 192.168.1.7"
 ])
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p6 dynamic_addresses], [0],
-    ["0a:00:00:00:00:07 192.168.1.8"
+    ["0a:00:00:a8:01:09 192.168.1.8"
 ])
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p7 dynamic_addresses], [0],
-    ["0a:00:00:00:00:08 192.168.1.9"
+    ["0a:00:00:a8:01:0a 192.168.1.9"
 ])
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p8 dynamic_addresses], [0],
-    ["0a:00:00:00:00:09 192.168.1.10"
+    ["0a:00:00:a8:01:0b 192.168.1.10"
 ])
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p9 dynamic_addresses], [0],
-    ["0a:00:00:00:00:0a 192.168.1.11"
+    ["0a:00:00:a8:01:0c 192.168.1.11"
 ])
 
 # Trying similar tests with a second switch. MAC addresses should be unique
@@ -5104,98 +5461,98 @@ ovn-nbctl ls-add sw1
 ovn-nbctl lsp-add sw1 p10 -- lsp-set-addresses p10 dynamic
 ovn-nbctl --wait=sb add Logical-Switch sw1 other_config subnet=192.168.1.0/24
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p10 dynamic_addresses], [0],
-     ["0a:00:00:00:00:0b 192.168.1.2"
+     ["0a:00:00:a8:01:0d 192.168.1.2"
 ])
 
 for n in `seq 11 19`; do
     ovn-nbctl --wait=sb lsp-add sw1 "p$n" -- lsp-set-addresses "p$n" dynamic
 done
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p11 dynamic_addresses], [0],
-     ["0a:00:00:00:00:0c 192.168.1.3"
+     ["0a:00:00:a8:01:0e 192.168.1.3"
 ])
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p12 dynamic_addresses], [0],
-     ["0a:00:00:00:00:0d 192.168.1.4"
+     ["0a:00:00:a8:01:0f 192.168.1.4"
 ])
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p13 dynamic_addresses], [0],
-     ["0a:00:00:00:00:0e 192.168.1.5"
+     ["0a:00:00:a8:01:10 192.168.1.5"
 ])
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p14 dynamic_addresses], [0],
-     ["0a:00:00:00:00:0f 192.168.1.6"
+     ["0a:00:00:a8:01:11 192.168.1.6"
 ])
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p15 dynamic_addresses], [0],
-     ["0a:00:00:00:00:10 192.168.1.7"
+     ["0a:00:00:a8:01:12 192.168.1.7"
 ])
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p16 dynamic_addresses], [0],
-     ["0a:00:00:00:00:11 192.168.1.8"
+     ["0a:00:00:a8:01:13 192.168.1.8"
 ])
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p17 dynamic_addresses], [0],
-     ["0a:00:00:00:00:12 192.168.1.9"
+     ["0a:00:00:a8:01:14 192.168.1.9"
 ])
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p18 dynamic_addresses], [0],
-     ["0a:00:00:00:00:13 192.168.1.10"
+     ["0a:00:00:a8:01:15 192.168.1.10"
 ])
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p19 dynamic_addresses], [0],
-     ["0a:00:00:00:00:14 192.168.1.11"
+     ["0a:00:00:a8:01:16 192.168.1.11"
 ])
 
 # Change a port's address to test for multiple ip's for a single address entry
 # and addresses set by the user.
-ovn-nbctl lsp-set-addresses p0 "0a:00:00:00:00:15 192.168.1.12 192.168.1.14"
+ovn-nbctl lsp-set-addresses p0 "0a:00:00:a8:01:17 192.168.1.2 192.168.1.12 192.168.1.14"
 ovn-nbctl --wait=sb lsp-add sw0 p20 -- lsp-set-addresses p20 dynamic
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p20 dynamic_addresses], [0],
-     ["0a:00:00:00:00:16 192.168.1.13"
+     ["0a:00:00:a8:01:18 192.168.1.13"
 ])
 
 # Test for logical router port address management.
 ovn-nbctl create Logical_Router name=R1
 ovn-nbctl -- --id=@lrp create Logical_Router_port name=sw0 \
-network="192.168.1.1/24" mac=\"0a:00:00:00:00:17\" \
+network="192.168.1.1/24" mac=\"0a:00:00:a8:01:19\" \
 -- add Logical_Router R1 ports @lrp -- lsp-add sw0 rp-sw0 \
 -- set Logical_Switch_Port rp-sw0 type=router options:router-port=sw0
 ovn-nbctl --wait=sb lsp-add sw0 p21 -- lsp-set-addresses p21 dynamic
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p21 dynamic_addresses], [0],
-     ["0a:00:00:00:00:18 192.168.1.15"
+     ["0a:00:00:a8:01:1a 192.168.1.15"
 ])
 
 # Test for address reuse after logical port is deleted.
 ovn-nbctl lsp-del p0
 ovn-nbctl --wait=sb lsp-add sw0 p23 -- lsp-set-addresses p23 dynamic
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p23 dynamic_addresses], [0],
-     ["0a:00:00:00:00:19 192.168.1.2"
+     ["0a:00:00:a8:01:03 192.168.1.2"
 ])
 
 # Test for multiple addresses to one logical port.
 ovn-nbctl lsp-add sw0 p25 -- lsp-set-addresses p25 \
-"0a:00:00:00:00:1a 192.168.1.12" "0a:00:00:00:00:1b 192.168.1.14"
+"0a:00:00:a8:01:1b 192.168.1.12" "0a:00:00:a8:01:1c 192.168.1.14"
 ovn-nbctl --wait=sb lsp-add sw0 p26 -- lsp-set-addresses p26 dynamic
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p26 dynamic_addresses], [0],
-     ["0a:00:00:00:00:1c 192.168.1.16"
+     ["0a:00:00:a8:01:17 192.168.1.16"
 ])
 
 # Test for exhausting subnet address space.
 ovn-nbctl ls-add sw2 -- add Logical-Switch sw2 other_config subnet=172.16.1.0/30
 ovn-nbctl --wait=sb lsp-add sw2 p27 -- lsp-set-addresses p27 dynamic
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p27 dynamic_addresses], [0],
-     ["0a:00:00:00:00:1d 172.16.1.2"
+     ["0a:00:00:10:01:03 172.16.1.2"
 ])
 
 ovn-nbctl --wait=sb lsp-add sw2 p28 -- lsp-set-addresses p28 dynamic
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p28 dynamic_addresses], [0],
-     ["0a:00:00:00:00:1e"
+     ["0a:00:00:00:00:01"
 ])
 
 # Test that address management does not add duplicate MAC for lsp/lrp peers.
 ovn-nbctl create Logical_Router name=R2
 ovn-nbctl ls-add sw3
 ovn-nbctl lsp-add sw3 p29 -- lsp-set-addresses p29 \
-"0a:00:00:00:00:1f"
+"0a:00:00:a8:01:18"
 ovn-nbctl -- --id=@lrp create Logical_Router_port name=sw3 \
-network="192.168.2.1/24" mac=\"0a:00:00:00:00:1f\" \
+network="192.168.2.1/24" mac=\"0a:00:00:a8:01:18\" \
 -- add Logical_Router R2 ports @lrp -- lsp-add sw3 rp-sw3 \
 -- set Logical_Switch_Port rp-sw3 type=router options:router-port=sw3
 ovn-nbctl --wait=sb lsp-add sw0 p30 -- lsp-set-addresses p30 dynamic
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p30 dynamic_addresses], [0],
-     ["0a:00:00:00:00:20 192.168.1.17"
+     ["0a:00:00:a8:01:1d 192.168.1.17"
 ])
 
 # Test static MAC address with dynamically allocated IP
@@ -5208,7 +5565,6 @@ AT_CHECK([ovn-nbctl get Logical-Switch-Port p31 dynamic_addresses], [0],
 # Update the static MAC address with dynamically allocated IP and check
 # if the MAC address is updated in 'Logical_Switch_Port.dynamic_adddresses'
 ovn-nbctl --wait=sb lsp-set-addresses p31 "fe:dc:ba:98:76:55 dynamic"
-ovn-nbctl get Logical-Switch-Port p31 dynamic_addresses
 
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p31 dynamic_addresses], [0],
      ["fe:dc:ba:98:76:55 192.168.1.18"
@@ -5216,7 +5572,7 @@ AT_CHECK([ovn-nbctl get Logical-Switch-Port p31 dynamic_addresses], [0],
 
 ovn-nbctl --wait=sb lsp-set-addresses p31 "dynamic"
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p31 dynamic_addresses], [0],
-     ["fe:dc:ba:98:76:55 192.168.1.18"
+     ["0a:00:00:a8:01:1e 192.168.1.18"
 ])
 
 ovn-nbctl --wait=sb lsp-set-addresses p31 "fe:dc:ba:98:76:56 dynamic"
@@ -5233,21 +5589,21 @@ ovn-nbctl --wait=sb lsp-add sw0 p32 -- lsp-set-addresses p32 \
 "dynamic"
 # 192.168.1.20 should be assigned as 192.168.1.19 is excluded.
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p32 dynamic_addresses], [0],
-     ["0a:00:00:00:00:21 192.168.1.20"
+     ["0a:00:00:a8:01:1e 192.168.1.20"
 ])
 
 ovn-nbctl --wait=sb lsp-add sw0 p33 -- lsp-set-addresses p33 \
 "dynamic"
 # 192.168.1.22 should be assigned as 192.168.1.21 is excluded.
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p33 dynamic_addresses], [0],
-     ["0a:00:00:00:00:22 192.168.1.22"
+     ["0a:00:00:a8:01:1f 192.168.1.22"
 ])
 
 ovn-nbctl --wait=sb lsp-add sw0 p34 -- lsp-set-addresses p34 \
 "dynamic"
 # 192.168.1.51 should be assigned as 192.168.1.23-192.168.1.50 is excluded.
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p34 dynamic_addresses], [0],
-     ["0a:00:00:00:00:23 192.168.1.51"
+     ["0a:00:00:a8:01:34 192.168.1.51"
 ])
 
 # Now clear the exclude_ips list. 192.168.1.19 should be assigned.
@@ -5255,7 +5611,7 @@ ovn-nbctl --wait=sb set Logical-switch sw0 other_config:exclude_ips="invalid"
 ovn-nbctl --wait=sb lsp-add sw0 p35 -- lsp-set-addresses p35 \
 "dynamic"
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p35 dynamic_addresses], [0],
-     ["0a:00:00:00:00:24 192.168.1.19"
+     ["0a:00:00:a8:01:20 192.168.1.19"
 ])
 
 # Set invalid data in exclude_ips list. It should be ignored.
@@ -5264,7 +5620,7 @@ ovn-nbctl --wait=sb lsp-add sw0 p36 -- lsp-set-addresses p36 \
 "dynamic"
 # 192.168.1.21 should be assigned as that's the next free one.
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p36 dynamic_addresses], [0],
-     ["0a:00:00:00:00:25 192.168.1.21"
+     ["0a:00:00:a8:01:21 192.168.1.21"
 ])
 
 # Clear the dynamic addresses assignment request.
@@ -5281,16 +5637,17 @@ ovn-nbctl --wait=sb lsp-add sw0 p37 -- lsp-set-addresses p37 \
 # With prefix aef0 and mac 0a:00:00:00:00:26, the dynamic IPv6 should be
 # - aef0::800:ff:fe00:26 (EUI64)
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p37 dynamic_addresses], [0],
-     ["0a:00:00:00:00:26 192.168.1.21 aef0::800:ff:fe00:26"
+     ["0a:00:00:a8:01:21 192.168.1.21 aef0::800:ff:fea8:121"
 ])
 
 ovn-nbctl --wait=sb ls-add sw4
-ovn-nbctl --wait=sb set Logical-switch sw4 other_config:ipv6_prefix="bef0::"
+ovn-nbctl --wait=sb set Logical-switch sw4 other_config:ipv6_prefix="bef0::" \
+-- set Logical-switch sw4 other_config:subnet=192.168.2.0/30
 ovn-nbctl --wait=sb lsp-add sw4 p38 -- lsp-set-addresses p38 \
 "dynamic"
 
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p38 dynamic_addresses], [0],
-     ["0a:00:00:00:00:27 bef0::800:ff:fe00:27"
+     ["0a:00:00:a8:02:03 192.168.2.2 bef0::800:ff:fea8:203"
 ])
 
 ovn-nbctl --wait=sb lsp-add sw4 p39 -- lsp-set-addresses p39 \
@@ -5300,29 +5657,130 @@ AT_CHECK([ovn-nbctl get Logical-Switch-Port p39 dynamic_addresses], [0],
      ["f0:00:00:00:10:12 bef0::f200:ff:fe00:1012"
 ])
 
-# Clear the other_config for sw4. No dynamic ip should be assigned.
-ovn-nbctl --wait=sb clear Logical-switch sw4 other_config
+# Test the case where IPv4 addresses are exhausted and IPv6 prefix is set
+# p40 should not have an IPv4 address since the pool is exhausted
 ovn-nbctl --wait=sb lsp-add sw4 p40 -- lsp-set-addresses p40 \
 "dynamic"
-
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p40 dynamic_addresses], [0],
+         ["0a:00:00:00:00:02 bef0::800:ff:fe00:2"
+])
+
+# Test dynamic changes on switch ports.
+#
+ovn-nbctl --wait=sb ls-add sw5
+ovn-nbctl --wait=sb lsp-add sw5 p41 -- lsp-set-addresses p41 \
+"dynamic"
+# p41 will start with nothing
+AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
          [[[]]
 ])
 
-# Test the case where IPv4 addresses are exhausted and IPv6 prefix is set
-ovn-nbctl --wait=sb set Logical-switch sw4 other_config:subnet=192.168.2.0/30 \
--- set Logical-switch sw4 other_config:ipv6_prefix="bef0::"
+# Set a subnet. Now p41 should have an ipv4 address, too
+ovn-nbctl --wait=sb add Logical-Switch sw5 other_config subnet=192.168.1.0/24
+AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
+         ["0a:00:00:a8:01:22 192.168.1.2"
+])
 
-# Now p40 should be assigned with dynamic addresses.
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p40 dynamic_addresses], [0],
-         ["0a:00:00:00:00:28 192.168.2.2 bef0::800:ff:fe00:28"
+# Clear the other_config. The IPv4 address should be gone
+ovn-nbctl --wait=sb clear Logical-Switch sw5 other_config
+AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
+         [[[]]
 ])
 
-ovn-nbctl --wait=sb lsp-add sw4 p41 -- lsp-set-addresses p41 \
-"dynamic"
-# p41 should not have IPv4 address (as the pool is exhausted).
+# Set an IPv6 prefix. Now p41 should have an IPv6 address.
+ovn-nbctl --wait=sb set Logical-Switch sw5 other_config:ipv6_prefix="aef0::"
+AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
+         ["0a:00:00:00:00:03 aef0::800:ff:fe00:3"
+])
+
+# Change the MAC address to a static one. The IPv6 address should update.
+ovn-nbctl --wait=sb lsp-set-addresses p41 "f0:00:00:00:10:2b dynamic"
+AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
+         ["f0:00:00:00:10:2b aef0::f200:ff:fe00:102b"
+])
+
+# Change the IPv6 prefix. The IPv6 address should update.
+ovn-nbctl --wait=sb set Logical-Switch sw5 other_config:ipv6_prefix="bef0::"
+AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
+         ["f0:00:00:00:10:2b bef0::f200:ff:fe00:102b"
+])
+
+# Clear the other_config. The IPv6 address should be gone
+ovn-nbctl --wait=sb clear Logical-Switch sw5 other_config
 AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
-         ["0a:00:00:00:00:29 bef0::800:ff:fe00:29"
+         [[[]]
+])
+
+# Set the subnet again. Now p41 should get the IPv4 address again.
+ovn-nbctl --wait=sb add Logical-Switch sw5 other_config subnet=192.168.1.0/24
+AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
+         ["f0:00:00:00:10:2b 192.168.1.2"
+])
+
+# Add an excluded IP address that conflicts with p41. p41 should update.
+ovn-nbctl --wait=sb add Logical-Switch sw5 other_config \
+exclude_ips="192.168.1.2"
+AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
+         ["f0:00:00:00:10:2b 192.168.1.3"
+])
+
+# Add static ip address
+ovn-nbctl --wait=sb lsp-set-addresses p41 "dynamic 192.168.1.100"
+ovn-nbctl list Logical-Switch-Port p41
+ovn-nbctl --wait=sb lsp-add sw5 p42 -- lsp-set-addresses p42 \
+"dynamic 192.168.1.101"
+AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
+         ["0a:00:00:a8:01:65 192.168.1.100"
+])
+AT_CHECK([ovn-nbctl get Logical-Switch-Port p42 dynamic_addresses], [0],
+         ["0a:00:00:a8:01:66 192.168.1.101"
+])
+
+# define a mac address prefix
+ovn-nbctl ls-add sw6
+ovn-nbctl --wait=hv set NB_Global . options:mac_prefix="00:11:22:33:44:55"
+ovn-nbctl --wait=sb set Logical-Switch sw6 other_config:subnet=192.168.100.0/24
+for n in $(seq 1 3); do
+    ovn-nbctl --wait=sb lsp-add sw6 "p5$n" -- lsp-set-addresses "p5$n" dynamic
+done
+AT_CHECK([ovn-nbctl get Logical-Switch-Port p51 dynamic_addresses], [0],
+    ["00:11:22:a8:64:03 192.168.100.2"
+])
+AT_CHECK([ovn-nbctl get Logical-Switch-Port p52 dynamic_addresses], [0],
+    ["00:11:22:a8:64:04 192.168.100.3"
+])
+AT_CHECK([ovn-nbctl get Logical-Switch-Port p53 dynamic_addresses], [0],
+    ["00:11:22:a8:64:05 192.168.100.4"
+])
+
+# verify configuration order does not break IPAM/MACAM
+ovn-nbctl ls-add sw7
+for n in $(seq 1 3); do
+    ovn-nbctl --wait=sb lsp-add sw7 "p7$n" -- lsp-set-addresses "p7$n" dynamic
+done
+ovn-nbctl --wait=sb set Logical-Switch sw7 other_config:ipv6_prefix="bef0::"
+p71_addr=$(ovn-nbctl get Logical-Switch-Port p71 dynamic_addresses)
+p72_addr=$(ovn-nbctl get Logical-Switch-Port p72 dynamic_addresses)
+p73_addr=$(ovn-nbctl get Logical-Switch-Port p73 dynamic_addresses)
+AT_CHECK([test "$p71_addr" != "$p72_addr"], [0], [])
+AT_CHECK([test "$p71_addr" != "$p73_addr"], [0], [])
+AT_CHECK([test "$p72_addr" != "$p73_addr"], [0], [])
+
+# request to assign mac only
+#
+ovn-nbctl ls-add sw8
+ovn-nbctl --wait=sb set Logical-Switch sw8 other_config:mac_only=true
+for n in $(seq 1 3); do
+    ovn-nbctl --wait=sb lsp-add sw8 "p8$n" -- lsp-set-addresses "p8$n" dynamic
+done
+AT_CHECK([ovn-nbctl get Logical-Switch-Port p81 dynamic_addresses], [0],
+    ["00:11:22:00:00:06"
+])
+AT_CHECK([ovn-nbctl get Logical-Switch-Port p82 dynamic_addresses], [0],
+    ["00:11:22:00:00:07"
+])
+AT_CHECK([ovn-nbctl get Logical-Switch-Port p83 dynamic_addresses], [0],
+    ["00:11:22:00:00:08"
 ])
 
 as ovn-sb
@@ -5360,17 +5818,17 @@ ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice type=router
 # Create logical port foo1 in foo
 ovn-nbctl --wait=sb lsp-add foo foo1 \
 -- lsp-set-addresses foo1 "dynamic"
-AT_CHECK([ovn-nbctl --timeout=10 wait-until Logical-Switch-Port foo1 dynamic_addresses='"0a:00:00:00:00:01 192.168.1.2"'], [0])
+AT_CHECK([ovn-nbctl --timeout=10 wait-until Logical-Switch-Port foo1 dynamic_addresses='"0a:00:00:a8:01:03 192.168.1.2"'], [0])
 
 # Create logical port alice1 in alice
 ovn-nbctl --wait=sb lsp-add alice alice1 \
 -- lsp-set-addresses alice1 "dynamic"
-AT_CHECK([ovn-nbctl --timeout=10 wait-until Logical-Switch-Port alice1 dynamic_addresses='"0a:00:00:00:00:02 192.168.2.2"'])
+AT_CHECK([ovn-nbctl --timeout=10 wait-until Logical-Switch-Port alice1 dynamic_addresses='"0a:00:00:a8:02:03 192.168.2.2"'])
 
 # Create logical port foo2 in foo
 ovn-nbctl --wait=sb lsp-add foo foo2 \
 -- lsp-set-addresses foo2 "dynamic"
-AT_CHECK([ovn-nbctl --timeout=10 wait-until Logical-Switch-Port foo2 dynamic_addresses='"0a:00:00:00:00:03 192.168.1.3"'])
+AT_CHECK([ovn-nbctl --timeout=10 wait-until Logical-Switch-Port foo2 dynamic_addresses='"0a:00:00:a8:01:04 192.168.1.3"'])
 
 # Create a hypervisor and create OVS ports corresponding to logical ports.
 net_add n1
@@ -5406,15 +5864,15 @@ ip_to_hex() {
 }
 
 # Send ip packets between foo1 and foo2
-src_mac="0a0000000001"
-dst_mac="0a0000000003"
+src_mac="0a0000a80103"
+dst_mac="0a0000a80104"
 src_ip=`ip_to_hex 192 168 1 2`
 dst_ip=`ip_to_hex 192 168 1 3`
 packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
 as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
 
 # Send ip packets between foo1 and alice1
-src_mac="0a0000000001"
+src_mac="0a0000a80103"
 dst_mac="000000010203"
 src_ip=`ip_to_hex 192 168 1 2`
 dst_ip=`ip_to_hex 192 168 2 2`
@@ -5439,8 +5897,8 @@ echo "------ hv1 dump ----------"
 as hv1 ovs-ofctl dump-flows br-int
 
 # Packet to Expect at foo2
-src_mac="0a0000000001"
-dst_mac="0a0000000003"
+src_mac="0a0000a80103"
+dst_mac="0a0000a80104"
 src_ip=`ip_to_hex 192 168 1 2`
 dst_ip=`ip_to_hex 192 168 1 3`
 expected=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
@@ -5451,7 +5909,7 @@ AT_CHECK([cat received1.packets], [0], [expout])
 
 # Packet to Expect at alice1
 src_mac="000000010204"
-dst_mac="0a0000000002"
+dst_mac="0a0000a80203"
 src_ip=`ip_to_hex 192 168 1 2`
 dst_ip=`ip_to_hex 192 168 2 2`
 expected=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000
@@ -6002,7 +6460,7 @@ AT_CHECK([ovn-nbctl lsp-add lsw0 localvif1])
 AT_CHECK([ovn-nbctl lsp-set-addresses localvif1 "f0:00:00:00:00:01 192.168.1.1"])
 AT_CHECK([ovn-nbctl lsp-set-port-security localvif1 "f0:00:00:00:00:01"])
 AT_CHECK([ovn-nbctl lsp-add lsw0 localvif2])
-AT_CHECK([ovn-nbctl lsp-set-addresses localvif2 "f0:00:00:00:00:01 192.168.1.2"])
+AT_CHECK([ovn-nbctl lsp-set-addresses localvif2 "f0:00:00:00:00:02 192.168.1.2"])
 AT_CHECK([ovn-nbctl lsp-set-port-security localvif2 "f0:00:00:00:00:02"])
 AT_CHECK([ovn-nbctl lsp-add lsw0 localvif3])
 AT_CHECK([ovn-nbctl lsp-set-addresses localvif3 "f0:00:00:00:00:03 192.168.1.3"])
@@ -6153,6 +6611,88 @@ OVN_CLEANUP([hv])
 AT_CLEANUP
 
 
+AT_SETUP([ovn -- ACL rate-limited logging])
+AT_KEYWORDS([ovn])
+ovn_start
+
+net_add n1
+
+sim_add hv
+as hv
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+for i in lp1 lp2; do
+    ovs-vsctl -- add-port br-int $i -- \
+        set interface $i external-ids:iface-id=$i \
+        options:tx_pcap=hv/$i-tx.pcap \
+        options:rxq_pcap=hv/$i-rx.pcap
+done
+
+lp1_mac="f0:00:00:00:00:01"
+lp1_ip="192.168.1.2"
+
+lp2_mac="f0:00:00:00:00:02"
+lp2_ip="192.168.1.3"
+
+ovn-nbctl ls-add lsw0
+ovn-nbctl --wait=sb lsp-add lsw0 lp1
+ovn-nbctl --wait=sb lsp-add lsw0 lp2
+ovn-nbctl lsp-set-addresses lp1 $lp1_mac
+ovn-nbctl lsp-set-addresses lp2 $lp2_mac
+ovn-nbctl --wait=sb sync
+
+
+# Add an ACL that rate-limits logs at 10 per second.
+ovn-nbctl meter-add http-rl1 drop 10 pktps
+ovn-nbctl --log --severity=alert --meter=http-rl1 --name=http-acl1 acl-add lsw0 to-lport 1000 'tcp.dst==80' drop
+
+# Add an ACL that rate-limits logs at 5 per second.
+ovn-nbctl meter-add http-rl2 drop 5 pktps
+ovn-nbctl --log --severity=alert --meter=http-rl2 --name=http-acl2 acl-add lsw0 to-lport 1000 'tcp.dst==81' allow
+
+# Add an ACL that doesn't rate-limit logs.
+ovn-nbctl --log --severity=alert --name=http-acl3 acl-add lsw0 to-lport 1000 'tcp.dst==82' drop
+
+
+# For each ACL, send 100 packets.
+for i in `seq 1 100`; do
+    ovs-appctl netdev-dummy/receive lp1 'in_port(1),eth(src=f0:00:00:00:00:01,dst=f0:00:00:00:00:02),eth_type(0x0800),ipv4(src=192.168.1.2,dst=192.168.1.3,proto=6,tos=0,ttl=64,frag=no),tcp(src=7777,dst=80)'
+
+    ovs-appctl netdev-dummy/receive lp1 'in_port(1),eth(src=f0:00:00:00:00:01,dst=f0:00:00:00:00:02),eth_type(0x0800),ipv4(src=192.168.1.2,dst=192.168.1.3,proto=6,tos=0,ttl=64,frag=no),tcp(src=7777,dst=81)'
+
+    ovs-appctl netdev-dummy/receive lp1 'in_port(1),eth(src=f0:00:00:00:00:01,dst=f0:00:00:00:00:02),eth_type(0x0800),ipv4(src=192.168.1.2,dst=192.168.1.3,proto=6,tos=0,ttl=64,frag=no),tcp(src=7777,dst=82)'
+done
+
+# The rate at which packets are sent is highly system-dependent, so we
+# can't count on precise drop counts.  To work around that, we just
+# check that exactly 100 "http-acl3" actions were logged and that there
+# were more "http-acl1" actions than "http-acl2" ones.
+OVS_WAIT_UNTIL([ test 100 = $(grep -c 'http-acl3' hv/ovn-controller.log) ])
+
+# On particularly slow or overloaded systems, the transmission rate may
+# be lower than the configured meter rate.  To prevent false test
+# failures, we check the duration count of the meter, and if it's
+# greater than nine seconds, just skip the test.
+d_secs=$(as hv ovs-ofctl -O OpenFlow13 meter-stats br-int | grep "meter:1" | sed 's/.* duration:\([[0-9]]\{1,\}\)\.[[0-9]]\+s .*/\1/')
+
+echo "Meter duration: $d_secs"
+AT_SKIP_IF([test $d_secs -gt 9])
+
+# Print some information that may help debugging.
+as hv ovs-appctl -t ovn-controller meter-table-list
+as hv ovs-ofctl -O OpenFlow13 meter-stats br-int
+
+n_acl1=$(grep -c 'http-acl1' hv/ovn-controller.log)
+n_acl2=$(grep -c 'http-acl2' hv/ovn-controller.log)
+n_acl3=$(grep -c 'http-acl3' hv/ovn-controller.log)
+
+AT_CHECK([ test $n_acl3 -gt $n_acl1 ], [0], [])
+AT_CHECK([ test $n_acl1 -gt $n_acl2 ], [0], [])
+
+OVN_CLEANUP([hv])
+AT_CLEANUP
+
+
 AT_SETUP([ovn -- DSCP marking and meter check])
 AT_KEYWORDS([ovn])
 ovn_start
@@ -6667,6 +7207,17 @@ packet=${dst_mac}${src_mac}8100000208004500001c000000003f110100${src_ip}${dst_ip
 echo  $packet >> expected1
 OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1])
 
+# Send broadcast packet from foo1. foo1 should not receive the same packet.
+src_mac="f00000010205"
+dst_mac="ffffffffffff"
+src_ip=`ip_to_hex 192 168 1 2`
+dst_ip=`ip_to_hex 255 255 255 255`
+packet=${dst_mac}${src_mac}8100000108004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
+as hv1 ovs-appctl netdev-dummy/receive vm1 $packet
+
+# expected packet at VM1
+OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1])
+
 OVN_CLEANUP([hv1],[hv2])
 
 AT_CLEANUP
@@ -7867,9 +8418,9 @@ AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=32 | grep =0x3,metadata=0x1 |
 AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=32 | grep =0x3,metadata=0x1 | wc -l], [0], [0
 ])
 # Check that arp reply on distributed gateway port is only programmed on hv2
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep arp | grep =0x2,metadata=0x1 | wc -l], [0], [0
+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep arp | grep load:0x2- | grep =0x2,metadata=0x1 | wc -l], [0], [0
 ])
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep arp | grep =0x2,metadata=0x1 | wc -l], [0], [1
+AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep arp | grep load:0x2- | grep =0x2,metadata=0x1 | wc -l], [0], [1
 ])
 
 
@@ -7947,34 +8498,11 @@ src_ip=`ip_to_hex 192 168 1 2`
 dst_ip=`ip_to_hex 172 16 1 3`
 packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
 
-# ARP request packet to expect at outside1
-src_mac="000002010203"
-src_ip=`ip_to_hex 172 16 1 1`
-arp_request=ffffffffffff${src_mac}08060001080006040001${src_mac}${src_ip}000000000000${dst_ip}
-
-as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-
-echo $arp_request >> hv3-vif1.expected
-OVN_CHECK_PACKETS([hv3/vif1-tx.pcap], [hv3-vif1.expected])
-
-# Send ARP reply from outside1 back to the router
-reply_mac="f00000010204"
-arp_reply=${src_mac}${reply_mac}08060001080006040002${reply_mac}${dst_ip}${src_mac}${src_ip}
-
-as hv3 ovs-appctl netdev-dummy/receive hv3-vif1 $arp_reply
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
 # Packet to Expect at outside1
 src_mac="000002010203"
 dst_mac="f00000010204"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 172 16 1 3`
 expected=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000
 
-# Resend packet from foo1 to outside1
 as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
 
 echo "------ hv1 dump ----------"
@@ -8189,6 +8717,267 @@ OVN_CLEANUP([hv1],[hv2],[hv3])
 
 AT_CLEANUP
 
+# VLAN traffic for external network redirected through distributed router
+# gateway port should use vlans(i.e input network vlan tag) across hypervisors
+# instead of tunneling.
+AT_SETUP([ovn -- vlan traffic for external network with distributed router gateway port])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+ovn_start
+
+# Logical network:
+# # One LR R1 that has switches foo (192.168.1.0/24) and
+# # alice (172.16.1.0/24) connected to it.  The logical port
+# # between R1 and alice has a "redirect-chassis" specified,
+# # i.e. it is the distributed router gateway port(172.16.1.6).
+# # Switch alice also has a localnet port defined.
+# # An additional switch outside has the same subnet as alice
+# # (172.16.1.0/24), a localnet port and nexthop port(172.16.1.1)
+# # which will receive the packet destined for external network
+# # (i.e 8.8.8.8 as destination ip).
+
+# Physical network:
+# # Three hypervisors hv[123].
+# # hv1 hosts vif foo1.
+# # hv2 is the "redirect-chassis" that hosts the distributed router gateway port.
+# # hv3 hosts nexthop port vif outside1.
+# # All other tests connect hypervisors to network n1 through br-phys for tunneling.
+# # But in this test, hv1 won't connect to n1(and no br-phys in hv1), and
+# # in order to show vlans(instead of tunneling) used between hv1 and hv2,
+# # a new network n2 created and hv1 and hv2 connected to this network through br-ex.
+# # hv2 and hv3 are still connected to n1 network through br-phys.
+net_add n1
+
+# We are not calling ovn_attach for hv1, to avoid adding br-phys.
+# Tunneling won't work in hv1 as ovn-encap-ip is not added to any bridge in hv1
+sim_add hv1
+as hv1
+ovs-vsctl \
+    -- set Open_vSwitch . external-ids:system-id=hv1 \
+    -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
+    -- set Open_vSwitch . external-ids:ovn-encap-type=geneve,vxlan \
+    -- set Open_vSwitch . external-ids:ovn-encap-ip=192.168.0.1 \
+    -- add-br br-int \
+    -- set bridge br-int fail-mode=secure other-config:disable-in-band=true \
+    -- set Open_vSwitch . external-ids:ovn-bridge-mappings=public:br-ex
+
+start_daemon ovn-controller
+ovs-vsctl -- add-port br-int hv1-vif1 -- \
+    set interface hv1-vif1 external-ids:iface-id=foo1 \
+    ofport-request=1
+
+sim_add hv2
+as hv2
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.2
+ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings="public:br-ex,phys:br-phys"
+
+sim_add hv3
+as hv3
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.3
+ovs-vsctl -- add-port br-int hv3-vif1 -- \
+    set interface hv3-vif1 external-ids:iface-id=outside1 \
+    options:tx_pcap=hv3/vif1-tx.pcap \
+    options:rxq_pcap=hv3/vif1-rx.pcap \
+    ofport-request=1
+ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings="phys:br-phys"
+
+# Create network n2 for vlan connectivity between hv1 and hv2
+net_add n2
+
+as hv1
+ovs-vsctl add-br br-ex
+net_attach n2 br-ex
+
+as hv2
+ovs-vsctl add-br br-ex
+net_attach n2 br-ex
+
+OVN_POPULATE_ARP
+
+ovn-nbctl create Logical_Router name=R1
+
+ovn-nbctl ls-add foo
+ovn-nbctl ls-add alice
+ovn-nbctl ls-add outside
+
+# Connect foo to R1
+ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
+ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
+    type=router options:router-port=foo \
+    -- lsp-set-addresses rp-foo router
+
+# Connect alice to R1 as distributed router gateway port (172.16.1.6) on hv2
+ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.6/24 \
+    -- set Logical_Router_Port alice options:redirect-chassis="hv2"
+ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
+    type=router options:router-port=alice \
+    -- lsp-set-addresses rp-alice router \
+
+# Create logical port foo1 in foo
+ovn-nbctl lsp-add foo foo1 \
+-- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
+
+# Create logical port outside1 in outside, which is a nexthop address
+# for 172.16.1.0/24
+ovn-nbctl lsp-add outside outside1 \
+-- lsp-set-addresses outside1 "f0:00:00:01:02:04 172.16.1.1"
+
+# Set default gateway (nexthop) to 172.16.1.1
+ovn-nbctl lr-route-add R1 "0.0.0.0/0" 172.16.1.1 alice
+AT_CHECK([ovn-nbctl lr-nat-add R1 snat 172.16.1.6 192.168.1.1/24])
+ovn-nbctl set Logical_Switch_Port rp-alice options:nat-addresses=router
+
+ovn-nbctl lsp-add foo ln-foo
+ovn-nbctl lsp-set-addresses ln-foo unknown
+ovn-nbctl lsp-set-options ln-foo network_name=public
+ovn-nbctl lsp-set-type ln-foo localnet
+AT_CHECK([ovn-nbctl set Logical_Switch_Port ln-foo tag=2])
+
+# Create localnet port in alice
+ovn-nbctl lsp-add alice ln-alice
+ovn-nbctl lsp-set-addresses ln-alice unknown
+ovn-nbctl lsp-set-type ln-alice localnet
+ovn-nbctl lsp-set-options ln-alice network_name=phys
+
+# Create localnet port in outside
+ovn-nbctl lsp-add outside ln-outside
+ovn-nbctl lsp-set-addresses ln-outside unknown
+ovn-nbctl lsp-set-type ln-outside localnet
+ovn-nbctl lsp-set-options ln-outside network_name=phys
+
+# Allow some time for ovn-northd and ovn-controller to catch up.
+# XXX This should be more systematic.
+ovn-nbctl --wait=hv --timeout=3 sync
+
+# Check that there is a logical flow in logical switch foo's pipeline
+# to set the outport to rp-foo (which is expected).
+OVS_WAIT_UNTIL([test 1 = `ovn-sbctl dump-flows foo | grep ls_in_l2_lkup | \
+grep rp-foo | grep -v is_chassis_resident | wc -l`])
+
+# Set the option 'reside-on-redirect-chassis' for foo
+ovn-nbctl set logical_router_port foo options:reside-on-redirect-chassis=true
+# Check that there is a logical flow in logical switch foo's pipeline
+# to set the outport to rp-foo with the condition is_chassis_redirect.
+ovn-sbctl dump-flows foo
+OVS_WAIT_UNTIL([test 1 = `ovn-sbctl dump-flows foo | grep ls_in_l2_lkup | \
+grep rp-foo | grep is_chassis_resident | wc -l`])
+
+echo "---------NB dump-----"
+ovn-nbctl show
+echo "---------------------"
+ovn-nbctl list logical_router
+echo "---------------------"
+ovn-nbctl list nat
+echo "---------------------"
+ovn-nbctl list logical_router_port
+echo "---------------------"
+
+echo "---------SB dump-----"
+ovn-sbctl list datapath_binding
+echo "---------------------"
+ovn-sbctl list port_binding
+echo "---------------------"
+ovn-sbctl dump-flows
+echo "---------------------"
+ovn-sbctl list chassis
+echo "---------------------"
+
+for chassis in hv1 hv2 hv3; do
+    as $chassis
+    echo "------ $chassis dump ----------"
+    ovs-vsctl show br-int
+    ovs-ofctl show br-int
+    ovs-ofctl dump-flows br-int
+    echo "--------------------------"
+done
+
+ip_to_hex() {
+    printf "%02x%02x%02x%02x" "$@"
+}
+
+foo1_ip=$(ip_to_hex 192 168 1 2)
+gw_ip=$(ip_to_hex 172 16 1 6)
+dst_ip=$(ip_to_hex 8 8 8 8)
+nexthop_ip=$(ip_to_hex 172 16 1 1)
+
+foo1_mac="f00000010203"
+foo_mac="000001010203"
+gw_mac="000002010203"
+nexthop_mac="f00000010204"
+
+# Send ip packet from foo1 to 8.8.8.8
+src_mac="f00000010203"
+dst_mac="000001010203"
+packet=${foo_mac}${foo1_mac}08004500001c0000000040110000${foo1_ip}${dst_ip}0035111100080000
+
+# Wait for GARPs announcing gw IP to arrive
+OVS_WAIT_UNTIL([
+    test `as hv2 ovs-ofctl dump-flows br-int | grep table=66 | \
+grep actions=mod_dl_dst:f0:00:00:01:02:04 | wc -l` -eq 1
+    ])
+
+# VLAN tagged packet with router port(192.168.1.1) MAC as destination MAC
+# is expected on bridge connecting hv1 and hv2
+expected=${foo_mac}${foo1_mac}8100000208004500001c0000000040110000${foo1_ip}${dst_ip}0035111100080000
+echo $expected > hv1-br-ex_n2.expected
+
+# Packet to Expect at outside1 i.e nexthop(172.16.1.1) port.
+# As connection tracking not enabled for this test, snat can't be done on the packet.
+# We still see foo1 as the source ip address. But source mac(gateway MAC) and
+# dest mac(nexthop mac) are properly configured.
+expected=${nexthop_mac}${gw_mac}08004500001c000000003f110100${foo1_ip}${dst_ip}0035111100080000
+echo $expected > hv3-vif1.expected
+
+reset_pcap_file() {
+    local iface=$1
+    local pcap_file=$2
+    ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
+options:rxq_pcap=dummy-rx.pcap
+    rm -f ${pcap_file}*.pcap
+    ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
+options:rxq_pcap=${pcap_file}-rx.pcap
+}
+
+as hv1 reset_pcap_file br-ex_n2 hv1/br-ex_n2
+as hv3 reset_pcap_file hv3-vif1 hv3/vif1
+sleep 2
+# Take note of how many packets arrived on the VLAN switch before generating
+# further traffic
+n_packets=`as hv1 ovs-ofctl dump-flows br-int table=65 | grep "priority=100,reg15=0x1,metadata=0x2" | grep actions=clone | sed 's/.*n_packets=\([[0-9]]*\),.*/\1/'`
+as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
+sleep 2
+
+# On hv1, the packet should not go from vlan switch pipleline to router
+# pipeline
+as hv1 ovs-ofctl dump-flows br-int
+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=65 | grep "priority=100,reg15=0x1,metadata=0x2" \
+| grep actions=clone | grep -v n_packets=$n_packets | wc -l], [0], [[0
+]])
+
+# On hv1, table 32 check that no packet goes via the tunnel port
+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=32 \
+| grep "NXM_NX_TUN_ID" | grep -v n_packets=0 | wc -l], [0], [[0
+]])
+
+ip_packet() {
+    grep "1010203f00000010203"
+}
+
+# Check vlan tagged packet on the bridge connecting hv1 and hv2 with the
+# foo1's mac.
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-ex_n2-tx.pcap | ip_packet | uniq > hv1-br-ex_n2
+cat hv1-br-ex_n2.expected > expout
+AT_CHECK([sort hv1-br-ex_n2], [0], [expout])
+
+# Check expected packet on nexthop interface
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv3/vif1-tx.pcap | grep ${foo1_ip}${dst_ip} | uniq > hv3-vif1
+cat hv3-vif1.expected > expout
+AT_CHECK([sort hv3-vif1], [0], [expout])
+
+OVN_CLEANUP([hv1],[hv2],[hv3])
+AT_CLEANUP
+
 AT_SETUP([ovn -- IPv6 ND Router Solicitation responder])
 AT_KEYWORDS([ovn-nd_ra])
 AT_SKIP_IF([test $HAVE_PYTHON = no])
@@ -8522,8 +9311,6 @@ packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111
 as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
 OVS_WAIT_UNTIL([test `ovn-sbctl find mac_binding ip="10.0.0.2" | wc -l` -gt 0])
 ovn-nbctl --wait=hv sync
-# Send the second packet to reach the destination.
-as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
 
 # Packet to Expect at 'alice1'
 src_mac="000000010204"
@@ -8761,7 +9548,7 @@ for chassis in gw1 gw2 hv1 hv2; do
     ovs-ofctl dump-flows br-int
     echo "--------------------------"
 done
-function bfd_dump() {
+bfd_dump() {
     for chassis in gw1 gw2 hv1 hv2; do
         as $chassis
         echo "------ $chassis dump (BFD)----"
@@ -8879,7 +9666,7 @@ for chassis in gw1 gw2; do
 done
 # make sure BFD is not enabled to hv2, we don't need it
 AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-hv2-0],[0],
-         [[enable=false
+         [[
 ]])
 
 
@@ -8893,7 +9680,7 @@ for chassis in gw1 gw2; do
 done
 # make sure BFD is not enabled to hv1, we don't need it
 AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-hv1-0],[0],
-         [[enable=false
+         [[
 ]])
 
 sleep 3  # let BFD sessions settle so we get the right flows on the right chassis
@@ -8923,6 +9710,33 @@ AT_CHECK([ovn-sbctl --columns chassis --bare find Port_Binding logical_port=cr-o
          [0],[[1
 ]])
 
+ovn-nbctl --wait=hv set NB_Global . options:"bfd-min-rx"=2000
+as gw2
+for chassis in gw1 hv1 hv2; do
+    echo "checking gw2 -> $chassis"
+    OVS_WAIT_UNTIL([
+    bfd_cfg=$(ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0)
+    test "$bfd_cfg" = "enable=true min_rx=2000"
+])
+done
+ovn-nbctl --wait=hv set NB_Global . options:"bfd-min-tx"=1500
+for chassis in gw1 hv1 hv2; do
+    echo "checking gw2 -> $chassis"
+    OVS_WAIT_UNTIL([
+    bfd_cfg=$(ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0)
+    test "$bfd_cfg" = "enable=true min_rx=2000 min_tx=1500"
+])
+done
+ovn-nbctl remove NB_Global . options "bfd-min-rx"
+ovn-nbctl --wait=hv set NB_Global . options:"bfd-mult"=5
+for chassis in gw1 hv1 hv2; do
+    echo "checking gw2 -> $chassis"
+    OVS_WAIT_UNTIL([
+    bfd_cfg=$(ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0)
+    test "$bfd_cfg" = "enable=true min_tx=1500 mult=5"
+])
+done
+
 OVN_CLEANUP([gw1],[gw2],[hv1],[hv2])
 
 AT_CLEANUP
@@ -9185,7 +9999,7 @@ ovn-nbctl lr-add lr0_ip6
 ovn-nbctl lrp-add lr0_ip6 lrp0_ip6 00:00:00:00:af:01 aef0:0:0:0:0:0:0:0/64
 ovn-nbctl lsp-add sw0_ip6 lrp0_ip6-attachment
 ovn-nbctl lsp-set-type lrp0_ip6-attachment router
-ovn-nbctl lsp-set-addresses lrp0_ip6-attachment 00:00:00:00:af:01
+ovn-nbctl lsp-set-addresses lrp0_ip6-attachment router
 ovn-nbctl lsp-set-options lrp0_ip6-attachment router-port=lrp0_ip6
 ovn-nbctl set logical_router_port lrp0_ip6 ipv6_ra_configs:address_mode=slaac
 
@@ -9199,6 +10013,10 @@ ovn-nbctl lrp-add lr0_ip6 ip6_public 00:00:02:01:02:04 \
 2001:db8:1:0:200:02ff:fe01:0204/64 \
 -- set Logical_Router_port ip6_public options:redirect-chassis="hv1"
 
+#install static route
+ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
+ip_prefix="\:\:/0" nexthop="2001\:db8\:1\:0\:200\:02ff\:fe01\:1305" \
+-- add Logical_Router lr0_ip6 static_routes @lrt
 
 ovn-nbctl lsp-add public rp-ip6_public -- set Logical_Switch_Port \
 rp-ip6_public  type=router options:router-port=ip6_public \
@@ -9218,6 +10036,19 @@ ovs-vsctl -- add-port br-int hv1-vif1 -- \
 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
 
 OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw0_ip6-port1` = xup])
+
+# There should be 2 Neighbor Advertisement flows for the router port
+# aef0:: ip address in logical switch pipeline with action nd_na_router.
+AT_CHECK([ovn-sbctl dump-flows sw0_ip6 | grep ls_in_arp_rsp | \
+grep "nd_na_router" | wc -l], [0], [2
+])
+
+# There should be 4 Neighbor Advertisement flows with action nd_na_router
+# in the router pipeline for the router lr0_ip6.
+AT_CHECK([ovn-sbctl dump-flows lr0_ip6 | grep nd_na_router | \
+wc -l], [0], [4
+])
+
 cr_uuid=`ovn-sbctl find port_binding logical_port=cr-ip6_public | grep _uuid | cut -f2 -d ":"`
 
 # There is only one chassis.
@@ -9228,36 +10059,82 @@ trim_zeros() {
     sed 's/\(00\)\{1,\}$//'
 }
 
+reset_pcap_file() {
+    local iface=$1
+    local pcap_file=$2
+    ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
+options:rxq_pcap=dummy-rx.pcap
+    rm -f ${pcap_file}*.pcap
+    ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
+options:rxq_pcap=${pcap_file}-rx.pcap
+}
+
 # Test the IPv6 Neighbor Solicitation (NS) - nd_ns action for unknown MAC
 # addresses. ovn-controller should generate an IPv6 NS request for IPv6
 # packets whose MAC is unknown (in the ARP_REQUEST router pipeline stage.
 # test_ipv6 INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT...
 # This function sends ipv6 packet
 test_ipv6() {
-    local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4
-    dst_ip=20010db800010000020002fffe010205
+    local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5
+    local dst_mcast_mac=$6 mcast_node_ip=$7 nd_target=$8
 
     local packet=${dst_mac}${src_mac}86dd6000000000083aff${src_ip}${dst_ip}
     packet=${packet}8000000000000000
-    shift; shift; shift; shift
 
-    dst_mac=3333ff010205
     src_mac=000002010204
-    mcast_node_ip=ff0200000000000000000001ff010205
-    expected_packet=${dst_mac}${src_mac}86dd6000000000203aff${src_ip}
-    expected_packet=${expected_packet}${mcast_node_ip}8700XXXX00000000${dst_ip}
-    expected_packet=${expected_packet}0101${src_mac}
+    expected_packet=${dst_mcast_mac}${src_mac}86dd6000000000203aff${src_ip}
+    expected_packet=${expected_packet}${mcast_node_ip}8700XXXX00000000
+    expected_packet=${expected_packet}${nd_target}0101${src_mac}
 
     as hv1 ovs-appctl netdev-dummy/receive hv1-vif${inport} $packet
+    rm -f ipv6_ns.expected
     echo $expected_packet >> ipv6_ns.expected
 }
 
 src_mac=506400000002
 dst_mac=00000000af01
 src_ip=aef0000000000000526400fffe000002
+dst_ip=20010db800010000020002fffe010205
+dst_mcast_mac=3333ff010205
+mcast_node_ip=ff0200000000000000000001ff010205
+nd_target=20010db800010000020002fffe010205
 # Send an IPv6 packet. Generated IPv6 Neighbor solicitation packet
 # should be received by the ports attached to br-phys.
-test_ipv6 1 $src_mac $dst_mac $src_ip 2
+test_ipv6 1 $src_mac $dst_mac $src_ip $dst_ip $dst_mcast_mac \
+$mcast_node_ip $nd_target
+
+OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys_n1-tx.pcap | cut -d " " -f1)])
+OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys-tx.pcap | cut -d " " -f1)])
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-phys_n1-tx.pcap | \
+trim_zeros > 1.packets
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-phys-tx.pcap | \
+trim_zeros > 2.packets
+
+cat ipv6_ns.expected | cut -c -112 > expout
+AT_CHECK([cat 1.packets | cut -c -112], [0], [expout])
+AT_CHECK([cat 2.packets | cut -c -112], [0], [expout])
+
+# Skipping the ICMPv6 checksum
+cat ipv6_ns.expected | cut -c 117- > expout
+AT_CHECK([cat 1.packets | cut -c 117-], [0], [expout])
+AT_CHECK([cat 2.packets | cut -c 117-], [0], [expout])
+
+# Now send a packet with destination ip other than
+# 2001:db8:1:0:200:02ff:fe01:0204/64 prefix.
+reset_pcap_file br-phys_n1 hv1/br-phys_n1
+reset_pcap_file br-phys hv1/br-phys
+
+src_mac=506400000002
+dst_mac=00000000af01
+src_ip=aef0000000000000526400fffe000002
+dst_ip=20020ab8000100000200020000020306
+# multicast mac of the nexthop IP - 2001:db8:1:0:200:02ff:fe01:1305
+dst_mcast_mac=3333ff011305
+mcast_node_ip=ff0200000000000000000001ff011305
+nd_target=20010db800010000020002fffe011305
+test_ipv6 1 $src_mac $dst_mac $src_ip $dst_ip $dst_mcast_mac \
+$mcast_node_ip $nd_target
 
 OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys_n1-tx.pcap | cut -d " " -f1)])
 OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys-tx.pcap | cut -d " " -f1)])
@@ -9723,8 +10600,8 @@ for i in 1 2 3; do
         for k in 1 2 3; do
             ovn-nbctl \
                 -- lsp-add ls$i lp$i$j$k \
-                -- lsp-set-addresses lp$i$j$k "f0:00:00:00:0$i:$j$k \
-                   192.168.$i$j.$k"
+                -- lsp-set-addresses lp$i$j$k \
+                   "f0:00:00:00:0$i:$j$k 192.168.$i$j.$k"
             # logical ports lp[12]?1 belongs to port group pg1
             if test $i != 3 && test $k == 1; then
                 pg1_ports="$pg1_ports `get_lsp_uuid $i$j$k`"
@@ -9949,8 +10826,8 @@ for i in 1 2 3; do
         for k in 1 2 3; do
             ovn-nbctl \
                 -- lsp-add ls$i lp$i$j$k \
-                -- lsp-set-addresses lp$i$j$k "f0:00:00:00:0$i:$j$k \
-                   192.168.$i$j.$k"
+                -- lsp-set-addresses lp$i$j$k \
+                   "f0:00:00:00:0$i:$j$k 192.168.$i$j.$k"
             # logical ports lp[12]?1 belongs to port group pg1
             if test $i != 3 && test $k == 1; then
                 pg1_ports="$pg1_ports `get_lsp_uuid $i$j$k`"
@@ -10043,13 +10920,20 @@ OVN_POPULATE_ARP
 # XXX This should be more systematic.
 sleep 1
 
-# test_ip INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT...
+lsp_to_mac() {
+    echo f0:00:00:00:0${1:0:1}:${1:1:2}
+}
+
+lrp_to_mac() {
+    echo 00:00:00:00:ff:$1
+}
+
+# test_icmp INPORT SRC_MAC DST_MAC SRC_IP DST_IP ICMP_TYPE OUTPORT...
 #
-# This shell function causes a packet to be received on INPORT.  The packet's
-# content has Ethernet destination DST and source SRC (each exactly 12 hex
-# digits) and Ethernet type ETHTYPE (4 hex digits).  The OUTPORTs (zero or
-# more) list the VIFs on which the packet should be received.  INPORT and the
-# OUTPORTs are specified as logical switch port numbers, e.g. 123 for vif123.
+# This shell function causes a ICMP packet to be received on INPORT.
+# The OUTPORTs (zero or more) list the VIFs on which the packet should
+# be received.  INPORT and the OUTPORTs are specified as logical switch
+# port numbers, e.g. 123 for vif123.
 for i in 1 2 3; do
     for j in 1 2 3; do
         for k in 1 2 3; do
@@ -10057,26 +10941,34 @@ for i in 1 2 3; do
         done
     done
 done
-test_ip() {
-    # This packet has bad checksums but logical L3 routing doesn't check.
-    local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5
-    local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-    shift; shift; shift; shift; shift
+
+test_icmp() {
+    local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 icmp_type=$6
+    local packet="inport==\"lp$inport\" && eth.src==$src_mac &&
+                  eth.dst==$dst_mac && ip.ttl==64 && ip4.src==$src_ip
+                  && ip4.dst==$dst_ip && icmp4.type==$icmp_type &&
+                  icmp4.code==0"
+    shift; shift; shift; shift; shift; shift
     hv=hv`vif_to_hv $inport`
-    as $hv ovs-appctl netdev-dummy/receive vif$inport $packet
-    #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $packet
+    as $hv ovs-appctl -t ovn-controller inject-pkt "$packet"
     in_ls=`vif_to_ls $inport`
     in_lrp=`vif_to_lrp $inport`
     for outport; do
         out_ls=`vif_to_ls $outport`
         if test $in_ls = $out_ls; then
             # Ports on the same logical switch receive exactly the same packet.
-            echo $packet
+            echo $packet | ovstest test-ovn expr-to-packets
         else
             # Routing decrements TTL and updates source and dest MAC
             # (and checksum).
             out_lrp=`vif_to_lrp $outport`
-            echo f00000000${outport}00000000ff${out_lrp}08004500001c00000000"3f1101"00${src_ip}${dst_ip}0035111100080000
+            exp_smac=`lrp_to_mac $out_lrp`
+            exp_dmac=`lsp_to_mac $outport`
+            exp_packet="eth.src==$exp_smac && eth.dst==$exp_dmac &&
+                ip.ttl==63 && ip4.src==$src_ip && ip4.dst==$dst_ip &&
+                icmp4.type==$icmp_type && icmp4.code==0"
+            echo $exp_packet | ovstest test-ovn expr-to-packets
+
         fi >> $outport.expected
     done
 }
@@ -10099,14 +10991,17 @@ for is in 1 2 3; do
     for ks in 1 2 3; do
       bcast=
       s=$is$js$ks
-      smac=f00000000$s
-      sip=`ip_to_hex 192 168 $is$js $ks`
+      slsp_mac=`lsp_to_mac $s`
+      slrp_mac=`lrp_to_mac $is$js`
+      sip=192.168.$is$js.$ks
       for id in 1 2 3; do
           for jd in 1 2 3; do
               for kd in 1 2 3; do
                 d=$id$jd$kd
-                dip=`ip_to_hex 192 168 $id$jd $kd`
-                if test $is = $id; then dmac=f00000000$d; else dmac=00000000ff$is$js; fi
+                dlsp_mac=`lsp_to_mac $d`
+                dlrp_mac=`lrp_to_mac $id$jd`
+                dip=192.168.$id$jd.$kd
+                if test $is = $id; then dmac=$dlsp_mac; else dmac=$slrp_mac; fi
                 if test $d != $s; then unicast=$d; else unicast=; fi
 
                 # packets matches ACL1 but not ACL2 should be dropped
@@ -10115,7 +11010,16 @@ for is in 1 2 3; do
                         unicast=
                     fi
                 fi
-                test_ip $s $smac $dmac $sip $dip $unicast #1
+                # icmp request (type = 8)
+                test_icmp $s $slsp_mac $dmac $sip $dip 8 $unicast
+
+                # if packets are not dropped, test the return traffic (icmp echo)
+                # to make sure stateful works, too.
+                if test x$unicast != x; then
+                    if test $is = $id; then dmac=$slsp_mac; else dmac=$dlrp_mac; fi
+                    # icmp echo (type = 0)
+                    test_icmp $unicast $dlsp_mac $dmac $dip $sip 0 $s
+                fi
               done
           done
         done
@@ -10141,6 +11045,115 @@ done
 OVN_CLEANUP([hv1], [hv2], [hv3])
 AT_CLEANUP
 
+AT_SETUP([ovn -- Address Set generation from Port Groups (static addressing)])
+ovn_start
+
+ovn-nbctl ls-add ls1
+
+ovn-nbctl lsp-add ls1 lp1
+ovn-nbctl lsp-add ls1 lp2
+ovn-nbctl lsp-add ls1 lp3
+
+ovn-nbctl lsp-set-addresses lp1 "02:00:00:00:00:01 10.0.0.1 2001:db8::1"
+ovn-nbctl lsp-set-addresses lp2 "02:00:00:00:00:02 10.0.0.2 2001:db8::2"
+ovn-nbctl lsp-set-addresses lp3 "02:00:00:00:00:03 10.0.0.3 2001:db8::3"
+
+ovn-nbctl create Port_Group name=pg1
+ovn-nbctl create Port_Group name=pg2
+
+ovn-nbctl --id=@p get Logical_Switch_Port lp1 -- add Port_Group pg1 ports @p
+ovn-nbctl --id=@p get Logical_Switch_Port lp2 -- add Port_Group pg1 ports @p
+ovn-nbctl --id=@p get Logical_Switch_Port lp2 -- add Port_Group pg2 ports @p
+ovn-nbctl --id=@p get Logical_Switch_Port lp3 -- add Port_Group pg2 ports @p
+
+ovn-nbctl --wait=sb sync
+
+dnl Check if port group address sets were populated with ports' addresses
+AT_CHECK([ovn-sbctl get Address_Set pg1_ip4 addresses],
+         [0], [[["10.0.0.1", "10.0.0.2"]]
+])
+AT_CHECK([ovn-sbctl get Address_Set pg2_ip4 addresses],
+         [0], [[["10.0.0.2", "10.0.0.3"]]
+])
+AT_CHECK([ovn-sbctl get Address_Set pg1_ip6 addresses],
+         [0], [[["2001:db8::1", "2001:db8::2"]]
+])
+AT_CHECK([ovn-sbctl get Address_Set pg2_ip6 addresses],
+         [0], [[["2001:db8::2", "2001:db8::3"]]
+])
+
+ovn-nbctl --wait=sb lsp-set-addresses lp1 \
+    "02:00:00:00:00:01 10.0.0.11 2001:db8::11"
+
+dnl Check if updated address got propagated to the port group address sets
+AT_CHECK([ovn-sbctl get Address_Set pg1_ip4 addresses],
+         [0], [[["10.0.0.11", "10.0.0.2"]]
+])
+AT_CHECK([ovn-sbctl get Address_Set pg1_ip6 addresses],
+         [0], [[["2001:db8::11", "2001:db8::2"]]
+])
+
+AT_CLEANUP
+
+AT_SETUP([ovn -- Address Set generation from Port Groups (dynamic addressing)])
+ovn_start
+
+ovn-nbctl ls-add ls1
+ovn-nbctl ls-add ls2
+ovn-nbctl ls-add ls3
+
+ovn-nbctl set Logical_Switch ls1 \
+    other_config:subnet=10.1.0.0/24 other_config:ipv6_prefix="2001:db8:1::"
+ovn-nbctl set Logical_Switch ls2 \
+    other_config:subnet=10.2.0.0/24 other_config:ipv6_prefix="2001:db8:2::"
+ovn-nbctl set Logical_Switch ls3 \
+    other_config:subnet=10.3.0.0/24 other_config:ipv6_prefix="2001:db8:3::"
+
+ovn-nbctl lsp-add ls1 lp1
+ovn-nbctl lsp-add ls2 lp2
+ovn-nbctl lsp-add ls3 lp3
+
+ovn-nbctl lsp-set-addresses lp1 "02:00:00:00:00:01 dynamic"
+ovn-nbctl lsp-set-addresses lp2 "02:00:00:00:00:02 dynamic"
+ovn-nbctl lsp-set-addresses lp3 "02:00:00:00:00:03 dynamic"
+
+ovn-nbctl create Port_Group name=pg1
+ovn-nbctl create Port_Group name=pg2
+
+ovn-nbctl --id=@p get Logical_Switch_Port lp1 -- add Port_Group pg1 ports @p
+ovn-nbctl --id=@p get Logical_Switch_Port lp2 -- add Port_Group pg1 ports @p
+ovn-nbctl --id=@p get Logical_Switch_Port lp2 -- add Port_Group pg2 ports @p
+ovn-nbctl --id=@p get Logical_Switch_Port lp3 -- add Port_Group pg2 ports @p
+
+ovn-nbctl --wait=sb sync
+
+dnl Check if port group address sets were populated with ports' addresses
+AT_CHECK([ovn-sbctl get Address_Set pg1_ip4 addresses],
+         [0], [[["10.1.0.2", "10.2.0.2"]]
+])
+AT_CHECK([ovn-sbctl get Address_Set pg2_ip4 addresses],
+         [0], [[["10.2.0.2", "10.3.0.2"]]
+])
+AT_CHECK([ovn-sbctl get Address_Set pg1_ip6 addresses],
+         [0], [[["2001:db8:1::ff:fe00:1", "2001:db8:2::ff:fe00:2"]]
+])
+AT_CHECK([ovn-sbctl get Address_Set pg2_ip6 addresses],
+         [0], [[["2001:db8:2::ff:fe00:2", "2001:db8:3::ff:fe00:3"]]
+])
+
+ovn-nbctl set Logical_Switch ls1 \
+    other_config:subnet=10.11.0.0/24 other_config:ipv6_prefix="2001:db8:11::"
+
+dnl Check if updated address got propagated to the port group address sets
+AT_CHECK([ovn-sbctl get Address_Set pg1_ip4 addresses],
+         [0], [[["10.11.0.2", "10.2.0.2"]]
+])
+AT_CHECK([ovn-sbctl get Address_Set pg1_ip6 addresses],
+         [0], [[["2001:db8:11::ff:fe00:1", "2001:db8:2::ff:fe00:2"]]
+])
+
+AT_CLEANUP
+
 AT_SETUP([ovn -- ACL conjunction])
 ovn_start
 
@@ -10303,6 +11316,25 @@ test_ip_packet() {
     as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
 }
 
+# test_ip6_packet INPORT HV ETH_SRC ETH_DST IPV6_SRC IPV6_DST IPV6_ROUTER EXP_ICMP_CHKSUM
+#
+# Causes a packet to be received on INPORT of the hypervisor HV. The packet is an IPv6
+# packet with ETH_SRC, ETH_DST, IPV6_SRC and IPV6_DST as specified.
+# IPV6_ROUTER and EXP_ICMP_CHKSUM are the source IP and checksum of the icmpv6 ttl exceeded
+# packet sent by OVN logical router
+test_ip6_packet() {
+    local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv6_src=$5 ipv6_dst=$6 ipv6_router=$7 exp_icmp_chksum=$8
+    shift 8
+
+    local ip6_hdr=6000000000151101${ipv6_src}${ipv6_dst}
+    local packet=${eth_dst}${eth_src}86dd${ip6_hdr}dbb8303900155bac6b646f65206676676e6d66720a
+
+    local reply=${eth_src}${eth_dst}86dd6000000000303afe${ipv6_router}${ipv6_src}0300${exp_icmp_chksum}00000000${ip6_hdr}
+    echo $reply >> vif$inport.expected
+
+    as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
+}
+
 ip_to_hex() {
     printf "%02x%02x%02x%02x" "$@"
 }
@@ -10317,7 +11349,7 @@ for i in 1 2; do
     ovn_attach n$i br-phys 192.168.$i.1
 
     ovn-nbctl lsp-add sw$i sw$i-p${i}0 -- \
-        lsp-set-addresses sw$i-p${i}0 "00:00:00:00:00:0$i 192.168.$i.1"
+        lsp-set-addresses sw$i-p${i}0 "00:00:00:00:00:0$i 192.168.$i.1 2001:db8:$i::11"
 
     ovs-vsctl -- add-port br-int vif$i -- \
         set interface vif$i \
@@ -10329,10 +11361,10 @@ done
 
 ovn-nbctl lr-add lr0
 for i in 1 2; do
-    ovn-nbctl lrp-add lr0 lrp$i 00:00:00:00:ff:0$i 192.168.$i.254/24
+    ovn-nbctl lrp-add lr0 lrp$i 00:00:00:00:ff:0$i 192.168.$i.254/24 2001:db8:$i::1/64
     ovn-nbctl -- lsp-add sw$i lrp$i-attachment \
               -- set Logical_Switch_Port lrp$i-attachment type=router \
-                options:router-port=lrp$i addresses='"00:00:00:00:ff:'0$i'"'
+                options:router-port=lrp$i addresses='"00:00:00:00:ff:0'$i' 192.168.'$i'.254 2001:db8:'$i'::1"'
 done
 
 OVN_POPULATE_ARP
@@ -10340,7 +11372,646 @@ OVN_POPULATE_ARP
 ovn-nbctl --wait=hv sync
 
 test_ip_packet 1 1 000000000001 00000000ff01 $(ip_to_hex 192 168 1 1) $(ip_to_hex 192 168 2 1) $(ip_to_hex 192 168 1 254) 0000 7dae f4ff
+test_ip6_packet 1 1 000000000001 00000000ff01 20010db8000100000000000000000011 20010db8000200000000000000000011 20010db8000100000000000000000001 d461
 OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
 
 OVN_CLEANUP([hv1], [hv2])
 AT_CLEANUP
+
+AT_SETUP([ovn -- router port unreachable])
+AT_KEYWORDS([router-port-unreachable])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+ovn_start
+
+# test_ip_packet INPORT HV ETH_SRC ETH_DST IPV4_SRC IPV4_ROUTER L4_PROTCOL IP_CHKSUM EXP_IP_CHKSUM EXP_ICMP_CHKSUM EXP_ICMP_CODE
+#
+# Causes a packet to be received on INPORT of the hypervisor HV. The packet is an IPv4 packet with
+# ETH_SRC, ETH_DST, IPV4_SRC, IPV4_ROUTER, L4_PROTCOL, IP_CHKSUM as specified.
+# EXP_IP_CHKSUM and EXP_ICMP_CHKSUM are the ip and icmp checksums of the icmp frame generated by OVN logical router
+# EXP_ICMP_CODE are code and type of the icmp frame generated by OVN logical router
+#
+# INPORT is a lport number, e.g. 11 for vif11.
+# HV is a hypervisor number
+# ETH_SRC and ETH_DST are each 12 hex digits.
+# IPV4_SRC and IPV4_ROUTER are each 8 hex digits.
+# IP_CHKSUM, EXP_IP_CHSUM and EXP_ICMP_CHKSUM are each 4 hex digits
+test_ip_packet() {
+    local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv4_src=$5 ip_router=$6 l4_proto=$7 ip_chksum=$8
+    local exp_ip_chksum=$9 exp_icmp_chksum=${10} exp_icmp_code=${11}
+    shift 11
+
+    local ip_ttl=ff
+    local packet=${eth_dst}${eth_src}08004500001400004000${ip_ttl}${l4_proto}${ip_chksum}${ipv4_src}${ip_router}
+
+    local reply_icmp_ttl=fe
+    local icmp_data=00000000
+    local reply_icmp_payload=${exp_icmp_code}${exp_icmp_chksum}${icmp_data}
+    local reply=${eth_src}${eth_dst}08004500001c00004000${reply_icmp_ttl}01${exp_ip_chksum}${ip_router}${ipv4_src}${reply_icmp_payload}
+    echo $reply >> vif$inport.expected
+
+    as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
+}
+
+# test_tcp_syn_packet INPORT HV ETH_SRC ETH_DST IPV4_SRC IPV4_ROUTER IP_CHKSUM TCP_SPORT TCP_DPORT TCP_CHKSUM EXP_IP_CHKSUM EXP_TCP_RST_CHKSUM
+#
+# Causes a packet to be received on INPORT of the hypervisor HV. The packet is an TCP syn segment with
+# ETH_SRC, ETH_DST, IPV4_SRC, IPV4_ROUTER, IP_CHKSUM, TCP_SPORT, TCP_DPORT, TCP_CHKSUM  as specified.
+# EXP_IP_CHKSUM and EXP_TCP_RST_CHKSUM are the ip and tcp checksums of the tcp reset segment generated by OVN logical router
+#
+# INPORT is an lport number, e.g. 11 for vif11.
+# HV is an hypervisor number
+# ETH_SRC and ETH_DST are each 12 hex digits.
+# IPV4_SRC and IPV4_ROUTER are each 8 hex digits.
+# TCP_SPORT and TCP_DPORT are 4 hex digits.
+# IP_CHKSUM, TCP_CHKSUM, EXP_IP_CHSUM and EXP_TCP_RST_CHKSUM are each 4 hex digits
+test_tcp_syn_packet() {
+    local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv4_src=$5 ip_router=$6 ip_chksum=$7
+    local tcp_sport=$8 tcp_dport=$9 tcp_chksum=${10}
+    local exp_ip_chksum=${11} exp_tcp_rst_chksum=${12}
+    shift 12
+
+    local ip_ttl=ff
+    local packet=${eth_dst}${eth_src}08004500002800004000${ip_ttl}06${ip_chksum}${ipv4_src}${ip_router}${tcp_sport}${tcp_dport}000000010000000050027210${tcp_chksum}0000
+
+    local tcp_rst_ttl=fe
+    local reply=${eth_src}${eth_dst}08004500002800004000${tcp_rst_ttl}06${exp_ip_chksum}${ip_router}${ipv4_src}${tcp_dport}${tcp_sport}000000000000000150040000${exp_tcp_rst_chksum}0000
+    echo $reply >> vif$inport.expected
+
+    as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
+}
+
+# test_tcp6_packet INPORT HV ETH_SRC ETH_DST IPV6_SRC IPV6_ROUTER TCP_SPORT TCP_DPORT TCP_CHKSUM EXP_TCP_RST_CHKSUM
+#
+# Causes a packet to be received on INPORT of the hypervisor HV. The packet is a TCP syn segment with
+# ETH_SRC, ETH_DST, IPV6_SRC, IPV6_ROUTER, TCP_SPORT, TCP_DPORT and TCP_CHKSUM as specified.
+# EXP_TCP_RST_CHKSUM is the tcp checksums of the tcp reset segment generated by OVN logical router
+test_tcp6_packet() {
+    local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv6_src=$5 ipv6_router=$6
+    local tcp_sport=$7 tcp_dport=$8 tcp_chksum=$9
+    local exp_tcp_rst_chksum=${10}
+    shift 10
+
+    local ip6_hdr=60000000001406ff${ipv6_src}${ipv6_router}
+    local packet=${eth_dst}${eth_src}86dd${ip6_hdr}${tcp_sport}${tcp_dport}000000010000000050027210${tcp_chksum}0000
+
+    local reply=${eth_src}${eth_dst}86dd60000000001406fe${ipv6_router}${ipv6_src}${tcp_dport}${tcp_sport}000000000000000150040000${exp_tcp_rst_chksum}0000
+    echo $reply >> vif$inport.expected
+
+    as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
+}
+
+# test_ip6_packet INPORT HV ETH_SRC ETH_DST IPV6_SRC IPV6_DST IPV6_PROTO IPV6_LEN DATA EXP_ICMP_CODE EXP_ICMP_CHKSUM
+#
+# Causes a packet to be received on INPORT of the hypervisor HV. The packet is an IPv6
+# packet with ETH_SRC, ETH_DST, IPV6_SRC, IPV6_DST, IPV6_PROTO, IPV6_LEN and DATA as specified.
+# EXP_ICMP_CODE and EXP_ICMP_CHKSUM are the code and checksum of the icmp6 packet sent by OVN logical router
+test_ip6_packet() {
+    local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv6_src=$5 ipv6_dst=$6 ipv6_proto=$7 ipv6_len=$8 data=$9
+    local exp_icmp_code=${10} exp_icmp_chksum=${11}
+    shift 11
+
+    local ip6_hdr=60000000${ipv6_len}${ipv6_proto}ff${ipv6_src}${ipv6_dst}
+    local packet=${eth_dst}${eth_src}86dd${ip6_hdr}${data}
+
+    local reply=${eth_src}${eth_dst}86dd6000000000303afe${ipv6_dst}${ipv6_src}${exp_icmp_code}${exp_icmp_chksum}00000000${ip6_hdr}
+    echo $reply >> vif$inport.expected
+
+    as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
+}
+
+ip_to_hex() {
+    printf "%02x%02x%02x%02x" "$@"
+}
+
+for i in 1 2; do
+    net_add n$i
+    ovn-nbctl ls-add sw$i
+
+    sim_add hv$i
+    as hv$i
+    ovs-vsctl add-br br-phys
+    ovn_attach n$i br-phys 192.168.$i.1
+
+    ovn-nbctl lsp-add sw$i sw$i-p${i}0 -- \
+        lsp-set-addresses sw$i-p${i}0 "00:00:00:00:00:0$i 192.168.$i.1 2001:db8:$i::11"
+
+    ovs-vsctl -- add-port br-int vif$i -- \
+        set interface vif$i \
+        external-ids:iface-id=sw$i-p${i}0 \
+            options:tx_pcap=hv$i/vif$i-tx.pcap \
+            options:rxq_pcap=hv$i/vif$i-rx.pcap \
+            ofport-request=$i
+done
+
+ovn-nbctl lr-add lr0
+for i in 1 2; do
+    ovn-nbctl lrp-add lr0 lrp$i 00:00:00:00:ff:0$i 192.168.$i.254/24 2001:db8:$i::1/64
+    ovn-nbctl -- lsp-add sw$i lrp$i-attachment \
+              -- set Logical_Switch_Port lrp$i-attachment type=router \
+                options:router-port=lrp$i addresses='"00:00:00:00:ff:0'$i' 192.168.'$i'.254 2001:db8:'$i'::1"'
+done
+
+OVN_POPULATE_ARP
+# allow some time for ovn-northd and ovn-controller to catch up.
+ovn-nbctl --wait=hv sync
+
+test_ip_packet 1 1 000000000001 00000000ff01 $(ip_to_hex 192 168 1 1) $(ip_to_hex 192 168 1 254) 11 0000 7dae fcfc 0303
+test_ip_packet 1 1 000000000001 00000000ff01 $(ip_to_hex 192 168 1 1) $(ip_to_hex 192 168 1 254) 84 0000 7dae fcfd 0302
+test_ip6_packet 1 1 000000000001 00000000ff01 20010db8000100000000000000000011 20010db8000100000000000000000001 11 0015 dbb8303900155bac6b646f65206676676e6d66720a 0104 d570
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
+
+test_tcp_syn_packet 2 2 000000000002 00000000ff02 $(ip_to_hex 192 168 2 1) $(ip_to_hex 192 168 2 254) 0000 8b40 3039 0000 7bae 4486
+test_ip6_packet 2 2 000000000002 00000000ff02 20010db8000200000000000000000011 20010db8000200000000000000000001 84 0004 01020304 0103 627e
+test_tcp6_packet 2 2 000000000002 00000000ff02 20010db8000200000000000000000011 20010db8000200000000000000000001 8b40 3039 0000 4486
+OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [vif2.expected])
+
+OVN_CLEANUP([hv1], [hv2])
+AT_CLEANUP
+
+AT_SETUP([ovn -- ovn-controller exit])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+ovn_start
+# Logical network:
+# One Logical Router: ro, with two logical switches sw1 and sw2.
+# sw1 is for subnet 10.0.0.0/8
+# sw2 is for subnet 20.0.0.0/8
+# sw1 has a single port bound on hv1
+# sw2 has a single port bound on hv2
+
+ovn-nbctl lr-add ro
+ovn-nbctl ls-add sw1
+ovn-nbctl ls-add sw2
+
+sw1_ro_mac=00:00:10:00:00:01
+sw1_ro_ip=10.0.0.1
+sw2_ro_mac=00:00:20:00:00:01
+sw2_ro_ip=20.0.0.1
+sw1_p1_mac=00:00:10:00:00:02
+sw1_p1_ip=10.0.0.2
+sw2_p1_mac=00:00:20:00:00:02
+sw2_p1_ip=20.0.0.2
+
+ovn-nbctl lrp-add ro ro-sw1 $sw1_ro_mac ${sw1_ro_ip}/8
+ovn-nbctl lrp-add ro ro-sw2 $sw2_ro_mac ${sw2_ro_ip}/8
+ovn-nbctl lsp-add sw1 sw1-ro -- set Logical_Switch_Port sw1-ro type=router \
+  options:router-port=ro-sw1 addresses=\"$sw1_ro_mac\"
+ovn-nbctl lsp-add sw2 sw2-ro -- set Logical_Switch_Port sw2-ro type=router \
+  options:router-port=ro-sw2 addresses=\"$sw2_ro_mac\"
+
+ovn-nbctl lsp-add sw1 sw1-p1 \
+-- lsp-set-addresses sw1-p1 "$sw1_p1_mac $sw1_p1_ip"
+
+ovn-nbctl lsp-add sw2 sw2-p1 \
+-- lsp-set-addresses sw2-p1 "$sw2_p1_mac $sw2_p1_ip"
+
+net_add n1
+
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+ovs-vsctl -- add-port br-int hv1-vif1 -- \
+    set interface hv1-vif1 external-ids:iface-id=sw1-p1 \
+    options:tx_pcap=hv1/vif1-tx.pcap \
+    options:rxq_pcap=hv1/vif1-rx.pcap \
+    ofport-request=1
+
+sim_add hv2
+as hv2
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.2
+ovs-vsctl -- add-port br-int hv2-vif1 -- \
+    set interface hv2-vif1 external-ids:iface-id=sw2-p1 \
+    options:tx_pcap=hv2/vif1-tx.pcap \
+    options:rxq_pcap=hv2/vif1-rx.pcap \
+    ofport-request=1
+
+OVN_POPULATE_ARP
+
+sleep 1
+
+packet="inport==\"sw1-p1\" && eth.src==$sw1_p1_mac && eth.dst==$sw1_ro_mac &&
+       ip4 && ip.ttl==64 && ip4.src==$sw1_p1_ip && ip4.dst==$sw2_p1_ip &&
+       udp && udp.src==53 && udp.dst==4369"
+
+# Start by Sending the packet and make sure it makes it there as expected
+as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"
+
+# Expected packet has TTL decreased by 1
+expected="eth.src==$sw2_ro_mac && eth.dst==$sw2_p1_mac &&
+       ip4 && ip.ttl==63 && ip4.src==$sw1_p1_ip && ip4.dst==$sw2_p1_ip &&
+       udp && udp.src==53 && udp.dst==4369"
+echo $expected | ovstest test-ovn expr-to-packets > expected
+
+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+
+# Stop ovn-controller on hv2
+as hv2 ovs-appctl -t ovn-controller exit
+
+# Now send the packet again. This time, it should not arrive.
+as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"
+
+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+
+# Start ovn-controller again just so OVN_CLEANUP doesn't complain
+as hv2 start_daemon ovn-controller
+
+OVN_CLEANUP([hv1],[hv2])
+AT_CLEANUP
+
+AT_SETUP([ovn -- ovn-controller restart])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+ovn_start
+
+# Logical network:
+# One Logical Router: ro, with two logical switches sw1 and sw2.
+# sw1 is for subnet 10.0.0.0/8
+# sw2 is for subnet 20.0.0.0/8
+# sw1 has a single port bound on hv1
+# sw2 has a single port bound on hv2
+
+ovn-nbctl lr-add ro
+ovn-nbctl ls-add sw1
+ovn-nbctl ls-add sw2
+
+sw1_ro_mac=00:00:10:00:00:01
+sw1_ro_ip=10.0.0.1
+sw2_ro_mac=00:00:20:00:00:01
+sw2_ro_ip=20.0.0.1
+sw1_p1_mac=00:00:10:00:00:02
+sw1_p1_ip=10.0.0.2
+sw2_p1_mac=00:00:20:00:00:02
+sw2_p1_ip=20.0.0.2
+
+ovn-nbctl lrp-add ro ro-sw1 $sw1_ro_mac ${sw1_ro_ip}/8
+ovn-nbctl lrp-add ro ro-sw2 $sw2_ro_mac ${sw2_ro_ip}/8
+ovn-nbctl lsp-add sw1 sw1-ro -- set Logical_Switch_Port sw1-ro type=router \
+  options:router-port=ro-sw1 addresses=\"$sw1_ro_mac\"
+ovn-nbctl lsp-add sw2 sw2-ro -- set Logical_Switch_Port sw2-ro type=router \
+  options:router-port=ro-sw2 addresses=\"$sw2_ro_mac\"
+
+ovn-nbctl lsp-add sw1 sw1-p1 \
+-- lsp-set-addresses sw1-p1 "$sw1_p1_mac $sw1_p1_ip"
+
+ovn-nbctl lsp-add sw2 sw2-p1 \
+-- lsp-set-addresses sw2-p1 "$sw2_p1_mac $sw2_p1_ip"
+
+net_add n1
+
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+ovs-vsctl -- add-port br-int hv1-vif1 -- \
+    set interface hv1-vif1 external-ids:iface-id=sw1-p1 \
+    options:tx_pcap=hv1/vif1-tx.pcap \
+    options:rxq_pcap=hv1/vif1-rx.pcap \
+    ofport-request=1
+
+sim_add hv2
+as hv2
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.2
+ovs-vsctl -- add-port br-int hv2-vif1 -- \
+    set interface hv2-vif1 external-ids:iface-id=sw2-p1 \
+    options:tx_pcap=hv2/vif1-tx.pcap \
+    options:rxq_pcap=hv2/vif1-rx.pcap \
+    ofport-request=1
+
+OVN_POPULATE_ARP
+
+sleep 1
+
+packet="inport==\"sw1-p1\" && eth.src==$sw1_p1_mac && eth.dst==$sw1_ro_mac &&
+       ip4 && ip.ttl==64 && ip4.src==$sw1_p1_ip && ip4.dst==$sw2_p1_ip &&
+       udp && udp.src==53 && udp.dst==4369"
+
+# Start by Sending the packet and make sure it makes it there as expected
+as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"
+
+# Expected packet has TTL decreased by 1
+expected="eth.src==$sw2_ro_mac && eth.dst==$sw2_p1_mac &&
+       ip4 && ip.ttl==63 && ip4.src==$sw1_p1_ip && ip4.dst==$sw2_p1_ip &&
+       udp && udp.src==53 && udp.dst==4369"
+echo $expected | ovstest test-ovn expr-to-packets > expected
+
+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+
+# Stop ovn-controller on hv2 with --restart flag
+as hv2 ovs-appctl -t ovn-controller exit --restart
+
+# Now send the packet again. This time, it should still arrive
+as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"
+
+cat expected expected > expected2
+
+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected2])
+
+# Start ovn-controller again just so OVN_CLEANUP doesn't complain
+as hv2 start_daemon ovn-controller
+
+OVN_CLEANUP([hv1],[hv2])
+
+AT_CLEANUP
+
+AT_SETUP([ovn -- ovn-nbctl duplicate addresses])
+ovn_start
+
+# Set up a switch with some switch ports of varying address types
+ovn-nbctl ls-add sw1
+ovn-nbctl set logical_switch sw1 other_config:subnet=192.168.0.0/24
+
+ovn-nbctl lsp-add sw1 sw1-p1
+ovn-nbctl lsp-add sw1 sw1-p2
+ovn-nbctl lsp-add sw1 sw1-p3
+ovn-nbctl lsp-add sw1 sw1-p4
+
+ovn-nbctl lsp-set-addresses sw1-p1 "00:00:00:00:00:01 10.0.0.1 aef0::1" "00:00:00:00:00:02 10.0.0.2 aef0::2"
+ovn-nbctl lsp-set-addresses sw1-p2 "00:00:00:00:00:03 dynamic"
+ovn-nbctl lsp-set-addresses sw1-p3 "dynamic"
+ovn-nbctl lsp-set-addresses sw1-p4 "router"
+ovn-nbctl lsp-set-addresses sw1-p5 "unknown"
+
+ovn-nbctl list logical_switch_port
+
+# Now try to add duplicate addresses on a new port. These should all fail
+ovn-nbctl lsp-add sw1 sw1-p5
+AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 10.0.0.1"], [1], [],
+[ovn-nbctl: Error on switch sw1: duplicate IPv4 address 10.0.0.1
+])
+AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 10.0.0.2"], [1], [],
+[ovn-nbctl: Error on switch sw1: duplicate IPv4 address 10.0.0.2
+])
+AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 aef0::1"], [1], [],
+[ovn-nbctl: Error on switch sw1: duplicate IPv6 address aef0::1
+])
+AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 aef0::2"], [1], [],
+[ovn-nbctl: Error on switch sw1: duplicate IPv6 address aef0::2
+])
+AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 192.168.0.2"], [1], [],
+[ovn-nbctl: Error on switch sw1: duplicate IPv4 address 192.168.0.2
+])
+AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 192.168.0.3"], [1], [],
+[ovn-nbctl: Error on switch sw1: duplicate IPv4 address 192.168.0.3
+])
+
+# Now try re-setting sw1-p1. This should succeed
+AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p1 "00:00:00:00:00:01 10.0.0.1 aef0::1"])
+
+# Now create a new switch and try setting IP addresses the same as the
+# first switch. This should succeed.
+ovn-nbctl ls-add sw2
+ovn-nbctl lsp-add sw2 sw2-p1
+
+AT_CHECK([ovn-nbctl lsp-set-addresses sw2-p1 "00:00:00:00:00:04 10.0.0.1"])
+AT_CHECK([ovn-nbctl lsp-set-addresses sw2-p1 "00:00:00:00:00:04 192.168.0.2"])
+AT_CHECK([ovn-nbctl lsp-set-addresses sw2-p1 "00:00:00:00:00:04 192.168.0.3"])
+AT_CHECK([ovn-nbctl lsp-set-addresses sw2-p1 "00:00:00:00:00:04 aef0::1"])
+
+AT_CLEANUP
+
+AT_SETUP([ovn -- IP packet buffering])
+AT_KEYWORDS([ip-buffering])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+ovn_start
+
+# Logical network:
+# One LR lr0 that has switches sw0 (192.168.1.0/24) and
+# sw1 (172.16.1.0/24) connected to it.
+#
+# Physical network:
+# Tw0 hypervisors hv[12].
+# hv1 hosts vif sw0-p0.
+# hv1 hosts vif sw1-p0.
+
+send_icmp_packet() {
+    local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv4_src=$5 ipv4_dst=$6 ip_chksum=$7 data=$8
+    shift 8
+
+    local ip_ttl=ff
+    local ip_len=001c
+    local packet=${eth_dst}${eth_src}08004500${ip_len}00004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}${data}
+    as hv$hv ovs-appctl netdev-dummy/receive hv$hv-vif$inport $packet
+}
+
+send_icmp6_packet() {
+    local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv6_src=$5 ipv6_dst=$6 ipv6_router=$7 exp_icmp_chksum=$8
+    shift 8
+
+    local ip6_hdr=6000000000083aff${ipv6_src}${ipv6_dst}
+    local packet=${eth_dst}${eth_src}86dd${ip6_hdr}8000dcb662f00001
+
+    as hv$hv ovs-appctl netdev-dummy/receive hv$hv-vif$inport $packet
+}
+
+get_arp_req() {
+    local eth_src=$1 spa=$2 tpa=$3
+    local request=ffffffffffff${eth_src}08060001080006040001${eth_src}${spa}000000000000${tpa}
+    echo $request
+}
+
+send_arp_reply() {
+    local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 spa=$5 tpa=$6
+    local request=${eth_dst}${eth_src}08060001080006040002${eth_src}${spa}${eth_dst}${tpa}
+    as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request
+}
+
+send_na() {
+    local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 src_ip=$5 dst_ip=$6
+    local ip6_hdr=6000000000203aff${src_ip}${dst_ip}
+    local request=${eth_dst}${eth_src}86dd${ip6_hdr}8800d78440000000${src_ip}0201${eth_src}
+
+    as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request
+}
+
+get_nd() {
+    local eth_src=$1 src_ip=$2 dst_ip=$3 ta=$4
+    local ip6_hdr=6000000000203aff${src_ip}${dst_ip}
+    request=3333ff000010${eth_src}86dd${ip6_hdr}8700357600000000${ta}0101${eth_src}
+
+    echo $request
+}
+
+net_add n1
+
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+ovs-vsctl -- add-port br-int hv1-vif1 -- \
+    set interface hv1-vif1 external-ids:iface-id=sw0-p0 \
+    options:tx_pcap=hv1/vif1-tx.pcap \
+    options:rxq_pcap=hv1/vif1-rx.pcap \
+    ofport-request=1
+
+sim_add hv2
+as hv2
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.2
+ovs-vsctl -- add-port br-int hv2-vif1 -- \
+    set interface hv2-vif1 external-ids:iface-id=sw1-p0 \
+    options:tx_pcap=hv2/vif1-tx.pcap \
+    options:rxq_pcap=hv2/vif1-rx.pcap \
+    ofport-request=1
+
+ovn-nbctl create Logical_Router name=lr0 options:chassis=hv1
+ovn-nbctl ls-add sw0
+ovn-nbctl ls-add sw1
+
+ovn-nbctl lrp-add lr0 sw0 00:00:01:01:02:03 192.168.1.1/24 2001::1/64
+ovn-nbctl lsp-add sw0 rp-sw0 -- set Logical_Switch_Port rp-sw0 \
+    type=router options:router-port=sw0 \
+    -- lsp-set-addresses rp-sw0 router
+
+ovn-nbctl lrp-add lr0 sw1 00:00:02:01:02:03 172.16.1.1/24 2002::1/64
+ovn-nbctl lsp-add sw1 rp-sw1 -- set Logical_Switch_Port rp-sw1 \
+    type=router options:router-port=sw1 \
+    -- lsp-set-addresses rp-sw1 router
+
+ovn-nbctl lsp-add sw0 sw0-p0 \
+    -- lsp-set-addresses sw0-p0 "f0:00:00:01:02:03 192.168.1.2 2001::2"
+
+ovn-nbctl lsp-add sw1 sw1-p0 \
+    -- lsp-set-addresses sw1-p0 unknown
+
+OVN_POPULATE_ARP
+ovn-nbctl --wait=hv sync
+
+ip_to_hex() {
+    printf "%02x%02x%02x%02x" "$@"
+}
+
+src_mac=f00000010203
+src_ip=$(ip_to_hex 192 168 1 2)
+src_ip6=20010000000000000000000000000002
+
+router_mac0=000001010203
+router_mac1=000002010203
+router_ip=$(ip_to_hex 172 16 1 1)
+router_ip6=20020000000000000000000000000001
+
+dst_mac=001122334455
+dst_ip=$(ip_to_hex 172 16 1 10)
+dst_ip6=20020000000000000000000000000010
+
+data=0800bee4391a0001
+
+send_icmp_packet 1 1 $src_mac $router_mac0 $src_ip $dst_ip 0000 $data
+send_arp_reply 2 1 $dst_mac $router_mac1 $dst_ip $router_ip
+echo $(get_arp_req $router_mac1 $router_ip $dst_ip) > expected
+echo "${dst_mac}${router_mac1}08004500001c00004000fe010100${src_ip}${dst_ip}${data}" >> expected
+
+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+
+nd_ip=ff0200000000000000000001ff000010
+ip6_hdr=6000000000083afe${src_ip6}${dst_ip6}
+
+send_icmp6_packet 1 1 $src_mac $router_mac0 $src_ip6 $dst_ip6
+echo $(get_nd $router_mac1 $src_ip6 $nd_ip $dst_ip6) >> expected
+echo "${dst_mac}${router_mac1}86dd${ip6_hdr}8000dcb662f00001" >> expected
+send_na 2 1 $dst_mac $router_mac1 $dst_ip6 $router_ip6
+
+OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+
+OVN_CLEANUP([hv1],[hv2])
+AT_CLEANUP
+
+AT_SETUP([ovn -- neighbor update on same HV])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+ovn_start
+
+# Logical network:
+# A public switch (pub) with a localnet port connected to two LRs (lr0 and lr1)
+# each with a distributed gateway port.
+# Two VMs: lp0 on sw0 connected to lr0
+#          lp1 on sw1 connected to lr1
+#
+# This test adds a floating IP to each VM so when they are bound to the same
+# hypervisor, it checks that the GARP sent by ovn-controller causes the
+# MAC_Binding entries to be updated properly on each logical router.
+# It will also capture packets on the physical interface to make sure that the
+# GARPs have been sent out to the external network as well.
+
+# Create logical switches
+ovn-nbctl ls-add sw0
+ovn-nbctl ls-add sw1
+ovn-nbctl ls-add pub
+
+# Created localnet port on public switch
+ovn-nbctl lsp-add pub ln-pub
+ovn-nbctl lsp-set-type ln-pub localnet
+ovn-nbctl lsp-set-addresses ln-pub unknown
+ovn-nbctl lsp-set-options ln-pub network_name=phys
+
+# Create logical routers and connect them to public switch
+ovn-nbctl create Logical_Router name=lr0
+ovn-nbctl create Logical_Router name=lr1
+
+ovn-nbctl lrp-add lr0 lr0-pub f0:00:00:00:00:01 172.24.4.220/24
+ovn-nbctl lsp-add pub pub-lr0 -- set Logical_Switch_Port pub-lr0 \
+    type=router options:router-port=lr0-pub options:nat-addresses="router" addresses="router"
+ovn-nbctl lrp-add lr1 lr1-pub f0:00:00:00:01:01 172.24.4.221/24
+ovn-nbctl lsp-add pub pub-lr1 -- set Logical_Switch_Port pub-lr1 \
+    type=router options:router-port=lr1-pub options:nat-addresses="router" addresses="router"
+
+ovn-nbctl lrp-set-gateway-chassis lr0-pub hv1 10
+ovn-nbctl lrp-set-gateway-chassis lr1-pub hv1 10
+
+# Connect sw0 and sw1 to lr0 and lr1
+ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.254/24
+ovn-nbctl lsp-add sw0 sw0-lr0 -- set Logical_Switch_Port sw0-lr0 type=router \
+    options:router-port=lr0-sw0 addresses="router"
+ovn-nbctl lrp-add lr1 lr1-sw1 00:00:00:00:ff:02 20.0.0.254/24
+ovn-nbctl lsp-add sw1 sw1-lr1 -- set Logical_Switch_Port sw1-lr1 type=router \
+    options:router-port=lr1-sw1 addresses="router"
+
+
+# Add SNAT rules
+ovn-nbctl lr-nat-add lr0 snat 172.24.4.220 10.0.0.0/24
+ovn-nbctl lr-nat-add lr1 snat 172.24.4.221 20.0.0.0/24
+
+net_add n1
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 172.24.4.1
+ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
+
+ovs-vsctl add-port br-int vif0 -- set Interface vif0 external-ids:iface-id=lp0
+ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1
+
+ovn-nbctl lsp-add sw0 lp0
+ovn-nbctl lsp-add sw1 lp1
+ovn-nbctl lsp-set-addresses lp0 "50:54:00:00:00:01 10.0.0.10"
+ovn-nbctl lsp-set-addresses lp1 "50:54:00:00:00:02 20.0.0.10"
+
+OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lp0` = xup])
+OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lp1` = xup])
+
+# Create two floating IPs, one for each VIF
+ovn-nbctl lr-nat-add lr0 dnat_and_snat 172.24.4.100 10.0.0.10
+ovn-nbctl lr-nat-add lr1 dnat_and_snat 172.24.4.200 20.0.0.10
+
+# Check that the MAC_Binding entries have been properly created
+OVS_WAIT_UNTIL([test `ovn-sbctl find mac_binding logical_port="lr0-pub" ip="172.24.4.200" | wc -l` -gt 0])
+OVS_WAIT_UNTIL([test `ovn-sbctl find mac_binding logical_port="lr1-pub" ip="172.24.4.100" | wc -l` -gt 0])
+
+# Check that the GARPs went also to the external physical network
+# Wait until at least 4 packets have arrived and copy them to a separate file as
+# more GARPs are expected in the capture in order to avoid race conditions.
+OVS_WAIT_UNTIL([test `$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-phys-tx.pcap | wc -l` -gt 4])
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-phys-tx.pcap | head -n4 > hv1/br-phys-tx4.pcap
+
+# GARP for lp0 172.24.4.100 on lr0-pub MAC (f0:00:00:00:00:01)
+echo "fffffffffffff0000000000108060001080006040001f00000000001ac180464000000000000ac180464" > expout
+# GARP for 172.24.4.220 on lr0-pub (f0:00:00:00:00:01)
+echo "fffffffffffff0000000000108060001080006040001f00000000001ac1804dc000000000000ac1804dc" >> expout
+# GARP for lp1 172.24.4.200 on lr1-pub MAC (f0:00:00:00:01:01)
+echo "fffffffffffff0000000010108060001080006040001f00000000101ac1804c8000000000000ac1804c8" >> expout
+# GARP for 172.24.4.221 on lr1-pub (f0:00:00:00:01:01)
+echo "fffffffffffff0000000010108060001080006040001f00000000101ac1804dd000000000000ac1804dd" >> expout
+AT_CHECK([sort hv1/br-phys-tx4.pcap], [0], [expout])
+#OVN_CHECK_PACKETS([hv1/br-phys-tx4.pcap], [br-phys.expected])
+
+OVN_CLEANUP([hv1])
+AT_CLEANUP