diff --git a/coreapi/CMakeLists.txt b/coreapi/CMakeLists.txt
index 6a4373ea36e72a27dd7b9684aeb7448a3b849df8..8f4a6d90be19ac191972d1ab182cba87a2420491 100644
--- a/coreapi/CMakeLists.txt
+++ b/coreapi/CMakeLists.txt
@@ -69,7 +69,6 @@ set(LINPHONE_SOURCE_FILES_C
 	lpconfig.c
 	lsd.c
 	misc.c
-	nat_policy.c
 	payload_type.c
 	player.c
 	presence.c
diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c
index bc85d78951cc2b72849d88aedb3116525f1b7b70..31f7e308966bed0f56ab764113eb253d379c307f 100644
--- a/coreapi/linphonecore.c
+++ b/coreapi/linphonecore.c
@@ -169,7 +169,6 @@ bool_t linphone_core_sound_resources_need_locking(LinphoneCore *lc, const Linpho
 #include "enum.h"
 #include "contact_providers_priv.h"
 
-const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc);
 static void toggle_video_preview(LinphoneCore *lc, bool_t val);
 
 
@@ -5813,7 +5812,7 @@ void linphone_core_send_dtmf(LinphoneCore *lc, char dtmf) {
 void linphone_core_set_stun_server(LinphoneCore *lc, const char *server) {
 	if (lc->nat_policy != NULL) {
 		linphone_nat_policy_set_stun_server(lc->nat_policy, server);
-		linphone_nat_policy_save_to_config(lc->nat_policy);
+		NatPolicy::toCpp(lc->nat_policy)->saveToConfig();
 	} else {
 		linphone_config_set_string(lc->config, "net", "stun_server", server);
 	}
@@ -5852,31 +5851,6 @@ const char *linphone_core_get_nat_address(const LinphoneCore *lc) {
 	return lc->net_conf.nat_address;
 }
 
-const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc) {
-	struct sockaddr_storage ss;
-	socklen_t ss_len;
-	int error;
-	char ipstring [INET6_ADDRSTRLEN];
-
-	if (lc->net_conf.nat_address==NULL) return NULL;
-
-	if (parse_hostname_to_addr (lc->net_conf.nat_address, &ss, &ss_len, 5060)<0) {
-		return lc->net_conf.nat_address;
-	}
-
-	error = bctbx_getnameinfo((struct sockaddr *)&ss, ss_len,
-		ipstring, sizeof(ipstring), NULL, 0, NI_NUMERICHOST);
-	if (error) {
-		return lc->net_conf.nat_address;
-	}
-
-	if (lc->net_conf.nat_address_ip!=NULL){
-		ms_free(lc->net_conf.nat_address_ip);
-	}
-	lc->net_conf.nat_address_ip = ms_strdup (ipstring);
-	return lc->net_conf.nat_address_ip;
-}
-
 void linphone_core_set_firewall_policy(LinphoneCore *lc, LinphoneFirewallPolicy pol) {
 	LinphoneNatPolicy *nat_policy;
 	char *stun_server = NULL;
@@ -5968,8 +5942,8 @@ void linphone_core_set_nat_policy(LinphoneCore *lc, LinphoneNatPolicy *policy) {
 		lc->nat_policy = policy;
 		/*start an immediate (but asynchronous) resolution.*/
 		linphone_nat_policy_resolve_stun_server(policy);
-		linphone_config_set_string(lc->config, "net", "nat_policy_ref", lc->nat_policy->ref);
-		linphone_nat_policy_save_to_config(lc->nat_policy);
+		linphone_config_set_string(lc->config, "net", "nat_policy_ref", NatPolicy::toCpp(policy)->getRef().c_str());
+		NatPolicy::toCpp(policy)->saveToConfig();
 	}
 
 	lc->sal->enableNatHelper(!!linphone_config_get_int(lc->config, "net", "enable_nat_helper", 1));
@@ -7095,7 +7069,7 @@ void sip_config_uninit(LinphoneCore *lc)
 			LinphoneAccount *acc = (LinphoneAccount *)(elem->data);
 			Account::toCpp(acc)->unpublish(); /* to unpublish without changing the stored flag enable_publish */
 			LinphoneNatPolicy *policy = linphone_account_params_get_nat_policy(linphone_account_get_params(acc));
-			if (policy) linphone_nat_policy_release(policy);
+			if (policy) NatPolicy::toCpp(policy)->release();
 
 			/* Do not unregister when push notifications are allowed, otherwise this clears tokens from the SIP server.*/
 			if (!linphone_account_params_get_push_notification_allowed(linphone_account_get_params(acc)) && !linphone_account_params_get_remote_push_notification_allowed(linphone_account_get_params(acc))) {
@@ -7165,7 +7139,7 @@ void sip_config_uninit(LinphoneCore *lc)
 	}
 #endif
 
-	if (lc->nat_policy) linphone_nat_policy_release(lc->nat_policy);
+	if (lc->nat_policy) NatPolicy::toCpp(lc->nat_policy)->release();
 
 	for (i = 0; i < 5 ; ++i) lc->sal->iterate(); /*make sure event are purged*/
 	lc->sal=NULL;
diff --git a/coreapi/nat_policy.c b/coreapi/nat_policy.c
deleted file mode 100644
index 9f92a016f40143b2abf5e1894f77622ede5c5683..0000000000000000000000000000000000000000
--- a/coreapi/nat_policy.c
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * Copyright (c) 2010-2019 Belledonne Communications SARL.
- *
- * This file is part of Liblinphone.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "linphone/core.h"
-
-#include "c-wrapper/c-wrapper.h"
-
-// TODO: From coreapi. Remove me later.
-#include "private.h"
-
-static LinphoneNatPolicy * _linphone_nat_policy_new_with_ref(LinphoneCore *lc, const char *ref) {
-	LinphoneNatPolicy *policy = belle_sip_object_new(LinphoneNatPolicy);
-	policy->lc = lc;
-	policy->ref = belle_sip_strdup(ref);
-	return policy;
-}
-
-static LinphoneNatPolicy * linphone_nat_policy_new(LinphoneCore *lc) {
-	char ref[17] = { 0 };
-	belle_sip_random_token(ref, 16);
-	return _linphone_nat_policy_new_with_ref(lc, ref);
-}
-
-static void linphone_nat_policy_destroy(LinphoneNatPolicy *policy) {
-	if (policy->ref) belle_sip_free(policy->ref);
-	if (policy->stun_server) belle_sip_free(policy->stun_server);
-	if (policy->stun_server_username) belle_sip_free(policy->stun_server_username);
-	if (policy->resolver_results) belle_sip_object_unref(policy->resolver_results);
-	if (policy->stun_resolver_context) {
-		belle_sip_resolver_context_cancel(policy->stun_resolver_context);
-		belle_sip_object_unref(policy->stun_resolver_context);
-	}
-}
-
-
-/* Simply cancel pending DNS resoltion, as the core is going to shutdown.*/
-void linphone_nat_policy_release(LinphoneNatPolicy *policy){
-	if (policy->stun_resolver_context) {
-		belle_sip_resolver_context_cancel(policy->stun_resolver_context);
-		belle_sip_object_unref(policy->stun_resolver_context);
-		policy->stun_resolver_context = NULL;
-	}
-}
-
-bool_t linphone_nat_policy_stun_server_activated(LinphoneNatPolicy *policy) {
-	const char *server = linphone_nat_policy_get_stun_server(policy);
-	return (server != NULL) && (server[0] != '\0')
-		&& ((linphone_nat_policy_stun_enabled(policy) == TRUE) || (linphone_nat_policy_turn_enabled(policy) == TRUE));
-}
-
-
-static void _linphone_nat_policy_clone(LinphoneNatPolicy *policy, const LinphoneNatPolicy *other){
-	policy->lc = other->lc;
-	policy->ref = belle_sip_strdup(other->ref);
-	policy->stun_server = belle_sip_strdup(other->stun_server);
-	policy->stun_server_username = belle_sip_strdup(other->stun_server_username);
-	/* don't clone the resolver context and results */
-	policy->stun_enabled = other->stun_enabled;
-	policy->ice_enabled = other->ice_enabled;
-	policy->turn_enabled = other->turn_enabled;
-	policy->upnp_enabled = other->upnp_enabled;
-	policy->turn_udp_enabled = other->turn_udp_enabled;
-	policy->turn_tcp_enabled = other->turn_tcp_enabled;
-	policy->turn_tls_enabled = other->turn_tls_enabled;
-}
-
-
-BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneNatPolicy);
-
-BELLE_SIP_INSTANCIATE_VPTR(LinphoneNatPolicy, belle_sip_object_t,
-	(belle_sip_object_destroy_t)linphone_nat_policy_destroy,
-	(belle_sip_object_clone_t)_linphone_nat_policy_clone, // clone
-	NULL, // marshal
-	FALSE
-);
-
-
-static void _linphone_nat_policy_save_to_config(const LinphoneNatPolicy *policy, LpConfig *config, int index) {
-	char *section;
-	bctbx_list_t *l = NULL;
-
-	section = belle_sip_strdup_printf("nat_policy_%i", index);
-	linphone_config_set_string(config, section, "ref", policy->ref);
-	linphone_config_set_string(config, section, "stun_server", policy->stun_server);
-	linphone_config_set_string(config, section, "stun_server_username", policy->stun_server_username);
-	if (linphone_nat_policy_upnp_enabled(policy)) {
-		l = bctbx_list_append(l, (void *)"upnp");
-	} else {
-		if (linphone_nat_policy_stun_enabled(policy)) l = bctbx_list_append(l, (void *)"stun");
-		if (linphone_nat_policy_turn_enabled(policy)) l = bctbx_list_append(l, (void *)"turn");
-		if (linphone_nat_policy_ice_enabled(policy)) l = bctbx_list_append(l, (void *)"ice");
-	}
-	linphone_config_set_string_list(config, section, "protocols", l);
-	belle_sip_free(section);
-	bctbx_list_free(l);
-}
-
-LinphoneNatPolicy *linphone_nat_policy_clone(const LinphoneNatPolicy *other){
-	return (LinphoneNatPolicy*) belle_sip_object_clone(BELLE_SIP_OBJECT(other));
-}
-
-void linphone_nat_policy_save_to_config(const LinphoneNatPolicy *policy) {
-	LpConfig *config = policy->lc->config;
-	char *section;
-	int index;
-	bool_t finished = FALSE;
-
-	for (index = 0; finished != TRUE; index++) {
-		section = belle_sip_strdup_printf("nat_policy_%i", index);
-		if (linphone_config_has_section(config, section)) {
-			const char *config_ref = linphone_config_get_string(config, section, "ref", NULL);
-			if ((config_ref != NULL) && (strcmp(config_ref, policy->ref) == 0)) {
-				_linphone_nat_policy_save_to_config(policy, config, index);
-				finished = TRUE;
-			}
-		} else {
-			_linphone_nat_policy_save_to_config(policy, config, index);
-			finished = TRUE;
-		}
-		belle_sip_free(section);
-	}
-}
-
-LinphoneNatPolicy * linphone_nat_policy_ref(LinphoneNatPolicy *policy) {
-	belle_sip_object_ref(policy);
-	return policy;
-}
-
-void linphone_nat_policy_unref(LinphoneNatPolicy *policy) {
-	belle_sip_object_unref(policy);
-}
-
-void *linphone_nat_policy_get_user_data(const LinphoneNatPolicy *policy) {
-	return policy->user_data;
-}
-
-void linphone_nat_policy_set_user_data(LinphoneNatPolicy *policy, void *ud) {
-	policy->user_data = ud;
-}
-
-
-void linphone_nat_policy_clear(LinphoneNatPolicy *policy) {
-	linphone_nat_policy_enable_stun(policy, FALSE);
-	linphone_nat_policy_enable_turn(policy, FALSE);
-	linphone_nat_policy_enable_ice(policy, FALSE);
-	linphone_nat_policy_enable_upnp(policy, FALSE);
-	linphone_nat_policy_set_stun_server(policy, NULL);
-	linphone_nat_policy_set_stun_server_username(policy, NULL);
-}
-
-bool_t linphone_nat_policy_stun_enabled(const LinphoneNatPolicy *policy) {
-	return policy->stun_enabled;
-}
-
-void linphone_nat_policy_enable_stun(LinphoneNatPolicy *policy, bool_t enable) {
-	policy->stun_enabled = enable;
-}
-
-bool_t linphone_nat_policy_turn_enabled(const LinphoneNatPolicy *policy) {
-	return policy->turn_enabled;
-}
-
-void linphone_nat_policy_enable_turn(LinphoneNatPolicy *policy, bool_t enable) {
-	policy->turn_enabled = enable;
-}
-
-bool_t linphone_nat_policy_ice_enabled(const LinphoneNatPolicy *policy) {
-	return policy->ice_enabled;
-}
-
-void linphone_nat_policy_enable_ice(LinphoneNatPolicy *policy, bool_t enable) {
-	policy->ice_enabled = enable;
-}
-
-bool_t linphone_nat_policy_upnp_enabled(const LinphoneNatPolicy *policy) {
-	return policy->upnp_enabled;
-}
-
-void linphone_nat_policy_enable_upnp(LinphoneNatPolicy *policy, bool_t enable) {
-	policy->upnp_enabled = enable;
-	if (enable) {
-		ms_warning("uPnP NAT policy is no longer supported");
-	}
-}
-
-const char * linphone_nat_policy_get_stun_server(const LinphoneNatPolicy *policy) {
-	return policy->stun_server;
-}
-
-void linphone_nat_policy_set_stun_server(LinphoneNatPolicy *policy, const char *stun_server) {
-	char *new_stun_server = NULL;
-
-	if (stun_server != NULL) new_stun_server = belle_sip_strdup(stun_server);
-	if (policy->stun_server != NULL) {
-		belle_sip_free(policy->stun_server);
-		policy->stun_server = NULL;
-	}
-	if (new_stun_server != NULL) {
-		policy->stun_server = new_stun_server;
-	}
-	if (policy->resolver_results) {
-		belle_sip_object_unref(policy->resolver_results);
-		policy->resolver_results = NULL;
-	}
-	if (policy->stun_resolver_context){
-		belle_sip_resolver_context_cancel(policy->stun_resolver_context);
-		belle_sip_object_unref(policy->stun_resolver_context);
-		policy->stun_resolver_context = NULL;
-		
-	}
-	linphone_nat_policy_resolve_stun_server(policy);
-}
-
-const char * linphone_nat_policy_get_stun_server_username(const LinphoneNatPolicy *policy) {
-	return policy->stun_server_username;
-}
-
-void linphone_nat_policy_set_stun_server_username(LinphoneNatPolicy *policy, const char *username) {
-	char *new_username = NULL;
-
-	if (username != NULL) new_username = belle_sip_strdup(username);
-	if (policy->stun_server_username != NULL) {
-		belle_sip_free(policy->stun_server_username);
-		policy->stun_server_username = NULL;
-	}
-	if (new_username != NULL) policy->stun_server_username = new_username;
-}
-
-static void stun_server_resolved(void *data, belle_sip_resolver_results_t *results) {
-	LinphoneNatPolicy *policy = (LinphoneNatPolicy *)data;
-
-	if (policy->resolver_results) {
-		belle_sip_object_unref(policy->resolver_results);
-		policy->resolver_results = NULL;
-	}
-	
-	if (belle_sip_resolver_results_get_addrinfos(results)) {
-		ms_message("Stun server resolution successful.");
-		belle_sip_object_ref(results);
-		policy->resolver_results = results;
-	} else {
-		ms_warning("Stun server resolution failed.");
-	}
-	if (policy->stun_resolver_context){
-		belle_sip_object_unref(policy->stun_resolver_context);
-		policy->stun_resolver_context = NULL;
-	}
-}
-
-void linphone_nat_policy_resolve_stun_server(LinphoneNatPolicy *policy) {
-	const char *service = NULL;
-
-	if (linphone_nat_policy_stun_server_activated(policy) && (policy->lc->sal != NULL) && !policy->stun_resolver_context) {
-		char host[NI_MAXHOST];
-		int port = 0;
-		linphone_parse_host_port(policy->stun_server, host, sizeof(host), &port);
-		if (linphone_nat_policy_turn_enabled(policy)) service = "turn";
-		else if (linphone_nat_policy_stun_enabled(policy)) service = "stun";
-		if (service != NULL) {
-			int family = AF_INET;
-			if (linphone_core_ipv6_enabled(policy->lc) == TRUE) family = AF_INET6;
-			ms_message("Starting stun server resolution [%s]", host);
-			if (port == 0) {
-				port = 3478;
-				policy->stun_resolver_context = policy->lc->sal->resolve(service, "udp", host, port, family, stun_server_resolved, policy);
-			} else {
-				policy->stun_resolver_context = policy->lc->sal->resolveA(host, port, family, stun_server_resolved, policy);
-			}
-			if (policy->stun_resolver_context) belle_sip_object_ref(policy->stun_resolver_context);
-		}
-	}
-}
-
-const struct addrinfo * linphone_nat_policy_get_stun_server_addrinfo(LinphoneNatPolicy *policy) {
-	/*
-	 * It is critical not to block for a long time if it can't be resolved, otherwise this stucks the main thread when making a call.
-	 * On the contrary, a fully asynchronous call initiation is complex to develop.
-	 * The compromise is then:
-	 *  - have a cache of the stun server addrinfo
-	 *  - this cached value is returned when it is non-null
-	 *  - an asynchronous resolution is asked each time this function is called to ensure frequent refreshes of the cached value.
-	 *  - if no cached value exists, block for a short time; this case must be unprobable because the resolution will be asked each
-	 *    time the stun server value is changed.
-	 */
-	if (linphone_nat_policy_stun_server_activated(policy) && (policy->resolver_results == NULL)) {
-		int wait_ms = 0;
-		int wait_limit = 1000;
-		linphone_nat_policy_resolve_stun_server(policy);
-		while ((policy->resolver_results == NULL) && (policy->stun_resolver_context != NULL) && (wait_ms < wait_limit)) {
-			policy->lc->sal->iterate();
-			ms_usleep(50000);
-			wait_ms += 50;
-		}
-	}
-	return policy->resolver_results ? belle_sip_resolver_results_get_addrinfos(policy->resolver_results) : NULL;
-}
-
-LinphoneNatPolicy * linphone_core_create_nat_policy(LinphoneCore *lc) {
-	return linphone_nat_policy_new(lc);
-}
-
-LinphoneNatPolicy * linphone_config_create_nat_policy_from_section(const LinphoneConfig *config, const char* section) {
-	const char *config_ref = linphone_config_get_string(config, section, "ref", NULL);
-	const char *server = linphone_config_get_string(config, section, "stun_server", NULL);
-	const char *username = linphone_config_get_string(config, section, "stun_server_username", NULL);
-	bctbx_list_t *l = linphone_config_get_string_list(config, section, "protocols", NULL);
-	bool_t turn_enable_udp = linphone_config_get_bool(config, section, "turn_enable_udp", TRUE);
-	bool_t turn_enable_tcp = linphone_config_get_bool(config, section, "turn_enable_tcp", FALSE);
-	bool_t turn_enable_tls = linphone_config_get_bool(config, section, "turn_enable_tls", FALSE);
-	LinphoneNatPolicy *policy;
-	if (config_ref)
-		policy = _linphone_nat_policy_new_with_ref(NULL, config_ref);
-	else
-		policy = linphone_nat_policy_new(NULL);
-
-	if (server != NULL) linphone_nat_policy_set_stun_server(policy, server);
-	if (username != NULL) linphone_nat_policy_set_stun_server_username(policy, username);
-	if (l != NULL) {
-		bool_t upnp_enabled = FALSE;
-		bctbx_list_t *elem;
-		for (elem = l; elem != NULL; elem = elem->next) {
-			const char *value = (const char *)elem->data;
-			if (strcmp(value, "stun") == 0) linphone_nat_policy_enable_stun(policy, TRUE);
-			else if (strcmp(value, "turn") == 0) linphone_nat_policy_enable_turn(policy, TRUE);
-			else if (strcmp(value, "ice") == 0) linphone_nat_policy_enable_ice(policy, TRUE);
-			else if (strcmp(value, "upnp") == 0) upnp_enabled = TRUE;
-		}
-		if (upnp_enabled) linphone_nat_policy_enable_upnp(policy, TRUE);
-		bctbx_list_free_with_data(l, (bctbx_list_free_func)ms_free);
-	}
-	linphone_nat_policy_enable_udp_turn_transport(policy, turn_enable_udp);
-	linphone_nat_policy_enable_tcp_turn_transport(policy, turn_enable_tcp);
-	linphone_nat_policy_enable_tls_turn_transport(policy, turn_enable_tls);
-	return policy;
-}
-LinphoneNatPolicy * linphone_core_create_nat_policy_from_config(LinphoneCore *lc, const char *ref) {
-	LpConfig *config = lc->config;
-	LinphoneNatPolicy *policy = NULL;
-	char *section;
-	int index;
-	bool_t finished = FALSE;
-
-	for (index = 0; finished != TRUE; index++) {
-		section = belle_sip_strdup_printf("nat_policy_%i", index);
-		if (linphone_config_has_section(config, section)) {
-			const char *config_ref = linphone_config_get_string(config, section, "ref", NULL);
-			if ((config_ref != NULL) && (strcmp(config_ref, ref) == 0)) {
-				policy = linphone_config_create_nat_policy_from_section(config, section);
-				policy->lc = lc;
-				finished = TRUE;
-			}
-		} else finished = TRUE;
-		belle_sip_free(section);
-	}
-	return policy;
-}
-
-void linphone_nat_policy_enable_udp_turn_transport(LinphoneNatPolicy *policy, bool_t enable) {
-	policy->turn_udp_enabled = enable;
-}
-
-bool_t linphone_nat_policy_udp_turn_transport_enabled(LinphoneNatPolicy *policy) {
-	return policy->turn_udp_enabled;
-}
-
-void linphone_nat_policy_enable_tcp_turn_transport(LinphoneNatPolicy *policy, bool_t enable) {
-	policy->turn_tcp_enabled = enable;
-}
-
-bool_t linphone_nat_policy_tcp_turn_transport_enabled(LinphoneNatPolicy *policy) {
-	return policy->turn_tcp_enabled;
-}
-
-void linphone_nat_policy_enable_tls_turn_transport(LinphoneNatPolicy *policy, bool_t enable) {
-	policy->turn_tls_enabled = enable;
-}
-
-bool_t linphone_nat_policy_tls_turn_transport_enabled(LinphoneNatPolicy *policy) {
-	return policy->turn_tls_enabled;
-}
-
-LinphoneCore *linphone_nat_policy_get_core(const LinphoneNatPolicy *policy) {
-	return policy->lc;
-}
diff --git a/coreapi/private_functions.h b/coreapi/private_functions.h
index fcd3873d84202468b8f66635c736ea893a3cffb8..1039ebcf8d9ed2722857a639c4de11670434bc86 100644
--- a/coreapi/private_functions.h
+++ b/coreapi/private_functions.h
@@ -523,10 +523,6 @@ LinphoneContent *linphone_content_from_sal_body_handler(const SalBodyHandler *re
 void linphone_core_invalidate_friend_subscriptions(LinphoneCore *lc);
 void linphone_core_register_offer_answer_providers(LinphoneCore *lc);
 
-bool_t linphone_nat_policy_stun_server_activated(LinphoneNatPolicy *policy);
-void linphone_nat_policy_release(LinphoneNatPolicy *policy);
-void linphone_nat_policy_save_to_config(const LinphoneNatPolicy *policy);
-
 void linphone_core_create_im_notif_policy(LinphoneCore *lc);
 
 LINPHONE_PUBLIC LinphoneFriend * linphone_friend_new_from_config_file(LinphoneCore *lc, int index);
@@ -708,7 +704,6 @@ LinphoneVideoDefinition * linphone_factory_find_supported_video_definition(const
 LinphoneVideoDefinition * linphone_factory_find_supported_video_definition_by_name(const LinphoneFactory *factory, const char *name);
 
 const char* _linphone_config_load_from_xml_string(LpConfig *lpc, const char *buffer);
-LinphoneNatPolicy * linphone_config_create_nat_policy_from_section(const LinphoneConfig *config, const char* section);
 void _linphone_config_apply_factory_config (LpConfig *config);
 
 SalCustomHeader *linphone_info_message_get_headers (const LinphoneInfoMessage *im);
diff --git a/coreapi/private_structs.h b/coreapi/private_structs.h
index 25e7fcebf241979af3b48f5c02af82a83e99d705..90ec03a8ecf58e89e2bb9fbeaf5437e5d875f2a4 100644
--- a/coreapi/private_structs.h
+++ b/coreapi/private_structs.h
@@ -396,25 +396,7 @@ struct _LinphoneBuffer {
 
 BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneBuffer);
 
-struct _LinphoneNatPolicy {
-	belle_sip_object_t base;
-	void *user_data;
-	LinphoneCore *lc;
-	belle_sip_resolver_context_t *stun_resolver_context;
-	belle_sip_resolver_results_t *resolver_results;
-	char *stun_server;
-	char *stun_server_username;
-	char *ref;
-	bool_t stun_enabled;
-	bool_t turn_enabled;
-	bool_t ice_enabled;
-	bool_t upnp_enabled;
-	bool_t turn_udp_enabled;
-	bool_t turn_tcp_enabled;
-	bool_t turn_tls_enabled;
-};
-
-BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneNatPolicy);
+
 
 struct _LinphoneImNotifPolicy {
 	belle_sip_object_t base;
diff --git a/coreapi/tester_utils.cpp b/coreapi/tester_utils.cpp
index 934e5758455f7ba7635c2b3fef44dfe1fd14029b..3a392afb09ce6b94ace3740e38e39e427edf9244 100644
--- a/coreapi/tester_utils.cpp
+++ b/coreapi/tester_utils.cpp
@@ -153,6 +153,11 @@ SalMediaDescription *_linphone_call_get_local_desc (const LinphoneCall *call) {
 		Call::toCpp(call)->getActiveSession()))->getLocalDesc().get();
 }
 
+SalMediaDescription *_linphone_call_get_remote_desc (const LinphoneCall *call) {
+	return L_GET_PRIVATE(static_pointer_cast<LinphonePrivate::MediaSession>(
+		Call::toCpp(call)->getActiveSession()))->getRemoteDesc().get();
+}
+
 SalMediaDescription *_linphone_call_get_result_desc (const LinphoneCall *call) {
 	return L_GET_PRIVATE(static_pointer_cast<LinphonePrivate::MediaSession>(
 		Call::toCpp(call)->getActiveSession()))->getResultDesc().get();
diff --git a/coreapi/tester_utils.h b/coreapi/tester_utils.h
index 2fbb69495168e915d4937a15befd3f838f2d5fe3..1b378d7e3e7649a230502de762247bb42f35ff5f 100644
--- a/coreapi/tester_utils.h
+++ b/coreapi/tester_utils.h
@@ -42,6 +42,7 @@ LINPHONE_BEGIN_NAMESPACE
 	class SalSubscribeOp;
 LINPHONE_END_NAMESPACE
 LINPHONE_PUBLIC LinphonePrivate::SalMediaDescription *_linphone_call_get_local_desc (const LinphoneCall *call);
+LINPHONE_PUBLIC LinphonePrivate::SalMediaDescription *_linphone_call_get_remote_desc (const LinphoneCall *call);
 LINPHONE_PUBLIC LinphonePrivate::SalMediaDescription *_linphone_call_get_result_desc (const LinphoneCall *call);
 extern "C" {
 	LINPHONE_PUBLIC LinphoneEvent *linphone_event_new_with_op(LinphoneCore *lc, LinphonePrivate::SalEventOp *op, LinphoneSubscriptionDir dir, const char *name);
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index c51d5210e17e6f29d23d4dcffc20599006728870..4baed1a2ffa010e18aabfdc4824dcea3fa66df4e 100644
--- a/include/CMakeLists.txt
+++ b/include/CMakeLists.txt
@@ -48,7 +48,6 @@ set(ROOT_HEADER_FILES
 	logging.h
 	lpconfig.h
 	misc.h
-	nat_policy.h
 	payload_type.h
 	player.h
 	presence.h
@@ -115,6 +114,7 @@ set(C_API_HEADER_FILES
 	c-digest-authentication-policy.h
 	c-types.h
 	c-video-source-descriptor.h
+	c-nat-policy.h
 )
 
 set(ENUMS_HEADER_FILES
diff --git a/include/linphone/api/c-api.h b/include/linphone/api/c-api.h
index 87829a2f1fb92cd39d8cb21ada7b4ff4db7c1a9c..3b817e552ded559b4cbc7b707cc071e6e05a6e9c 100644
--- a/include/linphone/api/c-api.h
+++ b/include/linphone/api/c-api.h
@@ -61,5 +61,6 @@
 #include "linphone/api/c-digest-authentication-policy.h"
 #include "linphone/api/c-types.h"
 #include "linphone/api/c-video-source-descriptor.h"
+#include "linphone/api/c-nat-policy.h"
 
 #endif // ifndef _L_C_API_H_
diff --git a/include/linphone/nat_policy.h b/include/linphone/api/c-nat-policy.h
similarity index 81%
rename from include/linphone/nat_policy.h
rename to include/linphone/api/c-nat-policy.h
index 5a4f13a4540a0a470e1e57b5e4dac5c1c9baebf5..59292741553b1964f1d124aed80e204255a13d0d 100644
--- a/include/linphone/nat_policy.h
+++ b/include/linphone/api/c-nat-policy.h
@@ -158,6 +158,48 @@ LINPHONE_PUBLIC const char * linphone_nat_policy_get_stun_server(const LinphoneN
  */
 LINPHONE_PUBLIC void linphone_nat_policy_set_stun_server(LinphoneNatPolicy *policy, const char *stun_server);
 
+
+/**
+ * Get the mandatory v4 IP address to use with this NAT policy as server-reflexive candidate for ICE.
+ * Used when STUN or TURN are enabled.
+ * @param policy #LinphoneNatPolicy object @notnil
+ * @return the nat v4 address. @maybenil
+ */
+LINPHONE_PUBLIC const char * linphone_nat_policy_get_nat_v4_address(const LinphoneNatPolicy *policy);
+
+/**
+ * Set the mandatory v4 IP address to use with this NAT policy as server-reflexive candidate for ICE.
+ * The IP address is used only if no stun server is set for server-reflexive candidate gathering.
+ * Using this method is useful when Liblinphone is used in a server product, when the server 
+ * does not own the public IP address.
+ * Used when STUN or TURN are enabled.
+ * @param policy #LinphoneNatPolicy object @notnil
+ * @param v4_address The STUN server to use with this NAT policy. @maybenil
+ */
+LINPHONE_PUBLIC void linphone_nat_policy_set_nat_v4_address(LinphoneNatPolicy *policy, const char *v4_address);
+
+
+/**
+ * Get the mandatory v6 IP address to use with this NAT policy as server-reflexive candidate for ICE.
+ * Used when STUN or TURN are enabled.
+ * @param policy #LinphoneNatPolicy object @notnil
+ * @return the nat v4 address. @maybenil
+ */
+LINPHONE_PUBLIC const char * linphone_nat_policy_get_nat_v6_address(const LinphoneNatPolicy *policy);
+
+/**
+ * Set the mandatory v6 IP address to use with this NAT policy as server-reflexive candidate for ICE.
+ * The IP address is used only if no stun server is set for server-reflexive candidate gathering.
+ * Using this method is useful when Liblinphone is used in a server product, when the server 
+ * does not own the public IP address.
+ * Used when STUN or TURN are enabled.
+ * @param policy #LinphoneNatPolicy object @notnil
+ * @param v4_address The STUN server to use with this NAT policy. @maybenil
+ */
+LINPHONE_PUBLIC void linphone_nat_policy_set_nat_v6_address(LinphoneNatPolicy *policy, const char *v4_address);
+
+
+
 /**
  * Get the username used to authenticate with the STUN/TURN server.
  * The authentication will search for a #LinphoneAuthInfo with this username.
@@ -205,7 +247,7 @@ LINPHONE_PUBLIC void linphone_nat_policy_enable_udp_turn_transport(LinphoneNatPo
  * @param policy #LinphoneNatPolicy object @notnil
  * @return Boolean value telling whether UDP TURN transport is enabled.
  */
-LINPHONE_PUBLIC bool_t linphone_nat_policy_udp_turn_transport_enabled(LinphoneNatPolicy *policy);
+LINPHONE_PUBLIC bool_t linphone_nat_policy_udp_turn_transport_enabled(const LinphoneNatPolicy *policy);
 
 /**
  * Enable TCP TURN transport.
@@ -221,7 +263,7 @@ LINPHONE_PUBLIC void linphone_nat_policy_enable_tcp_turn_transport(LinphoneNatPo
  * @param policy #LinphoneNatPolicy object @notnil
  * @return Boolean value telling whether TCP TURN transport is enabled.
  */
-LINPHONE_PUBLIC bool_t linphone_nat_policy_tcp_turn_transport_enabled(LinphoneNatPolicy *policy);
+LINPHONE_PUBLIC bool_t linphone_nat_policy_tcp_turn_transport_enabled(const LinphoneNatPolicy *policy);
 
 /**
  * Enable TLS TURN transport.
@@ -237,7 +279,7 @@ LINPHONE_PUBLIC void linphone_nat_policy_enable_tls_turn_transport(LinphoneNatPo
  * @param policy #LinphoneNatPolicy object @notnil
  * @return Boolean value telling whether TLS TURN transport is enabled.
  */
-LINPHONE_PUBLIC bool_t linphone_nat_policy_tls_turn_transport_enabled(LinphoneNatPolicy *policy);
+LINPHONE_PUBLIC bool_t linphone_nat_policy_tls_turn_transport_enabled(const LinphoneNatPolicy *policy);
 
 /**
  * Returns the #LinphoneCore object managing this nat policy, if any.
diff --git a/include/linphone/core.h b/include/linphone/core.h
index b9ce6e1b2e02bc6038e221d71c977e38dceee4c6..c256075436fef2f5a6f0c2a42447fe4a2fdcdd57 100644
--- a/include/linphone/core.h
+++ b/include/linphone/core.h
@@ -54,7 +54,6 @@
 #include "linphone/logging.h"
 #include "linphone/lpconfig.h"
 #include "linphone/misc.h"
-#include "linphone/nat_policy.h"
 #include "linphone/payload_type.h"
 #include "linphone/player.h"
 #include "linphone/presence.h"
@@ -2748,20 +2747,23 @@ LINPHONE_PUBLIC LinphoneUpnpState linphone_core_get_upnp_state(const LinphoneCor
 LINPHONE_PUBLIC const char * linphone_core_get_upnp_external_ipaddress(const LinphoneCore *core);
 
 /**
- * Set the public IP address of NAT when using the firewall policy is set to use NAT.
+ * Deprecated. This function was used to force a given IP address to appear in SDP.
+ * Unfortunately, this cannot work as explained by https://www.rfc-editor.org/rfc/rfc5389#section-2 .
  * @param core #LinphoneCore object. @notnil
  * @param addr The public IP address of NAT to use. @maybenil
+ * @deprecated 12/10/2022
  * @ingroup network_parameters
  */
-LINPHONE_PUBLIC void linphone_core_set_nat_address(LinphoneCore *core, const char *addr);
+LINPHONE_PUBLIC LINPHONE_DEPRECATED void linphone_core_set_nat_address(LinphoneCore *core, const char *addr);
 
 /**
- * Get the public IP address of NAT being used.
+ * Deprecated. Get the public IP address of NAT being used.
  * @param core #LinphoneCore object. @notnil
  * @return The public IP address of NAT being used. @maybenil
+ * @deprecated 12/10/2022
  * @ingroup network_parameters
  */
-LINPHONE_PUBLIC const char *linphone_core_get_nat_address(const LinphoneCore *core);
+LINPHONE_PUBLIC LINPHONE_DEPRECATED const char *linphone_core_get_nat_address(const LinphoneCore *core);
 
 /**
  * Set the policy to use to pass through NATs/firewalls.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 97c7553cad1e3cfbd3df28a839d7213ea5e08474..9cc76fdebdc86204a10992467dd4bea5fad9611e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -254,6 +254,7 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES
 	logger/logger.h
 	nat/ice-service.h
 	nat/stun-client.h
+	nat/nat-policy.h
 	object/app-data-container.h
 	object/base-object-p.h
 	object/base-object.h
@@ -388,6 +389,7 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES
 	c-wrapper/api/c-recorder-params.cpp
 	c-wrapper/api/c-search-result.cpp
 	c-wrapper/api/c-video-source-descriptor.cpp
+	c-wrapper/api/c-nat-policy.cpp
 	c-wrapper/internal/c-sal.cpp
 	c-wrapper/internal/c-tools.cpp
 	call/call-log.cpp
@@ -483,6 +485,7 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES
 	logger/logger.cpp
 	nat/ice-service.cpp
 	nat/stun-client.cpp
+	nat/nat-policy.cpp
 	object/app-data-container.cpp
 	object/base-object.cpp
 	object/clonable-object.cpp
diff --git a/src/account/account-params.cpp b/src/account/account-params.cpp
index ca4de70ac45dc3da4104bcf02cf15a959ad035be..f9968fe960c89544108461e58b1d9e9d8cbfeeef 100644
--- a/src/account/account-params.cpp
+++ b/src/account/account-params.cpp
@@ -23,7 +23,7 @@
 #include "push-notification/push-notification-config.h"
 #include "c-wrapper/internal/c-tools.h"
 #include "linphone/api/c-address.h"
-#include "linphone/nat_policy.h"
+#include "nat/nat-policy.h"
 #include "linphone/types.h"
 #include "private.h"
 
@@ -74,12 +74,24 @@ AccountParams::AccountParams (LinphoneCore *lc) {
 	mPushNotificationAllowed = lc ? !!linphone_config_get_default_int(lc->config, "proxy", "push_notification_allowed", pushAllowedDefault) : pushAllowedDefault;
 	mRemotePushNotificationAllowed = lc ? !!linphone_config_get_default_int(lc->config, "proxy", "remote_push_notification_allowed", remotePushAllowedDefault) : remotePushAllowedDefault;
 	mRefKey = lc ? linphone_config_get_default_string(lc->config, "proxy", "refkey", "") : "";
+
+	/* CAUTION: the nat_policy_ref meaning in default values is different than in usual [nat_policy_%i] section.
+	 * This is not consistent and error-prone.
+	 * Normally, the nat_policy_ref refers to a "ref" entry within a [nat_policy_%i] section.
+	 */
 	string natPolicyRef = lc ? linphone_config_get_default_string(lc->config, "proxy", "nat_policy_ref", "") : "";
 	if (!natPolicyRef.empty()) {
-		LinphoneNatPolicy *policy = linphone_config_create_nat_policy_from_section(lc->config, natPolicyRef.c_str());
-		setNatPolicy(policy);
-		if (policy) {
-			linphone_nat_policy_unref(policy);
+		NatPolicy * policy = nullptr;
+		if (linphone_config_has_section(lc->config, natPolicyRef.c_str())){
+			/* Odd method - to be deprecated, inconsistent */
+			policy = new NatPolicy(L_GET_CPP_PTR_FROM_C_OBJECT(lc), NatPolicy::ConstructionMethod::FromSectionName, natPolicyRef);
+		}else{
+			/* Usual method */
+			policy = new NatPolicy(L_GET_CPP_PTR_FROM_C_OBJECT(lc), NatPolicy::ConstructionMethod::FromRefName, natPolicyRef);
+		}
+		if (policy){
+			setNatPolicy(policy->toC());
+			policy->unref();
 		} else {
 			lError() << "Cannot create default nat policy with ref [" << natPolicyRef << "] for account [" << this << "]";
 		}
@@ -853,8 +865,8 @@ void AccountParams::writeToConfigFile (LinphoneConfig *config, int index) {
 	linphone_config_set_int(config, key, "publish_expires", mPublishExpires);
 
 	if (mNatPolicy != NULL) {
-		linphone_config_set_string(config, key, "nat_policy_ref", mNatPolicy->ref);
-		linphone_nat_policy_save_to_config(mNatPolicy);
+		linphone_config_set_string(config, key, "nat_policy_ref", NatPolicy::toCpp(mNatPolicy)->getRef().c_str());
+		NatPolicy::toCpp(mNatPolicy)->saveToConfig();
 	}
 
 	linphone_config_set_string(config, key, "conference_factory_uri", mConferenceFactoryUri.c_str());
diff --git a/src/account/account.cpp b/src/account/account.cpp
index 8069bea7b864718c2dced2dfaef979ffc691d7f5..159d45b49a6af6ade01dfe74d20c9882a5744c59 100644
--- a/src/account/account.cpp
+++ b/src/account/account.cpp
@@ -1186,7 +1186,6 @@ void Account::onAudioVideoConferenceFactoryAddressChanged (const LinphoneAddress
 }
 
 void Account::onNatPolicyChanged (LinphoneNatPolicy *policy) {
-	policy->lc = mCore;
 }
 
 LinphoneProxyConfig *Account::getConfig () const {
diff --git a/src/c-wrapper/api/c-nat-policy.cpp b/src/c-wrapper/api/c-nat-policy.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..066475e3597fc53e6ae1e9a05018dfba190d7118
--- /dev/null
+++ b/src/c-wrapper/api/c-nat-policy.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2010-2019 Belledonne Communications SARL.
+ *
+ * This file is part of Liblinphone.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "linphone/api/c-nat-policy.h"
+#include "nat/nat-policy.h"
+
+
+using namespace ::LinphonePrivate;
+
+LinphoneNatPolicy * linphone_core_create_nat_policy(LinphoneCore *lc) {
+	return (new NatPolicy(L_GET_CPP_PTR_FROM_C_OBJECT(lc)))->toC();
+}
+
+LinphoneNatPolicy * linphone_core_create_nat_policy_from_config(LinphoneCore *core, const char *ref){
+	return (new NatPolicy(L_GET_CPP_PTR_FROM_C_OBJECT(core), NatPolicy::ConstructionMethod::FromSectionName, ref))->toC();
+}
+
+
+LinphoneNatPolicy *linphone_nat_policy_clone(const LinphoneNatPolicy *other){
+	return (new NatPolicy(*NatPolicy::toCpp(other)))->toC();
+}
+
+
+LinphoneNatPolicy * linphone_nat_policy_ref(LinphoneNatPolicy *policy) {
+	belle_sip_object_ref(policy);
+	return policy;
+}
+
+void linphone_nat_policy_unref(LinphoneNatPolicy *policy) {
+	belle_sip_object_unref(policy);
+}
+
+void *linphone_nat_policy_get_user_data(const LinphoneNatPolicy *policy) {
+	return NatPolicy::toCpp(policy)->getUserData();
+}
+
+void linphone_nat_policy_set_user_data(LinphoneNatPolicy *policy, void *ud) {
+	NatPolicy::toCpp(policy)->setUserData(ud);
+}
+
+void linphone_nat_policy_clear(LinphoneNatPolicy *policy) {
+	NatPolicy::toCpp(policy)->clear();
+}
+
+bool_t linphone_nat_policy_stun_enabled(const LinphoneNatPolicy *policy) {
+	return NatPolicy::toCpp(policy)->stunEnabled() ? TRUE : FALSE;
+}
+
+void linphone_nat_policy_enable_stun(LinphoneNatPolicy *policy, bool_t enable) {
+	NatPolicy::toCpp(policy)->enableStun(!!enable);
+}
+
+bool_t linphone_nat_policy_turn_enabled(const LinphoneNatPolicy *policy) {
+	return NatPolicy::toCpp(policy)->turnEnabled() ? TRUE : FALSE;
+}
+
+void linphone_nat_policy_enable_turn(LinphoneNatPolicy *policy, bool_t enable) {
+	NatPolicy::toCpp(policy)->enableTurn(!!enable);
+}
+
+bool_t linphone_nat_policy_ice_enabled(const LinphoneNatPolicy *policy) {
+	return NatPolicy::toCpp(policy)->iceEnabled() ? TRUE : FALSE;
+}
+
+void linphone_nat_policy_enable_ice(LinphoneNatPolicy *policy, bool_t enable) {
+	NatPolicy::toCpp(policy)->enableIce(!!enable);
+}
+
+bool_t linphone_nat_policy_upnp_enabled(const LinphoneNatPolicy *policy) {
+	return NatPolicy::toCpp(policy)->upnpEnabled() ? TRUE : FALSE;
+}
+
+void linphone_nat_policy_enable_upnp(LinphoneNatPolicy *policy, bool_t enable) {
+	if (enable) {
+		ms_warning("uPnP NAT policy is no longer supported");
+	}
+	NatPolicy::toCpp(policy)->enableUpnp(!!enable);
+}
+
+const char * linphone_nat_policy_get_stun_server(const LinphoneNatPolicy *policy) {
+	return L_STRING_TO_C(NatPolicy::toCpp(policy)->getStunServer());
+}
+
+void linphone_nat_policy_resolve_stun_server(LinphoneNatPolicy *policy){
+	NatPolicy::toCpp(policy)->resolveStunServer();
+}
+
+const struct addrinfo * linphone_nat_policy_get_stun_server_addrinfo(LinphoneNatPolicy *policy){
+	return NatPolicy::toCpp(policy)->getStunServerAddrinfo();
+}
+
+void linphone_nat_policy_set_stun_server(LinphoneNatPolicy *policy, const char *stun_server) {
+	NatPolicy::toCpp(policy)->setStunServer(L_C_TO_STRING(stun_server));
+}
+
+const char * linphone_nat_policy_get_stun_server_username(const LinphoneNatPolicy *policy) {
+	return L_STRING_TO_C(NatPolicy::toCpp(policy)->getStunServerUsername());
+}
+
+void linphone_nat_policy_set_stun_server_username(LinphoneNatPolicy *policy, const char *username) {
+	NatPolicy::toCpp(policy)->setStunServerUsername(L_C_TO_STRING(username));
+}
+
+
+void linphone_nat_policy_enable_udp_turn_transport(LinphoneNatPolicy *policy, bool_t enable) {
+	NatPolicy::toCpp(policy)->enableTurnUdp(!!enable);
+}
+
+bool_t linphone_nat_policy_udp_turn_transport_enabled(const LinphoneNatPolicy *policy) {
+	return NatPolicy::toCpp(policy)->turnUdpEnabled() ? TRUE : FALSE;
+}
+
+void linphone_nat_policy_enable_tcp_turn_transport(LinphoneNatPolicy *policy, bool_t enable) {
+	NatPolicy::toCpp(policy)->enableTurnTcp(!!enable);
+}
+
+bool_t linphone_nat_policy_tcp_turn_transport_enabled(const LinphoneNatPolicy *policy) {
+	return NatPolicy::toCpp(policy)->turnTcpEnabled() ? TRUE : FALSE;
+}
+
+void linphone_nat_policy_enable_tls_turn_transport(LinphoneNatPolicy *policy, bool_t enable) {
+	NatPolicy::toCpp(policy)->enableTurnTls(!!enable);
+}
+
+bool_t linphone_nat_policy_tls_turn_transport_enabled(const LinphoneNatPolicy *policy) {
+	return NatPolicy::toCpp(policy)->turnTlsEnabled() ? TRUE : FALSE;
+}
+
+LinphoneCore *linphone_nat_policy_get_core(const LinphoneNatPolicy *policy) {
+	return NatPolicy::toCpp(policy)->getCore()->getCCore();
+}
+
+const char * linphone_nat_policy_get_nat_v4_address(const LinphoneNatPolicy *policy){
+	return L_STRING_TO_C(NatPolicy::toCpp(policy)->getNatV4Address());
+}
+
+void linphone_nat_policy_set_nat_v4_address(LinphoneNatPolicy *policy, const char *v4_address){
+	NatPolicy::toCpp(policy)->setNatV4Address(L_C_TO_STRING(v4_address));
+}
+
+const char * linphone_nat_policy_get_nat_v6_address(const LinphoneNatPolicy *policy){
+	return L_STRING_TO_C(NatPolicy::toCpp(policy)->getNatV6Address());
+}
+
+void linphone_nat_policy_set_nat_v6_address(LinphoneNatPolicy *policy, const char *v6_address){
+	NatPolicy::toCpp(policy)->setNatV6Address(L_C_TO_STRING(v6_address));
+}
diff --git a/src/c-wrapper/c-wrapper.h b/src/c-wrapper/c-wrapper.h
index 4c1ddc772a90bd0a7126080b3672af7d9e45bfb4..f892c1556e938c9fbfe9fa8fc092fad5c3a33a4c 100644
--- a/src/c-wrapper/c-wrapper.h
+++ b/src/c-wrapper/c-wrapper.h
@@ -260,7 +260,6 @@ BELLE_SIP_TYPE_ID(LinphoneLDAPContactSearch),
 BELLE_SIP_TYPE_ID(LinphoneLoggingService),
 BELLE_SIP_TYPE_ID(LinphoneLoggingServiceCbs),
 BELLE_SIP_TYPE_ID(LinphoneMagicSearchCbs),
-BELLE_SIP_TYPE_ID(LinphoneNatPolicy),
 BELLE_SIP_TYPE_ID(LinphoneParticipant),
 BELLE_SIP_TYPE_ID(LinphoneParticipantDevice),
 BELLE_SIP_TYPE_ID(LinphoneParticipantDeviceCbs),
diff --git a/src/conference/session/media-session-p.h b/src/conference/session/media-session-p.h
index 7f3e0d2cfae5415cbd3772ee7d1b5e7fa5067321..40342f185586a1dec906759445f471817feec3f2 100644
--- a/src/conference/session/media-session-p.h
+++ b/src/conference/session/media-session-p.h
@@ -84,6 +84,7 @@ public:
 
 	IceService &getIceService() const { return streamsGroup->getIceService(); }
 	std::shared_ptr<SalMediaDescription> getLocalDesc () const { return localDesc; }
+	std::shared_ptr<SalMediaDescription> getRemoteDesc() const { return streamsGroup->getCurrentOfferAnswerContext().remoteMediaDescription; }
 
 	int setupEncryptionKey (SalSrtpCryptoAlgo & crypto, MSCryptoSuite suite, unsigned int tag) const;
 	std::vector<SalSrtpCryptoAlgo> generateNewCryptoKeys() const;
diff --git a/src/nat/ice-service.cpp b/src/nat/ice-service.cpp
index fb992a14c66375d63c2a04116a2b714101dc2900..6a227b420a65480846c87c6953edeaf93d74dd63 100644
--- a/src/nat/ice-service.cpp
+++ b/src/nat/ice-service.cpp
@@ -286,6 +286,38 @@ int IceService::gatherLocalCandidates(){
 	return 0;
 }
 
+void IceService::addPredefinedSflrxCandidates(const NatPolicy *natPolicy){
+	if (!natPolicy) return;
+	bool ipv6Allowed = linphone_core_ipv6_enabled(getCCore());
+	const string &ipv4 = natPolicy->getNatV4Address();
+	const string &ipv6 = natPolicy->getNatV6Address();
+	if (ipv4.empty() && ipv6.empty()) return;
+	const auto & streams = mStreamsGroup.getStreams();
+	for (auto & stream : streams){
+		if (!stream) continue;
+		size_t index = stream->getIndex();
+		IceCheckList *cl = ice_session_check_list(mIceSession, (int)index);
+		if (cl && ice_check_list_state(cl) != ICL_Completed && !ice_check_list_candidates_gathered(cl)) {
+			if (!ipv4.empty()){
+				ice_add_local_candidate(cl, "srflx", AF_INET, L_STRING_TO_C(ipv4), stream->getPortConfig().rtpPort, ICE_RTP_COMPONENT_ID, nullptr);
+			}
+			if (!ipv6.empty() && ipv6Allowed){
+				ice_add_local_candidate(cl, "srflx", AF_INET6, L_STRING_TO_C(ipv6), stream->getPortConfig().rtpPort, ICE_RTP_COMPONENT_ID, nullptr);
+			}
+			if (!rtp_session_rtcp_mux_enabled(cl->rtp_session)) {
+				if (!ipv4.empty()){
+					ice_add_local_candidate(cl, "srflx", AF_INET, L_STRING_TO_C(ipv4), stream->getPortConfig().rtcpPort, ICE_RTCP_COMPONENT_ID, nullptr);
+				}
+				if (!ipv6.empty() && ipv6Allowed){
+					ice_add_local_candidate(cl, "srflx", AF_INET6, L_STRING_TO_C(ipv6), stream->getPortConfig().rtcpPort, ICE_RTCP_COMPONENT_ID, nullptr);
+				}
+			}
+		}
+	}
+	ice_session_set_base_for_srflx_candidates(mIceSession);
+	lInfo() << "Configuration-defined server reflexive candidates added to check lists.";
+}
+
 /** Return values:
  *  1: STUN gathering is started
  *  0: no STUN gathering is started, but it's ok to proceed with ICE anyway (with local candidates only or because STUN gathering was already done before)
@@ -295,9 +327,10 @@ int IceService::gatherIceCandidates () {
 	const struct addrinfo *ai = nullptr;
 	int err = 0;
 	
-	LinphoneNatPolicy *natPolicy = getMediaSessionPrivate().getNatPolicy();
-	if (natPolicy && linphone_nat_policy_stun_server_activated(natPolicy)) {
-		ai = linphone_nat_policy_get_stun_server_addrinfo(natPolicy);
+	LinphoneNatPolicy *cNatPolicy = getMediaSessionPrivate().getNatPolicy();
+	NatPolicy *natPolicy =  cNatPolicy ? NatPolicy::toCpp(cNatPolicy) : nullptr;
+	if (natPolicy && natPolicy->stunServerActivated()) {
+		ai = natPolicy->getStunServerAddrinfo();
 		if (ai)
 			ai = getIcePreferredStunServerAddrinfo(ai);
 		else
@@ -314,16 +347,16 @@ int IceService::gatherIceCandidates () {
 		return -1;
 	}
 	
-	if (ai && natPolicy && linphone_nat_policy_stun_server_activated(natPolicy)) {
-		string server = linphone_nat_policy_get_stun_server(natPolicy);
-		lInfo() << "ICE: gathering candidates from [" << server << "] using " << (linphone_nat_policy_turn_enabled(natPolicy) ? "TURN" : "STUN");
+	if (ai && natPolicy && natPolicy->stunServerActivated()) {
+		const string &server = natPolicy->getStunServer();
+		lInfo() << "ICE: gathering candidates from [" << server << "] using " << (natPolicy->turnEnabled() ? "TURN" : "STUN");
 		// Gather local srflx candidates.
-		if (linphone_nat_policy_turn_enabled(natPolicy)) {
+		if (natPolicy->turnEnabled()) {
 			ice_session_enable_turn(mIceSession, TRUE);
 
-			if (linphone_nat_policy_tls_turn_transport_enabled(natPolicy)) {
+			if (natPolicy->turnTlsEnabled()) {
 				ice_session_set_turn_transport(mIceSession, "tls");
-			} else if (linphone_nat_policy_tcp_turn_transport_enabled(natPolicy)) {
+			} else if (natPolicy->turnTcpEnabled()) {
 				ice_session_set_turn_transport(mIceSession, "tcp");
 			} else {
 				ice_session_set_turn_transport(mIceSession, "udp");
@@ -333,13 +366,14 @@ int IceService::gatherIceCandidates () {
 
 			char host[NI_MAXHOST];
 			int port = 0;
-			linphone_parse_host_port(linphone_nat_policy_get_stun_server(natPolicy), host, sizeof(host), &port);
+			linphone_parse_host_port(server.c_str(), host, sizeof(host), &port);
 			ice_session_set_turn_cn(mIceSession, host);
 		}
 		ice_session_set_stun_auth_requested_cb(mIceSession, MediaSessionPrivate::stunAuthRequestedCb, &getMediaSessionPrivate());
 		err = ice_session_gather_candidates(mIceSession, ai->ai_addr, (socklen_t)ai->ai_addrlen) ? 1 : 0;
 	} else {
-		lInfo() << "ICE: bypass candidates gathering";
+		lInfo() << "ICE: bypass server-reflexive candidates gathering";
+		addPredefinedSflrxCandidates(natPolicy);
 	}
 	if (err == 0) gatheringFinished();
 	return err;
diff --git a/src/nat/ice-service.h b/src/nat/ice-service.h
index 9d2f4703cb8c09f2705316a38ddb04c6a606412d..914b7d72e568a5afbc422e2803cd8d1c75034194 100644
--- a/src/nat/ice-service.h
+++ b/src/nat/ice-service.h
@@ -24,6 +24,7 @@
 
 #include "conference/session/call-session.h"
 #include "conference/session/media-description-renderer.h"
+#include "nat/nat-policy.h"
 
 LINPHONE_BEGIN_NAMESPACE
 
@@ -132,6 +133,7 @@ private:
 	void checkSession(IceRole role, bool preferIpv6DefaultCandidates);
 	int gatherIceCandidates ();
 	int gatherLocalCandidates();
+	void addPredefinedSflrxCandidates(const NatPolicy *natPolicy);
 	bool hasRelayCandidates(const SalMediaDescription &md)const;
 	void chooseDefaultCandidates(const OfferAnswerContext & ctx);
 	StreamsGroup & mStreamsGroup;
diff --git a/src/nat/nat-policy.cpp b/src/nat/nat-policy.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c5795e0cf7e17c43b834bdf90511756d7feb8dc0
--- /dev/null
+++ b/src/nat/nat-policy.cpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2010-2022 Belledonne Communications SARL.
+ *
+ * This file is part of Liblinphone.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "nat-policy.h"
+
+#include <cstring>
+
+#include "private.h"
+
+LINPHONE_BEGIN_NAMESPACE
+
+NatPolicy::NatPolicy(const std::shared_ptr<Core> &core, NatPolicy::ConstructionMethod method, const std::string &value) : CoreAccessor(core){
+	LpConfig *config = linphone_core_get_config(core->getCCore());
+	if (method == ConstructionMethod::Default){
+		mRef = value;
+		if (mRef.empty()){
+			char ref[17] = { 0 };
+			belle_sip_random_token(ref, 16);
+			mRef = ref;
+		}
+	}else if (method == ConstructionMethod::FromSectionName){
+		initFromSection(config, value.c_str());
+	} else if (method == ConstructionMethod::FromRefName){
+		int index;
+
+		for (index = 0; ; index++) {
+			std::ostringstream section;
+			section << "nat_policy_" << index;
+			if (linphone_config_has_section(config, section.str().c_str())) {
+				const char *config_ref = linphone_config_get_string(config, section.str().c_str(), "ref", NULL);
+				if ((config_ref != NULL) && (strcmp(config_ref, value.c_str()) == 0)) {
+					initFromSection(config, section.str().c_str());
+					break;
+				}
+			} else {
+				lError() << "There is no NatPolicy with ref [" << value << "]";
+				break;
+			}
+		}
+	}
+}
+
+NatPolicy::NatPolicy(const NatPolicy &other): HybridObject<LinphoneNatPolicy, NatPolicy>(other), CoreAccessor(other.getCore()){
+	mStunServer = other.mStunServer;
+	mStunServerUsername = other.mStunServerUsername;
+	mRef = other.mRef;
+	mStunEnabled = other.mStunEnabled;
+	mTurnEnabled = other.mTurnEnabled;
+	mIceEnabled = other.mIceEnabled;
+	mUpnpEnabled = other.mUpnpEnabled;
+	mTurnUdpEnabled = other.mTurnUdpEnabled;
+	mTurnTcpEnabled = false;
+	mTurnTlsEnabled = false;
+}
+
+NatPolicy::~NatPolicy(){
+	clearResolverContexts();
+}
+
+void NatPolicy::clearResolverContexts(){
+	if (mStunResolverContext) {
+		belle_sip_resolver_context_cancel(mStunResolverContext);
+		belle_sip_object_unref(mStunResolverContext);
+		mStunResolverContext = nullptr;
+	}
+	if (mResolverResults){
+		belle_sip_object_unref(mResolverResults);
+		mResolverResults = nullptr;
+	}
+}
+
+/* Simply cancel pending DNS resoltion, as the core is going to shutdown.*/
+void NatPolicy::release(){
+	clearResolverContexts();
+}
+
+bool NatPolicy::stunServerActivated() const{
+	return !mStunServer.empty() && (mStunEnabled || mTurnEnabled);
+}
+
+void NatPolicy::saveToConfig(LinphoneConfig *config, int index) const{
+	char *section;
+	bctbx_list_t *l = NULL;
+
+	section = belle_sip_strdup_printf("nat_policy_%i", index);
+	linphone_config_set_string(config, section, "ref", mRef.c_str());
+	linphone_config_set_string(config, section, "stun_server", mStunServer.c_str());
+	linphone_config_set_string(config, section, "stun_server_username", mStunServerUsername.c_str());
+	if (mUpnpEnabled) {
+		l = bctbx_list_append(l, (void *)"upnp");
+	} else {
+		if (mStunEnabled) l = bctbx_list_append(l, (void *)"stun");
+		if (mTurnEnabled) l = bctbx_list_append(l, (void *)"turn");
+		if (mIceEnabled) l = bctbx_list_append(l, (void *)"ice");
+	}
+	linphone_config_set_string_list(config, section, "protocols", l);
+	belle_sip_free(section);
+	bctbx_list_free(l);
+}
+
+void NatPolicy::saveToConfig() {
+	LinphoneConfig *config = linphone_core_get_config(getCore()->getCCore());
+	char *section;
+	int index;
+	bool_t finished = FALSE;
+
+	for (index = 0; finished != TRUE; index++) {
+		section = belle_sip_strdup_printf("nat_policy_%i", index);
+		if (linphone_config_has_section(config, section)) {
+			const char *config_ref = linphone_config_get_string(config, section, "ref", NULL);
+			if ((config_ref != NULL) && (strcmp(config_ref, mRef.c_str()) == 0)) {
+				saveToConfig(config, index);
+				finished = TRUE;
+			}
+		} else {
+			saveToConfig(config, index);
+			finished = TRUE;
+		}
+		belle_sip_free(section);
+	}
+}
+
+void NatPolicy::clear(){
+	clearResolverContexts();
+	mStunServer.clear();
+	mStunServerUsername.clear();
+	mStunEnabled = false;
+	mTurnEnabled = false;
+	mIceEnabled = false;
+	mUpnpEnabled = false;
+	mTurnUdpEnabled = false;
+	mTurnTcpEnabled = false;
+	mTurnTlsEnabled = false;
+}
+
+void NatPolicy::setStunServer(const std::string &stunServer){
+	mStunServer = stunServer;
+	clearResolverContexts();
+	resolveStunServer();
+}
+
+const std::string &NatPolicy::getStunServer()const{
+	return mStunServer;
+}
+
+void NatPolicy::setStunServerUsername(const std::string &stunServerUsername){
+	mStunServerUsername = stunServerUsername;
+}
+const std::string &NatPolicy::getStunServerUsername()const{
+	return mStunServerUsername;
+}
+
+void NatPolicy::setNatV4Address(const std::string &natV4Address){
+	mNatV4Address = natV4Address;
+}
+
+const std::string & NatPolicy::getNatV4Address()const{
+	return mNatV4Address;
+}
+
+void NatPolicy::setNatV6Address(const std::string &natV6Address){
+	mNatV6Address = natV6Address;
+}
+const std::string & NatPolicy::getNatV6Address()const{
+	return mNatV6Address;
+}
+
+void NatPolicy::stunServerResolved(belle_sip_resolver_results_t *results){
+	if (mResolverResults) {
+		belle_sip_object_unref(mResolverResults);
+		mResolverResults = nullptr;
+	}
+	
+	if (belle_sip_resolver_results_get_addrinfos(results)) {
+		ms_message("Stun server resolution successful.");
+		belle_sip_object_ref(results);
+		mResolverResults = results;
+	} else {
+		ms_warning("Stun server resolution failed.");
+	}
+	if (mStunResolverContext){
+		belle_sip_object_unref(mStunResolverContext);
+		mStunResolverContext = nullptr;
+	}
+	
+}
+
+void NatPolicy::sStunServerResolved(void *data, belle_sip_resolver_results_t *results) {
+	NatPolicy *policy = static_cast<NatPolicy*>(data);
+	policy->stunServerResolved(results);
+}
+
+void NatPolicy::resolveStunServer() {
+	LinphoneCore *lc = getCore()->getCCore();
+	const char *service = NULL;
+
+	if (stunServerActivated() && (lc->sal != NULL) && !mStunResolverContext) {
+		char host[NI_MAXHOST];
+		int port = 0;
+		linphone_parse_host_port(mStunServer.c_str(), host, sizeof(host), &port);
+		if (mTurnEnabled) service = "turn";
+		else if (mStunEnabled) service = "stun";
+		if (service != NULL) {
+			int family = AF_INET;
+			if (linphone_core_ipv6_enabled(lc) == TRUE) family = AF_INET6;
+			ms_message("Starting stun server resolution [%s]", host);
+			if (port == 0) {
+				port = 3478;
+				mStunResolverContext = lc->sal->resolve(service, "udp", host, port, family, sStunServerResolved, this);
+			} else {
+				mStunResolverContext = lc->sal->resolveA(host, port, family, sStunServerResolved, this);
+			}
+			if (mStunResolverContext) belle_sip_object_ref(mStunResolverContext);
+		}
+	}
+}
+
+const struct addrinfo * NatPolicy::getStunServerAddrinfo() {
+	/*
+	 * It is critical not to block for a long time if it can't be resolved, otherwise this stucks the main thread when making a call.
+	 * On the contrary, a fully asynchronous call initiation is complex to develop.
+	 * The compromise is then:
+	 *  - have a cache of the stun server addrinfo
+	 *  - this cached value is returned when it is non-null
+	 *  - an asynchronous resolution is asked each time this function is called to ensure frequent refreshes of the cached value.
+	 *  - if no cached value exists, block for a short time; this case must be unprobable because the resolution will be asked each
+	 *    time the stun server value is changed.
+	 */
+	if (stunServerActivated() && (mResolverResults == nullptr)) {
+		int wait_ms = 0;
+		int wait_limit = 1000;
+		resolveStunServer();
+		while ((mResolverResults == nullptr) && (mStunResolverContext != nullptr) && (wait_ms < wait_limit)) {
+			getCore()->getCCore()->sal->iterate();
+			ms_usleep(10000);
+			wait_ms += 10;
+		}
+	}
+	return mResolverResults ? belle_sip_resolver_results_get_addrinfos(mResolverResults) : nullptr;
+}
+
+void NatPolicy::initFromSection(const LinphoneConfig *config, const char* section) {
+	mRef = linphone_config_get_string(config, section, "ref", "");
+	mStunServer = linphone_config_get_string(config, section, "stun_server", "");
+	mStunServerUsername = linphone_config_get_string(config, section, "stun_server_username", "");
+	mTurnUdpEnabled = !!linphone_config_get_bool(config, section, "turn_enable_udp", TRUE);
+	mTurnTcpEnabled = !!linphone_config_get_bool(config, section, "turn_enable_tcp", FALSE);
+	mTurnTlsEnabled = !!linphone_config_get_bool(config, section, "turn_enable_tls", FALSE);
+	bctbx_list_t *l = linphone_config_get_string_list(config, section, "protocols", NULL);
+	
+	if (l != NULL) {
+		bctbx_list_t *elem;
+		for (elem = l; elem != NULL; elem = elem->next) {
+			const char *value = (const char *)elem->data;
+			if (strcmp(value, "stun") == 0) enableStun(true);
+			else if (strcmp(value, "turn") == 0) enableTurn(true);
+			else if (strcmp(value, "ice") == 0) enableIce(true);
+			else if (strcmp(value, "upnp") == 0) mUpnpEnabled = true;
+		}
+		bctbx_list_free_with_data(l, (bctbx_list_free_func)ms_free);
+	}
+}
+
+
+
+
+
+LINPHONE_END_NAMESPACE
+
diff --git a/src/nat/nat-policy.h b/src/nat/nat-policy.h
new file mode 100644
index 0000000000000000000000000000000000000000..e3583f20e81b5bd9b6682c74db21e3f766752095
--- /dev/null
+++ b/src/nat/nat-policy.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2010-2022 Belledonne Communications SARL.
+ *
+ * This file is part of Liblinphone.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef nat_policy_hh
+#define nat_policy_hh
+
+#include "c-wrapper/c-wrapper.h"
+
+
+LINPHONE_BEGIN_NAMESPACE
+
+
+
+class NatPolicy : public bellesip::HybridObject<LinphoneNatPolicy, NatPolicy>, public CoreAccessor{
+public:
+	enum class ConstructionMethod{
+		Default,
+		FromSectionName,
+		FromRefName
+	};
+	NatPolicy(const std::shared_ptr<Core> &core, ConstructionMethod method = ConstructionMethod::Default, const std::string &value = "");
+	NatPolicy(const NatPolicy &other);
+	~NatPolicy();
+	
+	void setStunServer(const std::string &stunServer);
+	const std::string &getStunServer()const;
+	
+	void setStunServerUsername(const std::string &stunServerUsername);
+	const std::string &getStunServerUsername()const;
+	
+	void setNatV4Address(const std::string &natV4Address);
+	const std::string & getNatV4Address()const;
+	
+	void setNatV6Address(const std::string &natV6Address);
+	const std::string & getNatV6Address()const;
+	
+	void enableStun(bool enable){ mStunEnabled = enable; };
+	bool stunEnabled() const { return mStunEnabled; };
+	
+	void enableTurn(bool enable){ mTurnEnabled = enable; };
+	bool turnEnabled() const { return mTurnEnabled; };
+	
+	void enableIce(bool enable){ mIceEnabled = enable; };
+	bool iceEnabled() const { return mIceEnabled; };
+	
+	void enableUpnp(bool enable){ mUpnpEnabled = enable; };
+	bool upnpEnabled() const { return mUpnpEnabled; };
+	
+	void enableTurnUdp(bool enable){ mTurnUdpEnabled = enable; };
+	bool turnUdpEnabled() const { return mTurnUdpEnabled; };
+	
+	void enableTurnTcp(bool enable){ mTurnTcpEnabled = enable; };
+	bool turnTcpEnabled() const { return mTurnTcpEnabled; };
+	
+	void enableTurnTls(bool enable){ mTurnTlsEnabled = enable; };
+	bool turnTlsEnabled() const { return mTurnTlsEnabled; };
+	
+	void setUserData(void *d){ mUserData = d ;}
+	void *getUserData()const{ return mUserData; }
+	
+	const std::string &getRef()const{ return mRef; } 
+	
+	const struct addrinfo * getStunServerAddrinfo();
+	
+	void clear();
+	void release();
+	bool stunServerActivated()const;
+	void saveToConfig();
+	void resolveStunServer();
+private:
+	void saveToConfig(LinphoneConfig *config, int index) const;
+	void initFromSection(const LinphoneConfig *config, const char* section);
+	static void sStunServerResolved(void *data, belle_sip_resolver_results_t *results);
+	void stunServerResolved(belle_sip_resolver_results_t *results);
+	void clearResolverContexts();
+	void *mUserData = nullptr;
+	belle_sip_resolver_context_t *mStunResolverContext = nullptr;
+	belle_sip_resolver_results_t *mResolverResults = nullptr;
+	std::string mStunServer;
+	std::string mStunServerUsername;
+	std::string mRef;
+	std::string mNatV4Address;
+	std::string mNatV6Address;
+	bool mStunEnabled = false;
+	bool mTurnEnabled = false;
+	bool mIceEnabled = false;
+	bool mUpnpEnabled = false;
+	bool mTurnUdpEnabled = false;
+	bool mTurnTcpEnabled = false;
+	bool mTurnTlsEnabled = false;
+};
+
+LINPHONE_END_NAMESPACE
+
+#endif
diff --git a/src/sal/sal_stream_description.h b/src/sal/sal_stream_description.h
index 1d6d81c2ea3921b1015fedc85591333e94442f13..3c6c42bc1a99a22d4c4efd3bc2226cc631d10169 100644
--- a/src/sal/sal_stream_description.h
+++ b/src/sal/sal_stream_description.h
@@ -144,6 +144,7 @@ class LINPHONE_PUBLIC SalStreamDescription {
 		const std::string & getIceUfrag() const;
 		const std::string & getIcePwd() const;
 		bool getIceMismatch() const;
+		const std::vector<SalIceCandidate> & getIceCandidates()const{return ice_candidates; }
 		const SalIceCandidate & getIceCandidateAtIndex(const std::size_t & idx) const;
 		const SalIceRemoteCandidate & getIceRemoteCandidateAtIndex(const std::size_t & idx) const;
 
diff --git a/src/utils/if-addrs.cpp b/src/utils/if-addrs.cpp
index d70760fd62ca0b7925109fbe6bc797f7201d687f..5b5151959304680fe57da0e9a75d886c9031f482 100644
--- a/src/utils/if-addrs.cpp
+++ b/src/utils/if-addrs.cpp
@@ -137,7 +137,7 @@ public:
 	}
 };
 
-void getAddress(IP_ADAPTER_UNICAST_ADDRESS * unicastAddress, std::list<AddressData> * pList){
+static void getAddress(IP_ADAPTER_UNICAST_ADDRESS * unicastAddress, std::list<AddressData> * pList){
 	SOCKET_ADDRESS * pAddr = &unicastAddress->Address;
 	char szAddr[INET6_ADDRSTRLEN];
 	DWORD dwSize = INET6_ADDRSTRLEN;
diff --git a/tester/call_ice_tester.cpp b/tester/call_ice_tester.cpp
index d7233f180d9b322deb69ba320a05346ee7cef887..2e7489e30222509027891f6c6d12c5f6a2f0b93d 100644
--- a/tester/call_ice_tester.cpp
+++ b/tester/call_ice_tester.cpp
@@ -248,6 +248,69 @@ static bool_t is_matching_a_local_address(const std::string &ip, const bctbx_lis
 }
 
 
+static bool assert_ice_candidate_presence(const LinphonePrivate::SalMediaDescription *md, const std::string &type, const std::string &addr){
+	const LinphonePrivate::SalStreamDescription &st = md->getStreamIdx(0);
+	for (const auto & candidate : st.getIceCandidates()){
+		if (candidate.addr == addr && candidate.type == type) return true;
+	}
+	return false;
+}
+
+/*
+ * this test setups hardcoded non working server-reflexive address, to simulate what would happen in a server that is behind a nat.
+ */
+static void call_with_configured_sflrx_addresses(void){
+	LinphoneCoreManager * marie = linphone_core_manager_new( "marie_rc");
+	LinphoneCoreManager *pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
+	LinphoneCall *pauline_call, *marie_call;
+	const char *mariev4 = "5.135.31.160";
+	const char *mariev6 = "2001:41d0:303:3aee::1";
+	const char *paulinev4 = "5.135.31.161";
+	const char *paulinev6 = "2001:41d0:303:3aee::2";
+
+	LinphoneNatPolicy *pol = linphone_core_create_nat_policy(marie->lc);
+	linphone_nat_policy_enable_ice(pol, TRUE);
+	linphone_nat_policy_set_nat_v4_address(pol, mariev4);
+	linphone_nat_policy_set_nat_v6_address(pol, mariev6);
+	linphone_core_set_nat_policy(marie->lc, pol);
+	linphone_nat_policy_unref(pol);
+	
+	pol = linphone_core_create_nat_policy(pauline->lc);
+	linphone_nat_policy_enable_ice(pol, TRUE);
+	linphone_nat_policy_set_nat_v4_address(pol, paulinev4);
+	linphone_nat_policy_set_nat_v6_address(pol, paulinev6);
+	linphone_core_set_nat_policy(pauline->lc, pol);
+	linphone_nat_policy_unref(pol);
+
+	marie_call = linphone_core_invite_address(marie->lc, pauline->identity);
+	BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallOutgoingRinging, 1));
+	BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallIncomingReceived, 1));
+	pauline_call = linphone_core_get_current_call(pauline->lc);
+
+	if (marie_call && pauline_call){
+		linphone_call_accept(pauline_call);
+		BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 1));
+		/*check immmediately that the offer and answer have expected sflrx candidates.*/
+		BC_ASSERT_TRUE(assert_ice_candidate_presence(_linphone_call_get_remote_desc(pauline_call),
+							     "srflx", mariev4));
+		BC_ASSERT_TRUE(assert_ice_candidate_presence(_linphone_call_get_remote_desc(marie_call),
+							     "srflx", paulinev4));
+		if (liblinphone_tester_ipv6_available()){
+			BC_ASSERT_TRUE(assert_ice_candidate_presence(_linphone_call_get_remote_desc(pauline_call),
+							     "srflx", mariev6));
+			BC_ASSERT_TRUE(assert_ice_candidate_presence(_linphone_call_get_remote_desc(marie_call),
+							     "srflx", paulinev6));
+		}
+		
+		BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2));
+		BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 2));
+		check_ice(marie, pauline, LinphoneIceStateHostConnection);
+	}
+	end_call(marie, pauline);
+	
+	linphone_core_manager_destroy(marie);
+	linphone_core_manager_destroy(pauline);
+}
 
 /*
  * this test checks that default candidates are correct (c= line shall be set to the ICE default candidate).
@@ -858,6 +921,7 @@ static test_t call_with_ice_tests[] = {
 	TEST_ONE_TAG("Call with ICE with default default candidate", call_with_ice_with_default_candidate_relay_or_stun, "ICE"),
 	TEST_ONE_TAG("Call with ICE without stun server", call_with_ice_without_stun, "ICE"),
 	TEST_ONE_TAG("Call with ICE without stun server one side", call_with_ice_without_stun2, "ICE"),
+	TEST_ONE_TAG("Call with ICE and configured server-reflexive addresses", call_with_configured_sflrx_addresses, "ICE"),
 	TEST_ONE_TAG("Call with ICE and stun server not responding", call_with_ice_stun_not_responding, "ICE"),
 	TEST_ONE_TAG("Call with ICE ufrag and password set in SDP m line", call_with_ice_ufrag_and_password_set_in_sdp_m_line, "ICE"),
 	TEST_ONE_TAG("Call with ICE ufrag and password set in SDP m line 2", call_with_ice_ufrag_and_password_set_in_sdp_m_line_2, "ICE"),
diff --git a/tester/proxy_config_tester.c b/tester/proxy_config_tester.c
index 49a8e8abb6f6c62d8285bba697e8516edc6fd323..2f059c390eb9ce5afd41ba47d1c8cc1d1e5cc8db 100644
--- a/tester/proxy_config_tester.c
+++ b/tester/proxy_config_tester.c
@@ -240,6 +240,7 @@ static void load_dynamic_proxy_config(void) {
 	nat_policy = linphone_proxy_config_get_nat_policy(proxy);
 
 	if (BC_ASSERT_PTR_NOT_NULL(nat_policy)) {
+		BC_ASSERT_STRING_EQUAL(linphone_nat_policy_get_stun_server(nat_policy), "stun.linphone.org");
 		BC_ASSERT_TRUE(linphone_nat_policy_ice_enabled(nat_policy));
 		BC_ASSERT_TRUE(linphone_nat_policy_stun_enabled(nat_policy));
 		BC_ASSERT_FALSE(linphone_nat_policy_turn_enabled(nat_policy));