• 原始套接字-IP头包含选项


    IP头包含选项

    • 创建原始套接字之后,再打开IP_HDRINCL选项,即可在IP头中封装自己的协议,而不仅仅使用系统预定义的协议。一般可以使用这种方法发送原始UDP和TCP协议,而不是使用系统预定义的协议。一般的,可以使用这种方法来发送原始UDP和TCP数据(win xp sp2已经不支持原始tcp数据的发送了)。这里将介绍如何建立自己的UDP封包,一旦掌握了管理UDP头,即可掌握如何创建自己的协议头,或者封装在IP中的协议头。
    • 对于IPV4来说,堆栈会检查IPV4头中的几个域,例如IPV4标识位就由堆栈设置,如果有必要堆栈将数据块分块。就是说,如果创建了一个原始IPV4封包,设置了IP_HDRINCL发送的封包比MTU大小大,堆栈将把数据分为多块。
    • 当使用这个头包含选项之后,就需要自己在每个发送调用中填写IP头,以及任何封装在里面的协议头。

    IP数据报格式

    • 我们称网络层数据报为数据报,IP数据报包含IP头部分和文本部分。IP头有20字节的固定部分和一个长度可变的可选部分。IP头格式如下所示。

    • Version域,者四位指定了数据报的IP版本,对IPv4来说,此域值为4

    • IHL(IP Header Length的缩写),因为IP头的长度不是固定的所以 需要这四位IP数据报中数据部分的开始位置,大多数IP数据报不包含此选项,索引IP数据报有20个字节的头长度。

    • Type of service(服务器类型TOS)域:包含在IPV4头中,用来区分不同类型的IP数据报

    • Total length域:这里是IP数据报的总长度,IP头加数据。这个域是16字节长,所以IP数据报大小理论最大值是65535字节,然而数据包很少有超过1500字节的。

    • Identification域:用来标识以发送的IPV4封包,通常系统每发送一次这个封包就增加一次这个值。

    • Flags和Fragment offset域:当ipv4封包被分割为较小包时,使用这两个域。DF不要分割,这是给路由器的我一个命令,不要分割数据,主机无法将他们恢复回来。MF代表更多的分割。

    • Time to live:(生存时间TTL),包含TTL域是为了确保数据报不会永远呆在网络里打圈。每当数据包被路由器处理时,这个域就会减一,减到0这个报文就会被丢弃。

    • Protocol域:当IP数据报到达目的地时候才使用此域,它指定了数据报的数据部分将要传递给那个传输层协议。6代表要传递给TCP,17代表传递给UDP。

    • Header checksum域:头校验和帮助路由器检测接收到的IP数据报中的位错误。

    • Source address和Destination address域:指定此数据报源IP地址和目的IP地址。

    • Options域:选项域是一个长度可变的域,它包含了可选的信息,最大值是40字节。

    • 为了在数据中包含IP头,下面定义CIPHeader结构

    typedef struct _IPHeader//20个字节
    {
    UCHAR iphVerLen;//版本号和长度
    UCHAR ipTos;//服务类型
    USHORT ipLength;//封包总长度,既整个数据报的长度
    USHORT ipID;//封包标识,唯一标识发送的每一个数据报
    USHORT ipFlags;//标志
    UCHAR IP TTL;//生存时间TTL
    UCHAR ipPROTOCOL;//协议,可能是TCO、UDP、ICMP等
    USHORT ipChecksum ;//校验和
    ULONG ipSource;//源IP地址
    ULONG ipDestination;//目的IP地址
    }IPHeader,*PIPHeader;
    

    UDP数据报格式

    • UDP头仅有八个字节长,包含四个域。

    • 开头两个域分别是源端口号和目的端口号,每个都是16位。第三个域是UDP长度,这是UDP头部和数据报部分的总长。第四个域是校验和,之后就是应用层数据了。

    • 如下定义UDPHeader结构描述UDP头

    typedef struct _UDPHeader{
    USHORT sourcePort;//源端口号
    USHORT destinationPort;//目的端口号
    USHORT len;//封包长度
    USHORT checksum;//校验和
    }UDPHeader,*PUDPHeader;
    
    • 因为UDP是不可靠连接,所以计算校验和是可选的。和IP的校验和不同,UDP的校验和除了包含UDP节之外,还包含IP头中的几个域。计算UDP校验和所需的额外的域称为伪头,UDP校验和基于如下几个域。
     * 32位的源IP地址
     * 32位的目的IP地址
     * 8位0域
     * 8位协议域
     * 16位UDP长度
     * 16位源端口号
     * 16位目的端口号
     * 16位UDP长度
     * 16位UDP校验和(0)
     * UDP载荷
    
    • 开始的5个域形成了UDP伪头,下面是自定义的UDP登报校验和的例程。
    void ComputeUdpPseudoHeaderChecksum(
    IPHeader *pIphdr,
    UDPHeader *pUdphdr,
    char* payload,
    int payloadlen
    ){
    char buff[1024];
    char* ptr=buff;
    int chksumlen=0;
    ULONG zero=0;
    
    //包含源ip地址和目的ip地址
    memcpy(ptr,&pIphdr->ipSource,sizeof(pIphdr->ipSource));
    ptr+=sizeof(pIphdr->ipSource);
    chksumlen+=sizeof(pIphdr->ipSource)
    memcpy(ptr,&pIphdr->ipDestination,sizeof(pIphdr->ipDestination));
    ptr+=sizeof(pIphdr->ipDestination);
    chksumlen+=sizeof(pIphdr->ipDestination);
    
    //包含8位0域
    memcpy(ptr,&zero,1);
    ptr+=1;
    chksumlen+=1;
    
    //协议
    memcpy(ptr,&pIphdr->ipProtocol,sizeof(pIphdr->ipProtocol));
    ptr+=sizeof(pIphdr->ipProtocol);
    chksumlen+=sizeof(pIphdr->ipProtocol);
    
    //UDP长度
    memcpy(ptr,&pUdphdr->len,sizeof(pUdphdr->len));
    ptr+=sizeof(pUdphdr->len);
    chksumlen+=sizeof(pUdphdr->len);
    
    //UDP源端口号
    memcpy(ptr,&pUdphdr->sourcePort,sizeof(pUdphdr->sourcePort));
    ptr+=sizeof(pUdphdr->sourcePort);
    chksumlen+=sizeof(pUdphdr->sourcePort);
    
    //UDP目的端口号
    memcpy(ptr,&pUdphdr->destinationPort,sizeof(pUdphdr->destinationPort));
    ptr+=sizeof(pUdphdr->destinationPort);
    chksumlen+=sizeof(pUdphdr->destinationPort);
    
    //又是UDP长度
    memcpy(ptr,&pUdphdr->len,sizeof(pUdphdr->len));
    ptr+=sizeof(pUdphdr->len);
    chksumlen+=sizeof(pUdphdr->len);
    
    //16位的UDP校验和,置为0
    memcpy(ptr,&zero,sizeof(USHORT));
    ptr+=sizeof(pUdphdr->USHORT);
    chksumlen+=sizeof(pUdphdr->USHORT);
    
    //净荷
    memcpy(ptr,payload,payloaden);
    ptr+=payloaden
    chksumlen+=payloaden
    
    //补齐到下一个16位边界
    for(int i=0li<payloadlen%2;i++){
    *ptr=0;
    ptr++;
    chksumlen++;
    }
    pUdphdr->checksum=checksum((USHORT*)buff,chksumlen);//计算这个校验和,将结构填充到UDP头
    }
    
    • 校验和是以字为单位计算的,所以数据的长度如果不是单字倍数的话,需要以0补足。

    发送原始UDP封包实例

  • 相关阅读:
    JavaScript递归方法 生成 json tree 树形结构数据
    分布式系统唯一ID生成方案汇总
    Twitter-Snowflake,64位自增ID算法详解
    手机端页面自适应解决方案—rem布局
    vue.js之路由
    kafka数据迁移实践
    mysql查询时强制区分大小写
    js加密参数传给后台,后台解密base64
    Target runtime com.genuitec.runtime.generic.jee60 is not defined
    怎么在点击浏览器前进、后退键时刷新页面而不读取缓存
  • 原文地址:https://www.cnblogs.com/binarysystemloophole/p/13525557.html
Copyright © 2020-2023  润新知