我刚刚开始使用套接字编程,所以可能是我错过了一些微不足道的东西,但即使经过许多谷歌搜索和尝试各种各样的事情,我也无法弄清楚我做错了什么。我正在使用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库编写一个监听器,但是套接字可以更好地使用。
无论如何,我会感谢能帮助我弄清楚如何使这项工作的人。