diff options
author | Matus Fabian <matfabia@cisco.com> | 2017-01-27 00:47:58 -0800 |
---|---|---|
committer | Ole Trøan <otroan@employees.org> | 2017-01-31 18:47:47 +0000 |
commit | e1ae29a3f7a7bd79bb266803982fd0259b040922 (patch) | |
tree | 88f07c8d4377f8c00d4925db98d170f7eea5160b | |
parent | d9b74a964458ed31e7acb567a5f40259c671025a (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.c | 111 | ||||
-rw-r--r-- | test/test_snat.py | 67 |
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: |