cnat: Ip ICMP error support
Type: feature Add CNAT translation for ICMP 4 & 6 errors inner packet will be translated according to existing sessions. Change-Id: If118751988f44ef96b800878596296d1ab8ab6f8 Signed-off-by: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
This commit is contained in:
Nathan Skrzypczak
committed by
Dave Barach
parent
277d402bee
commit
ece39214bc
@ -61,12 +61,10 @@ cnat_client_free_by_ip (ip46_address_t * ip, u8 af)
|
||||
cnat_client_t *cc;
|
||||
cc = (AF_IP4 == af ?
|
||||
cnat_client_ip4_find (&ip->ip4) : cnat_client_ip6_find (&ip->ip6));
|
||||
/* This can happen if the translation gets deleted
|
||||
before the session */
|
||||
if (NULL == cc)
|
||||
return;
|
||||
ASSERT (NULL != cc);
|
||||
|
||||
if ((0 == cnat_client_uncnt_session (cc))
|
||||
&& (cc->flags & CNAT_FLAG_EXPIRES))
|
||||
&& (cc->flags & CNAT_FLAG_EXPIRES) && (0 == cc->tr_refcnt))
|
||||
cnat_client_destroy (cc);
|
||||
}
|
||||
|
||||
@ -101,7 +99,6 @@ cnat_client_throttle_pool_process ()
|
||||
/* *INDENT-ON* */
|
||||
vec_foreach (ai, del_vec)
|
||||
{
|
||||
/* Free session */
|
||||
addr = pool_elt_at_index (cnat_client_db.throttle_pool[i], *ai);
|
||||
pool_put (cnat_client_db.throttle_pool[i], addr);
|
||||
}
|
||||
@ -127,7 +124,7 @@ cnat_client_translation_deleted (index_t cci)
|
||||
ASSERT (!(cc->flags & CNAT_FLAG_EXPIRES));
|
||||
cc->tr_refcnt--;
|
||||
|
||||
if (0 == cc->tr_refcnt)
|
||||
if (0 == cc->tr_refcnt && 0 == cc->session_refcnt)
|
||||
cnat_client_destroy (cc);
|
||||
}
|
||||
|
||||
@ -171,6 +168,8 @@ cnat_client_add (const ip_address_t * ip, u8 flags)
|
||||
cci = cc - cnat_client_pool;
|
||||
cc->parent_cci = cci;
|
||||
cc->flags = flags;
|
||||
cc->tr_refcnt = 0;
|
||||
cc->session_refcnt = 0;
|
||||
|
||||
ip_address_copy (&cc->cc_ip, ip);
|
||||
cnat_client_db_add (cc);
|
||||
@ -238,9 +237,16 @@ cnat_client_dpo_interpose (const dpo_id_t * original,
|
||||
int
|
||||
cnat_client_purge (void)
|
||||
{
|
||||
vlib_thread_main_t *tm = vlib_get_thread_main ();
|
||||
int nthreads;
|
||||
nthreads = tm->n_threads + 1;
|
||||
ASSERT (0 == hash_elts (cnat_client_db.crd_cip6));
|
||||
ASSERT (0 == hash_elts (cnat_client_db.crd_cip4));
|
||||
ASSERT (0 == pool_elts (cnat_client_pool));
|
||||
for (int i = 0; i < nthreads; i++)
|
||||
{
|
||||
ASSERT (0 == pool_elts (cnat_client_db.throttle_pool[i]));
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -81,7 +81,8 @@ cnat_snat_inline (vlib_main_t * vm,
|
||||
vnet_feature_next (&arc_next0, b);
|
||||
next0 = arc_next0;
|
||||
|
||||
if (iproto != IP_PROTOCOL_UDP && iproto != IP_PROTOCOL_TCP)
|
||||
if (iproto != IP_PROTOCOL_UDP && iproto != IP_PROTOCOL_TCP
|
||||
&& iproto != IP_PROTOCOL_ICMP && iproto != IP_PROTOCOL_ICMP6)
|
||||
{
|
||||
/* Dont translate */
|
||||
goto trace;
|
||||
|
@ -95,7 +95,8 @@ cnat_vip_inline (vlib_main_t * vm,
|
||||
|
||||
cc = cnat_client_get (vnet_buffer (b)->ip.adj_index[VLIB_TX]);
|
||||
|
||||
if (iproto != IP_PROTOCOL_UDP && iproto != IP_PROTOCOL_TCP)
|
||||
if (iproto != IP_PROTOCOL_UDP && iproto != IP_PROTOCOL_TCP
|
||||
&& iproto != IP_PROTOCOL_ICMP && iproto != IP_PROTOCOL_ICMP6)
|
||||
{
|
||||
/* Dont translate & follow the fib programming */
|
||||
next0 = cc->cc_parent.dpoi_next_node;
|
||||
@ -214,6 +215,7 @@ cnat_vip_inline (vlib_main_t * vm,
|
||||
}
|
||||
session->value.cs_lbi = dpo0->dpoi_index;
|
||||
|
||||
/* refcnt session in current client */
|
||||
cnat_client_cnt_session (cc);
|
||||
cnat_session_create (session, ctx, rsession_flags);
|
||||
created_session = 1;
|
||||
@ -222,7 +224,6 @@ cnat_vip_inline (vlib_main_t * vm,
|
||||
vnet_buffer (b)->ip.adj_index[VLIB_TX] = session->value.cs_lbi;
|
||||
}
|
||||
|
||||
|
||||
if (AF_IP4 == ctx->af)
|
||||
cnat_translation_ip4 (session, ip4, udp0);
|
||||
else
|
||||
|
@ -146,6 +146,11 @@ extern u64 cnat_session_scan (vlib_main_t * vm, f64 start_time, int i);
|
||||
*/
|
||||
extern int cnat_session_purge (void);
|
||||
|
||||
/**
|
||||
* Free a session & update refcounts
|
||||
*/
|
||||
extern void cnat_session_free (cnat_session_t * session);
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
|
@ -7,8 +7,11 @@ from vpp_ip import DpoProto
|
||||
|
||||
from scapy.packet import Raw
|
||||
from scapy.layers.l2 import Ether
|
||||
from scapy.layers.inet import IP, UDP, TCP
|
||||
from scapy.layers.inet6 import IPv6
|
||||
from scapy.layers.inet import IP, UDP, TCP, ICMP
|
||||
from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
|
||||
from scapy.layers.inet6 import IPv6, IPerror6, ICMPv6DestUnreach
|
||||
|
||||
import struct
|
||||
|
||||
from ipaddress import ip_address, ip_network, \
|
||||
IPv4Address, IPv6Address, IPv4Network, IPv6Network
|
||||
@ -39,6 +42,10 @@ class Ep(object):
|
||||
return {'addr': self.ip,
|
||||
'port': self.port}
|
||||
|
||||
@property
|
||||
def isV6(self):
|
||||
return ":" in self.ip
|
||||
|
||||
def __str__(self):
|
||||
return ("%s:%d" % (self.ip, self.port))
|
||||
|
||||
@ -180,10 +187,10 @@ class TestCNatTranslation(VppTestCase):
|
||||
i.admin_down()
|
||||
super(TestCNatTranslation, self).tearDown()
|
||||
|
||||
def cnat_create_translation(self, vip, nbr, isV6=False):
|
||||
ip_v = "ip6" if isV6 else "ip4"
|
||||
def cnat_create_translation(self, vip, nbr):
|
||||
ip_v = "ip6" if vip.isV6 else "ip4"
|
||||
dep = Ep(getattr(self.pg1.remote_hosts[nbr], ip_v), 4000 + nbr)
|
||||
sep = Ep("::", 0) if isV6 else Ep("0.0.0.0", 0)
|
||||
sep = Ep("::", 0) if vip.isV6 else Ep("0.0.0.0", 0)
|
||||
t1 = VppCNatTranslation(
|
||||
self, vip.l4p, vip,
|
||||
[EpTuple(sep, dep), EpTuple(sep, dep)])
|
||||
@ -341,7 +348,7 @@ class TestCNatTranslation(VppTestCase):
|
||||
|
||||
trs = []
|
||||
for nbr, vip in enumerate(vips):
|
||||
trs.append(self.cnat_create_translation(vip, nbr, isV6=isV6))
|
||||
trs.append(self.cnat_create_translation(vip, nbr))
|
||||
|
||||
self.logger.info(self.vapi.cli("sh cnat client"))
|
||||
self.logger.info(self.vapi.cli("sh cnat translation"))
|
||||
@ -372,8 +379,10 @@ class TestCNatTranslation(VppTestCase):
|
||||
n_tries += 1
|
||||
sessions = self.vapi.cnat_session_dump()
|
||||
self.sleep(2)
|
||||
print(self.vapi.cli("show cnat session verbose"))
|
||||
|
||||
self.assertTrue(n_tries < 100)
|
||||
self.vapi.cli("test cnat scanner off")
|
||||
|
||||
#
|
||||
# load some flows again and purge
|
||||
@ -398,6 +407,109 @@ class TestCNatTranslation(VppTestCase):
|
||||
self.vapi.cnat_session_purge()
|
||||
self.assertFalse(self.vapi.cnat_session_dump())
|
||||
|
||||
def test_icmp(self):
|
||||
vips = [
|
||||
Ep("30.0.0.1", 5555),
|
||||
Ep("30.0.0.2", 5554),
|
||||
Ep("30.0.0.2", 5553, UDP),
|
||||
Ep("30::1", 6666),
|
||||
Ep("30::2", 5553, UDP),
|
||||
]
|
||||
sport = 1234
|
||||
|
||||
self.pg0.generate_remote_hosts(len(vips))
|
||||
self.pg0.configure_ipv6_neighbors()
|
||||
self.pg0.configure_ipv4_neighbors()
|
||||
|
||||
self.pg1.generate_remote_hosts(len(vips))
|
||||
self.pg1.configure_ipv6_neighbors()
|
||||
self.pg1.configure_ipv4_neighbors()
|
||||
|
||||
self.vapi.cli("test cnat scanner off")
|
||||
trs = []
|
||||
for nbr, vip in enumerate(vips):
|
||||
trs.append(self.cnat_create_translation(vip, nbr))
|
||||
|
||||
self.logger.info(self.vapi.cli("sh cnat client"))
|
||||
self.logger.info(self.vapi.cli("sh cnat translation"))
|
||||
|
||||
for nbr, vip in enumerate(vips):
|
||||
if vip.isV6:
|
||||
client_addr = self.pg0.remote_hosts[0].ip6
|
||||
remote_addr = self.pg1.remote_hosts[nbr].ip6
|
||||
remote2_addr = self.pg2.remote_hosts[0].ip6
|
||||
else:
|
||||
client_addr = self.pg0.remote_hosts[0].ip4
|
||||
remote_addr = self.pg1.remote_hosts[nbr].ip4
|
||||
remote2_addr = self.pg2.remote_hosts[0].ip4
|
||||
IP46 = IPv6 if vip.isV6 else IP
|
||||
# from client to vip
|
||||
p1 = (Ether(dst=self.pg0.local_mac,
|
||||
src=self.pg0.remote_hosts[0].mac) /
|
||||
IP46(src=client_addr, dst=vip.ip) /
|
||||
vip.l4p(sport=sport, dport=vip.port) /
|
||||
Raw())
|
||||
|
||||
rxs = self.send_and_expect(self.pg0,
|
||||
p1 * N_PKTS,
|
||||
self.pg1)
|
||||
|
||||
for rx in rxs:
|
||||
self.assert_packet_checksums_valid(rx)
|
||||
self.assertEqual(rx[IP46].dst, remote_addr)
|
||||
self.assertEqual(rx[vip.l4p].dport, 4000 + nbr)
|
||||
self.assertEqual(rx[IP46].src, client_addr)
|
||||
self.assertEqual(rx[vip.l4p].sport, sport)
|
||||
|
||||
InnerIP = rxs[0][IP46]
|
||||
|
||||
ICMP46 = ICMPv6DestUnreach if vip.isV6 else ICMP
|
||||
ICMPelem = ICMPv6DestUnreach(code=1) if vip.isV6 else ICMP(type=11)
|
||||
# from vip to client, ICMP error
|
||||
p1 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) /
|
||||
IP46(src=remote_addr, dst=client_addr) /
|
||||
ICMPelem / InnerIP)
|
||||
|
||||
rxs = self.send_and_expect(self.pg1,
|
||||
p1 * N_PKTS,
|
||||
self.pg0)
|
||||
|
||||
TCPUDPError = TCPerror if vip.l4p == TCP else UDPerror
|
||||
IP46error = IPerror6 if vip.isV6 else IPerror
|
||||
for rx in rxs:
|
||||
self.assert_packet_checksums_valid(rx)
|
||||
self.assertEqual(rx[IP46].src, vip.ip)
|
||||
self.assertEqual(rx[ICMP46][IP46error].src, client_addr)
|
||||
self.assertEqual(rx[ICMP46][IP46error].dst, vip.ip)
|
||||
self.assertEqual(rx[ICMP46][IP46error]
|
||||
[TCPUDPError].sport, sport)
|
||||
self.assertEqual(rx[ICMP46][IP46error]
|
||||
[TCPUDPError].dport, vip.port)
|
||||
|
||||
# from other remote to client, ICMP error
|
||||
# outside shouldn't be NAT-ed
|
||||
p1 = (Ether(dst=self.pg2.local_mac, src=self.pg2.remote_mac) /
|
||||
IP46(src=remote2_addr, dst=client_addr) /
|
||||
ICMPelem / InnerIP)
|
||||
|
||||
rxs = self.send_and_expect(self.pg1,
|
||||
p1 * N_PKTS,
|
||||
self.pg0)
|
||||
|
||||
TCPUDPError = TCPerror if vip.l4p == TCP else UDPerror
|
||||
IP46error = IPerror6 if vip.isV6 else IPerror
|
||||
for rx in rxs:
|
||||
self.assert_packet_checksums_valid(rx)
|
||||
self.assertEqual(rx[IP46].src, remote2_addr)
|
||||
self.assertEqual(rx[ICMP46][IP46error].src, client_addr)
|
||||
self.assertEqual(rx[ICMP46][IP46error].dst, vip.ip)
|
||||
self.assertEqual(rx[ICMP46][IP46error]
|
||||
[TCPUDPError].sport, sport)
|
||||
self.assertEqual(rx[ICMP46][IP46error]
|
||||
[TCPUDPError].dport, vip.port)
|
||||
|
||||
self.vapi.cnat_session_purge()
|
||||
|
||||
def test_cnat6(self):
|
||||
# """ CNat Translation ipv6 """
|
||||
vips = [
|
||||
@ -478,7 +590,7 @@ class TestCNatSourceNAT(VppTestCase):
|
||||
|
||||
def cnat_test_sourcenat(self, srcNatAddr, l4p=TCP, isV6=False):
|
||||
ip_v = "ip6" if isV6 else "ip4"
|
||||
ip_class = IPv6 if isV6 else IP
|
||||
IP46 = IPv6 if isV6 else IP
|
||||
sports = [1234, 1235, 1236]
|
||||
dports = [6661, 6662, 6663]
|
||||
|
||||
@ -493,14 +605,17 @@ class TestCNatSourceNAT(VppTestCase):
|
||||
t1 = self.cnat_set_snat_address(srcNatAddr, self.pg0, isV6)
|
||||
|
||||
for nbr, remote_host in enumerate(self.pg1.remote_hosts):
|
||||
if isV6:
|
||||
client_addr = self.pg0.remote_hosts[0].ip6
|
||||
remote_addr = self.pg1.remote_hosts[nbr].ip6
|
||||
else:
|
||||
client_addr = self.pg0.remote_hosts[0].ip4
|
||||
remote_addr = self.pg1.remote_hosts[nbr].ip4
|
||||
# from pods to outside network
|
||||
p1 = (
|
||||
Ether(
|
||||
dst=self.pg0.local_mac,
|
||||
src=self.pg0.remote_hosts[0].mac) /
|
||||
ip_class(
|
||||
src=getattr(self.pg0.remote_hosts[0], ip_v),
|
||||
dst=getattr(remote_host, ip_v)) /
|
||||
Ether(dst=self.pg0.local_mac,
|
||||
src=self.pg0.remote_hosts[0].mac) /
|
||||
IP46(src=client_addr, dst=remote_addr) /
|
||||
l4p(sport=sports[nbr], dport=dports[nbr]) /
|
||||
Raw())
|
||||
|
||||
@ -510,21 +625,16 @@ class TestCNatSourceNAT(VppTestCase):
|
||||
self.pg1)
|
||||
for rx in rxs:
|
||||
self.assert_packet_checksums_valid(rx)
|
||||
self.assertEqual(
|
||||
rx[ip_class].dst,
|
||||
getattr(remote_host, ip_v))
|
||||
self.assertEqual(rx[IP46].dst, remote_addr)
|
||||
self.assertEqual(rx[l4p].dport, dports[nbr])
|
||||
self.assertEqual(
|
||||
rx[ip_class].src,
|
||||
srcNatAddr)
|
||||
self.assertEqual(rx[IP46].src, srcNatAddr)
|
||||
sport = rx[l4p].sport
|
||||
|
||||
# from outside to pods
|
||||
p2 = (
|
||||
Ether(
|
||||
dst=self.pg1.local_mac,
|
||||
src=self.pg1.remote_hosts[nbr].mac) /
|
||||
ip_class(src=getattr(remote_host, ip_v), dst=srcNatAddr) /
|
||||
Ether(dst=self.pg1.local_mac,
|
||||
src=self.pg1.remote_hosts[nbr].mac) /
|
||||
IP46(src=remote_addr, dst=srcNatAddr) /
|
||||
l4p(sport=dports[nbr], dport=sport) /
|
||||
Raw())
|
||||
|
||||
@ -535,18 +645,14 @@ class TestCNatSourceNAT(VppTestCase):
|
||||
|
||||
for rx in rxs:
|
||||
self.assert_packet_checksums_valid(rx)
|
||||
self.assertEqual(
|
||||
rx[ip_class].dst,
|
||||
getattr(self.pg0.remote_hosts[0], ip_v))
|
||||
self.assertEqual(rx[IP46].dst, client_addr)
|
||||
self.assertEqual(rx[l4p].dport, sports[nbr])
|
||||
self.assertEqual(rx[l4p].sport, dports[nbr])
|
||||
self.assertEqual(
|
||||
rx[ip_class].src,
|
||||
getattr(remote_host, ip_v))
|
||||
self.assertEqual(rx[IP46].src, remote_addr)
|
||||
|
||||
# add remote host to exclude list
|
||||
subnet_mask = 100 if isV6 else 16
|
||||
subnet = getattr(remote_host, ip_v) + "/" + str(subnet_mask)
|
||||
subnet = "%s/%d" % (remote_addr, subnet_mask)
|
||||
exclude_subnet = ip_network(subnet, strict=False)
|
||||
|
||||
t1.cnat_exclude_subnet(exclude_subnet)
|
||||
@ -558,13 +664,9 @@ class TestCNatSourceNAT(VppTestCase):
|
||||
self.pg1)
|
||||
for rx in rxs:
|
||||
self.assert_packet_checksums_valid(rx)
|
||||
self.assertEqual(
|
||||
rx[ip_class].dst,
|
||||
getattr(remote_host, ip_v))
|
||||
self.assertEqual(rx[IP46].dst, remote_addr)
|
||||
self.assertEqual(rx[l4p].dport, dports[nbr])
|
||||
self.assertEqual(
|
||||
rx[ip_class].src,
|
||||
getattr(self.pg0.remote_hosts[0], ip_v))
|
||||
self.assertEqual(rx[IP46].src, client_addr)
|
||||
|
||||
# remove remote host from exclude list
|
||||
t1.cnat_exclude_subnet(exclude_subnet, isAdd=False)
|
||||
@ -577,13 +679,9 @@ class TestCNatSourceNAT(VppTestCase):
|
||||
|
||||
for rx in rxs:
|
||||
self.assert_packet_checksums_valid(rx)
|
||||
self.assertEqual(
|
||||
rx[ip_class].dst,
|
||||
getattr(remote_host, ip_v))
|
||||
self.assertEqual(rx[IP46].dst, remote_addr)
|
||||
self.assertEqual(rx[l4p].dport, dports[nbr])
|
||||
self.assertEqual(
|
||||
rx[ip_class].src,
|
||||
srcNatAddr)
|
||||
self.assertEqual(rx[IP46].src, srcNatAddr)
|
||||
|
||||
def test_cnat6_sourcenat(self):
|
||||
# """ CNat Source Nat ipv6 """
|
||||
|
Reference in New Issue
Block a user