在 Boost ASIO 中,如何设置源 IP 地址以模拟另一台服务器的 IP 地址?
我有一个基于 Boost ASIO 的 C++ 服务器程序,我希望能够将 TCP 使用的源 IP 地址设置为另一台服务器的源 IP 地址.我知道可以读取源 IP 地址和目标 IP 地址,但大概也可以设置它们?
大概如果我在 C++ 代码中设置了错误"的源 IP 地址,就会与网络堆栈发生一些交互.即使C++代码是对的,网络堆栈不会在出路时重新设置源IP地址吗?正确的方法是编写 C++ ASIO 代码来选择特定的虚拟网络接口吗?一个配置了错误"静态 IP 地址的地址?我之前已经看到这是一种控制源 IP 地址的方法.这是我需要做的吗?
我想知道这样做的后果是什么.如果两台机器配置了相同的静态 IP 地址,可能会导致正常"服务器完全停止工作,这很糟糕.
我的服务器有 Windows 和 Linux 端口,我可以使用,以防提议的代码适用于一个操作系统而不是另一个操作系统.我目前倾向于 Kali Linux,因为我可以arpspoof"主服务器并有效地将其关闭一段时间.
解决方案可以通过手动构建网络和传输层标头,然后将标头和所需的有效载荷发送到出站数据的任意地址,将源 IP 设置为原始套接字.原始套接字的使用可能需要提升权限,或者可能被内核禁用或限制,例如在某些 Microsoft 平台.此外,由于 TCP 的三向握手和不可预测的序列号,伪造 TCP 段的有效性,在潜在的之外TCP 重置攻击,值得怀疑.
路由是一个不同的主题,取决于各种路由器和配置.例如,设备可能会执行出口过滤并丢弃具有设备无法验证的源地址的数据包.此外,IP 地址冲突的影响可能会有所不同,但通常会导致连接断断续续.
<小时>Boost.Asio 提供了一个 basic_raw_socket<Protocol>
模板,期望类型满足 协议 类型要求.例如,下面是起始或 raw
协议:
struct raw{typedef boost::asio::ip::basic_endpoint<raw>终点;int type() const { return SOCK_RAW;}int protocol() const { return IPPROTO_RAW;}int family() const { 返回 PF_INET;}};boost::asio::basic_raw_socket<raw>插座;
在处理原始套接字时,困难通常不在于使用 Boost.Asio,而在于必须实现网络和传输线协议.下面是一个完整的最小示例,我尝试通过创建 raw
协议并使用 basic_raw_socket
发送 UDP 消息来使其尽可能简单:
#include <算法>#include #include #include #include ///@brief 原始套接字提供原始套接字的协议.类生{上市:///@brief 原始端点的类型.typedef boost::asio::ip::basic_endpoint<raw>终点;///@brief 原始套接字类型.typedef boost::asio::basic_raw_socket<raw>插座;///@brief 原始解析器类型.typedef boost::asio::ip::basic_resolver<raw>解析器;///@brief 构造表示IPv4 RAW协议.静态原始 v4(){返回原始(IPPROTO_RAW,PF_INET);}///@brief 构造表示IPv6 RAW协议.静态原始 v6(){返回原始(IPPROTO_RAW,PF_INET6);}///@brief 默认构造函数.显式原始():协议_(IPPROTO_RAW),家庭_(PF_INET){}///@brief 获取协议类型的标识符.int type() const{返回 SOCK_RAW;}///@brief 获取协议的标识符.int protocol() const{返回协议_;}///@brief 获取协议族的标识符.int family() 常量{返回家庭_;}///@brief 比较两个协议的相等性.朋友布尔运算符==(const raw& p1,const raw& p2){返回 p1.protocol_ == p2.protocol_ &&p1.family_ == p2.family_;}///比较两个协议的不等式.朋友布尔运算符!=(const raw& p1, const raw& p2){返回 !(p1 == p2);}私人的:显式原始(int protocol_id,int protocol_family):协议_(协议_id),family_(protocol_family){}国际协议_;国际家庭_;};///@ 简短的 Mockup ipv4_header 没有选项.////IPv4 有线格式:////0 1 2 3//0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1//+-------+-------+-------+-------+-------+-------+-------+------+ ---//|版本|标题|类型 |总长度(以字节为单位)|^//|(4) |长度|服务 |||//+-------+-------+-------+-------+-------+-------+-------+------+ |//|标识|标志|片段偏移||//+-------+-------+-------+-------+-------+-------+-------+------+ 20 字节//|生活时间| 高分辨率照片| CLIPARTO协议 |头校验和 ||//+-------+-------+-------+-------+-------+-------+-------+------+ |//|源 IPv4 地址 ||//+-------+-------+-------+-------+-------+-------+-------+------+ |//|目标 IPv4 地址 |v//+-------+-------+-------+-------+-------+-------+-------+------+ ---///选项(如果有)///+-------+-------+-------+-------+-------+-------+-------+------+类 ipv4_header{上市:ipv4_header() { std::fill(buffer_.begin(), buffer_.end(), 0);}无效版本(升压::uint8_t值){缓冲区_[0] = (值<<4) |(buffer_[0] & 0x0F);}void header_length(boost::uint8_t 值){buffer_[0] = (值 & 0x0F) |(buffer_[0] & 0xF0);}void type_of_service(boost::uint8_t value) { buffer_[1] = value;}void total_length(boost::uint16_t value) { encode16(2, value);}无效标识(boost::uint16_t 值){ encode16(4,值);}void dont_fragment(布尔值){buffer_[6] ^= (-value ^ buffer_[6]) &0x40;}void more_fragments(bool 值){buffer_[6] ^= (-value ^ buffer_[6]) &0x20;}void fragment_offset(boost::uint16_t 值){//保留标志.自动标志 = static_cast<uint16_t>(buffer_[6] & 0xE0) <<8;encode16(6, (value & 0x1FFF) | flags);}void time_to_live(boost::uint8_t value) { buffer_[8] = value;}无效协议(boost::uint8_t 值){ buffer_[9] = value;}void checksum(boost::uint16_t value) { encode16(10, value);}void source_address(boost::asio::ip::address_v4 值){自动字节 = value.to_bytes();std::copy(bytes.begin(), bytes.end(), &buffer_[12]);}void destination_address(boost::asio::ip::address_v4 值){自动字节 = value.to_bytes();std::copy(bytes.begin(), bytes.end(), &buffer_[16]);}上市:std::size_t size() const { return buffer_.size();}const boost::array&数据()const { 返回缓冲区_;}私人的:void encode16(boost::uint8_t 索引,boost::uint16_t 值){buffer_[index] = (value >> 8) &0xFF;buffer_[索引 + 1] = 值 &0xFF;}boost::array缓冲_;};void calculate_checksum(ipv4_header& header){//将校验和清零.header.checksum(0);//校验和是的补码和的 16 位补码//标题中的所有 16 位字.//对所有 16 位字求和.自动数据 = header.data();自动求和 = std::accumulate(reinterpret_cast(&data[0]),reinterpret_cast(&data[0] + data.size()),0);//将 32 位折叠成 16 位.而(总和 > > 16){sum = (sum & 0xFFFF) + (sum >> 16);}header.checksum(~sum);}///@brief Mockup IPv4 UDP 标头.////UDP 线格式:////0 1 2 3//0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1//+-------+-------+-------+-------+-------+-------+-------+------+ ---//|源端口 |目的港|^//+-------+-------+-------+-------+-------+-------+-------+------+ 8 个字节//|长度 |校验和 |v//+-------+-------+-------+-------+-------+-------+-------+------+ ---///数据(如果有)///+-------+-------+-------+-------+-------+-------+-------+------+udp_header 类{上市:udp_header() { std::fill(buffer_.begin(), buffer_.end(), 0);}void source_port(boost::uint16_t value) { encode16(0, value);}void destination_port(boost::uint16_t value) { encode16(2, value);}void length(boost::uint16_t value) { encode16(4, value);}void checksum(boost::uint16_t value) { encode16(6, value);}上市:std::size_t size() const { return buffer_.size();}const boost::array&数据()const { 返回缓冲区_;}私人的:void encode16(boost::uint8_t 索引,boost::uint16_t 值){buffer_[index] = (value >> 8) &0xFF;buffer_[索引 + 1] = 值 &0xFF;}boost::array缓冲_;};int main(){boost::asio::io_service io_service;//创建所有 I/O 对象.boost::asio::ip::udp::socket 接收器(io_service,boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0));boost::asio::basic_raw_socket<raw>发件人(io_service,raw::endpoint(raw::v4(), 0));const autoreceiver_endpoint =receiver.local_endpoint();//制作一个带有有效载荷hello"的 UDP 消息来自//8.8.8.8:54321const boost::asio::ip::udp::endpoint spoofed_endpoint(boost::asio::ip::address_v4::from_string("8.8.8.8"),54321);const std::string payload = "你好";//创建 UDP 头.udp_header udp;udp.source_port(spoofed_endpoint.port());udp.destination_port(receiver_endpoint.port());udp.length(udp.size() + payload.size());//标题 + 有效载荷udp.checksum(0);//可选 IPv4//创建 IPv4 标头.ipv4_header ip;ip.version(4);//IPv4ip.header_length(ip.size()/4);//32 位字ip.type_of_service(0);//差异化服务代码点auto total_length = ip.size() + udp.size() + payload.size();ip.total_length(total_length);//整个消息.ip.identification(0);ip.dont_fragment(true);ip.more_fragments(false);ip.fragment_offset(0);ip.time_to_live(4);ip.source_address(spoofed_endpoint.address().to_v4());ip.destination_address(receiver_endpoint.address().to_v4());ip.protocol(IPPROTO_UDP);计算校验和(IP);//收集所有缓冲区并通过原始套接字发送.boost::array缓冲区 = {{boost::asio::buffer(ip.data()),boost::asio::buffer(udp.data()),boost::asio::buffer(payload)}};auto bytes_transferred = sender.send_to(buffers,raw::endpoint(receiver_endpoint.address(),receiver_endpoint.port()));断言(bytes_transferred == total_length);//在接收器上读取.std::vector缓冲区(有效载荷.大小(),' 0');boost::asio::ip::udp::endpoint sender_endpoint;bytes_transferred =receiver.receive_from(boost::asio::buffer(buffer), sender_endpoint);//核实.断言(bytes_transferred == payload.size());断言(std::string(buffer.begin(),buffer.end())==有效载荷);断言(spoofed_endpoint == sender_endpoint);//打印端点.std::cout <<实际发送方端点:" <<sender.local_endpoint() <<"
"接收方端点:" <<接收器.local_endpoint()<<"
"接收方的远程端点:" <<sender_endpoint <<std::endl;}
输出:
$ sudo ./a.out实际发送方端点:0.0.0.0:255接收器端点:0.0.0.0:44806接收方的远程端点:8.8.8.8:54321
如输出所示,虽然真正的发送方端点是 0.0.0.0:255
,但接收方认为发送方的端点是 8.8.8.8:54321
.>
I have a Boost ASIO-based C++ server program and I'd like to be able to set the source IP address used by TCP to that of another server. I know one can read the source and destination IP addresses but presumably they can be set as well?
Presumably if I set the "wrong" source IP address in the C++ code there will be some interaction with the network stack. Won't the network stack re-set the source IP address on the way out even if the C++ code is right? Is the right way to do this to write C++ ASIO code to pick a specific virtual network interface? One that is configured with the "wrong" static IP address? I have seen this before as a way to control the source IP address. Is that what I need to do instead?
I am wondering what the consequences of this would be. Having two machines with the same static IP address configured might cause the "normal" server to stop working completely, which would be bad.
I have both Windows and Linux ports of my server I can use in case the code proposed will work on one OS and not the other. I'm currently leaning towards Kali Linux as I can "arpspoof" the main server and effectively shut it off for a while.
解决方案One can set the source IP to an arbitrary address on outbound data by manually constructing both network and transport layer headers, then sending the headers and the desired payload to a raw socket. The use of raw sockets may require elevated permissions, or may be disabled or restricted by the kernel, such as on some Microsoft platforms. Furthermore, due to TCP's three-way handshake and unpredictable sequence numbers, the effectiveness of the forged TCP segment, outside of potential TCP reset attacks, is questionable.
Routing is a different topic and depends on the various routers and configurations. For instance, devices may perform egress filtering and drop packets with a source address for which the device cannot validate. Furthermore, the affects of an IP address conflict can vary, but it often results in an intermittent connection.
Boost.Asio provides a basic_raw_socket<Protocol>
template that expects a type meeting the Protocol type requirement. For instance, below is the start or a raw
protocol:
struct raw
{
typedef boost::asio::ip::basic_endpoint<raw> endpoint;
int type() const { return SOCK_RAW; }
int protocol() const { return IPPROTO_RAW; }
int family() const { return PF_INET; }
};
boost::asio::basic_raw_socket<raw> socket;
When dealing with raw sockets, the difficulty is often not in using Boost.Asio, but rather having to implement the Network and Transport wire protocol. Below is a complete minimal example where I tried to keep it as simple as possible by creating a raw
protocol and using basic_raw_socket
to send a UDP message:
#include <algorithm>
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/cstdint.hpp>
/// @brief raw socket provides the protocol for raw socket.
class raw
{
public:
///@brief The type of a raw endpoint.
typedef boost::asio::ip::basic_endpoint<raw> endpoint;
///@brief The raw socket type.
typedef boost::asio::basic_raw_socket<raw> socket;
///@brief The raw resolver type.
typedef boost::asio::ip::basic_resolver<raw> resolver;
///@brief Construct to represent the IPv4 RAW protocol.
static raw v4()
{
return raw(IPPROTO_RAW, PF_INET);
}
///@brief Construct to represent the IPv6 RAW protocol.
static raw v6()
{
return raw(IPPROTO_RAW, PF_INET6);
}
///@brief Default constructor.
explicit raw()
: protocol_(IPPROTO_RAW),
family_(PF_INET)
{}
///@brief Obtain an identifier for the type of the protocol.
int type() const
{
return SOCK_RAW;
}
///@brief Obtain an identifier for the protocol.
int protocol() const
{
return protocol_;
}
///@brief Obtain an identifier for the protocol family.
int family() const
{
return family_;
}
///@brief Compare two protocols for equality.
friend bool operator==(const raw& p1, const raw& p2)
{
return p1.protocol_ == p2.protocol_ && p1.family_ == p2.family_;
}
/// Compare two protocols for inequality.
friend bool operator!=(const raw& p1, const raw& p2)
{
return !(p1 == p2);
}
private:
explicit raw(int protocol_id, int protocol_family)
: protocol_(protocol_id),
family_(protocol_family)
{}
int protocol_;
int family_;
};
///@ brief Mockup ipv4_header for with no options.
//
// IPv4 wire format:
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-------+-------+-------+-------+-------+-------+-------+------+ ---
// |version|header | type of | total length in bytes | ^
// | (4) | length| service | | |
// +-------+-------+-------+-------+-------+-------+-------+------+ |
// | identification |flags| fragment offset | |
// +-------+-------+-------+-------+-------+-------+-------+------+ 20 bytes
// | time to live | protocol | header checksum | |
// +-------+-------+-------+-------+-------+-------+-------+------+ |
// | source IPv4 address | |
// +-------+-------+-------+-------+-------+-------+-------+------+ |
// | destination IPv4 address | v
// +-------+-------+-------+-------+-------+-------+-------+------+ ---
// / options (if any) /
// +-------+-------+-------+-------+-------+-------+-------+------+
class ipv4_header
{
public:
ipv4_header() { std::fill(buffer_.begin(), buffer_.end(), 0); }
void version(boost::uint8_t value) {
buffer_[0] = (value << 4) | (buffer_[0] & 0x0F);
}
void header_length(boost::uint8_t value)
{
buffer_[0] = (value & 0x0F) | (buffer_[0] & 0xF0);
}
void type_of_service(boost::uint8_t value) { buffer_[1] = value; }
void total_length(boost::uint16_t value) { encode16(2, value); }
void identification(boost::uint16_t value) { encode16(4, value); }
void dont_fragment(bool value)
{
buffer_[6] ^= (-value ^ buffer_[6]) & 0x40;
}
void more_fragments(bool value)
{
buffer_[6] ^= (-value ^ buffer_[6]) & 0x20;
}
void fragment_offset(boost::uint16_t value)
{
// Preserve flags.
auto flags = static_cast<uint16_t>(buffer_[6] & 0xE0) << 8;
encode16(6, (value & 0x1FFF) | flags);
}
void time_to_live(boost::uint8_t value) { buffer_[8] = value; }
void protocol(boost::uint8_t value) { buffer_[9] = value; }
void checksum(boost::uint16_t value) { encode16(10, value); }
void source_address(boost::asio::ip::address_v4 value)
{
auto bytes = value.to_bytes();
std::copy(bytes.begin(), bytes.end(), &buffer_[12]);
}
void destination_address(boost::asio::ip::address_v4 value)
{
auto bytes = value.to_bytes();
std::copy(bytes.begin(), bytes.end(), &buffer_[16]);
}
public:
std::size_t size() const { return buffer_.size(); }
const boost::array<uint8_t, 20>& data() const { return buffer_; }
private:
void encode16(boost::uint8_t index, boost::uint16_t value)
{
buffer_[index] = (value >> 8) & 0xFF;
buffer_[index + 1] = value & 0xFF;
}
boost::array<uint8_t, 20> buffer_;
};
void calculate_checksum(ipv4_header& header)
{
// Zero out the checksum.
header.checksum(0);
// Checksum is the 16-bit one's complement of the one's complement sum of
// all 16-bit words in the header.
// Sum all 16-bit words.
auto data = header.data();
auto sum = std::accumulate<boost::uint16_t*, boost::uint32_t>(
reinterpret_cast<boost::uint16_t*>(&data[0]),
reinterpret_cast<boost::uint16_t*>(&data[0] + data.size()),
0);
// Fold 32-bit into 16-bits.
while (sum >> 16)
{
sum = (sum & 0xFFFF) + (sum >> 16);
}
header.checksum(~sum);
}
///@brief Mockup IPv4 UDP header.
//
// UDP wire format:
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-------+-------+-------+-------+-------+-------+-------+------+ ---
// | source port | destination port | ^
// +-------+-------+-------+-------+-------+-------+-------+------+ 8 bytes
// | length | checksum | v
// +-------+-------+-------+-------+-------+-------+-------+------+ ---
// / data (if any) /
// +-------+-------+-------+-------+-------+-------+-------+------+
class udp_header
{
public:
udp_header() { std::fill(buffer_.begin(), buffer_.end(), 0); }
void source_port(boost::uint16_t value) { encode16(0, value); }
void destination_port(boost::uint16_t value) { encode16(2, value); }
void length(boost::uint16_t value) { encode16(4, value); }
void checksum(boost::uint16_t value) { encode16(6, value); }
public:
std::size_t size() const { return buffer_.size(); }
const boost::array<uint8_t, 8>& data() const { return buffer_; }
private:
void encode16(boost::uint8_t index, boost::uint16_t value)
{
buffer_[index] = (value >> 8) & 0xFF;
buffer_[index + 1] = value & 0xFF;
}
boost::array<uint8_t, 8> buffer_;
};
int main()
{
boost::asio::io_service io_service;
// Create all I/O objects.
boost::asio::ip::udp::socket receiver(io_service,
boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0));
boost::asio::basic_raw_socket<raw> sender(io_service,
raw::endpoint(raw::v4(), 0));
const auto receiver_endpoint = receiver.local_endpoint();
// Craft a UDP message with a payload 'hello' coming from
// 8.8.8.8:54321
const boost::asio::ip::udp::endpoint spoofed_endpoint(
boost::asio::ip::address_v4::from_string("8.8.8.8"),
54321);
const std::string payload = "hello";
// Create the UDP header.
udp_header udp;
udp.source_port(spoofed_endpoint.port());
udp.destination_port(receiver_endpoint.port());
udp.length(udp.size() + payload.size()); // Header + Payload
udp.checksum(0); // Optioanl for IPv4
// Create the IPv4 header.
ipv4_header ip;
ip.version(4); // IPv4
ip.header_length(ip.size() / 4); // 32-bit words
ip.type_of_service(0); // Differentiated service code point
auto total_length = ip.size() + udp.size() + payload.size();
ip.total_length(total_length); // Entire message.
ip.identification(0);
ip.dont_fragment(true);
ip.more_fragments(false);
ip.fragment_offset(0);
ip.time_to_live(4);
ip.source_address(spoofed_endpoint.address().to_v4());
ip.destination_address(receiver_endpoint.address().to_v4());
ip.protocol(IPPROTO_UDP);
calculate_checksum(ip);
// Gather up all the buffers and send through the raw socket.
boost::array<boost::asio::const_buffer, 3> buffers = {{
boost::asio::buffer(ip.data()),
boost::asio::buffer(udp.data()),
boost::asio::buffer(payload)
}};
auto bytes_transferred = sender.send_to(buffers,
raw::endpoint(receiver_endpoint.address(), receiver_endpoint.port()));
assert(bytes_transferred == total_length);
// Read on the reciever.
std::vector<char> buffer(payload.size(), '');
boost::asio::ip::udp::endpoint sender_endpoint;
bytes_transferred = receiver.receive_from(
boost::asio::buffer(buffer), sender_endpoint);
// Verify.
assert(bytes_transferred == payload.size());
assert(std::string(buffer.begin(), buffer.end()) == payload);
assert(spoofed_endpoint == sender_endpoint);
// Print endpoints.
std::cout <<
"Actual sender endpoint: " << sender.local_endpoint() << "
"
"Receiver endpoint: " << receiver.local_endpoint() << "
"
"Receiver's remote endpoint: " << sender_endpoint << std::endl;
}
Output:
$ sudo ./a.out
Actual sender endpoint: 0.0.0.0:255
Receiver endpoint: 0.0.0.0:44806
Receiver's remote endpoint: 8.8.8.8:54321
As indicated in the output, although the true sender endpoint is 0.0.0.0:255
, the receiver believes the sender's endpoint is 8.8.8.8:54321
.
相关文章