NAT44: identity NAT (VPP-1073) 99/9599/2
authorMatus Fabian <matfabia@cisco.com>
Tue, 28 Nov 2017 12:29:41 +0000 (04:29 -0800)
committerOle Trøan <otroan@employees.org>
Thu, 30 Nov 2017 12:09:11 +0000 (12:09 +0000)
Identity mapping translate an IP address to itself.

Change-Id: Icc0ca5102d32547a4b0c75720b5f5bf41ed69c71
Signed-off-by: Matus Fabian <matfabia@cisco.com>
src/plugins/nat/nat.api
src/plugins/nat/nat.c
src/plugins/nat/nat_api.c
test/test_nat.py
test/vpp_papi_provider.py

index d515a76..d8e0d7e 100644 (file)
@@ -393,6 +393,58 @@ define nat44_static_mapping_details {
   u32 vrf_id;
 };
 
+/** \brief Add/delete NAT44 identity mapping
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param is_add - 1 if add, 0 if delete
+    @param addr_only - 1 if address only mapping
+    @param ip_address - IPv4 address
+    @param protocol - IP protocol
+    @param port - port number
+    @param sw_if_index - interface (if set ip_address is ignored, ~0 means not
+                                    used)
+    @param vfr_id - VRF ID (if ~0 use default VRF)
+*/
+autoreply define nat44_add_del_identity_mapping {
+  u32 client_index;
+  u32 context;
+  u8 is_add;
+  u8 addr_only;
+  u8 ip_address[4];
+  u8 protocol;
+  u16 port;
+  u32 sw_if_index;
+  u32 vrf_id;
+};
+
+/** \brief Dump NAT44 identity mappings
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+define nat44_identity_mapping_dump {
+  u32 client_index;
+  u32 context;
+};
+
+/** \brief NAT44 identity mapping details response
+    @param context - sender context, to match reply w/ request
+    @param addr_only - 1 if address only mapping
+    @param ip_address - IPv4 address
+    @param protocol - IP protocol
+    @param port - port number
+    @param sw_if_index - interface
+    @param vfr_id - VRF ID
+*/
+define nat44_identity_mapping_details {
+  u32 context;
+  u8 addr_only;
+  u8 ip_address[4];
+  u8 protocol;
+  u16 port;
+  u32 sw_if_index;
+  u32 vrf_id;
+};
+
 /** \brief Add/delete NAT44 pool address from specific interfce
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
index 8c188f6..896fe5f 100644 (file)
@@ -733,7 +733,7 @@ delete:
       pool_put (sm->static_mappings, m);
     }
 
-  if (!addr_only)
+  if (!addr_only || (l_addr.as_u32 == e_addr.as_u32))
     return 0;
 
   /* Add/delete external address to FIB */
@@ -1240,7 +1240,7 @@ fib:
 
   pool_foreach (m, sm->static_mappings,
   ({
-    if (!(m->addr_only))
+    if (!(m->addr_only) || (m->local_addr.as_u32 == m->external_addr.as_u32))
       continue;
 
     snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del);
@@ -1990,7 +1990,7 @@ add_static_mapping_command_fn (vlib_main_t * vm,
   u32 sw_if_index = ~0;
   vnet_main_t * vnm = vnet_get_main();
   int rv;
-  snat_protocol_t proto;
+  snat_protocol_t proto = ~0;
   u8 proto_set = 0;
 
   /* Get a line of input. */
@@ -2089,6 +2089,100 @@ VLIB_CLI_COMMAND (add_static_mapping_command, static) = {
     "nat44 add static mapping tcp|udp|icmp local <addr> [<port>] external <addr> [<port>] [vrf <table-id>] [del]",
 };
 
+static clib_error_t *
+add_identity_mapping_command_fn (vlib_main_t * vm,
+                                 unformat_input_t * input,
+                                 vlib_cli_command_t * cmd)
+{
+  unformat_input_t _line_input, *line_input = &_line_input;
+  clib_error_t * error = 0;
+  ip4_address_t addr;
+  u32 port = 0, vrf_id = ~0;
+  int is_add = 1;
+  int addr_only = 1;
+  u32 sw_if_index = ~0;
+  vnet_main_t * vnm = vnet_get_main();
+  int rv;
+  snat_protocol_t proto;
+
+  addr.as_u32 = 0;
+
+  /* Get a line of input. */
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "%U", unformat_ip4_address, &addr))
+        ;
+      else if (unformat (line_input, "external %U",
+                         unformat_vnet_sw_interface, vnm, &sw_if_index))
+        ;
+      else if (unformat (line_input, "vrf %u", &vrf_id))
+        ;
+      else if (unformat (line_input, "%U %u", unformat_snat_protocol, &proto,
+                         &port))
+        addr_only = 0;
+      else if (unformat (line_input, "del"))
+        is_add = 0;
+      else
+        {
+          error = clib_error_return (0, "unknown input: '%U'",
+            format_unformat_error, line_input);
+          goto done;
+        }
+    }
+
+  rv = snat_add_static_mapping(addr, addr, (u16) port, (u16) port,
+                               vrf_id, addr_only, sw_if_index, proto, is_add);
+
+  switch (rv)
+    {
+    case VNET_API_ERROR_INVALID_VALUE:
+      error = clib_error_return (0, "External port already in use.");
+      goto done;
+    case VNET_API_ERROR_NO_SUCH_ENTRY:
+      if (is_add)
+        error = clib_error_return (0, "External addres must be allocated.");
+      else
+        error = clib_error_return (0, "Mapping not exist.");
+      goto done;
+    case VNET_API_ERROR_NO_SUCH_FIB:
+      error = clib_error_return (0, "No such VRF id.");
+      goto done;
+    case VNET_API_ERROR_VALUE_EXIST:
+      error = clib_error_return (0, "Mapping already exist.");
+      goto done;
+    default:
+      break;
+    }
+
+done:
+  unformat_free (line_input);
+
+  return error;
+}
+
+/*?
+ * @cliexpar
+ * @cliexstart{snat add identity mapping}
+ * Identity mapping translate an IP address to itself.
+ * To create identity mapping for address 10.0.0.3 port 6303 for TCP protocol
+ * use:
+ *  vpp# nat44 add identity mapping 10.0.0.3 tcp 6303
+ * To create identity mapping for address 10.0.0.3 use:
+ *  vpp# nat44 add identity mapping 10.0.0.3
+ * To create identity mapping for DHCP addressed interface use:
+ *  vpp# nat44 add identity mapping GigabitEthernet0/a/0 tcp 3606
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (add_identity_mapping_command, static) = {
+  .path = "nat44 add identity mapping",
+  .function = add_identity_mapping_command_fn,
+  .short_help = "nat44 add identity mapping <interface>|<ip4-addr> "
+    "[<protocol> <port>] [vrf <table-id>] [del]",
+};
+
 static clib_error_t *
 add_lb_static_mapping_command_fn (vlib_main_t * vm,
                                   unformat_input_t * input,
@@ -2999,6 +3093,7 @@ snat_ip4_add_del_interface_address_cb (ip4_main_t * im,
   snat_main_t *sm = &snat_main;
   snat_static_map_resolve_t *rp;
   u32 *indices_to_delete = 0;
+  ip4_address_t l_addr;
   int i, j;
   int rv;
 
@@ -3021,8 +3116,13 @@ snat_ip4_add_del_interface_address_cb (ip4_main_t * im,
                   /* On this interface? */
                   if (rp->sw_if_index == sw_if_index)
                     {
+                      /* Indetity mapping? */
+                      if (rp->l_addr.as_u32 == 0)
+                        l_addr.as_u32 = address[0].as_u32;
+                      else
+                        l_addr.as_u32 = rp->l_addr.as_u32;
                       /* Add the static mapping */
-                      rv = snat_add_static_mapping (rp->l_addr,
+                      rv = snat_add_static_mapping (l_addr,
                                                     address[0],
                                                     rp->l_port,
                                                     rp->e_port,
@@ -3032,7 +3132,7 @@ snat_ip4_add_del_interface_address_cb (ip4_main_t * im,
                                                     rp->proto,
                                                     rp->is_add);
                       if (rv)
-                        clib_warning ("snat_add_static_mapping returned %d", 
+                        clib_warning ("snat_add_static_mapping returned %d",
                                       rv);
                       vec_add1 (indices_to_delete, j);
                     }
index eae9764..3c58097 100644 (file)
@@ -798,7 +798,7 @@ vl_api_nat44_static_mapping_dump_t_handler (vl_api_nat44_static_mapping_dump_t
   /* *INDENT-OFF* */
   pool_foreach (m, sm->static_mappings,
   ({
-      if (!vec_len(m->locals))
+      if (!vec_len(m->locals) && (m->local_addr.as_u32 != m->external_addr.as_u32))
         send_nat44_static_mapping_details (m, q, mp->context);
   }));
   /* *INDENT-ON* */
@@ -806,7 +806,8 @@ vl_api_nat44_static_mapping_dump_t_handler (vl_api_nat44_static_mapping_dump_t
   for (j = 0; j < vec_len (sm->to_resolve); j++)
     {
       rp = sm->to_resolve + j;
-      send_nat44_static_map_resolve_details (rp, q, mp->context);
+      if (rp->l_addr.as_u32 != 0)
+       send_nat44_static_map_resolve_details (rp, q, mp->context);
     }
 }
 
@@ -821,6 +822,145 @@ vl_api_nat44_static_mapping_dump_t_print (vl_api_nat44_static_mapping_dump_t *
   FINISH;
 }
 
+static void
+  vl_api_nat44_add_del_identity_mapping_t_handler
+  (vl_api_nat44_add_del_identity_mapping_t * mp)
+{
+  snat_main_t *sm = &snat_main;
+  vl_api_nat44_add_del_identity_mapping_reply_t *rmp;
+  ip4_address_t addr;
+  u16 port = 0;
+  u32 vrf_id, sw_if_index;
+  int rv = 0;
+  snat_protocol_t proto = ~0;
+
+  if (mp->addr_only == 0)
+    {
+      port = clib_net_to_host_u16 (mp->port);
+      proto = ip_proto_to_snat_proto (mp->protocol);
+    }
+  vrf_id = clib_net_to_host_u32 (mp->vrf_id);
+  sw_if_index = clib_net_to_host_u32 (mp->sw_if_index);
+  if (sw_if_index != ~0)
+    addr.as_u32 = 0;
+  else
+    memcpy (&addr.as_u8, mp->ip_address, 4);
+
+
+  rv =
+    snat_add_static_mapping (addr, addr, port, port, vrf_id, mp->addr_only,
+                            sw_if_index, proto, mp->is_add);
+
+  REPLY_MACRO (VL_API_NAT44_ADD_DEL_IDENTITY_MAPPING_REPLY);
+}
+
+static void *vl_api_nat44_add_del_identity_mapping_t_print
+  (vl_api_nat44_add_del_identity_mapping_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: nat44_add_del_identity_mapping ");
+  if (mp->sw_if_index != ~0)
+    s = format (s, "sw_if_index %d", clib_net_to_host_u32 (mp->sw_if_index));
+  else
+    s = format (s, "addr %U", format_ip4_address, mp->ip_address);
+
+  if (mp->addr_only == 0)
+    s =
+      format (s, "protocol %d port %d", mp->protocol,
+             clib_net_to_host_u16 (mp->port));
+
+  if (mp->vrf_id != ~0)
+    s = format (s, "vrf %d", clib_net_to_host_u32 (mp->vrf_id));
+
+  FINISH;
+}
+
+static void
+send_nat44_identity_mapping_details (snat_static_mapping_t * m,
+                                    unix_shared_memory_queue_t * q,
+                                    u32 context)
+{
+  vl_api_nat44_identity_mapping_details_t *rmp;
+  snat_main_t *sm = &snat_main;
+
+  rmp = vl_msg_api_alloc (sizeof (*rmp));
+  memset (rmp, 0, sizeof (*rmp));
+  rmp->_vl_msg_id =
+    ntohs (VL_API_NAT44_IDENTITY_MAPPING_DETAILS + sm->msg_id_base);
+  rmp->addr_only = m->addr_only;
+  clib_memcpy (rmp->ip_address, &(m->local_addr), 4);
+  rmp->port = htons (m->local_port);
+  rmp->sw_if_index = ~0;
+  rmp->vrf_id = htonl (m->vrf_id);
+  rmp->protocol = snat_proto_to_ip_proto (m->proto);
+  rmp->context = context;
+
+  vl_msg_api_send_shmem (q, (u8 *) & rmp);
+}
+
+static void
+send_nat44_identity_map_resolve_details (snat_static_map_resolve_t * m,
+                                        unix_shared_memory_queue_t * q,
+                                        u32 context)
+{
+  vl_api_nat44_identity_mapping_details_t *rmp;
+  snat_main_t *sm = &snat_main;
+
+  rmp = vl_msg_api_alloc (sizeof (*rmp));
+  memset (rmp, 0, sizeof (*rmp));
+  rmp->_vl_msg_id =
+    ntohs (VL_API_NAT44_IDENTITY_MAPPING_DETAILS + sm->msg_id_base);
+  rmp->addr_only = m->addr_only;
+  rmp->port = htons (m->l_port);
+  rmp->sw_if_index = htonl (m->sw_if_index);
+  rmp->vrf_id = htonl (m->vrf_id);
+  rmp->protocol = snat_proto_to_ip_proto (m->proto);
+  rmp->context = context;
+
+  vl_msg_api_send_shmem (q, (u8 *) & rmp);
+}
+
+static void
+  vl_api_nat44_identity_mapping_dump_t_handler
+  (vl_api_nat44_identity_mapping_dump_t * mp)
+{
+  unix_shared_memory_queue_t *q;
+  snat_main_t *sm = &snat_main;
+  snat_static_mapping_t *m;
+  snat_static_map_resolve_t *rp;
+  int j;
+
+  q = vl_api_client_index_to_input_queue (mp->client_index);
+  if (q == 0)
+    return;
+
+  /* *INDENT-OFF* */
+  pool_foreach (m, sm->static_mappings,
+  ({
+      if (!vec_len(m->locals) && (m->local_addr.as_u32 == m->external_addr.as_u32))
+        send_nat44_identity_mapping_details (m, q, mp->context);
+  }));
+  /* *INDENT-ON* */
+
+  for (j = 0; j < vec_len (sm->to_resolve); j++)
+    {
+      rp = sm->to_resolve + j;
+      if (rp->l_addr.as_u32 == 0)
+       send_nat44_identity_map_resolve_details (rp, q, mp->context);
+    }
+}
+
+static void *vl_api_nat44_identity_mapping_dump_t_print
+  (vl_api_nat44_identity_mapping_dump_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: nat44_identity_mapping_dump ");
+
+  FINISH;
+}
+
 static void
   vl_api_nat44_add_del_interface_addr_t_handler
   (vl_api_nat44_add_del_interface_addr_t * mp)
@@ -2320,7 +2460,9 @@ _(NAT_REASS_DUMP, nat_reass_dump)                                       \
 _(NAT44_ADD_DEL_ADDRESS_RANGE, nat44_add_del_address_range)             \
 _(NAT44_INTERFACE_ADD_DEL_FEATURE, nat44_interface_add_del_feature)     \
 _(NAT44_ADD_DEL_STATIC_MAPPING, nat44_add_del_static_mapping)           \
+_(NAT44_ADD_DEL_IDENTITY_MAPPING, nat44_add_del_identity_mapping)       \
 _(NAT44_STATIC_MAPPING_DUMP, nat44_static_mapping_dump)                 \
+_(NAT44_IDENTITY_MAPPING_DUMP, nat44_identity_mapping_dump)             \
 _(NAT44_ADDRESS_DUMP, nat44_address_dump)                               \
 _(NAT44_INTERFACE_DUMP, nat44_interface_dump)                           \
 _(NAT44_ADD_DEL_INTERFACE_ADDR, nat44_add_del_interface_addr)           \
index 6eb54dd..0448fae 100644 (file)
@@ -783,6 +783,17 @@ class TestNAT44(MethodHolder):
                 local_num=0,
                 locals=[])
 
+        identity_mappings = self.vapi.nat44_identity_mapping_dump()
+        for id_m in identity_mappings:
+            self.vapi.nat44_add_del_identity_mapping(
+                addr_only=id_m.addr_only,
+                ip=id_m.ip_address,
+                port=id_m.port,
+                sw_if_index=id_m.sw_if_index,
+                vrf_id=id_m.vrf_id,
+                protocol=id_m.protocol,
+                is_add=0)
+
         adresses = self.vapi.nat44_address_dump()
         for addr in adresses:
             self.vapi.nat44_add_del_address_range(addr.ip_address,
@@ -1190,6 +1201,35 @@ class TestNAT44(MethodHolder):
         self.pg_start()
         self.pg3.assert_nothing_captured()
 
+    def test_identity_nat(self):
+        """ Identity NAT """
+
+        self.vapi.nat44_add_del_identity_mapping(ip=self.pg0.remote_ip4n)
+        self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index)
+        self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index,
+                                                  is_inside=0)
+
+        p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+             IP(src=self.pg1.remote_ip4, dst=self.pg0.remote_ip4) /
+             TCP(sport=12345, dport=56789))
+        self.pg1.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg0.get_capture(1)
+        p = capture[0]
+        try:
+            ip = p[IP]
+            tcp = p[TCP]
+            self.assertEqual(ip.dst, self.pg0.remote_ip4)
+            self.assertEqual(ip.src, self.pg1.remote_ip4)
+            self.assertEqual(tcp.dport, 56789)
+            self.assertEqual(tcp.sport, 12345)
+            self.check_tcp_checksum(p)
+            self.check_ip_checksum(p)
+        except:
+            self.logger.error(ppp("Unexpected or invalid packet:", p))
+            raise
+
     def test_static_lb(self):
         """ NAT44 local service load balancing """
         external_addr_n = socket.inet_pton(socket.AF_INET, self.nat_addr)
@@ -1785,6 +1825,38 @@ class TestNAT44(MethodHolder):
         static_mappings = self.vapi.nat44_static_mapping_dump()
         self.assertEqual(0, len(static_mappings))
 
+    def test_interface_addr_identity_nat(self):
+        """ Identity NAT with addresses from interface """
+
+        port = 53053
+        self.vapi.nat44_add_interface_addr(self.pg7.sw_if_index)
+        self.vapi.nat44_add_del_identity_mapping(
+            sw_if_index=self.pg7.sw_if_index,
+            port=port,
+            protocol=IP_PROTOS.tcp,
+            addr_only=0)
+
+        # identity mappings with external interface
+        identity_mappings = self.vapi.nat44_identity_mapping_dump()
+        self.assertEqual(1, len(identity_mappings))
+        self.assertEqual(self.pg7.sw_if_index,
+                         identity_mappings[0].sw_if_index)
+
+        # configure interface address and check identity mappings
+        self.pg7.config_ip4()
+        identity_mappings = self.vapi.nat44_identity_mapping_dump()
+        self.assertEqual(1, len(identity_mappings))
+        self.assertEqual(identity_mappings[0].ip_address,
+                         self.pg7.local_ip4n)
+        self.assertEqual(0xFFFFFFFF, identity_mappings[0].sw_if_index)
+        self.assertEqual(port, identity_mappings[0].port)
+        self.assertEqual(IP_PROTOS.tcp, identity_mappings[0].protocol)
+
+        # remove interface address and check identity mappings
+        self.pg7.unconfig_ip4()
+        identity_mappings = self.vapi.nat44_identity_mapping_dump()
+        self.assertEqual(0, len(identity_mappings))
+
     def test_ipfix_nat44_sess(self):
         """ IPFIX logging NAT44 session created/delted """
         self.ipfix_domain_id = 10
index 3dd3482..f8bca82 100644 (file)
@@ -1247,6 +1247,35 @@ class VppPapiProvider(object):
              'vrf_id': vrf_id,
              'protocol': protocol})
 
+    def nat44_add_del_identity_mapping(
+            self,
+            ip='0',
+            sw_if_index=0xFFFFFFFF,
+            port=0,
+            addr_only=1,
+            vrf_id=0,
+            protocol=0,
+            is_add=1):
+        """Add/delete NAT44 identity mapping
+
+        :param ip: IP address (Default value = 0)
+        :param sw_if_index: Interface instead of IP address
+        :param port: Port number (Default value = 0)
+        :param addr_only: 1 if address only mapping, 0 if address and port
+        :param vrf_id: VRF ID
+        :param protocol: IP protocol (Default value = 0)
+        :param is_add: 1 if add, 0 if delete (Default value = 1)
+        """
+        return self.api(
+            self.papi.nat44_add_del_identity_mapping,
+            {'is_add': is_add,
+             'addr_only': addr_only,
+             'ip_address': ip,
+             'port': port,
+             'sw_if_index': sw_if_index,
+             'vrf_id': vrf_id,
+             'protocol': protocol})
+
     def nat44_add_del_address_range(
             self,
             first_ip_address,
@@ -1291,6 +1320,12 @@ class VppPapiProvider(object):
         """
         return self.api(self.papi.nat44_static_mapping_dump, {})
 
+    def nat44_identity_mapping_dump(self):
+        """Dump NAT44 identity mappings
+        :return: Dictionary of NAT44 identity mappings
+        """
+        return self.api(self.papi.nat44_identity_mapping_dump, {})
+
     def nat_show_config(self):
         """Show NAT plugin config
         :return: NAT plugin config parameters