点击上方蓝字谈思实验室
获取更多汽车网络安全资讯
01
不安全的CAN总线
1、如何保证ECU接收到的数据是合法的?
比如ECU1接收ECU2发出的一帧0x100报文,协议层是不会区分是ECU1发的,还是非法接入OBD的CAN盒发的?如果ECU1接收到非ECU2发出的0x100报文岂不是很危险?
2、ECU如何知道另一个节点挂死
比如ECU1还是接收ECU2发出的一帧0x100报文,但是由于某些神秘原因(程序跑飞了)导致ECU2挂死或者掉线,那ECU1如何知道此时的接收到0x100无效?
CAN通讯是一种广播形式的通讯方式,自然协议层是无法做到数据合法性的校验,这部分工作需要应用层来完成。由此就出现了RollingCounter与Checksum。这两个东西好像也是功能安全的一部分,本期只是介绍这两个东西的原理,不对功能安全做过多讨论。
一个规范的CAN矩阵协议,每一帧报文都会要求有这个两个信号。如上图,从bit52到bit55是RollingCounter,取值范围0~15,ECU没发一次自动累加,满15就归零。从bit56到bit63(byte8)都是Checksum,取值范围0x00~0xFF,用来表示该条报文的校验值。
对于判断发送报文ECU有没有挂死很简单,只要在接收端对RollingCounter进行判断,几个周期内协议栈上的buff没有得到更新则就能判断为节点异常。
对于报文合法性的判断则就要负责得多,且会有各种花样
车企制定的通讯协议时,除了制定矩阵信号外,会规定Checksum的计算方法,比如这个是采用多项式的CRC校验算法。
并且规定出需要校验的数据,有的是对矩阵的前7个byte进行校验,有的则要增加报文ID作为校验计算的输入,各种花样都有。
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * CRC8校验子程序0x1D(x8+x4+x3+x2+1) * * * * * * * * * * * *
* * 参数1,uint8_t *data:需要计算的数据 * * * * * * * * * * *
* * 参数1,uint16_t len:需要计算的数据字节长度 * * * * * * * *
* * 返回值,uint8_t crc8:计算出的CRC值 * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
uint8_t crc_8find(uint8_t *data, uint16_t len)
{
uint8_t crc8 = 0x00;
while( len-- )
{
crc8 = crc_table[crc8 ^ *data];
data++;
}
return crc8;
}
甚至贴心的给出校验计算的伪代码,这函数写法C、C++应该都可以直接运行的。理解下上面的代码!
当ECU2发出0x100之前,会对RollingCounter累加,并且通过计算包含RollingCounter的前七个字节的CRC值,填充到数据域的第8个字节;
ECU1接收到0x100的时候使用同样的校验算法,计算数据域的前7个字节,并且与第8个字节发送源计算的CRC值进行比较。如果两者相同,则数据合法。
02
CANOE仿真
接下来在CANOE的CAPL进行RollingCounter与Checksum的模拟
创建两个ECU节点,ECU1为发送节点,ECU2为接收节点。ECU2会对ECU1的节点做Checksum,如果非法数据会在log窗口中打印出来。ECU1会而这个IG是用来模拟非法的数据发送。
int GetCrcChecksum (int crc_position ,message *data)
{
byte checksum;
byte bitIndex;
byte byteIndex;
byte tdata;
checksum = 0x00;
for (byteIndex = DBLookup(data).dlc; byteIndex >= 1; byteIndex--)
{
if(byteIndex-1 != crc_position)
{
tdata = data.byte(byteIndex-1);
}
else
{
tdata = 0;
}
checksum ^= tdata;
for (bitIndex = 0; bitIndex < 8; bitIndex++)
{
if ((checksum & 0x80) != 0)
{
checksum = (checksum << 1) ^ 0x1D; // cb_CRC_POLY: 0x1D
}
else
{
checksum = (checksum << 1);
}
}
}
checksum &= 0xFF;
return (checksum);
}
CAPL计算Checksum的函数,在ECU1与ECU2里都存在
on timer Timer1
{
Req.rollingCounter_0x100=LiveCount;
Req.checksum_0x100=GetCrcChecksum(7,Req);
output(Req);
setTimer(Timer1,20);
LiveCount=LiveCount+1;
if(LiveCount==16)
{
LiveCount=0;
}
ECU1的定时器函数,发送前调用GetCrcChecksum函数生成Checksum
on message RCTest1
{
if(this.checksum_0x100==GetCrcChecksum(7,this))
{
Rep=this;
}else
{
write("Invaild Message");
}
}
ECU2接收事件中重新计算Checksum跟发送报文里的Checksum进行比较,不同则抛出错误
按F2,开始仿真,可以监控到ECU1发送出来的rollingCounter与Checksum
在Write窗口中打印ECU2接收到的数据
在IG节点设置一个非法的报文,rollingcounter与Checksum都设置0
对非法数据进行抛出。
来源:古德曼汽车工业
精品活动推荐
原文始发于微信公众号(谈思实验室):CAN总线报文数据一致性校验
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论