📮 IP报头结构:数据包的"信封"

28 阅读6分钟

知识点编号:027
难度等级:⭐⭐(了解)
面试频率:🔥🔥


🎯 一句话总结

IP报头就像信封,上面写着发件人、收件人、邮戳等信息!📮


📦 IPv4报头结构

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|版本(4)| 头长度(4)|服务类型(8)|        总长度(16)              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          标识(16)             |标志(3)|    片偏移(13)         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   TTL(8)      |   协议(8)     |        头部校验和(16)          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        源IP地址(32)                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      目标IP地址(32)                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    选项(可变长度)                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      数据                                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

最小长度:20字节
最大长度:60字节(包含选项)

🔍 字段详解

1. 版本(Version,4位)

值:4(IPv4)或 6(IPv6)

示例:
0100 = 4 (IPv4)
0110 = 6 (IPv6)

作用:区分IP版本

2. 头长度(Header Length,4位)

单位:4字节
范围:5-15(20-60字节)

计算:
值=5 → 5×4 = 20字节(最小)
值=15 → 15×4 = 60字节(最大)

示例:
0101 = 520字节头部
0110 = 624字节头部(有4字节选项)

3. 服务类型(Type of Service,8位)

已废弃,现用于:
- 3位:优先级
- 4位:服务类型(D-低延迟、T-高吞吐、R-高可靠、C-低成本)
- 1位:保留

现代:
用DSCP(Differentiated Services Code Point)
实现QoS(服务质量)

4. 总长度(Total Length,16位)

整个IP包的长度(头部+数据)
单位:字节
范围:20-65535字节

示例:
总长度 = 1500字节
头长度 = 20字节
数据长度 = 1500 - 20 = 1480字节

5. 标识(Identification,16位)

用于分片重组
同一个包的所有片有相同标识

示例:
原包:标识=12345
片1:标识=12345
片2:标识=12345
片3:标识=12345

接收方根据相同标识重组

6. 标志(Flags,3位)

bit 0:保留,必须为0
bit 1:DF(Don't Fragment)- 不分片
bit 2:MF(More Fragments)- 还有更多片

示例:
DF=1, MF=0:不允许分片
DF=0, MF=1:允许分片,且还有更多片
DF=0, MF=0:最后一片或没分片

7. 片偏移(Fragment Offset,13位)

单位:8字节
表示该片在原包中的位置

示例:
片1:偏移=0(0字节)
片2:偏移=185(185×8=1480字节)
片3:偏移=370(370×8=2960字节)

8. TTL(Time To Live,8位)

生存时间,每跳减1
范围:0-255

常见值:
64:Linux/Mac
128:Windows
255:路由器

9. 协议(Protocol,8位)

上层协议类型

常见值:
1:ICMP
6:TCP
17:UDP
41:IPv6
89:OSPF

示例:
协议=6 → 上层是TCP
协议=17 → 上层是UDP

10. 头部校验和(Header Checksum,16位)

只校验IP头部,不校验数据

计算:
1. 把头部分成16位的字
2. 全部相加
3. 结果取反码

验证:
接收方重新计算校验和
如果不匹配,丢弃包

11. 源IP地址(32位)

发送方的IP地址

示例:
192.168.1.10
→ 二进制:11000000.10101000.00000001.00001010

12. 目标IP地址(32位)

接收方的IP地址

示例:
8.8.8.8
→ 二进制:00001000.00001000.00001000.00001000

13. 选项(Options,可变长度)

可选字段,很少使用

常见选项:
- 记录路由(Record Route)
- 时间戳(Timestamp)
- 松散源路由(Loose Source Routing)
- 严格源路由(Strict Source Routing)

长度:0-40字节

💻 Java解析IP头部

public class IPHeaderParser {
    
    public static void parseIPHeader(byte[] packet) {
        // 版本和头长度(第1字节)
        int versionAndLength = packet[0] & 0xFF;
        int version = (versionAndLength >> 4) & 0x0F;
        int headerLength = (versionAndLength & 0x0F) * 4;
        
        System.out.println("版本: " + version);
        System.out.println("头部长度: " + headerLength + " 字节");
        
        // 服务类型(第2字节)
        int tos = packet[1] & 0xFF;
        System.out.println("服务类型: " + tos);
        
        // 总长度(第3-4字节)
        int totalLength = ((packet[2] & 0xFF) << 8) | (packet[3] & 0xFF);
        System.out.println("总长度: " + totalLength + " 字节");
        
        // 标识(第5-6字节)
        int id = ((packet[4] & 0xFF) << 8) | (packet[5] & 0xFF);
        System.out.println("标识: " + id);
        
        // 标志和片偏移(第7-8字节)
        int flagsAndOffset = ((packet[6] & 0xFF) << 8) | (packet[7] & 0xFF);
        boolean df = ((flagsAndOffset >> 14) & 0x01) == 1;
        boolean mf = ((flagsAndOffset >> 13) & 0x01) == 1;
        int offset = (flagsAndOffset & 0x1FFF) * 8;
        
        System.out.println("DF标志: " + df);
        System.out.println("MF标志: " + mf);
        System.out.println("片偏移: " + offset + " 字节");
        
        // TTL(第9字节)
        int ttl = packet[8] & 0xFF;
        System.out.println("TTL: " + ttl);
        
        // 协议(第10字节)
        int protocol = packet[9] & 0xFF;
        String protocolName = getProtocolName(protocol);
        System.out.println("协议: " + protocol + " (" + protocolName + ")");
        
        // 校验和(第11-12字节)
        int checksum = ((packet[10] & 0xFF) << 8) | (packet[11] & 0xFF);
        System.out.println("校验和: " + Integer.toHexString(checksum));
        
        // 源IP地址(第13-16字节)
        String srcIP = String.format("%d.%d.%d.%d",
            packet[12] & 0xFF,
            packet[13] & 0xFF,
            packet[14] & 0xFF,
            packet[15] & 0xFF
        );
        System.out.println("源IP: " + srcIP);
        
        // 目标IP地址(第17-20字节)
        String dstIP = String.format("%d.%d.%d.%d",
            packet[16] & 0xFF,
            packet[17] & 0xFF,
            packet[18] & 0xFF,
            packet[19] & 0xFF
        );
        System.out.println("目标IP: " + dstIP);
    }
    
    private static String getProtocolName(int protocol) {
        switch (protocol) {
            case 1: return "ICMP";
            case 6: return "TCP";
            case 17: return "UDP";
            case 41: return "IPv6";
            case 89: return "OSPF";
            default: return "Unknown";
        }
    }
}

🐛 常见面试题

Q1:IPv4报头有哪些重要字段?

答案:

核心字段:

1. 版本(4位)- 区分IPv4/IPv6
2. 头长度(4位)- 头部大小
3. 总长度(16位)- 整个包大小
4. 标识(16位)- 分片标识
5. 标志(3位)- DF/MF
6. 片偏移(13位)- 分片位置
7. TTL(8位)- 生存时间
8. 协议(8位)- 上层协议(TCP/UDP)
9. 校验和(16位)- 头部校验
10. 源IP(32位)- 发送方地址
11. 目标IP(32位)- 接收方地址

最小长度:20字节
最大长度:60字节

Q2:IP头部校验和是如何计算的?

答案:

计算步骤:

1. 校验和字段设为0
2. 把IP头部分成16位的字
3. 所有字相加
4. 如果有进位,加到最低位
5. 结果取反码

示例:
头部:0x4500 0x003c 0x1c46 0x4000 0x4006 0x0000 ...
相加:0x4500 + 0x003c + 0x1c46 + ... = 0xb1e6
取反:~0xb1e6 = 0x4e19
校验和:0x4e19

验证:
接收方重新计算
如果结果是0xFFFF,校验通过 ✓
否则,头部损坏,丢弃 ✗

注意:
只校验IP头部,不校验数据
TCP/UDP有自己的校验和

🎓 总结

IP报头的关键点:

  1. 最小20字节:固定字段
  2. 最大60字节:包含选项
  3. 核心字段:源IP、目标IP、TTL、协议
  4. 分片字段:标识、标志、偏移

记忆口诀

版本头长服务型 📋
总长标识加标志 🏷️
TTL协议校验和 ✅
源IP目标IP地址 📮

文档创建时间:2025-10-31