linux网络编程之IP协议首部格式与其配套使用的四个协议(ARP,RARP,ICMP,IGMP)和TCP、UDP协议头结构总结

时间:2021-12-13 10:31:24

首先声明,这篇博客是几篇博客转载然后总结在一起的,只当是学习笔记,不在意是什么原创和转载了,学到东西就好。

1、IP协议首部格式(IP协议处余网络层)


IP数据报首部图片格式:
最高位在左边,记为0 bit;最低位在右边,记为31 bit

linux网络编程之IP协议首部格式与其配套使用的四个协议(ARP,RARP,ICMP,IGMP)和TCP、UDP协议头结构总结


头部代码结构如下

//定义IP首部

typedef struct _iphdr{

unsigned char h_lenver; //4 位IP版本号+4位首部长度

unsigned char tos; //8位服务类型TOS

unsigned short total_len; //16位IP包总长度(字节)

unsigned short ident; //1 6位标识, 用于辅助IP包的拆装

unsigned short frag_and_flags; //3位标志位+13位偏移位, 也是用于IP包的拆装

unsigned char ttl; //8位IP包生存时间 TTL

unsigned char proto; //8位协议 (TCP, UDP 或其他)

unsigned short checksum; //16位IP首部校验和,最初置零,等所有包头都填写正确后,计算并替换.

unsigned int sourceIP; //32位源IP地址

unsigned int destIP; //32位目的IP地址

}IP_HEADER;

版本:

占 4 位,指 IP 协议的版本目前的 IP 协议版本号为 4 (即 IPv4)

首部长度:

占4位,可表示的最大数值是15个单位(一个单位为 4 字节)因此IP 的首部长度的最大值是 60 字节

区分服务:

占8位,用来获得更好的服务,在旧标准中叫做服务类型,但实际上一直未被使用过.1998 年这个字段改名为区分服务.只有在使用区分服务(DiffServ)时,这个字段才起作用.一般的情况下都不使用这个字段

总长度:

占16位,指首部和数据之和的长度,单位为字节,因此数据报的最大长度为 65535 字节.总长度必须不超过最大传送单元 MTU

标识:

占16位,它是一个计数器,用来产生数据报的标识

标志(flag):

占3位,目前只有前两位有意义

    MF
        标志字段的最低位是 MF (More Fragment)
        MF=1 表示后面“还有分片”。MF=0 表示最后一个分片

    DF
        标志字段中间的一位是 DF (Don't Fragment)
        只有当 DF=0 时才允许分片

片偏移:

占12位,指较长的分组在分片后某片在原分组中的相对位置.片偏移以 8 个字节为偏移单位

linux网络编程之IP协议首部格式与其配套使用的四个协议(ARP,RARP,ICMP,IGMP)和TCP、UDP协议头结构总结

生存时间:

占8位,记为TTL (Time To Live) 数据报在网络中可通过的路由器数的最大值,TTL 字段是由发送端初始设置一个 8 bit字段.推荐的初始值由分配数字 RFC 指定,当前值为 64.发送 ICMP 回显应答时经常把 TTL 设为最大值 255

协议:

占8位,指出此数据报携带的数据使用何种协议以便目的主机的IP层将数据部分上交给哪个处理过程, 1表示为 ICMP 协议, 2表示为 IGMP 协议, 6表示为 TCP 协议, 17表示为 UDP 协议


linux网络编程之IP协议首部格式与其配套使用的四个协议(ARP,RARP,ICMP,IGMP)和TCP、UDP协议头结构总结


首部检验和:

占16位,只检验数据报的首部不检验数据部分.这里不采用 CRC 检验码而采用简单的计算方法

源地址和目的地址:

都各占 4 字节,分别记录源地址和目的地址


计算校验和的经典函数:

SHORT checksum(USHORT* buffer, int size)
{
    unsigned long cksum = 0;
    while(size>1)
    {
        cksum += *buffer++;
        size -= sizeof(USHORT);
    }
    if(size)
    {
        cksum += *(UCHAR*)buffer;
    }
    cksum = (cksum>>16) + (cksum&0xffff); 
    cksum += (cksum>>16); 
    return (USHORT)(~cksum);}




2、网际控制报文协议 ICMP(ICMP处于网络层)

目的:

为了提高 IP 数据报交付成功的机会

注意:

    允许主机和路由器报告差错情况和提供有关异常情况的报告
    ICMP不是高层协议,而是IP层的协议
    ICMP报文作为IP层数据报的数据,加上数据报的首部,组成 IP 数据报发送出去
    ICMP报文的前4个字节是统一的格式,共有三个字段:即类型,代码和检验和.接着的4个字节的内容与ICMP的类型有关

种类:

    ICMP 差错报告报文:
        终点不可达
        源点抑制(Source quench)
        时间超过
        参数问题
        改变路由(重定向)(Redirect)
    ICMP 询问报文:
        回送请求和回答报文
        时间戳请求和回答报文

报头结构(代码)形式

//定义ICMP首部
typedef struct _icmphdr{
unsigned char i_type; //8位类型
unsigned char i_code; //8位代码
unsigned short i_cksum; //16位校验和, 从TYPE开始,直到最后一位用户数据,如果为字节数为奇数则补充一位
unsigned short i_id ; //识别号(一般用进程号作为识别号), 用于匹配ECHO和ECHO REPLY包
unsigned short i_seq ; //报文序列号, 用于标记ECHO报文顺序
unsigned int timestamp; //时间戳
}ICMP_HEADER;


报文格式:

linux网络编程之IP协议首部格式与其配套使用的四个协议(ARP,RARP,ICMP,IGMP)和TCP、UDP协议头结构总结

或者用这张图片

linux网络编程之IP协议首部格式与其配套使用的四个协议(ARP,RARP,ICMP,IGMP)和TCP、UDP协议头结构总结

ICMP各种报文状态如下:

目的不可达报文

类型:3 代码:0至15 检验和
未使用(全0)
收到的IP数据报的一部分,包括IP首部以及数据报数据的前8个字节

源端抑制报文

类型:4 代码:0 检验和
未使用(全0)
收到的IP数据报的一部分,包括IP首部以及数据报数据的前8个字节

超时报文

类型:11 代码:0或1 检验和
未使用(全0)
收到的IP数据报的一部分,包括IP首部以及数据报数据的前8个字节

参数问题

类型:12 代码:0或1 检验和
指针 未使用(全0)
收到的IP数据报的一部分,包括IP首部以及数据报数据的前8个字节

改变路由

类型:5 代码:0到3 检验和
目标路由器IP地址
收到的IP数据报的一部分,包括IP首部以及数据报数据的前8个字节

回送请求和回答

类型:8或0 代码:0 检验和
标识符 序号
由请求报文发送;由回答报文重复

时间戳请求和回答

类型:13或14 代码:0 检验和
标识符 序号
原始时间戳
接收时间戳
发送时间戳

地址掩码请求和回答

类型:17或18 代码:0 检验和
标识符 序号
地址掩码

路由询问和通告

类型:10 代码:0 检验和
标识符 序号


3、传输控制协议 TCP(TCP位于传输层)

TCP头部图片介绍

linux网络编程之IP协议首部格式与其配套使用的四个协议(ARP,RARP,ICMP,IGMP)和TCP、UDP协议头结构总结

头部结构代码

struct tcphdr
{
    __u16   source;         /*源地址端口*/
    __u16   dest;           /*目的地址端口*/
    __u32   seq;            /*序列号*/
    __u32   ack_seq;        /*确认序列号*/
#if defined(__LITTLE_ENDIAN_BITFIELD)
    __u16 res1:4,         /*保留*/
         doff:4,            /*偏移*/
        fin:1,              /*关闭连接标志*/
        syn:1,              /*请求连接标志*/
        rst:1,              /*重置连接标志*/
        psh:1,              /*接收方尽快将数据放到应用层标志*/
        ack:1,              /*确认序号标志*/
        urg:1,              /*紧急指针标志*/
        ece:1,              /*拥塞标志位*/
        cwr:1;              /*拥塞标志位*/
#elif defined(__BIG_ENDIAN_BITFIELD)
    __u16 doff:4,         /*偏移*/
        res1:4,             /*保留*/
        cwr:1,              /*拥塞标志位*/
        ece:1,              /*拥塞标志位*/
        urg:1,              /*紧急指针标志*/
        ack:1,              /*确认序号标志*/
        psh:1,              /*接收方尽快将数据放到应用层标志*/
        rst:1,              /*重置连接标志*/
        syn:1,              /*请求连接标志*/
        fin:1;              /*关闭连接标志*/
#else
#error "Adjust your <asm/byteorder.h> defines"
#endif
    __u16   window;         /*滑动窗口大小*/
    __u16   check;          /*校验和*/
    __u16   urg_ptr;        /*紧急字段指针*/
};

头文件:linux/tcp.h

tcphdr->source
    16位源端口号
tcphdr->dest
    16位目的端口号
tcphdr->seq
    表示此次发送的数据在整个报文段中的起始字节数。序号是32 bit的无符号数。为了安全起见,它的初始值是一个随机生成的数,它到达32位最大值后,又从零开始。
tcphdr->ack_seq
    指定的是下一个期望接收的字节,而不是已经正确接收到的最后一个字节。
tcphdr->doff
    TCP头长度,指明了在TCP头部包含多少个32位的字。此信息是必须的,因为options域的长度是可变的,所以整个TCP头部的长度也是变化的。从技术上讲,这个域实际上指明了数据部分在段内部的其起始地址(以32位字作为单位进行计量),因为这个数值正好是按字为单位的TCP头部的��度,所以,二者的效果是等同的
tcphdr->res1为保留位
tcphdr->window
    是16位滑动窗口的大小,单位为字节,起始于确认序列号字段指明的值,这个值是接收端正期望接收的字节数,其最大值是63353字节。
    TCP中的流量控制是通过一个可变大小的滑动窗口来完成的。window域指定了从被确认的字节算起可以接收的多少个字节。window = 0也是合法的,这相当于说,到现在为止多达ack_seq-1个字节已经接收到了,但是接收方现在状态不佳,需要休息一下,等一会儿再继续接收更多的数据,谢谢。以后,接收方可以通过发送一个同样ack_seq但是window不为0的数据段,告诉发送方继续发送数据段。
tcphdr->check
    是检验和,覆盖了整个的TCP报文段,这是一个强制性的字段,一定是由发送端计算和存储,并由接收端进行验证。
tcphdr->urg_ptr
    这个域被用来指示紧急数据在当前数据段中的位置,它是一个相对于当前序列号的字节偏移值。这个设施可以代替中断信息。
fin, syn, rst, psh, ack, urg为6个标志位
    这6个位域已经保留了超过四分之一个世纪的时间而仍然原封未动,这样的事实正好也说明了TCP的设计者们考虑的是多么的周到。它们的含义如下:
    tcphdr->fin fin位被用于释放一个连接。它表示发送方已经没有数据要传输了。
    tcphdr->syn 同步序号,用来发起一个连接。syn位被用于建立连接的过程。在连接请求中,syn=1; ack=0表示该数据段没有使用捎带的确认域。连接应答捎带了一个确认,所以有syn=1; ack=1。本质上,syn位被用来表示connection request和connection accepted,然而进一步用ack位来区分这两种情况。
    tcphdr->rst 该为用于重置一个已经混乱的连接,之所以会混乱,可能是由于主机崩溃,或者其他的原因。该位也可以被用来拒绝一个无效的数据段,或者拒绝一个连接请求。一般而言,如果你得到的数据段设置了rst位,那说明你这一端有了问题。
    tcphdr->psh 接收方在收到数据后应立即请求将数据递交给应用程序,而不是将它缓冲起来直到整个缓冲区接收满为止(这样做的目的可能是为了效率的原因)
    tcphdr->ack ack位被设置为1表示tcphdr->ack_seq是有效的。如果ack为0,则该数据段不包含确认信息,所以,tcphdr->ack_seq域应该被忽略。
    tcphdr->urg 紧急指针有效
    tcphdr->ece 用途暂时不明
    tcphdr->cwr 用途暂时不明

对于TCP协议,其IP头部的protocol的值应该为6,通过计算IP头部的长度可以得到TCP头部的地址,即TCP的头部在IP头部长度ihl*4之后。


4、用户数据报协议 UDP(UDP处于传输层)

UDP协议是建立在IP协议基础之上的,用在传输层的协议。UDP和IP协议一样是不可靠的数据报服务。
0                               16                              32
-------------------------------------------------------
|       UDP源端口     |      UDP目的端口   |
-------------------------------------------------------
|   UDP数据报长度 |   UDP数据报校验 |
-------------------------------------------------------
|                              数据                             |
-------------------------------------------------------

UDP的头格式为:

struct udphdr
{
u_int16_t source; /*源地址端口*/
u_int16_t dest;    /*目的地址端口*/
u_int16_t len;     /*UDP长度*/
u_int16_t check;   /*UDP校验和*/
};

头文件:linux/udp.h


5、网际组管理协议IGMP(IGMP处于网络层协议)

报文格式:

linux网络编程之IP协议首部格式与其配套使用的四个协议(ARP,RARP,ICMP,IGMP)和TCP、UDP协议头结构总结
作用:

它是TCP/IP 协议族中负责IP 组播成员管理的协议,用来在IP 主机和与其直接相邻的组播路由器之间建立、维护组播组成员关系

功能:

当一台主机加入到一个新的组时,它发送一个IGMP消息到组地址以宣告它的成员身份,多播路由器和交换机就可以从中学习到组的成员.利用从IGMP中获取到的信息,路由器和交换机在每个接口上维护一个多播组成员的列表

两个阶段:

    加入:
        当主机加入新的多播组时,向多播组的多播地址发送IGMP 报文,声明自己要成为该组的成员.本地的多播路由器收到 IGMP 报文后,将组成员关系转发给因特网上的其他多播路由器
    询问:
        周期性地探询本地局域网上的主机,以便知道这些主机是否还继续是组的成员
    注意:
        因为组成员关系是动态的,因此本地多播路由器要只要对某个组有一个主机响应,那么多播路由器就认为这个组是活跃的
        但一个组在经过几次的探询后仍然没有一个主机响应,则不再将该组的成员关系转发给其他的多播路由器

数据包:

IGMP 使用 IP 数据报传递其报文(即 IGMP 报文加上 IP 首部构成 IP 数据报),但它也向 IP 提供服务

具体措施:

    在主机和多播路由器之间的所有通信都是使用 IP 多播
    多播路由器在探询组成员关系时,只需要对所有的组发送一个请求信息的询问报文,而不需要对每一个组发送一个询问报文。默认的询问速率是每 125 秒发送一次
    当同一个网络上连接有几个多播路由器时,它们能够迅速和有效地选择其中的一个来探询主机的成员关系
    在 IGMP 的询问报文中有一个数值 N,它指明一个最长响应时间(默认值为 10秒)。当收到询问时,主机在 0 到 N 之间随机选择发送响应所需经过的时延。对应于最小时延的响应最先发送
    同一个组内的每一个主机都要监听响应,只要有本组的其他主机先发送了响应,自己就可以不再发送响应了



6、地址解析协议 ARP (APR处于网络层协议)

说明:

ARP 每一个主机都设有一个ARP高速缓存(ARP cache),里面有所在的局域网上的各主机和路由器的IP地址到硬件地址的映射表

作用:

通过IP地址得知其物理地址

步骤:

linux网络编程之IP协议首部格式与其配套使用的四个协议(ARP,RARP,ICMP,IGMP)和TCP、UDP协议头结构总结

注意:

    网络上其他主机并不响应 ARP 询问,只有接收端主机接收到这个帧时,才向发送端主机做出这样的回应
    ARP 是解决同一个局域网上的主机或路由器的IP地址和硬件地址的映射问题.若所要找的主机和源主机不在同一个局域网上,那么就要通过ARP找到一个位于本局域网上的某个路由器的硬件地址,然后把分组发送给这个路由器,让这个路由器把分组转发给下一个网络.剩下的工作就由下一个网络来做
    从IP地址到硬件地址的解析是自动进行的,主机的用户对这种地址解析过程是不知道的.
    主机或路由器要和本网络上另一个已知 IP 地址的主机或路由器进行通信,ARP 协议会自动地将该IP地址解析为链路层所需要的硬件地址

四种典型情况:

    发送方是主机,要把 IP 数据报发送到本网络上的另一个主机.这时用 ARP 找到目的主机的硬件地址
    发送方是主机要 IP 数据报发送到其他网络的主机.这时 ARP 找到本网络上某个路由器硬件地址.剩下工作由这个路由器完成
    发送方是路由器,要把 IP 数据报转发到本网络上的一个主机.这时用 ARP 找到目的主机的硬件地址
    发送方是路由器,要把 IP 数据报转发到另一个网络上的一个主机.这时用 ARP 找到本网络上的一个路由器的硬件地址.剩下的工作由这个路由器来完成

报头格式:

linux网络编程之IP协议首部格式与其配套使用的四个协议(ARP,RARP,ICMP,IGMP)和TCP、UDP协议头结构总结
说明:

    硬件类型字段表示硬件地址的类型.它的值为1即表示以太网地址
    协议类型字段表示要映射的协议地址类型.它的值为 0x0800 即表示 IP 地址
    硬件地址长度和协议地址长度分别指出硬件地址和协议地址的长度,以字节为单位.对于以太网上 IP 地址的 ARP 请求或应答来说,它们的值分别为 6 和 4
    操作字段指出四种操作类型,它们是 ARP 请求(值为1)、ARP 应答(值为 2 )、RARP 请求(值为 3 )和 RARP 应答(值为 4 )
    接下来的四个字段是发送端的硬件地址(在本例中是以太网地址)、发送端的协议地址( IP 地址)、目的端的硬件地址和目的端的协议地址.



7、逆向地址解析协议 RARP(RARP处于网络层协议)


定义:

逆地址解析协议 RARP 使只知道自己硬件地址的主机能够知道其IP地址

用途:

这种主机往往是无盘工作站.因此 RARP 协议目前已很少使用

linux网络编程之IP协议首部格式与其配套使用的四个协议(ARP,RARP,ICMP,IGMP)和TCP、UDP协议头结构总结