原始UDP封包发送

发送原始UDP封包时:

1 以IPPROTO_UDP 为协议类型创建一个原始套接字,打开原始套接字上的IP_HDRINCL选项

2 构建UDP封包,要先设置IP头,设置UDP头,最后UDP净荷数据。

3 初始化完整的UDP封包之后,调用sendto函数即可将他发送。

计算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(USHORT);
    chksumlen += sizeof(USHORT);

    // 净荷
    memcpy(ptr, payload, payloadlen);
    ptr += payloadlen;
    chksumlen += payloadlen;

    // 补齐到下一个16位边界
    for(int i=0; i<payloadlen%2; i++)
    {
        *ptr = 0;
        ptr++;
        chksumlen++;
    }
    // 计算这个校验和,将结果填充到UDP头
    pUdphdr->checksum = checksum((USHORT*)buff, chksumlen);
}

发送原始UDP封包的过程如下:

int main()
{
    // 输入参数信息
    char szDestIp[] = "10.16.115.88";      //  <<== 填写目的IP地址
    char szSourceIp[] = "127.0.0.1";      //  <<== 填写您自己的IP地址

    USHORT nDestPort = 4567;
    USHORT nSourcePort = 8888;
    char szMsg[] = "This is a test \r\n";
    int nMsgLen = strlen(szMsg);

    // 创建原始套节字
    SOCKET sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_UDP);

    // 有效IP头包含选项
    BOOL bIncl = TRUE;
    ::setsockopt(sRaw, IPPROTO_IP, IP_HDRINCL, (char *)&bIncl, sizeof(bIncl));

    char buff[1024] = { 0 };

    // IP头
    IPHeader *pIphdr = (IPHeader *)buff;
    pIphdr->iphVerLen = (4<<4 | (sizeof(IPHeader)/sizeof(ULONG)));

    pIphdr->ipLength = ::htons(sizeof(IPHeader) + sizeof(UDPHeader) + nMsgLen);
    pIphdr->ipTTL = 128;
    pIphdr->ipProtocol = IPPROTO_UDP;
    pIphdr->ipSource = ::inet_addr(szSourceIp);
    pIphdr->ipDestination = ::inet_addr(szDestIp);
    pIphdr->ipChecksum = checksum((USHORT*)pIphdr, sizeof(IPHeader));

    // UDP头
    UDPHeader *pUdphdr = (UDPHeader *)&buff[sizeof(IPHeader)];
    pUdphdr->sourcePort = htons(8888);
    pUdphdr->destinationPort = htons(nDestPort);
    pUdphdr->len = htons(sizeof(UDPHeader) + nMsgLen);
    pUdphdr->checksum = 0;

    char *pData = &buff[sizeof(IPHeader) + sizeof(UDPHeader)];
    memcpy(pData, szMsg, nMsgLen);

    ComputeUdpPseudoHeaderChecksum(pIphdr, pUdphdr, pData, nMsgLen);

    // 设置目的地址
    SOCKADDR_IN destAddr = { 0 };
    destAddr.sin_family = AF_INET;
    destAddr.sin_port = htons(nDestPort);
    destAddr.sin_addr.S_un.S_addr = ::inet_addr(szDestIp);

    // 发送原始UDP封包
    int nRet;
    for(int i=0; i<5; i++)
    {
        nRet = ::sendto(sRaw, buff, 
            sizeof(IPHeader) + sizeof(UDPHeader) + nMsgLen, 0, (sockaddr*)&destAddr, sizeof(destAddr));
        if(nRet == SOCKET_ERROR)
        {
            printf(" sendto() failed: %d \n", ::WSAGetLastError());
            break;
        }
        else
        {
            printf(" sent %d bytes \n", nRet);
        }
    }

    ::closesocket(sRaw);

    getchar();
    return 0;
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏james大数据架构

使用AsyncTask异步更新UI界面及原理分析

概述: AsyncTask是在Android SDK 1.5之后推出的一个方便编写后台线程与UI线程交互的辅助类。AsyncTask的内部实现是一个线程池,所有...

272100
来自专栏静默虚空的博客

[协议]ICMP协议剖析

1、ICMP简介 ICMP全名为(INTERNET CONTROL MESSAGE PROTOCOL)网络控制消息协议。 ICMP的协议号为1。 ICMP报文就...

44470
来自专栏xingoo, 一个梦想做发明家的程序员

路由跟踪技术

用来确定路由器的IP地址,也就是在网络上到达特定主机所经过的计算机。 程序需要两个套接字: 1 一个用于接收ICMP封包的原始套接字sRaw 2 用于发送TTL...

19970
来自专栏androidBlog

HandlerThread源码分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/...

8810
来自专栏刺客博客

TCP/UDP常用端口列表

93940
来自专栏Play & Scala 技术分享

PlayScala 2.5.x - Filter开发指南

37360
来自专栏Android干货

Android项目实战(三十一):异步下载apk文件并安装(非静默安装)

33460
来自专栏ZKEASOFT

.Net Core使用HttpClient请求Web API注意事项

使用HttpClient可以很方便的请求Web API,但在使用时有一些需要注意的地方,不然会给你的程序带来毁灭性的问题。

18240
来自专栏aCloudDeveloper

internet 的一词多义

这是在《unix网络编程》中看到的比较全面的解释,在此作为一个整理。 一 是网际网,采用TCP/IP协议族通信的任何网络都是网际网,因特网就是一个网际网。 二 ...

18980
来自专栏耕耘实录

批量创建用户并使用sudo和ACL来控制用户权限

版权声明:本文为耕耘实录原创文章,各大自媒体平台同步更新。欢迎转载,转载请注明出处,谢谢。

12040

扫码关注云+社区

领取腾讯云代金券