aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/plugins/snat/snat.c111
-rw-r--r--test/test_snat.py67
2 files changed, 173 insertions, 5 deletions
diff --git a/src/plugins/snat/snat.c b/src/plugins/snat/snat.c
index fb566598730..6149411887a 100644
--- a/src/plugins/snat/snat.c
+++ b/src/plugins/snat/snat.c
@@ -22,6 +22,8 @@
#include <vlibapi/api.h>
#include <snat/snat.h>
#include <snat/snat_ipfix_logging.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/fib/ip4_fib.h>
#include <vlibapi/api.h>
#include <vlibmemory/api.h>
@@ -211,9 +213,66 @@ bad_tx_sw_if_index: \
#endif /* CLIB_DEBUG > 0 */
+/**
+ * @brief Add/del NAT address to FIB.
+ *
+ * Add the external NAT address to the FIB as receive entries. This ensures
+ * that VPP will reply to ARP for this address and we don't need to enable
+ * proxy ARP on the outside interface.
+ *
+ * @param sm SNAT main
+ * @param addr IPv4 address.
+ * @param sw_if_index Interface.
+ * @param is_add If 0 delete, otherwise add.
+ */
+static void
+snat_add_del_addr_to_fib (snat_main_t *sm,
+ ip4_address_t * addr,
+ u32 sw_if_index,
+ int is_add)
+{
+ ip4_main_t * ip4_main = sm->ip4_main;
+ ip4_address_t * first_int_addr;
+ fib_prefix_t prefix = {
+ .fp_len = 32,
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_addr = {
+ .ip4.as_u32 = addr->as_u32,
+ },
+ };
+ u32 fib_index = ip4_fib_table_get_index_for_sw_if_index(sw_if_index);
+
+ first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, 0);
+ if (first_int_addr)
+ {
+ if (first_int_addr->as_u32 == addr->as_u32)
+ return;
+ }
+
+ if (is_add)
+ fib_table_entry_update_one_path(fib_index,
+ &prefix,
+ FIB_SOURCE_INTERFACE,
+ (FIB_ENTRY_FLAG_CONNECTED |
+ FIB_ENTRY_FLAG_LOCAL |
+ FIB_ENTRY_FLAG_EXCLUSIVE),
+ FIB_PROTOCOL_IP4,
+ NULL,
+ sw_if_index,
+ ~0,
+ 1,
+ NULL,
+ FIB_ROUTE_PATH_FLAG_NONE);
+ else
+ fib_table_entry_delete(fib_index,
+ &prefix,
+ FIB_SOURCE_INTERFACE);
+}
+
void snat_add_address (snat_main_t *sm, ip4_address_t *addr)
{
snat_address_t * ap;
+ snat_interface_t *i;
/* Check if address already exists */
vec_foreach (ap, sm->addresses)
@@ -225,6 +284,15 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr)
vec_add2 (sm->addresses, ap, 1);
ap->addr = *addr;
clib_bitmap_alloc (ap->busy_port_bitmap, 65535);
+
+ /* Add external address to FIB */
+ pool_foreach (i, sm->interfaces,
+ ({
+ if (i->is_inside)
+ continue;
+
+ snat_add_del_addr_to_fib(sm, addr, i->sw_if_index, 1);
+ }));
}
static int is_snat_address_used_in_static_mapping (snat_main_t *sm,
@@ -297,6 +365,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
snat_address_t *a = 0;
u32 fib_index = ~0;
uword * p;
+ snat_interface_t *interface;
int i;
/* If outside FIB index is not resolved yet */
@@ -561,6 +630,18 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
pool_put (sm->static_mappings, m);
}
+ if (!addr_only)
+ return 0;
+
+ /* Add/delete external address to FIB */
+ pool_foreach (interface, sm->interfaces,
+ ({
+ if (interface->is_inside)
+ continue;
+
+ snat_add_del_addr_to_fib(sm, &e_addr, interface->sw_if_index, is_add);
+ }));
+
return 0;
}
@@ -574,6 +655,7 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm)
snat_user_t *u;
snat_main_per_thread_data_t *tsm;
snat_static_mapping_t *m;
+ snat_interface_t *interface;
int i;
/* Find SNAT address */
@@ -649,6 +731,15 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm)
vec_del1 (sm->addresses, i);
+ /* Delete external address from FIB */
+ pool_foreach (interface, sm->interfaces,
+ ({
+ if (interface->is_inside)
+ continue;
+
+ snat_add_del_addr_to_fib(sm, &addr, interface->sw_if_index, 0);
+ }));
+
return 0;
}
@@ -657,6 +748,8 @@ static int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
snat_main_t *sm = &snat_main;
snat_interface_t *i;
const char * feature_name;
+ snat_address_t * ap;
+ snat_static_mapping_t * m;
if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
feature_name = is_inside ? "snat-in2out-fast" : "snat-out2in-fast";
@@ -686,7 +779,7 @@ static int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
else
return VNET_API_ERROR_VALUE_EXIST;
- return 0;
+ goto fib;
}
}));
@@ -697,6 +790,22 @@ static int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
i->sw_if_index = sw_if_index;
i->is_inside = is_inside;
+ /* Add/delete external addresses to FIB */
+fib:
+ if (is_inside)
+ return 0;
+
+ vec_foreach (ap, sm->addresses)
+ snat_add_del_addr_to_fib(sm, &ap->addr, sw_if_index, !is_del);
+
+ pool_foreach (m, sm->static_mappings,
+ ({
+ if (!(m->addr_only))
+ continue;
+
+ snat_add_del_addr_to_fib(sm, &m->external_addr, sw_if_index, !is_del);
+ }));
+
return 0;
}
diff --git a/test/test_snat.py b/test/test_snat.py
index b6cc1c9a17f..a67deede7a5 100644
--- a/test/test_snat.py
+++ b/test/test_snat.py
@@ -6,7 +6,7 @@ import struct
from framework import VppTestCase, VppTestRunner
from scapy.layers.inet import IP, TCP, UDP, ICMP
-from scapy.layers.l2 import Ether
+from scapy.layers.l2 import Ether, ARP
from scapy.data import IP_PROTOS
from util import ppp
from ipfix import IPFIX, Set, Template, Data, IPFIXDecoder
@@ -524,9 +524,7 @@ class TestSNAT(VppTestCase):
self.pg3.assert_nothing_captured()
def test_multiple_inside_interfaces(self):
- """
- SNAT multiple inside interfaces with non-overlapping address space
- """
+ """ SNAT multiple inside interfaces (non-overlapping address space) """
self.snat_add_address(self.snat_addr)
self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index)
@@ -862,6 +860,67 @@ class TestSNAT(VppTestCase):
data = ipfix.decode_data_set(p.getlayer(Set))
self.verify_ipfix_addr_exhausted(data)
+ def test_pool_addr_fib(self):
+ """ S-NAT add pool addresses to FIB """
+ static_addr = '10.0.0.10'
+ self.snat_add_address(self.snat_addr)
+ self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index)
+ self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index,
+ is_inside=0)
+ self.snat_add_static_mapping(self.pg0.remote_ip4, static_addr)
+
+ # SNAT address
+ p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') /
+ ARP(op=ARP.who_has, pdst=self.snat_addr,
+ psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac))
+ self.pg1.add_stream(p)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture = self.pg1.get_capture(1)
+ self.assertTrue(capture[0].haslayer(ARP))
+ self.assertTrue(capture[0][ARP].op, ARP.is_at)
+
+ # 1:1 NAT address
+ p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') /
+ ARP(op=ARP.who_has, pdst=static_addr,
+ psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac))
+ self.pg1.add_stream(p)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture = self.pg1.get_capture(1)
+ self.assertTrue(capture[0].haslayer(ARP))
+ self.assertTrue(capture[0][ARP].op, ARP.is_at)
+
+ # send ARP to non-SNAT interface
+ p = (Ether(src=self.pg2.remote_mac, dst='ff:ff:ff:ff:ff:ff') /
+ ARP(op=ARP.who_has, pdst=self.snat_addr,
+ psrc=self.pg2.remote_ip4, hwsrc=self.pg2.remote_mac))
+ self.pg2.add_stream(p)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture = self.pg1.get_capture(0)
+
+ # remove addresses and verify
+ self.snat_add_address(self.snat_addr, is_add=0)
+ self.snat_add_static_mapping(self.pg0.remote_ip4, static_addr,
+ is_add=0)
+
+ p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') /
+ ARP(op=ARP.who_has, pdst=self.snat_addr,
+ psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac))
+ self.pg1.add_stream(p)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture = self.pg1.get_capture(0)
+
+ p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') /
+ ARP(op=ARP.who_has, pdst=static_addr,
+ psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac))
+ self.pg1.add_stream(p)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture = self.pg1.get_capture(0)
+
def tearDown(self):
super(TestSNAT, self).tearDown()
if not self.vpp_dead: