GBPv6: NAT66 actions for GBP 84/12084/4
authorNeale Ranns <neale.ranns@cisco.com>
Tue, 24 Apr 2018 14:45:33 +0000 (07:45 -0700)
committerDamjan Marion <dmarion.lists@gmail.com>
Wed, 25 Apr 2018 14:00:46 +0000 (14:00 +0000)
Change-Id: I379150a88f2d53d6281be41e8bad6fc4f4e88a71
Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
src/plugins/gbp/gbp_policy_dpo.c
src/vnet/fib/fib_path.c
src/vpp-api/vom/nat_binding.cpp
src/vpp-api/vom/nat_binding_cmds.cpp
src/vpp-api/vom/nat_binding_cmds.hpp
test/test_gbp.py

index bfe5974..0f62fb3 100644 (file)
@@ -92,6 +92,16 @@ gbp_policy_dpo_unlock (dpo_id_t * dpo)
     }
 }
 
+static u32
+gbp_policy_dpo_get_urpf (const dpo_id_t * dpo)
+{
+  gbp_policy_dpo_t *gpd;
+
+  gpd = gbp_policy_dpo_get_from_dpo (dpo);
+
+  return (gpd->gpd_sw_if_index);
+}
+
 void
 gbp_policy_dpo_add_or_lock (dpo_proto_t dproto,
                            epg_id_t epg, u32 sw_if_index, dpo_id_t * dpo)
@@ -138,6 +148,7 @@ const static dpo_vft_t gbp_policy_dpo_vft = {
   .dv_lock = gbp_policy_dpo_lock,
   .dv_unlock = gbp_policy_dpo_unlock,
   .dv_format = format_gbp_policy_dpo,
+  .dv_get_urpf = gbp_policy_dpo_get_urpf,
 };
 
 /**
@@ -499,7 +510,7 @@ VLIB_NODE_FUNCTION_MULTIARCH (gbp_ip4_lpm_classify_node, gbp_ip4_lpm_classify);
 
 VLIB_REGISTER_NODE (gbp_ip6_lpm_classify_node) = {
   .function = gbp_ip6_lpm_classify,
-  .name = "ip6-gpb-lpm-classify",
+  .name = "ip6-gbp-lpm-classify",
   .vector_size = sizeof (u32),
   .format_trace = format_gbp_classify_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
index e974e31..687e716 100644 (file)
@@ -2072,7 +2072,7 @@ fib_path_get_resolving_interface (fib_node_index_t path_index)
     case FIB_PATH_TYPE_BIER_IMP:
        break;
     }
-    return (~0);
+    return (dpo_get_urpf(&path->fp_dpo));
 }
 
 index_t
index eca3f90..64b7a10 100644 (file)
@@ -85,11 +85,20 @@ nat_binding::sweep()
 {
   if (m_binding) {
     if (direction_t::INPUT == m_dir) {
-      HW::enqueue(new nat_binding_cmds::unbind_44_input_cmd(
-        m_binding, m_itf->handle(), m_zone));
+      if (l3_proto_t::IPV4 == m_proto) {
+        HW::enqueue(new nat_binding_cmds::unbind_44_input_cmd(
+          m_binding, m_itf->handle(), m_zone));
+      } else {
+        HW::enqueue(new nat_binding_cmds::unbind_66_input_cmd(
+          m_binding, m_itf->handle(), m_zone));
+      }
     } else {
-      HW::enqueue(new nat_binding_cmds::unbind_44_output_cmd(
-        m_binding, m_itf->handle(), m_zone));
+      if (l3_proto_t::IPV4 == m_proto) {
+        HW::enqueue(new nat_binding_cmds::unbind_44_output_cmd(
+          m_binding, m_itf->handle(), m_zone));
+      } else {
+        VOM_LOG(log_level_t::ERROR) << "NAT 66 output feature not supported";
+      }
     }
   }
   HW::write();
@@ -100,11 +109,20 @@ nat_binding::replay()
 {
   if (m_binding) {
     if (direction_t::INPUT == m_dir) {
-      HW::enqueue(new nat_binding_cmds::bind_44_input_cmd(
-        m_binding, m_itf->handle(), m_zone));
+      if (l3_proto_t::IPV4 == m_proto) {
+        HW::enqueue(new nat_binding_cmds::bind_44_input_cmd(
+          m_binding, m_itf->handle(), m_zone));
+      } else {
+        HW::enqueue(new nat_binding_cmds::bind_66_input_cmd(
+          m_binding, m_itf->handle(), m_zone));
+      }
     } else {
-      HW::enqueue(new nat_binding_cmds::bind_44_output_cmd(
-        m_binding, m_itf->handle(), m_zone));
+      if (l3_proto_t::IPV4 == m_proto) {
+        HW::enqueue(new nat_binding_cmds::bind_44_output_cmd(
+          m_binding, m_itf->handle(), m_zone));
+      } else {
+        VOM_LOG(log_level_t::ERROR) << "NAT 66 output feature not supported";
+      }
     }
   }
 }
@@ -117,11 +135,20 @@ nat_binding::update(const nat_binding& desired)
  */
   if (!m_binding) {
     if (direction_t::INPUT == m_dir) {
-      HW::enqueue(new nat_binding_cmds::bind_44_input_cmd(
-        m_binding, m_itf->handle(), m_zone));
+      if (l3_proto_t::IPV4 == m_proto) {
+        HW::enqueue(new nat_binding_cmds::bind_44_input_cmd(
+          m_binding, m_itf->handle(), m_zone));
+      } else {
+        HW::enqueue(new nat_binding_cmds::bind_66_input_cmd(
+          m_binding, m_itf->handle(), m_zone));
+      }
     } else {
-      HW::enqueue(new nat_binding_cmds::bind_44_output_cmd(
-        m_binding, m_itf->handle(), m_zone));
+      if (l3_proto_t::IPV4 == m_proto) {
+        HW::enqueue(new nat_binding_cmds::bind_44_output_cmd(
+          m_binding, m_itf->handle(), m_zone));
+      } else {
+        VOM_LOG(log_level_t::ERROR) << "NAT 66 output feature not supported";
+      }
     }
   }
 }
@@ -214,6 +241,21 @@ nat_binding::event_handler::handle_populate(const client_db::key_t& key)
                    zone_t::from_vpp(payload.is_inside));
     OM::commit(key, nb);
   }
+
+  std::shared_ptr<nat_binding_cmds::dump_input_66_cmd> i6cmd =
+    std::make_shared<nat_binding_cmds::dump_input_66_cmd>();
+
+  HW::enqueue(i6cmd);
+  HW::write();
+
+  for (auto& record : *i6cmd) {
+    auto& payload = record.get_payload();
+
+    std::shared_ptr<interface> itf = interface::find(payload.sw_if_index);
+    nat_binding nb(*itf, direction_t::INPUT, l3_proto_t::IPV6,
+                   zone_t::from_vpp(payload.is_inside));
+    OM::commit(key, nb);
+  }
 }
 
 dependency_t
index 66b2827..4d98268 100644 (file)
@@ -214,7 +214,7 @@ dump_input_44_cmd::issue(connection& con)
 std::string
 dump_input_44_cmd::to_string() const
 {
-  return ("nat-input-binding-dump");
+  return ("nat-44-input-binding-dump");
 }
 
 dump_output_44_cmd::dump_output_44_cmd()
@@ -246,7 +246,123 @@ dump_output_44_cmd::issue(connection& con)
 std::string
 dump_output_44_cmd::to_string() const
 {
-  return ("nat-output-binding-dump");
+  return ("nat-44-output-binding-dump");
+}
+
+bind_66_input_cmd::bind_66_input_cmd(HW::item<bool>& item,
+                                     const handle_t& itf,
+                                     const nat_binding::zone_t& zone)
+  : rpc_cmd(item)
+  , m_itf(itf)
+  , m_zone(zone)
+{
+}
+
+bool
+bind_66_input_cmd::operator==(const bind_66_input_cmd& other) const
+{
+  return ((m_itf == other.m_itf) && (m_zone == other.m_zone));
+}
+
+rc_t
+bind_66_input_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.is_add = 1;
+  payload.is_inside = (nat_binding::zone_t::INSIDE == m_zone ? 1 : 0);
+  payload.sw_if_index = m_itf.value();
+
+  VAPI_CALL(req.execute());
+
+  m_hw_item.set(wait());
+
+  return rc_t::OK;
+}
+
+std::string
+bind_66_input_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "nat-66-input-binding-create: " << m_hw_item.to_string()
+    << " itf:" << m_itf << " " << m_zone.to_string();
+
+  return (s.str());
+}
+
+unbind_66_input_cmd::unbind_66_input_cmd(HW::item<bool>& item,
+                                         const handle_t& itf,
+                                         const nat_binding::zone_t& zone)
+  : rpc_cmd(item)
+  , m_itf(itf)
+  , m_zone(zone)
+{
+}
+
+bool
+unbind_66_input_cmd::operator==(const unbind_66_input_cmd& other) const
+{
+  return ((m_itf == other.m_itf) && (m_zone == other.m_zone));
+}
+
+rc_t
+unbind_66_input_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.is_add = 0;
+  payload.is_inside = (nat_binding::zone_t::INSIDE == m_zone ? 1 : 0);
+  payload.sw_if_index = m_itf.value();
+
+  VAPI_CALL(req.execute());
+
+  m_hw_item.set(wait());
+
+  return rc_t::OK;
+}
+
+std::string
+unbind_66_input_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "nat-66-input-binding-create: " << m_hw_item.to_string()
+    << " itf:" << m_itf << " " << m_zone.to_string();
+
+  return (s.str());
+}
+
+dump_input_66_cmd::dump_input_66_cmd()
+{
+}
+
+dump_input_66_cmd::dump_input_66_cmd(const dump_input_66_cmd& d)
+{
+}
+
+bool
+dump_input_66_cmd::operator==(const dump_input_66_cmd& other) const
+{
+  return (true);
+}
+
+rc_t
+dump_input_66_cmd::issue(connection& con)
+{
+  m_dump.reset(new msg_t(con.ctx(), std::ref(*this)));
+
+  VAPI_CALL(m_dump->execute());
+
+  wait();
+
+  return rc_t::OK;
+}
+
+std::string
+dump_input_66_cmd::to_string() const
+{
+  return ("nat-66-input-binding-dump");
 }
 
 }; // namespace nat_binding_cmds
index 1b51192..aa5a200 100644 (file)
@@ -336,90 +336,6 @@ private:
   const nat_binding::zone_t m_zone;
 };
 
-/**
- * A functor class that binds a NAT configuration to an output interface
- */
-/* class bind_66_output_cmd */
-/*   : public rpc_cmd<HW::item<bool>, */
-/*                    rc_t, */
-/*                    vapi::Nat66_interface_add_del_output_feature> */
-/* { */
-/* public: */
-/*   /\** */
-/*    * Constructor */
-/*    *\/ */
-/*   bind_66_output_cmd(HW::item<bool>& item, */
-/*                      const handle_t& itf, */
-/*                      const nat_binding::zone_t& zone); */
-
-/*   /\** */
-/*    * Issue the command to VPP/HW */
-/*    *\/ */
-/*   rc_t issue(connection& con); */
-/*   /\** */
-/*    * convert to string format for debug purposes */
-/*    *\/ */
-/*   std::string to_string() const; */
-
-/*   /\** */
-/*    * Comparison operator - only used for UT */
-/*    *\/ */
-/*   bool operator==(const bind_66_output_cmd& i) const; */
-
-/* private: */
-/*   /\** */
-/*    * The interface to bind */
-/*    *\/ */
-/*   const handle_t m_itf; */
-
-/*   /\** */
-/*    * The zone the interface is in */
-/*    *\/ */
-/*   const nat_binding::zone_t m_zone; */
-/* }; */
-
-/* /\** */
-/*  * A cmd class that unbinds a NAT configuration from an output interface */
-/*  *\/ */
-/* class unbind_66_output_cmd */
-/*   : public rpc_cmd<HW::item<bool>, */
-/*                    rc_t, */
-/*                    vapi::Nat66_interface_add_del_output_feature> */
-/* { */
-/* public: */
-/*   /\** */
-/*    * Constructor */
-/*    *\/ */
-/*   unbind_66_output_cmd(HW::item<bool>& item, */
-/*                        const handle_t& itf, */
-/*                        const nat_binding::zone_t& zone); */
-
-/*   /\** */
-/*    * Issue the command to VPP/HW */
-/*    *\/ */
-/*   rc_t issue(connection& con); */
-/*   /\** */
-/*    * convert to string format for debug purposes */
-/*    *\/ */
-/*   std::string to_string() const; */
-
-/*   /\** */
-/*    * Comparison operator - only used for UT */
-/*    *\/ */
-/*   bool operator==(const unbind_66_output_cmd& i) const; */
-
-/* private: */
-/*   /\** */
-/*    * The interface to bind */
-/*    *\/ */
-/*   const handle_t m_itf; */
-
-/*   /\** */
-/*    * The zone the interface is in */
-/*    *\/ */
-/*   const nat_binding::zone_t m_zone; */
-/* }; */
-
 /**
  * A cmd class that Dumps all the nat_statics
  */
@@ -453,40 +369,6 @@ private:
   HW::item<bool> item;
 };
 
-/**
- * A cmd class that Dumps all the nat_statics
- */
-/* class dump_output_66_cmd */
-/*   : public dump_cmd<vapi::Nat66_interface_output_feature_dump> */
-/* { */
-/* public: */
-/*   /\** */
-/*    * Constructor */
-/*    *\/ */
-/*   dump_output_66_cmd(); */
-/*   dump_output_66_cmd(const dump_output_66_cmd& d); */
-
-/*   /\** */
-/*    * Issue the command to VPP/HW */
-/*    *\/ */
-/*   rc_t issue(connection& con); */
-/*   /\** */
-/*    * convert to string format for debug purposes */
-/*    *\/ */
-/*   std::string to_string() const; */
-
-/*   /\** */
-/*    * Comparison operator - only used for UT */
-/*    *\/ */
-/*   bool operator==(const dump_output_66_cmd& i) const; */
-
-/* private: */
-/*   /\** */
-/*    * HW reutrn code */
-/*    *\/ */
-/*   HW::item<bool> item; */
-/* }; */
-
 }; // namespace nat_binding_cmds
 }; // namespace VOM
 
index 805f261..3d9ebbc 100644 (file)
@@ -45,9 +45,13 @@ class VppGbpEndpoint(VppObject):
         if is_ip6:
             self.proto = DpoProto.DPO_PROTO_IP6
             self.af = AF_INET6
+            self.is_ip6 = True
+            self.ip_len = 128
         else:
             self.proto = DpoProto.DPO_PROTO_IP4
             self.af = AF_INET
+            self.is_ip6 = False
+            self.ip_len = 32
         self.ip_n = inet_pton(self.af, ip)
         self.floating_ip_n = inet_pton(self.af, fip)
 
@@ -184,7 +188,8 @@ class VppGbpSubnet(VppObject):
         ss = self._test.vapi.gbp_subnet_dump()
         for s in ss:
             if s.subnet.table_id == self.table_id and \
-               s.subnet.address_length == self.address_len:
+               s.subnet.address_length == self.address_len and \
+               s.subnet.is_ip6 == self.is_ip6:
                 if self.is_ip6:
                     if s.subnet.address == self.address_n:
                         return True
@@ -353,6 +358,16 @@ class TestGBP(VppTestCase):
             self.assertEqual(r[IP].dst, tx[0][IP].dst)
         return rx
 
+    def send_and_expect_natted6(self, src, tx, dst, src_ip):
+        rx = self.send_and_expect(src, tx, dst)
+
+        for r in rx:
+            self.assertEqual(r[Ether].src, tx[0][Ether].src)
+            self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+            self.assertEqual(r[IPv6].src, src_ip)
+            self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst)
+        return rx
+
     def send_and_expect_unnatted(self, src, tx, dst, dst_ip):
         rx = self.send_and_expect(src, tx, dst)
 
@@ -363,6 +378,16 @@ class TestGBP(VppTestCase):
             self.assertEqual(r[IP].src, tx[0][IP].src)
         return rx
 
+    def send_and_expect_unnatted6(self, src, tx, dst, dst_ip):
+        rx = self.send_and_expect(src, tx, dst)
+
+        for r in rx:
+            self.assertEqual(r[Ether].src, tx[0][Ether].src)
+            self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+            self.assertEqual(r[IPv6].dst, dst_ip)
+            self.assertEqual(r[IPv6].src, tx[0][IPv6].src)
+        return rx
+
     def send_and_expect_double_natted(self, src, tx, dst, src_ip, dst_ip):
         rx = self.send_and_expect(src, tx, dst)
 
@@ -373,6 +398,16 @@ class TestGBP(VppTestCase):
             self.assertEqual(r[IP].src, src_ip)
         return rx
 
+    def send_and_expect_double_natted6(self, src, tx, dst, src_ip, dst_ip):
+        rx = self.send_and_expect(src, tx, dst)
+
+        for r in rx:
+            self.assertEqual(r[Ether].src, self.router_mac)
+            self.assertEqual(r[Ether].dst, dst.remote_mac)
+            self.assertEqual(r[IPv6].dst, dst_ip)
+            self.assertEqual(r[IPv6].src, src_ip)
+        return rx
+
     def test_gbp(self):
         """ Group Based Policy """
 
@@ -486,9 +521,9 @@ class TestGBP(VppTestCase):
                 self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index,
                                                           is_inside=1,
                                                           is_add=1)
-                self.vapi.nat66_add_del_interface(epg.bvi.sw_if_index,
-                #                                  is_inside=1,
-                #                                  is_add=1)
+                self.vapi.nat66_add_del_interface(epg.bvi.sw_if_index,
+                                                  is_inside=1,
+                                                  is_add=1)
 
             self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index,
                                                    epg.bvi_ip4_n,
@@ -496,10 +531,11 @@ class TestGBP(VppTestCase):
             self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index,
                                                    epg.bvi_ip6_n,
                                                    128,
-                                                   is_ipv6=1)
+                                                   is_ipv6=True)
 
             # EPG uplink interfaces in the BD
             epg.uplink.set_table_ip4(epg.rd)
+            epg.uplink.set_table_ip6(epg.rd)
             self.vapi.sw_interface_set_l2_bridge(epg.uplink.sw_if_index,
                                                  epg.bd)
 
@@ -533,6 +569,7 @@ class TestGBP(VppTestCase):
         for recirc in recircs:
             # EPG's ingress recirculation interface maps to its RD
             recirc.recirc.set_table_ip4(recirc.epg.rd)
+            recirc.recirc.set_table_ip6(recirc.epg.rd)
 
             # in the bridge to allow DVR. L2 emulation to punt to L3
             self.vapi.sw_interface_set_l2_bridge(recirc.recirc.sw_if_index,
@@ -540,22 +577,14 @@ class TestGBP(VppTestCase):
             self.vapi.sw_interface_set_l2_emulation(
                 recirc.recirc.sw_if_index)
 
-            if recirc.is_ext:
-                # recirc interfaces on NAT EPGs are outside and an
-                # output feature
-                self.vapi.nat44_interface_add_del_output_feature(
-                    recirc.recirc.sw_if_index,
-                    is_inside=0,
-                    is_add=1)
-            else:
-                self.vapi.nat44_interface_add_del_feature(
-                    recirc.recirc.sw_if_index,
-                    is_inside=0,
-                    is_add=1)
-                # self.vapi.nat66_add_del_interface(
-                #    recirc.recirc.sw_if_index,
-                #    is_inside=0,
-                #    is_add=1)
+            self.vapi.nat44_interface_add_del_feature(
+                recirc.recirc.sw_if_index,
+                is_inside=0,
+                is_add=1)
+            self.vapi.nat66_add_del_interface(
+                recirc.recirc.sw_if_index,
+                is_inside=0,
+                is_add=1)
 
             recirc.add_vpp_config()
 
@@ -569,7 +598,7 @@ class TestGBP(VppTestCase):
             # adj-fibs due to the fact the the BVI address has /32 and
             # the subnet is not attached.
             #
-            r = VppIpRoute(self, ep.ip, 32,
+            r = VppIpRoute(self, ep.ip, ep.ip_len,
                            [VppRoutePath(ep.ip,
                                          ep.epg.bvi.sw_if_index,
                                          proto=ep.proto)],
@@ -610,10 +639,10 @@ class TestGBP(VppTestCase):
                                                        ep.floating_ip_n,
                                                        vrf_id=0,
                                                        addr_only=1)
-            else:
-            # self.vapi.nat66_add_del_static_mapping(ep.ip_n,
-            #                                       ep.floating_ip_n,
-            #                                       vrf_id=20)
+            else:
+                self.vapi.nat66_add_del_static_mapping(ep.ip_n,
+                                                       ep.floating_ip_n,
+                                                       vrf_id=0)
 
             # VPP EP create ...
             ep.add_vpp_config()
@@ -633,11 +662,11 @@ class TestGBP(VppTestCase):
             self.vapi.bd_ip_mac_add_del(bd_id=epg_nat.bd,
                                         mac=ep.bin_mac,
                                         ip=ep.floating_ip_n,
-                                        is_ipv6=0,
+                                        is_ipv6=ep.is_ip6,
                                         is_add=1)
 
             # floating IPs route via EPG recirc
-            r = VppIpRoute(self, ep.floating_ip, 32,
+            r = VppIpRoute(self, ep.floating_ip, ep.ip_len,
                            [VppRoutePath(ep.floating_ip,
                                          ep.recirc.recirc.sw_if_index,
                                          is_dvr=1,
@@ -916,21 +945,39 @@ class TestGBP(VppTestCase):
                            sw_if_index=recirc_nat.recirc.sw_if_index,
                            epg=epg_nat.epg)
         se2.add_vpp_config()
+        se16 = VppGbpSubnet(self, 0, "::", 0,
+                            is_internal=False,
+                            sw_if_index=recirc_nat.recirc.sw_if_index,
+                            epg=epg_nat.epg,
+                            is_ip6=True)
+        se16.add_vpp_config()
         # in the NAT RD an external subnet via the NAT EPG's uplink
         se3 = VppGbpSubnet(self, 20, "0.0.0.0", 0,
                            is_internal=False,
                            sw_if_index=epg_nat.uplink.sw_if_index,
                            epg=epg_nat.epg)
+        se36 = VppGbpSubnet(self, 20, "::", 0,
+                            is_internal=False,
+                            sw_if_index=epg_nat.uplink.sw_if_index,
+                            epg=epg_nat.epg,
+                            is_ip6=True)
         se4 = VppGbpSubnet(self, 20, "11.0.0.0", 8,
                            is_internal=False,
                            sw_if_index=epg_nat.uplink.sw_if_index,
                            epg=epg_nat.epg)
         se3.add_vpp_config()
+        se36.add_vpp_config()
         se4.add_vpp_config()
 
         self.logger.info(self.vapi.cli("sh ip fib 0.0.0.0/0"))
         self.logger.info(self.vapi.cli("sh ip fib 11.0.0.1"))
+        self.logger.info(self.vapi.cli("sh ip6 fib ::/0"))
+        self.logger.info(self.vapi.cli("sh ip6 fib %s" %
+                                       eps[4].floating_ip))
 
+        #
+        # From an EP to an outside addess: IN2OUT
+        #
         pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac,
                                              dst=self.router_mac) /
                                        IP(src=eps[0].ip, dst="1.1.1.1") /
@@ -947,8 +994,22 @@ class TestGBP(VppTestCase):
         self.send_and_expect_natted(self.pg0,
                                     pkt_inter_epg_220_to_global * 65,
                                     self.pg7,
-                                    "11.0.0.1")
+                                    eps[0].floating_ip)
 
+        pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac,
+                                             dst=self.router_mac) /
+                                       IPv6(src=eps[4].ip, dst="6001::1") /
+                                       UDP(sport=1234, dport=1234) /
+                                       Raw('\xa5' * 100))
+
+        self.send_and_expect_natted6(self.pg0,
+                                     pkt_inter_epg_220_to_global * 65,
+                                     self.pg7,
+                                     eps[4].floating_ip)
+
+        #
+        # From a global address to an EP: OUT2IN
+        #
         pkt_inter_epg_220_from_global = (Ether(src=self.router_mac,
                                                dst=self.pg0.remote_mac) /
                                          IP(dst=eps[0].floating_ip,
@@ -964,9 +1025,25 @@ class TestGBP(VppTestCase):
 
         self.send_and_expect_unnatted(self.pg7,
                                       pkt_inter_epg_220_from_global * 65,
-                                      self.pg0,
-                                      "10.0.0.1")
+                                      eps[0].itf,
+                                      eps[0].ip)
 
+        pkt_inter_epg_220_from_global = (Ether(src=self.router_mac,
+                                               dst=self.pg0.remote_mac) /
+                                         IPv6(dst=eps[4].floating_ip,
+                                              src="6001::1") /
+                                         UDP(sport=1234, dport=1234) /
+                                         Raw('\xa5' * 100))
+
+        self.send_and_expect_unnatted6(self.pg7,
+                                       pkt_inter_epg_220_from_global * 65,
+                                       eps[4].itf,
+                                       eps[4].ip)
+
+        #
+        # From a local VM to another local VM using resp. public addresses:
+        #  IN2OUT2IN
+        #
         pkt_intra_epg_220_global = (Ether(src=self.pg0.remote_mac,
                                           dst=self.router_mac) /
                                     IP(src=eps[0].ip,
@@ -974,11 +1051,24 @@ class TestGBP(VppTestCase):
                                     UDP(sport=1234, dport=1234) /
                                     Raw('\xa5' * 100))
 
-        self.send_and_expect_double_natted(self.pg0,
+        self.send_and_expect_double_natted(eps[0].itf,
                                            pkt_intra_epg_220_global * 65,
-                                           self.pg1,
-                                           "11.0.0.1",
-                                           "10.0.0.2")
+                                           eps[1].itf,
+                                           eps[0].floating_ip,
+                                           eps[1].ip)
+
+        pkt_intra_epg_220_global = (Ether(src=self.pg4.remote_mac,
+                                          dst=self.router_mac) /
+                                    IPv6(src=eps[4].ip,
+                                         dst=eps[5].floating_ip) /
+                                    UDP(sport=1234, dport=1234) /
+                                    Raw('\xa5' * 100))
+
+        self.send_and_expect_double_natted6(eps[4].itf,
+                                            pkt_intra_epg_220_global * 65,
+                                            eps[5].itf,
+                                            eps[4].floating_ip,
+                                            eps[5].ip)
 
         #
         # cleanup
@@ -991,11 +1081,11 @@ class TestGBP(VppTestCase):
                                                        vrf_id=0,
                                                        addr_only=1,
                                                        is_add=0)
-            else:
-            # self.vapi.nat66_add_del_static_mapping(ep.ip_n,
-            #                                       ep.floating_ip_n,
-            #                                       vrf_id=0,
-            #                                       is_add=0)
+            else:
+                self.vapi.nat66_add_del_static_mapping(ep.ip_n,
+                                                       ep.floating_ip_n,
+                                                       vrf_id=0,
+                                                       is_add=0)
 
         for epg in epgs:
             # IP config on the BVI interfaces
@@ -1003,37 +1093,39 @@ class TestGBP(VppTestCase):
                                                    epg.bvi_ip4_n,
                                                    32,
                                                    is_add=0)
+            self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index,
+                                                   epg.bvi_ip6_n,
+                                                   128,
+                                                   is_add=0,
+                                                   is_ipv6=True)
             self.logger.info(self.vapi.cli("sh int addr"))
 
             epg.uplink.set_table_ip4(0)
+            epg.uplink.set_table_ip6(0)
 
             if epg != epgs[0] and epg != epgs[3]:
                 epg.bvi.set_table_ip4(0)
+                epg.bvi.set_table_ip6(0)
 
                 self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index,
                                                           is_inside=1,
                                                           is_add=0)
-                self.vapi.nat66_add_del_interface(epg.bvi.sw_if_index,
-                #                                  is_inside=1,
-                #                                  is_add=0)
+                self.vapi.nat66_add_del_interface(epg.bvi.sw_if_index,
+                                                  is_inside=1,
+                                                  is_add=0)
 
         for recirc in recircs:
             recirc.recirc.set_table_ip4(0)
-
-            if recirc.is_ext:
-                self.vapi.nat44_interface_add_del_output_feature(
-                    recirc.recirc.sw_if_index,
-                    is_inside=0,
-                    is_add=0)
-            else:
-                self.vapi.nat44_interface_add_del_feature(
-                    recirc.recirc.sw_if_index,
-                    is_inside=0,
-                    is_add=0)
-                # self.vapi.nat66_add_del_interface(
-                #    recirc.recirc.sw_if_index,
-                #    is_inside=0,
-                #    is_add=0)
+            recirc.recirc.set_table_ip6(0)
+
+            self.vapi.nat44_interface_add_del_feature(
+                recirc.recirc.sw_if_index,
+                is_inside=0,
+                is_add=0)
+            self.vapi.nat66_add_del_interface(
+                recirc.recirc.sw_if_index,
+                is_inside=0,
+                is_add=0)
 
 
 if __name__ == '__main__':