네트워크 보안/네트워크

strongswan #4 charon에서 패킷 처리 (sender, receiver, processor)

uzguns 2024. 11. 9. 13:36

Charon 프로세스 시작

1. starter_start_charon()을 호출하여 fork()를 통해 Charon 프로세스를 시작한다.

 

2. starter_stroke_add_conn()을 통해 stroke_msg 객체(type을 STR_ADD_CONN으로 설정)를 생성하여 연결 설정 메시지를 준비한다.

  • URI를 /etc/ipsec.d/run/charon.ctl로 설정하고 스트림을 생성
  • stream_service_create_unix  호출하여 Unix 도메인 소켓을 생성하고 연결 

Charon 초기화

INIT(this,
    .public = {
        .initialize = _initialize,
        .start = _start,
        .load_loggers = _load_loggers,
        .set_default_loggers = _set_default_loggers,
        .set_level = _set_level,
        .bus = bus_create(),
        .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
    }
);
  • initialize, start, load_loggers, set_default_loggers, set_level과 같은 초기화 함수들이 설정
  • kernel, attributes, controller, backend 등의 매니저를 생성하고, 이를 public 구조체에 할당하여 Charon의 각 기능을 초기화

 

플러그인 로드

 

Strongswan에는 구성 단계(strongswan.conf 실행) 중에 활성화하거나 비활성화할 수 있는 많은 플러그인이 있다. 컴파일 후 플러그인은 동적으로 로드된다.

 

stroke 플러그인을 예로 들어보자.

 

charon 프로세스는 시작 시 charon->initialize() 함수에서 strongswan.conf 파일을 읽어 플러그인 목록을 파싱한다.

	/* initialize daemon */
	if (!charon->initialize(charon,
				lib->settings->get_str(lib->settings, "charon.load", PLUGINS)))
	{
		DBG1(DBG_DMN, "initialization failed - aborting charon");
		goto deinit;
	}

 

charon이 strongswan.conf 파일의 load 항목을 확인하여 나열된 플러그인을 동적으로 로드하게된다.

Charon

초기화

1. library_init() 호출

2. libcharon_init() 호출

3. command line argument 파싱

4. charon->initialize() 호출

  • charon->initialize() -> lib->plugins->load()
  • initialize() 함수가 호출되면 load_plugins() 함수를 통해 모든 플러그인을 로드한다.
// charon/charon.c main
charon->initialize(charon, lib->settings->get_str(lib->settings, "charon.load", PLUGINS))

// libcharon/daemon.c initialize
if (!lib->plugins->load(lib->plugins, plugins))
{
    return FALSE;
}

 

Plugin Load

  • load_plugins() -> load_features() -> load_provided() -> load_feature() -> plugin_feature_load() -> reg->arg.cb.f()의 호출 흐름에서 sender_receiver_cb()와 sa_managers_cb() 콜백 함수가 호출된다.
  • load_plugins ()
    • 각 플러그인을 검색하여 find_plugin() 함수를 통해 찾고, 플러그인 라이브러리 파일 libstrongswan-%s.so를 로드한다.
    • %s는 플러그인의 이름을 나타냄, stroke 플러그인이라면 파일 이름은 libstrongswan-stroke.so
  • load_plugin()
    • load_plugin()은 dlopen()을 호출하여 플러그인 라이브러리 파일을 열고, %s-plugin-create()라는 생성자 함수를 호출하여 플러그인 객체를 만든다.
  • register_features()
    • 각 플러그인은 get_features() 함수를 통해 해당 플러그인이 제공하는 기능 정보를 가져온다.
    • 이를 통해 각 플러그인의 특징(feature) 정보를 등록한다.
  count = entry->plugin->get_features(entry->plugin, &feature);
  for (i = 0; i < count; i++)
  {
    switch (feature->kind)
    {
      case FEATURE_PROVIDE:
        lookup.feature = feature;
        registered = this->features->get(this->features, &lookup);
        if (!registered)
        {
          INIT(registered,
            .feature = feature,
            .plugins = linked_list_create(),
          );
          this->features->put(this->features, registered, registered);
        }
        INIT(provided,
          .entry = entry,
          .feature = feature,
          .reg = reg,
          .dependencies = count - i,
        );
        registered->plugins->insert_last(registered->plugins,
                         provided);
        entry->features->insert_last(entry->features, provided);
        break;
      case FEATURE_REGISTER:
      case FEATURE_CALLBACK:
        reg = feature;
        break;
      default:
        break;
    }
    feature++;
  }
  • load_features()
    • 각 플러그인이 제공하는 기능을 로드한다.
    • load_provided()와 load_feature()를 거쳐 plugin_feature_load() 함수가 호출된다.
    • plugin_feature_load()는 플러그인 등록 정보에서 콜백 함수(reg->arg.cb.f())를 호출한다.

예를 들어, stroke 플러그인의 경우 register_stroke() 콜백 함수가 호출된다.

register_stroke()는 stroke_socket_create() 함수를 통해 starter와 통신하기 위한 Unix 소켓을 생성한다.

이후 this->service = lib->streams->create_service를 호출하여 stream_service 객체를 생성하고, this->service->on_accept()을 호출하여 on_accept() 함수를 콜백으로 설정한다.

on_accept() 함수는 lib->watcher를 사용해 processor->job[] 대기열에 작업을 추가하고, 스레드에서 실행되도록 대기한다.

 

sender_receiver_cb() 함수는 receiver와 sender를 생성한다.

  • receiver_create()
    • 수신 전용 객체인 private_callback_job_t를 생성
    • callback job으로 receive_packets() 함수가 설정
private_callback_job_t *receiver = receiver_create();
receiver->job = (callback_job_cb_t)receive_packets;

 

  • sender_create()
    • 송신 전용 객체인 private_sender_t를 생성
    • callback job으로 send_packets() 함수가 설정
private_sender_t *sender = sender_create();
sender->job = (callback_job_cb_t)send_packets;

 

이렇게 생성된 receiver와 sender의 콜백 함수는 process->set_threads() 호출 시 스레드에 할당된다.

이후 생성된 스레드가 receive_packets()와 send_packets()를 실행한다.

 

 

References