1、AUTOSAR_E2E
E2E的概念是在运行时对与安全相关的数据交互进行保护,以防止通信链路故障的影响。基本实现方式是在要保护的数据区添加额外的E2E Header。发送端负责添加E2E Header,接收端会对Header进行校验检查,保证数据的完整性。
参考 AUTOSAR_PRS_E2EProtocol Specification R20-11 ,规定了不同的E2E Profiles,如1,2,4,5,6,7,8,11,22等
以E2E Profile 4为例,规范中定义了Profile 4 包含的控制字段Length、Counter、CRC、DataID,以及Header的数据结构
1.1 E2E Profile 4 Header的数据结构
Length : 16位,为了支持可变长度引入此字段。
Length = user data + E2E Header(CRC + Counter + Length + DataID)
Counter :16位, 取值 0 ~ 0xFFFF
DataID :32位,必须是全局唯一的,用户定义
CRC :32位,应该使用 Crc_CalculateCRC32P4() 函数 [SWS CRCLibrary],CRC32 0x1F4ACFB13多项式 ,确保高检测率和高汉明距离
在 E2E Profile 4, CRC计算包含 整个 E2E Header(CRC字段除外) 和 user data(RS_E2E_08531)
整个Header字段被编码为:
(1)大端:(最重要的字节在前面) - 由Profile规定,PS:高字节在前,低字节在后
(2)LSB(字节内的最低有效位)先传输 - 由TCP/IP总线强制
1.2 Creation of E2E-Header
1.2.1 E2E_P04Project
E2E_P04Project函数执行图表所示的步骤
E2E_P04Protect() 中 ”Verify inputs of the protect function”执行如下的步骤:
"Compute offset" 执行如下步骤
“Write Length” 执行步骤
“Write Counter”执行步骤:
“Wirte DataID” 执行步骤:
“Compute CRC”步骤:
“Write CRC” 步骤:
“Increment Counter” 步骤:
1.2.2 E2E_P04Forward
E2E Profile 4的E2E_P04Forward()函数由SW-C调用,以保护其应用程序数据,并转发接收 到的E2E-Status,用于基于面向服务通信的信号转换等用例。 如果接收到的 E2E状态等于E2E_P_OK,那么函数的行为应该与E2E_P04Protect()相同。
1.3 Evaluation of the E2E-Header
目录
1.3 Evaluation of the E2E-Header
E2E_P04Check函数按照本节中的以下七个图和PRS_E2EProtocol_00367图指定的操作。
E2E_P04Check() 函数中 ”Verify inputs of the check function” 执行步骤
"Read Length" 步骤:
“Read Counter” 步骤:
“Read DataID” 步骤:
“Read CRC” 步骤:
“Do Checks” 步骤:
2、vsomeip E2E
2.1 概要
vsomeip中也实现了E2E的相关机制,包括 Profile 1、Profile 4 和 Profile custom,整体是以插件的形式执行。
e2e源码文件路径: vsomeip-x.x.xx.x/implementation/e2e_protection/
2.2 实现流程
vsomeip E2E的发送 protecter 和 接收checker 都是在routing中完成的。以E2E Profile 4 为例分析。
2.2.1 Protect
implementation/routing/routing_manager_impl.cpp
挂载e2e插件
void routing_manager_impl::init() {
...
...
...
if( configuration_->is_e2e_enabled()) {
VSOMEIP_INFO << "E2E protection enabled.";
const char *its_e2e_module = getenv(VSOMEIP_ENV_E2E_PROTECTION_MODULE);
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
}
在usr data 发送的时候,给数据添加E2E Header,对消息进行保护
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_buffer its_buffer;
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 定位CRC计算起始位置
size_t its_base = e2e_provider_->get_protection_base({its_service, its_method});
// Build a corresponding buffer 将需要Protect的部分拷贝出来
its_buffer.assign(_data + its_base, _data + _size);
e2e_provider_->protect({ its_service, its_method }, its_buffer, _instance);
// Prepend header 将someip头插入protect数据区前面
its_buffer.insert(its_buffer.begin(), _data, _data + its_base);
_data = its_buffer.data();
}
#endif
}
}
implementation/e2e_protection/e2e_provider_impl.cpp
根据配置文件的设置,映射到不同的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;
}
获取Protect数据区的起始位置get_protection_base。以Profile 4 为例,此位置依赖于implementation/e2e_pretection/include/e2e/profile/profile04/profile_04.hpp profile_config中base_的赋值
std::size_t e2e_provider_impl::get_protection_base(e2exf::data_identifier_t id) const
{
const auto found_base = custom_bases_.find(id);
if (found_base != custom_bases_.end())
return (found_base->second);
return (0);
}
// [SWS_E2E_00200]
struct profile_config {
profile_config() = delete;
profile_config(uint32_t _data_id, size_t _offset,
size_t _min_data_length, size_t _max_data_length,
uint16_t _max_delta_counter)
: data_id_(_data_id), offset_(_offset),
min_data_length_(_min_data_length), max_data_length_(_max_data_length),
max_delta_counter_(_max_delta_counter),
base_(VSOMEIP_SOMEIP_HEADER_SIZE) { //起始位置包含someip Header的8个字节,根据需要自行修改
}
profile_config(const profile_config &_config) = default;
profile_config &operator=(const profile_config &_config) = default;
// [SWS_E2E_00334]
uint32_t data_id_;
size_t offset_; // This must be configured in bit but as a multiple of 8.
// As we must use it as an index, we do the math once at
// configuration time and use the correct data type here.
// Thus, this value is always the byte where the CRC starts.
size_t min_data_length_;
size_t max_data_length_;
uint16_t max_delta_counter_;
// SOME/IP base
size_t base_;
};
implementation/e2e_protection/src/e2e/profile/profile04/protector.cpp
AUTOSAR E2E Profile 4 Protect报头创建
void
protector::protect(e2e_buffer &_buffer, instance_t _instance) {
std::lock_guard<std::mutex> lock(protect_mutex_);
if (_instance > VSOMEIP_E2E_PROFILE04_MAX_INSTANCE) {
VSOMEIP_ERROR << "E2E Profile 4 can only be used for instances [1-255]";
return;
}
/** @req: [SWS_E2E_00363] */
if (verify_inputs(_buffer)) {
/** @req [SWS_E2E_00364] */
write_16(_buffer, static_cast<uint16_t>(_buffer.size()), 0);
/** @req [SWS_E2E_00365] */
write_16(_buffer, counter_, 2);
/** @req [SWS_E2E_00366] */
uint32_t its_data_id(uint32_t(_instance) << 24 | config_.data_id_);
write_32(_buffer, its_data_id, 4);
/** @req [SWS_E2E_00367] */
uint32_t its_crc = profile_04::compute_crc(config_, _buffer);
/** @req [SWS_E2E_0368] */
write_32(_buffer, its_crc, 8);
/** @req [SWS_E2E_00369] */
increment_counter();
}
}
implementation/e2e_protection/src/e2e/profile/profile04/profile_04.cpp
Profile 4 crc计算
uint32_t profile_04::compute_crc(const profile_config &_config, const e2e_buffer &_buffer) {
buffer_view its_before(_buffer, _config.offset_ + 8);
uint32_t computed_crc = e2e_crc::calculate_profile_04(its_before);
if (_config.offset_ + 12 < _buffer.size()) {
buffer_view its_after(_buffer, _config.offset_ + 12, _buffer.size());
computed_crc = e2e_crc::calculate_profile_04(its_after, computed_crc);
}
return (computed_crc);
}
implementation/e2e_protection/src/crc/crc.cpp
uint32_t e2e_crc::calculate_profile_04(buffer_view _buffer_view, const uint32_t _start_value) {
uint32_t crc = (_start_value ^ 0xFFFFFFFFU);
for (uint8_t byte : _buffer_view)
crc = lookup_table_profile_04_[static_cast<uint8_t>(byte ^ crc)] ^ (crc >> 8U);
return (crc ^ 0xFFFFFFFFU);
}
const uint32_t e2e_crc::lookup_table_profile_04_[256] = {
0x00000000U, 0x30850FF5U, 0x610A1FEAU, 0x518F101FU, 0xC2143FD4U, 0xF2913021U, 0xA31E203EU, 0x939B2FCBU,
0x159615F7U, 0x25131A02U, 0x749C0A1DU, 0x441905E8U, 0xD7822A23U, 0xE70725D6U, 0xB68835C9U, 0x860D3A3CU,
0x2B2C2BEEU, 0x1BA9241BU, 0x4A263404U, 0x7AA33BF1U, 0xE938143AU, 0xD9BD1BCFU, 0x88320BD0U, 0xB8B70425U,
0x3EBA3E19U, 0x0E3F31ECU, 0x5FB021F3U, 0x6F352E06U, 0xFCAE01CDU, 0xCC2B0E38U, 0x9DA41E27U, 0xAD2111D2U,
0x565857DCU, 0x66DD5829U, 0x37524836U, 0x07D747C3U, 0x944C6808U, 0xA4C967FDU, 0xF54677E2U, 0xC5C37817U,
0x43CE422BU, 0x734B4DDEU, 0x22C45DC1U, 0x12415234U, 0x81DA7DFFU, 0xB15F720AU, 0xE0D06215U, 0xD0556DE0U,
0x7D747C32U, 0x4DF173C7U, 0x1C7E63D8U, 0x2CFB6C2DU, 0xBF6043E6U, 0x8FE54C13U, 0xDE6A5C0CU, 0xEEEF53F9U,
0x68E269C5U, 0x58676630U, 0x09E8762FU, 0x396D79DAU, 0xAAF65611U, 0x9A7359E4U, 0xCBFC49FBU, 0xFB79460EU,
0xACB0AFB8U, 0x9C35A04DU, 0xCDBAB052U, 0xFD3FBFA7U, 0x6EA4906CU, 0x5E219F99U, 0x0FAE8F86U, 0x3F2B8073U,
0xB926BA4FU, 0x89A3B5BAU, 0xD82CA5A5U, 0xE8A9AA50U, 0x7B32859BU, 0x4BB78A6EU, 0x1A389A71U, 0x2ABD9584U,
0x879C8456U, 0xB7198BA3U, 0xE6969BBCU, 0xD6139449U, 0x4588BB82U, 0x750DB477U, 0x2482A468U, 0x1407AB9DU,
0x920A91A1U, 0xA28F9E54U, 0xF3008E4BU, 0xC38581BEU, 0x501EAE75U, 0x609BA180U, 0x3114B19FU, 0x0191BE6AU,
0xFAE8F864U, 0xCA6DF791U, 0x9BE2E78EU, 0xAB67E87BU, 0x38FCC7B0U, 0x0879C845U, 0x59F6D85AU, 0x6973D7AFU,
0xEF7EED93U, 0xDFFBE266U, 0x8E74F279U, 0xBEF1FD8CU, 0x2D6AD247U, 0x1DEFDDB2U, 0x4C60CDADU, 0x7CE5C258U,
0xD1C4D38AU, 0xE141DC7FU, 0xB0CECC60U, 0x804BC395U, 0x13D0EC5EU, 0x2355E3ABU, 0x72DAF3B4U, 0x425FFC41U,
0xC452C67DU, 0xF4D7C988U, 0xA558D997U, 0x95DDD662U, 0x0646F9A9U, 0x36C3F65CU, 0x674CE643U, 0x57C9E9B6U,
0xC8DF352FU, 0xF85A3ADAU, 0xA9D52AC5U, 0x99502530U, 0x0ACB0AFBU, 0x3A4E050EU, 0x6BC11511U, 0x5B441AE4U,
0xDD4920D8U, 0xEDCC2F2DU, 0xBC433F32U, 0x8CC630C7U, 0x1F5D1F0CU, 0x2FD810F9U, 0x7E5700E6U, 0x4ED20F13U,
0xE3F31EC1U, 0xD3761134U, 0x82F9012BU, 0xB27C0EDEU, 0x21E72115U, 0x11622EE0U, 0x40ED3EFFU, 0x7068310AU,
0xF6650B36U, 0xC6E004C3U, 0x976F14DCU, 0xA7EA1B29U, 0x347134E2U, 0x04F43B17U, 0x557B2B08U, 0x65FE24FDU,
0x9E8762F3U, 0xAE026D06U, 0xFF8D7D19U, 0xCF0872ECU, 0x5C935D27U, 0x6C1652D2U, 0x3D9942CDU, 0x0D1C4D38U,
0x8B117704U, 0xBB9478F1U, 0xEA1B68EEU, 0xDA9E671BU, 0x490548D0U, 0x79804725U, 0x280F573AU, 0x188A58CFU,
0xB5AB491DU, 0x852E46E8U, 0xD4A156F7U, 0xE4245902U, 0x77BF76C9U, 0x473A793CU, 0x16B56923U, 0x263066D6U,
0xA03D5CEAU, 0x90B8531FU, 0xC1374300U, 0xF1B24CF5U, 0x6229633EU, 0x52AC6CCBU, 0x03237CD4U, 0x33A67321U,
0x646F9A97U, 0x54EA9562U, 0x0565857DU, 0x35E08A88U, 0xA67BA543U, 0x96FEAAB6U, 0xC771BAA9U, 0xF7F4B55CU,
0x71F98F60U, 0x417C8095U, 0x10F3908AU, 0x20769F7FU, 0xB3EDB0B4U, 0x8368BF41U, 0xD2E7AF5EU, 0xE262A0ABU,
0x4F43B179U, 0x7FC6BE8CU, 0x2E49AE93U, 0x1ECCA166U, 0x8D578EADU, 0xBDD28158U, 0xEC5D9147U, 0xDCD89EB2U,
0x5AD5A48EU, 0x6A50AB7BU, 0x3BDFBB64U, 0x0B5AB491U, 0x98C19B5AU, 0xA84494AFU, 0xF9CB84B0U, 0xC94E8B45U,
0x3237CD4BU, 0x02B2C2BEU, 0x533DD2A1U, 0x63B8DD54U, 0xF023F29FU, 0xC0A6FD6AU, 0x9129ED75U, 0xA1ACE280U,
0x27A1D8BCU, 0x1724D749U, 0x46ABC756U, 0x762EC8A3U, 0xE5B5E768U, 0xD530E89DU, 0x84BFF882U, 0xB43AF777U,
0x191BE6A5U, 0x299EE950U, 0x7811F94FU, 0x4894F6BAU, 0xDB0FD971U, 0xEB8AD684U, 0xBA05C69BU, 0x8A80C96EU,
0x0C8DF352U, 0x3C08FCA7U, 0x6D87ECB8U, 0x5D02E34DU, 0xCE99CC86U, 0xFE1CC373U, 0xAF93D36CU, 0x9F16DC99U
};
2.2.2 Check
消息的验证主要是在收到消息阶段,也是在routing里面实现。
implementation/routing/routing_manager_impl.cpp
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
}
}
后续函数调用路径与 protect 类似,不再赘述。
3、vsomeip E2E 应用
vsomeip的发行包在 test/e2e_tests中提供了相应profile的应用例程,具体编译过程参考上一篇博客。
3.1 e2e 配置
client 和 service端配置方法相同
"e2e" :
{
"e2e_enabled" : "true", // E2E 插件使能
"protected" :
[
{
"service_id" : "0xd025",
"event_id" : "0x0001",
"profile" : "P04",
"variant" : "checker",
"crc_offset" : "64", //这里为报头偏移的 bit
"data_id" : "0x1011" // vsomeip实际发送会用_instance作为最高字节 its_data_id(uint32_t(_instance) << 24 | config_.data_id_);
},
{
"service_id" : "0xd025",
"event_id" : "0x8001",
"profile" : "P04",
"variant" : "checker",
"crc_offset" : "64",
"data_id" : "0x2d"
}
]
},
3.2 E2E Header 注意
加E2E保护时,根据profile4的标准长度,vsome是会覆盖掉E2E报头所在位置对应的data的12个字节,所以E2E报头不是位于 user data 后面的时候,一定要设计出放置报头的空间
3.3 测试数据
修改json配置文件,将报头放置在user data 后面;修改了报头计算的起始位置,从someip的Payload开计算