虽然capl对通用协议(ip/tcp/udp/icmp/someip)提供了接口和函数,用来组装报文,但是对于应用层上的协议,或一些客户自定义的协议,并没有现成的接口和方法来组装它们,所以学会如何自定义组装报文,就显得很重要


用capl自带的方法组装一条ICMPv4报文

capl提供了一系列的方法来组装和发送报文

如何组装自定义报文_网络协议

代码如下:

void InternalSendIcmpv4EchoRequest(char senderMacAddress[], 
char targetMacAddress[],
char senderIpv4Address[],
char targetIpv4Address[],
int length)
{
ethernetPacket pkt;
byte data[1500];
int i;

setBusContext(getBusNameContext("Ethernet"));

for (i=0; i<elcount(data); i++)
{
data[i] = 0xff;
}

pkt.icmpv4.echo.init();//相当于添加icmpv4 echo request协议首部,当然icmp的下层ipv4首部也是有的

pkt.ipv4.ResizeData(length);//设置ipv4 payload的大小

pkt.ipv4.SetData(8, data, length);//icmp echo request header 8个字节,这里要从index=8开始

pkt.ipv4.source.ParseAddress(senderIpv4Address);
pkt.ipv4.destination.ParseAddress(targetIpv4Address);

pkt.source.ParseAddress(senderMacAddress);
pkt.destination.ParseAddress(targetMacAddress);

pkt.CompletePacket();

output(pkt);
}

调用protocol.init()方法会自动对报文进行协议的封装,里面的字段会全部使用默认值,除非用户手动设置字段

这里有个疑问,为什么是pkt.icmpv4.echo.init(),而不是pkt.icmpv4.init()


因为icmp报文有多种类型,它们除了type、code和checksum外,其他字段并不相同,所以icmp报文还需要进一步确定是哪种类型的icmp,比如icmpv4.echo表示这是一条icmpv4中的echo request报文


用capl自定义组装一条icmpv4报文

如果我不用icmpv4.echo.init()函数来封装icmpv4首部,而是自定义首部呢?

首部里有多个字段,每个字段的字节数都不同,和struct结构体是不是很类似,所以我们用结构体来构造协议层

struct ICMPv4
{
byte type;
byte code;
word checksum;
word identifier;
word sequence;
byte data[50];
};

代码如下:

on key 't'
{
ethernetPacket pktt;
char senderMac[18] = "02:00:00:00:00:01";
char targetMac[18] = "08:00:00:00:10:01";
char senderIpv4[18] = "192.168.1.10";
char targetIpv4[18] = "192.168.1.20";
byte buffer[2000];
word temp = 0;
int i;
int j;
struct ICMPv4
{
byte type;
byte code;
word checksum;
word identifier;
word sequence;
byte data[50];
};
struct ICMPv4 icmpv4;
byte ipv4Payload[__size_of(struct ICMPv4)];//ip层的payload就是icmpv4的首部和数据

icmpv4.type = 0x08;//表示是一条icmpv4 echo request
icmpv4.code = 0x00;
icmpv4.checksum = 0x0000;//先设置为0,是为了计算checksum
icmpv4.identifier = 0x0000;
icmpv4.sequence = 0x0000;
for (i=0; i<elcount(icmpv4.data); i++)
{
icmpv4.data[i] = i;//对icmpv4 data赋值
}

memcpy_h2n(buffer, 0, icmpv4);//把icmpv4首部和数据赋值到buffer数组中,checksum设置为0

temp = GetOrVerifyChecksumValue(buffer, __size_of(struct ICMPv4));//计算这个buffer的checksum
write("checksum: 0x%4x", temp);

icmpv4.checksum = temp;//把计算出来的checksum赋值给checksum字段

memcpy_h2n(ipv4Payload, 0, icmpv4);//得到正确的icmpv4的首部加数据

pktt.ipv4.Init();//报文封装了ipv4协议

pktt.ipv4.source.ParseAddress(senderIpv4);
pktt.ipv4.destination.ParseAddress(targetIpv4);
pktt.ipv4.protocol = 0x01;//这是必须的,表明上层协议是icmp

pktt.ipv4.SetData(0, ipv4Payload, 8 + elcount(icmpv4.data));//把icmpv4首部和数据当做ipv4的payload

pktt.source.ParseAddress(senderMac);
pktt.destination.ParseAddress(targetMac);

pktt.CompletePacket();

output(pktt);
}

以上就是自定义协议层如何组装报文