IKEv2子网之间秘钥重协商

南楼画角 提交于 2019-12-10 06:52:41

以下根据strongswan代码中的testing/tests/ikev2/net2net-rekey/中的测试环境,验证一下网络到网络的IKEv2协议的秘钥重协商过程。拓扑结构如下:

在这里插入图片描述

拓扑图中使用到的设备包括:虚拟网关moon和sun。

moon网关配置

moon网关的配置文件:/etc/ipsec.conf ,内容如下。为了进行秘钥重协商,安全关联的生存期lifetime指定为较短的10s,正常情况下不应使用如此短的时间值。margintime指定在具体生存期到期之前多久开始进行重新协商,此处为5s秒,即在距离到期(10s)前5s开始重协商。rekeyfuzz字段的值为0%表示禁用重协商时间的随机功能。

conn %default
        ikelifetime=60m
        lifetime=10s
        margintime=5s
        rekeyfuzz=0%
        keyingtries=1
        keyexchange=ikev2
        mobike=no

conn net-net
        left=PH_IP_MOON
        leftcert=moonCert.pem
        leftid=@moon.strongswan.org
        leftsubnet=10.1.0.0/16
        leftfirewall=yes
        right=PH_IP_SUN
        rightid=@sun.strongswan.org
        rightsubnet=10.2.0.0/16
        auto=add

strongswan使用以下的公式计算重协商发起时间,根据以上的参数配置,可得到rekeytime为5秒。rekeyfuzz默认为100%。

rekeytime = lifetime - (margintime + random(0, margintime * rekeyfuzz))

文件strongswan-5.8.1/src/libcharon/plugins/stroke/stroke_socket.c中的函数stroke_add_conn处理连接配置的添加,例如添加以上的net-net连接。

static void stroke_add_conn(private_stroke_socket_t *this, stroke_msg_t *msg)
{
        this->config->add(this->config, msg);
        this->attribute->add_dns(this->attribute, msg);
        this->handler->add_attributes(this->handler, msg);

以上add函数指针指向同目录下文件stroke_config.c文件中的add函数,其中调用子函数build_peer_cfg处理秘钥重协商相关参数。jitter_time变量的值正是依照以上rekeyfuzz配置项的值与margintime值的乘积而得到的一个范围值。此值范围为[0,margintime]。peer.rekey_time的值初始化为生存期lifetime的值减去over_time(margintime)的值。

static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this, stroke_msg_t *msg, ike_cfg_t *ike_cfg)
{
        peer.jitter_time = msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100;
        peer.over_time = msg->add_conn.rekey.margin;
        if (msg->add_conn.rekey.reauth)
        {       
                peer.reauth_time = msg->add_conn.rekey.ike_lifetime - peer.over_time;
        } else {
                peer.rekey_time = msg->add_conn.rekey.ike_lifetime - peer.over_time;
        }

在文件libcharon/config/peer_cfg.c中的函数get_rekey_time用于获取秘钥重协商的时间,如下所示,为以上计算的rekey_time时间,加上[0,margintime]范围内的随机时间。另外,对于IKE连接的重新认证时间,margintime和rekeyfuzz配置参数同样有效,参见get_reauth_time函数。

METHOD(peer_cfg_t, get_rekey_time, uint32_t, private_peer_cfg_t *this, bool jitter)
{
        if (this->rekey_time == 0)
        {
                return 0;
        }
        if (this->jitter_time == 0 || !jitter)
        {
                return this->rekey_time;
        }
        return this->rekey_time - (random() % this->jitter_time);

moon网关的配置文件:/etc/strongswan.conf,内容如下。字段delete_rekeyed_delay值为2,意味着在秘钥重协商完成之后,延时2秒钟删除CHILD_SA子关联的inbound方向结构,以便能够处理延迟接收到的报文。此值配置为0,将等待CHILD_SA超时执行删除。

charon {
  load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac stroke kernel-netlink socket-default updown
  # remove rekeyed inbound SA a bit quicker for the test scenario
  delete_rekeyed_delay = 2
  syslog {
    daemon {
      knl = 2
    }
  }
}

在文件:strongswan-5.8.1/src/libcharon/sa/ikev2/tasks/child_delete.c中,函数destroy_and_reestablish负责删除CHILD_SA子关联。首先获得配置的delete_rekeyed_delay字段的值,如果未配置,使用默认值DELETE_REKEYED_DELAY(5秒)。再者对于秘钥重协商的SA,移除其outbond方向SA。最后,如果当前时间加上delay延时时间小于SA的生存期,延迟delay之后调用函数delete_child_sa_job_create_id删除inbound方向SA。

static status_t destroy_and_reestablish(private_child_delete_t *this)
{
        delay = lib->settings->get_int(lib->settings, "%s.delete_rekeyed_delay",
                                                                   DELETE_REKEYED_DELAY, lib->ns);
        enumerator = this->child_sas->create_enumerator(this->child_sas);
        while (enumerator->enumerate(enumerator, (void**)&entry))
        {
                child_sa = entry->child_sa;
                child_sa->set_state(child_sa, CHILD_DELETED);
                /* signal child down event if we weren't rekeying */
                protocol = child_sa->get_protocol(child_sa);
                if (!entry->rekeyed) { ... }
                else
                {
                        install_outbound(this, protocol, child_sa->get_rekey_spi(child_sa));
                        /* for rekeyed CHILD_SAs we uninstall the outbound SA but don't
                         * immediately destroy it, by default, so we can process delayed packets */
                        child_sa->remove_outbound(child_sa);
                        expire = child_sa->get_lifetime(child_sa, TRUE);
                        if (delay && (!expire || ((now + delay) < expire)))
                        {
                                lib->scheduler->schedule_job(lib->scheduler,
                                        (job_t*)delete_child_sa_job_create_id(
                                                                        child_sa->get_unique_id(child_sa)), delay);
                                continue;
                        }

sun网关配置

sun的配置文件:/etc/ipsec.conf,内容如下。无需要注意的特殊配置项。

conn %default
        ikelifetime=60m
        keylife=20m
        rekeymargin=3m
        keyingtries=1
        keyexchange=ikev2
        mobike=no

conn net-net
        left=PH_IP_SUN
        leftcert=sunCert.pem
        leftid=@sun.strongswan.org
        leftsubnet=10.2.0.0/16
        leftfirewall=yes
        right=PH_IP_MOON
        rightid=@moon.strongswan.org
        rightsubnet=10.1.0.0/16
        auto=add

连接建立

操作流程如下,在sun和moon两个网关上启动strongswan进程,之后在moon网关上发起net-net连接的建立。

sun::ipsec start
moon::ipsec start
sun::expect-connection net-net
moon::expect-connection net-net
moon::ipsec up net-net

内核SA过期

moon网关的内核的SA如下所示,可见在192.168.0.2到192.168.0.1反向的SA的软性超时时长为5秒钟,硬性时长为10秒钟,由配置一致。

src 192.168.0.1 dst 192.168.0.2
        proto esp spi 0xc14f1c0a(3243187210) reqid 1(0x00000001) mode tunnel
        lifetime config:
          expire add: soft 0(sec), hard 10(sec)
          expire use: soft 0(sec), hard 0(sec)
        lifetime current:
          168(bytes), 2(packets)
          add 2019-12-09 09:44:21 use 2019-12-09 09:44:22
        stats:
          replay-window 0 replay 0 failed 0
src 192.168.0.2 dst 192.168.0.1
        proto esp spi 0xcb131d54(3407027540) reqid 1(0x00000001) mode tunnel
        lifetime config:
          expire add: soft 5(sec), hard 10(sec)
          expire use: soft 0(sec), hard 0(sec)
        lifetime current:
          168(bytes), 2(packets)
          add 2019-12-09 09:44:21 use 2019-12-09 09:44:22

内核中结构体类型变量netlink_mgr初始化了notify函数调用xfrm_send_state_notify用于向应用层发送netlink消息通知,如下所示。

static struct xfrm_mgr netlink_mgr = {
    .notify     = xfrm_send_state_notify,
    .acquire    = xfrm_send_acquire,
    .compile_policy = xfrm_compile_policy,
    .notify_policy  = xfrm_send_policy_notify,
    .report     = xfrm_send_report,
    .migrate    = xfrm_send_migrate,
    .new_mapping    = xfrm_send_mapping,
    .is_alive   = xfrm_is_alive,
};

函数xfrm_send_state_notify内容如下,对于SA过期消息XFRM_MSG_EXPIRE,由函数xfrm_exp_state_notify处理。

static int xfrm_send_state_notify(struct xfrm_state *x, const struct km_event *c)
{

    switch (c->event) {
    case XFRM_MSG_EXPIRE:
        return xfrm_exp_state_notify(x, c);

在函数xfrm_exp_state_notify中,创建netlink消息报文skb,由子函数build_expire填充内容,最后,xfrm_nlmsg_multicast将消息多播发送到组XFRMNLGRP_EXPIRE。

static int xfrm_exp_state_notify(struct xfrm_state *x, const struct km_event *c)
{
    struct net *net = xs_net(x);
    struct sk_buff *skb;

    skb = nlmsg_new(xfrm_expire_msgsize(), GFP_ATOMIC);
    if (skb == NULL)
        return -ENOMEM;

    if (build_expire(skb, x, c) < 0) {
        kfree_skb(skb);
        return -EMSGSIZE;
    }

    return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);

目前发送到应用层的消息中包括portid,以及表明时硬性过期还是软性过期的hard值,SA的标记mark和xfrm虚拟接口索引if_id值。

static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c)
{
    struct xfrm_user_expire *ue;
    struct nlmsghdr *nlh;

    nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0);

    ue = nlmsg_data(nlh);
    copy_to_user_state(x, &ue->state);
    ue->hard = (c->data.hard != 0) ? 1 : 0;
    /* clear the padding bytes */
    memset(&ue->hard + 1, 0, sizeof(*ue) - offsetofend(typeof(*ue), hard));

    err = xfrm_mark_put(skb, &x->mark);
    err = xfrm_if_id_put(skb, x->if_id);

内核函数km_state_expired将调用以上netlink_mgr结构中的notify指针,事件类型为XFRM_MSG_EXPIRE。

void km_state_notify(struct xfrm_state *x, const struct km_event *c)
{       
    struct xfrm_mgr *km;
    rcu_read_lock();
    list_for_each_entry_rcu(km, &xfrm_km_list, list)
        if (km->notify)
            km->notify(x, c);
    rcu_read_unlock();
}   
void km_state_expired(struct xfrm_state *x, int hard, u32 portid)
{   
    struct km_event c;

    c.data.hard = hard;
    c.portid = portid;   
    c.event = XFRM_MSG_EXPIRE;
    km_state_notify(x, &c);

内核SA定时器

内核在分配SA结构xfrm_state时,初始化mtimer定时器,处理函数为xfrm_timer_handler。

struct xfrm_state *xfrm_state_alloc(struct net *net)
{
    struct xfrm_state *x;

    x = kmem_cache_alloc(xfrm_state_cache, GFP_ATOMIC | __GFP_ZERO);

    if (x) {
        tasklet_hrtimer_init(&x->mtimer, xfrm_timer_handler,
                    CLOCK_BOOTTIME, HRTIMER_MODE_ABS);
		x->curlft.add_time = ktime_get_real_seconds();

随后在xfrm_state_add(__xfrm_state_insert)函数或者xfrm_state_update函数中,将启动此定时器。

static void __xfrm_state_insert(struct xfrm_state *x)
{
    tasklet_hrtimer_start(&x->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL);

以下的超时处理函数xfrm_timer_handler,如果为软性过期,将调用km_state_expired发送SA过期消息。

static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
{
    struct tasklet_hrtimer *thr = container_of(me, struct tasklet_hrtimer, timer);
    struct xfrm_state *x = container_of(thr, struct xfrm_state, mtimer);
    time64_t now = ktime_get_real_seconds();

    if (x->lft.soft_add_expires_seconds) {
        long tmo = x->lft.soft_add_expires_seconds + x->curlft.add_time - now;
        if (tmo <= 0) {
            warn = 1;
            x->xflags &= ~XFRM_SOFT_EXPIRE;
        } else if (tmo < next) {
            next = tmo;
            x->xflags |= XFRM_SOFT_EXPIRE;
            x->saved_tmo = tmo;
        }
    }

    x->km.dying = warn;
    if (warn)
        km_state_expired(x, 0, 0);

成员变量soft_add_expires_seconds的值在xfrm_state的构建函数xfrm_state_construct中,由子函数copy_from_user_state从应用层的数据结构xfrm_usersa_info->lft中拷贝而来。x->curlft.add_time的值在xfrm_state结构的分配函数xfrm_state_alloc中配赋予当前的值。

static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p)
{
    memcpy(&x->id, &p->id, sizeof(x->id));
    memcpy(&x->sel, &p->sel, sizeof(x->sel));
    memcpy(&x->lft, &p->lft, sizeof(x->lft));

在strongswan文件strongswan-5.8.1/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c中,函数add_sa初始化了xfrm_lifetime_cfg结构相关的成员变量。其中soft_add_expires_seconds初始化为rekey的时长,而hard_add_expires_seconds初始化为lifttime的时长。

METHOD(kernel_ipsec_t, add_sa, status_t, private_kernel_netlink_ipsec_t *this, kernel_ipsec_sa_id_t *id, kernel_ipsec_add_sa_t *data)
{
        /* we use lifetimes since added, not since used */
        sa->lft.soft_add_expires_seconds = data->lifetime->time.rekey;
        sa->lft.hard_add_expires_seconds = data->lifetime->time.life;
        sa->lft.soft_use_expires_seconds = 0;
        sa->lft.hard_use_expires_seconds = 0;

秘钥重协商

文件strongswan-5.8.1/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c中的函数process_expire接收处理内核的安全关联到期消息XFRM_MSG_EXPIRE。其调用内核接口的expire函数指针进行处理。

/** Process a XFRM_MSG_EXPIRE from kernel */
static void process_expire(private_kernel_netlink_ipsec_t *this, struct nlmsghdr *hdr)
{
        struct xfrm_user_expire *expire;

        expire = NLMSG_DATA(hdr);
        protocol = expire->state.id.proto;
        spi = expire->state.id.spi;

        DBG2(DBG_KNL, "received a XFRM_MSG_EXPIRE");

        if (protocol == IPPROTO_ESP || protocol == IPPROTO_AH)
        {
                dst = xfrm2host(expire->state.family, &expire->state.id.daddr, 0);
                if (dst)
                {
                        charon->kernel->expire(charon->kernel, protocol, spi, dst, expire->hard != 0);
                        dst->destroy(dst);

如下为内核接口结构kernel_interface_t的expire函数,其中调用注册在内核接口上的所有listener结构的expire成员函数。

METHOD(kernel_interface_t, expire, void, private_kernel_interface_t *this, uint8_t protocol, uint32_t spi, host_t *dst, bool hard)
{
        kernel_listener_t *listener;
        enumerator_t *enumerator;

        this->mutex->lock(this->mutex);
        enumerator = this->listeners->create_enumerator(this->listeners);
        while (enumerator->enumerate(enumerator, &listener))
        {
                if (listener->expire && !listener->expire(listener, protocol, spi, dst, hard)) {
                        this->listeners->remove_at(this->listeners, enumerator);
                }

文件strongswan-5.8.1/src/libcharon/kernel/kernel_handler.c中函数kernel_handler_create注册了listener结构,用于处理安全关联的到期事件,其中的expire函数如下,由函数rekey_child_sa_job_create处理SA的秘钥重协商。

METHOD(kernel_listener_t, expire, bool, private_kernel_handler_t *this, uint8_t protocol, uint32_t spi, host_t *dst, bool hard)
{
        protocol_id_t proto = proto_ip2ike(protocol);

        DBG1(DBG_KNL, "creating %s job for CHILD_SA %N/0x%08x/%H", hard ? "delete" : "rekey", protocol_id_names, proto, ntohl(spi), dst);

        if (hard) {
                lib->processor->queue_job(lib->processor,
                                (job_t*)delete_child_sa_job_create(proto, spi, dst, hard));
        } else {
                lib->processor->queue_job(lib->processor,
                                (job_t*)rekey_child_sa_job_create(proto, spi, dst));
        }

文件strongswan-5.8.1/src/libcharon/processing/jobs/rekey_child_sa_job.c中函数execute为rekey_child_sa_job的执行函数。首先根据protocol,spi和dst三个参数获取到对应的ike_sa结构;接下来,执行ike_sa结构的rekey_child_sa函数。

METHOD(job_t, execute, job_requeue_t, private_rekey_child_sa_job_t *this)
{
        ike_sa_t *ike_sa;

        ike_sa = charon->child_sa_manager->checkout(charon->child_sa_manager, this->protocol, this->spi, this->dst, NULL);
        if (ike_sa == NULL)
        {
                DBG1(DBG_JOB, "CHILD_SA %N/0x%08x/%H not found for rekey", protocol_id_names, this->protocol, htonl(this->spi), this->dst);
        }
        else
        {
                if (ike_sa->get_state(ike_sa) != IKE_PASSIVE)
                {
                        ike_sa->rekey_child_sa(ike_sa, this->protocol, this->spi);
                }
                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
        }

以下为文件strongswan-5.8.1/src/libcharon/sa/ike_sa.c中的函数rekey_child_sa,其调用task_manager_t结构的两个指针函数:queue_child_rekey和initiate。

METHOD(ike_sa_t, rekey_child_sa, status_t, private_ike_sa_t *this, protocol_id_t protocol, uint32_t spi)
{
        if (this->state == IKE_PASSIVE)
        {
                return INVALID_STATE;
        }
        this->task_manager->queue_child_rekey(this->task_manager, protocol, spi);
        return this->task_manager->initiate(this->task_manager);

文件strongswan-5.8.1/src/libcharon/sa/ikev2/task_manager_v2.c中函数queue_child_rekey如下。其新增了一个任务task,此任务由函数child_rekey_create创建。

METHOD(task_manager_t, queue_child_rekey, void, private_task_manager_t *this, protocol_id_t protocol, uint32_t spi)
{              
        queue_task(this, (task_t*)child_rekey_create(this->ike_sa, protocol, spi));  

以上任务的实现位于文件strongswan-5.8.1/src/libcharon/sa/ikev2/tasks/child_rekey.c中,在函数child_rekey_create中依据protocol的值为连接的initiator或者responder初始化任务的build和process函数指针。对于此处的moon网关,由于是连接的发起方,使用build_i和process_i函数。

child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol, uint32_t spi)
{
        private_child_rekey_t *this;

        INIT(this,
                .public = {
                        .task = {
                                .get_type = _get_type,
                                .migrate = _migrate,
                                .destroy = _destroy,
                        },
                },
                .ike_sa = ike_sa,
                .protocol = protocol,
                .spi = spi,
        );

        if (protocol != PROTO_NONE)
        {       
                this->public.task.build = _build_i;
                this->public.task.process = _process_i;
                this->initiator = TRUE;
                this->child_create = NULL;

看一下在同一个文件中的函数build_i,首先需要获取到需要重协商秘钥的子SA,如果inbound没有找到,再次由outbound方向进行查找,但是由于使用inbound方向进行rekey操作,在oubound方向找到SA之后,进一步查找inbound方向的spi。其次,如果没有获得到SA结构,意味值其已经不存在了;或者,此SA结构的状态不是CHILD_INSTALLED;再或者,此rekey任务已经有了child_create_t结构,即已经开启过重协商任务,但是其SA的状态又不等于CHILD_REKEYING,表明出现了错误,以上任一情况都结束函数,不在进行后续处理。

METHOD(task_t, build_i, status_t, private_child_rekey_t *this, message_t *message)
{
        notify_payload_t *notify;
        uint32_t reqid;
        child_cfg_t *config;

        this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol, this->spi, TRUE);
        if (!this->child_sa)
        {       /* check if it is an outbound CHILD_SA */
                this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol, this->spi, FALSE);
                if (this->child_sa) {
                        /* we work only with the inbound SPI */
                        this->spi = this->child_sa->get_spi(this->child_sa, TRUE);
                }
        }
        if (!this->child_sa ||
                (!this->child_create && this->child_sa->get_state(this->child_sa) != CHILD_INSTALLED) ||
                (this->child_create && this->child_sa->get_state(this->child_sa) != CHILD_REKEYING))
        {
                /* CHILD_SA is gone or in the wrong state, unable to rekey */
                message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED);
                return SUCCESS;
        }
        config = this->child_sa->get_config(this->child_sa);

以下为正常的流程,在rekey任务结构中创建一个child_create_t结构,用于完成大部分重协商工作,此处继续使用上一次协商的DH组参数。以及继续使用reqid、mark和if_id值。之后,由刚刚创建的child_create_t结构中的任务函数build来完成rekey工作,其位于文件strongswan-5.8.1/src/libcharon/sa/ikev2/tasks/child_create.c中。另外,将在CREATE_CHILD_SA消息中增加REKEY_SA类型的通知(PLV2_NOTIFY)载荷。函数最后,将此SA的状态更新为CHILD_REKEYING。

        /* our CHILD_CREATE task does the hard work for us */
        if (!this->child_create) {
                proposal_t *proposal;
                uint16_t dh_group;

                this->child_create = child_create_create(this->ike_sa, config->get_ref(config), TRUE, NULL, NULL);

                proposal = this->child_sa->get_proposal(this->child_sa);
                if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &dh_group, NULL))
                {       /* reuse the DH group negotiated previously */
                        this->child_create->use_dh_group(this->child_create, dh_group);
                }
        }
        reqid = this->child_sa->get_reqid(this->child_sa);
        this->child_create->use_reqid(this->child_create, reqid);
        this->child_create->use_marks(this->child_create, this->child_sa->get_mark(this->child_sa, TRUE).value,
                                                this->child_sa->get_mark(this->child_sa, FALSE).value);
        this->child_create->use_if_ids(this->child_create, this->child_sa->get_if_id(this->child_sa, TRUE),
                                                this->child_sa->get_if_id(this->child_sa, FALSE));

        if (this->child_create->task.build(&this->child_create->task, message) != NEED_MORE) {
                schedule_delayed_rekey(this);
                message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED);
                return SUCCESS;
        }
        if (message->get_exchange_type(message) == CREATE_CHILD_SA) {
                /* don't add the notify if the CHILD_CREATE task changed the exchange */
                notify = notify_payload_create_from_protocol_and_type(PLV2_NOTIFY, this->protocol, REKEY_SA);
                notify->set_spi(notify, this->spi);
                message->add_payload(message, (payload_t*)notify);
        }
        this->child_sa->set_state(this->child_sa, CHILD_REKEYING);

同一个文件中的函数process_i处理rekey过程中的报文交互,可见其主要由child_create_t结构中的task任务处理rekey消息,最后,由child_delete_create负责删除之前的CHILD_SA。

METHOD(task_t, process_i, status_t, private_child_rekey_t *this, message_t *message)
{
        if (this->child_create->task.process(&this->child_create->task, message) == NEED_MORE) {
                /* bad DH group while rekeying, retry, or failure requiring deletion */
                return NEED_MORE;
        }

        /* rekeying done, delete the obsolete CHILD_SA using a subtask */
        this->child_delete = child_delete_create(this->ike_sa, protocol, spi, FALSE);
        this->public.task.build = (status_t(*)(task_t*,message_t*))build_i_delete;
        this->public.task.process = (status_t(*)(task_t*,message_t*))process_i_delete;

函数build_i_delete负责创建INFORMATIONAL类型的删除消息,通知对端删除指定的SA。

static status_t build_i_delete(private_child_rekey_t *this, message_t *message)
{
        /* update exchange type to INFORMATIONAL for the delete */
        message->set_exchange_type(message, INFORMATIONAL);

        return this->child_delete->task.build(&this->child_delete->task, message);

文件strongswan-5.8.1/src/libcharon/sa/ikev2/tasks/child_delete.c中的函数build_i负责创建CHILD_SA的DELETE消息。

METHOD(task_t, build_i, status_t, private_child_delete_t *this, message_t *message)
{
        INIT(entry,
                .child_sa = child_sa,
                .rekeyed = child_sa->get_state(child_sa) == CHILD_REKEYED,
        );
        this->child_sas->insert_last(this->child_sas, entry);
        log_children(this);
        build_payloads(this, message);

在接收到对端的响应之后,调用函数destroy_and_reestablish删除SA。在此函数中根据配置的delete_rekeyed_delay的值,执行一定的延迟操作。

METHOD(task_t, process_i, status_t, private_child_delete_t *this, message_t *message)
{
        process_payloads(this, message);
        DBG1(DBG_IKE, "CHILD_SA closed");
        return destroy_and_reestablish(this);

报文流程

以下为报文交互流程:

在这里插入图片描述

其中MID等于2的两条CREATE_CHILD_SA消息为rekey消息,其中设置了REKEY_SA通知载荷:

在这里插入图片描述

最后MID等于3的两条消息,用于删除之前的SA连接。

在这里插入图片描述

strongswan版本: 5.8.1
内核版本: 5.0

END

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!