NAT: session number limitation to avoid running out of memory crash (VPP-984)
Change-Id: I7f18f8c4ba609d96950dc1f833feb967d4a099b7
Signed-off-by: Matus Fabian <matfabia@cisco.com>
diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c
index dfe1030..dbbc67f 100755
--- a/src/plugins/nat/in2out.c
+++ b/src/plugins/nat/in2out.c
@@ -101,7 +101,8 @@
_(OUT_OF_PORTS, "Out of ports") \
_(BAD_OUTSIDE_FIB, "Outside VRF ID not found") \
_(BAD_ICMP_TYPE, "unsupported ICMP type") \
-_(NO_TRANSLATION, "No translation")
+_(NO_TRANSLATION, "No translation") \
+_(MAX_SESSIONS_EXCEEDED, "Maximum sessions exceeded")
typedef enum {
#define _(sym,str) SNAT_IN2OUT_ERROR_##sym,
@@ -242,6 +243,12 @@
u32 outside_fib_index;
uword * p;
+ if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index)))
+ {
+ b0->error = node->errors[SNAT_IN2OUT_ERROR_MAX_SESSIONS_EXCEEDED];
+ return SNAT_IN2OUT_NEXT_DROP;
+ }
+
p = hash_get (sm->ip4_main->fib_index_by_table_id, sm->outside_vrf_id);
if (! p)
{
@@ -1064,14 +1071,15 @@
ip->checksum = ip_csum_fold (sum);
}
-static void
+static snat_session_t *
snat_in2out_unknown_proto (snat_main_t *sm,
vlib_buffer_t * b,
ip4_header_t * ip,
u32 rx_fib_index,
u32 thread_index,
f64 now,
- vlib_main_t * vm)
+ vlib_main_t * vm,
+ vlib_node_runtime_t * node)
{
clib_bihash_kv_8_8_t kv, value;
clib_bihash_kv_16_8_t s_kv, s_value;
@@ -1108,6 +1116,12 @@
}
else
{
+ if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index)))
+ {
+ b->error = node->errors[SNAT_IN2OUT_ERROR_MAX_SESSIONS_EXCEEDED];
+ return 0;
+ }
+
u_key.addr = ip->src_address;
u_key.fib_index = rx_fib_index;
kv.key = u_key.as_u64;
@@ -1198,7 +1212,7 @@
goto create_ses;
}
}
- return;
+ return 0;
}
create_ses:
@@ -1342,6 +1356,8 @@
if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0)
vnet_buffer(b)->sw_if_index[VLIB_TX] = sm->outside_fib_index;
+
+ return s;
}
static snat_session_t *
@@ -1351,7 +1367,8 @@
u32 rx_fib_index,
u32 thread_index,
f64 now,
- vlib_main_t * vm)
+ vlib_main_t * vm,
+ vlib_node_runtime_t * node)
{
nat_ed_ses_key_t key;
clib_bihash_kv_16_8_t s_kv, s_value;
@@ -1386,6 +1403,12 @@
}
else
{
+ if (PREDICT_FALSE (maximum_sessions_exceeded (sm, thread_index)))
+ {
+ b->error = node->errors[SNAT_IN2OUT_ERROR_MAX_SESSIONS_EXCEEDED];
+ return 0;
+ }
+
l_key.addr = ip->src_address;
l_key.port = udp->src_port;
l_key.protocol = proto;
@@ -1598,8 +1621,10 @@
{
if (PREDICT_FALSE (proto0 == ~0))
{
- snat_in2out_unknown_proto (sm, b0, ip0, rx_fib_index0,
- thread_index, now, vm);
+ s0 = snat_in2out_unknown_proto (sm, b0, ip0, rx_fib_index0,
+ thread_index, now, vm, node);
+ if (!s0)
+ next0 = SNAT_IN2OUT_NEXT_DROP;
goto trace00;
}
@@ -1653,8 +1678,10 @@
{
if (is_slow_path)
{
- s0 = snat_in2out_lb(sm, b0, ip0, rx_fib_index0, thread_index,
- now, vm);
+ s0 = snat_in2out_lb(sm, b0, ip0, rx_fib_index0,
+ thread_index, now, vm, node);
+ if (!s0)
+ next0 = SNAT_IN2OUT_NEXT_DROP;
goto trace00;
}
else
@@ -1770,8 +1797,10 @@
{
if (PREDICT_FALSE (proto1 == ~0))
{
- snat_in2out_unknown_proto (sm, b1, ip1, rx_fib_index1,
- thread_index, now, vm);
+ s1 = snat_in2out_unknown_proto (sm, b1, ip1, rx_fib_index1,
+ thread_index, now, vm, node);
+ if (!s1)
+ next1 = SNAT_IN2OUT_NEXT_DROP;
goto trace01;
}
@@ -1825,8 +1854,10 @@
{
if (is_slow_path)
{
- s1 = snat_in2out_lb(sm, b1, ip1, rx_fib_index1, thread_index,
- now, vm);
+ s1 = snat_in2out_lb(sm, b1, ip1, rx_fib_index1,
+ thread_index, now, vm, node);
+ if (!s1)
+ next1 = SNAT_IN2OUT_NEXT_DROP;
goto trace01;
}
else
@@ -1978,8 +2009,10 @@
{
if (PREDICT_FALSE (proto0 == ~0))
{
- snat_in2out_unknown_proto (sm, b0, ip0, rx_fib_index0,
- thread_index, now, vm);
+ s0 = snat_in2out_unknown_proto (sm, b0, ip0, rx_fib_index0,
+ thread_index, now, vm, node);
+ if (!s0)
+ next0 = SNAT_IN2OUT_NEXT_DROP;
goto trace0;
}
@@ -2034,8 +2067,10 @@
{
if (is_slow_path)
{
- s0 = snat_in2out_lb(sm, b0, ip0, rx_fib_index0, thread_index,
- now, vm);
+ s0 = snat_in2out_lb(sm, b0, ip0, rx_fib_index0,
+ thread_index, now, vm, node);
+ if (!s0)
+ next0 = SNAT_IN2OUT_NEXT_DROP;
goto trace0;
}
else
diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c
index 5f3b006..612085f 100644
--- a/src/plugins/nat/nat.c
+++ b/src/plugins/nat/nat.c
@@ -2216,6 +2216,8 @@
/* for show commands, etc. */
sm->translation_buckets = translation_buckets;
sm->translation_memory_size = translation_memory_size;
+ /* do not exceed load factor 10 */
+ sm->max_translations = 10 * translation_buckets;
sm->user_buckets = user_buckets;
sm->user_memory_size = user_memory_size;
sm->max_translations_per_user = max_translations_per_user;
diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h
index 20e4595..d34ff07 100644
--- a/src/plugins/nat/nat.h
+++ b/src/plugins/nat/nat.h
@@ -345,6 +345,7 @@
u8 deterministic;
u32 translation_buckets;
u32 translation_memory_size;
+ u32 max_translations;
u32 user_buckets;
u32 user_memory_size;
u32 max_translations_per_user;
@@ -551,4 +552,13 @@
return 0;
}
+always_inline u8
+maximum_sessions_exceeded (snat_main_t *sm, u32 thread_index)
+{
+ if (pool_elts (sm->per_thread_data[thread_index].sessions) >= sm->max_translations)
+ return 1;
+
+ return 0;
+}
+
#endif /* __included_nat_h__ */
diff --git a/src/plugins/nat/out2in.c b/src/plugins/nat/out2in.c
index 6472e7f..e5426c1 100755
--- a/src/plugins/nat/out2in.c
+++ b/src/plugins/nat/out2in.c
@@ -87,7 +87,8 @@
_(UNSUPPORTED_PROTOCOL, "Unsupported protocol") \
_(OUT2IN_PACKETS, "Good out2in packets processed") \
_(BAD_ICMP_TYPE, "unsupported ICMP type") \
-_(NO_TRANSLATION, "No translation")
+_(NO_TRANSLATION, "No translation") \
+_(MAX_SESSIONS_EXCEEDED, "Maximum sessions exceeded")
typedef enum {
#define _(sym,str) SNAT_OUT2IN_ERROR_##sym,
@@ -139,6 +140,12 @@
dlist_elt_t * per_user_list_head_elt;
ip4_header_t *ip0;
+ if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index)))
+ {
+ b0->error = node->errors[SNAT_OUT2IN_ERROR_MAX_SESSIONS_EXCEEDED];
+ return 0;
+ }
+
ip0 = vlib_buffer_get_current (b0);
user_key.addr = in2out.addr;
@@ -620,14 +627,15 @@
return next0;
}
-static void
+static snat_session_t *
snat_out2in_unknown_proto (snat_main_t *sm,
vlib_buffer_t * b,
ip4_header_t * ip,
u32 rx_fib_index,
u32 thread_index,
f64 now,
- vlib_main_t * vm)
+ vlib_main_t * vm,
+ vlib_node_runtime_t * node)
{
clib_bihash_kv_8_8_t kv, value;
clib_bihash_kv_16_8_t s_kv, s_value;
@@ -660,13 +668,22 @@
}
else
{
+ if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index)))
+ {
+ b->error = node->errors[SNAT_OUT2IN_ERROR_MAX_SESSIONS_EXCEEDED];
+ return 0;
+ }
+
m_key.addr = ip->dst_address;
m_key.port = 0;
m_key.protocol = 0;
m_key.fib_index = rx_fib_index;
kv.key = m_key.as_u64;
if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
- return;
+ {
+ b->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
+ return 0;
+ }
m = pool_elt_at_index (sm->static_mappings, value.value);
@@ -753,6 +770,8 @@
clib_dlist_remove (tsm->list_pool, s->per_user_index);
clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index,
s->per_user_index);
+
+ return s;
}
static snat_session_t *
@@ -762,7 +781,8 @@
u32 rx_fib_index,
u32 thread_index,
f64 now,
- vlib_main_t * vm)
+ vlib_main_t * vm,
+ vlib_node_runtime_t * node)
{
nat_ed_ses_key_t key;
clib_bihash_kv_16_8_t s_kv, s_value;
@@ -797,6 +817,12 @@
}
else
{
+ if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index)))
+ {
+ b->error = node->errors[SNAT_OUT2IN_ERROR_MAX_SESSIONS_EXCEEDED];
+ return 0;
+ }
+
e_key.addr = ip->dst_address;
e_key.port = udp->dst_port;
e_key.protocol = proto;
@@ -998,8 +1024,10 @@
if (PREDICT_FALSE (proto0 == ~0))
{
- snat_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0,
- thread_index, now, vm);
+ s0 = snat_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0,
+ thread_index, now, vm, node);
+ if (!s0)
+ next0 = SNAT_OUT2IN_NEXT_DROP;
goto trace0;
}
@@ -1042,7 +1070,6 @@
thread_index);
if (!s0)
{
- b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
next0 = SNAT_OUT2IN_NEXT_DROP;
goto trace0;
}
@@ -1051,8 +1078,10 @@
{
if (PREDICT_FALSE (value0.value == ~0ULL))
{
- s0 = snat_out2in_lb(sm, b0, ip0, rx_fib_index0, thread_index, now,
- vm);
+ s0 = snat_out2in_lb(sm, b0, ip0, rx_fib_index0, thread_index,
+ now, vm, node);
+ if (!s0)
+ next0 = SNAT_OUT2IN_NEXT_DROP;
goto trace0;
}
else
@@ -1150,8 +1179,10 @@
if (PREDICT_FALSE (proto1 == ~0))
{
- snat_out2in_unknown_proto(sm, b1, ip1, rx_fib_index1,
- thread_index, now, vm);
+ s1 = snat_out2in_unknown_proto(sm, b1, ip1, rx_fib_index1,
+ thread_index, now, vm, node);
+ if (!s1)
+ next1 = SNAT_OUT2IN_NEXT_DROP;
goto trace1;
}
@@ -1194,7 +1225,6 @@
thread_index);
if (!s1)
{
- b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
next1 = SNAT_OUT2IN_NEXT_DROP;
goto trace1;
}
@@ -1203,8 +1233,10 @@
{
if (PREDICT_FALSE (value1.value == ~0ULL))
{
- s1 = snat_out2in_lb(sm, b1, ip1, rx_fib_index1, thread_index, now,
- vm);
+ s1 = snat_out2in_lb(sm, b1, ip1, rx_fib_index1, thread_index,
+ now, vm, node);
+ if (!s1)
+ next1 = SNAT_OUT2IN_NEXT_DROP;
goto trace1;
}
else
@@ -1328,8 +1360,10 @@
if (PREDICT_FALSE (proto0 == ~0))
{
- snat_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0,
- thread_index, now, vm);
+ s0 = snat_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0,
+ thread_index, now, vm, node);
+ if (!s0)
+ next0 = SNAT_OUT2IN_NEXT_DROP;
goto trace00;
}
@@ -1383,8 +1417,7 @@
thread_index);
if (!s0)
{
- b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
- next0 = SNAT_OUT2IN_NEXT_DROP;
+ next0 = SNAT_OUT2IN_NEXT_DROP;
goto trace00;
}
}
@@ -1392,8 +1425,10 @@
{
if (PREDICT_FALSE (value0.value == ~0ULL))
{
- s0 = snat_out2in_lb(sm, b0, ip0, rx_fib_index0, thread_index, now,
- vm);
+ s0 = snat_out2in_lb(sm, b0, ip0, rx_fib_index0, thread_index,
+ now, vm, node);
+ if (!s0)
+ next0 = SNAT_OUT2IN_NEXT_DROP;
goto trace00;
}
else