UDP协议报文分析和主动发送UDP简单实现

前言

  用 Wireshark 简单分析UDP报文解析,学习UDP报文的解析。在需要编写底层UDP报文的环境下(单片机等),简单编排UDP报文达到主动传输的目的。

一、Wireshark 报文解析

1、UDP报文实例

  
在这里插入图片描述

2、报文格式分析

  一帧UDP报文由四个部分组成:以太网头、IP头、UDP头、数据。

帧头帧数据含义对应报文(HEX)位数
以太网头目标MAC地址00:aa:00:00:00:7b6*8
源MAC地址e0:db:55:f8:d1:e56*8
协议类型08 002*8
IP头IP协议版本44
IP头长度54
服务类型TOS008
报文总长度(IP报文开始)00 212*8
标识符(序号)c2 a12*8
标志位 00 002*8
帧存活周期TTL408
数据协议118
IP校验和32 502*8
源IP地址c0 a8 02 0f4*8
目标IP地址c0 a8 02 7b4*8
UDP头源端口号2c ee2*8
目标端口号01 f62*8
报文总长度(UDP报文开始)00 0d2*8
UDP校验和17 f32*8
数据帧传输数据11 11 11 11 11n*8

①、以太网头

以太网头常用格式
  以太网头使用两种标准帧格式。第一种是上世纪80年代初提出的DIXv2格式,即EthernetII帧格式。EthernetII后来被IEEE802标准接纳,并写进了IEEE802.3x-1997的3.2.6节。第二种是1983年提出的IEEE802.3格式。普通情况下使用EthernetII帧格式更简单便捷。
  报文中 Type 为" 08 00 "表示后续报文的协议为IP协议帧,若 Type 为"08 06 "表示后续报文为ARP帧。
  

②、IP头

  若为主动发送的UDP报文,IP头中开头的“45 00”以及“00 00 40 11”可以不用修改,主要修改其他数据。其中“40”表示帧存活时间为64秒,“11”表示后续协议为UDP协议。
  IP头的数据帧主要修改的部分为:IP头开始的数据长度、标识符id、IP校验、对端IP、本机IP。
  数据长度:IP头的长度+后续报文长度。例如实例中的报文长度为0x0021(33),即IP头长度20、UDP头长度8和数据长度5相加(20+8+5)而来。
  标识符ID:可以理解为报文的编号,一般是由上一帧的标识符计算加一。
  对端IP:目标IP地址。
  本机IP:本机IP地址。
  

③、UDP头

  UDP头的数据帧主要注意UDP校验和的计算方式,需要同时计算UDP伪首部。

二、UDP主动传输数据的实现方式

1、说明

  以下方法的测试环境是在局域网内,且无其他设备同时连接的环境下实现的,仅用与点对点的传输方式。

2、发送UDP前的约定

  在发送UDP报文前,想要对端设备收到UDP报文,就必须知道对端的设备部分信息:目标MAC地址、目标IP地址、目标端口号。其中目标IP地址和目标端口号可以在约定后直接进行绑定,但目标MAC地址在很多情况下无法直接得出,且在对端设备更换后会随着变动。此时,我们可以用ARP协议获取MAC地址。

3、ARP报文

  ARP(Address Resolution Protocol),地址解析协议,是根据IP地址获取物理地址的一个TCP/IP协议。
  在发送UDP报文前,可以先发送一帧ARP报文请求,报文需要修改的部分为:本机MAC地址(30:3a:64:9f:32:06)、本机IP(0xc0a8 0067)、目标IP(0xc0a8 0065),其他部分可以参照图中请求报文的数据直接写死。报文中 06 04 后的两个字节是Opcode, 00 01 表示ARP请求报文,若为 00 02 表示应答报文。
  发送ARP报文后会收到ARP的回复报文,回复报文中包含目标IP地址和MAC地址,和本机的IP地址和MAC地址,可以在确认ARP回复报文的目标IP为本机IP后,再将该MAC地址缓存,取得需要的MAC地址为(3e:c8:be:6e:f4:06)。
  ARP报文举例,如图所示:
  请求报文:
请求报文
  应答报文:
应答报文

4、UDPIP报文

  通过ARP报文获取到对端MAC地址后,就可以开始编辑发送UDP报文了。
  (1)编写以太网头部。 写入目标MAC地址(00:aa:00:00:00:7b)、本机MAC地址(e0:db:55:f8:d1:e5)、Type(0x08 00)。
  (2)将IP头的数据长度、IP校验和的四个字节置为0,先填充的IP头部的其他位。 写入IP版本以及长度(0x45)、区分服务领域(0x00)、ID号(0xc2 a1)、标志位Flags(0x00)、ttl(0x40)、协议号(0x11)、本机IP地址(0xc0 a8 02 0f)、目标IP地址(0xc0 a8 02 7b)。
  (3)将UDP头的数据长度、IP校验和的四个字节置为0,先填充的UDP头部的其他位。 写入本机源端口(0x2c ee)、目标端口(0x01 f6)、ID号(0x00 00)。
  (4)填充UDP需要传输的数据。 在UDP报文头之后写入需要传输的数据(0x11 11 11 11 11)。
  (5)根据传输的数据长度,填充IP头中的总长度(0x00 21)。 该长度计算方式为:IP头报文长度20 + UDP报文头长度8 + 数据长度5 = 33。
  (6)根据传输的数据长度,填充UDP头中的总长度(0x00 0d)。 该长度计算方式为:UDP报文头长度8 + 数据长度5 = 13。
  (7)根据IP头报文已填充数据,计算校验和,填充IP头中的校验位(0x32 50)。
  (7)根据IP头和UDP头报文已填充数据,计算校验和,填充UDP头中的校验位(0x17 f3)。

三、UDP报文校验

1、IP校验和计算

  对IP报文头从首部开始以16bit为单位求和,将计算结果溢出16bit的部分加在低16bit上,取反。
  忽略校验位数据,将上述实例报文的IP头从首部开始以16bit为单位拆分后,得到一组数据:0x4500,0x0021,0xC2A1,0x0000,0x4011,0xC0A8,0x020F,0xC0A8,0x027B。
  将上述拆分的16bit数据累加:   0x4500+0x0021+0xC2A1+0x0000+0x4011+0xC0A8+0x020F+0xC0A8+0x027B=0x2 CDAD;
  将上结果的高16位加在低16位数据得到:0xCDAF;
  将得到的0xCDAF作位取反得:0x3250。
简单代码段如下:

/*sendbuf[]是要发送的报文,计算前要先填充其他数据*/
static u16_t
ipchksum()
{
  u16_t i,index=14; //从sendbuf[14]开始计算,即IP头开始位置。
  u32_t sum=0;

  for(i=0;i<5;i++){
	  sum+=((u16_t)sendbuf[index]<<8)+sendbuf[index+1];
	  index+=2;
  }
  index+=2;
  for(i=0;i<4;i++){
	  sum+=((u16_t)sendbuf[index]<<8)+sendbuf[index+1];
	  index+=2;
  }

  sum = (sum >> 16) + (sum & 0xffff);

  return (~sum & 0xffff);
}

2、UDP校验和计算

  对UDP报文头从首部开始以16bit为单位求和,求和时包含后续所有数据以及UDP伪首部数据;将计算结果溢出16bit的部分加在低16bit上,取反。
  (1)先计算伪首部,伪首部由四部分组成:源地址(0xc0a8,0x020f)、目的地址(0xc0a8,0x027b)、UDP数据长度(0x000d)、协议类型(0x0011)。将UDP伪首部以16位求和得到(0x0001 85F8)。
  (2)再计算UDP头部报文,由四部分组成:源端口(0x2cee)、目标端口(0x01f6)、UDP数据长度(0x000d)、数据(0x1111、0x1111、0x1100)。以16位求和得到(0x6213)。这里要注意的是数据结尾如果为单数,需要在低8位填充0x00,而不是高八位填充,即0x11填充为0x1100再求和。
  (3)将UDP的伪首部和与UDP头部和求和(0x1 85f8 + 0x6213);将计算结果的高16位加到低16位得到(0xe80c);进行取反运算,得到最终UDP校验和0x17f3。

简单代码段如下。

/*sendbuf[]是要发送的报文,计算前要先填充其他数据*/
static u16_t
udpchksum(u16_t datalen)
{
  u16_t i,index=26;
  u32_t sum=0;

  sum = 0x11; //计算伪首部
  for(i=0;i<7;i++){
	  sum+=((u16_t)sendbuf[index]<<8)+sendbuf[index+1];
	  index+=2;
  }
  index-=2;
  sum+=((u16_t)sendbuf[index]<<8)+sendbuf[index+1];
  index+=4;

  for(i=0;i<(datalen/2);i++){
	  sum+=((u16_t)sendbbf[index]<<8)+sendbuf[index+1];
	  index+=2;
  }
  if(datalen%2){
      sum+=((u16_t)sendbbf[index]<<8);
  }
  
  sum = (sum >> 16) + (sum & 0xffff);

  return (~sum & 0xffff);
}
  • 4
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Conrad_Feng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值