使用pcap发送的UDP数据包显示在Wireshark中但未到达套接字

时间:2016-05-19 09:24:13

标签: c++ networking udp boost-asio pcap

我刚刚开始使用套接字编程,所以可能是我错过了一些微不足道的东西,但即使经过许多谷歌搜索和尝试各种各样的事情,我也无法弄清楚我做错了什么。我正在使用Linux计算机,但这里提供的代码也应该在Windows上编译。

在我详细阐述我的问题之前,我先解释一下到目前为止我做了什么。我有一个程序在一个环回接口IP地址(127.1.0.0:4365)上侦听一个传入的UDP数据包。源代码如下(我实际上使用的是asio的非升级版本,但这无关紧要):

#include <asio.hpp>
using asio::ip::udp;

int main(int /*argc*/, char** /*argv*/)
{
    asio::io_service io_service;

    udp::socket sock(io_service,
            udp::endpoint(asio::ip::address::from_string("127.1.0.0"), 4365));

    char data[128];
    udp::endpoint senderEndpoint;
    size_t length = sock.receive_from(asio::buffer(data, sizeof(data)),
                                      senderEndpoint);

    printf("Received '%s' (%zu bytes) from %s.\n", data, length,
           senderEndpoint.address().to_string().data());

    return 0;
}

当我使用使用套接字发送一个UDP数据包的程序时,侦听器会正确接收UDP数据包。源代码是:

#include <asio.hpp>
using asio::ip::udp;

int main(int /*argc*/, char** /*argv*/)
{
    const uint8_t data[] = "Hello!";
    asio::io_service io_service;
    uint16_t port = 4365;

    asio::ip::address address = asio::ip::address::from_string("127.0.0.1");
    udp::socket sock(io_service, udp::endpoint(address, port));

    address = asio::ip::address::from_string("127.1.0.0");
    udp::endpoint destination(address, port);

    bool success = (sock.send_to(asio::buffer(data, sizeof(data)), destination)
                                                            == sizeof(data));

    if (success)
        printf("Packet sent.\n");
    else
        printf("Error sending packet.\n");

    return 0;
}

现在我面临的问题。我需要从使用pcap库的发件人接收数据包。当我使用以下源代码构造和发送UDP数据包时,我可以看到Wireshark发送数据包,除了IP标识和IP校验和之外,数据包与使用套接字程序发送的数据包相同。 / p>

#include <arpa/inet.h>
#include <pcap/pcap.h>
#include <string.h>
#include <stdint.h>

uint32_t checksum(const uint8_t* buf, uint nbytes, uint32_t sum)
{
    uint i;
    for (i = 0; i < (nbytes & ~1U); i += 2) {
        sum += *(uint16_t*)(buf + i);
        if (sum > 0xFFFF)
            sum -= 0xFFFF;
    }

    // checksum the last byte if present
    if (i < nbytes) {
        sum += buf[i];
        if (sum > 0xFFFF)
            sum -= 0xFFFF;
    }

    return sum;
}

uint16_t ipChecksum(const uint8_t* const packet)
{
    uint32_t sum = 0;
    sum = checksum(packet + 14, 20, sum);

    return ~sum;
}

uint16_t udpChecksum(const uint8_t* packet, int /*userDataLength*/)
{
    // protocol and UDP length
     uint32_t sum = htons(0x0011) + *(uint16_t*)(packet + 38);

#if 0
    // this is supposed to be the right way to calculate the UDP checksum, but
    // the result is not the same as when sockets are used
    sum = checksum(packet + 26, 14, sum); // IP source, destination, UDP header
    sum = checksum(packet + 42, userDataLength, sum); // user data

    return ~sum;
 #else
    // this calculation results in the same UDP checksum as calculated by the
    // socket library
     sum = checksum(packet + 26, 8, sum); // IP source, destination

    return sum;
#endif
}

int main(int /*argc*/, char** /*argv*/)
{
    printf("[pcap] Sending 'Hello!' to 127.1.0.0:4365 from 127.0.0.1:4365\n");

    const uint8_t data[] = "Hello!";

    // create the interface, configure, and activate
    char errbuf[PCAP_ERRBUF_SIZE];
    pcap_t* handle = pcap_create("lo", errbuf);
    if (handle == nullptr) {
        printf("Error: %s\n", errbuf);
        return -1;
    }
    if (pcap_activate(handle) != 0) {
        printf("Failed to activate the interface, try running as root.\n");
         return -1;
    }

    // create the packet
    int packetLength = 42 + sizeof(data);
    uint8_t* packet = new uint8_t[packetLength]; // header is 42 bytes

    // Ethernet II header
    memset(packet, 0, 6); // destination MAC
    memset(packet + 6, 0, 6); // source MAC
    memcpy(packet + 12, "\x08\x00", 2); // EtherType IPv4

    // IP Header
    memcpy(packet + 14, "\x45", 1); // version and header length
    memcpy(packet + 15, "\x00", 1); // services
    uint16_t tmp = htons(20 + 8 + sizeof(data)); // length = IP + UDP + data
    memcpy(packet + 16, &tmp, 2);
    memcpy(packet + 18, "\x00\x00", 2);// identification
    memcpy(packet + 20, "\x40", 1); // flags
    memcpy(packet + 21, "\x00", 1); // fragmentation offset
    memcpy(packet + 22, "\x40", 1); // time to live
    memcpy(packet + 23, "\x11", 1); // UDP protocol
    memcpy(packet + 24, "\x00\x00", 2); // checksum
    memcpy(packet + 26, "\x7F\x00\x00\x01", 4); // source IP
    memcpy(packet + 30, "\x7F\x01\x00\x00", 4); // destination IP
    tmp = ipChecksum(packet);
    memcpy(packet + 24, &tmp, 2); // checksum

     // UDP Header
    memcpy(packet + 34, "\x11\x0D", 2); // source port 4365
    memcpy(packet + 36, "\x11\x0D", 2); // destination port 4365
    tmp = htons(8 + sizeof(data)); // length = UDP header size + data size
    memcpy(packet + 38, &tmp, 2);
    memcpy(packet + 40, "\x00\x00", 2); // checksum

    // user data
    memcpy(packet + 42, data, sizeof(data));

    // calculate the UDP checksum
    tmp = udpChecksum(packet, sizeof(data));
    memcpy(packet + 40, &tmp, 2); // checksum

     // send the packet
    bool success = (pcap_inject(handle, packet, packetLength) == packetLength);

    pcap_close(handle);
    delete packet;

    if (success)
        printf("Packet sent!\n");
    else
        printf("Error sending packet\n");

    return 0;
}

尽管Wireshark显示了数据包,并且数据包几乎与套接字程序发送的数据包相同,但是侦听器无法接收它。即使我使用相同的IP标识,所以数据包是相同的,它也不起作用。

显而易见的解决方案是使用pcap库编写一个监听器,但是套接字可以更好地使用。

无论如何,我会感谢能帮助我弄清楚如何使这项工作的人。

0 个答案:

没有答案