summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFilip Varga <fivarga@cisco.com>2020-04-16 13:20:25 +0200
committerFilip Varga <fivarga@cisco.com>2020-05-04 12:15:02 +0200
commit6bb080f1e54391b161cf211a9cfa3f488f2fd331 (patch)
tree8af5f6df9baaacb2a6f10af8ff51e48f7de0846a
parent2f1563129ad8d34d365f5ef8620ff76ff7b08e70 (diff)
nat: per vrf session limits
Type: improvement Change-Id: I170256ab47978db34fb0ff6808d9cd54ab872410 Signed-off-by: Filip Varga <fivarga@cisco.com>
-rw-r--r--src/plugins/nat/in2out_ed.c8
-rw-r--r--src/plugins/nat/nat.api13
-rwxr-xr-xsrc/plugins/nat/nat.c23
-rw-r--r--src/plugins/nat/nat.h13
-rw-r--r--src/plugins/nat/nat44/inlines.h11
-rw-r--r--src/plugins/nat/nat44_cli.c55
-rw-r--r--src/plugins/nat/nat_api.c28
-rw-r--r--src/plugins/nat/out2in_ed.c16
-rw-r--r--src/plugins/nat/test/test_nat.py172
9 files changed, 310 insertions, 29 deletions
diff --git a/src/plugins/nat/in2out_ed.c b/src/plugins/nat/in2out_ed.c
index d78e552d3ba..f0bbe0b656e 100644
--- a/src/plugins/nat/in2out_ed.c
+++ b/src/plugins/nat/in2out_ed.c
@@ -318,7 +318,9 @@ slow_path_ed (snat_main_t * sm,
}
}
- if (PREDICT_FALSE (nat44_maximum_sessions_exceeded (sm, thread_index)))
+ // TODO: based on fib index do a lookup
+ if (PREDICT_FALSE
+ (nat44_ed_maximum_sessions_exceeded (sm, rx_fib_index, thread_index)))
{
if (!nat_global_lru_free_one (sm, thread_index, now))
{
@@ -812,7 +814,9 @@ nat44_ed_in2out_unknown_proto (snat_main_t * sm,
}
else
{
- if (PREDICT_FALSE (nat44_maximum_sessions_exceeded (sm, thread_index)))
+ if (PREDICT_FALSE
+ (nat44_ed_maximum_sessions_exceeded
+ (sm, rx_fib_index, thread_index)))
{
b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_SESSIONS_EXCEEDED];
nat_ipfix_logging_max_sessions (thread_index, sm->max_translations);
diff --git a/src/plugins/nat/nat.api b/src/plugins/nat/nat.api
index 10e7036608c..134f3e06d7a 100644
--- a/src/plugins/nat/nat.api
+++ b/src/plugins/nat/nat.api
@@ -139,6 +139,19 @@ autoreply define nat44_session_cleanup {
u32 context;
};
+/** \brief NAT44 set session limit
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param session_limit - session limit
+ @param vrf_id - vrf id
+*/
+autoreply define nat44_set_session_limit {
+ u32 client_index;
+ u32 context;
+ u32 session_limit;
+ u32 vrf_id;
+};
+
/** \brief Set NAT logging level
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c
index ba682f9325b..7cb0b53368c 100755
--- a/src/plugins/nat/nat.c
+++ b/src/plugins/nat/nat.c
@@ -329,6 +329,26 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index,
&s->out2in);
}
+int
+nat44_set_session_limit (u32 session_limit, u32 vrf_id)
+{
+ snat_main_t *sm = &snat_main;
+ u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
+ u32 len = vec_len (sm->max_translations_per_fib);
+
+ if (len <= fib_index)
+ {
+ vec_validate (sm->max_translations_per_fib, fib_index + 1);
+
+ for (; len < vec_len (sm->max_translations_per_fib); len++)
+ sm->max_translations_per_fib[len] = sm->max_translations;
+ }
+
+ sm->max_translations_per_fib[fib_index] = session_limit;
+ return 0;
+}
+
+
void
nat44_free_session_data (snat_main_t * sm, snat_session_t * s,
u32 thread_index, u8 is_ha)
@@ -4025,9 +4045,10 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
sm->translation_buckets = translation_buckets;
sm->translation_memory_size = translation_memory_size;
-
/* do not exceed load factor 10 */
sm->max_translations = 10 * translation_buckets;
+ vec_add1 (sm->max_translations_per_fib, sm->max_translations);
+
sm->max_translations_per_user = max_translations_per_user == ~0 ?
sm->max_translations : max_translations_per_user;
diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h
index 59a12437f77..9331901a3bc 100644
--- a/src/plugins/nat/nat.h
+++ b/src/plugins/nat/nat.h
@@ -634,12 +634,16 @@ typedef struct snat_main_s
u8 deterministic;
u8 out2in_dpo;
u8 endpoint_dependent;
+
u32 translation_buckets;
uword translation_memory_size;
u32 max_translations;
+ u32 *max_translations_per_fib;
+
u32 user_buckets;
uword user_memory_size;
u32 max_translations_per_user;
+
u32 outside_vrf_id;
u32 outside_fib_index;
u32 inside_vrf_id;
@@ -1254,6 +1258,15 @@ void nat_free_session_data (snat_main_t * sm, snat_session_t * s,
u32 thread_index, u8 is_ha);
/**
+ * @brief Set NAT44 session limit (session limit, vrf id)
+ *
+ * @param session_limit Session limit
+ * @param vrf_id VRF id
+ * @return 0 on success, non-zero value otherwise
+ */
+int nat44_set_session_limit (u32 session_limit, u32 vrf_id);
+
+/**
* @brief Free NAT44 ED session data (lookup keys, external addrres port)
*
* @param s NAT session
diff --git a/src/plugins/nat/nat44/inlines.h b/src/plugins/nat/nat44/inlines.h
index 3c88e15c05e..5cd4164acc8 100644
--- a/src/plugins/nat/nat44/inlines.h
+++ b/src/plugins/nat/nat44/inlines.h
@@ -31,6 +31,17 @@ nat44_maximum_sessions_exceeded (snat_main_t * sm, u32 thread_index)
return 0;
}
+static_always_inline u8
+nat44_ed_maximum_sessions_exceeded (snat_main_t * sm,
+ u32 fib_index, u32 thread_index)
+{
+ u32 translations;
+ translations = pool_elts (sm->per_thread_data[thread_index].sessions);
+ if (vec_len (sm->max_translations_per_fib) <= fib_index)
+ fib_index = 0;
+ return translations >= sm->max_translations_per_fib[fib_index];
+}
+
static_always_inline snat_session_t *
nat44_session_reuse_old (snat_main_t * sm, snat_user_t * u,
snat_session_t * s, u32 thread_index, f64 now)
diff --git a/src/plugins/nat/nat44_cli.c b/src/plugins/nat/nat44_cli.c
index 333b3854845..fe08832c641 100644
--- a/src/plugins/nat/nat44_cli.c
+++ b/src/plugins/nat/nat44_cli.c
@@ -1533,6 +1533,49 @@ print:
}
static clib_error_t *
+nat44_set_session_limit_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ snat_main_t *sm = &snat_main;
+ unformat_input_t _line_input, *line_input = &_line_input;
+ clib_error_t *error = 0;
+
+ u32 session_limit = 0, vrf_id = 0;
+
+ if (sm->deterministic)
+ return clib_error_return (0, UNSUPPORTED_IN_DET_MODE_STR);
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "%u", &session_limit))
+ ;
+ else if (unformat (line_input, "vrf %u", &vrf_id))
+ ;
+ else
+ {
+ error = clib_error_return (0, "unknown input '%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+ }
+
+ if (!session_limit)
+ error = clib_error_return (0, "missing value of session limit");
+ else if (nat44_set_session_limit (session_limit, vrf_id))
+ error = clib_error_return (0, "nat44_set_session_limit failed");
+
+done:
+ unformat_free (line_input);
+
+ return error;
+}
+
+static clib_error_t *
nat44_del_user_command_fn (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
{
@@ -2587,6 +2630,18 @@ VLIB_CLI_COMMAND (nat44_show_sessions_command, static) = {
/*?
* @cliexpar
+ * @cliexstart{set nat44 session limit}
+ * Set NAT44 session limit.
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (nat44_set_session_limit_command, static) = {
+ .path = "set nat44 session limit",
+ .short_help = "set nat44 session limit <limit> [vrf <table-id>]",
+ .function = nat44_set_session_limit_command_fn,
+};
+
+/*?
+ * @cliexpar
* @cliexstart{nat44 del user}
* To delete all NAT44 user sessions:
* vpp# nat44 del user 10.0.0.3
diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c
index a54a4790a20..c58e88bd7f0 100644
--- a/src/plugins/nat/nat_api.c
+++ b/src/plugins/nat/nat_api.c
@@ -242,6 +242,33 @@ vl_api_nat_worker_dump_t_print (vl_api_nat_worker_dump_t * mp, void *handle)
}
static void
+vl_api_nat44_set_session_limit_t_handler (vl_api_nat44_set_session_limit_t *
+ mp)
+{
+ snat_main_t *sm = &snat_main;
+ vl_api_nat44_set_session_limit_reply_t *rmp;
+ int rv = 0;
+
+ rv = nat44_set_session_limit
+ (ntohl (mp->session_limit), ntohl (mp->vrf_id));
+
+ REPLY_MACRO (VL_API_NAT_SET_WORKERS_REPLY);
+}
+
+static void *
+vl_api_nat44_set_session_limit_t_print (vl_api_nat44_set_session_limit_t *
+ mp, void *handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: nat44_set_session_limit ");
+ s = format (s, "session_limit %d", ntohl (mp->session_limit));
+ s = format (s, "vrf_id %d", ntohl (mp->vrf_id));
+
+ FINISH;
+}
+
+static void
vl_api_nat_set_log_level_t_handler (vl_api_nat_set_log_level_t * mp)
{
snat_main_t *sm = &snat_main;
@@ -3141,6 +3168,7 @@ _(NAT_SHOW_CONFIG, nat_show_config) \
_(NAT_SET_WORKERS, nat_set_workers) \
_(NAT_WORKER_DUMP, nat_worker_dump) \
_(NAT44_DEL_USER, nat44_del_user) \
+_(NAT44_SET_SESSION_LIMIT, nat44_set_session_limit) \
_(NAT_SET_LOG_LEVEL, nat_set_log_level) \
_(NAT_IPFIX_ENABLE_DISABLE, nat_ipfix_enable_disable) \
_(NAT_SET_TIMEOUTS, nat_set_timeouts) \
diff --git a/src/plugins/nat/out2in_ed.c b/src/plugins/nat/out2in_ed.c
index 1382125dcf7..26a2e877a54 100644
--- a/src/plugins/nat/out2in_ed.c
+++ b/src/plugins/nat/out2in_ed.c
@@ -193,6 +193,7 @@ create_session_for_static_mapping_ed (snat_main_t * sm,
snat_session_key_t l_key,
snat_session_key_t e_key,
vlib_node_runtime_t * node,
+ u32 rx_fib_index,
u32 thread_index,
twice_nat_type_t twice_nat,
lb_nat_type_t lb_nat, f64 now)
@@ -205,7 +206,8 @@ create_session_for_static_mapping_ed (snat_main_t * sm,
snat_session_key_t eh_key;
nat44_is_idle_session_ctx_t ctx;
- if (PREDICT_FALSE (nat44_maximum_sessions_exceeded (sm, thread_index)))
+ if (PREDICT_FALSE
+ (nat44_ed_maximum_sessions_exceeded (sm, rx_fib_index, thread_index)))
{
b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_SESSIONS_EXCEEDED];
nat_elog_notice ("maximum sessions exceeded");
@@ -359,7 +361,9 @@ create_bypass_for_fwd (snat_main_t * sm, vlib_buffer_t * b, ip4_header_t * ip,
{
u32 proto;
- if (PREDICT_FALSE (nat44_maximum_sessions_exceeded (sm, thread_index)))
+ if (PREDICT_FALSE
+ (nat44_ed_maximum_sessions_exceeded
+ (sm, rx_fib_index, thread_index)))
return;
s = nat_ed_session_alloc (sm, thread_index, now);
@@ -502,7 +506,8 @@ icmp_match_out2in_ed (snat_main_t * sm, vlib_node_runtime_t * node,
/* Create session initiated by host from external network */
s = create_session_for_static_mapping_ed (sm, b, l_key, e_key, node,
- thread_index, 0, 0,
+ rx_fib_index, thread_index, 0,
+ 0,
vlib_time_now
(sm->vlib_main));
@@ -568,7 +573,9 @@ nat44_ed_out2in_unknown_proto (snat_main_t * sm,
}
else
{
- if (PREDICT_FALSE (nat44_maximum_sessions_exceeded (sm, thread_index)))
+ if (PREDICT_FALSE
+ (nat44_ed_maximum_sessions_exceeded
+ (sm, rx_fib_index, thread_index)))
{
b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_SESSIONS_EXCEEDED];
nat_elog_notice ("maximum sessions exceeded");
@@ -1089,6 +1096,7 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm,
/* Create session initiated by host from external network */
s0 = create_session_for_static_mapping_ed (sm, b0, l_key0,
e_key0, node,
+ rx_fib_index0,
thread_index,
twice_nat0,
lb_nat0, now);
diff --git a/src/plugins/nat/test/test_nat.py b/src/plugins/nat/test/test_nat.py
index 19e4c50e8f0..2c0fa1017d6 100644
--- a/src/plugins/nat/test/test_nat.py
+++ b/src/plugins/nat/test/test_nat.py
@@ -1,40 +1,35 @@
#!/usr/bin/env python3
import ipaddress
+import random
import socket
-import unittest
import struct
-import random
-
-from framework import VppTestCase, VppTestRunner, running_extended_tests
+import unittest
+from io import BytesIO
+from time import sleep
import scapy.compat
+from framework import VppTestCase, VppTestRunner, running_extended_tests
+from ipfix import IPFIX, Set, Template, Data, IPFIXDecoder
+from scapy.all import bind_layers, Packet, ByteEnumField, ShortField, \
+ IPField, IntField, LongField, XByteField, FlagsField, FieldLenField, \
+ PacketListField
+from scapy.data import IP_PROTOS
from scapy.layers.inet import IP, TCP, UDP, ICMP
from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
+from scapy.layers.inet6 import ICMPv6DestUnreach, IPerror6, IPv6ExtHdrFragment
from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6EchoReply, \
ICMPv6ND_NS, ICMPv6ND_NA, ICMPv6NDOptDstLLAddr, fragment6
-from scapy.layers.inet6 import ICMPv6DestUnreach, IPerror6, IPv6ExtHdrFragment
from scapy.layers.l2 import Ether, ARP, GRE
-from scapy.data import IP_PROTOS
-from scapy.packet import bind_layers, Raw
-from util import ppp
-from ipfix import IPFIX, Set, Template, Data, IPFIXDecoder
-from time import sleep
-from util import ip4_range
-from vpp_papi import mac_pton
+from scapy.packet import Raw
from syslog_rfc5424_parser import SyslogMessage, ParseError
-from syslog_rfc5424_parser.constants import SyslogFacility, SyslogSeverity
-from io import BytesIO
-from vpp_papi import VppEnum
-from vpp_ip_route import VppIpRoute, VppRoutePath, FibPathType
-from vpp_neighbor import VppNeighbor
-from scapy.all import bind_layers, Packet, ByteEnumField, ShortField, \
- IPField, IntField, LongField, XByteField, FlagsField, FieldLenField, \
- PacketListField
-from ipaddress import IPv6Network
+from syslog_rfc5424_parser.constants import SyslogSeverity
+from util import ip4_range
from util import ppc, ppp
-from socket import inet_pton, AF_INET
from vpp_acl import AclRule, VppAcl, VppAclInterface
+from vpp_ip_route import VppIpRoute, VppRoutePath
+from vpp_neighbor import VppNeighbor
+from vpp_papi import VppEnum
# NAT HA protocol event data
@@ -4151,6 +4146,139 @@ class TestNAT44(MethodHolder):
self.logger.info(self.vapi.cli("show nat ha"))
+class TestNAT44EndpointDependent2(MethodHolder):
+ """ Endpoint-Dependent mapping and filtering test cases """
+
+ @classmethod
+ def setUpConstants(cls):
+ super(TestNAT44EndpointDependent2, cls).setUpConstants()
+ cls.vpp_cmdline.extend(["nat", "{", "endpoint-dependent", "}"])
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TestNAT44EndpointDependent2, cls).tearDownClass()
+
+ def tearDown(self):
+ super(TestNAT44EndpointDependent2, self).tearDown()
+
+ @classmethod
+ def create_and_add_ip4_table(cls, i, table_id):
+ cls.vapi.ip_table_add_del(is_add=1, table={'table_id': table_id})
+ i.set_table_ip4(table_id)
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestNAT44EndpointDependent2, cls).setUpClass()
+
+ cls.create_pg_interfaces(range(3))
+ cls.interfaces = list(cls.pg_interfaces)
+
+ cls.create_and_add_ip4_table(cls.pg1, 10)
+
+ for i in cls.interfaces:
+ i.admin_up()
+ i.config_ip4()
+ i.resolve_arp()
+
+ i.generate_remote_hosts(1)
+ i.configure_ipv4_neighbors()
+
+ def setUp(self):
+ super(TestNAT44EndpointDependent2, self).setUp()
+
+ nat_config = self.vapi.nat_show_config()
+ self.assertEqual(1, nat_config.endpoint_dependent)
+
+ def nat_add_inside_interface(self, i):
+ self.vapi.nat44_interface_add_del_feature(
+ flags=self.config_flags.NAT_IS_INSIDE,
+ sw_if_index=i.sw_if_index, is_add=1)
+
+ def nat_add_outside_interface(self, i):
+ self.vapi.nat44_interface_add_del_feature(
+ flags=self.config_flags.NAT_IS_OUTSIDE,
+ sw_if_index=i.sw_if_index, is_add=1)
+
+ def nat_add_interface_address(self, i):
+ self.nat_addr = i.local_ip4
+ self.vapi.nat44_add_del_interface_addr(
+ sw_if_index=i.sw_if_index, is_add=1)
+
+ def nat_add_address(self, address, vrf_id=0xFFFFFFFF):
+ self.nat_addr = address
+ self.nat44_add_address(address, vrf_id=vrf_id)
+
+ def cli(self, command):
+ result = self.vapi.cli(command)
+ self.logger.info(result)
+ # print(result)
+
+ def show_configuration(self):
+ self.cli("show interface")
+ self.cli("show interface address")
+ self.cli("show nat44 addresses")
+ self.cli("show nat44 interfaces")
+
+ def create_tcp_stream(self, in_if, out_if, count):
+ """
+ Create tcp packet stream
+
+ :param in_if: Inside interface
+ :param out_if: Outside interface
+ :param count: count of packets to generate
+ """
+ pkts = []
+ port = 6303
+
+ for i in range(count):
+ p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
+ IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=64) /
+ TCP(sport=port + i, dport=20))
+ pkts.append(p)
+
+ return pkts
+
+ def test_session_limit_per_vrf(self):
+
+ inside = self.pg0
+ inside_vrf10 = self.pg1
+ outside = self.pg2
+
+ limit = 5
+
+ # 2 interfaces pg0, pg1 (vrf10, limit 1 tcp session)
+ # non existing vrf_id makes process core dump
+ self.vapi.nat44_set_session_limit(session_limit=limit, vrf_id=10)
+
+ self.nat_add_inside_interface(inside)
+ self.nat_add_inside_interface(inside_vrf10)
+ self.nat_add_outside_interface(outside)
+
+ # vrf independent
+ self.nat_add_interface_address(outside)
+
+ # BUG: causing core dump - when bad vrf_id is specified
+ # self.nat44_add_address(outside.local_ip4, vrf_id=20)
+
+ self.show_configuration()
+
+ stream = self.create_tcp_stream(inside_vrf10, outside, limit * 2)
+ inside_vrf10.add_stream(stream)
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ capture = outside.get_capture(limit)
+
+ stream = self.create_tcp_stream(inside, outside, limit * 2)
+ inside.add_stream(stream)
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ capture = outside.get_capture(len(stream))
+
+
class TestNAT44EndpointDependent(MethodHolder):
""" Endpoint-Dependent mapping and filtering test cases """