1. 背景

E2E保护是autosar标准中定义的,主要是为了保护通信安全(功能安全相关)。因此在原有的通信协议上增加了对应E2E的保护头部分。相关E2E会修改整体的通信协议的payload部分,因此如果需要支持E2E保护,那么通信双方都需要增加E2E的保护机制。

E2E保护其实主要是在原有的包中增加了CRC校验的部分来保证消息的完整性(从安全角度来说,并不能完全保证),以及增加对应的引用计数,来判断消息是否有缺失,重复等等现象。

1.1. autosar描述profile4

在E2E profile4中,用户数据布局(要保护的数据的布局)不受端到端加密配置文件4的约束——只有一个要求,即要保护的数据长度是1字节的倍数。

上面所示的位编号表示位传输的顺序。 端到端加密报头字段(例如端到端加密计数器)被编码为:

1. 大端(最重要的字节在前面)-由profile规定

2. LSB第一(字节内最低有效位第一)——由TCPIP总线强制

例如,端到端加密计数器的16位按照以下顺序(数字越大,意义越大)传输:8 9 10 11 12 13 14 15 0 12 3 4 5 6 7。

通过配置整个端到端加密头的偏移量,头可以放置在受保护数据中的特定位置。

1.1.1. Counter

在 E2E Profile 4 中,计数器由 E2E Profile 初始化、递增、重置和检查。 计数器不被 E2E 监管的调用者操纵或使用。

[PRS_E2E_00478] 在 E2E 配置文件 4 中,在发送方,对于数据元素的第一个传输请求,计数器应初始化为 0,并应为每个后续发送请求递增 1。 当计数器达到最大值(0xFF’FF)时,它会以 0 重新开始下一次发送请求。(RS_E2E_08539)

注意:此规范以前被错误地标识为 PRS_E2EProtocol_00324。

请注意,计数器值 0xFF’FF 并未保留为特殊的无效值,而是用作普通计数器值。

1.1.2. Data ID

唯一的Data id用于验证每个传输的与安全相关的数据元素的身份。

[PRS_E2E_00326] 在端到端加密配置文件4中,数据ID需要明确的传输,即作为传输端到端加密头的一部分。

[PRS_E2E_UC_00327] 在端到端配置文件4中,数据id应该在通信系统网络中是全局唯一的(由多个ecu组成,每个ecu发送不同的数据)。

在使用端到端监管来保护数据元素(即来自RTE的调用)的情况下,由于通信的多重性(1:1或1:N),数据元素的消费者只期望一个特定的数据元素,该数据元素由端到端监管使用数据ID检查。

在使用端到端加密监管来保护消息(例如来自COM的调用)的情况下,接收端COM只希望收到一个特定的消息,该消息由端到端加密监管使用数据ID进行检查。

1.1.3. Length

引入Length字段是为了支持可变大小的长度——存储序列化数据的Data[]数组在每个周期中可能有不同的长度。 长度包括用户数据+端到端加密头(CRC + Counter + Length + DataID)。

1.1.4. CRC

E2E Profile 4 使用 32 位 CRC,以确保高检测率和高汉明距离。

[PRS_E2E_00329] dE2E Profile 4 应使用 SWS CRC Supervision 的 Crc_CalculateCRC32P4() 函数来计算 CRC.c(RS_E2E_08528, RS_E2E_- 08539)

注意:E2E Profile 4 使用的 CRC 与 FlexRay、CAN 和 TCP/IP 使用的 CRC 不同。 它还由不同的软件模块提供(FlexRay、CAN 和 TCP/IP 堆栈 CRC/校验和由通信控制器中的硬件支持或通信堆栈软件提供,但不由 CRC Supervision 提供)。

[PRS_E2E_00330] d在 E2E Profile 4 中,CRC 应在整个 E2E 标头(不包括 CRC 字节)和用户数据上计算。c(RS_E2E_08531)

1.1.5. Timeout detection

前面提到的机制(CRC、计数器、数据 ID、长度)能够检查接收到的数据元素的有效性,当接收器独立于数据传输执行时,即当接收器没有被阻塞等待数据元素或消息时,但是 相反,如果接收器读取当前可用的数据(即检查新数据是否可用)。 然后,通过计数器,接收器可以检测到通信丢失和超时。

1.1.6. 创建E2E-header

1.1.6.1. E2E_P04Protect

函数 E2E_P04Protect() 执行本节中以下九个图表指定的步骤。

[PRS_E2E_00362] d 函数 E2E_P04Protect() 应具有如图 6.28.c (RS_E2E_08539) 所示的整体行为

[PRS_E2E_00363] d E2E_P04Protect() 中的“验证保护功能的输入”步骤的行为如图 6.29.c (RS_E2E_08539)

[PRS_E2E_00376] d E2E_P04Protect()、E2E_P04Forward()和E2E_P04Check()中的“计算偏移量”步骤的行为如图6.30.c (RS_E2E_08539)所示。

[PRS_E2E_00364] d E2E_P04Protect()和E2E_P04Forward()中的步骤“Write Length”的行为如图6.31.c (RS_E2E_08539)所示。

offset 计算本地变量Uint16偏移量。 单位是[byte]

2. 现状

目前vsomeip主要以插件的形式增加了E2E保护的相关机制

3. 初始化

通过代码可以看到,整个E2E保护的部分,主要加载在routing进程中,因此对于客户端来说,无需关注具体E2E的实现,只需要在使用时打开对应的开关即可。

3.1. 加载E2E插件

#ifndef ANDROID
    //e2e功能是否配置了
    if( configuration_->is_e2e_enabled()) {
        VSOMEIP_INFO << "E2E protection enabled.";

        const char *its_e2e_module = getenv(VSOMEIP_ENV_E2E_PROTECTION_MODULE);
		//这里没有指定对应的E2E库的话,默认使用someip的
        std::string plugin_name = its_e2e_module != nullptr ? its_e2e_module : VSOMEIP_E2E_LIBRARY;

        auto its_plugin = plugin_manager::get()->get_plugin(plugin_type_e::APPLICATION_PLUGIN, plugin_name);
        if (its_plugin) {
            VSOMEIP_INFO << "E2E module loaded.";
            e2e_provider_ = std::dynamic_pointer_cast<e2e::e2e_provider>(its_plugin);
        }
    }

    if(e2e_provider_) {
        std::map<e2exf::data_identifier_t, std::shared_ptr<cfg::e2e>> its_e2e_configuration = configuration_->get_e2e_configuration();
        for (auto &identifier : its_e2e_configuration) {
			//添加对应的配置信息
            if(!e2e_provider_->add_configuration(identifier.second)) {
                VSOMEIP_INFO << "Unknown E2E profile: " << identifier.second->profile << ", skipping ...";
            }
        }
    }
#endif

3.2. 通过配置文件确定需要加载的profile

 //根据配置信息确定使用的profile
bool e2e_provider_impl::add_configuration(std::shared_ptr<cfg::e2e> config)
{
    if (config->profile == "CRC8" || config->profile == "P01") {
        process_e2e_profile<profile01::profile_config, profile01::profile_01_checker, profile01::protector>(config);
        return true;
    }

    if (config->profile == "CRC32" || config->profile == "CSTM") {
        process_e2e_profile<profile_custom::profile_config, profile_custom::profile_custom_checker, profile_custom::protector>(config);
        return true;
    }

    if (config->profile == "P04") {
        process_e2e_profile<profile04::profile_config, profile04::profile_04_checker, profile04::protector>(config);
        return true;
    }

    return false;
}

4. 消息保护

由于E2E主要时保护跨设备进行通信的,因此对于vsomeip整体的架构,本地的IPC通信就不需要E2E的保护了。

 bool routing_manager_impl::send(client_t _client, const byte_t *_data,
        length_t _size, instance_t _instance, bool _reliable,
        client_t _bound_client,
        credentials_t _credentials,
        uint8_t _status_check, bool _sent_from_remote) 

E2E保护部分代码实现。首先根据之前加载的配置文件中的信息判断当前的serviceid和methodid是否需要E2E保护。其次通过调用E2E的protect函数计算出E2E的头部信息,然后增加在data前面。

 if (e2e_provider_) {
                    if ( !is_service_discovery) {
                        service_t its_service = VSOMEIP_BYTES_TO_WORD(
                                _data[VSOMEIP_SERVICE_POS_MIN], _data[VSOMEIP_SERVICE_POS_MAX]);
                        method_t its_method = VSOMEIP_BYTES_TO_WORD(
                                _data[VSOMEIP_METHOD_POS_MIN], _data[VSOMEIP_METHOD_POS_MAX]);
#ifndef ANDROID
                        if (e2e_provider_->is_protected({its_service, its_method})) {
                            // Find out where the protected area starts
                            size_t its_base = e2e_provider_->get_protection_base({its_service, its_method});

                            // Build a corresponding buffer
                            its_buffer.assign(_data + its_base, _data + _size);

                            e2e_provider_->protect({ its_service, its_method }, its_buffer, _instance);

                            // Prepend header
                            its_buffer.insert(its_buffer.begin(), _data, _data + its_base);

                            _data = its_buffer.data();
                       }
#endif
                    }
                }

5. 消息验证

消息的验证主要是在收到消息阶段,因此也是在routing进程里面。

 void routing_manager_impl::on_message(const byte_t *_data, length_t _size,
        endpoint *_receiver, const boost::asio::ip::address &_destination,
        client_t _bound_client, credentials_t _credentials,
        const boost::asio::ip::address &_remote_address,
        std::uint16_t _remote_port) {

收到消息后,进行验证

  if (e2e_provider_) {
                its_method = VSOMEIP_BYTES_TO_WORD(
                           _data[VSOMEIP_METHOD_POS_MIN],
                           _data[VSOMEIP_METHOD_POS_MAX]);
#ifndef ANDROID
                if (e2e_provider_->is_checked({its_service, its_method})) {
                    auto its_base = e2e_provider_->get_protection_base({its_service, its_method});
                    e2e_buffer its_buffer(_data + its_base, _data + _size);
                    e2e_provider_->check({its_service, its_method},
                            its_buffer, its_instance, its_check_status);

                    if (its_check_status != e2e::profile_interface::generic_check_status::E2E_OK) {
                        VSOMEIP_INFO << "E2E protection: CRC check failed for service: "
                                << std::hex << its_service << " method: " << its_method;
                    }
                }
#endif
            }

6. 配置文件

相关的配置文件例子可以直接看源码目录下:test/e2e_tests/conf下面

service的配置:

"e2e": {
        "e2e_enabled": "true",
        "protected": [
            {
                "service_id": "0x0011",
                "event_id": "0x0033",
                "profile": "P04",
                "variant": "protector",
                "crc_offset" : "64",
                "data_id" : "0x2d"
            }
        ]
    }

client的配置:

"e2e": {
        "e2e_enabled": "true",
        "protected": [
            {
                "service_id": "0x0011",
                "event_id": "0x0033",
                "profile": "P04",
                "variant": "checker",
                "crc_offset" : "64",
                "data_id" : "0x2d"
            }
        ]
    }

7. 包结构

在没有加入E2E保护时的包内容如下:

client发出来的request包

server发送的response包:

加入E2E之后的包内容:

原始数据:

service回复的内容:

可以看到加上E2E保护时,根据profile4的标准长度,是会覆盖掉data的前12个字节。(这部分查了好久,以为是bug了。结果去看了对应的e2e的测试用例才发现,这里是需要开发者自己关注的,在数据前需要预留足够的空间给到E2E头的位置)。

这里还有一个问题,就是profile4用的someip head是8byte,因此在计算CRC的时候,offset必须指定大于64,否则会覆盖掉部分头信息。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐