IP_Header

本文详细解析IPv4报头各个字段的功能与作用,包括版本号、包头长度、服务类型等,并介绍了如何通过这些字段实现数据包的传输、分段及重组。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

版本号(Version)长度4比特。标识目前采用的IP协议的版本号。一般的值为0100(IPv4),IPv6的值(0110)。

 

IP包头长度(Header Length):长度4比特。这个字段的作用是为了描述IP包头的长度,因为在IP包头中有变长的可选部分。该部分占4个bit位,单位为32bit(4个字节),即本区域值= IP头部长度(单位为bit)/(8*4),因此,一个IP包头的长度最长为“1111”,即15*4=60个字节。IP包头最小长度为20字节。对于标准ipv4报头,这个字段的值肯定是20/4=5(10进制)=0101(2进制)。

 

服务类型(Type of Service):长度8比特。这个子段可以拆分成两个部分:Precedence和TOS。TOS目前不太使用。而Precedence则用于QOS("Quality of Service",中文名为"服务质量")应用。(TOS字段的详细描述RFC 1340 1349)。

 

IP包总长(Total Length):长度16比特。以字节为单位计算的IP包的长度 (包括头部和数据),所以IP包最大长度65535字节。

标识符(Identifier):长度16比特。该字段和Flag和Fragment Offest字段联合使用,对大的上层数据包进行分段(fragment)操作。

 

标记(Flag):长度3比特。该字段第一位不使用。第二位是DF位,DF位设为1时表明路由器不能对该上层数据包分段。如果一个上层数据包无法在不分段的情况下进行转发,则路由器会丢弃该上层数据包并返回一个错误信息。第三位是MF位,当路由器对一个上层数据包分段,则路由器会在最后一个分段的IP包的包头中将MF位设为0,其余IP包的包头中将MF设为1。

 

Bit 0: reserved, must be zero
Bit 1: (DF) 0 = May Fragment, 1 = Don't Fragment.
Bit 2: (MF) 0 = Last Fragment, 1 = More Fragments.

 

分段序号(Fragment Offset):长度13比特。该字段对包含分段的上层数据包的IP包赋予序号。由于IP包在网络上传送的时候不一定能按顺序到达,这个字段保证了目标路由器在接受到IP包之后能够还原分段的上层数据包。到某个包含分段的上层数据包的IP包在传送是丢失,则整个一系列包含分段的上层数据包的IP包都会被要求重传。

 

生存时间(Time to Live):长度8比特。当IP包进行传送时,先会对该字段赋予某个特定的值。当IP包经过每一个沿途的路由器的时候,每个沿途的路由器会将IP包的TTL值减少1。如果TTL减少为0,则该IP包会被丢弃。这个字段可以防止由于故障而导致IP包在网络中不停被转发。

 

协议(Protocol):长度8比特。标识了上层所使用的协议。

 

头部校验(Header Checksum):长度16比特,由于IP包头是变长的,所以提供一个头部校验来保证IP包头中信息的正确性。

 

源IP地址(Source IP Addresses):长度16比特。标识了这个IP包的源IP地址。

 

目的IP地址(Destination IP Address):长度16比特。标识了这个IP包的目的IP地址。

 

可选项(Options):这是一个可变长的字段,长度为0或32bit的整倍数,最大320bit,如果不足则填充到满。该字段由起源设备根据需要改写。可选项目包含以下内容:

 

    松散源路由(Loose source routing):给出一连串路由器接口的IP地址。IP包必须沿着这些IP地址传送,但是允许在相继的两个IP地址之间跳过多个路由器。

    严格源路由(Strict source routing):给出一连串路由器接口的IP地址。IP包必须沿着这些IP地址传送,如果下一跳不在IP地址表中则表示发生错误。

    路由记录(Record route):当IP包离开每个路由器的时候记录路由器的出站接口的IP地址。

    时间戳(Timestamps):当IP包离开每个路由器的时候记录时间。

 

填充(Padding):因为IP包头长度(Header Length)部分的单位为32bit,所以IP包头的长度必须为32bit的整数倍。因此,在可选项后面,IP协议会填充若干个0,以达到32bit的整数倍。

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netinet/ip.h> #include <netinet/udp.h> // 计算校验和函数 unsigned short checksum(void *b, int len) { unsigned short *buf = b; unsigned int sum = 0; unsigned short result; for (sum = 0; len > 1; len -= 2) sum += *buf++; if (len == 1) sum += *(unsigned char *)buf; sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16); result = ~sum; return result; } int main() { int sockfd; char buffer[1024]; struct sockaddr_in dest_addr; // 创建原始套接字 if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror("socket() error"); exit(EXIT_FAILURE); } // 设置目标地址 memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.sin_family = AF_INET; dest_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 目标IP // 构建IP头部 struct iphdr *ip_header = (struct iphdr *)buffer; ip_header->ihl = 5; // 头部长度(32位字) ip_header->version = 4; // IPv4 ip_header->tos = 0; // 服务类型 ip_header->tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) + 5; // 总长度 ip_header->id = htons(54321); // 标识 ip_header->frag_off = 0; // 分片偏移 ip_header->ttl = 64; // 生存时间 ip_header->protocol = IPPROTO_UDP; // 协议类型 ip_header->saddr = inet_addr("127.0.0.1"); // 源IP ip_header->daddr = dest_addr.sin_addr.s_addr; // 目标IP ip_header->check = 0; // 先置0再计算校验和 ip_header->check = checksum((unsigned short *)ip_header, ip_header->ihl*4); // 构建UDP头部 struct udphdr *udp_header = (struct udphdr *)(buffer + sizeof(struct iphdr)); udp_header->source = htons(12345); // 源端口 udp_header->dest = htons(54321); // 目标端口 udp_header->len = htons(sizeof(struct udphdr) + 5); // UDP总长度 udp_header->check = 0; // 先置0 // 添加UDP数据 char *data = buffer + sizeof(struct iphdr) + sizeof(struct udphdr); strcpy(data, "TEST"); // 计算UDP校验和(包含伪头部) char pseudo[1024]; struct pseudo_udp_header { u_int32_t src_ip; u_int32_t dst_ip; u_int8_t zero; u_int8_t protocol; u_int16_t length; } *pseudo_header = (struct pseudo_udp_header *)pseudo; pseudo_header->src_ip = ip_header->saddr; pseudo_header->dst_ip = ip_header->daddr; pseudo_header->zero = 0; pseudo_header->protocol = IPPROTO_UDP; pseudo_header->length = udp_header->len; // 拼接伪头部+UDP头部+数据 memcpy(pseudo + sizeof(struct pseudo_udp_header), udp_header, ntohs(udp_header->len)); udp_header->check = checksum(pseudo, sizeof(struct pseudo_udp_header) + ntohs(udp_header->len)); // 发送数据包 if (sendto(sockfd, buffer, ip_header->tot_len, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) < 0) { perror("sendto() error"); close(sockfd); exit(EXIT_FAILURE); } printf("UDP packet sent. Check Wireshark for verification.\n"); close(sockfd); return 0; } 分析
最新发布
08-08
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值