IP、TCP、UDP报文头说明

IP报文头(大端字节序)

public struct IP_HEADER
{
    public byte h_verlen;                       //4位IP版本号码 4位首部长度 
    public byte tos;                            //8位服务类型TOS 
    public ushort total_len;                    //16位总长度(字节)
    public ushort ident;                        //16位标识 
    public ushort frag_and_flags;               //3位标志 
    public byte ttl;                            //8位生存时间   TTL 
    public byte proto;                          //8位协议(TCP,UDP等) 
    public ushort checksum;                     //16位IP首部校验和 
    public uint sourceIP;                       //32位来源IP地址 
    public uint destIP;                         //32位目的IP地址 
}
  • 版本:
    • 通常值为4(IPv4)
  • 首部长度:
    • IP头长度 表明包含多少个32Bit 包括可选头(如果有) 值为IP头大小除以4 如:没有可选头IP头为20字节 则该值为5
  • 服务类型:
    • 前3Bit为优先权字段(现在已经被忽略)
    • 第8Bit保留位
    • 第4Bit:最小延时
    • 第5Bit:最大吞吐量
    • 第6Bit:最高可靠性
    • 第7Bit:最小花费
    • 注:4-7Bit只能开启其中一个 全部为0则表示没有特殊要求的一般服务(大部分主机忽略此字段)
  • 总长度:
    • 整个数据包的大小字节数(包括IP头及后面所有数据)
  • 标识:
    • 用来唯一标识一个主机发送的数据包 通常每发送一次 值加1
  • 标志:
    • 第1Bit:保留位
    • 第2Bit:不使用分段
    • 第3Bit:更多分段
  • 片偏移:
    • 如故标志位有是哦嗯分段 则此值应该是分段到原始数据报开头的偏移位置
  • 生存时间:
    • 设置经过路由器的个数 每经过一个路由器值减去1 若为0时数据包被丢弃 通常该值为32 64 128
  • 协议:
    • 指定此IP数据包使用的上层协议类型 如:TCP(6) UDP(17)等
  • 首部校验和:
    • 该值为IP报文头数据中每16Bit的二进制反码求和
  • 来源IP地址:
    • 向目标主机指明接入他的来源IP 若有数据回应 目标主机将使用此IP
  • 目标IP地址:
    • 这个就不用解释了 数据包的目的地IP地址
  • 可选字段:
    • 用来定义一些选项 如:时间戳等 注:并不是所有主机和路由器都支持这些选项 切若使用该字段则长度必须为32Bit的倍数 不足则填充0

TCP报文头(大端字节序)

public struct TCP_HEADER
{
    public ushort th_sport;                     //16位来源端口 
    public ushort th_dport;                     //16位目标端口 
    public int th_seq;                          //32位顺序号 
    public int th_ack;                          //32位确认号 
    public byte th_lenres;                      //4位首部长度/6位保留字 
    public byte th_flag;                        //6位标志位 
    public ushort th_win;                       //16位窗口大小 
    public ushort th_sum;                       //16位校验和 
    public ushort th_urp;                       //16位紧急数据偏移量 
}
  • 来源端口:
    • 向目标主机指明接入他的主机所使用的端口号 用于目标主机回应
  • 目标端口:
    • 指明要连接的目标主机的端口号
  • 顺序号:
    • 数据包编号 表明发送的数据包的顺序 其值通常应该为上次发送包中的顺序号+1 若该数据包是整个TCP连接中的第一个包(SYN包) 则该值随意(通常随机)
  • 确认号:
    • 通常该值是接受到的顺序号+1 若该数据包是整个TCP连接中的第一个数据包(SYN包) 则该值随意(通常为0) 其后则应该是接受到的数据包中的顺序号+1 注:ACK未开启时该值被忽略
  • 首部长度:
    • TCP头长度 表明包好多少个32Bit 包括可选头(如果有) 值为TCP头大小除以4 如:没有可选头TCP头为20字节 则该值为5
  • 标志位:
    • 紧急标志位(URG):开启时表明此数据包处于紧急状态应该优先处理
    • 确认标志位(ACK):开启时表明确认号有效 否则忽略确认号
    • 推送标志位(PSH):开启时表明应该尽快交付给应用进程 而不必等到缓存区填满才推送
    • 复位标志位(RST):开启时表明TCP连接出现连接出现错误 数据包非法拒绝连接
    • 同步标志位(SYN):开启时表明一个连接的请求或者接受报文
    • 终止标志位(FIN):开启时表明释放一个连接
  • 窗口大小:
    • 表示期望接受到的每个数据包字节数
  • 校验和:
    • 该值为TCP报文头括数据部分中每16Bit的二进制反码求和
  • 紧急指针:
    • 若指定该值 他应该是一个偏移量 该值加上顺序号表示紧急数据最后一个字节的顺序号
  • 可选字段:
    • 包含最大载荷与窗口比例等信息 注:若使用该字段则长度必须为32Bit的倍数 不足则填充0

UDP报文头(大端字节序)

public struct UDP_HEADER
{
    public ushort m_usSourPort;                 //16位来源IP地址
    public ushort m_usDestPort;                 //16位目标IP地址
    public ushort m_usLength;                   //16位数据包长度
    public ushort m_usCheckSum;                 //16位校验和
}
  • 来源端口:
    • 向目标主机指明接入他的主机所使用的端口号 用于目标主机回应
  • 目标端口:
    • 指明要连接的目标主机的端口号
  • 数据包长度:
    • UDP头和数据总长度字节数
  • 检验和:
    • 该值为UDP报文头括数据部分中每16Bit的二进制反码求和 注:UDP检验和不是必须的

PSD伪首部(大端字节序)

public struct PSD_HEADER
{
    public uint saddr;                      //源地址 
    public uint daddr;                      //目的地址 
    public byte mbz;
    public byte ptcl;                       //协议类型 
    public ushort length;    	            //上层协议数据长度 
}

注:伪首部并不是数据包中的有效数据 只用作校验和的计算

  • 来源IP地址:
    • 向目标主机指明接入他的来源IP 若有数据回应 目标主机将使用此IP
  • 目标IP地址:
    • 数据包的目的地IP地址
  • 协议号:
    • 指定此IP数据包使用的上层协议类型 如:TCP(6) UDP(17)
  • 包长度:
    • (TCP/UDP)数据包长度字节数(包头+数据部分)

校验和算法

  1. 先将需要计算checksum数据中的checksum设为0
  2. 计算checksum的数据按2Byte划分开来 每2Byte组成一个16Bit的值累加到一个32Bit值中 如果最后有单个Byte的数据 也累加进去
  3. 将32Bit值的高16Bit与低16Bit相加到一个新的32bit值中 若新的32Bit值大于0XFFFF再将新值的高16Bit与低16Bit相加
  4. 将上一步计算所得的16Bit值按位取反 即得到checksum值 存入数据的checksum字段即可

C/C++原本写法

USHORT 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);
}
我转的C#写法
public ushort CheckSum(byte[] byData) {
    uint cksum = 0;
    int index = 0;
    int nSize = byData.Length;
    while (nSize > 1) {
        cksum += BitConverter.ToUInt16(byData, index);
        index += 2;
        nSize -= 2;
    }
    if (nSize == 1) {
        cksum += byData[index - 1];
    }
    while (cksum >> 16 != 0)//上面第3点我不确定是直到只剩右边16位为止还是只需要两步
        cksum = (cksum >> 16) + (cksum & 0xFFFF);
    return (ushort)(~cksum);
}

这篇文章中有使用案例


添加时间:2016-12-08 16:40:55 编辑时间:2016-12-13 11:51:50 阅读:6604 
协议报文头 协议
还没有人留言 要不你来抢一个沙发?
  • 编写评论

      我觉得区分大小写是一个码农的基本素质
[访问统计] 今天:27 总数:271341 提示:未成年人 请在大人陪同下浏览本站内容 还有:世界上最帅的码农 -> 石头 RSS:http://www.st233.com/rss Powered by -> Crystal_lz