summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatus Fabian <matfabia@cisco.com>2017-06-08 05:24:28 -0700
committerOle Trøan <otroan@employees.org>2017-06-09 07:30:14 +0000
commit732036d677b84aa8eaea45f8059783e827622b77 (patch)
treebac6a6fccc1ae8500d5d3f54dbede362d0402459
parentef2a5bf0a31c9c0a94f9f497cb6353f46073e6ec (diff)
NAT64: ICMP error support
Added ICMP error messages translation. Added check for multi thread (not supported yet, so init failed). Added API definition for custom NAT64 refix. Change-Id: Ice2f04631af63e594aecc09087a1cf59f3b676fb Signed-off-by: Matus Fabian <matfabia@cisco.com>
-rw-r--r--src/plugins/snat/nat64.c13
-rw-r--r--src/plugins/snat/nat64_in2out.c75
-rw-r--r--src/plugins/snat/nat64_out2in.c84
-rw-r--r--src/plugins/snat/snat.api39
-rw-r--r--src/plugins/snat/snat.c6
-rw-r--r--src/vnet/ip/ip4_to_ip6.h13
-rw-r--r--test/test_snat.py90
7 files changed, 297 insertions, 23 deletions
diff --git a/src/plugins/snat/nat64.c b/src/plugins/snat/nat64.c
index 9b6b3c8a5ac..ef745473793 100644
--- a/src/plugins/snat/nat64.c
+++ b/src/plugins/snat/nat64.c
@@ -45,9 +45,19 @@ nat64_init (vlib_main_t * vm)
{
nat64_main_t *nm = &nat64_main;
clib_error_t *error = 0;
+ vlib_thread_main_t *tm = vlib_get_thread_main ();
+
+ if (tm->n_vlib_mains > 1)
+ {
+ error = clib_error_return (0, "multi thread not supported");
+ goto error;
+ }
if (nat64_db_init (&nm->db))
- error = clib_error_return (0, "NAT64 DB init failed");
+ {
+ error = clib_error_return (0, "NAT64 DB init failed");
+ goto error;
+ }
/* set session timeouts to default values */
nm->udp_timeout = SNAT_UDP_TIMEOUT;
@@ -56,6 +66,7 @@ nat64_init (vlib_main_t * vm)
nm->tcp_est_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT;
nm->tcp_incoming_syn_timeout = SNAT_TCP_INCOMING_SYN;
+error:
return error;
}
diff --git a/src/plugins/snat/nat64_in2out.c b/src/plugins/snat/nat64_in2out.c
index d9d94a975d1..eba6932681f 100644
--- a/src/plugins/snat/nat64_in2out.c
+++ b/src/plugins/snat/nat64_in2out.c
@@ -221,9 +221,11 @@ nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
}
else
{
- //TODO: ICMP error
- clib_warning ("not ICMP echo request/reply, %u", icmp->type);
- return -1;
+ if (!vec_len (nm->addr_pool))
+ return -1;
+
+ ip4->src_address.as_u32 = nm->addr_pool[0].addr.as_u32;
+ ip4->dst_address.as_u32 = daddr.ip4.as_u32;
}
return 0;
@@ -231,10 +233,71 @@ nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
static int
nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
- void *ctx)
+ void *arg)
{
- //TODO:
- return -1;
+ nat64_main_t *nm = &nat64_main;
+ nat64_in2out_set_ctx_t *ctx = arg;
+ nat64_db_st_entry_t *ste;
+ nat64_db_bib_entry_t *bibe;
+ ip46_address_t saddr, daddr;
+ u32 sw_if_index, fib_index;
+ snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol);
+
+ sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
+ fib_index =
+ fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
+
+ saddr.as_u64[0] = ip6->src_address.as_u64[0];
+ saddr.as_u64[1] = ip6->src_address.as_u64[1];
+ daddr.as_u64[0] = ip6->dst_address.as_u64[0];
+ daddr.as_u64[1] = ip6->dst_address.as_u64[1];
+
+ if (proto == SNAT_PROTOCOL_ICMP)
+ {
+ icmp46_header_t *icmp = ip6_next_header (ip6);
+ u16 in_id = ((u16 *) (icmp))[2];
+
+ if (!
+ (icmp->type == ICMP4_echo_request
+ || icmp->type == ICMP4_echo_reply))
+ return -1;
+
+ ste =
+ nat64_db_st_entry_find (&nm->db, &daddr, &saddr, in_id, 0, proto,
+ fib_index, 1);
+ if (!ste)
+ return -1;
+
+ bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
+ if (!bibe)
+ return -1;
+
+ ip4->dst_address.as_u32 = bibe->out_addr.as_u32;
+ ((u16 *) (icmp))[2] = bibe->out_port;
+ ip4->src_address.as_u32 = saddr.ip4.as_u32;
+ }
+ else
+ {
+ udp_header_t *udp = ip6_next_header (ip6);
+ u16 sport = udp->src_port;
+ u16 dport = udp->dst_port;
+
+ ste =
+ nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto,
+ fib_index, 1);
+ if (!ste)
+ return -1;
+
+ bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
+ if (!bibe)
+ return -1;
+
+ ip4->dst_address.as_u32 = bibe->out_addr.as_u32;
+ udp->dst_port = bibe->out_port;
+ ip4->src_address.as_u32 = saddr.ip4.as_u32;
+ }
+
+ return 0;
}
static uword
diff --git a/src/plugins/snat/nat64_out2in.c b/src/plugins/snat/nat64_out2in.c
index 3eed9974af2..c11bbb5a7b1 100644
--- a/src/plugins/snat/nat64_out2in.c
+++ b/src/plugins/snat/nat64_out2in.c
@@ -208,9 +208,12 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
}
else
{
- //TODO: ICMP error
- clib_warning ("not ICMP echo request/reply, %u", icmp->type);
- return -1;
+ ip6_header_t *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
+
+ ip6->src_address.as_u64[0] = ip6_saddr.as_u64[0];
+ ip6->src_address.as_u64[1] = ip6_saddr.as_u64[1];
+ ip6->dst_address.as_u64[0] = inner_ip6->src_address.as_u64[0];
+ ip6->dst_address.as_u64[1] = inner_ip6->src_address.as_u64[1];
}
return 0;
@@ -218,10 +221,79 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
static int
nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
- void *ctx)
+ void *arg)
{
- //TODO:
- return -1;
+ nat64_main_t *nm = &nat64_main;
+ nat64_out2in_set_ctx_t *ctx = arg;
+ nat64_db_bib_entry_t *bibe;
+ nat64_db_st_entry_t *ste;
+ ip46_address_t saddr, daddr;
+ ip6_address_t ip6_daddr;
+ u32 sw_if_index, fib_index;
+ snat_protocol_t proto = ip_proto_to_snat_proto (ip4->protocol);
+
+ sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
+ fib_index =
+ fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
+
+ memset (&saddr, 0, sizeof (saddr));
+ saddr.ip4.as_u32 = ip4->src_address.as_u32;
+ memset (&daddr, 0, sizeof (daddr));
+ daddr.ip4.as_u32 = ip4->dst_address.as_u32;
+
+ memcpy (&ip6_daddr, well_known_prefix, sizeof (ip6_daddr));
+ ip6_daddr.as_u32[3] = ip4->dst_address.as_u32;
+
+ if (proto == SNAT_PROTOCOL_ICMP)
+ {
+ icmp46_header_t *icmp = ip4_next_header (ip4);
+ u16 out_id = ((u16 *) (icmp))[2];
+
+ if (!
+ (icmp->type == ICMP6_echo_request
+ || icmp->type == ICMP6_echo_reply))
+ return -1;
+
+ ste =
+ nat64_db_st_entry_find (&nm->db, &saddr, &daddr, out_id, 0, proto,
+ fib_index, 0);
+ if (!ste)
+ return -1;
+
+ bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
+ if (!bibe)
+ return -1;
+
+ ip6->dst_address.as_u64[0] = ip6_daddr.as_u64[0];
+ ip6->dst_address.as_u64[1] = ip6_daddr.as_u64[1];
+ ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0];
+ ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1];
+ ((u16 *) (icmp))[2] = bibe->in_port;
+ }
+ else
+ {
+ udp_header_t *udp = ip4_next_header (ip4);
+ u16 dport = udp->dst_port;
+ u16 sport = udp->src_port;
+
+ ste =
+ nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto,
+ fib_index, 0);
+ if (!ste)
+ return -1;
+
+ bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
+ if (!bibe)
+ return -1;
+
+ ip6->dst_address.as_u64[0] = ip6_daddr.as_u64[0];
+ ip6->dst_address.as_u64[1] = ip6_daddr.as_u64[1];
+ ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0];
+ ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1];
+ udp->src_port = bibe->in_port;
+ }
+
+ return 0;
}
static uword
diff --git a/src/plugins/snat/snat.api b/src/plugins/snat/snat.api
index e6289af2586..62c9605847a 100644
--- a/src/plugins/snat/snat.api
+++ b/src/plugins/snat/snat.api
@@ -812,3 +812,42 @@ define nat64_st_details {
u32 vrf_id;
u8 proto;
};
+
+/** \brief Add/del NAT64 prefix
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param prefix - NAT64 prefix
+ @param prefix - NAT64 prefix length
+ @param vrf_id - VRF id of tenant
+ @param is_add - 1 if add, 0 if delete
+*/
+autoreply define nat64_add_del_prefix {
+ u32 client_index;
+ u32 context;
+ u8 prefix[16];
+ u8 prefix_len;
+ u32 vrf_id;
+ u8 is_add;
+};
+
+/** \brief Dump NAT64 prefix
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+*/
+define nat64_prefix_dump {
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief Dump NAT64 prefix details response
+ @param context - sender context, to match reply w/ request
+ @param prefix - NAT64 prefix
+ @param prefix - NAT64 prefix length
+ @param vrf_id - VRF id of tenant
+*/
+define nat64_prefix_details {
+ u32 context;
+ u8 prefix[16];
+ u8 prefix_len;
+ u32 vrf_id;
+};
diff --git a/src/plugins/snat/snat.c b/src/plugins/snat/snat.c
index e95abf27bd8..351f8dc7e12 100644
--- a/src/plugins/snat/snat.c
+++ b/src/plugins/snat/snat.c
@@ -725,7 +725,7 @@ snat_ip4_add_del_interface_address_cb (ip4_main_t * im,
static clib_error_t * snat_init (vlib_main_t * vm)
{
snat_main_t * sm = &snat_main;
- clib_error_t * error = 0;
+ clib_error_t * error = 0, * error_nat64 = 0;
ip4_main_t * im = &ip4_main;
ip_lookup_main_t * lm = &im->lookup_main;
uword *p;
@@ -782,7 +782,9 @@ static clib_error_t * snat_init (vlib_main_t * vm)
/* Init IPFIX logging */
snat_ipfix_logging_init(vm);
- error = nat64_init(vm);
+ error_nat64 = nat64_init(vm);
+ if (error_nat64)
+ clib_warning("NAT64 init failed: %U", format_clib_error, error_nat64);
return error;
}
diff --git a/src/vnet/ip/ip4_to_ip6.h b/src/vnet/ip/ip4_to_ip6.h
index 96b8bf1e676..965d27c3393 100644
--- a/src/vnet/ip/ip4_to_ip6.h
+++ b/src/vnet/ip/ip4_to_ip6.h
@@ -310,14 +310,11 @@ icmp_to_icmp6 (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx,
else if (PREDICT_TRUE (inner_ip4->protocol == IP_PROTOCOL_UDP))
{
inner_L4_checksum = &((udp_header_t *) (inner_ip4 + 1))->checksum;
- if (!*inner_L4_checksum)
- {
- return -1;
- }
- *inner_L4_checksum =
- ip_csum_fold (ip_csum_sub_even
- (*inner_L4_checksum,
- *((u64 *) (&inner_ip4->src_address))));
+ if (*inner_L4_checksum)
+ *inner_L4_checksum =
+ ip_csum_fold (ip_csum_sub_even
+ (*inner_L4_checksum,
+ *((u64 *) (&inner_ip4->src_address))));
}
else if (inner_ip4->protocol == IP_PROTOCOL_ICMP)
{
diff --git a/test/test_snat.py b/test/test_snat.py
index 64fa3057471..c6344a9ee74 100644
--- a/test/test_snat.py
+++ b/test/test_snat.py
@@ -8,6 +8,7 @@ from framework import VppTestCase, VppTestRunner, running_extended_tests
from scapy.layers.inet import IP, TCP, UDP, ICMP
from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6EchoReply
+from scapy.layers.inet6 import ICMPv6DestUnreach, IPerror6
from scapy.layers.l2 import Ether, ARP
from scapy.data import IP_PROTOS
from scapy.packet import bind_layers
@@ -2635,6 +2636,95 @@ class TestNAT64(MethodHolder):
ses_num_after_timeout = self.nat64_get_ses_num()
self.assertNotEqual(ses_num_before_timeout, ses_num_after_timeout)
+ def test_icmp_error(self):
+ """ NAT64 ICMP Error message translation """
+ self.tcp_port_in = 6303
+ self.udp_port_in = 6304
+ self.icmp_id_in = 6305
+
+ ses_num_start = self.nat64_get_ses_num()
+
+ self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n,
+ self.nat_addr_n)
+ self.vapi.nat64_add_del_interface(self.pg0.sw_if_index)
+ self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0)
+
+ # send some packets to create sessions
+ pkts = self.create_stream_in_ip6(self.pg0, self.pg1)
+ self.pg0.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture_ip4 = self.pg1.get_capture(len(pkts))
+ self.verify_capture_out(capture_ip4, packet_num=3,
+ nat_ip=self.nat_addr,
+ dst_ip=self.pg1.remote_ip4)
+
+ pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr)
+ self.pg1.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture_ip6 = self.pg0.get_capture(len(pkts))
+ ip = IPv6(src=''.join(['64:ff9b::', self.pg1.remote_ip4]))
+ self.verify_capture_in_ip6(capture_ip6, ip[IPv6].src,
+ self.pg0.remote_ip6)
+
+ # in2out
+ pkts = [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IPv6(src=self.pg0.remote_ip6, dst=ip[IPv6].src) /
+ ICMPv6DestUnreach(code=1) /
+ packet[IPv6] for packet in capture_ip6]
+ self.pg0.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture = self.pg1.get_capture(len(pkts))
+ for packet in capture:
+ try:
+ self.assertEqual(packet[IP].src, self.nat_addr)
+ self.assertEqual(packet[IP].dst, self.pg1.remote_ip4)
+ self.assertEqual(packet[ICMP].type, 3)
+ self.assertEqual(packet[ICMP].code, 13)
+ inner = packet[IPerror]
+ self.assertEqual(inner.src, self.pg1.remote_ip4)
+ self.assertEqual(inner.dst, self.nat_addr)
+ if inner.haslayer(TCPerror):
+ self.assertEqual(inner[TCPerror].dport, self.tcp_port_out)
+ elif inner.haslayer(UDPerror):
+ self.assertEqual(inner[UDPerror].dport, self.udp_port_out)
+ else:
+ self.assertEqual(inner[ICMPerror].id, self.icmp_id_out)
+ except:
+ self.logger.error(ppp("Unexpected or invalid packet:", packet))
+ raise
+
+ # out2in
+ pkts = [Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
+ IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+ ICMP(type=3, code=13) /
+ packet[IP] for packet in capture_ip4]
+ self.pg1.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture = self.pg0.get_capture(len(pkts))
+ for packet in capture:
+ try:
+ self.assertEqual(packet[IPv6].src, ip.src)
+ self.assertEqual(packet[IPv6].dst, self.pg0.remote_ip6)
+ icmp = packet[ICMPv6DestUnreach]
+ self.assertEqual(icmp.code, 1)
+ inner = icmp[IPerror6]
+ self.assertEqual(inner.src, self.pg0.remote_ip6)
+ self.assertEqual(inner.dst, ip.src)
+ if inner.haslayer(TCPerror):
+ self.assertEqual(inner[TCPerror].sport, self.tcp_port_in)
+ elif inner.haslayer(UDPerror):
+ self.assertEqual(inner[UDPerror].sport, self.udp_port_in)
+ else:
+ self.assertEqual(inner[ICMPv6EchoRequest].id,
+ self.icmp_id_in)
+ except:
+ self.logger.error(ppp("Unexpected or invalid packet:", packet))
+ raise
+
def nat64_get_ses_num(self):
"""
Return number of active NAT64 sessions.