快速模式第二包: quick_inI1_ouR1()

在这里插入图片描述

1. 序言

在介绍第②包quick_inI1_outR1()函数之前,先说明下处理流程中的主要的功能:

  • 协商第二阶段的SA算法信息,包括AH协议、ESP协议、封装模式等重要参数
  • 密钥材料交换,包括Nonce、KE(可选)
  • 使用ID载荷来协商两端的保护子网范围。
  • 建立IPSec SA结构。
  • 报文的认证和加密。

从上述作用可以看出,quick_inI1_outR1()及后续函数几乎实现了第一阶段的所有基本交换(第一阶段里的重要载荷在此流程中基本都有实现),因此第二包处理流程算是IKEv1协商流程里最为复杂的流程了。这里只是做一个简单的笔记说明核心流程,无法涉及到完整的交换流程。此外响应端通过此次交换后会建立一个inbound sa,这部分流程尚未看明白处理逻辑(可能在于涉及到内核路由表等内容,目前还没有get到)。因此如果需要深入了解此流程,请参考源码实现。

2. quick_inI1_outR1()流程图

刚才已经说明第二个报文的处理流程比较复杂,实现的功能也较其他接口复杂了很多,从流程图上便可以看出:

在这里插入图片描述

3. 快速模式消息②数据包格式

下表中的报文格式有部分字段应该为变长类型,但是并未标出,这一点请注意。

在这里插入图片描述

4. 源码分析

4.1 quick_inI1_outR1()

quick_inI1_outR1()接口的作用包括:

  • 检验报文的完整性

    • HASH载荷(杂凑载荷)既可以用来检验报文的完整性,也可以用来实现源认证功能,两者实际上是一致的。它计算范围是除了ISAKMP头部以外的完整报文进行杂凑运算。计算方式为:
      H A S H = P R F ( S K E Y I D − a , M s g I D ∣ N i ∣ S A ∣ N r [ ∣ I D i ∣ I D r ] ) HASH = PRF(SKEYID-a, MsgID | Ni | SA | Nr [ | IDi | IDr ] ) HASH=PRF(SKEYIDa,MsgIDNiSANr[IDiIDr])
      还需要注意的是快速模式的三个报文的HASH载荷的运算模式并不相同。
  • 解析报文中的ID载荷

    • 快速模式中,身份标识ID载荷缺省定义为ISAKMP双方的协商地址。如果双方需要指定身份ID载荷,则需要按照一定的顺序进行传输:IDi + IDr。还需要注意的是协商隧道时配置的保护子网(感兴趣流)是通过ID载荷来传输并完成协商的。ID载荷可以传输IPv4和IPv6的主机地址、子网地址、地址范围。因此使用ID载荷来协商感兴趣流完全满足需求。使用emit_subnet_id()来将保护子网填充到ID载荷,使用decode_net_id()将ID载荷解析为保护子网地址.
  • 保存IV值,并调用后续处理

quick_inI1_outR1()函数并没有协商保护子网信息,而是在后续接口中进行的协商。(NAT-T相关略)

stf_status
quick_inI1_outR1(struct msg_digest *md)
{
   
    const struct state *const p1st = md->st;
    struct connection *c = p1st->st_connection;
    struct payload_digest *const id_pd = md->chain[ISAKMP_NEXT_ID];
    struct verify_oppo_bundle b;

    /* HASH(1) in *//*使用第一阶段的算法、计算并检验报文的hash载荷*/
    CHECK_QUICK_HASH(md
	, quick_mode_hash12(hash_val, hash_pbs->roof, md->message_pbs.roof
	    , p1st, &md->hdr.isa_msgid, FALSE)
	, "HASH(1)", "Quick I1");

    /* [ IDci, IDcr ] in
     * We do this now (probably out of physical order) because
     * we wish to select the correct connection before we consult
     * it for policy.
     */

    if (id_pd != NULL)/*如果ID载荷存在*/
    {
   
	struct payload_digest *IDci = id_pd->next;

	/* ??? we are assuming IPSEC_DOI */

	/* IDci (initiator is peer) */
	if (!decode_net_id(&id_pd->payload.ipsec_id, &id_pd->pbs
			   , &b.his.net, "peer client"))/*获取到对端的网段*/
	    return STF_FAIL + INVALID_ID_INFORMATION;

        /* Hack for MS 818043 NAT-T Update */
        if (id_pd->payload.ipsec_id.isaiid_idtype == ID_FQDN) {
   /*将单个地址转换为子网地址*/
            loglog(RC_LOG_SERIOUS, "Applying workaround for MS-818043 NAT-T bug");
            memset(&b.his.net, 0, sizeof(ip_subnet));
            happy(addrtosubnet(&c->spd.that.host_addr, &b.his.net));
		}
        /* End Hack for MS 818043 NAT-T Update */

        b.his.proto = id_pd->payload.ipsec_id.isaiid_protoid;
        b.his.port = id_pd->payload.ipsec_id.isaiid_port;
        b.his.net.addr.u.v4.sin_port = htons(b.his.port);

        /* IDcr (we are responder) */

        if (!decode_net_id(&IDci->payload.ipsec_id, &IDci->pbs
                   , &b.my.net, "our client"))
            return STF_FAIL + INVALID_ID_INFORMATION;

        b.my.proto = IDci->payload.ipsec_id.isaiid_protoid;
        b.my.port = IDci->payload.ipsec_id.isaiid_port;
        b.my.net.addr.u.v4.sin_port = htons(b.my.port);

#ifdef NAT_TRAVERSAL
        /*
         * 略
         */
#endif
    }
    else
    {
     /*载荷中不存在ID载荷,如果两端的地址类型不一致的化则返回错误
        *
        *如果不存在ID载荷,则使用协商地址作为保护子网
        */
        /* implicit IDci and IDcr: peer and self */
        if (!sameaddrtype(&c->spd.this.host_addr, &c->spd.that.host_addr))
            return STF_FAIL;
    /*默认使用IP地址当作ID*/
        happy(addrtosubnet(&c->spd.this.host_addr, &b.my.net));
        happy(addrtosubnet(&c->spd.that.host_addr, &b.his.net));
        b.his.proto = b.my.proto = 0;
        b.his.port = b.my.port = 0;
    }
    b.step = vos_start;
    b.md = md;
    b.new_iv_len = p1st->st_new_iv_len;
    save_new_iv(p1st, b.new_iv);

    /*
     * FIXME - DAVIDM
     * "b" is on the stack,  for OPPO  tunnels this will be bad, in
     * quick_inI1_outR1_start_query it saves a pointer to it before
     * a crypto (async op).
     */
return quick_inI1_outR1_authtail(&b, NULL);
}
4.2 quick_inI1_outR1_authtail()

quick_inI1_outR1_authtail()函数作用包括如下几个:

  • 根据子网信息查询连接

    这部分代码没有看懂。img。按常理来说,接收此报文时已经确定了连接和状态信息,直接比较连接上的保护子网信息和SA载荷中的保护子网信息,确定是否匹配即可。但是openswan源码中的逻辑负责了很多,没有看明白这部分代码,先留一个疑问吧

  • 根据连接创建新的状态

  • 解析IPSec SA建议载荷

    • 解析SA载荷parse_ipsec_sa_body()

      这个接口是快速模式协商IPSec策略的核心接口,包括封装协议 (ESP | AH | IPCOM)、加密算法、认证算法(完整性算法)、隧道模式or传输模式等等,都是在此接口中进行协商的。此外,该函数也可以完成应答报文的SA载荷的封装。

      近700行的代码,不再另行说明了。

  • 解析Nonce载荷

  • 如果支持PFS,则解析KE载荷

    • 启动PFS功能(完美向前加密),则第二阶段需要再进行一次DH交换,因此需要重新计算生成KE载荷。PFS简单的说如果第一阶段的秘钥被破解(无论采用何种方式),由第一阶段密钥衍生的第二阶段密钥则不受影响。这就要求在第二阶段再次进行DH交换。
  • 构建密钥交换材料申请结构信息,包括:

    评论 2
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包

    打赏作者

    叨陪鲤

    你的鼓励将是我创作的最大动力

    ¥1 ¥2 ¥4 ¥6 ¥10 ¥20
    扫码支付:¥1
    获取中
    扫码支付

    您的余额不足,请更换扫码支付或充值

    打赏作者

    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值