Strongswan에서의 Taskmanager에 관한 글
2022. 3. 10. 15:51Strongswan의 Taskmanager의 API들 (Strongswan 5.8.2 버전 기준)
(src/libcharon/sa/task_manager.h)
먼저, Task manager는 task를 다루고, message exchange들을 처리합니다.
들어오는 요청들에 대해, task manager는 수요에 의해 새로운 task들을 만듭니다. 그리고,
모든 avilable(현재 가용한)한 테스크들을 통해 요청들은 처리합니다.
각각의 task는 요청을 검사하고, 응답에 필요에 따라 페이로드를 추가합니다.
나가는 요청들에 대해, task manager는 그것을 빌드하기 위해 task들을 통해 요청을 전달합니다. 응답은 완성되기 위해 각각의 task에 의해 처리됩니다.
task manager는 task를 저장할 internal queue를 가지고 있습니다. 그 테스크들은 완료가 되어야만 하는 것들이죠.
초기 IKE_SA 셋업을 위해, 각각의 테스크들은 큐에 저장됩니다:
unauthenticated IKE_SA 셋업을 위한 하나,
authentication을 위한 하나,
CHILD_SA 셋업을 위한 하나 그리고,
아마도 Virtual IP assignment(할당)을 위에 하나
가 될 겁니다.
Task manager는 또한, 재전송을 책임집니다. 그것은 backoff 알고리즘을 사용합니다.
timeout(제한시간)은 RETRANSMIT_TIMEOUT * (RETRANSMIT_BASE ** try) 계산법을 사용합니다.
RETRANSMIT_TRIES에 도달하게 된다면, 재전송을 포기합니다.
4s의 초기 TIMEOUT을 사용하면서, 1.8의 BASE, 그리고 5 TRIES는 주어져있습니다.
| relative | absolute
---------------------------------------------------------
4s * (1.8 ** 0) = 4s 4s
4s * (1.8 ** 1) = 7s 11s
4s * (1.8 ** 2) = 13s 24s
4s * (1.8 ** 3) = 23s 47s
4s * (1.8 ** 4) = 42s 89s
4s * (1.8 ** 5) = 76s 165s
task_manager_t {
status_t (*process_message) (task_manager_t *this, message_t *message);
status_t (*initiate) (task_manager_t *this);
void (*queue_task)(task_manager_t *this, task_t *task);
void (*queue_task_delayed)(task_manager_t *this, task_t *task, uint32_t delay);
void (*queue_ike)(task_manager_t *this);
void (*queue_ike_rekey)(task_manager_t *this);
void (*queue_ike_reauth)(task_manager_t *this);
void (*queue_mobike)(task_manager_t *this, bool roam, bool address);
void (*queue_ike_delete)(task_manager_t *this);
void (*queue_child)(task_manager_t *this, child_cfg_t *cfg, uint32_t reqid, traffic_selector_t *tsi, traffic_selector_t *tsr);
void (*queue_child_rekey)(task_manager_t *this, protocol_id_t protocol, uint32_t spi);
void (*queue_child_delete)(task_manager_t *this, protocol_id_t protocol, uint32_t spi, bool expired);
void (*queue_dpd)(task_manager_t *this);
status_t (*retransmit) (task_manager_t *this, uint32_t message_id);
void (*adopt_tasks) (task_manager_t *this, task_manager_t *other);
void (*incr_mid)(task_manager_t *this, bool initiate);
uint32_t (*get_mid)(task_manager_t *this, bool initiate);
void (*reset)(task_manager_t *this, uint32_t initiate, uint32_t respond);
bool (*busy) (task_manager_t *this);
enumerator_t* (*create_task_enumerator)(task_manager_t *this, task_queue_t queue);
void (*remove_task)(task_manager_t *this, enumerator_t *enumerator);
void (*flush)(task_manager_t *this);
void (*flush_queue)(task_manager_t *this, task_queue_t queue);
void (*destroy) (task_manager_t *this);
}
status_t (*process_message) (task_manager_t *this, message_t *message); | Process an incoming message. |
status_t (*initiate) (task_manager_t *this); | Initiate an exchange with the currently queued tasks. |
void (*queue_task)(task_manager_t *this, task_t *task); | Queue a task in the manager. |
void (*queue_task_delayed)(task_manager_t *this, task_t *task, uint32_t delay); | |
void (*queue_ike)(task_manager_t *this); | Queue IKE_SA establishing tasks. |
void (*queue_ike_rekey)(task_manager_t *this); | Queue IKE_SA rekey tasks. |
void (*queue_ike_reauth)(task_manager_t *this); | Queue IKE_SA reauth tasks. |
void (*queue_mobike)(task_manager_t *this, bool roam, bool address); | |
void (*queue_ike_delete)(task_manager_t *this); | |
void (*queue_child)(task_manager_t *this, child_cfg_t *cfg, uint32_t reqid, traffic_selector_t *tsi, traffic_selector_t *tsr); | |
void (*queue_child_rekey)(task_manager_t *this, protocol_id_t protocol, uint32_t spi); | |
void (*queue_child_delete)(task_manager_t *this, protocol_id_t protocol, uint32_t spi, bool expired); | |
void (*queue_dpd)(task_manager_t *this); | |
status_t (*retransmit) (task_manager_t *this, uint32_t message_id); | |
void (*adopt_tasks) (task_manager_t *this, task_manager_t *other); | |
void (*incr_mid)(task_manager_t *this, bool initiate); | |
uint32_t (*get_mid)(task_manager_t *this, bool initiate); | |
void (*reset)(task_manager_t *this, uint32_t initiate, uint32_t respond); | |
bool (*busy) (task_manager_t *this); | |
enumerator_t* (*create_task_enumerator)(task_manager_t *this, task_queue_t queue); | |
void (*remove_task)(task_manager_t *this, enumerator_t *enumerator); | Remove the task the given enumerator points to. @note This should be used with caution, in partciular, for tasks in the active and passive queues. @param enumerator enumerator created with the method above |
void (*flush)(task_manager_t *this); | |
void (*flush_queue)(task_manager_t *this, task_queue_t queue); | |
void (*destroy) (task_manager_t *this); |
task_manager_v2.c
(경로: src/libcharon/sa/ikev2/task_manager_v2.c)
static status_t process_request(..) 함수는 handle an incoming request message이다.
- 함수호출 다이어그램
ike_sa측에서 process_message() 부름 -> task_manager에서 process_message() -> process_request() (*1) -> build_response() -> 각 task->build 후 상태 return -> return -> return(process_message) ->
(※ 추가로 알면 좋을 것, task_manager에서 process_message() 말고, process_response를 호출할 수 있는데, 무조건 둘중의 하나만 호출하게 된다. 그것을 구분하는 건, msg->get_reqest의 값이 true면 process_message이고, false이면 process_response 쪽으로 호출된다. )
ike_sa <-> task_manager
$ grep -nir "ike_sa->process_message" .
./libcharon/tests/utils/exchange_test_helper.c:225: status = ike_sa->process_message(ike_sa, message);
./libcharon/processing/jobs/process_message_job.c:74: if (ike_sa->process_message(ike_sa, this->message) == DESTROY_ME)
./libcharon/sa/ikev2/task_manager_v2.c:1295: status = this->ike_sa->process_message(this->ike_sa, reassembled);
execute() -> (ike_sa->process_message()) 호출
그 전에, process_message_job이 만들어지는 부분을 확인해보자.
process_message_job_create() 함수가 부를 때, job_interface의 excute에 execute()함수를 등록한다.
recevie_packets() -> process_message_job_create() (lib->scheduler->schedule_job_mx(lib->scheduler, process_message_job_create(message), this->receive_delay) )
그렇다면 receive_packets 함수는 어떻게 불리우게 될까?
receiver_create()함수를 호출 할 때, 해당 함수를 등록한다.
- 함수호출 순서도
receiver_create() ->
lib->processor->queue_job(lib->processor,
callback_job_create_with_prio((callback_job_cb_t)receive_packets, this, NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
그렇다면, receiver_create는 언제 호출될까? (키워드 : receiver_create())
./libcharon/daemon.c 에서 sender_receiver_cb()를 호출될 때 불리운 후, daemon에 저장한다.
- 함수호출 순서도
sender_receiver_cb() -> receiver_create() (자세히, this->public.receiver = receiver_create();
- initialize() 함수에서 sender_receiver_cb를 등록
- daemon_create() 함수에서 initialize() 함수를 등록
- libcharon_init() 함수 안에서 daemon_create함수를 호출 (this = daemon_create();) (in /libcharon/daemon.c)
- charon.c의 main함수에서 libcharon_init()를 호출
여튼 요약하면, 패킷을 받앗을 때, recevie_packets()을 호출하여,
Strongswan에서의 ike_sa 단계에서, process_message를 호출하도록 처리한다. (ike_sa->process_message())
Build_i Build_r인지 확인
./libcharon/sa/ikev2/task_manager_v2.c
ike_init_create을 검색해보자. initiate가
true면 process 및 build_i
그게 아니면 process 및 build_r
./libcharon/sa/ikev2/task_manager_v2.c:990: task = (task_t*)ike_init_create(this->ike_sa, FALSE, NULL);
./libcharon/sa/ikev2/task_manager_v2.c:1820: queue_task(this, (task_t*)ike_init_create(this->ike_sa, TRUE, NULL));
990줄은 process_request() 함수 (들어오는 요청 메시지를 처리함)
1820줄은 queue_ike() 함수 (IKE 설립 task들을 queue함)
-----------------------------------------------------------------------------
부록
중간에서, execute()에 대한 언급이 있었는데, 이것을 조금 더 자세히 살펴보자,
./libstrongswan/processing/jobs/job.h:145: * expected to return from execute() itself (i.e. the thread won't be
./libstrongswan/processing/jobs/job.h:151: * @note This method could be called even before execute() has been called.
./libstrongswan/processing/processor.c:235: requeue = worker->job->execute(worker->job);
./libstrongswan/processing/processor.c:424: job->execute(job);
process에서 job->execute를 호출하는 것을 볼 수 있다.
1) process_jobs() (while) -> process_job() -> worker->job->execute();
2) execute_job() -> job->execute(job);
---->>> 이거는 processor_create() 호출 시, execute_job에 execute_job 함수가 등록되어 있음.
process_jobs는 아래와 같이 설명이 되어있다.
Process queued jobs, called by the worker threads.
그만 알아보자
------------------------------------------------------
(*1) process_request에 관해서,
이 함수 안에서, 메시지의 어떤 타입이냐에 따라서, array_insert를 할지 말지를 결정한다.
예를 들어, 해당 메시지 타입이;
IKE_SA_INIT 인 경우,
CREATE_CHILD_SA 인 경우,
INFORMATIONAL 인 경우,
ME_CONNECT 인 경우,
나머지 경우
이렇게 5가지 경우에 따라서, 어떤 테스크를 생성한 후, array_insert를 한 건지를 결정한다.
우선, 해당 메시지 타입에 따라서 어떤 task들을 insert 할 지를 결정한다.
/**
* handle an incoming request message
*/
static status_t process_request(private_task_manager_t *this,
message_t *message)
{
enumerator_t *enumerator;
task_t *task = NULL;
payload_t *payload;
notify_payload_t *notify;
delete_payload_t *delete;
ike_sa_state_t state;
if (array_count(this->passive_tasks) == 0)
{ /* create tasks depending on request type, if not already some queued */
state = this->ike_sa->get_state(this->ike_sa);
switch (message->get_exchange_type(message))
{
case IKE_SA_INIT:
{
task = (task_t*)ike_vendor_create(this->ike_sa, FALSE);
array_insert(this->passive_tasks, ARRAY_TAIL, task);
task = (task_t*)ike_init_create(this->ike_sa, FALSE, NULL);
array_insert(this->passive_tasks, ARRAY_TAIL, task);
task = (task_t*)ike_natd_create(this->ike_sa, FALSE);
array_insert(this->passive_tasks, ARRAY_TAIL, task);
task = (task_t*)ike_cert_pre_create(this->ike_sa, FALSE);
array_insert(this->passive_tasks, ARRAY_TAIL, task);
#ifdef ME
task = (task_t*)ike_me_create(this->ike_sa, FALSE);
array_insert(this->passive_tasks, ARRAY_TAIL, task);
#endif /* ME */
task = (task_t*)ike_auth_create(this->ike_sa, FALSE);
array_insert(this->passive_tasks, ARRAY_TAIL, task);
task = (task_t*)ike_cert_post_create(this->ike_sa, FALSE);
array_insert(this->passive_tasks, ARRAY_TAIL, task);
task = (task_t*)ike_config_create(this->ike_sa, FALSE);
array_insert(this->passive_tasks, ARRAY_TAIL, task);
task = (task_t*)child_create_create(this->ike_sa, NULL, FALSE,
NULL, NULL);
array_insert(this->passive_tasks, ARRAY_TAIL, task);
task = (task_t*)ike_auth_lifetime_create(this->ike_sa, FALSE);
array_insert(this->passive_tasks, ARRAY_TAIL, task);
task = (task_t*)ike_mobike_create(this->ike_sa, FALSE);
array_insert(this->passive_tasks, ARRAY_TAIL, task);
break;
}
case CREATE_CHILD_SA:
{ /* FIXME: we should prevent this on mediation connections */
bool notify_found = FALSE, ts_found = FALSE;
if (state == IKE_CREATED ||
state == IKE_CONNECTING)
{
DBG1(DBG_IKE, "received CREATE_CHILD_SA request for "
"unestablished IKE_SA, rejected");
return FAILED;
}
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
switch (payload->get_type(payload))
{
case PLV2_NOTIFY:
{ /* if we find a rekey notify, its CHILD_SA rekeying */
notify = (notify_payload_t*)payload;
if (notify->get_notify_type(notify) == REKEY_SA &&
(notify->get_protocol_id(notify) == PROTO_AH ||
notify->get_protocol_id(notify) == PROTO_ESP))
{
notify_found = TRUE;
}
break;
}
case PLV2_TS_INITIATOR:
case PLV2_TS_RESPONDER:
{ /* if we don't find a TS, its IKE rekeying */
ts_found = TRUE;
break;
}
default:
break;
}
}
enumerator->destroy(enumerator);
if (ts_found)
{
if (notify_found)
{
task = (task_t*)child_rekey_create(this->ike_sa,
PROTO_NONE, 0);
}
else
{
task = (task_t*)child_create_create(this->ike_sa, NULL,
FALSE, NULL, NULL);
}
}
else
{
task = (task_t*)ike_rekey_create(this->ike_sa, FALSE);
}
array_insert(this->passive_tasks, ARRAY_TAIL, task);
break;
}
case INFORMATIONAL:
{
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
switch (payload->get_type(payload))
{
case PLV2_NOTIFY:
{
notify = (notify_payload_t*)payload;
if (state == IKE_REKEYED)
{
DBG1(DBG_IKE, "received unexpected notify %N "
"for rekeyed IKE_SA, ignored",
notify_type_names,
notify->get_notify_type(notify));
break;
}
switch (notify->get_notify_type(notify))
{
case ADDITIONAL_IP4_ADDRESS:
case ADDITIONAL_IP6_ADDRESS:
case NO_ADDITIONAL_ADDRESSES:
case UPDATE_SA_ADDRESSES:
case NO_NATS_ALLOWED:
case UNACCEPTABLE_ADDRESSES:
case UNEXPECTED_NAT_DETECTED:
case COOKIE2:
case NAT_DETECTION_SOURCE_IP:
case NAT_DETECTION_DESTINATION_IP:
task = (task_t*)ike_mobike_create(
this->ike_sa, FALSE);
break;
case AUTH_LIFETIME:
task = (task_t*)ike_auth_lifetime_create(
this->ike_sa, FALSE);
break;
case AUTHENTICATION_FAILED:
/* initiator failed to authenticate us.
* We use ike_delete to handle this, which
* invokes all the required hooks. */
task = (task_t*)ike_delete_create(
this->ike_sa, FALSE);
break;
case REDIRECT:
task = (task_t*)ike_redirect_create(
this->ike_sa, NULL);
break;
case IKEV2_MESSAGE_ID_SYNC:
task = (task_t*)ike_mid_sync_create(
this->ike_sa);
break;
default:
break;
}
break;
}
case PLV2_DELETE:
{
delete = (delete_payload_t*)payload;
if (delete->get_protocol_id(delete) == PROTO_IKE)
{
task = (task_t*)ike_delete_create(this->ike_sa,
FALSE);
}
else
{
task = (task_t*)child_delete_create(this->ike_sa,
PROTO_NONE, 0, FALSE);
}
break;
}
default:
break;
}
if (task)
{
break;
}
}
enumerator->destroy(enumerator);
if (task == NULL)
{
task = (task_t*)ike_dpd_create(FALSE);
}
array_insert(this->passive_tasks, ARRAY_TAIL, task);
break;
}
#ifdef ME
case ME_CONNECT:
{
task = (task_t*)ike_me_create(this->ike_sa, FALSE);
array_insert(this->passive_tasks, ARRAY_TAIL, task);
}
#endif /* ME */
default:
break;
}
}
enumerator = array_create_enumerator(this->passive_tasks);
while (enumerator->enumerate(enumerator, &task))
{
if (!task->pre_process)
{
continue;
}
switch (task->pre_process(task, message))
{
case SUCCESS:
break;
case FAILED:
default:
/* just ignore the message */
DBG1(DBG_IKE, "ignore invalid %N request",
exchange_type_names, message->get_exchange_type(message));
enumerator->destroy(enumerator);
switch (message->get_exchange_type(message))
{
case IKE_SA_INIT:
/* no point in keeping the SA when it was created with
* an invalid IKE_SA_INIT message */
return DESTROY_ME;
default:
/* remove tasks we queued for this request */
flush_queue(this, TASK_QUEUE_PASSIVE);
/* fall-through */
case IKE_AUTH:
return NEED_MORE;
}
case DESTROY_ME:
/* critical failure, destroy IKE_SA */
enumerator->destroy(enumerator);
return DESTROY_ME;
}
}
enumerator->destroy(enumerator);
/* let the tasks process the message */
enumerator = array_create_enumerator(this->passive_tasks);
while (enumerator->enumerate(enumerator, (void*)&task))
{
switch (task->process(task, message))
{
case SUCCESS:
/* task completed, remove it */
array_remove_at(this->passive_tasks, enumerator);
task->destroy(task);
break;
case NEED_MORE:
/* processed, but task needs at least another call to build() */
break;
case FAILED:
default:
charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
/* FALL */
case DESTROY_ME:
/* critical failure, destroy IKE_SA */
array_remove_at(this->passive_tasks, enumerator);
enumerator->destroy(enumerator);
task->destroy(task);
return DESTROY_ME;
}
}
enumerator->destroy(enumerator);
return build_response(this, message);
}
'오픈소스(opensource) > Strongswan' 카테고리의 다른 글
[Strongswan] 주요 객체들 (0) | 2022.04.20 |
---|---|
Strongswan Structure/View (Strongswan의 구조도) (0) | 2022.04.04 |
[Strongswan] Strongswan 구조도 (0) | 2022.03.10 |
Make 명령어를 이용해 Strongswan 빌드해보기 (실행해보기) (0) | 2021.04.19 |