以下根据strongswan代码中的testing/tests/ikev2/net2net-fragmentation/中的测试环境,来看一下IKEv2协议的报文分片处理流程。拓扑结构如下:

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

网关配置

moon的配置文件:/etc/ipsec.conf,内容如下。注意此处fragmentation字段的值为yes,可能的取值还有:no、accept和force。其中yes表示如果对端支持分片,本端将开启分片功能。accept表示支持接收分片IKE报文,但是不会发送分片。force表示强制使用报文分片。

conn %defaultkeyexchange=ikev2fragmentation=yesconn net-netleft=PH_IP_MOONleftcert=moonCert.pemleftid=@moon.strongswan.orgleftsubnet=10.1.0.0/16leftfirewall=yesright=PH_IP_SUNrightid=@sun.strongswan.orgrightsubnet=10.2.0.0/16auto=add

配置文件/etc/strongswan.conf中的fragment_size指定分片的最大长度,默认为1280字节,指定为0的话,对于IPv4将使用值576。

charon {load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac stroke kernel-netlink socket-default updownfragment_size = 1088
}

此外,sun网关的配置与moon网关基本相同,区别在于一些主机相关选项差别。

配置消息发送

文件strongswan-5.8.1/src/starter/starter.c,使用默认的配置文件ipsec.conf(CONFIG_FILE)。

int main (int argc, char **argv)
{starter_config_t *cfg = NULL;starter_conn_t *conn, *conn2;if (!config_file) {config_file = lib->settings->get_str(lib->settings, "starter.config_file", CONFIG_FILE);}cfg = confread_load(config_file);for (;;) {/* Add stale conn and ca sections */if (starter_charon_pid()) {for (conn = cfg->conn_first; conn; conn = conn->next) {if (conn->state == STATE_TO_ADD) {if (starter_charon_pid()) {starter_stroke_add_conn(cfg, conn);

文件/strongswan-5.8.1/src/starter/confread.c中函数conn_defaults用于设置连接的默认值,例如fragmentation默认状态为开启FRAGMENTATION_YES。如果配置文件ipsec.conf中未设置这些值,将使用默认值。

static void conn_defaults(starter_conn_t *conn)
{conn->dpd_delay             =  30; /* seconds */conn->dpd_timeout           = 150; /* seconds */conn->replay_window         = SA_REPLAY_WINDOW_DEFAULT;conn->fragmentation         = FRAGMENTATION_YES;

文件strongswan-5.8.1/src/starter/starterstroke.c中的函数starter_stroke_add_conn负责创建stroke_msg_t数据结构,并将其发送到套接口对端。stroke_msg_t结构成员fragmentation保存着是否分片的标志值。

int starter_stroke_add_conn(starter_config_t *cfg, starter_conn_t *conn)
{stroke_msg_t *msg;msg = create_stroke_msg(STR_ADD_CONN);msg->add_conn.version = conn->keyexchange;...msg->add_conn.mobike = conn->options & SA_OPTION_MOBIKE;msg->add_conn.force_encap = conn->options & SA_OPTION_FORCE_ENCAP;msg->add_conn.fragmentation = conn->fragmentation;...return send_stroke_msg(msg);

配置信息接收

文件strongswan-5.8.1/src/libcharon/plugins/stroke/stroke_socket.c中的函数on_accept负责接收stroke配置消息。其由描述符中读取stroke_msg_t结构的数据,之后,根据其中的类型type字段进行响应的处理,对于连接添加操作STR_ADD_CONN,由函数stroke_add_conn进行处理。

static bool on_accept(private_stroke_socket_t *this, stream_t *stream)
{stroke_msg_t *msg;/* read length */if (!stream->read_all(stream, &len, sizeof(len))) {return FALSE;}if (len < offsetof(stroke_msg_t, buffer)) {DBG1(DBG_CFG, "invalid stroke message length %d", len);return FALSE;}/* read message (we need an additional byte to terminate the buffer) */msg = malloc(len + 1);msg->length = len;if (!stream->read_all(stream, (char*)msg + sizeof(len), len - sizeof(len))) {free(msg);return FALSE;}switch (msg->type){case STR_ADD_CONN:stroke_add_conn(this, msg);break;

同一个文件中的函数stroke_add_conn将调用stroke_config_t结构中的成员函数add来处理连接消息。

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);

文件strongswan-5.8.1/src/libcharon/plugins/stroke/stroke_config.c中的函数add,调用build_ike_cfg函数创建ike_cfg_t结构。

METHOD(stroke_config_t, add, void, private_stroke_config_t *this, stroke_msg_t *msg)
{ike_cfg_t *ike_cfg, *existing_ike;peer_cfg_t *peer_cfg, *existing;child_cfg_t *child_cfg;enumerator_t *enumerator;bool use_existing = FALSE;ike_cfg = build_ike_cfg(this, msg);if (!ike_cfg){return;}

同一个文件中的函数build_ike_cfg创建ike_cfg_create_t结构,并且调用ike_cfg_create函数,据此结构创建最终的ike_cfg_t结构。

static ike_cfg_t *build_ike_cfg(private_stroke_config_t *this, stroke_msg_t *msg)
{ike_cfg_create_t ike;ike_cfg_t *ike_cfg;char me[256], other[256];ike = (ike_cfg_create_t){....force_encap = msg->add_conn.force_encap,.fragmentation = msg->add_conn.fragmentation,};ike_cfg = ike_cfg_create(&ike);

在文件strongswan-5.8.1/src/libcharon/config/ike_cfg.c的函数ike_cfg_create中,fragmentation的值最终赋予了private_ike_cfg_t结构的成员变量fragmentation。

ike_cfg_t *ike_cfg_create(ike_cfg_create_t *data)
{private_ike_cfg_t *this;INIT(this,....force_encap = data->force_encap,.fragmentation = data->fragmentation,

分片大小

文件strongswan-5.8.1/src/libcharon/sa/ike_sa.c中函数ike_sa_create读取配置文件中fragment_size的数值,保存到private_ike_sa_t结构的成员变量fragment_size中,以供之后使用,这里我们设置的值为1088。如果配置文件中没有设置此字段,默认使用1280的长度值。

ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, ike_version_t version)
{private_ike_sa_t *this;INIT(this,.public = {},.ike_sa_id = ike_sa_id->clone(ike_sa_id),.fragment_size = lib->settings->get_int(lib->settings,"%s.fragment_size", 1280, lib->ns),);

文件strongswan-5.8.1/src/libcharon/sa/ikev2/task_manager_v2.c中函数generate_message用于发送IKEv2协议的报文。其调用以上的generate_message_fragmented函数。

/* Generates the given message and stores packet(s) in the given array*/
static bool generate_message(private_task_manager_t *this, message_t *message, array_t **packets)
{enumerator_t *fragments;packet_t *fragment;if (this->ike_sa->generate_message_fragmented(this->ike_sa, message, &fragments) != SUCCESS){return FALSE;}while (fragments->enumerate(fragments, &fragment)){array_insert_create(packets, ARRAY_TAIL, fragment);}fragments->destroy(fragments);array_compress(*packets);

文件strongswan-5.8.1/src/libcharon/sa/ike_sa.c中函数generate_message_fragmented处理报文分片。ike_cfg->fragmentation函数获取以上介绍的fragmentation配置,这里值为FRAGMENTATION_YES。对于FRAGMENTATION_FORCE,这里直接值为use_frags标志,使用分片发送。对于FRAGMENTATION_YES,需要检查是否支持分片扩展EXT_IKE_FRAGMENTATION,另外,对于IKEv1版本协议,如果支持Microsoft扩展,还需要检查是否请求了证书,如果未请求证书,不使用分片。按照注释中的说法,Windows 7和8端点只有在请求证书时,才接收分片报文(一般情况下,不带有证书的报文不会太大)。

METHOD(ike_sa_t, generate_message_fragmented, status_t, private_ike_sa_t *this, message_t *message, enumerator_t **packets)
{enumerator_t *fragments;packet_t *packet;status_t status;bool use_frags = FALSE;bool pre_generated = FALSE;if (this->ike_cfg){switch (this->ike_cfg->fragmentation(this->ike_cfg)){case FRAGMENTATION_FORCE:use_frags = TRUE;break;case FRAGMENTATION_YES:use_frags = supports_extension(this, EXT_IKE_FRAGMENTATION);if (use_frags && this->version == IKEV1 && supports_extension(this, EXT_MS_WINDOWS)){/* It seems Windows 7 and 8 peers only accept proprietary fragmented messages if they expect certificates. */use_frags = message->get_payload(message, PLV1_CERTIFICATE) != NULL;}break;default:break;}}

对于无需分片的报文,即use_frags为空,直接使用generate_message函数执行发送。

        if (!use_frags) {status = generate_message(this, message, &packet);if (status != SUCCESS) {return status;}*packets = enumerator_create_single(packet, NULL);return SUCCESS;}pre_generated = message->is_encoded(message);this->stats[STAT_OUTBOUND] = time_monotonic(NULL);message->set_ike_sa_id(message, this->ike_sa_id);if (!pre_generated) {charon->bus->message(charon->bus, message, FALSE, TRUE);}       status = message->fragment(message, this->keymat, this->fragment_size, &fragments);if (status == SUCCESS) {                       if (!pre_generated) {               charon->bus->message(charon->bus, message, FALSE, FALSE);}                       *packets = enumerator_create_filter(fragments, filter_fragments, this, NULL);}return status;

分配处理

对于分片报文,由文件strongswan-5.8.1/src/libcharon/encoding/message.c中的函数fragment进行处理。我们指定的参数frag_len是整个IP报文的长度,数据长度需要减去IP头部(不计算IP选项),和UDP头部长度,另外对于使用NAT-T(4500)端口的IKE报文,还需要减去4额字节的non-ESP marker(固定值:00 00 00 00),这里没有启用NAT-T,最后数据的长度为1088-20-8=1060字节。

METHOD(message_t, fragment, status_t, private_message_t *this, keymat_t *keymat, size_t frag_len, enumerator_t **fragments)
{encrypted_payload_t *encrypted = NULL;generator_t *generator = NULL;message_t *fragment;packet_t *packet;payload_type_t next = PL_NONE;/* frag_len is the complete IP datagram length, account for overhead (we assume no IP options/extension headers are used) */REDUCE_FRAG_LEN(frag_len, (src->get_family(src) == AF_INET) ? 20 : 40);/* 8 (UDP header) */REDUCE_FRAG_LEN(frag_len, 8);if (dst->get_port(dst) != IKEV2_UDP_PORT && src->get_port(src) != IKEV2_UDP_PORT) {       REDUCE_FRAG_LEN(frag_len, 4);           /* reduce length due to non-ESP marker */}

以下对于已经编码的数据,可直接获取其缓存的首地址和长度值,对于IKEv2版本,可使用get_payload获取到加密载荷。否则,使用generate_message生成编码数据。随后,验证数据长度是否超出规定的分片长度,这里为以上计算的值1060,如果数据未超过分片长度,或者其为未经过加密的IKEv2协议数据,不进行分片处理。

        if (is_encoded(this)) {       if (this->major_version == IKEV2_MAJOR_VERSION) {       encrypted = (encrypted_payload_t*)get_payload(this, PLV2_ENCRYPTED);}data = this->packet->get_data(this->packet);len = data.len;} else {       status = generate_message(this, keymat, &generator, &encrypted);if (status != SUCCESS) {       DESTROY_IF(generator);return status;}data = generator->get_chunk(generator, &lenpos);len = data.len + (encrypted ? encrypted->get_length(encrypted) : 0);}/* check if we actually need to fragment the message and if we have an encrypted payload for IKEv2 */if (len <= frag_len || (this->major_version == IKEV2_MAJOR_VERSION && !encrypted)) {if (generator) {status = finalize_message(this, keymat, generator, encrypted);if (status != SUCCESS)return status;}*fragments = enumerator_create_single(this->packet, NULL);return SUCCESS;}

至此,开始分片处理工作。将之前计算的分片阈值frag_len在减去28个字节,即1060-28=1032字节,此28字节为ISAKMP头部的长度。对于IKEv1版本协议,分片长度还需要减去8字节的分片载荷头部,此头部要来描述,总的分片数量,此报文的分片编号等信息。对于IKEv1,分片长度减小为1032-8=1024字节,即实际发送的数据部分的长度。

        /* frag_len denoted the maximum IKE message size so far, later on it will* denote the maximum content size of a fragment payload, therefore, account for IKE header */REDUCE_FRAG_LEN(frag_len, 28);if (this->major_version == IKEV1_MAJOR_VERSION){if (generator) {status = finalize_message(this, keymat, generator, encrypted);if (status != SUCCESS) {return status;}data = this->packet->get_data(this->packet);generator = NULL;}/* overhead for the fragmentation payload header */REDUCE_FRAG_LEN(frag_len, 8);

对于IKEv2协议而言,除了和IKEv1协议一样,分片长度要减去分片载荷头部长度的8个字节外,还需要减去加密算法使用的初始向量IV,以及完整性算法使用的ICV的长度,最后分片长度还需要是加密算法的块长度的整数倍。

        } else {aead_t *aead;if (generator) {generator->destroy(generator);generator = generator_create();} else {       generator = generator_create_no_dbg(); /* do not log again if it was generated previously */}next = encrypted->payload_interface.get_next_type((payload_t*)encrypted);encrypted->generate_payloads(encrypted, generator);data = generator->get_chunk(generator, &lenpos);if (!is_encoded(this)) {encrypted->destroy(encrypted);}aead = keymat->get_aead(keymat, FALSE);/* overhead for the encrypted fragment payload */REDUCE_FRAG_LEN(frag_len, aead->get_iv_size(aead));REDUCE_FRAG_LEN(frag_len, aead->get_icv_size(aead));REDUCE_FRAG_LEN(frag_len, 8);                                 /* fragmentation payload header */frag_len = round_down(frag_len, aead->get_block_size(aead));  /* padding and padding length */REDUCE_FRAG_LEN(frag_len, 1);

以下根据计算而来的分配长度frag_len,来计算长度为data.len的数据需要分成几个片段,count变量表示分片数量。

        count = data.len / frag_len + (data.len % frag_len ? 1 : 0);this->fragments = array_create(0, count);DBG1(DBG_ENC, "splitting IKE message (%zu bytes) into %hu fragments", len, count);

以下的循环使用create_fragment函数创建指定长度的分片,message的generate函数负责生产报文数据,最后将生成的报文分片插入message_t结构的fragments指定的数组中。

        for (num = 1; num <= count; num++) {       len = min(data.len, frag_len);fragment = create_fragment(this, next, num, count, chunk_create(data.ptr, len));status = fragment->generate(fragment, keymat, &packet);fragment->destroy(fragment);if (status != SUCCESS) {       DBG1(DBG_ENC, "failed to generate IKE fragment");clear_fragments(this);DESTROY_IF(generator);return FAILED;}array_insert(this->fragments, ARRAY_TAIL, packet);data = chunk_skip(data, len);}*fragments = array_create_enumerator(this->fragments);

分片

文件strongswan-5.8.1/src/libcharon/encoding/message.c中的函数create_fragment负责创建一个分片,对于IKEv1协议而言,message_id总是为0。另外需要注意的是,即使是quick模式或者transaction的分片报文,IKEv1仍将使用最初阶段一的交换类型:AGGRESSIVE或者ID_PROT。

函数fragment_payload_create_from_data负责创建fragment分片载荷头部。

static message_t *create_fragment(private_message_t *this, payload_type_t next, uint16_t num, uint16_t count, chunk_t data)
{enumerator_t *enumerator;payload_t *fragment, *payload;message_t *message;peer_cfg_t *peer_cfg;ike_sa_t *ike_sa;message = clone_message(this);if (this->major_version == IKEV1_MAJOR_VERSION){message->set_message_id(message, 0);  /* other implementations seem to just use 0 as message ID, so here we go *//* always use the initial message type for fragments, even for quick mode or transaction messages. */ike_sa = charon->bus->get_sa(charon->bus);if (ike_sa && (peer_cfg = ike_sa->get_peer_cfg(ike_sa)) && peer_cfg->use_aggressive(peer_cfg)){message->set_exchange_type(message, AGGRESSIVE);} else {message->set_exchange_type(message, ID_PROT);}fragment = (payload_t*)fragment_payload_create_from_data(num, num == count, data);

对于IKEv2协议而言,使用函数encrypted_fragment_payload_create_from_data创建fragment分片载荷头部数据,对于第一个分片,其可以包括未经过加密的载荷。

        } else {fragment = (payload_t*)encrypted_fragment_payload_create_from_data( num, count, data);if (num == 1) {/* only in the first fragment is this set to the type of the first payload in the encrypted payload */fragment->set_next_type(fragment, next);/* move unencrypted payloads to the first fragment */enumerator = this->payloads->create_enumerator(this->payloads);while (enumerator->enumerate(enumerator, &payload)){if (payload->get_type(payload) != PLV2_ENCRYPTED) {this->payloads->remove_at(this->payloads, enumerator);message->add_payload(message, payload);}}enumerator->destroy(enumerator);}}message->add_payload(message, (payload_t*)fragment);return message;

发送

文件strongswan-5.8.1/src/libcharon/sa/ikev2/task_manager_v2.c中的函数执行最终的发送操作。

static void send_packets(private_task_manager_t *this, array_t *packets, host_t *src, host_t *dst)
{packet_t *packet, *clone;int i;for (i = 0; i < array_count(packets); i++){array_get(packets, i, &packet);clone = packet->clone(packet);if (src) {clone->set_source(clone, src->clone(src));}if (dst) {clone->set_destination(clone, dst->clone(dst));}charon->sender->send(charon->sender, clone);

报文

以下为Non-ESP Marker字段和ISAKMP字段:

以为fragment分片载荷头部字段。

strongswan版本: 5.8.1
内核版本: 5.0

END

IKEv2协议报文分片处理相关推荐

  1. IKEv2协议中的EAP-TLS认证处理流程

    以下根据strongswan代码中的testing/tests/ikev2/rw-eap-tls-only/中的测试环境,验证一下IKEv2协议的EAP-TLS认证过程.拓扑结构如下: 拓扑图中使用到 ...

  2. 以太网 以太网帧格式与IP报文分片

    2.1.0 以太网 以太网帧格式与IP报文分片 一.以太网数据帧信息简介 以太网有两种类型的数据帧,一种是Ethernet_II另一种是IEEE802.3. 两者并没有明确的规定两种类型的使用场景,通 ...

  3. 报文分片(16位分片标识、3位标志、13位片偏移字段详解)

    目录 一.什么是分片? 二.如何分片? 三.如何组装?(16位分片标识.3位标志.13位片偏移) 1.回顾16位分片标识.3位标志.13位片偏移 2.组合分片报文 四.分片的影响 1.对UDP的影响 ...

  4. 计算机网络自顶向下 利用分组嗅探器Wireshark俘获协议报文并分析IP协议

    利用分组嗅探器Wireshark俘获协议报文并分析IP协议 监察第一个返回的ICMP数据包,看自己的计算机的IP地址是什么? 在IPpacket 的头部,上层协议域的值是什么? IP header有多 ...

  5. 计算机网络各层级协议报文形式详解

    网络层各级协议报文样式 概述 协议 所属层次 单位 长度 点对点协议 PPP 数据链路层 Mac帧 64~1518 IP协议 网络层 分组 packet 20~65535 UDP 运输层 数据报 8~ ...

  6. IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包

    IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包 文章目录 IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包 1. IKEv2 协商总体框架 2. 第二包流程图 3. ...

  7. IPSec之IKEv2协议详解

    IKEv2简介 IKEv2介绍: 定义在RFC4306 ,更新与 RFC 5996. 不兼容IKEv1,IKEv1不支持认证,IKEv2支持认证,EAP. 支持NAT穿越. IKEv2支持私密性.完整 ...

  8. 【计算机网络】应用层 : 万维网 和 HTTP 协议 ( 万维网概述 | HTTP 协议特点 | HTTP 协议连接方式 | HTTP 协议报文结构 | HTTP 请求报文 | HTTP 响应报文 )

    文章目录 一.万维网概述 ★ 二.HTTP 协议 ( 超文本传输协议 ) ★ 三.HTTP 协议特点 ★ 四.HTTP 连接方式 ★ 五.HTTP 协议报文结构 六.HTTP 请求报文 ★ 七.HTT ...

  9. 【计算机网络】网络层 : BGP 协议 ( BGP 协议简介 | BGP 协议信息交换 | BGP 协议报文格式 | BGP-4 常用报文 | RIP 、OSPF、BGP 协议对比 )

    文章目录 一.路由选择协议分类 二.BGP 协议 简介 三.BGP 协议 信息交换过程 三.BGP 协议 报文格式 四.BGP 协议 特点 五.BGP-4 协议的 四种报文 六.RIP .OSPF.B ...

  10. http协议报文格式原理图

    报文字段含义请参见:http协议报文字段含义

最新文章

  1. 隐藏十年竟无人发现!Sudo 漏洞被曝出:无需密码就能获取 root 权限
  2. 《剑指Offer》题目:树的子结构
  3. pyecharts开篇(python可视化神器)
  4. Excel自定义函数使用正则表达式详解
  5. WPF 动态更换图片路径
  6. jboss fuse 教程_在JBoss Fuse / Fabric8 / Karaf中使用Byteman
  7. springBoot shiro
  8. PostgreSQL用户角色和权限管理
  9. PHP电子合同对接流程,E签宝电子合同对接实战经验
  10. linux显卡驱动卸载和安装,Linux下Nvidia显卡驱动卸载和卸载后的问题
  11. Spring集合类型属性注入
  12. ​最强全集,数据科学领域,那些你不能不知道的大咖们!
  13. 零基础学习大数据难不难?小白如何上手大数据?
  14. 理解ES6中暂时性死区TDZ
  15. UML用例图之关联关系、包含关系、扩展关系、泛化关系详解
  16. [附源码]JAVA+ssm基于Internet快递柜管理系统(程序+Lw)
  17. IP地址(分类)、子网掩码、网络号、主机号、子网号
  18. 使用Zerotier实现免费内网穿透
  19. 【数学模拟卷总结】2023李林六套卷数学二第三套
  20. PID控制器中的常见问题

热门文章

  1. 计算机硬盘中病毒吗,硬盘中病毒能格式化吗
  2. 函数的连续性与间断点
  3. 手把手教用matlab做无人驾驶
  4. python并行编程 - 分布式篇
  5. excel熵值法计算权重_指标合成的客观权重法之熵权法
  6. 基于 O2O 视角的共享经济商业模式分析 ——以 ofo 共享单车为例
  7. 剖析kubernetes集群内部DNS解析原理
  8. 微信内置浏览器直接下载APP的解决方法
  9. day02【Collection、泛型】
  10. Springcloud config 出现Error occured cloning to base directory.