Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rate-limit nat port table being full warning #366

Merged
merged 2 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion include/dp_nat.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ struct snat_data {
uint16_t network_nat_port_range[2];
uint8_t ul_ip6[16]; /* VIP underlady */
uint8_t ul_nat_ip6[16]; /* NAT Gateway underlay */
uint64_t log_timestamp;
};

struct dnat_data {
Expand Down Expand Up @@ -110,7 +111,7 @@ uint32_t dp_get_vm_network_snat_ip(uint32_t vm_ip, uint32_t vni);
int dp_set_vm_network_snat_ip(uint32_t vm_ip, uint32_t s_ip, uint32_t vni, uint16_t min_port, uint16_t max_port,
uint8_t ul_ipv6[DP_VNF_IPV6_ADDR_SIZE]);
int dp_del_vm_network_snat_ip(uint32_t vm_ip, uint32_t vni);
int dp_allocate_network_snat_port(struct dp_flow *df, uint32_t vni);
int dp_allocate_network_snat_port(struct snat_data *snat_data, struct dp_flow *df, uint32_t vni);
const uint8_t *dp_lookup_network_nat_underlay_ip(struct dp_flow *df);
int dp_remove_network_snat_port(struct flow_value *cntrack);
int dp_list_nat_local_entries(uint32_t nat_ip, struct dp_grpc_responder *responder);
Expand Down
52 changes: 28 additions & 24 deletions src/dp_nat.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include "grpc/dp_grpc_responder.h"
#include "rte_flow/dp_rte_flow.h"

#define DP_NAT_FULL_LOG_DELAY 5 /* seconds */

TAILQ_HEAD(network_nat_head, network_nat_entry);

static struct rte_hash *ipv4_dnat_tbl = NULL;
Expand All @@ -23,6 +25,8 @@ static struct rte_hash *ipv4_netnat_portmap_tbl = NULL;
static struct rte_hash *ipv4_netnat_portoverload_tbl = NULL;
static struct network_nat_head nat_headp;

static uint64_t dp_nat_full_log_delay;

int dp_nat_init(int socket_id)
{
ipv4_snat_tbl = dp_create_jhash_table(DP_NAT_TABLE_MAX, sizeof(struct nat_key),
Expand All @@ -49,6 +53,8 @@ int dp_nat_init(int socket_id)

TAILQ_INIT(&nat_headp);

dp_nat_full_log_delay = rte_get_timer_hz() * DP_NAT_FULL_LOG_DELAY;

return DP_OK;
}

Expand All @@ -67,10 +73,14 @@ struct snat_data *dp_get_vm_snat_data(uint32_t vm_ip, uint32_t vni)
.ip = vm_ip,
.vni = vni
};
int ret;

// this can actually only fail on -ENOENT, because arguments will always be valid
if (DP_FAILED(rte_hash_lookup_data(ipv4_snat_tbl, &nkey, (void **)&data)))
ret = rte_hash_lookup_data(ipv4_snat_tbl, &nkey, (void **)&data);
if (DP_FAILED(ret)) {
if (ret != -ENOENT)
DPS_LOG_ERR("Cannot lookup snat data", DP_LOG_RET(ret));
return NULL;
}

return data;
}
Expand Down Expand Up @@ -405,10 +415,8 @@ const uint8_t *dp_lookup_network_nat_underlay_ip(struct dp_flow *df)
return NULL;
}

int dp_allocate_network_snat_port(struct dp_flow *df, uint32_t vni)
int dp_allocate_network_snat_port(struct snat_data *snat_data, struct dp_flow *df, uint32_t vni)
{
struct nat_key nkey;
struct snat_data *data;
struct netnat_portoverload_tbl_key portoverload_tbl_key;
struct netnat_portmap_key portmap_key;
struct netnat_portmap_data *portmap_data;
Expand All @@ -418,26 +426,13 @@ int dp_allocate_network_snat_port(struct dp_flow *df, uint32_t vni)
bool need_to_find_new_port = true;
uint32_t vm_ip = ntohl(df->src.src_addr);
uint16_t vm_port = ntohs(df->l4_info.trans_port.src_port);

nkey.ip = vm_ip;
nkey.vni = vni;

ret = rte_hash_lookup_data(ipv4_snat_tbl, &nkey, (void **)&data);
if (DP_FAILED(ret)) {
DPS_LOG_ERR("Cannot lookup ipv4 snat key", DP_LOG_RET(ret));
return ret;
}

if (data->network_nat_ip == 0) {
DPS_LOG_ERR("Snat ipv4 lookup data invalid");
return DP_ERROR;
}
uint64_t timestamp;

portmap_key.vm_src_ip = vm_ip;
portmap_key.vni = vni;
portmap_key.vm_src_port = vm_port;

portoverload_tbl_key.nat_ip = data->network_nat_ip;
portoverload_tbl_key.nat_ip = snat_data->network_nat_ip;
portoverload_tbl_key.dst_ip = ntohl(df->dst.dst_addr);
portoverload_tbl_key.dst_port = ntohs(df->l4_info.trans_port.dst_port);
portoverload_tbl_key.l4_type = df->l4_type;
Expand All @@ -463,8 +458,8 @@ int dp_allocate_network_snat_port(struct dp_flow *df, uint32_t vni)
}

if (need_to_find_new_port) {
min_port = data->network_nat_port_range[0];
max_port = data->network_nat_port_range[1];
min_port = snat_data->network_nat_port_range[0];
max_port = snat_data->network_nat_port_range[1];

vm_src_info_hash = (uint32_t)rte_hash_hash(ipv4_netnat_portmap_tbl, &portmap_key);

Expand All @@ -482,7 +477,16 @@ int dp_allocate_network_snat_port(struct dp_flow *df, uint32_t vni)
}

if (!allocated_port) {
DPS_LOG_ERR("No usable ipv4 port found for natting");
// This is normal once the port range gets saturated, but still helpful in logs.
// Therefore the log must be present, just rate-limited (per interface).
timestamp = rte_rdtsc();
if (timestamp > snat_data->log_timestamp + dp_nat_full_log_delay) {
snat_data->log_timestamp = timestamp;
DPS_LOG_WARNING("NAT portmap range is full",
DP_LOG_IPV4(snat_data->network_nat_ip),
DP_LOG_VNI(vni), DP_LOG_SRC_IPV4(vm_ip),
DP_LOG_SRC_PORT(vm_port));
}
return DP_ERROR;
}

Expand All @@ -496,7 +500,7 @@ int dp_allocate_network_snat_port(struct dp_flow *df, uint32_t vni)

if (need_to_find_new_port) {
portmap_data = rte_zmalloc("netnat_portmap_val", sizeof(struct netnat_portmap_data), RTE_CACHE_LINE_SIZE);
portmap_data->nat_ip = data->network_nat_ip;
portmap_data->nat_ip = snat_data->network_nat_ip;
portmap_data->nat_port = allocated_port;
portmap_data->flow_cnt++;

Expand Down
11 changes: 3 additions & 8 deletions src/nodes/snat_node.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
NEXT(SNAT_NEXT_FIREWALL, "firewall")
DP_NODE_REGISTER_NOINIT(SNAT, snat, NEXT_NODES);

static __rte_always_inline rte_edge_t get_next_index(struct rte_node *node, struct rte_mbuf *m)
static __rte_always_inline rte_edge_t get_next_index(__rte_unused struct rte_node *node, struct rte_mbuf *m)
{
struct dp_flow *df = dp_get_flow_ptr(m);
struct flow_value *cntrack = df->conntrack;
Expand Down Expand Up @@ -43,14 +43,9 @@ static __rte_always_inline rte_edge_t get_next_index(struct rte_node *node, stru
cntrack->nf_info.nat_type = DP_FLOW_NAT_TYPE_VIP;
}
if (snat_data->network_nat_ip != 0) {
ret = dp_allocate_network_snat_port(df, vni);
if (DP_FAILED(ret)) {
DPNODE_LOG_WARNING(node, "Failed to allocate new NAT port for connection",
DP_LOG_IPV4(snat_data->network_nat_ip),
DP_LOG_VNI(vni), DP_LOG_SRC_IPV4(src_ip),
DP_LOG_SRC_PORT(ntohs(df->l4_info.trans_port.src_port)));
ret = dp_allocate_network_snat_port(snat_data, df, vni);
if (DP_FAILED(ret))
return SNAT_NEXT_DROP;
}
nat_port = (uint16_t)ret;
ipv4_hdr->src_addr = htonl(snat_data->network_nat_ip);

Expand Down
Loading