diff --git a/console/commands.c b/console/commands.c
index debbefa3e0704f9fe753227ec654681017b26414..742e529e6c9246d9b8f3b42a2cdbfb6128e6cf7b 100644
--- a/console/commands.c
+++ b/console/commands.c
@@ -1490,7 +1490,8 @@ static void linphonec_proxy_add(LinphoneCore *lc) {
 		}
 
 		linphone_proxy_config_set_route(cfg, clean);
-		if (!linphone_proxy_config_get_route(cfg)) {
+		const char *route = linphone_proxy_config_get_route(cfg);
+		if (!route) {
 			linphonec_out("Invalid route.\n");
 			free(input);
 			continue;
@@ -1547,12 +1548,13 @@ static void linphonec_proxy_add(LinphoneCore *lc) {
 }
 
 static void linphonec_proxy_display(LinphoneProxyConfig *cfg) {
-	const char *route = linphone_proxy_config_get_route(cfg);
+	char *route = linphone_proxy_config_get_route(cfg);
 	char *identity = linphone_address_as_string(linphone_proxy_config_get_identity_address(cfg));
 	linphonec_out("sip address: %s\nroute: %s\nidentity: %s\nregister: %s\nexpires: %i\nregistered: %s\n",
 	              linphone_proxy_config_get_addr(cfg), (route != NULL) ? route : "", (identity != NULL) ? identity : "",
 	              linphone_proxy_config_register_enabled(cfg) ? "yes" : "no", linphone_proxy_config_get_expires(cfg),
 	              linphone_proxy_config_get_state(cfg) == LinphoneRegistrationOk ? "yes" : "no");
+	bctbx_free(route);
 	ms_free(identity);
 }
 
diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c
index 2152ed25360e51b54340e13a0ba057e29a58ca52..45920df44cc70cbd411981abeda72a5333453e2d 100644
--- a/coreapi/linphonecore.c
+++ b/coreapi/linphonecore.c
@@ -4481,16 +4481,6 @@ const char *linphone_core_get_identity(LinphoneCore *lc) {
 	return from;
 }
 
-const char *linphone_core_get_route(LinphoneCore *lc) {
-	CoreLogContextualizer logContextualizer(lc);
-	LinphoneProxyConfig *proxy = linphone_core_get_default_proxy_config(lc);
-	const char *route = NULL;
-	if (proxy != NULL) {
-		route = linphone_proxy_config_get_route(proxy);
-	}
-	return route;
-}
-
 LinphoneCall *
 linphone_core_start_refered_call(BCTBX_UNUSED(LinphoneCore *lc), LinphoneCall *call, const LinphoneCallParams *params) {
 	shared_ptr<LinphonePrivate::Call> referredCall =
@@ -4528,7 +4518,6 @@ static bctbx_list_t *make_routes_for_proxy(LinphoneProxyConfig *proxy, const Lin
 		}
 		proxy_routes_iterator = bctbx_list_next(proxy_routes_iterator);
 	}
-	bctbx_list_free_with_data((bctbx_list_t *)proxy_routes, (bctbx_list_free_func)bctbx_free);
 	if (srv_route) {
 		const auto srv_route_addr = LinphonePrivate::Address::toCpp(srv_route)->getSharedFromThis();
 		ret = bctbx_list_append(ret, sal_address_clone(srv_route_addr->getImpl()));
@@ -4706,30 +4695,41 @@ linphone_core_lookup_known_account_2(LinphoneCore *lc, const LinphoneAddress *ur
 	LinphoneAccount *found_acc = NULL;
 	LinphoneAccount *found_reg_acc = NULL;
 	LinphoneAccount *found_noreg_acc = NULL;
+	LinphoneAccount *found_acc_domain_match = NULL;
+	LinphoneAccount *found_reg_acc_domain_match = NULL;
+	LinphoneAccount *found_noreg_acc_domain_match = NULL;
 	LinphoneAccount *default_acc = lc->default_account;
 
 	if (!uri) {
 		ms_error("Cannot look for account for NULL uri, returning default");
 		return default_acc;
 	}
-	if (linphone_address_get_domain(uri) == NULL) {
+	const char *uri_domain = linphone_address_get_domain(uri);
+	if (uri_domain == NULL) {
 		ms_message("Cannot look for account for uri [%p] that has no domain set, returning default", uri);
 		return default_acc;
 	}
+
 	/*return default account if it is matching the destination uri*/
 	if (default_acc) {
-		const char *domain = linphone_account_params_get_domain(linphone_account_get_params(default_acc));
-		if (domain && !strcmp(domain, linphone_address_get_domain(uri))) {
+		const LinphoneAddress *identity_address =
+		    linphone_account_params_get_identity_address(linphone_account_get_params(default_acc));
+		if (linphone_address_weak_equal(identity_address, uri)) {
 			found_acc = default_acc;
 			goto end;
 		}
+		const char *domain = linphone_account_params_get_domain(linphone_account_get_params(default_acc));
+		if (domain && !strcmp(domain, uri_domain)) {
+			found_acc_domain_match = default_acc;
+		}
 	}
 
 	/*otherwise return first registered, then first registering matching, otherwise first matching */
 	for (elem = linphone_core_get_account_list(lc); elem != NULL; elem = elem->next) {
 		LinphoneAccount *acc = (LinphoneAccount *)elem->data;
-		const char *domain = linphone_account_params_get_domain(linphone_account_get_params(acc));
-		if (domain != NULL && strcmp(domain, linphone_address_get_domain(uri)) == 0) {
+		const LinphoneAddress *identity_address =
+		    linphone_account_params_get_identity_address(linphone_account_get_params(acc));
+		if (linphone_address_weak_equal(identity_address, uri)) {
 			if (linphone_account_get_state(acc) == LinphoneRegistrationOk) {
 				found_acc = acc;
 				break;
@@ -4740,16 +4740,38 @@ linphone_core_lookup_known_account_2(LinphoneCore *lc, const LinphoneAddress *ur
 				found_noreg_acc = acc;
 			}
 		}
+		const char *domain = linphone_account_params_get_domain(linphone_account_get_params(acc));
+		if (domain && !strcmp(domain, uri_domain)) {
+			if (!found_acc_domain_match && linphone_account_get_state(acc) == LinphoneRegistrationOk) {
+				found_acc_domain_match = acc;
+			} else if (!found_reg_acc_domain_match &&
+			           linphone_account_params_get_register_enabled(linphone_account_get_params(acc))) {
+				found_reg_acc_domain_match = acc;
+			} else if (!found_noreg_acc_domain_match) {
+				found_noreg_acc_domain_match = acc;
+			}
+		}
 	}
 end:
+	// ============ Choose the the most appropriate account =====================
+	// Check first if there is an account whose identity address matches the uri passed as argument to this function.
+	// Then try to guess an account based on the same domain as the address passed as argument to this function
+
+	// Account matched by identity address comparison
 	if (!found_acc && found_reg_acc) found_acc = found_reg_acc;
 	else if (!found_acc && found_noreg_acc) found_acc = found_noreg_acc;
 
+	// Default account fallback
 	if (found_acc && found_acc != default_acc) {
 		ms_debug("Overriding default account setting for this call/message/subscribe operation.");
 	} else if (fallback_to_default && !found_acc) {
 		found_acc = default_acc; /*when no matching account is found, use the default account*/
 	}
+
+	// Account matched by identity address domain comparison
+	if (!found_acc && found_acc_domain_match) found_acc = found_acc_domain_match;
+	else if (!found_acc && found_reg_acc_domain_match) found_acc = found_reg_acc_domain_match;
+	else if (!found_acc && found_noreg_acc_domain_match) found_acc = found_noreg_acc_domain_match;
 	return found_acc;
 }
 
@@ -4939,7 +4961,8 @@ LinphoneCall *linphone_core_invite_address_with_params_2(LinphoneCore *lc,
                                                          const LinphoneContent *content) {
 	CoreLogContextualizer logContextualizer(lc);
 	const char *from = NULL;
-	LinphoneProxyConfig *proxy = NULL;
+	LinphoneAccount *account = NULL;
+	LinphoneAddress *temp_url = NULL;
 	LinphoneAddress *parsed_url2 = NULL;
 	LinphoneCall *call;
 	LinphoneCallParams *cp;
@@ -4964,15 +4987,49 @@ LinphoneCall *linphone_core_invite_address_with_params_2(LinphoneCore *lc,
 
 	if (!L_GET_PRIVATE_FROM_C_OBJECT(lc)->canWeAddCall()) return NULL;
 
-	proxy = linphone_call_params_get_proxy_config(params);
-	if (proxy == NULL) proxy = linphone_core_lookup_known_proxy(lc, addr);
+	// ============= ACCOUNT ====================
+	// Try to retrieve the account from the call params
+	account = linphone_call_params_get_account(params);
+
+	// ============= FROM ====================
+	// Try to retrieve the from header from the call params
+	from = linphone_call_params_get_from_header(params);
+
+	// If no account is found, then look up for one either using either the from or the to address
+	if (account == NULL) {
+		temp_url = from ? linphone_address_new(from) : linphone_address_clone(addr);
+		account = linphone_core_lookup_known_account(lc, temp_url);
+		if (account && !from) {
+			const LinphoneAccountParams *account_params = linphone_account_get_params(account);
+			from = linphone_account_params_get_identity(account_params);
+		}
+		linphone_address_unref(temp_url);
+	}
+
+	// If variable is still NULL, then the SDK has to make a decision because one is dependent from
+	// the other one. In such a scenario, it is assumed that the application wishes to use the default account
+	if (account == NULL) account = linphone_core_get_default_account(lc);
+
+	// If an account has been found earlier on either because it has been set in the call params or it is the default
+	// one or it has been deduced thanks to the from or to addresses, then get the from address if not already set in
+	// the call params
+	if ((from == NULL) && (account != NULL)) {
+		const LinphoneAccountParams *account_params = linphone_account_get_params(account);
+		from = linphone_account_params_get_identity(account_params);
+	}
+
+	/* if no account or no identity defined for this account, default to primary contact*/
+	if (from == NULL) from = linphone_core_get_primary_contact(lc);
+
+	parsed_url2 = linphone_address_new(from);
 
 	cp = _linphone_call_params_copy(params);
 	if (!linphone_call_params_has_avpf_enabled_been_set(cp)) {
-		if (proxy != NULL) {
-			linphone_call_params_enable_avpf(cp, linphone_proxy_config_avpf_enabled(proxy));
+		if (account != NULL) {
+			linphone_call_params_enable_avpf(cp, linphone_account_is_avpf_enabled(account));
+			const LinphoneAccountParams *account_params = linphone_account_get_params(account);
 			linphone_call_params_set_avpf_rr_interval(
-			    cp, (uint16_t)(linphone_proxy_config_get_avpf_rr_interval(proxy) * 1000));
+			    cp, (uint16_t)(linphone_account_params_get_avpf_rr_interval(account_params) * 1000));
 		} else {
 			linphone_call_params_enable_avpf(cp, linphone_core_get_avpf_mode(lc) == LinphoneAVPFEnabled);
 			if (linphone_call_params_avpf_enabled(cp))
@@ -4981,15 +5038,7 @@ LinphoneCall *linphone_core_invite_address_with_params_2(LinphoneCore *lc,
 		}
 	}
 
-	// first check if the call params holds a from
-	from = linphone_call_params_get_from_header(params);
-
-	if ((from == NULL) && (proxy != NULL)) from = linphone_proxy_config_get_identity(proxy);
-	/* if no proxy or no identity defined for this proxy, default to primary contact*/
-	if (from == NULL) from = linphone_core_get_primary_contact(lc);
-
-	parsed_url2 = linphone_address_new(from);
-	call = linphone_call_new_outgoing(lc, parsed_url2, addr, cp, proxy);
+	call = linphone_call_new_outgoing(lc, parsed_url2, addr, cp, account);
 	linphone_address_unref(parsed_url2);
 
 	if (L_GET_PRIVATE_FROM_C_OBJECT(lc)->addCall(Call::toCpp(call)->getSharedFromThis()) != 0) {
@@ -5220,7 +5269,7 @@ LinphoneCall *linphone_core_find_call_from_uri(const LinphoneCore *lc, const cha
 
 LinphoneCall *linphone_core_get_call_by_remote_address2(const LinphoneCore *lc, const LinphoneAddress *raddr) {
 	CoreLogContextualizer logContextualizer(lc);
-	const auto remote_addr = LinphonePrivate::Address::toCpp(const_cast<LinphoneAddress *>(raddr))->getSharedFromThis();
+	const auto remote_addr = LinphonePrivate::Address::toCpp(raddr)->getSharedFromThis();
 	shared_ptr<LinphonePrivate::Call> call = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->getCallByRemoteAddress(remote_addr);
 	return call ? call->toC() : NULL;
 }
diff --git a/coreapi/misc.c b/coreapi/misc.c
index f56d6b2d8f8cfbd22930e7d7caf3dd61bddfe277..c20f0b89f3a413d16bbfdde5c353dd14b46e310f 100644
--- a/coreapi/misc.c
+++ b/coreapi/misc.c
@@ -148,8 +148,7 @@ bool_t lp_spawn_command_line_sync(const char *command, char **result, int *comma
 		}
 		(*result)[err] = 0;
 		err = pclose(f);
-		if (command_ret != NULL)
-			*command_ret = err;
+		if (command_ret != NULL) *command_ret = err;
 		return TRUE;
 	}
 #endif // ENABLE_MICROSOFT_STORE_APP
@@ -195,8 +194,7 @@ int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, sock
 		ms_error("getaddrinfo() failed for %s:%s : %s", host, port, gai_strerror(ret));
 		return -1;
 	}
-	if (!res)
-		return -1;
+	if (!res) return -1;
 	memcpy(ss, res->ai_addr, (size_t)res->ai_addrlen);
 	*socklen = (socklen_t)res->ai_addrlen;
 	freeaddrinfo(res);
@@ -205,9 +203,16 @@ int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, sock
 
 /* this functions runs a simple stun test and return the number of milliseconds to complete the tests, or -1 if the test
  * were failed.*/
-int linphone_run_stun_tests(LinphoneCore *lc, int audioPort, int videoPort, int textPort, char *audioCandidateAddr,
-							int *audioCandidatePort, char *videoCandidateAddr, int *videoCandidatePort,
-							char *textCandidateAddr, int *textCandidatePort) {
+int linphone_run_stun_tests(LinphoneCore *lc,
+                            int audioPort,
+                            int videoPort,
+                            int textPort,
+                            char *audioCandidateAddr,
+                            int *audioCandidatePort,
+                            char *videoCandidateAddr,
+                            int *videoCandidatePort,
+                            char *textCandidateAddr,
+                            int *textCandidatePort) {
 	LinphonePrivate::StunClient *client = new LinphonePrivate::StunClient(L_GET_CPP_PTR_FROM_C_OBJECT(lc));
 	int ret = client->run(audioPort, videoPort, textPort);
 	strncpy(audioCandidateAddr, client->getAudioCandidate().address.c_str(), LINPHONE_IPADDR_SIZE);
@@ -282,8 +287,7 @@ const char *linphone_ice_state_to_string(LinphoneIceState state) {
 bool_t linphone_core_media_description_contains_video_stream(const LinphonePrivate::SalMediaDescription *md) {
 
 	for (const auto &stream : md->streams) {
-		if (stream.type == SalVideo && stream.rtp_port != 0)
-			return TRUE;
+		if (stream.type == SalVideo && stream.rtp_port != 0) return TRUE;
 	}
 	return FALSE;
 }
@@ -503,28 +507,28 @@ LinphoneReason linphone_reason_from_sal(SalReason r) {
 
 LinphoneStreamType sal_stream_type_to_linphone(SalStreamType type) {
 	switch (type) {
-	case SalAudio:
-		return LinphoneStreamTypeAudio;
-	case SalVideo:
-		return LinphoneStreamTypeVideo;
-	case SalText:
-		return LinphoneStreamTypeText;
-	case SalOther:
-		return LinphoneStreamTypeUnknown;
+		case SalAudio:
+			return LinphoneStreamTypeAudio;
+		case SalVideo:
+			return LinphoneStreamTypeVideo;
+		case SalText:
+			return LinphoneStreamTypeText;
+		case SalOther:
+			return LinphoneStreamTypeUnknown;
 	}
 	return LinphoneStreamTypeUnknown;
 }
 
 SalStreamType linphone_stream_type_to_sal(LinphoneStreamType type) {
 	switch (type) {
-	case LinphoneStreamTypeAudio:
-		return SalAudio;
-	case LinphoneStreamTypeVideo:
-		return SalVideo;
-	case LinphoneStreamTypeText:
-		return SalText;
-	case LinphoneStreamTypeUnknown:
-		return SalOther;
+		case LinphoneStreamTypeAudio:
+			return SalAudio;
+		case LinphoneStreamTypeVideo:
+			return SalVideo;
+		case LinphoneStreamTypeText:
+			return SalText;
+		case LinphoneStreamTypeUnknown:
+			return SalOther;
 	}
 	return SalOther;
 }
@@ -601,8 +605,7 @@ static void linphone_core_migrate_proxy_config(LinphoneCore *lc, LinphoneTranspo
 		LinphoneAddress *proxy_addr = linphone_address_new(proxy);
 		LinphoneAddress *route_addr = NULL;
 		char *tmp;
-		if (route)
-			route_addr = linphone_address_new(route);
+		if (route) route_addr = linphone_address_new(route);
 		if (proxy_addr) {
 			linphone_address_set_transport(proxy_addr, type);
 			tmp = linphone_address_as_string(proxy_addr);
@@ -626,8 +629,7 @@ LinphoneStatus linphone_core_migrate_to_multi_transport(LinphoneCore *lc) {
 		int port;
 		if (get_unique_transport(lc, &tpt, &port) == 0) {
 			LinphoneSipTransports newtp = {0};
-			if (linphone_config_get_int(lc->config, "sip", "sip_random_port", 0))
-				port = -1;
+			if (linphone_config_get_int(lc->config, "sip", "sip_random_port", 0)) port = -1;
 			ms_message("Core is using a single SIP transport, migrating proxy config and enabling multi-transport.");
 			linphone_core_migrate_proxy_config(lc, tpt);
 			newtp.udp_port = port;
@@ -650,8 +652,7 @@ LinphoneToneDescription *linphone_tone_description_new(LinphoneToneID id, const
 }
 
 void linphone_tone_description_destroy(LinphoneToneDescription *obj) {
-	if (obj->audiofile)
-		ms_free(obj->audiofile);
+	if (obj->audiofile) ms_free(obj->audiofile);
 	ms_free(obj);
 }
 
@@ -732,12 +733,9 @@ const MSCryptoSuite *linphone_core_get_all_supported_srtp_crypto_suites(Linphone
 static char *seperate_string_list(char **str) {
 	char *ret;
 
-	if (str == NULL)
-		return NULL;
-	if (*str == NULL)
-		return NULL;
-	if (**str == '\0')
-		return NULL;
+	if (str == NULL) return NULL;
+	if (*str == NULL) return NULL;
+	if (**str == '\0') return NULL;
 
 	ret = *str;
 	for (; **str != '\0' && **str != ' ' && **str != ','; (*str)++)
@@ -1043,8 +1041,7 @@ bctbx_list_t *linphone_core_get_supported_file_formats_list(const LinphoneCore *
 bool_t linphone_core_file_format_supported(LinphoneCore *lc, const char *fmt) {
 	const char **formats = linphone_core_get_supported_file_formats(lc);
 	for (; *formats != NULL; ++formats) {
-		if (strcasecmp(*formats, fmt) == 0)
-			return TRUE;
+		if (strcasecmp(*formats, fmt) == 0) return TRUE;
 	}
 	return FALSE;
 }
@@ -1057,8 +1054,7 @@ bool_t linphone_core_symmetric_rtp_enabled(LinphoneCore *lc) {
 }
 
 LinphoneStatus linphone_core_set_network_simulator_params(LinphoneCore *lc, const OrtpNetworkSimulatorParams *params) {
-	if (params != &lc->net_conf.netsim_params)
-		lc->net_conf.netsim_params = *params;
+	if (params != &lc->net_conf.netsim_params) lc->net_conf.netsim_params = *params;
 
 	/* update all running streams. */
 	for (const auto &call : L_GET_CPP_PTR_FROM_C_OBJECT(lc)->getCalls()) {
diff --git a/coreapi/private_functions.h b/coreapi/private_functions.h
index 6870c277d9570fb1d0a5ee9565dc15a231859ce4..bd2d4489a2df7a82683df6431269c9192dfadf88 100644
--- a/coreapi/private_functions.h
+++ b/coreapi/private_functions.h
@@ -68,7 +68,7 @@ LinphoneCall *linphone_call_new_outgoing(struct _LinphoneCore *lc,
                                          const LinphoneAddress *from,
                                          const LinphoneAddress *to,
                                          const LinphoneCallParams *params,
-                                         LinphoneProxyConfig *cfg);
+                                         LinphoneAccount *account);
 LinphoneCall *linphone_call_new_incoming(struct _LinphoneCore *lc,
                                          const LinphoneAddress *from,
                                          const LinphoneAddress *to,
diff --git a/coreapi/proxy.c b/coreapi/proxy.c
index 1a8d0d5d57ae318ea438a35def6064b469c6d31c..9b5ac723a22a65a8d9585fa0ab7bf23f8709d475 100644
--- a/coreapi/proxy.c
+++ b/coreapi/proxy.c
@@ -540,7 +540,8 @@ void _linphone_proxy_config_unpublish(LinphoneProxyConfig *obj) {
 const char *linphone_proxy_config_get_route(const LinphoneProxyConfig *cfg) {
 	const bctbx_list_t *list = linphone_proxy_config_get_routes(cfg);
 	if (list != NULL) {
-		return (const char *)bctbx_list_get_data(list);
+		const char *route = (const char *)bctbx_list_get_data(list);
+		return route;
 	}
 
 	return NULL;
@@ -548,7 +549,7 @@ const char *linphone_proxy_config_get_route(const LinphoneProxyConfig *cfg) {
 
 const bctbx_list_t *linphone_proxy_config_get_routes(const LinphoneProxyConfig *cfg) {
 	const LinphoneAccountParams *params = cfg->edit ? cfg->edit : linphone_account_get_params(cfg->account);
-	return L_GET_C_LIST_FROM_CPP_LIST(AccountParams::toCpp(params)->getRoutesString());
+	return AccountParams::toCpp(params)->getRoutesCString();
 }
 
 const LinphoneAddress *linphone_proxy_config_get_identity_address(const LinphoneProxyConfig *cfg) {
diff --git a/include/linphone/api/c-call.h b/include/linphone/api/c-call.h
index 40df58ef375e2e0d7871bb4a9fc711cc505ef3a2..aaac8119bb540413721cae162b7322cea465e452 100644
--- a/include/linphone/api/c-call.h
+++ b/include/linphone/api/c-call.h
@@ -267,6 +267,13 @@ LINPHONE_PUBLIC const LinphoneErrorInfo *linphone_call_get_error_info(const Linp
  **/
 LINPHONE_PUBLIC const char *linphone_call_get_remote_user_agent(LinphoneCall *call);
 
+/**
+ * Returns the far end's sip contact as an address, if available.
+ * @param call #LinphoneCall object. @notnil
+ * @return the remote contact as a #LinphoneAddress or NULL. @maybenil
+ **/
+LINPHONE_PUBLIC const LinphoneAddress *linphone_call_get_remote_contact_address(LinphoneCall *call);
+
 /**
  * Returns the far end's sip contact as a string, if available.
  * @param call #LinphoneCall object. @notnil
diff --git a/src/account/account-params.cpp b/src/account/account-params.cpp
index 49cf60d010c58b9e4d6f40abbae5230e5f19835f..55a0ac3b7f2503c5e01a003714e11ace55dd52a8 100644
--- a/src/account/account-params.cpp
+++ b/src/account/account-params.cpp
@@ -56,7 +56,8 @@ AccountParams::AccountParams(LinphoneCore *lc) {
 	mProxyAddress = Address::create(mProxy);
 	string route = lc ? linphone_config_get_default_string(lc->config, "proxy", "reg_route", "") : "";
 	if (!route.empty()) {
-		mRoutes.emplace_back(Address::create(route));
+		const std::list<std::shared_ptr<Address>> routes{Address::create(route)};
+		setRoutes(routes);
 	}
 	mRealm = lc ? linphone_config_get_default_string(lc->config, "proxy", "realm", "") : "";
 	mQualityReportingEnabled =
@@ -306,7 +307,7 @@ AccountParams::AccountParams(const AccountParams &other) : HybridObject(other),
 
 	mFileTransferServer = other.mFileTransferServer;
 
-	mRoutes = other.mRoutes;
+	setRoutes(other.mRoutes);
 	mPrivacy = other.mPrivacy;
 	mIdentity = other.mIdentity;
 	if (other.mIdentityAddress) {
@@ -338,6 +339,9 @@ AccountParams::AccountParams(const AccountParams &other) : HybridObject(other),
 
 AccountParams::~AccountParams() {
 	if (mPushNotificationConfig) mPushNotificationConfig->unref();
+	if (mRoutesCString) {
+		bctbx_list_free_with_data(mRoutesCString, (bctbx_list_free_func)bctbx_free);
+	}
 }
 
 AccountParams *AccountParams::clone() const {
@@ -394,6 +398,7 @@ void AccountParams::setOutboundProxyEnabled(bool enable) {
 	} else {
 		mRoutes.clear();
 	}
+	updateRoutesCString();
 }
 
 void AccountParams::setPushNotificationAllowed(bool allow) {
@@ -475,12 +480,14 @@ void AccountParams::setFileTranferServer(const std::string &fileTransferServer)
 
 LinphoneStatus AccountParams::setRoutes(const std::list<std::shared_ptr<Address>> &routes) {
 	mRoutes = routes;
+	updateRoutesCString();
 	return 0;
 }
 
 LinphoneStatus AccountParams::setRoutesFromStringList(const bctbx_list_t *routes) {
 	mRoutes.clear();
 	bctbx_list_t *iterator = (bctbx_list_t *)routes;
+	bool error = false;
 	while (iterator != nullptr) {
 		char *route = (char *)bctbx_list_get_data(iterator);
 		if (route != NULL && route[0] != '\0') {
@@ -496,13 +503,13 @@ LinphoneStatus AccountParams::setRoutesFromStringList(const bctbx_list_t *routes
 				sal_address_unref(addr);
 				mRoutes.emplace_back(Address::create(tmp.c_str()));
 			} else {
-				return -1;
+				error = true;
 			}
 		}
 		iterator = bctbx_list_next(iterator);
 	}
-
-	return 0;
+	updateRoutesCString();
+	return (error) ? -1 : 0;
 }
 
 void AccountParams::setPrivacy(LinphonePrivacyMask privacy) {
@@ -705,11 +712,26 @@ const std::list<std::shared_ptr<Address>> &AccountParams::getRoutes() const {
 }
 
 const std::list<std::string> AccountParams::getRoutesString() const {
-	std::list<std::string> routes;
+	std::list<std::string> routesString;
 	for (const auto &r : mRoutes) {
-		routes.push_back(r->toString());
+		routesString.push_back(r->toString());
 	}
-	return routes;
+	return routesString;
+}
+
+void AccountParams::updateRoutesCString() {
+	if (mRoutesCString) {
+		bctbx_list_free_with_data(mRoutesCString, (bctbx_list_free_func)bctbx_free);
+		mRoutesCString = nullptr;
+	}
+	const auto routeString = getRoutesString();
+	if (!routeString.empty()) {
+		mRoutesCString = L_GET_C_LIST_FROM_CPP_LIST(routeString);
+	}
+}
+
+const bctbx_list_t *AccountParams::getRoutesCString() const {
+	return mRoutesCString;
 }
 
 LinphonePrivacyMask AccountParams::getPrivacy() const {
@@ -844,9 +866,8 @@ void AccountParams::writeToConfigFile(LinphoneConfig *config, int index) {
 		linphone_config_set_string(config, key, "reg_proxy", mProxy.c_str());
 	}
 	if (!mRoutes.empty()) {
-		auto routesString = L_GET_C_LIST_FROM_CPP_LIST(getRoutesString());
+		auto routesString = getRoutesCString();
 		linphone_config_set_string_list(config, key, "reg_route", routesString);
-		bctbx_list_free_with_data(routesString, (bctbx_list_free_func)bctbx_free);
 	} else {
 		linphone_config_clean_entry(config, key, "reg_route");
 	}
diff --git a/src/account/account-params.h b/src/account/account-params.h
index 1a7caf7b38042c3e8605765a09771dc4afc646b7..ef00a096d23a48d016c70fbcd5ae2eec605023a4 100644
--- a/src/account/account-params.h
+++ b/src/account/account-params.h
@@ -119,6 +119,7 @@ public:
 	const std::string getDomain() const;
 	const std::list<std::shared_ptr<Address>> &getRoutes() const;
 	const std::list<std::string> getRoutesString() const;
+	const bctbx_list_t *getRoutesCString() const;
 	LinphonePrivacyMask getPrivacy() const;
 	const std::shared_ptr<Address> &getIdentityAddress() const;
 	LinphoneAVPFMode getAvpfMode() const;
@@ -144,7 +145,9 @@ public:
 	void writeToConfigFile(LinphoneConfig *config, int index);
 
 private:
+	void updateRoutesCString();
 	void setCustomContact(const std::string &contact);
+
 	int mExpires;
 	int mQualityReportingInterval;
 	int mPublishExpires;
@@ -181,6 +184,7 @@ private:
 	std::string mPictureUri;
 
 	std::list<std::shared_ptr<Address>> mRoutes;
+	bctbx_list_t *mRoutesCString = nullptr;
 
 	LinphonePrivacyMask mPrivacy;
 
diff --git a/src/c-wrapper/api/c-call.cpp b/src/c-wrapper/api/c-call.cpp
index 762b55e236970ca4e9237c6b65b47cba14decb3f..a75c8da7da3fafb9a7afa48f8294370aa9a2e80b 100644
--- a/src/c-wrapper/api/c-call.cpp
+++ b/src/c-wrapper/api/c-call.cpp
@@ -299,6 +299,12 @@ const char *linphone_call_get_remote_user_agent(LinphoneCall *call) {
 	return L_STRING_TO_C(Call::toCpp(call)->getRemoteUserAgent());
 }
 
+const LinphoneAddress *linphone_call_get_remote_contact_address(LinphoneCall *call) {
+	CallLogContextualizer logContextualizer(call);
+	const auto &remoteContactAddress = Call::toCpp(call)->getRemoteContactAddress();
+	return remoteContactAddress ? remoteContactAddress->toC() : nullptr;
+}
+
 const char *linphone_call_get_remote_contact(LinphoneCall *call) {
 	CallLogContextualizer logContextualizer(call);
 	return L_STRING_TO_C(Call::toCpp(call)->getRemoteContact());
@@ -695,10 +701,10 @@ LinphoneCall *linphone_call_new_outgoing(LinphoneCore *lc,
                                          const LinphoneAddress *from,
                                          const LinphoneAddress *to,
                                          const LinphoneCallParams *params,
-                                         LinphoneProxyConfig *cfg) {
+                                         LinphoneAccount *account) {
 	LinphoneCall *lcall = Call::createCObject(
 	    L_GET_CPP_PTR_FROM_C_OBJECT(lc), LinphoneCallOutgoing, Address::toCpp(from)->getSharedFromThis(),
-	    Address::toCpp(to)->getSharedFromThis(), cfg ? Account::toCpp(cfg->account)->getSharedFromThis() : nullptr,
+	    Address::toCpp(to)->getSharedFromThis(), account ? Account::toCpp(account)->getSharedFromThis() : nullptr,
 	    nullptr, L_GET_CPP_PTR_FROM_C_OBJECT(params));
 
 	return lcall;
diff --git a/src/call/call.cpp b/src/call/call.cpp
index a2bf2d7c6c81359451e936d4387340183b127cde..bb317ed944da5ba7a4277238c96e04626b87cec8 100644
--- a/src/call/call.cpp
+++ b/src/call/call.cpp
@@ -1142,7 +1142,7 @@ const string &Call::getReferTo() const {
 	return getActiveSession()->getReferTo();
 }
 
-const std::shared_ptr<Address> &Call::getReferToAddress() const {
+const std::shared_ptr<Address> Call::getReferToAddress() const {
 	return getActiveSession()->getReferToAddress();
 }
 
diff --git a/src/call/call.h b/src/call/call.h
index 21627614e3a5140013ace15a1cc1c7f040e5ccae..b371d286de9638f984781b9d6f5a580ba22f4c59 100644
--- a/src/call/call.h
+++ b/src/call/call.h
@@ -170,7 +170,7 @@ public:
 	float getRecordVolume() const;
 	std::shared_ptr<Call> getReferer() const;
 	const std::string &getReferTo() const;
-	const std::shared_ptr<Address> &getReferToAddress() const;
+	const std::shared_ptr<Address> getReferToAddress() const;
 	const std::shared_ptr<Address> getRemoteAddress() const;
 	const std::shared_ptr<Address> getRemoteContactAddress() const;
 	const std::string &getRemoteContact() const;
diff --git a/src/chat/encryption/encryption-engine.h b/src/chat/encryption/encryption-engine.h
index da7d63a3d2693bb9246cb837e43a20ddb4d98cb5..f9e71bb36697b04d69ef52590555997d60dd67ca 100644
--- a/src/chat/encryption/encryption-engine.h
+++ b/src/chat/encryption/encryption-engine.h
@@ -131,7 +131,8 @@ public:
 	getSecurityLevel(BCTBX_UNUSED(const std::list<std::string> &deviceIds)) const {
 		return AbstractChatRoom::SecurityLevel::ClearText;
 	}
-	virtual std::list<EncryptionParameter> getEncryptionParameters() {
+	virtual std::list<EncryptionParameter>
+	getEncryptionParameters(BCTBX_UNUSED(const std::shared_ptr<Account> &account)) {
 		return std::list<EncryptionParameter>();
 	}
 
diff --git a/src/chat/encryption/lime-x3dh-encryption-engine.cpp b/src/chat/encryption/lime-x3dh-encryption-engine.cpp
index 4a6ebb602cab597c1a9e2e0bd5f32699e1a0e189..59dcc1b874c21b9eb66503b421b821dfdc946d10 100644
--- a/src/chat/encryption/lime-x3dh-encryption-engine.cpp
+++ b/src/chat/encryption/lime-x3dh-encryption-engine.cpp
@@ -708,24 +708,21 @@ AbstractChatRoom::SecurityLevel LimeX3dhEncryptionEngine::getSecurityLevel(const
 	return limeStatus2ChatRoomSecLevel(limeManager->get_peerDeviceStatus(deviceId));
 }
 
-list<EncryptionParameter> LimeX3dhEncryptionEngine::getEncryptionParameters() {
-	// Get proxy config
-	LinphoneProxyConfig *proxy = linphone_core_get_default_proxy_config(getCore()->getCCore());
-	if (!proxy) {
-		lWarning() << "[LIME] No proxy config available, unable to setup identity key for ZRTP auxiliary shared secret";
+list<EncryptionParameter> LimeX3dhEncryptionEngine::getEncryptionParameters(const std::shared_ptr<Account> &account) {
+	// Sanity checks on the account
+	if (!account) {
+		lWarning() << "[LIME] No account available, unable to setup identity key for ZRTP auxiliary shared secret";
 		return {};
 	}
 
 	// Get local device Id from local contact address
-	const LinphoneAddress *contactAddress = linphone_proxy_config_get_contact(proxy);
+	const auto &contactAddress = account->getContactAddress();
 	if (!contactAddress) {
 		lWarning()
 		    << "[LIME] No contactAddress available, unable to setup identity key for ZRTP auxiliary shared secret";
 		return {};
 	}
-	std::shared_ptr<Address> identityAddress =
-	    Address::toCpp(const_cast<LinphoneAddress *>(contactAddress))->getSharedFromThis();
-	string localDeviceId = identityAddress->asStringUriOnly();
+	string localDeviceId = contactAddress->asStringUriOnly();
 	vector<uint8_t> Ik;
 
 	try {
diff --git a/src/chat/encryption/lime-x3dh-encryption-engine.h b/src/chat/encryption/lime-x3dh-encryption-engine.h
index e8f858c7bbdbee08a08cbeca796020a0d5de6bc4..6d1e815d59f4f1a0c4828c13a1a63d7a3a4b19ed 100644
--- a/src/chat/encryption/lime-x3dh-encryption-engine.h
+++ b/src/chat/encryption/lime-x3dh-encryption-engine.h
@@ -133,7 +133,7 @@ public:
 	AbstractChatRoom::SecurityLevel getSecurityLevel(const std::string &deviceId) const override;
 	AbstractChatRoom::SecurityLevel getSecurityLevel(const std::list<std::string> &deviceIds) const override;
 	EncryptionEngine::EngineType getEngineType() override;
-	std::list<EncryptionParameter> getEncryptionParameters() override;
+	std::list<EncryptionParameter> getEncryptionParameters(const std::shared_ptr<Account> &account) override;
 	void cleanDb() override;
 
 	// CoreListener overrides
diff --git a/src/conference/session/call-session-p.h b/src/conference/session/call-session-p.h
index 8f87eb8dabb1525eccfb4016b579948bc48bf9b6..ddee8bbdf1ca603561612b3f9d5266897008d95a 100644
--- a/src/conference/session/call-session-p.h
+++ b/src/conference/session/call-session-p.h
@@ -152,6 +152,7 @@ protected:
 	mutable std::string referTo;
 	std::shared_ptr<Address> referToAddress;
 	mutable std::shared_ptr<Address> requestAddress;
+	mutable std::shared_ptr<Address> mRemoteContactAddress;
 	// This counter is used to keep active track of reINVITEs and UPDATEs under processing at any given time.
 	// In fact Linphone can have multiple active transaction at the same time on the same dialog as the transaction
 	// queue is popped after receiving the 100 Trying and not the 200 Ok
diff --git a/src/conference/session/call-session.cpp b/src/conference/session/call-session.cpp
index 70e83e689b5c2d63b2783808878544ab1721629d..7786dfe135856aa479d1675a9779c4f8e1075e23 100644
--- a/src/conference/session/call-session.cpp
+++ b/src/conference/session/call-session.cpp
@@ -1326,14 +1326,32 @@ void CallSession::configure(LinphoneCallDir direction,
 		d->params->initDefault(getCore(), LinphoneCallIncoming);
 	}
 
+	assignAccount(account);
+}
+
+void CallSession::configure(LinphoneCallDir direction, const string &callid) {
+	L_D();
+	d->direction = direction;
+
+	// Keeping a valid address while following https://www.ietf.org/rfc/rfc3323.txt guidelines.
+	const auto anonymous = Address::create("Anonymous <sip:anonymous@anonymous.invalid>");
+	d->log = CallLog::create(getCore(), direction, anonymous, anonymous);
+	d->log->setCallId(callid);
+}
+
+void CallSession::assignAccount(const std::shared_ptr<Account> &account) {
+	L_D();
 	d->setDestAccount(account);
 	if (!d->getDestAccount()) {
 		/* Try to define the destination account if it has not already been done to have a correct contact field in the
 		 * SIP messages */
-		const LinphoneAddress *toAddr = to->toC();
+		const LinphoneAddress *toAddr = d->log->getToAddress()->toC();
+		const LinphoneAddress *fromAddr = d->log->getFromAddress()->toC();
+		const auto &core = getCore()->getCCore();
+		const auto &direction = d->log->getDirection();
 		auto cAccount = (direction == LinphoneCallIncoming)
 		                    ? linphone_core_lookup_account_by_identity_strict(core, toAddr)
-		                    : linphone_core_lookup_account_by_identity(core, toAddr);
+		                    : linphone_core_lookup_account_by_identity(core, fromAddr);
 		if (!cAccount && linphone_core_conference_server_enabled(core)) {
 			// In the case of a server, clients may call the conference factory in order to create a conference
 			cAccount = linphone_core_lookup_account_by_conference_factory_strict(core, toAddr);
@@ -1345,16 +1363,6 @@ void CallSession::configure(LinphoneCallDir direction,
 	}
 }
 
-void CallSession::configure(LinphoneCallDir direction, const string &callid) {
-	L_D();
-	d->direction = direction;
-
-	// Keeping a valid address while following https://www.ietf.org/rfc/rfc3323.txt guidelines.
-	const auto anonymous = Address::create("Anonymous <sip:anonymous@anonymous.invalid>");
-	d->log = CallLog::create(getCore(), direction, anonymous, anonymous);
-	d->log->setCallId(callid);
-}
-
 bool CallSession::isOpConfigured() {
 	L_D();
 	return d->op ? true : false;
@@ -1775,7 +1783,7 @@ const string &CallSession::getReferTo() const {
 	return Utils::getEmptyConstRefObject<string>();
 }
 
-const std::shared_ptr<Address> &CallSession::getReferToAddress() const {
+const std::shared_ptr<Address> CallSession::getReferToAddress() const {
 	L_D();
 	return d->referToAddress;
 }
@@ -1798,15 +1806,18 @@ const std::shared_ptr<Address> CallSession::getRemoteContactAddress() const {
 	L_D();
 	auto op = d->op;
 	if (!op) {
-		return nullptr;
+		d->mRemoteContactAddress = nullptr;
+		return d->mRemoteContactAddress;
 	}
 	auto salRemoteContactAddress = op->getRemoteContactAddress();
 	if (!salRemoteContactAddress) {
-		return nullptr;
+		d->mRemoteContactAddress = nullptr;
+		return d->mRemoteContactAddress;
 	}
 	std::shared_ptr<Address> remoteContactAddress = Address::create();
 	remoteContactAddress->setImpl(salRemoteContactAddress);
-	return remoteContactAddress;
+	d->mRemoteContactAddress = remoteContactAddress;
+	return d->mRemoteContactAddress;
 }
 
 const CallSessionParams *CallSession::getRemoteParams() {
diff --git a/src/conference/session/call-session.h b/src/conference/session/call-session.h
index ebb7a65e536b3c991894a3647aee4c49a0bef9c5..7c4127cf415613888958c1be339b67683802caa5 100644
--- a/src/conference/session/call-session.h
+++ b/src/conference/session/call-session.h
@@ -162,7 +162,7 @@ public:
 	LinphoneReason getReason() const;
 	std::shared_ptr<CallSession> getReferer() const;
 	const std::string &getReferTo() const;
-	const std::shared_ptr<Address> &getReferToAddress() const;
+	const std::shared_ptr<Address> getReferToAddress() const;
 	const std::shared_ptr<Address> getRemoteAddress() const;
 	const std::string &getRemoteContact() const;
 	const std::shared_ptr<Address> getRemoteContactAddress() const;
@@ -195,6 +195,7 @@ protected:
 	CallSession::State getPreviousState() const;
 	CallSession::State getLastStableState() const;
 	void updateContactAddress(Address &contactAddress) const;
+	void assignAccount(const std::shared_ptr<Account> &account);
 
 private:
 	// bool mIsDeclining = false;
diff --git a/src/conference/session/media-session-p.h b/src/conference/session/media-session-p.h
index e59e1b44f3156f8febae62c59f0e5a30c3060494..617b81ce70a98435277113f24966de9de9792ce5 100644
--- a/src/conference/session/media-session-p.h
+++ b/src/conference/session/media-session-p.h
@@ -115,7 +115,7 @@ public:
 	std::vector<SalSrtpCryptoAlgo> generateNewCryptoKeys() const;
 
 	const LinphoneStreamInternalStats *getStreamInternalStats(LinphoneStreamType type) const;
-	LinphoneNatPolicy *getNatPolicy() const {
+	const std::shared_ptr<NatPolicy> getNatPolicy() const {
 		return natPolicy;
 	}
 
@@ -360,7 +360,7 @@ private:
 
 	mutable LinphoneMediaEncryption negotiatedEncryption = LinphoneMediaEncryptionNone;
 
-	LinphoneNatPolicy *natPolicy = nullptr;
+	std::shared_ptr<NatPolicy> natPolicy = nullptr;
 	std::unique_ptr<StunClient> stunClient;
 
 	std::queue<std::function<LinphoneStatus()>> iceDeferedGatheringTasks;
diff --git a/src/conference/session/media-session.cpp b/src/conference/session/media-session.cpp
index 6ef1a566f9146834fd70f7477e6103eaeb19ec5c..efce62b266fede61dde32c9787a90a7801618d9c 100644
--- a/src/conference/session/media-session.cpp
+++ b/src/conference/session/media-session.cpp
@@ -256,7 +256,7 @@ void MediaSessionPrivate::accepted() {
 				 * is complicated and confusing from a signaling standpoint, ICE we will skip the STUN gathering by not
 				 * giving enough time for the gathering step. Only local candidates will be answered in the ACK.
 				 */
-				if (linphone_nat_policy_ice_enabled(natPolicy)) {
+				if (natPolicy && natPolicy->iceEnabled()) {
 					if (getStreamsGroup().prepare()) {
 						lWarning() << "Some gathering is needed for ICE, however since a defered sending of ACK is not "
 						              "supported"
@@ -875,6 +875,9 @@ void MediaSessionPrivate::setCurrentParams(MediaSessionParams *msp) {
 void MediaSessionPrivate::setParams(MediaSessionParams *msp) {
 	if (params) delete params;
 	params = msp;
+	// Pass the account used for the call to the local parameters.
+	// It has been chosen at the start and it should not be changed anymore
+	params->setAccount(getDestAccount());
 }
 
 void MediaSessionPrivate::setRemoteParams(MediaSessionParams *msp) {
@@ -1292,32 +1295,33 @@ int MediaSessionPrivate::portFromStreamIndex(int index) {
  */
 void MediaSessionPrivate::runStunTestsIfNeeded() {
 	L_Q();
-	if (linphone_nat_policy_stun_enabled(natPolicy) &&
-	    !(linphone_nat_policy_ice_enabled(natPolicy) || linphone_nat_policy_turn_enabled(natPolicy))) {
-		stunClient = makeUnique<StunClient>(q->getCore());
+	if (natPolicy && natPolicy->stunEnabled() && !(natPolicy->iceEnabled() || natPolicy->turnEnabled()) && op) {
 		const std::shared_ptr<SalMediaDescription> &md = localIsOfferer ? localDesc : op->getRemoteMediaDescription();
-		const auto audioStreamIndex = md->findIdxBestStream(SalAudio);
-		int audioPort = portFromStreamIndex(audioStreamIndex);
+		if (md) {
+			const auto audioStreamIndex = md->findIdxBestStream(SalAudio);
+			int audioPort = portFromStreamIndex(audioStreamIndex);
 
-		std::shared_ptr<MediaConference::Conference> conference =
-		    listener ? listener->getCallSessionConference(q->getSharedFromThis()) : nullptr;
-		bool isConferenceLayoutActiveSpeaker = false;
-		if (conference) {
-			bool isInLocalConference = getParams()->getPrivate()->getInConference();
-			const auto &confLayout = isInLocalConference ? getRemoteParams()->getConferenceVideoLayout()
-			                                             : getParams()->getConferenceVideoLayout();
-			isConferenceLayoutActiveSpeaker = (confLayout == ConferenceLayout::ActiveSpeaker);
+			std::shared_ptr<MediaConference::Conference> conference =
+			    listener ? listener->getCallSessionConference(q->getSharedFromThis()) : nullptr;
+			bool isConferenceLayoutActiveSpeaker = false;
+			if (conference) {
+				bool isInLocalConference = getParams()->getPrivate()->getInConference();
+				const auto &confLayout = isInLocalConference ? getRemoteParams()->getConferenceVideoLayout()
+				                                             : getParams()->getConferenceVideoLayout();
+				isConferenceLayoutActiveSpeaker = (confLayout == ConferenceLayout::ActiveSpeaker);
+			}
+			const auto mainStreamAttrValue = isConferenceLayoutActiveSpeaker
+			                                     ? MediaSessionPrivate::ActiveSpeakerVideoContentAttribute
+			                                     : MediaSessionPrivate::GridVideoContentAttribute;
+			const auto videoStreamIndex =
+			    conference ? md->findIdxStreamWithContent(mainStreamAttrValue) : md->findIdxBestStream(SalVideo);
+			int videoPort = portFromStreamIndex(videoStreamIndex);
+			const auto textStreamIndex = md->findIdxBestStream(SalText);
+			int textPort = portFromStreamIndex(textStreamIndex);
+			stunClient = makeUnique<StunClient>(q->getCore());
+			int ret = stunClient->run(audioPort, videoPort, textPort);
+			if (ret >= 0) pingTime = ret;
 		}
-		const auto mainStreamAttrValue = isConferenceLayoutActiveSpeaker
-		                                     ? MediaSessionPrivate::ActiveSpeakerVideoContentAttribute
-		                                     : MediaSessionPrivate::GridVideoContentAttribute;
-		const auto videoStreamIndex =
-		    conference ? md->findIdxStreamWithContent(mainStreamAttrValue) : md->findIdxBestStream(SalVideo);
-		int videoPort = portFromStreamIndex(videoStreamIndex);
-		const auto textStreamIndex = md->findIdxBestStream(SalText);
-		int textPort = portFromStreamIndex(textStreamIndex);
-		int ret = stunClient->run(audioPort, videoPort, textPort);
-		if (ret >= 0) pingTime = ret;
 	}
 }
 
@@ -2726,7 +2730,7 @@ void MediaSessionPrivate::setupImEncryptionEngineParameters(std::shared_ptr<SalM
 	auto encryptionEngine = q->getCore()->getEncryptionEngine();
 	if (!encryptionEngine) return;
 
-	list<EncryptionParameter> paramList = encryptionEngine->getEncryptionParameters();
+	list<EncryptionParameter> paramList = encryptionEngine->getEncryptionParameters(getDestAccount());
 
 	// Loop over IM Encryption Engine parameters and append them to the SDP
 	for (const auto &[name, value] : paramList) {
@@ -3885,7 +3889,7 @@ LinphoneStatus MediaSessionPrivate::accept(const MediaSessionParams *msp, BCTBX_
 		updateLocalMediaDescriptionFromIce(op->getRemoteMediaDescription() == nullptr);
 		return startAccept();
 	};
-	if (linphone_nat_policy_ice_enabled(natPolicy) && getStreamsGroup().prepare()) {
+	if (natPolicy && natPolicy->iceEnabled() && getStreamsGroup().prepare()) {
 		queueIceGatheringTask(acceptCompletionTask);
 		return 0; /* Deferred until completion of ICE gathering */
 	}
@@ -3932,7 +3936,7 @@ MediaSessionPrivate::acceptUpdate(const CallSessionParams *csp, CallSession::Sta
 		return 0;
 	};
 
-	if (linphone_nat_policy_ice_enabled(natPolicy) && getStreamsGroup().prepare()) {
+	if (natPolicy && natPolicy->iceEnabled() && getStreamsGroup().prepare()) {
 		lInfo() << "Acceptance of incoming reINVITE is deferred to ICE gathering completion.";
 		queueIceGatheringTask(acceptCompletionTask);
 		return 0; /* Deferred until completion of ICE gathering */
@@ -4043,11 +4047,11 @@ void MediaSessionPrivate::stunAuthRequestedCb(const char *realm,
 		}
 	}
 	if (!stunAccount) return;
-	const char *user = nullptr;
+	const char *user = NULL;
 	const auto &accountParams = stunAccount->getAccountParams();
 	const auto &proxyNatPolicy = accountParams->getNatPolicy();
 	if (proxyNatPolicy) user = L_STRING_TO_C(proxyNatPolicy->getStunServerUsername());
-	else if (natPolicy) user = linphone_nat_policy_get_stun_server_username(natPolicy);
+	else if (natPolicy) user = L_STRING_TO_C(natPolicy->getStunServerUsername());
 	if (!user) {
 		/* If the username has not been found in the nat_policy, take the username from the currently used proxy config
 		 */
@@ -4100,7 +4104,6 @@ MediaSession::~MediaSession() {
 	L_D();
 	cancelDtmfs();
 	d->freeResources();
-	if (d->natPolicy) linphone_nat_policy_unref(d->natPolicy);
 }
 
 // -----------------------------------------------------------------------------
@@ -4192,14 +4195,8 @@ void MediaSession::cancelDtmfs() {
 	d->dtmfSequence.clear();
 }
 
-void MediaSession::setNatPolicy(LinphoneNatPolicy *pol) {
+void MediaSession::setNatPolicy(const std::shared_ptr<NatPolicy> &pol) {
 	L_D();
-	if (pol) {
-		linphone_nat_policy_ref(pol);
-	}
-	if (d->natPolicy) {
-		linphone_nat_policy_unref(d->natPolicy);
-	}
 	d->natPolicy = pol;
 }
 
@@ -4219,32 +4216,10 @@ void MediaSession::configure(LinphoneCallDir direction,
 
 	CallSession::configure(direction, account, op, from, to);
 
-	const auto &accountParams = account ? account->getAccountParams() : nullptr;
-	if (!d->natPolicy) {
-		if (accountParams) {
-			const auto accountNatPolicy = accountParams->getNatPolicy();
-			if (accountNatPolicy) {
-				d->natPolicy = accountNatPolicy->toC();
-			}
-		}
-		if (!d->natPolicy) d->natPolicy = linphone_core_get_nat_policy(getCore()->getCCore());
-		linphone_nat_policy_ref(d->natPolicy);
-	}
-
 	if (direction == LinphoneCallOutgoing) {
 		d->selectOutgoingIpVersion();
 		isOfferer = makeLocalDescription = !getCore()->getCCore()->sip_conf.sdp_200_ack;
 		remote = to->clone()->toSharedPtr();
-		/* The enablement of rtp bundle is controlled at first by the Account, then the Core.
-		 * Then the value is stored and later updated into MediaSessionParams. */
-		bool rtpBundleEnabled = false;
-		if (accountParams) {
-			rtpBundleEnabled = accountParams->rtpBundleEnabled();
-		} else {
-			lInfo() << "No account set for this call, using rtp bundle enablement from LinphoneCore.";
-			rtpBundleEnabled = linphone_core_rtp_bundle_enabled(getCore()->getCCore());
-		}
-		d->getParams()->enableRtpBundle(rtpBundleEnabled);
 	} else if (direction == LinphoneCallIncoming) {
 		d->selectIncomingIpVersion();
 		/* Note that the choice of IP version for streams is later refined by setCompatibleIncomingCallParams() when
@@ -4260,15 +4235,45 @@ void MediaSession::configure(LinphoneCallDir direction,
 		 * in fixCallParams() */
 	}
 
+	assignAccount(account);
+	// At this point, the account is set if found
+	const auto &selectedAccount = d->params->getAccount();
+	const auto &accountParams = selectedAccount ? selectedAccount->getAccountParams() : nullptr;
+
+	if (direction == LinphoneCallOutgoing) {
+		/* The enablement of rtp bundle is controlled at first by the Account, then the Core.
+		 * Then the value is stored and later updated into MediaSessionParams. */
+		bool rtpBundleEnabled = false;
+		if (accountParams) {
+			rtpBundleEnabled = accountParams->rtpBundleEnabled();
+		} else {
+			lInfo() << "No account set for this call, using rtp bundle enablement from LinphoneCore.";
+			rtpBundleEnabled = linphone_core_rtp_bundle_enabled(getCore()->getCCore());
+		}
+		d->getParams()->enableRtpBundle(rtpBundleEnabled);
+	}
+
 	lInfo() << "Rtp bundle is " << (d->getParams()->rtpBundleEnabled() ? "enabled." : "disabled.");
 
-	if (makeLocalDescription) {
-		/* Do not make a local media description when sending an empty INVITE. */
-		d->makeLocalMediaDescription(isOfferer, isCapabilityNegotiationEnabled(), false);
+	if (!d->natPolicy) {
+		if (accountParams) {
+			const auto accountNatPolicy = accountParams->getNatPolicy();
+			if (accountNatPolicy) {
+				d->natPolicy = accountNatPolicy;
+			}
+		}
+		if (!d->natPolicy) {
+			d->natPolicy = NatPolicy::toCpp(linphone_core_get_nat_policy(getCore()->getCCore()))->getSharedFromThis();
+		}
 	}
 
 	if (d->natPolicy) d->runStunTestsIfNeeded();
 	d->discoverMtu(remote);
+
+	if (makeLocalDescription) {
+		/* Do not make a local media description when sending an empty INVITE. */
+		d->makeLocalMediaDescription(isOfferer, isCapabilityNegotiationEnabled(), false);
+	}
 }
 
 LinphoneStatus MediaSession::deferUpdate() {
@@ -4290,27 +4295,25 @@ void MediaSession::initiateIncoming() {
 	L_D();
 	CallSession::initiateIncoming();
 
-	if (d->natPolicy) {
-		if (linphone_nat_policy_ice_enabled(d->natPolicy)) {
-			d->deferIncomingNotification = d->getStreamsGroup().prepare();
-			/*
-			 * If ICE gathering is done, we can update the local media description immediately.
-			 * Otherwise, we'll get the ORTP_EVENT_ICE_GATHERING_FINISHED event later.
-			 */
-			if (d->deferIncomingNotification) {
-				auto incomingNotificationTask = [d]() {
-					/* There is risk that the call can be terminated before this task is executed, for example if
-					 * offer/answer fails.*/
-					if (d->state != State::Idle && d->state != State::PushIncomingReceived) return 0;
-					d->deferIncomingNotification = false;
-					d->updateLocalMediaDescriptionFromIce(d->localIsOfferer);
-					d->startIncomingNotification();
-					return 0;
-				};
-				d->queueIceGatheringTask(incomingNotificationTask);
-			} else {
+	if (d->natPolicy && d->natPolicy->iceEnabled()) {
+		d->deferIncomingNotification = d->getStreamsGroup().prepare();
+		/*
+		 * If ICE gathering is done, we can update the local media description immediately.
+		 * Otherwise, we'll get the ORTP_EVENT_ICE_GATHERING_FINISHED event later.
+		 */
+		if (d->deferIncomingNotification) {
+			auto incomingNotificationTask = [d]() {
+				/* There is risk that the call can be terminated before this task is executed, for example if
+				 * offer/answer fails.*/
+				if (d->state != State::Idle && d->state != State::PushIncomingReceived) return 0;
+				d->deferIncomingNotification = false;
 				d->updateLocalMediaDescriptionFromIce(d->localIsOfferer);
-			}
+				d->startIncomingNotification();
+				return 0;
+			};
+			d->queueIceGatheringTask(incomingNotificationTask);
+		} else {
+			d->updateLocalMediaDescriptionFromIce(d->localIsOfferer);
 		}
 	}
 }
@@ -4319,7 +4322,7 @@ bool MediaSession::initiateOutgoing(const string &subject, const Content *conten
 	L_D();
 	bool defer = CallSession::initiateOutgoing(subject, content);
 
-	if (linphone_nat_policy_ice_enabled(d->natPolicy)) {
+	if (d->natPolicy && d->natPolicy->iceEnabled()) {
 		if (getCore()->getCCore()->sip_conf.sdp_200_ack)
 			lWarning() << "ICE is not supported when sending INVITE without SDP";
 		else {
@@ -4461,7 +4464,7 @@ LinphoneStatus MediaSession::resume() {
 		};
 
 		const auto preparingStreams = d->getStreamsGroup().prepare();
-		if (linphone_nat_policy_ice_enabled(d->natPolicy) && preparingStreams) {
+		if (d->natPolicy && d->natPolicy->iceEnabled() && preparingStreams) {
 			lInfo() << "Defer CallSession " << this << " (local address " << getLocalAddress()->toString()
 			        << " remote address " << getRemoteAddress()->toString() << ") resume to gather ICE candidates";
 			d->queueIceGatheringTask(updateCompletionTask);
@@ -4840,7 +4843,7 @@ LinphoneStatus MediaSession::update(const MediaSessionParams *msp,
 
 		const auto preparingStreams = d->getStreamsGroup().prepare();
 		// reINVITE sent after full state must be sent after ICE negotiations are completed if ICE is enabled
-		if (linphone_nat_policy_ice_enabled(d->natPolicy) && preparingStreams) {
+		if (d->natPolicy && d->natPolicy->iceEnabled() && preparingStreams) {
 			lInfo() << "Defer CallSession " << this << " (local address " << *getLocalAddress() << " remote address "
 			        << *getRemoteAddress() << ") update to gather ICE candidates";
 			d->queueIceGatheringTask(updateCompletionTask);
diff --git a/src/conference/session/media-session.h b/src/conference/session/media-session.h
index 0fa7cc6d6971fddc33d4a7d0e1f3838bc3b649d8..f617530052d74b95836ffc688201bb5badb68ff7 100644
--- a/src/conference/session/media-session.h
+++ b/src/conference/session/media-session.h
@@ -35,6 +35,7 @@ class IceAgent;
 class MediaSessionPrivate;
 class Participant;
 class StreamsGroup;
+class NatPolicy;
 
 namespace MediaConference {
 class Conference;
@@ -66,7 +67,7 @@ public:
 	LinphoneStatus acceptEarlyMedia(const MediaSessionParams *msp = nullptr);
 	LinphoneStatus acceptUpdate(const MediaSessionParams *msp);
 	void cancelDtmfs();
-	void setNatPolicy(LinphoneNatPolicy *pol);
+	void setNatPolicy(const std::shared_ptr<NatPolicy> &pol);
 	void setSubject(const std::string &subject);
 	bool toneIndicationsEnabled() const;
 	void configure(LinphoneCallDir direction,
diff --git a/src/core/core-call.cpp b/src/core/core-call.cpp
index f86f013bd604d11e6cacd966fd0ac7ab1583ba50..fe16054507059d46cab67c7ea9892d6a813ec2db 100644
--- a/src/core/core-call.cpp
+++ b/src/core/core-call.cpp
@@ -246,7 +246,7 @@ bool Core::areSoundResourcesLocked() const {
 	return false;
 }
 
-shared_ptr<Call> Core::getCallByRemoteAddress(const std::shared_ptr<Address> &addr) const {
+shared_ptr<Call> Core::getCallByRemoteAddress(const std::shared_ptr<const Address> &addr) const {
 	L_D();
 	for (const auto &call : d->calls) {
 		if (call->getRemoteAddress()->weakEqual(*addr)) return call;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index c16f4b191b0aaf2572acfaa0ee0f0a3d936526c1..c9c2eef7f76cc88a05c8034841b2c5f38ca84395 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -1787,23 +1787,19 @@ shared_ptr<CallSession> Core::createOrUpdateConferenceOnServer(const std::shared
 	std::shared_ptr<Address> meCleanedAddress = Address::create(localAddr->getUriWithoutGruu());
 	session->configure(LinphoneCallOutgoing, nullptr, nullptr, meCleanedAddress, conferenceFactoryUri);
 	const auto destAccount = session->getPrivate()->getDestAccount();
-	const LinphoneNatPolicy *natPolicy = nullptr;
+	std::shared_ptr<NatPolicy> natPolicy = nullptr;
 	if (destAccount) {
 		const auto accountParams = destAccount->getAccountParams();
-		const auto &cppNatPolicy = accountParams->getNatPolicy();
-		if (cppNatPolicy) {
-			natPolicy = cppNatPolicy->toC();
-		}
+		natPolicy = accountParams->getNatPolicy();
 	}
 	if (!natPolicy) {
-		natPolicy = linphone_core_get_nat_policy(getCCore());
+		natPolicy = NatPolicy::toCpp(linphone_core_get_nat_policy(getCCore()))->getSharedFromThis();
 	}
 	if (natPolicy) {
-		LinphoneNatPolicy *newNatPolicy = linphone_nat_policy_clone(natPolicy);
+		auto newNatPolicy = natPolicy->clone()->toSharedPtr();
 		// remove stun server asynchronous gathering, we don't actually need it and it looses some time.
-		linphone_nat_policy_enable_stun(newNatPolicy, false);
+		newNatPolicy->enableStun(false);
 		session->setNatPolicy(newNatPolicy);
-		linphone_nat_policy_unref(newNatPolicy);
 	}
 	session->initiateOutgoing();
 	session->startInvite(nullptr, confParams->getSubject(), nullptr);
diff --git a/src/core/core.h b/src/core/core.h
index de10048af1d19994b5d38cd939d8ab1e66da9ec6..c9443ad58894138e70e8ac9754e0380cf7bafe47 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -153,7 +153,7 @@ public:
 	// ---------------------------------------------------------------------------
 
 	bool areSoundResourcesLocked() const;
-	std::shared_ptr<Call> getCallByRemoteAddress(const std::shared_ptr<Address> &addr) const;
+	std::shared_ptr<Call> getCallByRemoteAddress(const std::shared_ptr<const Address> &addr) const;
 	std::shared_ptr<Call> getCallByCallId(const std::string &callId) const;
 	const std::list<std::shared_ptr<Call>> &getCalls() const;
 	unsigned int getCallCount() const;
diff --git a/src/nat/ice-service.cpp b/src/nat/ice-service.cpp
index d3df6ebb03aaa89dec417478bc39af870b3ddbce..15fe6c780058dec9408b886aec7e7fd11be8aba0 100644
--- a/src/nat/ice-service.cpp
+++ b/src/nat/ice-service.cpp
@@ -76,8 +76,8 @@ bool IceService::iceFoundInMediaDescription(const std::shared_ptr<SalMediaDescri
 }
 
 void IceService::checkSession(IceRole role, bool preferIpv6DefaultCandidates) {
-	LinphoneNatPolicy *natPolicy = getMediaSessionPrivate().getNatPolicy();
-	if (!natPolicy || !linphone_nat_policy_ice_enabled(natPolicy)) {
+	const auto natPolicy = getMediaSessionPrivate().getNatPolicy();
+	if (!natPolicy || !natPolicy->iceEnabled()) {
 		return;
 	}
 
@@ -291,7 +291,7 @@ int IceService::gatherLocalCandidates() {
 	return 0;
 }
 
-void IceService::addPredefinedSflrxCandidates(const NatPolicy *natPolicy) {
+void IceService::addPredefinedSflrxCandidates(const std::shared_ptr<NatPolicy> &natPolicy) {
 	if (!natPolicy) return;
 	bool ipv6Allowed = linphone_core_ipv6_enabled(getCCore());
 	const string &ipv4 = natPolicy->getNatV4Address();
@@ -337,8 +337,7 @@ int IceService::gatherIceCandidates() {
 	const struct addrinfo *ai = nullptr;
 	int err = 0;
 
-	LinphoneNatPolicy *cNatPolicy = getMediaSessionPrivate().getNatPolicy();
-	NatPolicy *natPolicy = cNatPolicy ? NatPolicy::toCpp(cNatPolicy) : nullptr;
+	const auto &natPolicy = getMediaSessionPrivate().getNatPolicy();
 	if (natPolicy && natPolicy->stunServerActivated()) {
 		ai = natPolicy->getStunServerAddrinfo();
 		if (ai) ai = getIcePreferredStunServerAddrinfo(ai);
@@ -847,8 +846,7 @@ void IceService::handleIceEvent(const OrtpEvent *ev) {
 			break;
 		case ORTP_EVENT_ICE_GATHERING_FINISHED:
 			if (!evd->info.ice_processing_successful)
-				lWarning() << "No STUN answer from ["
-				           << linphone_nat_policy_get_stun_server(getMediaSessionPrivate().getNatPolicy())
+				lWarning() << "No STUN answer from [" << getMediaSessionPrivate().getNatPolicy()->getStunServer()
 				           << "], continuing without STUN";
 			mStreamsGroup.finishPrepare();
 			if (mListener) mListener->onGatheringFinished(*this);
diff --git a/src/nat/ice-service.h b/src/nat/ice-service.h
index 1f50d8ffa662835151b8e73a052d376dcb2c8f02..4b5d77164a9e19932488f95a6b8590f9d49e0179 100644
--- a/src/nat/ice-service.h
+++ b/src/nat/ice-service.h
@@ -147,7 +147,7 @@ private:
 	void checkSession(IceRole role, bool preferIpv6DefaultCandidates);
 	int gatherIceCandidates();
 	int gatherLocalCandidates();
-	void addPredefinedSflrxCandidates(const NatPolicy *natPolicy);
+	void addPredefinedSflrxCandidates(const std::shared_ptr<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
index 56a42cb7de2ee96aed7ffa14c068d3f6442a66de..cd169a944e85b60e3dc173bb1cfd6498698e24a2 100644
--- a/src/nat/nat-policy.cpp
+++ b/src/nat/nat-policy.cpp
@@ -81,6 +81,10 @@ NatPolicy::~NatPolicy() {
 	clearResolverContexts();
 }
 
+NatPolicy *NatPolicy::clone() const {
+	return new NatPolicy(*this);
+}
+
 void NatPolicy::clearResolverContexts() {
 	if (mStunResolverContext) {
 		belle_sip_resolver_context_cancel(mStunResolverContext);
diff --git a/src/nat/nat-policy.h b/src/nat/nat-policy.h
index b5ea4d076ca63b33a9ef2a208f6bb27439ecc13e..0a39979739a70980fe51c92519c65a1964c4b982 100644
--- a/src/nat/nat-policy.h
+++ b/src/nat/nat-policy.h
@@ -34,6 +34,8 @@ public:
 	NatPolicy(const NatPolicy &other);
 	~NatPolicy();
 
+	virtual NatPolicy *clone() const override;
+
 	void setStunServer(const std::string &stunServer);
 	const std::string &getStunServer() const;
 
diff --git a/tester/CMakeLists.txt b/tester/CMakeLists.txt
index 77cf911b3977a343e9e254f4ff76d01e493887a1..0d1d56b01678b8c9a334b79b5955f5e23889bfc4 100644
--- a/tester/CMakeLists.txt
+++ b/tester/CMakeLists.txt
@@ -363,10 +363,6 @@ if(APPLE)
 	endif()
 endif()
 
-set(GROUP_CHAT_BENCHMARK_SOURCE_CXX
-	shared_tester_functions.cpp
-)
-
 set(GROUP_CHAT_BENCHMARK_SOURCE_C
 	accountmanager.c
 	tester.c
@@ -376,6 +372,7 @@ set(GROUP_CHAT_BENCHMARK_SOURCE_C
 
 set(GROUP_CHAT_BENCHMARK_SOURCE_CXX
 	shared_tester_functions.cpp
+	tester.cpp
 )
 
 set(GROUP_CHAT_BENCHMARK_HEADERS
diff --git a/tester/audio-quality-tester.cpp b/tester/audio-quality-tester.cpp
index ad34701925448e47460dcc775d5724ca2bdc7206..ae7e74473e505ce7757b831f30aed7069eca66af 100644
--- a/tester/audio-quality-tester.cpp
+++ b/tester/audio-quality-tester.cpp
@@ -261,8 +261,8 @@ static void audio_call_loss_resilience(const char *codec_name,
 				linphone_payload_type_set_recv_fmtp(paulinePt, paulineFmtp.toString().c_str());
 				for (int loopIndex = 0; loopIndex < 2; ++loopIndex) {
 					recordFileName = useinbandfec[inbandIndex] + "_" + std::to_string(lossRates[lossRateIndex]) + "_" +
-					                 std::to_string(lossRates[loopIndex]) + "_" +
-					                 packetLossPercentage[packetLossIndex] + "_out_" + recordFileNameRoot;
+					                 std::to_string(loopIndex) + "_" + packetLossPercentage[packetLossIndex] + "_out_" +
+					                 recordFileNameRoot;
 					bc_free(recordPath);
 					recordPath = bc_tester_file(recordFileName.c_str());
 					linphone_core_set_record_file(pauline->lc, recordPath);
diff --git a/tester/call_flexfec_tester.cpp b/tester/call_flexfec_tester.cpp
index 6978f64cbb7afe7acd5b942c40e3830a05643695..9cca6344330de94ced9bcec9c3c087dd2792a2ea 100644
--- a/tester/call_flexfec_tester.cpp
+++ b/tester/call_flexfec_tester.cpp
@@ -96,8 +96,8 @@ static void video_call_with_flexfec_base(flexfec_tests_params params) {
 	linphone_core_set_network_simulator_params(pauline->lc, &network_params);
 
 	if (params.ice) {
-		enable_stun_in_core(marie, TRUE, TRUE);
-		enable_stun_in_core(pauline, TRUE, TRUE);
+		enable_stun_in_mgr(marie, TRUE, TRUE, TRUE, TRUE);
+		enable_stun_in_mgr(pauline, TRUE, TRUE, TRUE, TRUE);
 	}
 	enable_rtp_bundle(marie->lc, TRUE);
 	enable_rtp_bundle(pauline->lc, TRUE);
diff --git a/tester/call_ice_tester.cpp b/tester/call_ice_tester.cpp
index b305eb4495da675aedbbddb7e66bb4669e6c7b76..3e7e08cdfc734bcef1ccdaade0cfe980942f7969 100644
--- a/tester/call_ice_tester.cpp
+++ b/tester/call_ice_tester.cpp
@@ -103,12 +103,12 @@ static void _early_media_call_with_ice(bool_t callee_has_ice) {
 	lcs = bctbx_list_append(lcs, marie->lc);
 	lcs = bctbx_list_append(lcs, pauline->lc);
 
-	enable_stun_in_core(pauline, TRUE, TRUE);
-	linphone_core_manager_wait_for_stun_resolution(pauline);
+	enable_stun_in_mgr(pauline, TRUE, TRUE, TRUE, TRUE);
 
 	if (callee_has_ice) {
-		enable_stun_in_core(marie, TRUE, TRUE);
-		linphone_core_manager_wait_for_stun_resolution(marie);
+		// TODO: allow disabling ice or STUN at the core level and enabling in the account
+		// enable_stun_in_mgr(marie, TRUE, TRUE, TRUE, FALSE);
+		enable_stun_in_mgr(marie, TRUE, TRUE, TRUE, TRUE);
 	}
 
 	pauline_call = linphone_core_invite_address(pauline->lc, marie->identity);
@@ -178,10 +178,8 @@ static void audio_call_with_ice_no_matching_audio_codecs(void) {
 	linphone_core_enable_payload_type(marie->lc, linphone_core_find_payload_type(marie->lc, "PCMA", 8000, 1),
 	                                  TRUE); /* Enable PCMA */
 
-	enable_stun_in_core(marie, TRUE, TRUE);
-	enable_stun_in_core(pauline, TRUE, TRUE);
-	linphone_core_manager_wait_for_stun_resolution(marie);
-	linphone_core_manager_wait_for_stun_resolution(pauline);
+	enable_stun_in_mgr(marie, TRUE, TRUE, TRUE, FALSE);
+	enable_stun_in_mgr(pauline, TRUE, TRUE, FALSE, FALSE);
 
 	out_call = linphone_core_invite_address(marie->lc, pauline->identity);
 	linphone_call_ref(out_call);
@@ -331,11 +329,12 @@ static void _call_with_ice_with_default_candidate(bool_t dont_default_to_stun_ca
 	                        dont_default_to_stun_candidates);
 	linphone_config_set_int(linphone_core_get_config(marie->lc), "rtp", "prefer_ipv6", (int)with_ipv6_prefered);
 
-	enable_stun_in_core(marie, TRUE, TRUE);
-	linphone_core_manager_wait_for_stun_resolution(marie);
+	enable_stun_in_mgr(marie, TRUE, TRUE, TRUE, TRUE);
+	enable_stun_in_mgr(pauline, TRUE, TRUE, TRUE, TRUE);
 
-	enable_stun_in_core(pauline, TRUE, TRUE);
-	linphone_core_manager_wait_for_stun_resolution(pauline);
+	// TODO: allow disabling ice or STUN at the core level and enabling in the account
+	// enable_stun_in_mgr(marie, TRUE, TRUE, TRUE, FALSE);
+	// enable_stun_in_mgr(pauline, TRUE, TRUE, TRUE, FALSE);
 
 	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));
@@ -422,21 +421,46 @@ static void call_with_ice_stun_not_responding(void) {
 	LinphoneCoreManager *pauline =
 	    linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
 
+	bctbx_list_t *mgrList = NULL;
+	mgrList = bctbx_list_append(mgrList, marie);
+	mgrList = bctbx_list_append(mgrList, pauline);
+
+	for (const bctbx_list_t *mgr_it = mgrList; mgr_it != NULL; mgr_it = mgr_it->next) {
+		LinphoneCoreManager *mgr = (LinphoneCoreManager *)(bctbx_list_get_data(mgr_it));
+		linphone_core_set_stun_server(mgr->lc, NULL);
+		const bctbx_list_t *accounts = linphone_core_get_account_list(mgr->lc);
+		for (const bctbx_list_t *account_it = accounts; account_it != NULL; account_it = account_it->next) {
+			LinphoneAccount *account = (LinphoneAccount *)(bctbx_list_get_data(account_it));
+			const LinphoneAccountParams *account_params = linphone_account_get_params(account);
+			LinphoneAccountParams *new_account_params = linphone_account_params_clone(account_params);
+			linphone_account_params_set_nat_policy(new_account_params, NULL); // Force to use core policy
+			linphone_account_set_params(account, new_account_params);
+			linphone_account_params_unref(new_account_params);
+		}
+	}
+
+	const char *stun_server = "belledonne-communications.com:443";
+
 	/*set dummy stun servers*/
-	linphone_core_set_stun_server(marie->lc, "belledonne-communications.com:443");
-	linphone_core_set_stun_server(pauline->lc, "belledonne-communications.com:443");
+	linphone_core_set_stun_server(marie->lc, stun_server);
+	linphone_core_set_stun_server(pauline->lc, stun_server);
+
 	/*we expect ICE to continue without stun candidates*/
 	_call_with_ice_base(marie, pauline, TRUE, TRUE, TRUE, FALSE, FALSE);
 
 	/*retry but with nat policy instead of core */
-	linphone_core_set_stun_server(marie->lc, NULL);
-	linphone_core_set_stun_server(pauline->lc, NULL);
-	linphone_nat_policy_set_stun_server(linphone_core_get_nat_policy(marie->lc), "belledonne-communications.com:443");
-	linphone_nat_policy_set_stun_server(linphone_core_get_nat_policy(pauline->lc), "belledonne-communications.com:443");
+	for (const bctbx_list_t *mgr_it = mgrList; mgr_it != NULL; mgr_it = mgr_it->next) {
+		LinphoneCoreManager *mgr = (LinphoneCoreManager *)(bctbx_list_get_data(mgr_it));
+		linphone_core_set_stun_server(mgr->lc, NULL);
+		linphone_nat_policy_set_stun_server(linphone_core_get_nat_policy(mgr->lc), "belledonne-communications.com:443");
+	}
+
 	/*we expect ICE to continue without stun candidates*/
 	_call_with_ice_base(marie, pauline, TRUE, TRUE, TRUE, FALSE, FALSE);
 	_call_with_ice_base(marie, pauline, TRUE, TRUE, TRUE, FALSE, TRUE);
 
+	bctbx_list_free(mgrList);
+
 	linphone_core_manager_destroy(marie);
 	linphone_core_manager_destroy(pauline);
 }
@@ -535,11 +559,8 @@ static void call_with_ice_no_sdp(void) {
 
 	linphone_core_enable_sdp_200_ack(pauline->lc, TRUE);
 
-	enable_stun_in_core(marie, TRUE, TRUE);
-	linphone_core_manager_wait_for_stun_resolution(marie);
-
-	enable_stun_in_core(pauline, TRUE, TRUE);
-	linphone_core_manager_wait_for_stun_resolution(pauline);
+	enable_stun_in_mgr(marie, TRUE, TRUE, TRUE, FALSE);
+	enable_stun_in_mgr(pauline, TRUE, TRUE, TRUE, FALSE);
 
 	BC_ASSERT_TRUE(call(pauline, marie));
 
@@ -583,11 +604,8 @@ static void ice_added_by_reinvite(void) {
 	liblinphone_tester_check_rtcp(marie, pauline);
 
 	/*enable ICE on both ends*/
-	enable_stun_in_core(marie, TRUE, TRUE);
-	linphone_core_manager_wait_for_stun_resolution(marie);
-
-	enable_stun_in_core(pauline, TRUE, TRUE);
-	linphone_core_manager_wait_for_stun_resolution(pauline);
+	enable_stun_in_mgr(marie, TRUE, TRUE, TRUE, TRUE);
+	enable_stun_in_mgr(pauline, TRUE, TRUE, TRUE, TRUE);
 
 	c = linphone_core_get_current_call(marie->lc);
 	params = linphone_core_create_call_params(marie->lc, c);
@@ -762,11 +780,8 @@ static void call_terminated_during_ice_reinvite(void) {
 	    linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
 	LinphoneCall *pauline_call, *marie_call;
 
-	enable_stun_in_core(marie, TRUE, TRUE);
-	linphone_core_manager_wait_for_stun_resolution(marie);
-
-	enable_stun_in_core(pauline, TRUE, TRUE);
-	linphone_core_manager_wait_for_stun_resolution(pauline);
+	enable_stun_in_mgr(marie, TRUE, TRUE, TRUE, TRUE);
+	enable_stun_in_mgr(pauline, TRUE, TRUE, TRUE, TRUE);
 
 	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));
@@ -805,19 +820,27 @@ static void call_with_ice_and_dual_stack_stun_server(void) {
 	    linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
 	bctbx_list_t *local_addresses = linphone_fetch_local_addresses();
 	LinphoneCall *pauline_call, *marie_call;
-	LinphoneNatPolicy *pol;
 
-	pol = linphone_core_get_nat_policy(marie->lc);
+	LinphoneNatPolicy *pol = linphone_core_get_nat_policy(marie->lc);
 	pol = linphone_nat_policy_clone(pol);
 	linphone_nat_policy_set_stun_server(pol, "sip.example.org"); /* this host has ipv4 and ipv6 address.*/
 	linphone_nat_policy_enable_stun(pol, TRUE);
 	linphone_nat_policy_enable_ice(pol, TRUE);
 	linphone_core_set_nat_policy(marie->lc, pol);
 	linphone_nat_policy_unref(pol);
+
+	const bctbx_list_t *accounts = linphone_core_get_account_list(marie->lc);
+	for (const bctbx_list_t *account_it = accounts; account_it != NULL; account_it = account_it->next) {
+		LinphoneAccount *account = (LinphoneAccount *)(bctbx_list_get_data(account_it));
+		const LinphoneAccountParams *account_params = linphone_account_get_params(account);
+		LinphoneAccountParams *new_account_params = linphone_account_params_clone(account_params);
+		linphone_account_params_set_nat_policy(new_account_params, NULL);
+		linphone_account_set_params(account, new_account_params);
+		linphone_account_params_unref(new_account_params);
+	}
 	linphone_core_manager_wait_for_stun_resolution(marie);
 
-	enable_stun_in_core(pauline, TRUE, TRUE);
-	linphone_core_manager_wait_for_stun_resolution(pauline);
+	enable_stun_in_mgr(pauline, TRUE, TRUE, TRUE, TRUE);
 
 	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));
@@ -879,8 +902,28 @@ static void srtp_ice_call_to_no_encryption(void) {
 	linphone_core_enable_video_capture(marie->lc, TRUE);
 	linphone_core_enable_video_capture(pauline->lc, TRUE);
 
+	// Marie and Pauline use the Nat Policy stored in the core
+	const bctbx_list_t *accounts = linphone_core_get_account_list(marie->lc);
+	for (const bctbx_list_t *account_it = accounts; account_it != NULL; account_it = account_it->next) {
+		LinphoneAccount *account = (LinphoneAccount *)(bctbx_list_get_data(account_it));
+		const LinphoneAccountParams *account_params = linphone_account_get_params(account);
+		LinphoneAccountParams *new_account_params = linphone_account_params_clone(account_params);
+		linphone_account_params_set_nat_policy(new_account_params, NULL);
+		linphone_account_set_params(account, new_account_params);
+		linphone_account_params_unref(new_account_params);
+	}
 	enable_stun_in_core(marie, TRUE, TRUE);
 	linphone_core_manager_wait_for_stun_resolution(marie);
+
+	accounts = linphone_core_get_account_list(pauline->lc);
+	for (const bctbx_list_t *account_it = accounts; account_it != NULL; account_it = account_it->next) {
+		LinphoneAccount *account = (LinphoneAccount *)(bctbx_list_get_data(account_it));
+		const LinphoneAccountParams *account_params = linphone_account_get_params(account);
+		LinphoneAccountParams *new_account_params = linphone_account_params_clone(account_params);
+		linphone_account_params_set_nat_policy(new_account_params, NULL);
+		linphone_account_set_params(account, new_account_params);
+		linphone_account_params_unref(new_account_params);
+	}
 	enable_stun_in_core(pauline, TRUE, TRUE);
 	linphone_core_manager_wait_for_stun_resolution(pauline);
 
diff --git a/tester/call_multi_tester.c b/tester/call_multi_tester.c
index d38c862d3261e1fe61e447d783600d18da64ae69..fc26816b9df6070e9d7498301896bb181ba59772 100644
--- a/tester/call_multi_tester.c
+++ b/tester/call_multi_tester.c
@@ -318,7 +318,9 @@ static void _simple_call_transfer(bool_t transferee_is_default_account) {
 
 	BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneTransferCallConnected, 1, 2000));
 
-	BC_ASSERT_STRING_EQUAL(linphone_call_get_remote_address_as_string(laure_call), marie_identity);
+	char *remote_address_str = linphone_call_get_remote_address_as_string(laure_call);
+	BC_ASSERT_STRING_EQUAL(remote_address_str, marie_identity);
+	ms_free(remote_address_str);
 
 	// terminate marie to pauline call
 	BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, 1, 2000));
@@ -702,14 +704,13 @@ void stop_ringing_when_accepting_call_while_holding_another(bool_t activate_ice)
 	core_list = bctbx_list_append(core_list, laure->lc);
 
 	// Enable ICE
-	enable_stun_in_core(marie, TRUE, activate_ice);
-	enable_stun_in_core(pauline, TRUE, activate_ice);
-	enable_stun_in_core(laure, TRUE, activate_ice);
-	if (activate_ice) {
-		linphone_core_manager_wait_for_stun_resolution(marie);
-		linphone_core_manager_wait_for_stun_resolution(pauline);
-		linphone_core_manager_wait_for_stun_resolution(laure);
-	}
+	// TODO: allow disabling ice or STUN at the core level and enabling in the account
+	// enable_stun_in_mgr(marie, TRUE, activate_ice, TRUE, TRUE);
+	// enable_stun_in_mgr(pauline, TRUE, activate_ice, TRUE, TRUE);
+	// enable_stun_in_mgr(laure, TRUE, activate_ice, TRUE, TRUE);
+	enable_stun_in_mgr(marie, TRUE, activate_ice, TRUE, activate_ice);
+	enable_stun_in_mgr(pauline, TRUE, activate_ice, TRUE, activate_ice);
+	enable_stun_in_mgr(laure, TRUE, activate_ice, TRUE, activate_ice);
 
 	// Marie calls Pauline
 	BC_ASSERT_PTR_NOT_NULL(linphone_core_invite_address_with_params(marie->lc, pauline->identity, marie_params));
diff --git a/tester/call_secure_tester.cpp b/tester/call_secure_tester.cpp
index 3fa61fee427c087a9a747dc4f3367d2ee12faf04..d48861b440e234c995458a9ec49608bd4a73bb4b 100644
--- a/tester/call_secure_tester.cpp
+++ b/tester/call_secure_tester.cpp
@@ -1659,15 +1659,8 @@ static void _dtls_srtp_audio_call_with_rtcp_mux(bool_t rtcp_mux_not_accepted) {
 	setup_dtls_srtp(marie, pauline);
 	{
 		/*enable ICE on both ends*/
-		LinphoneNatPolicy *pol;
-		pol = linphone_core_get_nat_policy(marie->lc);
-		linphone_nat_policy_enable_ice(pol, TRUE);
-		linphone_nat_policy_enable_stun(pol, TRUE);
-		linphone_core_set_nat_policy(marie->lc, pol);
-		pol = linphone_core_get_nat_policy(pauline->lc);
-		linphone_nat_policy_enable_ice(pol, TRUE);
-		linphone_nat_policy_enable_stun(pol, TRUE);
-		linphone_core_set_nat_policy(pauline->lc, pol);
+		enable_stun_in_mgr(marie, TRUE, TRUE, TRUE, TRUE);
+		enable_stun_in_mgr(pauline, TRUE, TRUE, TRUE, TRUE);
 	}
 
 	BC_ASSERT_TRUE(call(marie, pauline));
diff --git a/tester/call_single_tester.c b/tester/call_single_tester.c
index 5fe9986df6cde8a03dfbece413b77ec8e7784f05..5f94f008a7bb530c87a6c30f847fe195081aa438 100644
--- a/tester/call_single_tester.c
+++ b/tester/call_single_tester.c
@@ -768,7 +768,8 @@ static void call_outbound_with_multiple_proxy(void) {
 	linphone_proxy_config_set_identity_address(registered_lpc, identity_address);
 	linphone_address_unref(identity_address);
 	linphone_proxy_config_set_server_addr(registered_lpc, linphone_proxy_config_get_addr(lpc));
-	linphone_proxy_config_set_route(registered_lpc, linphone_proxy_config_get_route(lpc));
+	const char *route = linphone_proxy_config_get_route(lpc);
+	linphone_proxy_config_set_route(registered_lpc, route);
 	linphone_proxy_config_enable_register(registered_lpc, TRUE);
 
 	linphone_core_add_proxy_config(marie->lc, registered_lpc);
@@ -793,6 +794,79 @@ static void call_outbound_with_multiple_proxy(void) {
 	linphone_core_manager_destroy(pauline);
 }
 
+static void call_outbound_using_secondary_account(void) {
+	// Caller
+	LinphoneCoreManager *marie = linphone_core_manager_create("marie_dual_proxy_rc");
+	set_lime_server_and_curve(25519, marie);
+	linphone_core_manager_start(marie, TRUE);
+
+	// Callee
+	LinphoneCoreManager *pauline =
+	    linphone_core_manager_create(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
+	set_lime_server_and_curve(25519, pauline);
+	linphone_core_manager_start(pauline, TRUE);
+
+	BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &pauline->stat.number_of_X3dhUserCreationSuccess, 1));
+	BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_X3dhUserCreationSuccess, 2));
+
+	LinphoneAccount *secondary_account = NULL;
+	const LinphoneAccount *default_account = linphone_core_get_default_account(marie->lc);
+	const bctbx_list_t *accounts = linphone_core_get_account_list(marie->lc);
+	for (const bctbx_list_t *account_it = accounts; account_it != NULL; account_it = account_it->next) {
+		LinphoneAccount *account = (LinphoneAccount *)account_it->data;
+		if (account != default_account) {
+			secondary_account = account;
+		}
+	}
+
+	BC_ASSERT_PTR_NOT_NULL(secondary_account);
+	if (!secondary_account) {
+		goto end;
+	}
+
+	const LinphoneAccountParams *secondary_account_params = linphone_account_get_params(secondary_account);
+	const LinphoneAddress *secondary_account_identity =
+	    linphone_account_params_get_identity_address(secondary_account_params);
+
+	const LinphoneAccountParams *default_account_params = linphone_account_get_params(default_account);
+	const LinphoneAddress *default_account_identity =
+	    linphone_account_params_get_identity_address(default_account_params);
+
+	BC_ASSERT_FALSE(linphone_address_weak_equal(default_account_identity, secondary_account_identity));
+
+	LinphoneCallParams *params = linphone_core_create_call_params(marie->lc, NULL);
+	char *secondary_account_identity_str = linphone_address_as_string(secondary_account_identity);
+	linphone_call_params_set_from_header(params, secondary_account_identity_str);
+	ms_free(secondary_account_identity_str);
+
+	BC_ASSERT_TRUE(call_with_caller_params(marie, pauline, params));
+	linphone_call_params_unref(params);
+
+	LinphoneCall *marie_call = linphone_core_get_current_call(marie->lc);
+	BC_ASSERT_PTR_NOT_NULL(marie_call);
+	if (marie_call) {
+		const LinphoneCallParams *marie_call_parameters = linphone_call_get_params(marie_call);
+		const LinphoneAccount *marie_call_account = linphone_call_params_get_account(marie_call_parameters);
+		const LinphoneAccountParams *marie_call_account_params = linphone_account_get_params(marie_call_account);
+		const LinphoneAddress *marie_call_account_identity =
+		    linphone_account_params_get_identity_address(marie_call_account_params);
+		BC_ASSERT_TRUE(linphone_address_weak_equal(marie_call_account_identity, secondary_account_identity));
+	}
+
+	LinphoneCall *pauline_call = linphone_core_get_current_call(pauline->lc);
+	BC_ASSERT_PTR_NOT_NULL(pauline_call);
+	if (pauline_call) {
+		const LinphoneAddress *pauline_call_remote_contact_address =
+		    linphone_call_get_remote_contact_address(pauline_call);
+		BC_ASSERT_TRUE(linphone_address_weak_equal(pauline_call_remote_contact_address, secondary_account_identity));
+	}
+	end_call(marie, pauline);
+
+end:
+	linphone_core_manager_destroy(marie);
+	linphone_core_manager_destroy(pauline);
+}
+
 static void call_outbound_using_different_proxies(void) {
 	LinphoneCoreManager *marie = linphone_core_manager_new_with_proxies_check("marie_dual_proxy_rc", FALSE); // Caller
 	LinphoneCoreManager *pauline = linphone_core_manager_new_with_proxies_check("pauline_tcp_rc", FALSE);    // Callee
@@ -824,14 +898,14 @@ static void call_outbound_using_different_proxies(void) {
 						BC_ASSERT_TRUE(wait_for_until(marie->lc, pauline->lc,
 						                              &pauline->stat.number_of_LinphoneCallIncomingReceived, call_count,
 						                              10000));
-						LinphoneCall *callee = linphone_core_get_current_call(pauline->lc);
-						BC_ASSERT_PTR_NOT_NULL(callee);
-						if (callee) {
-							const LinphoneAddress *remoteAddress = linphone_call_get_remote_address(callee);
-							BC_ASSERT_TRUE(linphone_address_weak_equal(
-							    remoteAddress,
-							    marieProxyAddress)); // Main test : callee get a call from the selected proxy of caller
-						}
+					}
+					LinphoneCall *callee = linphone_core_get_current_call(pauline->lc);
+					BC_ASSERT_PTR_NOT_NULL(callee);
+					if (callee) {
+						const LinphoneAddress *remoteAddress = linphone_call_get_remote_address(callee);
+						BC_ASSERT_TRUE(linphone_address_weak_equal(
+						    remoteAddress,
+						    marieProxyAddress)); // Main test : callee get a call from the selected proxy of caller
 					}
 				}
 			}
@@ -2303,12 +2377,10 @@ void _call_with_ice_base(LinphoneCoreManager *pauline,
 	linphone_core_set_user_agent(marie->lc, "Natted Linphone", NULL);
 
 	if (callee_with_ice) {
-		enable_stun_in_core(marie, TRUE, TRUE);
-		linphone_core_manager_wait_for_stun_resolution(marie);
+		enable_stun_in_mgr(marie, TRUE, TRUE, TRUE, TRUE);
 	}
 	if (caller_with_ice) {
-		enable_stun_in_core(pauline, TRUE, TRUE);
-		linphone_core_manager_wait_for_stun_resolution(pauline);
+		enable_stun_in_mgr(pauline, TRUE, TRUE, TRUE, TRUE);
 	}
 
 	if (random_ports) {
@@ -3928,10 +4000,8 @@ static void _call_base_with_configfile(LinphoneMediaEncryption mode,
 		}
 
 		if (policy == LinphonePolicyUseIce) {
-			enable_stun_in_core(marie, TRUE, TRUE);
-			linphone_core_manager_wait_for_stun_resolution(marie);
-			enable_stun_in_core(pauline, TRUE, TRUE);
-			linphone_core_manager_wait_for_stun_resolution(pauline);
+			enable_stun_in_mgr(marie, TRUE, TRUE, TRUE, TRUE);
+			enable_stun_in_mgr(pauline, TRUE, TRUE, TRUE, TRUE);
 		}
 
 		BC_ASSERT_TRUE((call_ok = call(pauline, marie)));
@@ -5220,8 +5290,7 @@ void early_media_without_sdp_in_200_base(bool_t use_video, bool_t use_ice) {
 	lcs = bctbx_list_append(lcs, marie->lc);
 	lcs = bctbx_list_append(lcs, pauline->lc);
 	if (use_ice) {
-		enable_stun_in_core(marie, TRUE, TRUE);
-		linphone_core_manager_wait_for_stun_resolution(marie);
+		enable_stun_in_mgr(marie, TRUE, TRUE, TRUE, TRUE);
 		/* We need RTP symmetric because ICE will put the STUN address in the C line, and no relay is made in this
 		 * scenario.*/
 		linphone_config_set_int(linphone_core_get_config(pauline->lc), "rtp", "symmetric", 1);
@@ -6320,11 +6389,8 @@ void _call_with_rtcp_mux(bool_t caller_rtcp_mux, bool_t callee_rtcp_mux, bool_t
 		linphone_core_set_user_agent(pauline->lc, "Natted Linphone", NULL);
 		linphone_core_set_user_agent(marie->lc, "Natted Linphone", NULL);
 
-		enable_stun_in_core(marie, TRUE, TRUE);
-		linphone_core_manager_wait_for_stun_resolution(marie);
-
-		enable_stun_in_core(pauline, TRUE, TRUE);
-		linphone_core_manager_wait_for_stun_resolution(pauline);
+		enable_stun_in_mgr(marie, TRUE, TRUE, TRUE, TRUE);
+		enable_stun_in_mgr(pauline, TRUE, TRUE, TRUE, TRUE);
 	}
 	if (!with_ice_reinvite) {
 		linphone_config_set_int(linphone_core_get_config(pauline->lc), "sip", "update_call_when_ice_completed", 0);
@@ -7376,6 +7442,7 @@ static test_t call_tests[] = {
     TEST_NO_TAG("IPv6 call over NAT64", v6_call_over_nat_64),
     TEST_NO_TAG("Outbound call with multiple proxy possible", call_outbound_with_multiple_proxy),
     TEST_NO_TAG("Outbound call using different proxies", call_outbound_using_different_proxies),
+    TEST_ONE_TAG("Outbound call using secondary account", call_outbound_using_secondary_account, "LimeX3DH"),
     TEST_NO_TAG("Audio call recording", audio_call_recording_test),
     TEST_NO_TAG("Multiple answers to a call", multiple_answers_call),
     TEST_NO_TAG("Multiple answers to a call with media relay", multiple_answers_call_with_media_relay),
@@ -7391,7 +7458,7 @@ static test_t call_tests[] = {
     TEST_NO_TAG("Early-media call with updated codec", early_media_call_with_codec_update),
     TEST_NO_TAG("Call terminated by caller", call_terminated_by_caller),
     TEST_NO_TAG("Call without SDP", call_with_no_sdp),
-    TEST_NO_TAG("Call without SDP to a lime X3DH enabled device", call_with_no_sdp_lime),
+    TEST_ONE_TAG("Call without SDP to a lime X3DH enabled device", call_with_no_sdp_lime, "LimeX3DH"),
     TEST_NO_TAG("Call without SDP and ACK without SDP", call_with_no_sdp_ack_without_sdp),
     TEST_NO_TAG("Call paused with RTP port to 0", call_paused_with_rtp_port_to_zero),
     TEST_NO_TAG("Call paused resumed", call_paused_resumed),
diff --git a/tester/call_with_rtp_bundle_tester.c b/tester/call_with_rtp_bundle_tester.c
index 76fd30b047aa157b3453dc9608508e43647ebaf1..017d6c57285571d6e2b3a9bf0bd64a8b08c20794 100644
--- a/tester/call_with_rtp_bundle_tester.c
+++ b/tester/call_with_rtp_bundle_tester.c
@@ -183,15 +183,8 @@ static void audio_video_call(const params_t *params) {
 
 	if (params->with_ice) {
 		/*enable ICE on both ends*/
-		LinphoneNatPolicy *pol;
-		pol = linphone_core_get_nat_policy(marie->lc);
-		linphone_nat_policy_enable_ice(pol, TRUE);
-		linphone_nat_policy_enable_stun(pol, TRUE);
-		linphone_core_set_nat_policy(marie->lc, pol);
-		pol = linphone_core_get_nat_policy(pauline->lc);
-		linphone_nat_policy_enable_ice(pol, TRUE);
-		linphone_nat_policy_enable_stun(pol, TRUE);
-		linphone_core_set_nat_policy(pauline->lc, pol);
+		enable_stun_in_mgr(marie, TRUE, TRUE, TRUE, TRUE);
+		enable_stun_in_mgr(pauline, TRUE, TRUE, TRUE, TRUE);
 	}
 
 	if (params->with_dtls_srtp) {
@@ -389,28 +382,30 @@ static void call_with_mandatory_bundle(void) {
 }
 
 static void simple_audio_video_call_with_bundle_enabled_by_reinvite(void) {
-	LinphoneCoreManager* marie;
-	LinphoneCoreManager* pauline;
+	LinphoneCoreManager *marie;
+	LinphoneCoreManager *pauline;
 	LinphoneCall *pauline_call, *marie_call;
 	LinphoneVideoActivationPolicy *vpol = linphone_factory_create_video_activation_policy(linphone_factory_get());
-	
-	marie = linphone_core_manager_new( "marie_rc");
+
+	marie = linphone_core_manager_new("marie_rc");
 	pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
 
 	{
 		LinphoneAccount *marie_account = linphone_core_get_default_account(marie->lc);
-		LinphoneAccountParams *marie_account_params = linphone_account_params_clone(linphone_account_get_params(marie_account));
+		LinphoneAccountParams *marie_account_params =
+		    linphone_account_params_clone(linphone_account_get_params(marie_account));
 		linphone_account_params_enable_rtp_bundle(marie_account_params, TRUE);
 		linphone_account_set_params(marie_account, marie_account_params);
 		linphone_account_params_unref(marie_account_params);
 
 		LinphoneAccount *pauline_account = linphone_core_get_default_account(pauline->lc);
-		LinphoneAccountParams *pauline_account_params = linphone_account_params_clone(linphone_account_get_params(pauline_account));
+		LinphoneAccountParams *pauline_account_params =
+		    linphone_account_params_clone(linphone_account_get_params(pauline_account));
 		linphone_account_params_enable_rtp_bundle(pauline_account_params, TRUE);
 		linphone_account_set_params(pauline_account, pauline_account_params);
 		linphone_account_params_unref(pauline_account_params);
 	}
-	
+
 	linphone_video_activation_policy_set_automatically_initiate(vpol, TRUE);
 	linphone_video_activation_policy_set_automatically_accept(vpol, TRUE);
 
@@ -418,24 +413,24 @@ static void simple_audio_video_call_with_bundle_enabled_by_reinvite(void) {
 	linphone_core_enable_video_display(marie->lc, TRUE);
 	linphone_core_enable_video_capture(pauline->lc, TRUE);
 	linphone_core_enable_video_display(pauline->lc, TRUE);
-	
+
 	linphone_core_set_preferred_video_definition_by_name(marie->lc, "QVGA");
 	linphone_core_set_preferred_video_definition_by_name(pauline->lc, "QVGA");
 
 	linphone_core_set_video_device(marie->lc, "Mire: Mire (synthetic moving picture)");
 	linphone_core_set_video_device(pauline->lc, "Mire: Mire (synthetic moving picture)");
-	
+
 	linphone_core_set_video_activation_policy(marie->lc, vpol);
 	linphone_core_set_video_activation_policy(pauline->lc, vpol);
 	linphone_video_activation_policy_unref(vpol);
 
 	LinphoneCallParams *marie_params = linphone_core_create_call_params(marie->lc, NULL);
-	linphone_call_params_enable_video(marie_params,FALSE);
+	linphone_call_params_enable_video(marie_params, FALSE);
 	LinphoneCallParams *pauline_params = linphone_core_create_call_params(pauline->lc, NULL);
-	linphone_call_params_enable_rtp_bundle(pauline_params,FALSE);
-	linphone_call_params_enable_video(pauline_params,FALSE);
+	linphone_call_params_enable_rtp_bundle(pauline_params, FALSE);
+	linphone_call_params_enable_video(pauline_params, FALSE);
 
-	BC_ASSERT_TRUE(call_with_params(marie,pauline,marie_params,pauline_params));
+	BC_ASSERT_TRUE(call_with_params(marie, pauline, marie_params, pauline_params));
 
 	linphone_call_params_unref(marie_params);
 	linphone_call_params_unref(pauline_params);
@@ -458,17 +453,21 @@ static void simple_audio_video_call_with_bundle_enabled_by_reinvite(void) {
 	stats initial_marie_stat = marie->stat;
 	stats initial_pauline_stat = pauline->stat;
 
-	LinphoneCallParams * new_params = linphone_core_create_call_params(pauline->lc, pauline_call);
-	linphone_call_params_enable_video (new_params, TRUE);
+	LinphoneCallParams *new_params = linphone_core_create_call_params(pauline->lc, pauline_call);
+	linphone_call_params_enable_video(new_params, TRUE);
 	/* This method is deprecated, but we still use to test that disablement of bundle is working. */
 	linphone_call_params_enable_rtp_bundle(new_params, TRUE);
 	linphone_call_update(pauline_call, new_params);
-	linphone_call_params_unref (new_params);
+	linphone_call_params_unref(new_params);
 
-	BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneCallUpdatedByRemote, initial_marie_stat.number_of_LinphoneCallUpdatedByRemote + 1));
-	BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &pauline->stat.number_of_LinphoneCallUpdating, initial_pauline_stat.number_of_LinphoneCallUpdating + 1));
-	BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, initial_marie_stat.number_of_LinphoneCallStreamsRunning + 1));
-	BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, initial_pauline_stat.number_of_LinphoneCallStreamsRunning + 1));
+	BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneCallUpdatedByRemote,
+	                        initial_marie_stat.number_of_LinphoneCallUpdatedByRemote + 1));
+	BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &pauline->stat.number_of_LinphoneCallUpdating,
+	                        initial_pauline_stat.number_of_LinphoneCallUpdating + 1));
+	BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneCallStreamsRunning,
+	                        initial_marie_stat.number_of_LinphoneCallStreamsRunning + 1));
+	BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning,
+	                        initial_pauline_stat.number_of_LinphoneCallStreamsRunning + 1));
 
 	check_rtp_bundle(pauline_call, TRUE, TRUE);
 	BC_ASSERT_TRUE(linphone_call_params_video_enabled(linphone_call_get_current_params(pauline_call)));
@@ -478,39 +477,42 @@ static void simple_audio_video_call_with_bundle_enabled_by_reinvite(void) {
 	BC_ASSERT_TRUE(linphone_call_params_video_enabled(linphone_call_get_current_params(marie_call)));
 	BC_ASSERT_TRUE(linphone_call_params_rtp_bundle_enabled(linphone_call_get_current_params(marie_call)));
 
-	liblinphone_tester_check_rtcp(marie,pauline);
+	liblinphone_tester_check_rtcp(marie, pauline);
 	liblinphone_tester_set_next_video_frame_decoded_cb(pauline_call);
-	BC_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&pauline->stat.number_of_IframeDecoded,1));
+	BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_IframeDecoded, 1));
 	liblinphone_tester_set_next_video_frame_decoded_cb(marie_call);
-	BC_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_IframeDecoded,1));
+	BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_IframeDecoded, 1));
+
+	// make sure receive frame rate computation is done with a significant number of frame
+	wait_for_until(marie->lc, pauline->lc, NULL, 0, 2000);
 
-	//make sure receive frame rate computation is done with a significant number of frame
-	wait_for_until(marie->lc,pauline->lc,NULL,0,2000);
+	BC_ASSERT_GREATER(linphone_call_params_get_received_framerate(linphone_call_get_current_params(pauline_call)), 8.0,
+	                  float, "%f");
+	BC_ASSERT_GREATER(linphone_call_params_get_received_framerate(linphone_call_get_current_params(marie_call)), 8.0,
+	                  float, "%f");
 
-	BC_ASSERT_GREATER(linphone_call_params_get_received_framerate(linphone_call_get_current_params(pauline_call)), 8.0, float, "%f");
-	BC_ASSERT_GREATER(linphone_call_params_get_received_framerate(linphone_call_get_current_params(marie_call)), 8.0, float, "%f");
+	// Wait to see any undesirable side effect
+	wait_for_until(marie->lc, pauline->lc, NULL, 0, 1000);
 
-	//Wait to see any undesirable side effect
-	wait_for_until(marie->lc,pauline->lc,NULL,0,1000);
+	end_call(marie, pauline);
 
-	end_call(marie,pauline);
-	
 end:
 	linphone_core_manager_destroy(pauline);
 	linphone_core_manager_destroy(marie);
 }
 
 static test_t call_with_rtp_bundle_tests[] = {
-	TEST_NO_TAG("Simple audio call", simple_audio_call),
-	TEST_NO_TAG("Simple audio call with DTLS-SRTP", simple_audio_call_with_srtp_dtls),
-	TEST_NO_TAG("Simple audio-video call", simple_audio_video_call),
-	TEST_NO_TAG("Simple audio-video call with bundle refused", simple_audio_video_call_bundle_refused),
-	TEST_NO_TAG("Simple audio-video call with bundle disable", simple_audio_video_call_with_bundle_disable),
-	TEST_NO_TAG("Simple audio-video call with bundle enabled by reINVITE", simple_audio_video_call_with_bundle_enabled_by_reinvite),
-	TEST_NO_TAG("Audio-video call with ICE", audio_video_call_with_ice),
-	TEST_NO_TAG("Audio-video call with ICE and DTLS-SRTP", audio_video_call_with_ice_and_dtls_srtp),
-	TEST_NO_TAG("Mandatory bundle", call_with_mandatory_bundle),
-	TEST_NO_TAG("Audio-video call with forced media relay", audio_video_call_with_forced_media_relay),
+    TEST_NO_TAG("Simple audio call", simple_audio_call),
+    TEST_NO_TAG("Simple audio call with DTLS-SRTP", simple_audio_call_with_srtp_dtls),
+    TEST_NO_TAG("Simple audio-video call", simple_audio_video_call),
+    TEST_NO_TAG("Simple audio-video call with bundle refused", simple_audio_video_call_bundle_refused),
+    TEST_NO_TAG("Simple audio-video call with bundle disable", simple_audio_video_call_with_bundle_disable),
+    TEST_NO_TAG("Simple audio-video call with bundle enabled by reINVITE",
+                simple_audio_video_call_with_bundle_enabled_by_reinvite),
+    TEST_NO_TAG("Audio-video call with ICE", audio_video_call_with_ice),
+    TEST_NO_TAG("Audio-video call with ICE and DTLS-SRTP", audio_video_call_with_ice_and_dtls_srtp),
+    TEST_NO_TAG("Mandatory bundle", call_with_mandatory_bundle),
+    TEST_NO_TAG("Audio-video call with forced media relay", audio_video_call_with_forced_media_relay),
 };
 
 test_suite_t call_with_rtp_bundle_test_suite = {"Call with RTP bundle",
@@ -522,4 +524,3 @@ test_suite_t call_with_rtp_bundle_test_suite = {"Call with RTP bundle",
                                                     sizeof(call_with_rtp_bundle_tests[0]),
                                                 call_with_rtp_bundle_tests,
                                                 0};
-
diff --git a/tester/capability_negotiation_tester.cpp b/tester/capability_negotiation_tester.cpp
index 37c6e74299ba2693fac2015b3dccd63d827d9d25..b137d6609f09a9ddfd5d7b6fd916d798e0eac166 100644
--- a/tester/capability_negotiation_tester.cpp
+++ b/tester/capability_negotiation_tester.cpp
@@ -214,8 +214,10 @@ LinphoneCoreManager *create_core_mgr_with_capability_negotiation_setup(const cha
 	}
 
 	if (enable_ice) {
-		enable_stun_in_core(mgr, TRUE, enable_ice);
-		linphone_core_manager_wait_for_stun_resolution(mgr);
+		// TODO: allow disabling ice or STUN at the core level and enabling in the account
+		// Enable ICE at the account level but not at the core level
+		// enable_stun_in_mgr(mgr, TRUE, enable_ice, FALSE, FALSE);
+		enable_stun_in_mgr(mgr, TRUE, enable_ice, TRUE, enable_ice);
 	}
 
 	return mgr;
@@ -304,9 +306,9 @@ void encrypted_call_with_params_base(LinphoneCoreManager *caller,
 			potentialConfigurationChosen = false;
 		}
 
-		LinphoneNatPolicy *caller_nat_policy = linphone_core_get_nat_policy(caller->lc);
+		LinphoneNatPolicy *caller_nat_policy = get_nat_policy_for_call(caller, callerCall);
 		const bool_t caller_ice_enabled = linphone_nat_policy_ice_enabled(caller_nat_policy);
-		LinphoneNatPolicy *callee_nat_policy = linphone_core_get_nat_policy(callee->lc);
+		LinphoneNatPolicy *callee_nat_policy = get_nat_policy_for_call(callee, calleeCall);
 		const bool_t callee_ice_enabled = linphone_nat_policy_ice_enabled(callee_nat_policy);
 
 		const bool_t capabilityNegotiationReinviteEnabled =
@@ -666,9 +668,9 @@ void call_with_update_and_incompatible_encs_in_call_params_base(const bool_t ena
 	BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneCallUpdatedByRemote,
 	                        (marie_stat.number_of_LinphoneCallUpdatedByRemote + 1)));
 
-	LinphoneNatPolicy *marie_nat_policy = linphone_core_get_nat_policy(marie->lc);
+	LinphoneNatPolicy *marie_nat_policy = get_nat_policy_for_call(marie, marieCall);
 	const bool_t marie_ice_enabled = linphone_nat_policy_ice_enabled(marie_nat_policy);
-	LinphoneNatPolicy *pauline_nat_policy = linphone_core_get_nat_policy(pauline->lc);
+	LinphoneNatPolicy *pauline_nat_policy = get_nat_policy_for_call(pauline, paulineCall);
 	const bool_t pauline_ice_enabled = linphone_nat_policy_ice_enabled(pauline_nat_policy);
 	const int expectedStreamsRunning = 1 + ((pauline_ice_enabled && marie_ice_enabled) ? 1 : 0);
 
@@ -1565,9 +1567,9 @@ static void call_with_no_sdp_on_update_base(const bool_t caller_cap_neg,
 
 	liblinphone_tester_check_rtcp(marie, pauline);
 
-	LinphoneNatPolicy *marie_nat_policy = linphone_core_get_nat_policy(marie->lc);
+	LinphoneNatPolicy *marie_nat_policy = get_nat_policy_for_call(marie, marieCall);
 	const bool_t marie_ice_enabled = linphone_nat_policy_ice_enabled(marie_nat_policy);
-	LinphoneNatPolicy *pauline_nat_policy = linphone_core_get_nat_policy(pauline->lc);
+	LinphoneNatPolicy *pauline_nat_policy = get_nat_policy_for_call(pauline, paulineCall);
 	const bool_t pauline_ice_enabled = linphone_nat_policy_ice_enabled(pauline_nat_policy);
 
 	bool potentialConfigurationChosen = (caller_cap_neg && callee_cap_neg);
@@ -1956,9 +1958,9 @@ static void call_changes_enc_on_update_base(const bool_t caller_cap_neg,
 
 	liblinphone_tester_check_rtcp(marie, pauline);
 
-	LinphoneNatPolicy *marie_nat_policy = linphone_core_get_nat_policy(marie->lc);
+	LinphoneNatPolicy *marie_nat_policy = get_nat_policy_for_call(marie, marieCall);
 	const bool_t marie_ice_enabled = linphone_nat_policy_ice_enabled(marie_nat_policy);
-	LinphoneNatPolicy *pauline_nat_policy = linphone_core_get_nat_policy(pauline->lc);
+	LinphoneNatPolicy *pauline_nat_policy = get_nat_policy_for_call(pauline, paulineCall);
 	const bool_t pauline_ice_enabled = linphone_nat_policy_ice_enabled(pauline_nat_policy);
 
 	bool capabilityNegotiationReinviteEnabled =
@@ -3536,4 +3538,3 @@ test_suite_t capability_negotiation_no_sdp_test_suite = {"Capability Negotiation
                                                              sizeof(capability_negotiation_tests_no_sdp[0]),
                                                          capability_negotiation_tests_no_sdp,
                                                          0};
-
diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h
index c5514f91c95af078e24d91aff20046621d2d4e29..0308cb817ebeb8e4ccee6bb87304a767efa36912 100644
--- a/tester/liblinphone_tester.h
+++ b/tester/liblinphone_tester.h
@@ -21,6 +21,8 @@
 #ifndef LIBLINPHONE_TESTER_H_
 #define LIBLINPHONE_TESTER_H_
 
+#include <stdbool.h>
+
 #include "linphone/core.h"
 #include <bctoolbox/tester.h>
 #include <mediastreamer2/msutils.h>
@@ -967,6 +969,16 @@ LinphoneAddress *linphone_core_manager_resolve(LinphoneCoreManager *mgr, const L
 FILE *sip_start(const char *senario, const char *dest_username, const char *passwd, LinphoneAddress *dest_addres);
 
 void early_media_without_sdp_in_200_base(bool_t use_video, bool_t use_ice);
+LinphoneNatPolicy *get_nat_policy_for_call(LinphoneCoreManager *mgr, LinphoneCall *call);
+void enable_stun_in_mgr(LinphoneCoreManager *mgr,
+                        const bool_t account_enable_stun,
+                        const bool_t account_enable_ice,
+                        const bool_t core_enable_stun,
+                        const bool_t core_enable_ice);
+void enable_stun_in_account(LinphoneCoreManager *mgr,
+                            LinphoneAccount *account,
+                            const bool_t enable_stun,
+                            const bool_t enable_ice);
 void enable_stun_in_core(LinphoneCoreManager *mgr, const bool_t enable_stun, const bool_t enable_ice);
 void linphone_conf_event_notify(LinphoneEvent *lev);
 void _check_friend_result_list(
@@ -1017,6 +1029,7 @@ void set_lime_server_and_curve_list_tls(const int curveId,
                                         bool_t tls_auth_server,
                                         bool_t required);
 
+bool is_filepath_encrypted(const char *filepath);
 typedef struct _LinphoneAccountCreatorStats {
 	int cb_done;
 } LinphoneAccountCreatorStats;
@@ -1028,6 +1041,7 @@ void account_creator_reset_cb_done(LinphoneAccountCreatorCbs *cbs);
 void lime_delete_DRSessions(const char *limedb);
 void lime_setback_usersUpdateTs(const char *limedb, int days);
 uint64_t lime_get_userUpdateTs(const char *limedb);
+char *lime_get_userIk(LinphoneCoreManager *mgr, char *gruu);
 
 void liblinphone_tester_simulate_mire_defunct(
     MSFilter *filter,
diff --git a/tester/local_conference_tester_functions.cpp b/tester/local_conference_tester_functions.cpp
index cb2a3cc52edb0e6de3cdb7825bec12449cb5396a..76e2b2186411da9563ce557027ed9be857ebceba 100644
--- a/tester/local_conference_tester_functions.cpp
+++ b/tester/local_conference_tester_functions.cpp
@@ -2697,8 +2697,10 @@ void create_conference_base(time_t start_time,
 				linphone_core_set_default_proxy_config(mgr->lc, NULL);
 			}
 
-			enable_stun_in_core(mgr, enable_stun, enable_ice);
-			linphone_core_manager_wait_for_stun_resolution(mgr);
+			// TODO: allow disabling ice or STUN at the core level and enabling in the account
+			// Enable ICE at the account level but not at the core level
+			// enable_stun_in_mgr(mgr, enable_stun, enable_ice, FALSE, FALSE);
+			enable_stun_in_mgr(mgr, enable_stun, enable_ice, enable_stun, enable_ice);
 
 			linphone_config_set_int(linphone_core_get_config(mgr->lc), "sip", "update_call_when_ice_completed", TRUE);
 			linphone_config_set_int(linphone_core_get_config(mgr->lc), "sip",
@@ -6540,8 +6542,7 @@ void create_one_participant_conference_toggle_video_base(LinphoneConferenceLayou
 				linphone_core_set_default_conference_layout(mgr->lc, layout);
 			}
 
-			enable_stun_in_core(mgr, enable_stun, enable_ice);
-			linphone_core_manager_wait_for_stun_resolution(mgr);
+			enable_stun_in_mgr(mgr, enable_stun, enable_ice, enable_stun, enable_ice);
 
 			coresList = bctbx_list_append(coresList, mgr->lc);
 		}
@@ -7861,7 +7862,7 @@ void create_simple_conference_merging_calls_base(bool_t enable_ice,
 		BC_ASSERT_TRUE(call(marie.getCMgr(), pauline.getCMgr()));
 
 		for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) {
-			enable_stun_in_core(mgr, enable_ice, enable_ice);
+			enable_stun_in_mgr(mgr, enable_ice, enable_ice, enable_ice, enable_ice);
 		}
 
 		LinphoneCall *marie_call_pauline = linphone_core_get_current_call(marie.getLc());
diff --git a/tester/local_ice_conference_tester.cpp b/tester/local_ice_conference_tester.cpp
index b87731d202996320e462a8e92e0f2b8858611d26..43fe0a6cd729ea8d6323fd6d58087f9a267a9403 100644
--- a/tester/local_ice_conference_tester.cpp
+++ b/tester/local_ice_conference_tester.cpp
@@ -121,8 +121,12 @@ static void abort_call_to_ice_conference(void) {
 				linphone_core_set_default_conference_layout(mgr->lc, layout);
 			}
 
-			enable_stun_in_core(mgr, TRUE, TRUE);
-			linphone_core_manager_wait_for_stun_resolution(mgr);
+			const bctbx_list_t *accounts = linphone_core_get_account_list(mgr->lc);
+			for (const bctbx_list_t *account_it = accounts; account_it != NULL; account_it = account_it->next) {
+				LinphoneAccount *account = (LinphoneAccount *)(bctbx_list_get_data(account_it));
+				enable_stun_in_account(mgr, account, TRUE, TRUE);
+			}
+			enable_stun_in_mgr(mgr, TRUE, TRUE, TRUE, TRUE);
 			coresList = bctbx_list_append(coresList, mgr->lc);
 		}
 
diff --git a/tester/local_inpromptu_conference_tester.cpp b/tester/local_inpromptu_conference_tester.cpp
index 09689698398fe933031e85e6372cc1366d9f493f..335714a2b79b977d285f886ed3898eb6d0be2030 100644
--- a/tester/local_inpromptu_conference_tester.cpp
+++ b/tester/local_inpromptu_conference_tester.cpp
@@ -76,8 +76,7 @@ static void create_conference_dial_out_base(bool_t send_ics,
 				linphone_core_set_default_conference_layout(mgr->lc, layout);
 			}
 
-			enable_stun_in_core(mgr, enable_stun, enable_ice);
-			linphone_core_manager_wait_for_stun_resolution(mgr);
+			enable_stun_in_mgr(mgr, enable_stun, enable_ice, enable_stun, enable_ice);
 
 			coresList = bctbx_list_append(coresList, mgr->lc);
 		}
@@ -1038,8 +1037,7 @@ static void create_simple_conference_dial_out_with_some_calls_declined_base(Linp
 				linphone_core_set_default_conference_layout(mgr->lc, LinphoneConferenceLayoutActiveSpeaker);
 			}
 
-			enable_stun_in_core(mgr, TRUE, TRUE);
-			linphone_core_manager_wait_for_stun_resolution(mgr);
+			enable_stun_in_mgr(mgr, TRUE, TRUE, FALSE, TRUE);
 
 			coresList = bctbx_list_append(coresList, mgr->lc);
 		}
diff --git a/tester/local_scheduled_conference_tester.cpp b/tester/local_scheduled_conference_tester.cpp
index bd933b74e0230278b71ba23826dd984ca8c06a6f..13d8f1b1460bd584beb46fda3f7dccd0206a2c4c 100644
--- a/tester/local_scheduled_conference_tester.cpp
+++ b/tester/local_scheduled_conference_tester.cpp
@@ -868,8 +868,9 @@ static void create_conference_with_codec_mismatch_base(bool_t organizer_codec_mi
 				linphone_core_set_media_encryption(mgr->lc, LinphoneMediaEncryptionSRTP);
 			}
 
-			enable_stun_in_core(mgr, TRUE, TRUE);
-			linphone_core_manager_wait_for_stun_resolution(mgr);
+			// TODO: allow disabling ice or STUN at the core level and enabling in the account
+			// enable_stun_in_mgr(mgr, TRUE, TRUE, TRUE, FALSE);
+			enable_stun_in_mgr(mgr, TRUE, TRUE, TRUE, TRUE);
 
 			if ((organizer_codec_mismatch && (mgr == marie.getCMgr())) ||
 			    (!organizer_codec_mismatch && (mgr == michelle.getCMgr()))) {
@@ -2076,8 +2077,7 @@ static void change_active_speaker(void) {
 				linphone_core_set_default_conference_layout(mgr->lc, LinphoneConferenceLayoutActiveSpeaker);
 			}
 
-			enable_stun_in_core(mgr, TRUE, FALSE);
-			linphone_core_manager_wait_for_stun_resolution(mgr);
+			enable_stun_in_mgr(mgr, TRUE, FALSE, TRUE, FALSE);
 
 			coresList = bctbx_list_append(coresList, mgr->lc);
 		}
diff --git a/tester/proxy_config_tester.c b/tester/proxy_config_tester.c
index ccf7f82a3565afd73cf8b761f1921c688d9f1acf..eb490b70a1f46e2bae59fea0e59cbbd23bd92be1 100644
--- a/tester/proxy_config_tester.c
+++ b/tester/proxy_config_tester.c
@@ -267,16 +267,20 @@ static void single_route(void) {
 	BC_ASSERT_PTR_NOT_NULL(routes);
 	BC_ASSERT_EQUAL((int)bctbx_list_size(routes), 1, int, "%d");
 	const char *route = (const char *)bctbx_list_get_data(routes);
-	BC_ASSERT_STRING_EQUAL(linphone_proxy_config_get_route(marie_cfg), "<sip:sip.example.org;transport=tcp>");
+	const char *marie_route = linphone_proxy_config_get_route(marie_cfg);
+	BC_ASSERT_STRING_EQUAL(marie_route, "<sip:sip.example.org;transport=tcp>");
 	BC_ASSERT_STRING_EQUAL(route, "<sip:sip.example.org;transport=tcp>");
+	routes = NULL;
 
 	linphone_proxy_config_set_route(marie_cfg, "sip.linphone.org");
 	routes = linphone_proxy_config_get_routes(marie_cfg);
 	BC_ASSERT_PTR_NOT_NULL(routes);
 	BC_ASSERT_EQUAL((int)bctbx_list_size(routes), 1, int, "%d");
 	route = (const char *)bctbx_list_get_data(routes);
-	BC_ASSERT_STRING_EQUAL(linphone_proxy_config_get_route(marie_cfg), "sip:sip.linphone.org");
+	marie_route = linphone_proxy_config_get_route(marie_cfg);
+	BC_ASSERT_STRING_EQUAL(marie_route, "sip:sip.linphone.org");
 	BC_ASSERT_STRING_EQUAL(route, "sip:sip.linphone.org");
+	routes = NULL;
 
 	linphone_core_manager_destroy(marie);
 }
@@ -289,6 +293,7 @@ static void multiple_route(void) {
 	linphone_proxy_config_set_routes(marie_cfg, NULL); // Clear routes
 	const bctbx_list_t *empty_routes = linphone_proxy_config_get_routes(marie_cfg);
 	BC_ASSERT_EQUAL((int)bctbx_list_size(empty_routes), 0, int, "%d");
+	empty_routes = NULL;
 
 	bctbx_list_t *new_routes = NULL;
 	new_routes = bctbx_list_append(new_routes, ms_strdup("<sip:sip.example.org;transport=tcp>"));
@@ -305,10 +310,12 @@ static void multiple_route(void) {
 	BC_ASSERT_EQUAL((int)bctbx_list_size(routes), 2, int, "%d"); // 2 are good, 2 are bad
 
 	const char *route = (const char *)bctbx_list_get_data(routes);
-	BC_ASSERT_STRING_EQUAL(linphone_proxy_config_get_route(marie_cfg), "<sip:sip.example.org;transport=tcp>");
+	const char *marie_route = linphone_proxy_config_get_route(marie_cfg);
+	BC_ASSERT_STRING_EQUAL(marie_route, "<sip:sip.example.org;transport=tcp>");
 	BC_ASSERT_STRING_EQUAL(route, "<sip:sip.example.org;transport=tcp>");
 	route = (const char *)bctbx_list_get_data(bctbx_list_next(routes));
 	BC_ASSERT_STRING_EQUAL(route, "sip:sip.linphone.org");
+	routes = NULL;
 
 	linphone_core_manager_destroy(marie);
 }
@@ -1291,4 +1298,4 @@ test_suite_t proxy_config_test_suite = {"Proxy config",
                                         liblinphone_tester_after_each,
                                         sizeof(proxy_config_tests) / sizeof(proxy_config_tests[0]),
                                         proxy_config_tests,
-                                        0};
\ No newline at end of file
+                                        0};
diff --git a/tester/remote-provisioning-tester.cpp b/tester/remote-provisioning-tester.cpp
index 06ae7bd646f0355dc302fb5e83d9faeffa30c29f..8dae1e81075ba4947402887382e4c485c74d237d 100644
--- a/tester/remote-provisioning-tester.cpp
+++ b/tester/remote-provisioning-tester.cpp
@@ -102,7 +102,8 @@ static void remote_provisioning_default_values(void) {
 	BC_ASSERT_TRUE(linphone_proxy_config_register_enabled(lpc));
 	BC_ASSERT_EQUAL(linphone_proxy_config_get_expires(lpc), 604800, int, "%d");
 	BC_ASSERT_STRING_EQUAL(linphone_proxy_config_get_server_addr(lpc), "<sip:sip.linphone.org:5223;transport=tls>");
-	BC_ASSERT_STRING_EQUAL(linphone_proxy_config_get_route(lpc), "<sip:sip.linphone.org:5223;transport=tls>");
+	const char *route = linphone_proxy_config_get_route(lpc);
+	BC_ASSERT_STRING_EQUAL(route, "<sip:sip.linphone.org:5223;transport=tls>");
 	BC_ASSERT_STRING_EQUAL(linphone_proxy_config_get_identity(lpc), "sip:?@sip.linphone.org");
 	{
 		LpConfig *lp = linphone_core_get_config(marie->lc);
diff --git a/tester/shared_tester_functions.cpp b/tester/shared_tester_functions.cpp
index c6a8f2948b842712ed6545e0a2660a8161f37d54..efc79fe392c7fbc33cafa9723a69d95a73587fcb 100644
--- a/tester/shared_tester_functions.cpp
+++ b/tester/shared_tester_functions.cpp
@@ -39,6 +39,41 @@ using namespace std;
 
 using namespace LinphonePrivate;
 
+void check_lime_ik(LinphoneCoreManager *mgr, LinphoneCall *call) {
+
+	if (!linphone_core_lime_x3dh_enabled(mgr->lc)) {
+		return;
+	}
+
+	// Do not check Ik if database is encrypted
+	if (is_filepath_encrypted(mgr->lime_database_path)) {
+		return;
+	}
+	const LinphoneCallParams *call_parameters = linphone_call_get_params(call);
+	LinphoneAccount *call_account = linphone_call_params_get_account(call_parameters);
+	BC_ASSERT_PTR_NOT_NULL(call_account);
+	char *refIk = NULL;
+	if (call_account) {
+		const LinphoneAccountParams *call_account_params = linphone_account_get_params(call_account);
+		const char *lime_server_url = linphone_account_params_get_lime_server_url(call_account_params);
+		bool_t lime_server_found = (lime_server_url != NULL);
+		if (!lime_server_found) return;
+		const LinphoneAddress *call_account_contact = linphone_account_get_contact_address(call_account);
+		char *call_account_contact_str = linphone_address_as_string_uri_only(call_account_contact);
+		refIk = lime_get_userIk(mgr, call_account_contact_str);
+		BC_ASSERT_PTR_NOT_NULL(refIk);
+		ms_free(call_account_contact_str);
+
+		SalMediaDescription *desc = _linphone_call_get_local_desc(call);
+		belle_sdp_session_description_t *sdp = desc->toSdp();
+		const char *ik = belle_sdp_session_description_get_attribute_value(sdp, "Ik");
+		BC_ASSERT_PTR_NOT_NULL(ik);
+		BC_ASSERT_PTR_NOT_NULL(refIk);
+		if (refIk && ik) BC_ASSERT_STRING_EQUAL(refIk, ik);
+		if (refIk) ms_free(refIk);
+	}
+}
+
 static void check_ice_from_rtp(LinphoneCall *c1, LinphoneCall *c2, LinphoneStreamType stream_type) {
 	MediaStream *ms;
 	LinphoneCallStats *stats;
diff --git a/tester/shared_tester_functions.h b/tester/shared_tester_functions.h
index f226172d761db50b3b227b97dbe9165937bf5c72..adbc8b0a775fe254e1530a14632deb6c9f21d8c8 100644
--- a/tester/shared_tester_functions.h
+++ b/tester/shared_tester_functions.h
@@ -29,6 +29,7 @@ extern "C" {
 
 bool_t check_ice(LinphoneCoreManager *caller, LinphoneCoreManager *callee, LinphoneIceState state);
 bool_t check_ice_sdp(LinphoneCall *call);
+void check_lime_ik(LinphoneCoreManager *mgr, LinphoneCall *call);
 
 typedef enum _TesterIceCandidateType {
 	TesterIceCandidateHost,
diff --git a/tester/tester.c b/tester/tester.c
index 12cad48ffd9e136effc475bbaffec05c51089488..4b0d390a68e34c36f6d2b1eaf76ca7bc158c75a2 100644
--- a/tester/tester.c
+++ b/tester/tester.c
@@ -2845,8 +2845,7 @@ void linphone_core_manager_uninit(LinphoneCoreManager *mgr) {
 	linphone_core_manager_uninit2(mgr, TRUE, TRUE);
 }
 
-void linphone_core_manager_wait_for_stun_resolution(LinphoneCoreManager *mgr) {
-	LinphoneNatPolicy *nat_policy = linphone_core_get_nat_policy(mgr->lc);
+static void linphone_nat_policy_wait_for_stun_resolution(LinphoneCoreManager *mgr, LinphoneNatPolicy *nat_policy) {
 	if ((nat_policy != NULL) && (linphone_nat_policy_get_stun_server(nat_policy) != NULL) &&
 	    (linphone_nat_policy_stun_enabled(nat_policy) || linphone_nat_policy_turn_enabled(nat_policy)) &&
 	    (linphone_nat_policy_ice_enabled(nat_policy))) {
@@ -2855,6 +2854,18 @@ void linphone_core_manager_wait_for_stun_resolution(LinphoneCoreManager *mgr) {
 	}
 }
 
+void linphone_core_manager_wait_for_stun_resolution(LinphoneCoreManager *mgr) {
+	LinphoneNatPolicy *nat_policy = linphone_core_get_nat_policy(mgr->lc);
+	linphone_nat_policy_wait_for_stun_resolution(mgr, nat_policy);
+	const bctbx_list_t *accounts = linphone_core_get_account_list(mgr->lc);
+	for (const bctbx_list_t *account_it = accounts; account_it != NULL; account_it = account_it->next) {
+		LinphoneAccount *account = (LinphoneAccount *)(bctbx_list_get_data(account_it));
+		const LinphoneAccountParams *account_params = linphone_account_get_params(account);
+		LinphoneNatPolicy *account_nat_policy = linphone_account_params_get_nat_policy(account_params);
+		linphone_nat_policy_wait_for_stun_resolution(mgr, account_nat_policy);
+	}
+}
+
 void linphone_core_manager_uninit3(LinphoneCoreManager *mgr) {
 	if (mgr->lc && linphone_core_get_global_state(mgr->lc) != LinphoneGlobalOff &&
 	    !linphone_core_is_network_reachable(mgr->lc)) {
@@ -4317,13 +4328,23 @@ bool_t call_with_params2(LinphoneCoreManager *caller_mgr,
 		BC_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call_remote_address(
 		    callee_mgr->lc)); /*only relevant if one call, otherwise, not always set*/
 
-	callee_call = linphone_core_get_call_by_remote_address2(callee_mgr->lc, caller_mgr->identity);
+	LinphoneAddress *callee_from = NULL;
+	if (caller_params) {
+		const char *callee_from_str = linphone_call_params_get_from_header(caller_params);
+		if (callee_from_str) {
+			callee_from = linphone_address_new(callee_from_str);
+		}
+	}
+	if (!callee_from) {
+		callee_from = linphone_address_clone(caller_mgr->identity);
+	}
+
+	callee_call = linphone_core_get_call_by_remote_address2(callee_mgr->lc, callee_from);
 
 	if (!linphone_core_get_current_call(caller_mgr->lc) ||
 	    (!callee_call && !linphone_core_get_current_call(callee_mgr->lc)) /*for privacy case*/) {
 		return 0;
 	} else if (caller_mgr->identity) {
-		LinphoneAddress *callee_from = linphone_address_clone(caller_mgr->identity);
 		linphone_address_set_port(callee_from, 0); /*remove port because port is never present in from header*/
 
 		if (linphone_call_params_get_privacy(linphone_call_get_current_params(
@@ -4332,14 +4353,17 @@ bool_t call_with_params2(LinphoneCoreManager *caller_mgr,
 			if (!linphone_config_get_int(linphone_core_get_config(callee_mgr->lc), "sip",
 			                             "call_logs_use_asserted_id_instead_of_from", 0)) {
 				BC_ASSERT_PTR_NOT_NULL(callee_call);
-				BC_ASSERT_TRUE(linphone_address_weak_equal(callee_from, linphone_call_get_remote_address(callee_call)));
+				if (callee_call) {
+					BC_ASSERT_TRUE(
+					    linphone_address_weak_equal(callee_from, linphone_call_get_remote_address(callee_call)));
+				}
 			}
 		} else {
 			BC_ASSERT_FALSE(linphone_address_weak_equal(
 			    callee_from, linphone_call_get_remote_address(linphone_core_get_current_call(callee_mgr->lc))));
 		}
-		linphone_address_unref(callee_from);
 	}
+	linphone_address_unref(callee_from);
 
 	if (callee_stats->number_of_startRingbackTone == callee_stats->number_of_stopRingbackTone) {
 		if (callee_should_ring) {
@@ -4479,10 +4503,13 @@ bool_t call_with_params2(LinphoneCoreManager *caller_mgr,
 		BC_ASSERT_PTR_NOT_NULL(caller_call);
 		const LinphoneCallParams *caller_call_param = linphone_call_get_current_params(caller_call);
 		const LinphoneMediaEncryption caller_enc = linphone_call_params_get_media_encryption(caller_call_param);
+		check_lime_ik(caller_mgr, caller_call);
+
 		callee_call = linphone_core_get_current_call(callee_mgr->lc);
 		BC_ASSERT_PTR_NOT_NULL(callee_call);
 		const LinphoneCallParams *callee_call_param = linphone_call_get_current_params(callee_call);
 		const LinphoneMediaEncryption callee_enc = linphone_call_params_get_media_encryption(callee_call_param);
+		check_lime_ik(callee_mgr, callee_call);
 
 		// Ensure that encryption on both sides is the same
 		BC_ASSERT_EQUAL(caller_enc, matched_enc, int, "%d");
@@ -4511,8 +4538,8 @@ bool_t call_with_params2(LinphoneCoreManager *caller_mgr,
 		const bool_t callerSendIceReInviteWithDtls = linphone_config_get_int(
 		    linphone_core_get_config(caller_mgr->lc), "sip", "update_call_when_ice_completed_with_dtls", FALSE);
 
-		LinphoneNatPolicy *caller_policy = linphone_core_get_nat_policy(caller_mgr->lc);
-		LinphoneNatPolicy *callee_policy = linphone_core_get_nat_policy(callee_mgr->lc);
+		LinphoneNatPolicy *caller_policy = get_nat_policy_for_call(caller_mgr, caller_call);
+		LinphoneNatPolicy *callee_policy = get_nat_policy_for_call(callee_mgr, callee_call);
 		bool_t capability_negotiation_reinvite_enabled = linphone_core_sdp_200_ack_enabled(caller_mgr->lc)
 		                                                     ? callee_capability_negotiation_reinvite_enabled
 		                                                     : caller_capability_negotiation_reinvite_enabled;
@@ -5256,6 +5283,78 @@ void set_lime_server_and_curve_list(const int curveId, bctbx_list_t *managerList
 	set_lime_server_and_curve_list_tls(curveId, managerList, FALSE, FALSE);
 }
 
+LinphoneNatPolicy *get_nat_policy_for_call(LinphoneCoreManager *mgr, LinphoneCall *call) {
+	const LinphoneCallParams *call_params = linphone_call_get_params(call);
+	const LinphoneAccount *account = linphone_call_params_get_account(call_params);
+	const LinphoneAccountParams *account_params = linphone_account_get_params(account);
+	LinphoneNatPolicy *account_nat_policy = linphone_account_params_get_nat_policy(account_params);
+	LinphoneNatPolicy *core_nat_policy = linphone_core_get_nat_policy(mgr->lc);
+	return (account_nat_policy) ? account_nat_policy : core_nat_policy;
+}
+
+void enable_stun_in_mgr(LinphoneCoreManager *mgr,
+                        const bool_t account_enable_stun,
+                        const bool_t account_enable_ice,
+                        const bool_t core_enable_stun,
+                        const bool_t core_enable_ice) {
+	const bctbx_list_t *accounts = linphone_core_get_account_list(mgr->lc);
+	for (const bctbx_list_t *account_it = accounts; account_it != NULL; account_it = account_it->next) {
+		LinphoneAccount *account = (LinphoneAccount *)(bctbx_list_get_data(account_it));
+		enable_stun_in_account(mgr, account, account_enable_stun, account_enable_ice);
+	}
+	enable_stun_in_core(mgr, core_enable_stun, core_enable_ice);
+	linphone_core_manager_wait_for_stun_resolution(mgr);
+}
+
+void enable_stun_in_account(LinphoneCoreManager *mgr,
+                            LinphoneAccount *account,
+                            const bool_t enable_stun,
+                            const bool_t enable_ice) {
+	LinphoneCore *lc = mgr->lc;
+	LinphoneNatPolicy *nat_policy = NULL;
+	LinphoneNatPolicy *core_nat_policy = linphone_core_get_nat_policy(lc);
+	const LinphoneAccountParams *account_params = linphone_account_get_params(account);
+	LinphoneNatPolicy *account_nat_policy = linphone_account_params_get_nat_policy(account_params);
+	char *stun_server = NULL;
+	char *stun_server_username = NULL;
+
+	if (account_nat_policy != NULL) {
+		nat_policy = linphone_nat_policy_ref(account_nat_policy);
+	} else if (core_nat_policy != NULL) {
+		nat_policy = linphone_nat_policy_ref(core_nat_policy);
+	}
+
+	if (nat_policy) {
+		stun_server = ms_strdup(linphone_nat_policy_get_stun_server(nat_policy));
+		stun_server_username = ms_strdup(linphone_nat_policy_get_stun_server_username(nat_policy));
+		linphone_nat_policy_clear(nat_policy);
+	} else {
+		nat_policy = linphone_core_create_nat_policy(lc);
+		stun_server = ms_strdup(linphone_core_get_stun_server(lc));
+	}
+
+	linphone_nat_policy_enable_stun(nat_policy, enable_stun);
+
+	if (enable_ice) {
+		linphone_nat_policy_enable_ice(nat_policy, TRUE);
+	}
+
+	if (stun_server_username != NULL) {
+		linphone_nat_policy_set_stun_server_username(nat_policy, stun_server_username);
+		ms_free(stun_server_username);
+	}
+	if (stun_server != NULL) {
+		linphone_nat_policy_set_stun_server(nat_policy, stun_server);
+		ms_free(stun_server);
+	}
+
+	LinphoneAccountParams *new_account_params = linphone_account_params_clone(account_params);
+	linphone_account_params_set_nat_policy(new_account_params, nat_policy);
+	linphone_account_set_params(account, new_account_params);
+	linphone_account_params_unref(new_account_params);
+	linphone_nat_policy_unref(nat_policy);
+}
+
 void enable_stun_in_core(LinphoneCoreManager *mgr, const bool_t enable_stun, const bool_t enable_ice) {
 	LinphoneCore *lc = mgr->lc;
 	LinphoneNatPolicy *nat_policy = linphone_core_get_nat_policy(lc);
diff --git a/tester/tester.cpp b/tester/tester.cpp
index 00ffc813d3c795ff67faeb405dfd443d688fe1e3..633e0d5ea6a9f1c2d775401dba1df9973cfbf05c 100644
--- a/tester/tester.cpp
+++ b/tester/tester.cpp
@@ -18,20 +18,37 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <array>
 #include <exception>
 #include <string>
+#include <vector>
 
+#include <bctoolbox/crypto.h>
 #include <bctoolbox/utils.hh>
+#include <bctoolbox/vfs_encrypted.hh>
 #include <belr/grammarbuilder.h>
 
 #ifdef HAVE_SOCI
-#include <soci/backend-loader.h>
 #include <soci/soci.h>
-#endif
+#endif // HAVE_SOCI
 
 #include "liblinphone_tester.h"
 #include "logger/logger.h"
 
+#ifdef HAVE_LIME_X3DH
+#include "chat/encryption/lime-x3dh-encryption-engine.h"
+#endif // HAVE_LIME_X3DH
+
+bool is_filepath_encrypted(const char *filepath) {
+	bool ret = false;
+	auto fp = bctbx_file_open(&bctoolbox::bcEncryptedVfs, filepath, "r");
+	if (fp != NULL) {
+		ret = (bctbx_file_is_encrypted(fp) == TRUE);
+		bctbx_file_close(fp);
+	}
+	return ret;
+}
+
 /* */
 #ifndef _MSC_VER
 #pragma GCC diagnostic push
@@ -44,7 +61,7 @@ void lime_delete_DRSessions(const char *limedb) {
 		// Delete all sessions from the DR_sessions table
 		sql << "DELETE FROM DR_sessions;";
 	} catch (std::exception &e) { // swallow any error on DB
-		lWarning() << "Cannot delete DRSessions in base " << limedb << ". Error is " << e.what();
+		lWarning() << "Cannot delete DRSessions in database " << limedb << ". Error is " << e.what();
 	}
 #endif
 }
@@ -56,7 +73,8 @@ void lime_setback_usersUpdateTs(const char *limedb, int days) {
 		// Set back in time the users updateTs by the given number of days
 		sql << "UPDATE Lime_LocalUsers SET updateTs = date (updateTs, '-" << days << " day');";
 	} catch (std::exception &e) { // swallow any error on DB
-		lWarning() << "Cannot setback in time the lime users update ts on base " << limedb << ". Error is " << e.what();
+		lWarning() << "Cannot setback in time the lime users update ts on database " << limedb << ". Error is "
+		           << e.what();
 	}
 #endif
 }
@@ -68,8 +86,38 @@ uint64_t lime_get_userUpdateTs(const char *limedb) {
 		// get the users updateTs in unixepoch form - we may have more than one, just return the first one
 		sql << "SELECT strftime('%s', updateTs) as t FROM Lime_LocalUsers LIMIT 1;", soci::into(ret);
 	} catch (std::exception &e) { // swallow any error on DB
-		lWarning() << "Cannot fetch the lime users update ts on base " << limedb << ". Error is " << e.what();
+		lWarning() << "Cannot fetch the lime users update ts on database " << limedb << ". Error is " << e.what();
+	}
+#endif
+	return ret;
+}
+
+char *lime_get_userIk(LinphoneCoreManager *mgr, char *gruu) {
+	char *ret = NULL;
+#ifdef HAVE_SOCI
+#ifdef HAVE_LIME_X3DH
+	const char *limedb = mgr->lime_database_path;
+	try {
+		soci::session sql("sqlite3", limedb); // open the DB
+		soci::blob ik_blob(sql);
+		const std::string userGruu(gruu);
+		sql << "SELECT Ik FROM Lime_LocalUsers WHERE UserId = :UserId LIMIT 1;", soci::into(ik_blob),
+		    soci::use(userGruu);
+		if (sql.got_data()) { // Found it, it is stored in one buffer Public || Private
+			std::array<unsigned char, BCTBX_EDDSA_448_PUBLIC_SIZE> ikRaw;
+			const size_t public_key_size = ik_blob.get_len() / 2;
+			ik_blob.read(0, (char *)(ikRaw.data()), public_key_size); // Read the public key
+			std::vector<uint8_t> ik(ikRaw.cbegin(), ikRaw.cbegin() + public_key_size);
+			std::string ikStr = LinphonePrivate::encodeBase64(ik);
+			if (!ikStr.empty()) {
+				ret = ms_strdup(ikStr.c_str());
+			}
+		}
+	} catch (std::exception &e) { // swallow any error on DB
+		lWarning() << "Cannot fetch the lime users to get the Identity Key value on database " << limedb
+		           << ". Error is " << e.what();
 	}
+#endif // HAVE_LIME_X3DH
 #endif
 	return ret;
 }
diff --git a/tester/tunnel_tester.c b/tester/tunnel_tester.c
index f45ebc0ef66b3fc8ab3dc6a267db73204ec6e08a..0daac5dde6d6a947339f21d0ef5a4ac417bdca1c 100644
--- a/tester/tunnel_tester.c
+++ b/tester/tunnel_tester.c
@@ -65,7 +65,8 @@ static void call_with_tunnel_base(LinphoneTunnelMode tunnel_mode,
 		LinphoneCall *pauline_call, *marie_call;
 		LinphoneProxyConfig *proxy = linphone_core_get_default_proxy_config(pauline->lc);
 		LinphoneAddress *server_addr = linphone_address_new(linphone_proxy_config_get_server_addr(proxy));
-		LinphoneAddress *route = linphone_address_new(linphone_proxy_config_get_route(proxy));
+		const char *route_str = linphone_proxy_config_get_route(proxy);
+		LinphoneAddress *route = linphone_address_new(route_str);
 		LinphoneTunnel *tunnel = NULL;
 		char tunnel_ip[64];
 		char *public_ip = NULL, *public_ip2 = NULL;
diff --git a/tester/vfs-encryption-tester.cpp b/tester/vfs-encryption-tester.cpp
index 0be50f2263f96226516d6eb6dcff3f64403d1586..510ca0d432ee0a0ece13a65999804d5876291410 100644
--- a/tester/vfs-encryption-tester.cpp
+++ b/tester/vfs-encryption-tester.cpp
@@ -27,16 +27,6 @@
 #include <fstream>
 #include <iostream>
 
-static bool is_encrypted(const char *filepath) {
-	bool ret = false;
-	auto fp = bctbx_file_open(&bctoolbox::bcEncryptedVfs, filepath, "r");
-	if (fp != NULL) {
-		ret = (bctbx_file_is_encrypted(fp) == TRUE);
-		bctbx_file_close(fp);
-	}
-	return ret;
-}
-
 static void enable_encryption(const uint16_t encryptionModule, const bool encryptDbJournal = true) {
 	// enable encryption. The call to linphone_factory_set_vfs_encryption will set the VfsEncryption class callback
 	if (encryptionModule == LINPHONE_VFS_ENCRYPTION_PLAIN) {
@@ -126,15 +116,15 @@ static void register_user(const uint16_t encryptionModule, const char *random_id
 
 	// check the linphone dbs and local_rc are encrypted or not
 	if (encryptionModule == LINPHONE_VFS_ENCRYPTION_PLAIN) {
-		BC_ASSERT_FALSE(is_encrypted(linphone_db));
-		BC_ASSERT_FALSE(is_encrypted(lime_db));
-		BC_ASSERT_FALSE(is_encrypted(zrtp_secrets_db));
-		BC_ASSERT_FALSE(is_encrypted(localRc));
+		BC_ASSERT_FALSE(is_filepath_encrypted(linphone_db));
+		BC_ASSERT_FALSE(is_filepath_encrypted(lime_db));
+		BC_ASSERT_FALSE(is_filepath_encrypted(zrtp_secrets_db));
+		BC_ASSERT_FALSE(is_filepath_encrypted(localRc));
 	} else {
-		BC_ASSERT_TRUE(is_encrypted(linphone_db));
-		BC_ASSERT_TRUE(is_encrypted(lime_db));
-		BC_ASSERT_TRUE(is_encrypted(zrtp_secrets_db));
-		BC_ASSERT_TRUE(is_encrypted(localRc));
+		BC_ASSERT_TRUE(is_filepath_encrypted(linphone_db));
+		BC_ASSERT_TRUE(is_filepath_encrypted(lime_db));
+		BC_ASSERT_TRUE(is_filepath_encrypted(zrtp_secrets_db));
+		BC_ASSERT_TRUE(is_filepath_encrypted(localRc));
 	}
 
 	if (createUsers == false) {
@@ -267,11 +257,11 @@ static void zrtp_call(const uint16_t encryptionModule,
 	end_call(marie, pauline);
 
 	if (encryptionModule == LINPHONE_VFS_ENCRYPTION_PLAIN) {
-		BC_ASSERT_FALSE(is_encrypted(marie_zidCache));
-		BC_ASSERT_FALSE(is_encrypted(pauline_zidCache));
+		BC_ASSERT_FALSE(is_filepath_encrypted(marie_zidCache));
+		BC_ASSERT_FALSE(is_filepath_encrypted(pauline_zidCache));
 	} else {
-		BC_ASSERT_TRUE(is_encrypted(marie_zidCache));
-		BC_ASSERT_TRUE(is_encrypted(pauline_zidCache));
+		BC_ASSERT_TRUE(is_filepath_encrypted(marie_zidCache));
+		BC_ASSERT_TRUE(is_filepath_encrypted(pauline_zidCache));
 	}
 
 	// cleaning
@@ -284,23 +274,23 @@ static void zrtp_call(const uint16_t encryptionModule,
 
 	// check the linphone dbs and local_rc are encrypted or not
 	if (encryptionModule == LINPHONE_VFS_ENCRYPTION_PLAIN) {
-		BC_ASSERT_FALSE(is_encrypted(marie_linphone_db));
-		BC_ASSERT_FALSE(is_encrypted(marie_lime_db));
-		BC_ASSERT_FALSE(is_encrypted(marie_zrtp_secrets_db));
-		BC_ASSERT_FALSE(is_encrypted(marie_rc));
-		BC_ASSERT_FALSE(is_encrypted(pauline_linphone_db));
-		BC_ASSERT_FALSE(is_encrypted(pauline_lime_db));
-		BC_ASSERT_FALSE(is_encrypted(pauline_zrtp_secrets_db));
-		BC_ASSERT_FALSE(is_encrypted(pauline_rc));
+		BC_ASSERT_FALSE(is_filepath_encrypted(marie_linphone_db));
+		BC_ASSERT_FALSE(is_filepath_encrypted(marie_lime_db));
+		BC_ASSERT_FALSE(is_filepath_encrypted(marie_zrtp_secrets_db));
+		BC_ASSERT_FALSE(is_filepath_encrypted(marie_rc));
+		BC_ASSERT_FALSE(is_filepath_encrypted(pauline_linphone_db));
+		BC_ASSERT_FALSE(is_filepath_encrypted(pauline_lime_db));
+		BC_ASSERT_FALSE(is_filepath_encrypted(pauline_zrtp_secrets_db));
+		BC_ASSERT_FALSE(is_filepath_encrypted(pauline_rc));
 	} else {
-		BC_ASSERT_TRUE(is_encrypted(marie_linphone_db));
-		BC_ASSERT_TRUE(is_encrypted(marie_lime_db));
-		BC_ASSERT_TRUE(is_encrypted(marie_zrtp_secrets_db));
-		BC_ASSERT_TRUE(is_encrypted(marie_rc));
-		BC_ASSERT_TRUE(is_encrypted(pauline_linphone_db));
-		BC_ASSERT_TRUE(is_encrypted(pauline_lime_db));
-		BC_ASSERT_TRUE(is_encrypted(pauline_zrtp_secrets_db));
-		BC_ASSERT_TRUE(is_encrypted(pauline_rc));
+		BC_ASSERT_TRUE(is_filepath_encrypted(marie_linphone_db));
+		BC_ASSERT_TRUE(is_filepath_encrypted(marie_lime_db));
+		BC_ASSERT_TRUE(is_filepath_encrypted(marie_zrtp_secrets_db));
+		BC_ASSERT_TRUE(is_filepath_encrypted(marie_rc));
+		BC_ASSERT_TRUE(is_filepath_encrypted(pauline_linphone_db));
+		BC_ASSERT_TRUE(is_filepath_encrypted(pauline_lime_db));
+		BC_ASSERT_TRUE(is_filepath_encrypted(pauline_zrtp_secrets_db));
+		BC_ASSERT_TRUE(is_filepath_encrypted(pauline_rc));
 	}
 
 	if (createUsers == false) { // we reused users, now clean the local files
@@ -493,12 +483,12 @@ static void file_transfer_test(const uint16_t encryptionModule,
 
 	// It shall be encrypted
 	if (encryptionModule == LINPHONE_VFS_ENCRYPTION_PLAIN) {
-		BC_ASSERT_FALSE(is_encrypted(receivePaulineFilepath));
+		BC_ASSERT_FALSE(is_filepath_encrypted(receivePaulineFilepath));
 	} else {
-		BC_ASSERT_TRUE(is_encrypted(receivePaulineFilepath));
+		BC_ASSERT_TRUE(is_filepath_encrypted(receivePaulineFilepath));
 	}
 	// but not the original file sent
-	BC_ASSERT_FALSE(is_encrypted(sendFilepath));
+	BC_ASSERT_FALSE(is_filepath_encrypted(sendFilepath));
 
 	// Get the file using the linphone content API
 	msg = pauline->stat.last_received_chat_message;
@@ -518,7 +508,7 @@ static void file_transfer_test(const uint16_t encryptionModule,
 		// get a plain version of the file
 		plainFilePath = linphone_content_export_plain_file(content);
 		// check it is plain and match the sent one
-		BC_ASSERT_FALSE(is_encrypted(plainFilePath));
+		BC_ASSERT_FALSE(is_filepath_encrypted(plainFilePath));
 		compare_files(plainFilePath, sendFilepath);
 		plainFilePath2 = linphone_content_export_plain_file(content);
 		// Make sure the second path returned is different from the first one as the file already exists
@@ -560,23 +550,23 @@ end:
 
 	// check the linphone dbs and local_rc are encrypted or not
 	if (encryptionModule == LINPHONE_VFS_ENCRYPTION_PLAIN) {
-		BC_ASSERT_FALSE(is_encrypted(marie_linphone_db));
-		BC_ASSERT_FALSE(is_encrypted(marie_lime_db));
-		BC_ASSERT_FALSE(is_encrypted(marie_zrtp_secrets_db));
-		BC_ASSERT_FALSE(is_encrypted(marie_rc));
-		BC_ASSERT_FALSE(is_encrypted(pauline_linphone_db));
-		BC_ASSERT_FALSE(is_encrypted(pauline_lime_db));
-		BC_ASSERT_FALSE(is_encrypted(pauline_zrtp_secrets_db));
-		BC_ASSERT_FALSE(is_encrypted(pauline_rc));
+		BC_ASSERT_FALSE(is_filepath_encrypted(marie_linphone_db));
+		BC_ASSERT_FALSE(is_filepath_encrypted(marie_lime_db));
+		BC_ASSERT_FALSE(is_filepath_encrypted(marie_zrtp_secrets_db));
+		BC_ASSERT_FALSE(is_filepath_encrypted(marie_rc));
+		BC_ASSERT_FALSE(is_filepath_encrypted(pauline_linphone_db));
+		BC_ASSERT_FALSE(is_filepath_encrypted(pauline_lime_db));
+		BC_ASSERT_FALSE(is_filepath_encrypted(pauline_zrtp_secrets_db));
+		BC_ASSERT_FALSE(is_filepath_encrypted(pauline_rc));
 	} else {
-		BC_ASSERT_TRUE(is_encrypted(marie_linphone_db));
-		BC_ASSERT_TRUE(is_encrypted(marie_lime_db));
-		BC_ASSERT_TRUE(is_encrypted(marie_zrtp_secrets_db));
-		BC_ASSERT_TRUE(is_encrypted(marie_rc));
-		BC_ASSERT_TRUE(is_encrypted(pauline_linphone_db));
-		BC_ASSERT_TRUE(is_encrypted(pauline_lime_db));
-		BC_ASSERT_TRUE(is_encrypted(pauline_zrtp_secrets_db));
-		BC_ASSERT_TRUE(is_encrypted(pauline_rc));
+		BC_ASSERT_TRUE(is_filepath_encrypted(marie_linphone_db));
+		BC_ASSERT_TRUE(is_filepath_encrypted(marie_lime_db));
+		BC_ASSERT_TRUE(is_filepath_encrypted(marie_zrtp_secrets_db));
+		BC_ASSERT_TRUE(is_filepath_encrypted(marie_rc));
+		BC_ASSERT_TRUE(is_filepath_encrypted(pauline_linphone_db));
+		BC_ASSERT_TRUE(is_filepath_encrypted(pauline_lime_db));
+		BC_ASSERT_TRUE(is_filepath_encrypted(pauline_zrtp_secrets_db));
+		BC_ASSERT_TRUE(is_filepath_encrypted(pauline_rc));
 	}
 
 	if (createUsers == false) { // we reused users, now clean the local files