aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatus Fabian <matfabia@cisco.com>2017-01-27 00:47:58 -0800
committerOle Trøan <otroan@employees.org>2017-01-31 18:47:47 +0000
commite1ae29a3f7a7bd79bb266803982fd0259b040922 (patch)
tree88f07c8d4377f8c00d4925db98d170f7eea5160b
parentd9b74a964458ed31e7acb567a5f40259c671025a (diff)
SNAT: Add outbound addresses to FIB (VPP-613)
Add the external NAT address to the FIB as receive entries. This ensures that VPP will reply to ARP for these addresses and we don't need to enable proxy ARP on the outside interface. Change-Id: I1db153373c43fec4808845449a17085509ca588c Signed-off-by: Matus Fabian <matfabia@cisco.com>
-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: