本文共 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
创建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.
--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
调用starter_start_charon(), 该函数会fork()子进程去跑charon程序。
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类型)
charon
starter程序中已做过一些介绍。
这里创建private_daemon_t对象。
private_daemon_t *this = daemon_create();
解析debug type&level等
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:
创建收包的private_callback_job_t对象,并设置 callback job 为 receive_packets().
创建发包的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().
创建private_ike_sa_manager_t对象
创建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 >]
这个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()
循环等待处理signal.
当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(), 在这个函数里:
这个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
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/