From 41fef50d5db5e7deb3cfd901c3108abbc4406813 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Fri, 22 Sep 2017 02:43:05 -0700 Subject: NAT: session number limitation to avoid running out of memory crash (VPP-984) Change-Id: I7f18f8c4ba609d96950dc1f833feb967d4a099b7 Signed-off-by: Matus Fabian --- src/plugins/nat/in2out.c | 69 ++++++++++++++++++++++++++++++++----------- src/plugins/nat/nat.c | 2 ++ src/plugins/nat/nat.h | 10 +++++++ src/plugins/nat/out2in.c | 77 +++++++++++++++++++++++++++++++++++------------- 4 files changed, 120 insertions(+), 38 deletions(-) diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c index dfe103033ae..dbbc67f9327 100755 --- a/src/plugins/nat/in2out.c +++ b/src/plugins/nat/in2out.c @@ -101,7 +101,8 @@ _(IN2OUT_PACKETS, "Good in2out packets processed") \ _(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 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0, 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 @@ snat_hairpinning_unknown_proto (snat_main_t *sm, 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 @@ snat_in2out_unknown_proto (snat_main_t *sm, } 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 @@ snat_in2out_unknown_proto (snat_main_t *sm, goto create_ses; } } - return; + return 0; } create_ses: @@ -1342,6 +1356,8 @@ create_ses: 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 @@ snat_in2out_lb (snat_main_t *sm, 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 @@ snat_in2out_lb (snat_main_t *sm, } 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 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { 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 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { 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 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { 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 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { 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 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { 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 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { 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 5f3b006efda..612085fc132 100644 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -2216,6 +2216,8 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) /* 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 20e45952d55..d34ff07b7fe 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -345,6 +345,7 @@ typedef struct snat_main_s { 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 @@ is_interface_addr(snat_main_t *sm, vlib_node_runtime_t *node, u32 sw_if_index0, 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 6472e7ff50c..e5426c1a80e 100755 --- a/src/plugins/nat/out2in.c +++ b/src/plugins/nat/out2in.c @@ -87,7 +87,8 @@ vlib_node_registration_t snat_det_out2in_node; _(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 @@ create_session_for_static_mapping (snat_main_t *sm, 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 @@ static inline u32 icmp_out2in_slow_path (snat_main_t *sm, 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 @@ snat_out2in_unknown_proto (snat_main_t *sm, } 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 @@ snat_out2in_unknown_proto (snat_main_t *sm, 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 @@ snat_out2in_lb (snat_main_t *sm, 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 @@ snat_out2in_lb (snat_main_t *sm, } 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 @@ snat_out2in_node_fn (vlib_main_t * vm, 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 @@ snat_out2in_node_fn (vlib_main_t * vm, thread_index); if (!s0) { - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; next0 = SNAT_OUT2IN_NEXT_DROP; goto trace0; } @@ -1051,8 +1078,10 @@ snat_out2in_node_fn (vlib_main_t * vm, { 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 @@ snat_out2in_node_fn (vlib_main_t * vm, 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 @@ snat_out2in_node_fn (vlib_main_t * vm, thread_index); if (!s1) { - b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; next1 = SNAT_OUT2IN_NEXT_DROP; goto trace1; } @@ -1203,8 +1233,10 @@ snat_out2in_node_fn (vlib_main_t * vm, { 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 @@ snat_out2in_node_fn (vlib_main_t * vm, 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 @@ snat_out2in_node_fn (vlib_main_t * vm, 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 @@ snat_out2in_node_fn (vlib_main_t * vm, { 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 -- cgit 1.2.3-korg