博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux下strongswan workflow
阅读量:4056 次
发布时间:2019-05-25

本文共 11529 字,大约阅读时间需要 38 分钟。

Strongswan starter is the excutable program, located at /usr/libexec/ipsec/starter.(strongswan/src/starter/starter.c)

Usage: starter [--nofork] [--auto-update <sec>]

[--debug|--debug-more|--debug-all|--nolog]

[--attach-gdb] [--daemon <name>]

[--conf <path to ipsec.conf>]

有几个program.

starter(strongswan/src/starter/)

charon(strongswan/src/charon/)

先来介绍starter

starter

  1. 调用library_init()

创建private_library_t、printf_hook_t,初始化成员(比如创建private_settings_t等),调用this->public.settings->load_files(this->public.settings, this->public.conf, FALSE),这里的this->public.conf为strongswan.conf

#file strongswan.conf

charon {

            load_modular = yes

            plugins {

                         include strongswan.d/charon/*.conf

            }

}

 

include strongswan.d/*.conf

这里load_files的操作会加载strongswan.conf以及strongswan.d/下的所有*.conf文件,start.conf就在这里加载。

#file starter.conf

starter {

 

    # Location of the ipsec.conf file

    # config_file = ${sysconfdir}/ipsec.conf

 

    # Disable charon plugin load option warning.

    # load_warning = yes

 

}

#streams创建

private_library_t *this;

this->public.streams = stream_manager_create();

这里创建private_stream_manager_t对象。

add_stream()创建stream_entry_t对象,并插入到this->services.

add_service() 创建service_entry_t对象,并插入到this->services.

  1. 解析命令行参数

--daemon:  指定daemon,默认为charon

--conf: 指定ipsec 配置文件,默认为strongswan.d/starter.conf中的config_file指示的ipsec.conf文件。

Ipsec.conf文件的内容如下(sample):

# ipsec.conf - strongSwan IPsec configuration file

 

# basic configuration

 

config setup

        # strictcrlpolicy=yes

        # uniqueids = no

 

# Add connections here.

 

# Sample VPN connections

 

#conn sample-self-signed

#      leftsubnet=10.1.0.0/16

#      leftcert=selfCert.der

#      leftsendcert=never

#      right=192.168.0.2

#      rightsubnet=10.2.0.0/16

#      rightcert=peerCert.der

#      auto=start

 

#conn sample-with-ca-cert

#      leftsubnet=10.1.0.0/16

#      leftcert=myCert.pem

#      right=192.168.0.2

#      rightsubnet=10.2.0.0/16

#      rightid="C=CH, O=Linux strongSwan CN=peer name"

#      auto=start

  1. 读取并解析ipsec.conf配置,设置signal处理函数

  1. 启动charon进程

调用starter_start_charon(), 该函数会fork()子进程去跑charon程序。

  1. 读取配置数据,通过stroke发送

starter_stroke_add_conn(): 创建stroke_msg (type为STR_ADD_CONN),

连接到socket,

stream = lib->streams->connect(lib->streams, uri), 这里的uri为unix:///etc/ipsec.d/run/charon.ctl,.

然后stream->write_all(stream, msg, msg->length)将msg发送出去。

lib->streams->connect()会触发stream_create_unix()。

stream_create_unix()解析地址(保留unix://后面的),创建socket( socket(AF_UNIX, SOCK_STREAM, 0) ), 并connect到这个socket.

最后创建private_stream_t对象(记录了创建的socket id),并返回this->public (stream_t类型)

  1.  
  2.  

charon

  1. 调用library_init()

starter程序中已做过一些介绍。

  1. 调用libcharon_init()

这里创建private_daemon_t对象。

private_daemon_t  *this = daemon_create();

  1. 命令行参数解析

解析debug type&level等

  1. 调用charon->initialize()

charon->initialize() -> lib->plugins->load()

                                        lib->processor->queue_job()

charon->initialize(charon, lib->settings->get_str(lib->settings, "charon.load", PLUGINS))

charon->initialize() /* load_plugins() */ 加载charon.load中描述的plugins.

#charon.conf

charon {

    # Plugins to load in the IKE daemon charon.

             # load =

}

charon->initialize() -> load_plugins () -> find_plugin () //查找plugin-lib文件: libstrongswan-%s.so

-> load_plugin() //%s-plugin-create()创建plugin_t对象。

-> register_features() //entry->plugin->get_features(),就是调用

plugin的get_features(), 获取plugins的

feature信息。

-> load_features() -> load_provided() -> load_feature() ->

plugin_feature_load() -> reg->arg.cb.f() //对于troke plugin,

这个callback就是register_stroke(), register_stroke()会创建

和starter通信用的unix socket ,见 stroke_socket_create(),

然后调用(this->service = lib->streams->create_service创建

stream_service, 最后调用this->service->on_accept(),并设

置callback函数为on_accept() /* stroke_socket.c */,

this->service->on_accept()通过lib->watcher, 创建一个job,

并放入了processor->job[]队列中,等待thread调度执行。

 

load_plugin()里调用dlopen()打开plugin的lib文件,然后调用create_plugin()去调用dlopen()打开后的plugin里面的构造函数%s-plugin-create(), 这里%s为plugin name,比如stroke plugin的话,就是stroke-plugin-create(),这个构造函数创建了private_stroke_plugin_t对象。

plugin对应的lib文件名为libstrongswan-%s.so, %s为plugin name,比如stroke plugin对应的为libstrongswan-stroke.so

charon->initialize()在开始注册的static feature

在load_plugins() -> load_features() load_provided() -> load_feature()

-> plugin_feature_load() -> reg->arg.cb.f()时会调用sender_receiver_cb()和

sa_managers_cb()

PROVIDE ‘libcharon-receive’对应的为sender_receiver_cb(), PROVIDE ‘libcharon-sa-managers’对应的为sa_managers_cb()。

sender_receiver_cb()创建receiver & sender:

  • receiver_create()

创建收包的private_callback_job_t对象,并设置 callback job 为 receive_packets().

  • sender_create()

创建发包的private_sender_t对象,并设置callback job为send_packets。

Receiver & sender的callback job函数在process->set_threads()时,由被创建出来的thread取走,并执行receive_packets() and send_packets().

sa_managers_cb()创建ike_sa_manager()和child_sa_manager().

  • ike_sa_manager_create()

创建private_ike_sa_manager_t对象

  • child_sa_manager_create()

创建private_child_sa_manager_t对象

 

加载完所有的plugins和features后,启动queue job.

lib->processor->queue_job(lib->processor, (job_t*)start_action_job_create());

这个queue_job就是将start_action_job_create()创建的private_start_action_job_t对象插入到lib->processer. jobs[<job_priority_t >]

  1. 设置signal处理函数
  2. 调用charon->start()

这个start()就是调用lib->processor->set_threads(lib->processor, lib->settings->get_int(lib->settings, "%s.threads", DEFAULT_THREADS, lib->ns)))

processor.set_threads()创建一堆thread, 默认为DEFAULT_THREADS (16)个,并设置thread的处理函数为process_jobs().

process_jobs()通过get_job()从processor->job[]中取出job, 然后调用process_job()去执行job->excute(), job->excute()最终会调用到之前注册上去的job的callback函数,比如PROVIDE ‘libcharon-receive’的receive_packets() & send_packets(),stroke_plugin的on_accept()

  1. 调用run()

循环等待处理signal.

  1.  

 

当strongswan程序starter调用 connect()时, charon/stroke_plugin的job回调函数on_accept()被激活,并读取starter发送STR_ADD_CONN msg. 对于STR_ADD_CONN msg, 调用stroke_add_conn()处理。

stroke_add_conn(), 首先解析msg, 然后调用this->config->add(),这里的this是stroke_socket, 参考s stroke_socket_create() in stroke_socket.c. this->config->add()就是stroke_config.c中的add().

add()就是按照msg的信息来build ike_cfg及对应的proposal, peer_cfg及proposal,child_cfg(phase 2 cfg)及proposal等。

最后合并这些信息,放到private_stroke_config_t的list这条link上。

 

starter 启动charon时在发送STR_ADD_CONN后,会发送一个STR_INITIATE msg (对于startup的场景).

Charon收到STR_INITIATE msg后,调用stroke_initiate()处理。

stroke_initiate() -> this->control->initiate(), 这里就进入了stroke_control.c中的initiate(). msg->initiate.name和msg->add_conn.name是一致的(peer_cfg结构中的name也是这个),表示某个connection.

Stroke_control.c的initiate()首先调用stroke_config.c的get_peer_cfg_by_name()获取之前STR_ADD_CONN时创建的peer_cfg.

然后获取child_cfg.

最后调用charon_initiate().

Charon_initiate()中调用controller->initiate()

     Note: msg->output_verbosity会被starter设置为-1.

至此,程序就到了controller.c的initiate()函数了。这里,initiate()又调用initiate_execute()。

Initiate()调用了charon->ike_sa_manager->checkout_by_config(), 这里charon->ike_sa_manager就是之前load_plugins时注册static features时所创建(详见sa_managers_cb())。

因此charon->ike_sa_manager->checkout_by_config()为ike_sa_manager.c中的checkout_by_config().

从上述代码片段看,如果reuser_ikesa没有打开,那么每一个conn都会通过checkout_new()来创建一个ike_sa, 即private_ike_sa_t对象.

initiate_execute()创建了ike_sa对象后,调用其initiate().

Ike_sa->initiate()即为ike_sa.c中的initiate().

Ike_sa.c的initiate()读取local, remote地址后,调用task_manager_v2.c的initiate(). /*ikev2*/

在这个task_manager_v2.c的initiate()中,build message, 并产生packets 数据。最后调用retransmit().

这个task_manager_v2.c的retransmit()做了什么呢?

就是把ike_sa_init包发出去。

send_packets()调用charon->sender->send()

这个charon->sender也是在load_plugins时加载static feature时创建的,详见sender_receiver_cb().

charon->sender->send()就是sender.c中的send_(), 这个send_()调用send_no_marker().

send_no_marker()把要发送的包放入发送队列this-list中。

charon->sender对象在创建时创建了一个job, 专门用于从this-list中取packets来发送。

这个job专门占据一个线程来跑,send_packets()从list中取到包后,调用charon->socket->send()将包发出去。

这里charon->socket->send就是socket_manager.c中的sender()

socket_manager.c的sender()直接调用this->socket->send()来发送包,这里的this->socket是通过socket_manager.c的add_socket()注册的。

这个socket的注册是在socket_default_plugin的feature中的,load_plugins时会注册。

charon->socket->add_socket()会调用socket_default_socket_create()这个callback。

这个socket_default_socket_create() /* socket_default_socket.c */创建private_socket_default_socket_t对象,并创建udp socket.

This->ipv4是ipv4地址族的,this->ipv6是ipv6地址族的。

所以send最终是调用socket_default_socket.c中的sender()。

Socket_default_socket.c中的sender调用send_msg_v4() / send_msg_v6(), 这个send_msg_v4() / send_msg_v6()调用send_msg_generic(), send_msg_generic()调用sendmsg()将ike sa init包发送到对端。

至此,ipsec initiator 发送了IKE_SA_INIT给对端(ipsec responsor).

 

当对端发送IKE_SA_INIT response消息后,本端的处理:

Receiver job thread调用receive_packets() /* receiver.c */

先通过socket收包

然后对包进行必要的一些解析,获取src,dst, 然后将packets转成message.

最后创建一个该包的job,等待线程处理。

我们来看看线程怎么处理这个包的。在process_message.c的execute().

首先找到对应本端之前发送的IKE_SA_INIT时创建的ike_sa, 然后进入ike_sa->process_message()处理。

ike_sa->process_message()调用ike_sa->task_manager->process_message()处理。

ike_sa->task_manager->process_message()在task_manager_v2.c的process_message().

ike_sa->task_manager->process_message()进一步解析message, 然后进入process_response().

process_response()从this->active_task()中取出之前发送IKE_SA_INIT时active的task,来pre_process和process.

这里的task是什么时候加入的?

这需要回到发送IKE_SA_INIT时ike的initiate()。

且在task->build()的时候,将ike_sa的状态由IKE_CREATED变为IKE_CONNECTIING.

上述为queue_task的task列表,下面的为IKE_SA_INIT(initiator)时active的task list:

因此,结合上述两个task list, activate_task list中有下面这些:

TASK_CHILD_CREATE(ike_sa initiate()时queue入),TASK_IKE_VENDOR, TASK_IKE_INIT, TASK_IKE_NATD, TASK_IKE_CERT_PRE, TASK_IKE_ME, TASK_IKE_AUTH, TASK_IKE_CERT_POST, TASK_IKE_CONFIG, TASK_IKE_AUTH_LIFETIME, TASK_IKE_MOBIKE

而TASK_CHILD_CREATE是没有被active的,因为之前queue_ike中就没queue.

对于TASK_IKE_INIT, task->process为ike_init.c中的process_i(). /* i表示initiator, r表示responsor */, TASK_IKE_INIT处理完返回SUCCESS,然后从activate_task中删除。

对于TASK_IKE_AUTH, task->process为ike_auth.c中的process_i(). 这个返回NEED_MORE。

process_response()最后又调用initiate() /* task_manager_v2.c */。

这是activate task的数目不是0了,而是这些:

TASK_CHILD_CREATE(ike_sa initiate()时queue入), TASK_IKE_CERT_PRE, TASK_IKE_ME, TASK_IKE_AUTH, TASK_IKE_CERT_POST, TASK_IKE_CONFIG, TASK_IKE_AUTH_LIFETIME, TASK_IKE_MOBIKE

和之前相比, TASK_IKE_VENDOR , TASK_IKE_INIT, TASK_IKE_NATD 这些没了,因为在task->process()后被删除了。

因此这时的change就是IKE_AUTH了。

然后调用task->build()构建各个数据,cert, auth等。最后生成message.

generate_message()在产生IKE_AUTH数据时会进行加密。

Ike_sa.c的generate_message_fragmented()调用自己模块的generate_message().

这个generate_message()调用message的generate().

message的generate()调用自身的generate_message().

请求消息的rule.

所以,对于发出去的IKE_AUTH包进行了加密运算。

最后调用initiate() /* task_manager_v2.c */将包发出去了.

接着,收到对端来的IKE_AUTH包后,task_manager_v2.c的process_message() -> parse_message(), 在parse_message()时解密包。之后进入process_response()处理。

process_response()调用task->process()时,调用ike_auth.c的process_i(),该函数处理完authority后,设置为IKE_ESTABLISHED状态。

处理结果返回SUCCESS,于是删除TASK_IKE_AUTH.

处理TASK_IKE_CONFIG时,设置vitual ip, 即向kernel配置ip。

Task->process()在处理这个IKE_AUTH时,删除之前的大部分activate_task,因此activate_task里面只剩下TASK_CHILD_CREATE了。(array_count(this->active_tasks) != 0)

之后再调用initiate(),准备开始处理CHILD_SA。此时,ike_sa的状态为IKE_ESTABLISHED.

在TASK_CHILD_CREATE的task->process()中,调用select_and_install(), 在这个函数里:

  1. This->child_sa->install() /*child_sa.c*/ 调用install_internal(), install_internal()调用charon->kernel->add_sa().

这个charon->kernel->add_sa() /*kernel_interface.c*/调用this->ipsec->add_sa().

这里的this->ipsec是通过Kernel_netlink_plugin.c的get_features时注册的(见load_plugins())。

所以,这里this->ipsec->add_sa()就是kernel_netlink_ipsec.c中的add_sa(). 这个kernel_netlink_ipsec.c中的add_sa()就是组netlink消息,向kernel发送XFRM_MSG_NEWSA命令,即创建SA , 相当于命令ip xfrm state add

  1. This->child_sa->install_policies() /*child_sa.c*/调用了install_policies_internal() -> install_policies_inbound() -> charon->kernel->add_policy()

charon->kernel->add_policy() /*kernel_interface.c*/调用了this->ipsec->add_policy():

这个this->ipsec->add_policy()为kernel_netlink_ipsec.c中的add_policy()。这个add_policy()调用add_policy_internal(),这个add_policy_internal()就是向kernel发送XFRM_MSG_NEWPOLICY命令,创建policy, 相当于命令ip xfrm policy add.

转载地址:http://xtlci.baihongyu.com/

你可能感兴趣的文章
谈谈软件从业学习方向--同济大学软件学院JacksonWan
查看>>
Java中利用JMF编写摄像头拍照程序
查看>>
读《SQL Server2000》数据库设计与系统管理
查看>>
java23面试题
查看>>
大学生IT创业还有神话吗?
查看>>
Java技术的新方向
查看>>
Java的JDBC数据库连接池实现方法
查看>>
Struts + Spring + Hibernate 框架资源--整理
查看>>
创业实验室-第一堂课感想
查看>>
创业实验室-第二堂课感想
查看>>
软考-下个阶段征服对象
查看>>
非阻塞套接字(Nonblocking Sockets) 概述
查看>>
Java NIO API详解
查看>>
也许可以更power
查看>>
我来拉Eclipse
查看>>
自己编写的NIO非阻塞聊天室
查看>>
毕业生的来清醒一下
查看>>
EL第四节课回想
查看>>
一些界面标准规范
查看>>
不是人过的四天!
查看>>