roger 发表于 2019-5-3 18:44:22

UDP首部校验的计算

# 例一
![](data/attachment/album/201905/03/173054dhro9vo1f9li79v8.png)
### 得出数值:
![](data/attachment/album/201905/03/174909nv88z8wb8dlfkw8k.jpg)
# 例二
UDP首部校验和的计算IP/ICMP/IGMP/TCP/UDP等协议的校验和算法都是相同的,算法如下:
在发送数据时,为了计算IP数据包的校验和。应该按如下步骤:
1.把IP数据包的校验和字段置为0;
2.把首部看成以16位为单位的数字组成,依次进行二进制反码求和;
3.把得到的结果存入校验和字段中。
在接收数据时,计算数据包的校验和相对简单,按如下步骤:
1.把首部看成以16位为单位的数字组成,依次进行二进制反码求和,包括校验和字段;
2. 检查计算出的校验和的结果是否等于零(反码应为16个1);
3.如果等于零,说明被整除,校验是和正确。否则,校验和就是错误的,协议栈要抛弃这个数据包。
所谓的二进制反码求和,即为先进行二进制求和,然后对和取反。计算对IP首部检验和的算法如下:
1.把IP数据包的校验和字段置为0;
2.把首部看成以16位为单位的数字组成,依次进行二进制求和(注意:求和时应将最高位的进位保存,所以加法应采用32位加法);
3.将上述加法过程中产生的进位(最高位的进位)加到低16位(采用32位加法时,即为将高16位与低16位相加,之后还要把该次加法最高位产生的进位加到低16位)
4.将上述的和取反,即得到校验和。
5.其中,二进制反码求和的计算方法:
首先,我们计算如图所示的部分和。我们把每一列相加,如果有进位,就加到下一列。注意以下几点:
![](data/attachment/album/201905/03/174437i44iynaaofka6vve.jpg)
( 二进制记法的部分和)
6.补充:此次操作是二进制求和,最后一列(左第一位得到数值是5=101)
①当我们加第1列(最右边一列)的时候,我们得到7。在二进制中,数7是111。我们保留最右边的1,把其余的位进到第2列和第3列。
②当我们加第2列时,我们计入从第1列来的进位。结果是8,它是二进制的1000。我们保留第一个位(最右边的),把其余100进位给第3列、第4列和第5列。
③对每一列重复以上过程。
④当我们加完最后一列时,我们有两个1没有列可以进行相加。这两个1在下一个步骤中应与部分和(Partial sum)相加。
# 实战
## UDP报文图解
![](data/attachment/album/201905/03/183118idox7eakkgc9k7kk.png)
为了弄清楚这些字段究竟是什么东西,下面我们使用 wireshark 来抓取一个 UDP 包来详细分析。为了制造这个 UDP 包,使用如下代码来向某 ip 地址发送一段数据(这个 ip 不一定非得实际存在,我们只需要观察基于 UDP 协议封装的数据,所以只要能被 wireshark 获取就行)
```
import java.io.IOException;
import java.net.*;
public class Client {

    private DatagramSocket socket;

    public Client() throws SocketException {
      socket = new DatagramSocket();
    }
    public void run() throws IOException {
      InetAddress ip = InetAddress.getByName("11.111.111.111");
      String message = "hello UDP";
      byte[] buffer = message.getBytes();
      DatagramPacket sendPacket = new DatagramPacket(buffer, buffer.length, ip, 12345);
      socket.send(sendPacket);
    }
    public static void main(String[] args) throws IOException {
      new Client().run();
    }
}
```

在运行之前,启动 wireshark 捕获,之后找到 Destination 栏为 11.111.111.111 的 UDP 行,类似于下图所示
![](data/attachment/album/201905/03/183529het2gtz7dtjjkjqj.png)
然后找到这个 UDP 包的详细信息
![](data/attachment/album/201905/03/183610znqdff1fn5uzc7ch.png)
以及这段信息的十六进制表示
![](data/attachment/album/201905/03/183656gcfczntzvhly5ygy.png)

|key |human| hex|
| -------- | -------- | -------- |
|Source|      192.168.1.106|      c0a8 016a|
|Destination|      11.111.111.111|      0b6f 6f6f|
|Protocol|      UDP(17)|      11|
|Length      |17      |11|
|Source Port|      63549      |f83d|
|Destination Port|12345      |3039
|Length      |17|      11|
|Checksum|      0xb12d      |b12d|
|Data      |hello UDP|      6865 6c6c 6f20 5544 5000|
然后就可以开始着手校验和的计算了,但在这之前还应注意,上表中有一项 Checksum ,这是发送方根据发送内容计算出来校验和,接受方需要根据收到的内容重新计算一遍校验和,然后再对比两者。所以在接收方计算时应该忽略这里 Checksum 项。

## 校验和的计算规则
校验和的计算规则很简单,就是将上表中所有的 16 进制数加起来,之后取反码。有一点需要注意的是,如果遇到最高位进位,那么需要对结果进行回卷,意思是
![](data/attachment/album/201905/03/184239bui5dwwkrzofl24e.png)
简单来说,就是将要进的那一位加到尾部,上面是以二进制演示的,对于16进制同样适用。
页: [1]
查看完整版本: UDP首部校验的计算