diff --git a/include/dp_nat.h b/include/dp_nat.h index e787ebf52..5d46c4057 100644 --- a/include/dp_nat.h +++ b/include/dp_nat.h @@ -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 { @@ -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); diff --git a/src/dp_nat.c b/src/dp_nat.c index 0fae9e8e4..9cdb22193 100644 --- a/src/dp_nat.c +++ b/src/dp_nat.c @@ -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; @@ -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), @@ -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; } @@ -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; } @@ -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; @@ -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; @@ -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); @@ -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; } @@ -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++; diff --git a/src/nodes/snat_node.c b/src/nodes/snat_node.c index 45ce0720e..618e568dd 100644 --- a/src/nodes/snat_node.c +++ b/src/nodes/snat_node.c @@ -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; @@ -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);